talk2me/venv/lib/python3.11/site-packages/pyttsx3/driver.py
2025-04-04 13:23:15 -06:00

227 lines
6.6 KiB
Python

import sys
import traceback
import weakref
import importlib
class DriverProxy(object):
'''
Proxy to a driver implementation.
@ivar _module: Module containing the driver implementation
@type _module: module
@ivar _engine: Reference to the engine that owns the driver
@type _engine: L{engine.Engine}
@ivar _queue: Queue of commands outstanding for the driver
@type _queue: list
@ivar _busy: True when the driver is busy processing a command, False when
not
@type _busy: bool
@ivar _name: Name associated with the current utterance
@type _name: str
@ivar _debug: Debugging output enabled or not
@type _debug: bool
@ivar _iterator: Driver iterator to invoke when in an external run loop
@type _iterator: iterator
'''
def __init__(self, engine, driverName, debug):
'''
Constructor.
@param engine: Reference to the engine that owns the driver
@type engine: L{engine.Engine}
@param driverName: Name of the driver module to use under drivers/ or
None to select the default for the platform
@type driverName: str
@param debug: Debugging output enabled or not
@type debug: bool
'''
if driverName is None:
# pick default driver for common platforms
if sys.platform == 'darwin':
driverName = 'nsss'
elif sys.platform == 'win32':
driverName = 'sapi5'
else:
driverName = 'espeak'
# import driver module
name = 'pyttsx3.drivers.%s' % driverName
self._module = importlib.import_module(name)
# build driver instance
self._driver = self._module.buildDriver(weakref.proxy(self))
# initialize refs
self._engine = engine
self._queue = []
self._busy = True
self._name = None
self._iterator = None
self._debug = debug
self._current_text = ''
def __del__(self):
try:
self._driver.destroy()
except (AttributeError, TypeError):
pass
def _push(self, mtd, args, name=None):
'''
Adds a command to the queue.
@param mtd: Method to invoke to process the command
@type mtd: method
@param args: Arguments to apply when invoking the method
@type args: tuple
@param name: Name associated with the command
@type name: str
'''
self._queue.append((mtd, args, name))
self._pump()
def _pump(self):
'''
Attempts to process the next command in the queue if one exists and the
driver is not currently busy.
'''
while (not self._busy) and len(self._queue):
cmd = self._queue.pop(0)
self._name = cmd[2]
try:
cmd[0](*cmd[1])
except Exception as e:
self.notify('error', exception=e)
if self._debug:
traceback.print_exc()
def notify(self, topic, **kwargs):
'''
Sends a notification to the engine from the driver.
@param topic: Notification topic
@type topic: str
@param kwargs: Arbitrary keyword arguments
@type kwargs: dict
'''
if 'name' not in kwargs or kwargs['name'] is None: # Avoid overwriting
kwargs['name'] = self._name
self._engine._notify(topic, **kwargs)
def setBusy(self, busy):
'''
Called by the driver to indicate it is busy.
@param busy: True when busy, false when idle
@type busy: bool
'''
self._busy = busy
if not self._busy:
self._pump()
def isBusy(self):
'''
@return: True if the driver is busy, false if not
@rtype: bool
'''
return self._busy
def say(self, text, name):
'''
Called by the engine to push a say command onto the queue.
@param text: Text to speak
@type text: unicode
@param name: Name to associate with the utterance
@type name: str
'''
self._current_text = text
self._push(self._driver.say, (text,), name)
def stop(self):
'''
Called by the engine to stop the current utterance and clear the queue
of commands.
'''
# clear queue up to first end loop command
while(True):
try:
mtd, args, name = self._queue[0]
except IndexError:
break
if(mtd == self._engine.endLoop):
break
self._queue.pop(0)
self._driver.stop()
def save_to_file(self, text, filename, name):
'''
Called by the engine to push a say command onto the queue.
@param text: Text to speak
@type text: unicode
@param name: Name to associate with the utterance
@type name: str
'''
self._push(self._driver.save_to_file, (text, filename), name)
def getProperty(self, name):
'''
Called by the engine to get a driver property value.
@param name: Name of the property
@type name: str
@return: Property value
@rtype: object
'''
return self._driver.getProperty(name)
def setProperty(self, name, value):
'''
Called by the engine to set a driver property value.
@param name: Name of the property
@type name: str
@param value: Property value
@type value: object
'''
self._push(self._driver.setProperty, (name, value))
def runAndWait(self):
'''
Called by the engine to start an event loop, process all commands in
the queue at the start of the loop, and then exit the loop.
'''
self._push(self._engine.endLoop, tuple())
self._driver.startLoop()
def startLoop(self, useDriverLoop):
'''
Called by the engine to start an event loop.
'''
if useDriverLoop:
self._driver.startLoop()
else:
self._iterator = self._driver.iterate()
def endLoop(self, useDriverLoop):
'''
Called by the engine to stop an event loop.
'''
self._queue = []
self._driver.stop()
if useDriverLoop:
self._driver.endLoop()
else:
self._iterator = None
self.setBusy(True)
def iterate(self):
'''
Called by the engine to iterate driver commands and notifications from
within an external event loop.
'''
try:
next(self._iterator)
except StopIteration:
pass