Пример #1
0
 def create_voice(self):
     from calibre.utils.windows.winsapi import ISpVoice
     self.sp_voice = ISpVoice()
     self.events_thread = Thread(name='SAPIEvents',
                                 target=self.wait_for_events,
                                 daemon=True)
     self.events_thread.start()
Пример #2
0
 def __init__(self):
     from calibre.utils.windows.winsapi import ISpVoice
     self.sp_voice = ISpVoice()
     self.events_thread = Thread(name='SAPIEvents',
                                 target=self.wait_for_events,
                                 daemon=True)
     self.events_thread.start()
     self.current_stream_number = None
     self.current_callback = None
Пример #3
0
 def __init__(self, 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)
     self.events_thread.start()
     self.current_stream_number = None
     self.current_callback = None
     self.dispatch_on_main_thread = dispatch_on_main_thread
     self.status = {'synthesizing': False, 'paused': False}
Пример #4
0
class Client:
    def __init__(self):
        from calibre.utils.windows.winsapi import ISpVoice
        self.sp_voice = ISpVoice()
        self.events_thread = Thread(name='SAPIEvents',
                                    target=self.wait_for_events,
                                    daemon=True)
        self.events_thread.start()
        self.current_stream_number = None
        self.current_callback = None

    def __del__(self):
        if self.sp_voice is not None:
            self.sp_voice.shutdown_event_loop()
            self.events_thread.join(5)
            self.sp_voice = None

    shutdown = __del__

    def wait_for_events(self):
        while True:
            if self.sp_voice.wait_for_event() is False:
                break
            c = self.current_callback
            if c is not None:
                c()

    def get_events(self):
        from calibre_extensions.winsapi import SPEI_TTS_BOOKMARK, SPEI_START_INPUT_STREAM, SPEI_END_INPUT_STREAM
        ans = []
        for (stream_number, event_type,
             event_data) in self.sp_voice.get_events():
            if stream_number == self.current_stream_number:
                if event_type == SPEI_TTS_BOOKMARK:
                    event = Event(EventType.mark, event_data)
                elif event_type == SPEI_START_INPUT_STREAM:
                    event = Event(EventType.begin)
                elif event_type == SPEI_END_INPUT_STREAM:
                    event = Event(EventType.end)
                else:
                    continue
                ans.append(event)
        return ans

    def speak_simple_text(self, text):
        from calibre_extensions.winsapi import SPF_ASYNC, SPF_PURGEBEFORESPEAK, SPF_IS_NOT_XML
        self.current_callback = None
        self.current_stream_number = self.sp_voice.speak(
            text, SPF_ASYNC | SPF_PURGEBEFORESPEAK | SPF_IS_NOT_XML)

    def speak_marked_text(self, text, callback):
        from calibre_extensions.winsapi import SPF_ASYNC, SPF_PURGEBEFORESPEAK, SPF_IS_XML
        self.current_callback = callback
        self.current_stream_number = self.sp_voice.speak(
            text, SPF_ASYNC | SPF_PURGEBEFORESPEAK | SPF_IS_XML, True)
