addes speed config for voice
This commit is contained in:
parent
a2c0196e05
commit
1ab8a6a2fe
|
|
@ -38,6 +38,7 @@ import websockets
|
||||||
from faster_whisper import WhisperModel
|
from faster_whisper import WhisperModel
|
||||||
from openwakeword.model import Model as WakeWordModel
|
from openwakeword.model import Model as WakeWordModel
|
||||||
from piper import PiperVoice
|
from piper import PiperVoice
|
||||||
|
from piper.config import SynthesisConfig
|
||||||
|
|
||||||
from modes import Mode, detect_mode_switch, should_speak
|
from modes import Mode, detect_mode_switch, should_speak
|
||||||
|
|
||||||
|
|
@ -131,6 +132,7 @@ class VoiceEngine:
|
||||||
self.voices: dict[str, PiperVoice] = {}
|
self.voices: dict[str, PiperVoice] = {}
|
||||||
self.default_voice = "ramona"
|
self.default_voice = "ramona"
|
||||||
self.highlight_voice = "thorsten"
|
self.highlight_voice = "thorsten"
|
||||||
|
self.speech_speed = 1.0 # 0.5 = langsam, 1.0 = normal, 2.0 = schnell
|
||||||
|
|
||||||
def initialize(self) -> None:
|
def initialize(self) -> None:
|
||||||
"""Laedt die Piper-Stimmen aus dem Voices-Verzeichnis."""
|
"""Laedt die Piper-Stimmen aus dem Voices-Verzeichnis."""
|
||||||
|
|
@ -216,8 +218,9 @@ class VoiceEngine:
|
||||||
continue
|
continue
|
||||||
with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as tmp:
|
with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as tmp:
|
||||||
tmp_path = tmp.name
|
tmp_path = tmp.name
|
||||||
|
syn_config = SynthesisConfig(length_scale=1.0 / max(0.3, self.speech_speed))
|
||||||
with wave.open(tmp_path, "wb") as wav_file:
|
with wave.open(tmp_path, "wb") as wav_file:
|
||||||
voice.synthesize_wav(sentence, wav_file)
|
voice.synthesize_wav(sentence, wav_file, syn_config=syn_config)
|
||||||
with wave.open(tmp_path, "rb") as wav_file:
|
with wave.open(tmp_path, "rb") as wav_file:
|
||||||
if sample_rate is None:
|
if sample_rate is None:
|
||||||
sample_rate = wav_file.getframerate()
|
sample_rate = wav_file.getframerate()
|
||||||
|
|
@ -494,6 +497,7 @@ class ARIABridge:
|
||||||
vc = json.load(f)
|
vc = json.load(f)
|
||||||
self.voice_engine.default_voice = vc.get("defaultVoice", "ramona")
|
self.voice_engine.default_voice = vc.get("defaultVoice", "ramona")
|
||||||
self.voice_engine.highlight_voice = vc.get("highlightVoice", "thorsten")
|
self.voice_engine.highlight_voice = vc.get("highlightVoice", "thorsten")
|
||||||
|
self.voice_engine.speech_speed = vc.get("speechSpeed", 1.0)
|
||||||
self.tts_enabled = vc.get("ttsEnabled", True)
|
self.tts_enabled = vc.get("ttsEnabled", True)
|
||||||
logger.info("Voice-Config geladen: %s", vc)
|
logger.info("Voice-Config geladen: %s", vc)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
@ -1024,6 +1028,10 @@ class ARIABridge:
|
||||||
self.tts_enabled = bool(payload["ttsEnabled"])
|
self.tts_enabled = bool(payload["ttsEnabled"])
|
||||||
logger.info("[rvs] TTS %s", "aktiviert" if self.tts_enabled else "deaktiviert")
|
logger.info("[rvs] TTS %s", "aktiviert" if self.tts_enabled else "deaktiviert")
|
||||||
changed = True
|
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)
|
||||||
|
changed = True
|
||||||
# Persistent speichern in Shared Volume
|
# Persistent speichern in Shared Volume
|
||||||
if changed:
|
if changed:
|
||||||
try:
|
try:
|
||||||
|
|
@ -1032,6 +1040,7 @@ class ARIABridge:
|
||||||
"defaultVoice": self.voice_engine.default_voice,
|
"defaultVoice": self.voice_engine.default_voice,
|
||||||
"highlightVoice": self.voice_engine.highlight_voice,
|
"highlightVoice": self.voice_engine.highlight_voice,
|
||||||
"ttsEnabled": getattr(self, "tts_enabled", True),
|
"ttsEnabled": getattr(self, "tts_enabled", True),
|
||||||
|
"speechSpeed": self.voice_engine.speech_speed,
|
||||||
}
|
}
|
||||||
with open("/shared/config/voice_config.json", "w") as f:
|
with open("/shared/config/voice_config.json", "w") as f:
|
||||||
json.dump(config_data, f, indent=2)
|
json.dump(config_data, f, indent=2)
|
||||||
|
|
|
||||||
|
|
@ -414,10 +414,21 @@
|
||||||
<option value="ramona">Ramona (weiblich)</option>
|
<option value="ramona">Ramona (weiblich)</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div style="display:flex;align-items:center;gap:12px;">
|
<div style="display:flex;align-items:center;gap:12px;margin-bottom:12px;">
|
||||||
<label style="color:#8888AA;font-size:12px;">TTS aktiv:</label>
|
<label style="color:#8888AA;font-size:12px;">TTS aktiv:</label>
|
||||||
<label class="toggle"><input type="checkbox" id="diag-tts-enabled" checked onchange="sendVoiceConfig()"><span class="slider"></span></label>
|
<label class="toggle"><input type="checkbox" id="diag-tts-enabled" checked onchange="sendVoiceConfig()"><span class="slider"></span></label>
|
||||||
</div>
|
</div>
|
||||||
|
<div style="margin-bottom:4px;">
|
||||||
|
<label style="color:#8888AA;font-size:12px;">Sprechgeschwindigkeit: <span id="speed-label">1.0x</span></label>
|
||||||
|
</div>
|
||||||
|
<div style="display:flex;align-items:center;gap:8px;">
|
||||||
|
<span style="color:#555570;font-size:11px;">0.5x</span>
|
||||||
|
<input type="range" id="diag-speech-speed" min="0.5" max="2.0" step="0.1" value="1.0"
|
||||||
|
oninput="document.getElementById('speed-label').textContent=this.value+'x'"
|
||||||
|
onchange="sendVoiceConfig()"
|
||||||
|
style="flex:1;accent-color:#0096FF;">
|
||||||
|
<span style="color:#555570;font-size:11px;">2.0x</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -646,6 +657,9 @@
|
||||||
document.getElementById('diag-default-voice').value = msg.defaultVoice || 'ramona';
|
document.getElementById('diag-default-voice').value = msg.defaultVoice || 'ramona';
|
||||||
document.getElementById('diag-highlight-voice').value = msg.highlightVoice || 'thorsten';
|
document.getElementById('diag-highlight-voice').value = msg.highlightVoice || 'thorsten';
|
||||||
document.getElementById('diag-tts-enabled').checked = msg.ttsEnabled !== false;
|
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';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1143,7 +1157,8 @@
|
||||||
const defaultVoice = document.getElementById('diag-default-voice').value;
|
const defaultVoice = document.getElementById('diag-default-voice').value;
|
||||||
const highlightVoice = document.getElementById('diag-highlight-voice').value;
|
const highlightVoice = document.getElementById('diag-highlight-voice').value;
|
||||||
const ttsEnabled = document.getElementById('diag-tts-enabled').checked;
|
const ttsEnabled = document.getElementById('diag-tts-enabled').checked;
|
||||||
send({ action: 'send_voice_config', defaultVoice, highlightVoice, ttsEnabled });
|
const speechSpeed = parseFloat(document.getElementById('diag-speech-speed').value);
|
||||||
|
send({ action: 'send_voice_config', defaultVoice, highlightVoice, ttsEnabled, speechSpeed });
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Highlight-Trigger ────────────────────────
|
// ── Highlight-Trigger ────────────────────────
|
||||||
|
|
|
||||||
|
|
@ -1135,6 +1135,7 @@ wss.on("connection", (ws) => {
|
||||||
defaultVoice: msg.defaultVoice || "ramona",
|
defaultVoice: msg.defaultVoice || "ramona",
|
||||||
highlightVoice: msg.highlightVoice || "thorsten",
|
highlightVoice: msg.highlightVoice || "thorsten",
|
||||||
ttsEnabled: msg.ttsEnabled !== false,
|
ttsEnabled: msg.ttsEnabled !== false,
|
||||||
|
speechSpeed: msg.speechSpeed || 1.0,
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
fs.mkdirSync("/shared/config", { recursive: true });
|
fs.mkdirSync("/shared/config", { recursive: true });
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue