pygstormgames/speech.py

93 lines
3.0 KiB
Python

"""Speech and text display module for PygStormGames.
Provides text-to-speech functionality with screen text display support.
Uses either speechd or accessible_output2 as the speech backend.
"""
import time
import pyglet
from pyglet.window import key
import textwrap
class Speech:
"""Handles speech output and text display."""
def __init__(self):
"""Initialize speech system with fallback providers."""
self._lastSpoken = {"text": None, "time": 0}
self._active = False
self._speechDelay = 250 # ms delay between identical messages
# Try to initialize speech providers in order of preference
try:
import speechd
self._speech = speechd.Client()
self._provider = "speechd"
self._active = True
except ImportError:
try:
import accessible_output2.outputs.auto
self._speech = accessible_output2.outputs.auto.Auto()
self._provider = "accessible_output2"
self._active = True
except ImportError:
raise RuntimeError("No speech providers found. Install either speechd or accessible_output2.")
# Display settings
self._font = pyglet.text.Label(
'',
font_name='Arial',
font_size=36,
x=400, y=300, # Will be centered later
anchor_x='center', anchor_y='center',
multiline=True,
width=760 # Allow 20px margin on each side
)
def speak(self, text, interrupt=True):
"""Speak text and display it on screen.
Args:
text (str): Text to speak and display
interrupt (bool): Whether to interrupt current speech
"""
currentTime = time.time() * 1000
# Prevent rapid repeated messages
if (self._lastSpoken["text"] == text and
currentTime - self._lastSpoken["time"] < self._speechDelay):
return
# Update last spoken tracking
self._lastSpoken["text"] = text
self._lastSpoken["time"] = currentTime
# Handle speech output based on provider
try:
if self._provider == "speechd":
if interrupt:
self._speech.cancel()
self._speech.speak(text)
else:
self._speech.speak(text, interrupt=interrupt)
except Exception as e:
self._active = False
# Update display text
self._font.text = text
# Center text vertically based on line count
lineCount = len(text.split('\n'))
self._font.y = 300 + (lineCount * self._font.font_size // 4)
def cleanup(self):
"""Clean up speech system resources."""
if not self._active:
return
if self._provider == "speechd":
self._speech.close()
def draw(self):
"""Draw the current text on screen."""
self._font.draw()