Пример #5
0
class Client:

    mark_template = '<bookmark mark="{}"/>'
    name = 'sapi'
    min_rate = -10
    max_rate = 10

    @classmethod
    def escape_marked_text(cls, text):
        return prepare_string_for_xml(text)

    def __init__(self, settings=None, dispatch_on_main_thread=lambda f: f()):
        self.create_voice()
        self.ignore_next_stop_event = self.ignore_next_start_event = False
        self.default_system_rate = self.sp_voice.get_current_rate()
        self.default_system_voice = self.sp_voice.get_current_voice()
        self.default_system_sound_output = self.sp_voice.get_current_sound_output(
        )
        self.current_stream_number = None
        self.current_callback = None
        self.dispatch_on_main_thread = dispatch_on_main_thread
        self.current_marked_text = self.last_mark = None
        self.status = {'synthesizing': False, 'paused': False}
        self.settings = settings or {}
        self.apply_settings()

    def create_voice(self):
        from calibre.utils.windows.winsapi import ISpVoice
        self.sp_voice = ISpVoice()
        self.events_thread = Thread(name='SAPIEvents',
                                    target=self.wait_for_events,
                                    daemon=True)
        self.events_thread.start()

    def __del__(self):
        if self.sp_voice is not None:
            self.sp_voice.shutdown_event_loop()
            self.events_thread.join(5)
            self.sp_voice = None

    shutdown = __del__

    def apply_settings(self, new_settings=None):
        if self.status['paused']:
            self.sp_voice.resume()
            self.ignore_next_stop_event = True
            self.status = {'synthesizing': False, 'paused': False}
        if new_settings is not None:
            self.settings = new_settings
        self.sp_voice.set_current_rate(
            self.settings.get('rate', self.default_system_rate))
        self.sp_voice.set_current_voice(
            self.settings.get('voice') or self.default_system_voice)
        self.sp_voice.set_current_sound_output(
            self.settings.get('sound_output')
            or self.default_system_sound_output)

    def wait_for_events(self):
        while True:
            if self.sp_voice.wait_for_event() is False:
                break
            self.dispatch_on_main_thread(self.handle_events)

    def handle_events(self):
        from calibre_extensions.winsapi import (SPEI_END_INPUT_STREAM,
                                                SPEI_START_INPUT_STREAM,
                                                SPEI_TTS_BOOKMARK)
        c = self.current_callback
        for (stream_number, event_type,
             event_data) in self.sp_voice.get_events():
            if event_type == SPEI_TTS_BOOKMARK:
                self.last_mark = event_data
                event = Event(EventType.mark, event_data)
            elif event_type == SPEI_START_INPUT_STREAM:
                if self.ignore_next_start_event:
                    self.ignore_next_start_event = False
                    continue
                event = Event(EventType.begin)
                self.status = {'synthesizing': True, 'paused': False}
            elif event_type == SPEI_END_INPUT_STREAM:
                if self.ignore_next_stop_event:
                    self.ignore_next_stop_event = False
                    continue
                event = Event(EventType.end)
                self.status = {'synthesizing': False, 'paused': False}
            else:
                continue
            if c is not None and stream_number == self.current_stream_number:
                try:
                    c(event)
                except Exception:
                    import traceback
                    traceback.print_exc()

    def speak(self, text, is_xml=False, want_events=True):
        from calibre_extensions.winsapi import (SPF_ASYNC, SPF_IS_NOT_XML,
                                                SPF_PURGEBEFORESPEAK,
                                                SPF_IS_XML)
        flags = SPF_IS_XML if is_xml else SPF_IS_NOT_XML
        self.current_stream_number = self.sp_voice.speak(
            text, flags | SPF_PURGEBEFORESPEAK | SPF_ASYNC, want_events)
        return self.current_stream_number

    def speak_simple_text(self, text):
        self.current_callback = None
        self.current_marked_text = self.last_mark = None
        self.speak(text)

    def speak_marked_text(self, text, callback):
        self.current_marked_text = text
        self.last_mark = None
        self.current_callback = callback
        self.speak(text, is_xml=True)

    def stop(self):
        from calibre_extensions.winsapi import SPF_PURGEBEFORESPEAK
        if self.status['paused']:
            self.sp_voice.resume()
        self.sp_voice.speak('', SPF_PURGEBEFORESPEAK, False)
        self.status = {'synthesizing': False, 'paused': False}
        if self.current_callback is not None:
            self.current_callback(Event(EventType.cancel))
        self.current_callback = None

    def pause(self):
        if self.status['synthesizing'] and not self.status['paused']:
            self.sp_voice.pause()
            self.status = {'synthesizing': True, 'paused': True}
            if self.current_callback is not None:
                self.current_callback(Event(EventType.pause))

    def resume(self):
        if self.status['paused']:
            self.sp_voice.resume()
            self.status = {'synthesizing': True, 'paused': False}
            if self.current_callback is not None:
                self.current_callback(Event(EventType.resume))

    def resume_after_configure(self):
        if self.status['paused']:
            self.resume()
            return
        if self.last_mark is None:
            idx = -1
        else:
            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.ignore_next_start_event = True
        if self.current_callback is not None:
            self.current_callback(Event(EventType.resume))
        self.speak(text, is_xml=True)
        self.status = {'synthesizing': True, 'paused': False}

    def get_voice_data(self):
        ans = getattr(self, 'voice_data', None)
        if ans is None:
            ans = self.voice_data = self.sp_voice.get_all_voices()
        return ans

    def get_sound_outputs(self):
        ans = getattr(self, 'sound_outputs', None)
        if ans is None:
            ans = self.sound_outputs = self.sp_voice.get_all_sound_outputs()
        return ans

    def config_widget(self, backend_settings, parent):
        from calibre.gui2.tts.windows_config import Widget
        return Widget(self, backend_settings, parent)

    def change_rate(self, steps=1):
        rate = current_rate = self.settings.get('rate',
                                                self.default_system_rate)
        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.pause()
            self.apply_settings()
            if prev_state['synthesizing']:
                self.status = {'synthesizing': True, 'paused': False}
                self.resume_after_configure()
            return self.settings
