Fixes to the voice driver. It should actually work completely now.
This commit is contained in:
@ -3,6 +3,7 @@
|
||||
import subprocess
|
||||
import importlib.util
|
||||
import os
|
||||
import time
|
||||
|
||||
class DynamicVoiceCommand:
|
||||
"""Dynamic command class for voice selection"""
|
||||
@ -23,34 +24,42 @@ class DynamicVoiceCommand:
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
self.env['runtime']['outputManager'].presentText(f"Testing {self.voice} from {self.module}", interrupt=True)
|
||||
self.env['runtime']['outputManager'].presentText(f"Testing voice {self.voice} from {self.module}. Please wait.", interrupt=True)
|
||||
|
||||
# Test voice first
|
||||
if self.testVoice():
|
||||
self.env['runtime']['outputManager'].presentText("Voice test completed. Press Enter again to apply.", interrupt=True)
|
||||
# Brief pause before testing to avoid speech overlap
|
||||
time.sleep(0.5)
|
||||
|
||||
# Test voice
|
||||
testResult, errorMsg = self.testVoice()
|
||||
if testResult:
|
||||
self.env['runtime']['outputManager'].presentText("Voice test completed successfully. Navigate to Apply Tested Voice to use this voice.", interrupt=False, flush=False)
|
||||
|
||||
# Store for confirmation
|
||||
# Store for confirmation (use same variables as apply_tested_voice.py)
|
||||
self.env['commandBuffer']['lastTestedModule'] = self.module
|
||||
self.env['commandBuffer']['lastTestedVoice'] = self.voice
|
||||
self.env['commandBuffer']['pendingVoiceModule'] = self.module
|
||||
self.env['commandBuffer']['pendingVoiceVoice'] = self.voice
|
||||
self.env['commandBuffer']['voiceTestCompleted'] = True
|
||||
|
||||
self.env['runtime']['outputManager'].playSound('Accept')
|
||||
else:
|
||||
self.env['runtime']['outputManager'].presentText("Voice test failed", interrupt=True)
|
||||
self.env['runtime']['outputManager'].playSound('Error')
|
||||
self.env['runtime']['outputManager'].presentText(f"Voice test failed: {errorMsg}", interrupt=False, flush=False)
|
||||
|
||||
except Exception as e:
|
||||
self.env['runtime']['outputManager'].presentText(f"Voice selection error: {str(e)}", interrupt=True)
|
||||
self.env['runtime']['outputManager'].playSound('Error')
|
||||
self.env['runtime']['outputManager'].presentText(f"Voice selection error: {str(e)}", interrupt=False, flush=False)
|
||||
|
||||
def testVoice(self):
|
||||
"""Test voice with spd-say"""
|
||||
try:
|
||||
cmd = ['spd-say', '-o', self.module, '-y', self.voice, self.testMessage]
|
||||
result = subprocess.run(cmd, timeout=8)
|
||||
return result.returncode == 0
|
||||
except Exception:
|
||||
return False
|
||||
cmd = ['spd-say', '-C', '-w', '-o', self.module, '-y', self.voice, self.testMessage]
|
||||
result = subprocess.run(cmd, timeout=8, capture_output=True, text=True)
|
||||
if result.returncode == 0:
|
||||
return True, "Voice test successful"
|
||||
else:
|
||||
error_msg = result.stderr.strip() if result.stderr else f"Command failed with return code {result.returncode}"
|
||||
return False, error_msg
|
||||
except subprocess.TimeoutExpired:
|
||||
return False, "Voice test timed out"
|
||||
except Exception as e:
|
||||
return False, f"Error running voice test: {str(e)}"
|
||||
|
||||
def setCallback(self, callback):
|
||||
pass
|
||||
@ -80,30 +89,83 @@ class DynamicApplyVoiceCommand:
|
||||
|
||||
self.env['runtime']['outputManager'].presentText(f"Applying {voice} from {module}", interrupt=True)
|
||||
|
||||
# Apply to runtime settings
|
||||
# Debug: Show current settings
|
||||
settingsManager = self.env['runtime']['settingsManager']
|
||||
settingsManager.settings['speech']['driver'] = 'speechdDriver'
|
||||
settingsManager.settings['speech']['module'] = module
|
||||
settingsManager.settings['speech']['voice'] = voice
|
||||
currentModule = settingsManager.getSetting('speech', 'module')
|
||||
currentVoice = settingsManager.getSetting('speech', 'voice')
|
||||
self.env['runtime']['outputManager'].presentText(f"Current: {currentVoice} from {currentModule}", interrupt=False, flush=False)
|
||||
|
||||
# Reinitialize speech driver
|
||||
if 'speechDriver' in self.env['runtime']:
|
||||
speechDriver = self.env['runtime']['speechDriver']
|
||||
speechDriver.shutdown()
|
||||
speechDriver.initialize(self.env)
|
||||
# Apply to runtime settings with fallback
|
||||
settingsManager = self.env['runtime']['settingsManager']
|
||||
|
||||
# Store old values for safety
|
||||
oldDriver = settingsManager.getSetting('speech', 'driver')
|
||||
oldModule = settingsManager.getSetting('speech', 'module')
|
||||
oldVoice = settingsManager.getSetting('speech', 'voice')
|
||||
|
||||
try:
|
||||
# Apply new settings to runtime only (use setSetting to update settingArgDict)
|
||||
settingsManager.setSetting('speech', 'driver', 'speechdDriver')
|
||||
settingsManager.setSetting('speech', 'module', module)
|
||||
settingsManager.setSetting('speech', 'voice', voice)
|
||||
|
||||
self.env['runtime']['outputManager'].presentText("Voice applied successfully!", interrupt=True)
|
||||
self.env['runtime']['outputManager'].playSound('Accept')
|
||||
# Apply settings to speech driver directly
|
||||
if 'speechDriver' in self.env['runtime']:
|
||||
speechDriver = self.env['runtime']['speechDriver']
|
||||
|
||||
# Get current module to see if we're changing modules
|
||||
currentModule = settingsManager.getSetting('speech', 'module')
|
||||
moduleChanging = (currentModule != module)
|
||||
|
||||
# Set module and voice on driver instance first
|
||||
speechDriver.setModule(module)
|
||||
speechDriver.setVoice(voice)
|
||||
|
||||
if moduleChanging:
|
||||
# Module change requires reinitializing the speech driver
|
||||
self.env['runtime']['outputManager'].presentText(f"Switching from {currentModule} to {module} module", interrupt=True)
|
||||
speechDriver.shutdown()
|
||||
speechDriver.initialize(self.env)
|
||||
# Re-set after initialization
|
||||
speechDriver.setModule(module)
|
||||
speechDriver.setVoice(voice)
|
||||
self.env['runtime']['outputManager'].presentText("Speech driver reinitialized", interrupt=True)
|
||||
|
||||
# Debug: verify what was actually set
|
||||
self.env['runtime']['outputManager'].presentText(f"Speech driver now has module: {speechDriver.module}, voice: {speechDriver.voice}", interrupt=True)
|
||||
|
||||
# Force application by speaking a test message
|
||||
self.env['runtime']['outputManager'].presentText("Voice applied successfully! You should hear this in the new voice.", interrupt=True)
|
||||
|
||||
# Brief pause then more speech to test
|
||||
time.sleep(1)
|
||||
self.env['runtime']['outputManager'].presentText("Use save settings to make permanent", interrupt=True)
|
||||
|
||||
# Clear pending state
|
||||
self.env['commandBuffer']['voiceTestCompleted'] = False
|
||||
|
||||
# Exit vmenu after successful application
|
||||
self.env['runtime']['vmenuManager'].setActive(False)
|
||||
|
||||
except Exception as e:
|
||||
# Revert on failure
|
||||
settingsManager.settings['speech']['driver'] = oldDriver
|
||||
settingsManager.settings['speech']['module'] = oldModule
|
||||
settingsManager.settings['speech']['voice'] = oldVoice
|
||||
|
||||
# Try to reinitialize with old settings
|
||||
if 'speechDriver' in self.env['runtime']:
|
||||
try:
|
||||
speechDriver = self.env['runtime']['speechDriver']
|
||||
speechDriver.shutdown()
|
||||
speechDriver.initialize(self.env)
|
||||
except:
|
||||
pass # If this fails, at least we tried
|
||||
|
||||
self.env['runtime']['outputManager'].presentText(f"Failed to apply voice, reverted: {str(e)}", interrupt=False, flush=False)
|
||||
|
||||
except Exception as e:
|
||||
self.env['runtime']['outputManager'].presentText(f"Apply failed: {str(e)}", interrupt=True)
|
||||
self.env['runtime']['outputManager'].playSound('Error')
|
||||
self.env['runtime']['outputManager'].presentText(f"Apply voice error: {str(e)}", interrupt=False, flush=False)
|
||||
|
||||
def setCallback(self, callback):
|
||||
pass
|
||||
@ -129,13 +191,9 @@ def addDynamicVoiceMenus(vmenuManager):
|
||||
for module in modules[:8]: # Limit to 8 modules to keep menu manageable
|
||||
moduleMenu = {}
|
||||
|
||||
# Get voices for this module (limit to prevent huge menus)
|
||||
# Get voices for this module
|
||||
voices = getModuleVoices(module)
|
||||
if voices:
|
||||
# Limit voices to keep menu usable
|
||||
if len(voices) > 50:
|
||||
voices = voices[:50]
|
||||
moduleMenu['Note: Showing first 50 voices Action'] = createInfoCommand(f"Module {module} has {len(getModuleVoices(module))} voices, showing first 50", env)
|
||||
|
||||
# Add voice commands
|
||||
for voice in voices:
|
||||
|
@ -192,3 +192,12 @@ class outputManager():
|
||||
self.presentText(' review cursor ', interrupt=interrupt_p)
|
||||
else:
|
||||
self.presentText(' text cursor ', interrupt=interrupt_p)
|
||||
|
||||
def resetSpeechDriver(self):
|
||||
"""Reset speech driver to clean state - called by settingsManager"""
|
||||
if 'speechDriver' in self.env['runtime'] and self.env['runtime']['speechDriver']:
|
||||
try:
|
||||
self.env['runtime']['speechDriver'].reset()
|
||||
self.env['runtime']['debug'].writeDebugOut("Speech driver reset successfully", debug.debugLevel.INFO)
|
||||
except Exception as e:
|
||||
self.env['runtime']['debug'].writeDebugOut(f"resetSpeechDriver error: {e}", debug.debugLevel.ERROR)
|
||||
|
Reference in New Issue
Block a user