mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
TTS config on linux works
This commit is contained in:
parent
27352f0813
commit
962e68ed19
@ -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):
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user