Initial pass at getting all the scripts and menus working with the new everything in home setup. Also unify speech settings. Not sure why I thought separate speech settings for all menus was a good idea lol.
This commit is contained in:
@@ -3,51 +3,28 @@
|
||||
|
||||
# Self-voiced Speech Rate Configuration Menu
|
||||
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import curses
|
||||
import speechd # Python bindings for Speech Dispatcher
|
||||
import re
|
||||
import subprocess
|
||||
from stormux_speech_settings import (
|
||||
SPEECHD_CONFIG,
|
||||
FEX_SPEECHD_CONFIG,
|
||||
SpeechSettings,
|
||||
load_speech_settings,
|
||||
save_speech_settings,
|
||||
update_speechd_config_content as update_shared_speechd_config_content,
|
||||
write_speechd_config,
|
||||
)
|
||||
|
||||
|
||||
def update_speechd_config_content(content, rate, volume, pitch):
|
||||
"""Return speechd.conf content with updated default voice parameters."""
|
||||
newContent = content
|
||||
|
||||
replacements = (
|
||||
("DefaultRate", rate),
|
||||
("DefaultVolume", volume),
|
||||
("DefaultPitch", pitch),
|
||||
return update_shared_speechd_config_content(
|
||||
content,
|
||||
SpeechSettings(rate=rate, volume=volume, pitch=pitch),
|
||||
)
|
||||
|
||||
for settingName, settingValue in replacements:
|
||||
activePattern = rf'^(\s*){settingName}\s+(-?\d+)'
|
||||
commentedPattern = rf'^(\s*)#\s*{settingName}\s+(-?\d+)'
|
||||
replacement = rf'\1{settingName} {settingValue}'
|
||||
|
||||
if re.search(activePattern, newContent, re.MULTILINE):
|
||||
newContent = re.sub(
|
||||
activePattern,
|
||||
replacement,
|
||||
newContent,
|
||||
flags=re.MULTILINE,
|
||||
)
|
||||
else:
|
||||
newContent = re.sub(
|
||||
commentedPattern,
|
||||
replacement,
|
||||
newContent,
|
||||
flags=re.MULTILINE,
|
||||
)
|
||||
|
||||
return newContent
|
||||
|
||||
|
||||
def get_writable_config_targets(configTargets):
|
||||
"""Return existing config paths to update, preserving order."""
|
||||
return [configPath for configPath in configTargets if os.path.exists(configPath)]
|
||||
|
||||
|
||||
class SpeechRateMenu:
|
||||
def __init__(self, title="Speech Configuration"):
|
||||
@@ -59,11 +36,8 @@ class SpeechRateMenu:
|
||||
self.modes = ["Rate", "Volume", "Pitch"]
|
||||
self.stdscr = None
|
||||
self.cursesInitialized = False # Flag to track if curses has been initialized
|
||||
self.configFile = "/etc/speech-dispatcher/speechd.conf"
|
||||
self.configTargets = [
|
||||
self.configFile,
|
||||
os.path.expanduser("~/.fex-emu/RootFS/ArchLinux/etc/speech-dispatcher/speechd.conf"),
|
||||
]
|
||||
self.configFile = str(SPEECHD_CONFIG)
|
||||
self.configTargets = [SPEECHD_CONFIG, FEX_SPEECHD_CONFIG]
|
||||
|
||||
# Load current settings from config FIRST
|
||||
self.load_current_settings()
|
||||
@@ -90,35 +64,10 @@ class SpeechRateMenu:
|
||||
def load_current_settings(self):
|
||||
"""Load the current default settings from speechd.conf"""
|
||||
try:
|
||||
with open(self.configFile, 'r') as f:
|
||||
content = f.read()
|
||||
|
||||
# Load Rate
|
||||
activeMatch = re.search(r'^\s*DefaultRate\s+(-?\d+)', content, re.MULTILINE)
|
||||
if activeMatch:
|
||||
self.currentRate = int(activeMatch.group(1))
|
||||
else:
|
||||
commentedMatch = re.search(r'^\s*#\s*DefaultRate\s+(-?\d+)', content, re.MULTILINE)
|
||||
if commentedMatch:
|
||||
self.currentRate = int(commentedMatch.group(1))
|
||||
|
||||
# Load Volume
|
||||
activeMatch = re.search(r'^\s*DefaultVolume\s+(-?\d+)', content, re.MULTILINE)
|
||||
if activeMatch:
|
||||
self.currentVolume = int(activeMatch.group(1))
|
||||
else:
|
||||
commentedMatch = re.search(r'^\s*#\s*DefaultVolume\s+(-?\d+)', content, re.MULTILINE)
|
||||
if commentedMatch:
|
||||
self.currentVolume = int(commentedMatch.group(1))
|
||||
|
||||
# Load Pitch
|
||||
activeMatch = re.search(r'^\s*DefaultPitch\s+(-?\d+)', content, re.MULTILINE)
|
||||
if activeMatch:
|
||||
self.currentPitch = int(activeMatch.group(1))
|
||||
else:
|
||||
commentedMatch = re.search(r'^\s*#\s*DefaultPitch\s+(-?\d+)', content, re.MULTILINE)
|
||||
if commentedMatch:
|
||||
self.currentPitch = int(commentedMatch.group(1))
|
||||
settings = load_speech_settings(self.configFile)
|
||||
self.currentRate = settings.rate
|
||||
self.currentVolume = settings.volume
|
||||
self.currentPitch = settings.pitch
|
||||
|
||||
except Exception:
|
||||
# If loading fails, we'll use default values
|
||||
@@ -127,26 +76,14 @@ class SpeechRateMenu:
|
||||
def save_settings_to_config(self):
|
||||
"""Save the current settings to the speech-dispatcher config file"""
|
||||
try:
|
||||
for configPath in get_writable_config_targets(self.configTargets):
|
||||
with open(configPath, 'r') as f:
|
||||
content = f.read()
|
||||
|
||||
newContent = update_speechd_config_content(
|
||||
content,
|
||||
self.currentRate,
|
||||
self.currentVolume,
|
||||
self.currentPitch,
|
||||
)
|
||||
|
||||
tempFile = f"/tmp/{os.path.basename(configPath)}.new"
|
||||
with open(tempFile, 'w') as f:
|
||||
f.write(newContent)
|
||||
|
||||
subprocess.run(
|
||||
["sudo", "mv", tempFile, configPath],
|
||||
check=True,
|
||||
)
|
||||
|
||||
save_speech_settings(
|
||||
SpeechSettings(
|
||||
rate=self.currentRate,
|
||||
volume=self.currentVolume,
|
||||
pitch=self.currentPitch,
|
||||
),
|
||||
self.configTargets,
|
||||
)
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
@@ -230,7 +167,7 @@ class SpeechRateMenu:
|
||||
def confirm_saved_settings(self):
|
||||
"""Restart speechd, announce success, and wait for confirmation."""
|
||||
subprocess.run(
|
||||
["sudo", "killall", "speech-dispatcher"],
|
||||
["killall", "speech-dispatcher"],
|
||||
check=False,
|
||||
)
|
||||
time.sleep(1)
|
||||
|
||||
Reference in New Issue
Block a user