Add slower and faster buttons to the TTS bar

This commit is contained in:
Kovid Goyal 2020-12-10 08:59:50 +05:30
parent fbde6307c3
commit 52e855b130
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
6 changed files with 62 additions and 18 deletions

3
imgsrc/srv/faster.svg Normal file
View File

@ -0,0 +1,3 @@
<svg viewBox="0 0 576 512">
<path d="M135.52 412.67a15.99 15.99 0 0 0-7.52 13.57v37.74c0 12.57 13.82 20.23 24.48 13.57l66.69-39.87-47.85-47.85-35.8 22.84zm416.76-195.42l-56.42-34.62c-.06-13.95-2.28-30.77-7.07-48.67-11.32-42.24-31.91-73.43-45.99-69.66-14.08 3.77-16.32 41.08-5 83.32.65 2.44 1.44 4.7 2.15 7.06-4.89-6.08-10.23-12.23-16.31-18.32-30.93-30.92-64.35-47.64-74.66-37.33s6.4 43.73 37.33 74.66c12.67 12.67 25.67 22.77 37.42 29.78-3.14 5.76-5.71 12.06-6.89 19.32-.49 3.03-.71 6.15-.75 9.31C364.55 195.11 261.59 128 192 128c-52.08 0-85.21 28.24-104.72 54.09-1.77-2.7-2.96-5.66-5.33-8.03-18.75-18.75-49.14-18.75-67.88 0-18.74 18.74-18.74 49.14 0 67.88 16.4 16.39 41.29 17.57 59.92 5.29l191.4 191.4c6 6 14.14 9.37 22.63 9.37h144c8.84 0 16-7.16 16-16v-16c0-17.67-14.33-32-32-32h-96v-55.59c0-35.53-23.86-67.16-58.02-76.91l-42.38-12.11c-8.5-2.44-13.42-11.3-11-19.78 2.44-8.52 11.41-13.33 19.78-11l42.38 12.11C318.59 234.38 352 278.66 352 328.41V352l64-32h103.35c31.29 0 56.65-25.36 56.65-56.65a56.66 56.66 0 0 0-23.72-46.1zM496 256c-8.84 0-16-7.16-16-16s7.16-16 16-16 16 7.16 16 16-7.16 16-16 16z" />
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

3
imgsrc/srv/slower.svg Normal file
View File

@ -0,0 +1,3 @@
<svg viewBox="0 0 576 512">
<path d="M68.25 256h279.51c23.54 0 40.97-19.8 35.1-40.04C362.84 146.97 292.33 64 208.41 64h-.82c-83.91 0-154.43 82.97-174.44 151.96C27.27 236.2 44.71 256 68.25 256zm484.03-118.75l-48.65-34.75c-35.17-17.42-80.49 1.57-86.81 40.31-.54 3.32-.82 6.72-.82 10.19v71.22c-.03 13.88-4.6 27.18-13.27 38.44-12.42 16.11-31.25 25.34-51.68 25.34H18.6C8.33 288 0 296.33 0 306.6c0 8 5.12 15.11 12.71 17.64l98.29 22.1L66.17 424c-6.16 10.67 1.54 24 13.86 24h36.95c5.71 0 11-3.05 13.86-8l40.3-69.8c25.99 8.52 45.55 13.8 84.87 13.8s58.89-5.28 84.87-13.8l40.3 69.8c2.86 4.95 8.14 8 13.86 8h36.95c12.32 0 20.01-13.33 13.86-24l-47.21-81.76c21.25-8.42 40.36-21.78 54.81-40.53 14.08-18.28 22.47-39.4 25.29-61.7h40.62c31.29 0 56.65-25.36 56.65-56.65a56.7 56.7 0 0 0-23.73-46.11zM480 176c-8.84 0-16-7.16-16-16s7.16-16 16-16 16 7.16 16 16-7.16 16-16 16z" />
</svg>

After

Width:  |  Height:  |  Size: 868 B

View File

@ -19,6 +19,8 @@ class Client:
mark_template = '<mark name="{}"/>'
name = 'speechd'
min_rate = -100
max_rate = 100
@classmethod
def escape_marked_text(cls, text):
@ -71,7 +73,6 @@ class Client:
self.shutdown()
self.settings_applied = False
self.ensure_state()
self.settings_applied = True
om = self.settings.get('output_module')
if om:
self.ssip_client.set_output_module(om)
@ -81,6 +82,7 @@ class Client:
rate = self.settings.get('rate')
if rate:
self.ssip_client.set_rate(rate)
self.settings_applied = True
def set_use_ssml(self, on):
from speechd.client import DataMode, SSIPCommunicationError
@ -190,3 +192,18 @@ class Client:
ans[om] = tuple(self.ssip_client.list_synthesis_voices())
self.ssip_client.set_output_module(output_module)
return ans
def change_rate(self, steps=1):
rate = current_rate = self.settings.get('rate') or 0
step_size = (self.max_rate - self.min_rate) // 10
rate += steps * step_size
rate = max(self.min_rate, min(rate, self.max_rate))
if rate != current_rate:
self.settings['rate'] = rate
prev_state = self.status.copy()
self.apply_settings()
if prev_state['synthesizing'] and not prev_state['paused']:
self.status['synthesizing'] = True
self.status['paused'] = True
self.resume_after_configure()
return self.settings

