Skip to content

Instantly share code, notes, and snippets.

@willwade
Last active November 6, 2024 12:56
Show Gist options
  • Save willwade/cc268ef0b0b0ec106e644eec6835b228 to your computer and use it in GitHub Desktop.
Save willwade/cc268ef0b0b0ec106e644eec6835b228 to your computer and use it in GitHub Desktop.
A very rough untested attempt at a .net driver for speech synth
import clr
import threading
import queue
import time
from System.Speech.Synthesis import SpeechSynthesizer, SpeakCompletedEventArgs
clr.AddReference("System.Speech")
class DotNetSpeech:
def __init__(self, proxy):
# Initialize .NET SpeechSynthesizer
self._synthesizer = SpeechSynthesizer()
self._proxy = proxy
self._queue = queue.Queue()
self._speaking = False
self._looping = False
self._stop_requested = False
self._rate = 1.0
self._volume = 1.0
self._current_voice = None
self._synthesizer.SpeakCompleted += self._on_speak_completed
def _on_speak_completed(self, sender, event_args: SpeakCompletedEventArgs):
"""Callback for when speech completes."""
if self._stop_requested:
self._stop_requested = False
else:
self._proxy.notify("finished-utterance", completed=True)
self._speaking = False
self._proxy.setBusy(False)
def say(self, text):
"""Queue a speech request."""
self._queue.put(("say", text))
self._proxy.setBusy(True)
self._start_processing()
def stop(self):
"""Stop current speech and clear the queue."""
self._stop_requested = True
self._synthesizer.SpeakAsyncCancelAll()
self._queue.queue.clear()
def save_to_file(self, text, filename):
"""Save spoken text to a file."""
self._synthesizer.SetOutputToWaveFile(filename)
self._synthesizer.Speak(text)
self._synthesizer.SetOutputToDefaultAudioDevice()
def setProperty(self, name, value):
"""Set properties like rate and volume."""
if name == "rate":
self._synthesizer.Rate = int(value * 10)
self._rate = value
elif name == "volume":
self._synthesizer.Volume = int(value * 100)
self._volume = value
elif name == "voice":
self._synthesizer.SelectVoice(value)
self._current_voice = value
else:
raise KeyError(f"Unknown property '{name}'")
def getProperty(self, name):
"""Get property values."""
if name == "rate":
return self._rate
elif name == "volume":
return self._volume
elif name == "voices":
return [
{
"Id": voice.VoiceInfo.Id,
"Name": voice.VoiceInfo.Name,
"Gender": voice.VoiceInfo.Gender.ToString(),
"Age": voice.VoiceInfo.Age.ToString(),
"Language": voice.VoiceInfo.Culture.Name,
}
for voice in self._synthesizer.GetInstalledVoices()
]
else:
raise KeyError(f"Unknown property '{name}'")
def _start_processing(self):
"""Process the speech queue in a background thread."""
if not self._speaking and not self._queue.empty():
self._speaking = True
action, text = self._queue.get()
if action == "say":
threading.Thread(target=self._synthesizer.SpeakAsync, args=(text,)).start()
def startLoop(self):
"""Start an internal loop to process the queue."""
self._looping = True
while self._looping:
self._start_processing()
time.sleep(0.1)
def endLoop(self):
"""End the internal loop."""
self._looping = False
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment