TTS config on linux works

This commit is contained in:
Kovid Goyal 2020-12-06 18:58:50 +05:30
parent 27352f0813
commit 962e68ed19
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
5 changed files with 78 additions and 13 deletions

View File

@ -19,8 +19,9 @@ class Client:
def escape_marked_text(cls, text):
return prepare_string_for_xml(text)
def __init__(self, dispatch_on_main_thread=lambda f: f()):
def __init__(self, settings, dispatch_on_main_thread=lambda f: f()):
self.status = {'synthesizing': False, 'paused': False}
self.settings = settings
self.dispatch_on_main_thread = dispatch_on_main_thread
self.current_marked_text = None
self.last_mark = None
@ -59,12 +60,22 @@ class Client:
self.set_use_ssml(use_ssml)
def apply_settings(self, new_settings=None):
if new_settings is not None:
self.settings = new_settings
if self.settings_applied:
self.shutdown()
self.settings_applied = False
self.ensure_state()
self.settings_applied = True
# TODO: Implement this
om = self.settings.get('output_module')
if om:
self.ssip_client.set_output_module(om)
voice = self.settings.get('voice')
if voice:
self.ssip_client.set_synthesis_voice(voice[0])
rate = self.settings.get('rate')
if rate:
self.ssip_client.set_rate(rate)
def set_use_ssml(self, on):
from speechd.client import DataMode, SSIPCommunicationError
@ -148,6 +159,7 @@ class Client:
text = self.current_marked_text
else:
text = self.current_marked_text[idx:]
self.ensure_state(use_ssml=True)
self.ssip_client.speak(text, callback=self.current_callback)
def stop(self):

View File

@ -4,8 +4,8 @@
from contextlib import suppress
from PyQt5.Qt import (
QAbstractItemView, QAbstractTableModel, QComboBox, QFontMetrics, QFormLayout, Qt,
QTableView, QWidget, QSortFilterProxyModel, QItemSelectionModel
QAbstractItemView, QAbstractTableModel, QComboBox, QFontMetrics, QFormLayout,
QItemSelectionModel, QSortFilterProxyModel, QSlider, Qt, QTableView, QWidget
)
from calibre.gui2.preferences.look_feel import BusyCursor
@ -57,14 +57,30 @@ class VoicesModel(QAbstractTableModel):
finally:
self.endResetModel()
def index_for_voice(self, v):
r = 0
if v != self.system_default_voice:
try:
idx = self.current_voices.index(v)
except Exception:
return
r = idx + 1
return self.index(r, 0)
class Widget(QWidget):
def __init__(self, tts_client, initial_backend_settings, parent=None):
def __init__(self, tts_client, initial_backend_settings=None, parent=None):
QWidget.__init__(self, parent)
self.l = l = QFormLayout(self)
self.tts_client = tts_client
self.speed = s = QSlider(Qt.Orientation.Horizontal, self)
s.setMinimumWidth(200)
l.addRow(_('&Speed of speech:'), s)
s.setRange(-100, 100)
s.setSingleStep(5)
self.output_modules = om = QComboBox(self)
with BusyCursor():
self.voice_data = self.tts_client.get_voice_data()
@ -72,7 +88,7 @@ class Widget(QWidget):
om.addItem(_('System default'), self.system_default_output_module)
for x in self.voice_data:
om.addItem(x, x)
l.addRow(_('Speech synthesizer:'), om)
l.addRow(_('Speech s&ynthesizer:'), om)
self.voices = v = QTableView(self)
self.voices_model = VoicesModel(self.voice_data, self.system_default_output_module, parent=v)
@ -88,8 +104,12 @@ class Widget(QWidget):
v.sortByColumn(0, Qt.SortOrder.AscendingOrder)
om.currentIndexChanged.connect(self.output_module_changed)
l.addRow(v)
self.backend_settings = initial_backend_settings or {}
def restore_to_defaults(self):
self.backend_settings = {}
def sizeHint(self):
ans = super().sizeHint()
ans.setHeight(max(ans.height(), 600))
@ -127,6 +147,15 @@ class Widget(QWidget):
om = self.selected_output_module
self.voices_model.change_output_module(om)
@property
def rate(self):
return self.speed.value()
@rate.setter
def rate(self, val):
val = int(val or 0)
self.speed.setValue(val)
@property
def backend_settings(self):
ans = {}
@ -136,6 +165,9 @@ class Widget(QWidget):
voice = self.selected_voice
if voice != VoicesModel.system_default_voice:
ans['voice'] = voice
rate = self.rate
if rate:
ans['rate'] = rate
return ans
@backend_settings.setter
@ -144,6 +176,7 @@ class Widget(QWidget):
self.selected_output_module = om
voice = val.get('voice') or VoicesModel.system_default_voice
self.selected_voice = voice
self.rate = val.get('rate') or 0
if __name__ == '__main__':
@ -154,3 +187,4 @@ if __name__ == '__main__':
w = Widget(c, {})
w.show()
app.exec_()
print(w.backend_settings)