Пример #6
0
class Client:

    mark_template = '<bookmark mark="{}"/>'
    name = 'sapi'

    @classmethod
    def escape_marked_text(cls, text):
        return prepare_string_for_xml(text)

    def __init__(self, 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)
        self.events_thread.start()
        self.current_stream_number = None
        self.current_callback = None
        self.dispatch_on_main_thread = dispatch_on_main_thread
        self.status = {'synthesizing': False, 'paused': False}

    def __del__(self):
        if self.sp_voice is not None:
            self.sp_voice.shutdown_event_loop()
            self.events_thread.join(5)
            self.sp_voice = None

    shutdown = __del__

    def wait_for_events(self):
        while True:
            if self.sp_voice.wait_for_event() is False:
                break
            self.dispatch_on_main_thread(self.handle_events)

    def handle_events(self):
        from calibre_extensions.winsapi import (SPEI_END_INPUT_STREAM,
                                                SPEI_START_INPUT_STREAM,
                                                SPEI_TTS_BOOKMARK)
        c = self.current_callback
        for (stream_number, event_type,
             event_data) in self.sp_voice.get_events():
            if event_type == SPEI_TTS_BOOKMARK:
                event = Event(EventType.mark, event_data)
            elif event_type == SPEI_START_INPUT_STREAM:
                event = Event(EventType.begin)
                self.status = {'synthesizing': True, 'paused': False}
            elif event_type == SPEI_END_INPUT_STREAM:
                event = Event(EventType.end)
                self.status = {'synthesizing': False, 'paused': False}
            else:
                continue
            if c is not None and stream_number == self.current_stream_number:
                try:
                    c(event)
                except Exception:
                    import traceback
                    traceback.print_exc()

    def speak_simple_text(self, text):
        from calibre_extensions.winsapi import (SPF_ASYNC, SPF_IS_NOT_XML,
                                                SPF_PURGEBEFORESPEAK)
        self.current_callback = None
        self.current_stream_number = self.sp_voice.speak(
            text, SPF_ASYNC | SPF_PURGEBEFORESPEAK | SPF_IS_NOT_XML, True)

    def speak_marked_text(self, text, callback):
        from calibre_extensions.winsapi import (SPF_ASYNC, SPF_IS_XML,
                                                SPF_PURGEBEFORESPEAK)
        self.current_callback = callback
        self.current_stream_number = self.sp_voice.speak(
            text, SPF_ASYNC | SPF_PURGEBEFORESPEAK | SPF_IS_XML, True)

    def stop(self):
        from calibre_extensions.winsapi import SPF_PURGEBEFORESPEAK
        if self.status['paused']:
            self.sp_voice.resume()
        self.sp_voice.speak('', SPF_PURGEBEFORESPEAK, False)
        self.status = {'synthesizing': False, 'paused': False}
        if self.current_callback is not None:
            self.current_callback(Event(EventType.cancel))
        self.current_callback = None

    def pause(self):
        if self.status['synthesizing'] and not self.status['paused']:
            self.sp_voice.pause()
            self.status = {'synthesizing': True, 'paused': True}
            if self.current_callback is not None:
                self.current_callback(Event(EventType.pause))

    def resume(self):
        if self.status['paused']:
            self.sp_voice.resume()
            self.status = {'synthesizing': True, 'paused': False}
            if self.current_callback is not None:
                self.current_callback(Event(EventType.resume))