View File

@ -76,10 +76,12 @@ class Widget(QWidget):
self.tts_client = tts_client
self.speed = s = QSlider(Qt.Orientation.Horizontal, self)
s.setTickPosition(QSlider.TickPosition.TicksAbove)
s.setMinimumWidth(200)
l.addRow(_('&Speed of speech:'), s)
s.setRange(-100, 100)
s.setSingleStep(5)
s.setRange(self.tts_client.min_rate, self.tts_client.max_rate)
s.setSingleStep(10)
s.setTickInterval((self.tts_client.max_rate - self.tts_client.min_rate) // 2)
self.output_modules = om = QComboBox(self)
with BusyCursor():
@ -183,7 +185,7 @@ if __name__ == '__main__':
from calibre.gui2 import Application
from calibre.gui2.tts.implementation import Client
app = Application([])
c = Client()
c = Client({})
w = Widget(c, {})
w.show()
app.exec_()

View File

@ -34,13 +34,12 @@ class Config(Dialog):
return super().accept()
def add_markup(text_parts):
def add_markup(text_parts, mark_template):
from calibre.gui2.tts.implementation import Client
buf = []
bm = Client.mark_template
for x in text_parts:
if isinstance(x, int):
buf.append(bm.format(x))
buf.append(mark_template.format(x))
else:
buf.append(Client.escape_marked_text(x))
return ''.join(buf)
@ -64,11 +63,15 @@ class TTS(QObject):
import traceback
traceback.print_exc()
@property
def tts_client_class(self):
from calibre.gui2.tts.implementation import Client
return Client
@property
def tts_client(self):
if self._tts_client is None:
from calibre.gui2.tts.implementation import Client
self._tts_client = Client(self.backend_settings, self.dispatch_on_main_thread_signal.emit)
self._tts_client = self.tts_client_class(self.backend_settings, self.dispatch_on_main_thread_signal.emit)
return self._tts_client
def shutdown(self):
@ -91,7 +94,7 @@ class TTS(QObject):
return error_dialog(self.parent(), _('Text-to-Speech unavailable'), str(err), show=True)
def play(self, data):
marked_text = add_markup(data['marked_text'])
marked_text = add_markup(data['marked_text'], self.tts_client_class.mark_template)
self.tts_client.speak_marked_text(marked_text, self.callback)
def pause(self, data):
@ -114,23 +117,31 @@ class TTS(QObject):
@property
def backend_settings(self):
from calibre.gui2.tts.implementation import Client
key = 'tts_' + Client.name
key = 'tts_' + self.tts_client_class.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)
key = 'tts_' + self.tts_client_class.name
vprefs.set(key, val or {})
def configure(self, data):
ui_settings = get_pref_group('tts').copy()
d = Config(self.tts_client, ui_settings, self.backend_settings, parent=self.parent())
if d.exec_() == QDialog.DialogCode.Accepted:
self.backend_settings = d.backend_settings
s = d.backend_settings
self.backend_settings = s
self.tts_client.apply_settings(s)
self.settings_changed.emit(d.ui_settings)
else:
self.settings_changed.emit(None)
def slower(self, data):
settings = self.tts_client.change_rate(steps=-1)
if settings is not None:
self.backend_settings = settings
def faster(self, data):
settings = self.tts_client.change_rate(steps=1)
if settings is not None:
self.backend_settings = settings

View File

@ -117,6 +117,8 @@ class ReadAloud:
bar.appendChild(cb(None, 'hourglass', _('Pause reading')))
else:
bar.appendChild(cb('play', 'play', _('Start reading') if self.state is STOPPED else _('Resume reading')))
bar.appendChild(cb('slower', 'slower', _('Slow down speech')))
bar.appendChild(cb('faster', 'faster', _('Speed up speech')))
bar.appendChild(cb('configure', 'cogs', _('Configure Read aloud')))
bar.appendChild(cb('hide', 'close', _('Close Read aloud')))
if self.state is not WAITING_FOR_PLAY_TO_START:
@ -135,6 +137,12 @@ class ReadAloud:
self.waiting_for_configure = True
ui_operations.tts('configure')
def slower(self):
ui_operations.tts('slower')
def faster(self):
ui_operations.tts('faster')
def play(self):
if self.state is PAUSED:
ui_operations.tts('resume_after_configure' if self.waiting_for_configure else 'resume')