View File

@ -14,7 +14,7 @@ class Client:
def escape_marked_text(cls, text):
return text.replace('[[', ' [ [ ').replace(']]', ' ] ] ')
def __init__(self, dispatch_on_main_thread):
def __init__(self, settings, dispatch_on_main_thread):
from calibre_extensions.cocoa import NSSpeechSynthesizer
self.nsss = NSSpeechSynthesizer(self.handle_message)
self.current_callback = None

View File

@ -19,7 +19,7 @@ class Client:
def escape_marked_text(cls, text):
return prepare_string_for_xml(text)
def __init__(self, dispatch_on_main_thread):
def __init__(self, settings, dispatch_on_main_thread):
from calibre.utils.windows.winsapi import ISpVoice
self.sp_voice = ISpVoice()
self.events_thread = Thread(name='SAPIEvents', target=self.wait_for_events, daemon=True)

View File

@ -2,7 +2,7 @@
# vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>
from PyQt5.Qt import QObject, QVBoxLayout, pyqtSignal
from PyQt5.Qt import QDialogButtonBox, QObject, QVBoxLayout, pyqtSignal
from calibre.gui2 import error_dialog
from calibre.gui2.viewer.config import get_pref_group, vprefs
@ -22,6 +22,12 @@ class Config(Dialog):
self.config_widget = self.tts_client.config_widget(self.backend_settings, self)
l.addWidget(self.config_widget)
l.addWidget(self.bb)
self.config_widget.restore_to_defaults
b = self.bb.addButton(QDialogButtonBox.StandardButton.RestoreDefaults)
b.clicked.connect(self.restore_to_defaults)
def restore_to_defaults(self):
self.config_widget.restore_to_defaults()
def accept(self):
self.backend_settings = self.config_widget.backend_settings
@ -62,7 +68,7 @@ class TTS(QObject):
def tts_client(self):
if self._tts_client is None:
from calibre.gui2.tts.implementation import Client
self._tts_client = Client(self.dispatch_on_main_thread_signal.emit)
self._tts_client = Client(self.backend_settings, self.dispatch_on_main_thread_signal.emit)
return self._tts_client
def shutdown(self):
@ -103,10 +109,23 @@ class TTS(QObject):
def stop(self, data):
self.tts_client.stop()
@property
def backend_settings(self):
from calibre.gui2.tts.implementation import Client
key = 'tts_' + Client.name
return vprefs.get(key) or {}
@backend_settings.setter
def backend_settings(self, val):
from calibre.gui2.tts.implementation import Client
key = 'tts_' + Client.name
val = val or {}
vprefs.set(key, val)
self.tts_client.apply_settings(val)
def configure(self, data):
ui_settings = get_pref_group('tts').copy()
key = 'tts_' + self.tts_client.name
d = Config(self.tts_client, ui_settings, vprefs.get(key) or {}, parent=self.parent())
d = Config(self.tts_client, ui_settings, self.backend_settings, parent=self.parent())
if d.exec_() == d.DialogCode.Accepted:
vprefs[key] = d.backend_settings
self.backend_settings = d.backend_settings
self.settings_changed.emit(d.ui_settings)