Пример #7
0
class Client:

    mark_template = '<bookmark mark="{}"/>'
    name = 'sapi'
    min_rate = -10
    max_rate = 10
    chunk_size = 128 * 1024

    @classmethod
    def escape_marked_text(cls, text):
        return prepare_string_for_xml(text)

    def __init__(self, settings=None, dispatch_on_main_thread=lambda f: f()):
        self.create_voice()
        self.ignore_next_stop_event = None
        self.ignore_next_start_event = False
        self.default_system_rate = self.sp_voice.get_current_rate()
        self.default_system_voice = self.sp_voice.get_current_voice()
        self.default_system_sound_output = self.sp_voice.get_current_sound_output(
        )
        self.current_stream_queue = SpeechQueue()
        self.current_callback = None
        self.dispatch_on_main_thread = dispatch_on_main_thread
        self.synthesizing = False
        self.pause_count = 0
        self.settings = settings or {}
        self.apply_settings()

    @property
    def status(self):
        return {
            'synthesizing': self.synthesizing,
            'paused': self.pause_count > 0
        }

    def clear_pauses(self):
        while self.pause_count:
            self.sp_voice.resume()
            self.pause_count -= 1

    def create_voice(self):
        from calibre.utils.windows.winsapi import ISpVoice
        self.sp_voice = ISpVoice()
        self.events_thread = Thread(name='SAPIEvents',
                                    target=self.wait_for_events,
                                    daemon=True)
        self.events_thread.start()

    def __del__(self):
        if self.sp_voice is not None:
            self.sp_voice.shutdown_event_loop()
            self.events_thread.join(5)
            self.sp_voice = None

    shutdown = __del__

    def apply_settings(self, new_settings=None):
        if self.pause_count:
            self.clear_pauses()
            self.ignore_next_stop_event = monotonic()
            self.synthesizing = False
        if new_settings is not None:
            self.settings = new_settings
        self.sp_voice.set_current_rate(
            self.settings.get('rate', self.default_system_rate))
        self.sp_voice.set_current_voice(
            self.settings.get('voice') or self.default_system_voice)
        self.sp_voice.set_current_sound_output(
            self.settings.get('sound_output')
            or self.default_system_sound_output)

    def wait_for_events(self):
        while True:
            if self.sp_voice.wait_for_event() is False:
                break
            self.dispatch_on_main_thread(self.handle_events)

    def handle_events(self):
        from calibre_extensions.winsapi import (SPEI_END_INPUT_STREAM,
                                                SPEI_START_INPUT_STREAM,
                                                SPEI_TTS_BOOKMARK)
        c = self.current_callback

        for (stream_number, event_type,
             event_data) in self.sp_voice.get_events():
            if event_type == SPEI_TTS_BOOKMARK:
                self.current_stream_queue.last_mark = event_data
                event = Event(EventType.mark, event_data)
            elif event_type == SPEI_START_INPUT_STREAM:
                self.current_stream_queue.start(stream_number)
                if self.ignore_next_start_event:
                    self.ignore_next_start_event = False
                    continue
                self.synthesizing = True
                if not self.current_stream_queue.is_at_start:
                    continue
                event = Event(EventType.begin)
            elif event_type == SPEI_END_INPUT_STREAM:
                if self.ignore_next_stop_event is not None and monotonic(
                ) - self.ignore_next_stop_event < 2:
                    self.ignore_next_stop_event = None
                    continue
                self.synthesizing = False
                if not self.current_stream_queue.is_at_end:
                    continue
                event = Event(EventType.end)
            else:
                continue
            if c is not None and stream_number == self.current_stream_queue.current_stream_number:
                try:
                    c(event)
                except Exception:
                    import traceback
                    traceback.print_exc()

    def speak(self, text, is_xml=False, want_events=True, purge=True):
        from calibre_extensions.winsapi import (SPF_ASYNC, SPF_IS_NOT_XML,
                                                SPF_PURGEBEFORESPEAK,
                                                SPF_IS_XML)
        flags = SPF_IS_XML if is_xml else SPF_IS_NOT_XML
        if purge:
            flags |= SPF_PURGEBEFORESPEAK
        return self.sp_voice.speak(text, flags | SPF_ASYNC, want_events)

    def purge(self):
        from calibre_extensions.winsapi import SPF_PURGEBEFORESPEAK
        self.sp_voice.speak('', SPF_PURGEBEFORESPEAK, False)
        self.synthesizing = False

    def speak_simple_text(self, text):
        self.current_callback = None
        self.current_stream_queue.clear()
        number = self.speak(text)
        self.clear_pauses()
        self.current_stream_queue.add(number, text)

    def speak_marked_text(self, text, callback):
        self.clear_pauses()
        self.current_stream_queue.clear()
        if self.synthesizing:
            self.ignore_next_stop_event = monotonic()
        self.current_callback = callback
        for i, chunk in enumerate(
                add_markup(text, self.mark_template, self.escape_marked_text,
                           self.chunk_size)):
            number = self.speak(chunk, is_xml=True, purge=i == 0)
            self.current_stream_queue.add(number, chunk)

    def stop(self):
        self.clear_pauses()
        self.purge()
        if self.current_callback is not None:
            self.current_callback(Event(EventType.cancel))
        self.current_callback = None

    def pause(self):
        self.sp_voice.pause()
        self.pause_count += 1
        if self.current_callback is not None:
            self.current_callback(Event(EventType.pause))

    def resume(self):
        if self.pause_count:
            self.clear_pauses()
            if self.current_callback is not None:
                self.current_callback(Event(EventType.resume))

    def resume_after_configure(self):
        if self.pause_count:
            self.clear_pauses()
            return
        chunks = tuple(
            self.current_stream_queue.resume_from_last_mark(
                self.mark_template))
        self.ignore_next_start_event = True
        self.current_stream_queue.clear(keep_mark=True)
        self.purge()
        for chunk in chunks:
            number = self.speak(chunk, is_xml=True, purge=False)
            self.current_stream_queue.add(number, chunk)
        if self.current_callback is not None:
            self.current_callback(Event(EventType.resume))
        self.synthesizing = bool(chunks)

    def get_voice_data(self):
        ans = getattr(self, 'voice_data', None)
        if ans is None:
            ans = self.voice_data = self.sp_voice.get_all_voices()
        return ans

    def get_sound_outputs(self):
        ans = getattr(self, 'sound_outputs', None)
        if ans is None:
            ans = self.sound_outputs = self.sp_voice.get_all_sound_outputs()
        return ans

    def config_widget(self, backend_settings, parent):
        from calibre.gui2.tts.windows_config import Widget
        return Widget(self, backend_settings, parent)

    def change_rate(self, steps=1):
        rate = current_rate = self.settings.get('rate',
                                                self.default_system_rate)
        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
            was_synthesizing = self.synthesizing
            self.pause()
            self.apply_settings()
            if was_synthesizing:
                self.synthesizing = True
                self.resume_after_configure()
            return self.settings