diff --git a/android/src/screens/SettingsScreen.tsx b/android/src/screens/SettingsScreen.tsx index b924aa3..f24d816 100644 --- a/android/src/screens/SettingsScreen.tsx +++ b/android/src/screens/SettingsScreen.tsx @@ -74,7 +74,8 @@ const SettingsScreen: React.FC = () => { const [ttsEnabled, setTtsEnabled] = useState(true); const [defaultVoice, setDefaultVoice] = useState('ramona'); const [highlightVoice, setHighlightVoice] = useState('thorsten'); - const [speechSpeed, setSpeechSpeed] = useState(1.0); + const [speedRamona, setSpeedRamona] = useState(1.0); + const [speedThorsten, setSpeedThorsten] = useState(1.0); const [editingPath, setEditingPath] = useState(false); const [tempPath, setTempPath] = useState(''); @@ -104,8 +105,11 @@ const SettingsScreen: React.FC = () => { AsyncStorage.getItem('aria_highlight_voice').then(saved => { if (saved) setHighlightVoice(saved); }); - AsyncStorage.getItem('aria_speech_speed').then(saved => { - if (saved) setSpeechSpeed(parseFloat(saved)); + AsyncStorage.getItem('aria_speed_ramona').then(saved => { + if (saved) setSpeedRamona(parseFloat(saved)); + }); + AsyncStorage.getItem('aria_speed_thorsten').then(saved => { + if (saved) setSpeedThorsten(parseFloat(saved)); }); }, []); @@ -525,41 +529,49 @@ const SettingsScreen: React.FC = () => { - {/* Sprechgeschwindigkeit */} + {/* Sprechgeschwindigkeit Ramona */} - Sprechgeschwindigkeit: {speechSpeed.toFixed(1)}x - - 0.5x - - { - const layout = e.nativeEvent; - // Einfacher Tap-basierter Slider - }} - > - - - - - - 2.0x - + Ramona Speed: {speedRamona.toFixed(1)}x {[0.5, 0.75, 1.0, 1.25, 1.5, 2.0].map(speed => ( { - setSpeechSpeed(speed); - AsyncStorage.setItem('aria_speech_speed', String(speed)); - rvs.send('config' as any, { speechSpeed: speed }); + setSpeedRamona(speed); + AsyncStorage.setItem('aria_speed_ramona', String(speed)); + rvs.send('config' as any, { speedRamona: speed }); }} style={{ paddingHorizontal: 10, paddingVertical: 6, borderRadius: 6, - backgroundColor: speechSpeed === speed ? '#0096FF' : '#1E1E2E', + backgroundColor: speedRamona === speed ? '#0096FF' : '#1E1E2E', }} > - + + {speed}x + + + ))} + + + + {/* Sprechgeschwindigkeit Thorsten */} + + Thorsten Speed: {speedThorsten.toFixed(1)}x + + {[0.5, 0.75, 1.0, 1.25, 1.5, 2.0].map(speed => ( + { + setSpeedThorsten(speed); + AsyncStorage.setItem('aria_speed_thorsten', String(speed)); + rvs.send('config' as any, { speedThorsten: speed }); + }} + style={{ + paddingHorizontal: 10, paddingVertical: 6, borderRadius: 6, + backgroundColor: speedThorsten === speed ? '#0096FF' : '#1E1E2E', + }} + > + {speed}x diff --git a/bridge/aria_bridge.py b/bridge/aria_bridge.py index 62b17de..e16ee80 100644 --- a/bridge/aria_bridge.py +++ b/bridge/aria_bridge.py @@ -132,7 +132,7 @@ class VoiceEngine: self.voices: dict[str, PiperVoice] = {} self.default_voice = "ramona" self.highlight_voice = "thorsten" - self.speech_speed = 1.0 # 0.5 = langsam, 1.0 = normal, 2.0 = schnell + self.speech_speed = {"ramona": 1.0, "thorsten": 1.0} def initialize(self) -> None: """Laedt die Piper-Stimmen aus dem Voices-Verzeichnis.""" @@ -218,7 +218,8 @@ class VoiceEngine: continue with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as tmp: tmp_path = tmp.name - syn_config = SynthesisConfig(length_scale=1.0 / max(0.3, self.speech_speed)) + speed = self.speech_speed.get(voice_name, 1.0) + syn_config = SynthesisConfig(length_scale=1.0 / max(0.3, speed)) with wave.open(tmp_path, "wb") as wav_file: voice.synthesize_wav(sentence, wav_file, syn_config=syn_config) with wave.open(tmp_path, "rb") as wav_file: @@ -497,7 +498,10 @@ class ARIABridge: vc = json.load(f) self.voice_engine.default_voice = vc.get("defaultVoice", "ramona") self.voice_engine.highlight_voice = vc.get("highlightVoice", "thorsten") - self.voice_engine.speech_speed = vc.get("speechSpeed", 1.0) + self.voice_engine.speech_speed = { + "ramona": vc.get("speedRamona", 1.0), + "thorsten": vc.get("speedThorsten", 1.0), + } self.tts_enabled = vc.get("ttsEnabled", True) logger.info("Voice-Config geladen: %s", vc) except Exception as e: @@ -1028,9 +1032,13 @@ class ARIABridge: self.tts_enabled = bool(payload["ttsEnabled"]) logger.info("[rvs] TTS %s", "aktiviert" if self.tts_enabled else "deaktiviert") changed = True - if "speechSpeed" in payload: - self.voice_engine.speech_speed = max(0.3, min(2.0, float(payload["speechSpeed"]))) - logger.info("[rvs] Sprechgeschwindigkeit: %.1f", self.voice_engine.speech_speed) + if "speedRamona" in payload: + self.voice_engine.speech_speed["ramona"] = max(0.3, min(2.0, float(payload["speedRamona"]))) + logger.info("[rvs] Speed Ramona: %.1f", self.voice_engine.speech_speed["ramona"]) + changed = True + if "speedThorsten" in payload: + self.voice_engine.speech_speed["thorsten"] = max(0.3, min(2.0, float(payload["speedThorsten"]))) + logger.info("[rvs] Speed Thorsten: %.1f", self.voice_engine.speech_speed["thorsten"]) changed = True # Persistent speichern in Shared Volume if changed: @@ -1040,7 +1048,8 @@ class ARIABridge: "defaultVoice": self.voice_engine.default_voice, "highlightVoice": self.voice_engine.highlight_voice, "ttsEnabled": getattr(self, "tts_enabled", True), - "speechSpeed": self.voice_engine.speech_speed, + "speedRamona": self.voice_engine.speech_speed.get("ramona", 1.0), + "speedThorsten": self.voice_engine.speech_speed.get("thorsten", 1.0), } with open("/shared/config/voice_config.json", "w") as f: json.dump(config_data, f, indent=2) diff --git a/diagnostic/index.html b/diagnostic/index.html index e97a1e0..e457763 100644 --- a/diagnostic/index.html +++ b/diagnostic/index.html @@ -419,12 +419,23 @@
- + +
+
+ 0.5x + + 2.0x +
+
+
0.5x - 2.0x @@ -657,9 +668,12 @@ document.getElementById('diag-default-voice').value = msg.defaultVoice || 'ramona'; document.getElementById('diag-highlight-voice').value = msg.highlightVoice || 'thorsten'; document.getElementById('diag-tts-enabled').checked = msg.ttsEnabled !== false; - const speed = msg.speechSpeed || 1.0; - document.getElementById('diag-speech-speed').value = speed; - document.getElementById('speed-label').textContent = speed + 'x'; + const sr = msg.speedRamona || 1.0; + const st = msg.speedThorsten || 1.0; + document.getElementById('diag-speed-ramona').value = sr; + document.getElementById('speed-ramona-label').textContent = sr + 'x'; + document.getElementById('diag-speed-thorsten').value = st; + document.getElementById('speed-thorsten-label').textContent = st + 'x'; return; } @@ -1157,8 +1171,9 @@ const defaultVoice = document.getElementById('diag-default-voice').value; const highlightVoice = document.getElementById('diag-highlight-voice').value; const ttsEnabled = document.getElementById('diag-tts-enabled').checked; - const speechSpeed = parseFloat(document.getElementById('diag-speech-speed').value); - send({ action: 'send_voice_config', defaultVoice, highlightVoice, ttsEnabled, speechSpeed }); + const speedRamona = parseFloat(document.getElementById('diag-speed-ramona').value); + const speedThorsten = parseFloat(document.getElementById('diag-speed-thorsten').value); + send({ action: 'send_voice_config', defaultVoice, highlightVoice, ttsEnabled, speedRamona, speedThorsten }); } // ── Highlight-Trigger ──────────────────────── diff --git a/diagnostic/server.js b/diagnostic/server.js index e970f93..9f18e47 100644 --- a/diagnostic/server.js +++ b/diagnostic/server.js @@ -1135,7 +1135,8 @@ wss.on("connection", (ws) => { defaultVoice: msg.defaultVoice || "ramona", highlightVoice: msg.highlightVoice || "thorsten", ttsEnabled: msg.ttsEnabled !== false, - speechSpeed: msg.speechSpeed || 1.0, + speedRamona: msg.speedRamona || 1.0, + speedThorsten: msg.speedThorsten || 1.0, }; try { fs.mkdirSync("/shared/config", { recursive: true });