macOS: Workaround for Apple being unable to resume speech after changing the voice

This commit is contained in:
Kovid Goyal 2020-12-07 15:50:46 +05:30
parent c8306289f9
commit ed4a397de1
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
4 changed files with 24 additions and 2 deletions

View File

@ -166,6 +166,7 @@ class Client:
text = self.current_marked_text[idx:] text = self.current_marked_text[idx:]
self.ensure_state(use_ssml=True) self.ensure_state(use_ssml=True)
self.ssip_client.speak(wrap_in_ssml(text), callback=self.current_callback) self.ssip_client.speak(wrap_in_ssml(text), callback=self.current_callback)
resume_after_configure = resume
def stop(self): def stop(self):
self.current_callback = self.current_marked_text = self.last_mark = None self.current_callback = self.current_marked_text = self.last_mark = None

View File

@ -21,6 +21,7 @@ class Client:
self.default_system_rate = self.nsss.get_current_rate() self.default_system_rate = self.nsss.get_current_rate()
self.default_system_voice = self.nsss.get_current_voice() self.default_system_voice = self.nsss.get_current_voice()
self.current_callback = None self.current_callback = None
self.current_marked_text = self.last_mark = None
self.dispatch_on_main_thread = dispatch_on_main_thread self.dispatch_on_main_thread = dispatch_on_main_thread
self.status = {'synthesizing': False, 'paused': False} self.status = {'synthesizing': False, 'paused': False}
self.apply_settings(settings) self.apply_settings(settings)
@ -38,6 +39,7 @@ class Client:
from calibre_extensions.cocoa import MARK, END from calibre_extensions.cocoa import MARK, END
event = None event = None
if message_type == MARK: if message_type == MARK:
self.last_mark = data
if data == self.END_MARK: if data == self.END_MARK:
event = Event(EventType.end) event = Event(EventType.end)
self.status = {'synthesizing': False, 'paused': False} self.status = {'synthesizing': False, 'paused': False}
@ -56,6 +58,7 @@ class Client:
def speak_simple_text(self, text): def speak_simple_text(self, text):
self.current_callback = None self.current_callback = None
self.current_marked_text = self.last_mark = None
self.nsss.speak(self.escape_marked_text(text)) self.nsss.speak(self.escape_marked_text(text))
self.status = {'synthesizing': True, 'paused': False} self.status = {'synthesizing': True, 'paused': False}
@ -64,6 +67,8 @@ class Client:
# on macOS didFinishSpeaking is never called for some reason, so work # on macOS didFinishSpeaking is never called for some reason, so work
# around it by adding an extra, special mark at the end # around it by adding an extra, special mark at the end
text += self.mark_template.format(self.END_MARK) text += self.mark_template.format(self.END_MARK)
self.current_marked_text = text
self.last_mark = None
self.nsss.speak(text) self.nsss.speak(text)
self.status = {'synthesizing': True, 'paused': False} self.status = {'synthesizing': True, 'paused': False}
self.current_callback(Event(EventType.begin)) self.current_callback(Event(EventType.begin))
@ -82,6 +87,19 @@ class Client:
if self.current_callback is not None: if self.current_callback is not None:
self.current_callback(Event(EventType.resume)) self.current_callback(Event(EventType.resume))
def resume_after_configure(self):
if self.status['paused'] and self.last_mark is not None and self.current_marked_text:
mark = self.mark_template.format(self.last_mark)
idx = self.current_marked_text.find(mark)
if idx == -1:
text = self.current_marked_text
else:
text = self.current_marked_text[idx:]
self.nsss.speak(text)
self.status = {'synthesizing': True, 'paused': False}
if self.current_callback is not None:
self.current_callback(Event(EventType.resume))
def stop(self): def stop(self):
self.nsss.stop() self.nsss.stop()

View File

@ -100,6 +100,9 @@ class TTS(QObject):
def resume(self, data): def resume(self, data):
self.tts_client.resume() self.tts_client.resume()
def resume_after_configure(self, data):
self.tts_client.resume_after_configure()
def callback(self, event): def callback(self, event):
data = event.data data = event.data
if event.type is event.type.mark: if event.type is event.type.mark:

View File

@ -137,7 +137,8 @@ class ReadAloud:
def play(self): def play(self):
if self.state is PAUSED: if self.state is PAUSED:
ui_operations.tts('resume') ui_operations.tts('resume_after_configure' if self.waiting_for_configure else 'resume')
self.waiting_for_configure = False
self.state = PLAYING self.state = PLAYING
elif self.state is STOPPED: elif self.state is STOPPED:
self.send_message('play') self.send_message('play')
@ -203,7 +204,6 @@ class ReadAloud:
self.view.show_next_spine_item() self.view.show_next_spine_item()
elif which is 'configured': elif which is 'configured':
if self.waiting_for_configure: if self.waiting_for_configure:
self.waiting_for_configure = False
self.play() self.play()
if data is not None: if data is not None:
pass pass