Skip to content

Commit

Permalink
v35.30; support Piper TTS on Linux
Browse files Browse the repository at this point in the history
  • Loading branch information
eliranwong committed May 24, 2024
1 parent 1e24078 commit c732335
Show file tree
Hide file tree
Showing 10 changed files with 75 additions and 28 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -117,3 +117,5 @@ tmpicns.rsrc
log.txt
.python-version
google-cloud-key.json
audio/*.onnx
audio/*.json
2 changes: 1 addition & 1 deletion UniqueBibleAppVersion.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
35.29
35.30
5 changes: 5 additions & 0 deletions gui/ConfigFlagsWindow.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ def getOptions(self):
("fcitx", config.fcitx, self.fcitxChanged, False, config.thisTranslation["fcitx"]),
("ibus", config.ibus, self.ibusChanged, False, config.thisTranslation["ibus"]),
("espeak", config.espeak, self.espeakChanged, False, config.thisTranslation["espeak"]),
("piper", config.piper, self.piperChanged, False, "piper text-to-speech feature"),
]
if config.developer:
options += [
Expand Down Expand Up @@ -495,6 +496,10 @@ def espeakChanged(self):
config.espeak = not config.espeak
self.parent.handleRestart()

def piperChanged(self):
config.piper = not config.piper
self.parent.handleRestart()

def forceOnlineTtsChanged(self):
config.forceOnlineTts = not config.forceOnlineTts
self.parent.handleRestart()
Expand Down
3 changes: 3 additions & 0 deletions latest_changes.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
Changes in 35.30:
* Support Piper TTS on Linux

Changes in 35.29:
* Add concordance to api server

Expand Down
18 changes: 9 additions & 9 deletions patches.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1301,10 +1301,8 @@
(34.16, "file", "htmlResources/material/communication/import_export/materialiconsoutlined/48dp/2x/outline_import_export_black_48dp.png")
(34.17, "file", "plugins/menu/Terminal Mode.py")
(34.20, "file", "gui/PlaylistUI.py")
(34.23, "file", "gui/ConfigFlagsWindow.py")
(34.23, "file", "util/RemoteCliMainWindow.py")
(34.26, "file", "install/module.py")
(34.26, "file", "util/text_editor_checkup.py")
(34.27, "file", "htmlResources/material/action/question_answer/materialiconsoutlined/48dp/2x/outline_question_answer_black_48dp.png")
(34.28, "file", "plugins/context/Read All Verses.py")
(34.31, "file", "gui/Styles.py")
Expand Down Expand Up @@ -1354,10 +1352,8 @@
(35.13, "file", "util/CrossPlatform.py")
(35.16, "file", "util/VlcUtil.py")
(35.19, "file", "startup/nonGui.py")
(35.19, "file", "util/ConfigUtil.py")
(35.20, "file", "util/TextUtil.py")
(35.22, "file", "util/RemoteCliHandler.py")
(35.23, "file", "util/checkup.py")
(35.24, "file", "gui/MaterialMainWindow.py")
(35.24, "file", "util/ShortcutUtil.py")
(35.25, "file", "db/StatisticsWordsSqlite.py")
Expand All @@ -1383,17 +1379,21 @@
(35.25, "file", "plugins/menu/Display Word Frequency.py")
(35.25, "file", "util/GitHubRepoInfo.py")
(35.25, "file", "util/LanguageUtil.py")
(35.25, "file", "util/TextCommandParser.py")
(35.26, "file", "file_manager.txt")
(35.26, "file", "util/LocalCliHandler.py")
(35.26, "file", "util/terminal_mode_dialogs.py")
(35.26, "file", "util/terminal_system_command_prompt.py")
(35.26, "file", "util/TextEditorUtility.py")
(35.27, "file", "db/BiblesSqlite.py")
(35.28, "file", ".gitignore")
(35.28, "file", "gui/MainWindow.py")
(35.28, "file", "util/DatafileLocation.py")
(35.29, "file", "util/RemoteApiHandler.py")
(35.29, "file", "latest_changes.txt")
(35.29, "file", "UniqueBibleAppVersion.txt")
(35.29, "file", "patches.txt")
(35.30, "file", "gui/ConfigFlagsWindow.py")
(35.30, "file", "util/checkup.py")
(35.30, "file", "util/ConfigUtil.py")
(35.30, "file", "util/text_editor_checkup.py")
(35.30, "file", "util/TextCommandParser.py")
(35.30, "file", "util/TextEditorUtility.py")
(35.30, "file", "latest_changes.txt")
(35.30, "file", "UniqueBibleAppVersion.txt")
(35.30, "file", "patches.txt")
6 changes: 6 additions & 0 deletions util/ConfigUtil.py
Original file line number Diff line number Diff line change
Expand Up @@ -687,6 +687,12 @@ def getCurrentVenvDir():
setConfig("enableSystemTrayOnLinux", """
# Enable UBA system tray on Linux os""",
False)
setConfig("piper", """
# Piper text-to-speech feature on Linux""",
False)
setConfig("piperVoice", """
# Piper text-to-speech voice""",
"en_US-lessac-medium")
setConfig("forceOnlineTts", """
# This forces default text-to-speech feature uses online service, even if offline tts engine is installed.""",
False)
Expand Down
22 changes: 20 additions & 2 deletions util/TextCommandParser.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# coding=utf-8
import glob, pprint, traceback, pydoc, threading, asyncio
import glob, pprint, traceback, pydoc, threading, asyncio, shutil
import os, re, webbrowser, platform, zipfile, subprocess, config
from prompt_toolkit.input import create_input
from prompt_toolkit.keys import Keys
Expand Down Expand Up @@ -1541,6 +1541,8 @@ def googleTextToSpeech(self, command, source):
# speak:::
# run text to speech feature
def textToSpeech(self, command, source):
def getHideOutputSuffix():
return f" > /dev/null 2>&1"
if config.forceOnlineTts:
return self.googleTextToSpeech(command, source)
# Stop current playing first if any:
Expand Down Expand Up @@ -1599,7 +1601,23 @@ def textToSpeech(self, command, source):
# Use "grc" to read, becuase it sounds closer to "he" than "en" does.
language = "grc"

if platform.system() == "Linux" and config.espeak:
if platform.system() == "Linux" and config.piper and (shutil.which("cvlc") or shutil.which("aplay")):
model_dir = os.path.join(os.getcwd(), "audio")
model_path = f"""{os.path.join(model_dir, config.piperVoice)}.onnx"""
model_config_path = f"""{model_path}.json"""
if os.path.isfile(model_path):
if shutil.which("cvlc"):
cmd = f'''"{shutil.which("piper")}" --model "{model_path}" --config "{model_config_path}" --output-raw | cvlc --play-and-exit --rate {config.vlcSpeed} --demux=rawaud --rawaud-channels=1 --rawaud-samplerate=22050 -{getHideOutputSuffix()}'''
elif shutil.which("aplay"):
cmd = f'''"{shutil.which("piper")}" --model "{model_path}" --config "{model_config_path}" --output-raw | aplay -r 22050 -f S16_LE -t raw -{getHideOutputSuffix()}'''
else:
print("[Downloading voice ...] ")
if shutil.which("cvlc"):
cmd = f'''"{shutil.which("piper")}" --model {config.piperVoice} --download-dir "{model_dir}" --data-dir "{model_dir}" --output-raw | cvlc --play-and-exit --rate {config.vlcSpeed} --demux=rawaud --rawaud-channels=1 --rawaud-samplerate=22050 -{getHideOutputSuffix()}'''
elif shutil.which("aplay"):
cmd = f'''"{shutil.which("piper")}" --model {config.piperVoice} --download-dir "{model_dir}" --data-dir "{model_dir}" --output-raw | aplay -r 22050 -f S16_LE -t raw -{getHideOutputSuffix()}'''
pydoc.pipepager(text, cmd=cmd)
elif platform.system() == "Linux" and config.espeak:
if WebtopUtil.isPackageInstalled("espeak"):
isoLang2epeakLang = TtsLanguages().isoLang2epeakLang
languages = isoLang2epeakLang.keys()
Expand Down
17 changes: 17 additions & 0 deletions util/TextEditorUtility.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,23 @@
from prompt_toolkit.styles import Style
#from util.terminal_system_command_prompt import SystemCommandPrompt

if config.qtLibrary == "pyside6":
try:
#QtTextToSpeech is currently not in PySide6 pip3 package
#ModuleNotFoundError: No module named 'PySide6.QtTextToSpeech'
from PySide6.QtTextToSpeech import QTextToSpeech
except:
pass
else:
try:
# Note: qtpy.QtTextToSpeech is not found!
from PySide2.QtTextToSpeech import QTextToSpeech
except:
try:
from PyQt5.QtTextToSpeech import QTextToSpeech
except:
pass


# Created for running text editor without UBA
class TextEditorUtility:
Expand Down
14 changes: 6 additions & 8 deletions util/checkup.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import config, subprocess, os, zipfile, platform, sys, re
import config, subprocess, os, zipfile, platform, sys, re, shutil
from shutil import copyfile
from util.WebtopUtil import WebtopUtil
from install.module import *
Expand Down Expand Up @@ -285,14 +285,12 @@ def isOfflineTtsInstalled():
if not key.endswith("(Enhanced)") and not key.endswith("(Premium)"):
config.macVoices[key] = macVoices[key]
return True if config.macVoices else False
elif platform.system() == "Linux" and config.espeak:
espeakInstalled, _ = subprocess.Popen("which espeak", shell=True, stdout=subprocess.PIPE).communicate()
if not espeakInstalled:
elif platform.system() == "Linux":
if not shutil.which("espeak") and config.espeak:
config.espeak = False
print("'espeak' is not found. To set up 'espeak', you may read https://github.com/eliranwong/ChromeOSLinux/blob/main/multimedia/espeak.md")
return False
else:
return True
if not shutil.which("piper") and config.piper:
config.piper = False
return True if shutil.which("piper") or shutil.which("espeak") else False
else:
if config.qtLibrary == "pyside6":
try:
Expand Down
14 changes: 6 additions & 8 deletions util/text_editor_checkup.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import config, subprocess, os, platform, sys, re, platform
import config, subprocess, os, platform, sys, re, platform, shutil


config.pipIsUpdated = False
Expand Down Expand Up @@ -143,14 +143,12 @@ def isOfflineTtsInstalled():
for key in sorted(macVoices):
config.macVoices[key] = macVoices[key]
return True if config.macVoices else False
elif platform.system() == "Linux" and config.espeak:
espeakInstalled, _ = subprocess.Popen("which espeak", shell=True, stdout=subprocess.PIPE).communicate()
if not espeakInstalled:
elif platform.system() == "Linux":
if not shutil.which("espeak") and config.espeak:
config.espeak = False
print("'espeak' is not found. To set up 'espeak', you may read https://github.com/eliranwong/ChromeOSLinux/blob/main/multimedia/espeak.md")
return False
else:
return True
if not shutil.which("piper") and config.piper:
config.piper = False
return True if shutil.which("piper") or shutil.which("espeak") else False
else:
if config.qtLibrary == "pyside6":
try:
Expand Down

0 comments on commit c732335

Please sign in to comment.