feat(diagnostic): Speed-Slider im Voice-Preview-Modal (nicht persistiert)
Neue −0.1 / +0.1 Buttons im Preview-Modal mit aktuellem Wert-Label. Bei jedem Oeffnen wird der Speed auf 1.0 zurueckgesetzt (bewusst kein persist — nur zum Experimentieren waehrend das Modal offen ist). - Range 0.1-5.0, gleiche wie in App-Settings - Wird beim Play an f5tts-bridge als speed-Param mitgegeben - Server clampt auf 0.1-5.0, Fallback 1.0 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
b80b813703
commit
feac7f2479
|
|
@ -145,6 +145,15 @@
|
||||||
</div>
|
</div>
|
||||||
<textarea id="voice-preview-text" rows="4"
|
<textarea id="voice-preview-text" rows="4"
|
||||||
style="background:#0D0D1A;border:1px solid #2A2A3E;border-radius:6px;padding:10px;color:#fff;font-size:13px;resize:vertical;"></textarea>
|
style="background:#0D0D1A;border:1px solid #2A2A3E;border-radius:6px;padding:10px;color:#fff;font-size:13px;resize:vertical;"></textarea>
|
||||||
|
|
||||||
|
<div style="display:flex;align-items:center;gap:10px;font-size:12px;color:#8888AA;">
|
||||||
|
<span style="min-width:120px;">Geschwindigkeit:</span>
|
||||||
|
<button onclick="adjustPreviewSpeed(-0.1)" class="btn secondary" style="padding:4px 10px;font-size:12px;">−0.1</button>
|
||||||
|
<span id="voice-preview-speed-value" style="min-width:52px;text-align:center;color:#fff;font-weight:600;">1.0 x</span>
|
||||||
|
<button onclick="adjustPreviewSpeed(0.1)" class="btn secondary" style="padding:4px 10px;font-size:12px;">+0.1</button>
|
||||||
|
<span style="color:#555570;font-size:11px;">(nur fuer dieses Modal, wird nicht gespeichert)</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div style="display:flex;gap:8px;align-items:center;">
|
<div style="display:flex;gap:8px;align-items:center;">
|
||||||
<button id="voice-preview-play" onclick="playVoicePreview()" class="btn primary" style="padding:8px 16px;">
|
<button id="voice-preview-play" onclick="playVoicePreview()" class="btn primary" style="padding:8px 16px;">
|
||||||
▶ Abspielen
|
▶ Abspielen
|
||||||
|
|
@ -1630,10 +1639,29 @@
|
||||||
|
|
||||||
// ── Voice Preview Modal ─────────────────────────
|
// ── Voice Preview Modal ─────────────────────────
|
||||||
const VOICE_PREVIEW_DEFAULT = 'Hallo, ich bin ARIA. Das hier ist ein kleiner Test damit du meine Stimme beurteilen kannst.';
|
const VOICE_PREVIEW_DEFAULT = 'Hallo, ich bin ARIA. Das hier ist ein kleiner Test damit du meine Stimme beurteilen kannst.';
|
||||||
|
const PREVIEW_SPEED_DEFAULT = 1.0;
|
||||||
|
const PREVIEW_SPEED_MIN = 0.1;
|
||||||
|
const PREVIEW_SPEED_MAX = 5.0;
|
||||||
let currentPreviewVoice = '';
|
let currentPreviewVoice = '';
|
||||||
|
let currentPreviewSpeed = PREVIEW_SPEED_DEFAULT;
|
||||||
|
|
||||||
|
function _refreshPreviewSpeedLabel() {
|
||||||
|
const el = document.getElementById('voice-preview-speed-value');
|
||||||
|
if (el) el.textContent = currentPreviewSpeed.toFixed(1) + ' x';
|
||||||
|
}
|
||||||
|
|
||||||
|
function adjustPreviewSpeed(delta) {
|
||||||
|
const next = Math.round((currentPreviewSpeed + delta) * 10) / 10;
|
||||||
|
if (next < PREVIEW_SPEED_MIN || next > PREVIEW_SPEED_MAX) return;
|
||||||
|
currentPreviewSpeed = next;
|
||||||
|
_refreshPreviewSpeedLabel();
|
||||||
|
}
|
||||||
|
|
||||||
function openVoicePreview(name) {
|
function openVoicePreview(name) {
|
||||||
currentPreviewVoice = name;
|
currentPreviewVoice = name;
|
||||||
|
// Speed bei jedem Oeffnen zuruecksetzen — bewusst kein persist
|
||||||
|
currentPreviewSpeed = PREVIEW_SPEED_DEFAULT;
|
||||||
|
_refreshPreviewSpeedLabel();
|
||||||
document.getElementById('voice-preview-name').textContent = name;
|
document.getElementById('voice-preview-name').textContent = name;
|
||||||
// Text bei jedem Oeffnen zuruecksetzen
|
// Text bei jedem Oeffnen zuruecksetzen
|
||||||
document.getElementById('voice-preview-text').value = VOICE_PREVIEW_DEFAULT;
|
document.getElementById('voice-preview-text').value = VOICE_PREVIEW_DEFAULT;
|
||||||
|
|
@ -1658,7 +1686,12 @@
|
||||||
}
|
}
|
||||||
document.getElementById('voice-preview-status').textContent = '⏳ Rendere...';
|
document.getElementById('voice-preview-status').textContent = '⏳ Rendere...';
|
||||||
document.getElementById('voice-preview-play').disabled = true;
|
document.getElementById('voice-preview-play').disabled = true;
|
||||||
send({ action: 'preview_voice', voice: currentPreviewVoice, text });
|
send({
|
||||||
|
action: 'preview_voice',
|
||||||
|
voice: currentPreviewVoice,
|
||||||
|
text,
|
||||||
|
speed: currentPreviewSpeed,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteXttsVoice(name) {
|
function deleteXttsVoice(name) {
|
||||||
|
|
|
||||||
|
|
@ -1469,7 +1469,7 @@ wss.on("connection", (ws) => {
|
||||||
} else if (msg.action === "test_tts") {
|
} else if (msg.action === "test_tts") {
|
||||||
handleTestTTS(ws, msg.text || "Test");
|
handleTestTTS(ws, msg.text || "Test");
|
||||||
} else if (msg.action === "preview_voice") {
|
} else if (msg.action === "preview_voice") {
|
||||||
handleVoicePreview(ws, msg.voice || "", msg.text || "Hallo.");
|
handleVoicePreview(ws, msg.voice || "", msg.text || "Hallo.", msg.speed);
|
||||||
} else if (msg.action === "check_tts") {
|
} else if (msg.action === "check_tts") {
|
||||||
handleCheckTTS(ws);
|
handleCheckTTS(ws);
|
||||||
} else if (msg.action === "check_desktop") {
|
} else if (msg.action === "check_desktop") {
|
||||||
|
|
@ -1704,8 +1704,11 @@ function _handlePreviewChunk(payload) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleVoicePreview(clientWs, voice, text) {
|
async function handleVoicePreview(clientWs, voice, text, speed) {
|
||||||
try {
|
try {
|
||||||
|
// Speed clampen — Browser-Slider ist 0.1-5.0
|
||||||
|
let spd = parseFloat(speed);
|
||||||
|
if (!isFinite(spd) || spd < 0.1 || spd > 5.0) spd = 1.0;
|
||||||
const requestId = crypto.randomUUID();
|
const requestId = crypto.randomUUID();
|
||||||
_previewPending.set(requestId, { clientWs, chunks: [], sampleRate: 0, channels: 0 });
|
_previewPending.set(requestId, { clientWs, chunks: [], sampleRate: 0, channels: 0 });
|
||||||
// Timeout safety net
|
// Timeout safety net
|
||||||
|
|
@ -1720,10 +1723,10 @@ async function handleVoicePreview(clientWs, voice, text) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, 60000);
|
}, 60000);
|
||||||
log("info", "server", `Voice-Preview: voice="${voice}" text="${text.slice(0, 60)}"`);
|
log("info", "server", `Voice-Preview: voice="${voice}" speed=${spd.toFixed(1)}x text="${text.slice(0, 60)}"`);
|
||||||
sendToRVS_raw({
|
sendToRVS_raw({
|
||||||
type: "xtts_request",
|
type: "xtts_request",
|
||||||
payload: { text, language: "de", requestId, voice, speed: 1.0 },
|
payload: { text, language: "de", requestId, voice, speed: spd },
|
||||||
timestamp: Date.now(),
|
timestamp: Date.now(),
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue