Merge PR #79: feat(tts): StepFun voice selection via CharacterDesigner + provider-aware beat-audio
- StepFun voice selection: CharacterDesigner picks a preset voiceId from the 32-entry catalog (zero extra LLM call); pickStepfunVoiceId remains as fallback. - Prebaked homepage cards enriched with stepfunVoiceId (147 characters, gemini model). - /api/tts-provider endpoint + client probe: skip the ~220KB Xiaomi reference audio when the server runs StepFun (saves Fast Origin Transfer bandwidth). - Server-side resolveVoice normalization: re-provisions on provider mismatch. - Removed hardcoded 1.2x speech playback speed (was for slow MiMo voice). - Hardened voice-provider validation per PR-agent review. Xiaomi path prompt is byte-identical to history (prompt-cache-preserving).
This commit is contained in:
@@ -19,9 +19,6 @@ const SHADOW =
|
||||
|
||||
const DEFAULT_CHAR_MS = 28;
|
||||
const MIN_CHAR_MS = 30;
|
||||
// Voice playback speed multiplier. >1 speeds up the (somewhat slow) MiMo voice
|
||||
// while preserving pitch. Typewriter pacing is divided by the same factor.
|
||||
const SPEECH_RATE = 1.2;
|
||||
// If audio metadata never arrives within this window, give up waiting and
|
||||
// let the typewriter run at default speed.
|
||||
const AUDIO_WAIT_TIMEOUT_MS = 2500;
|
||||
@@ -261,7 +258,6 @@ export function PlayCanvas({
|
||||
const el = audioRef.current;
|
||||
if (!el) return;
|
||||
el.muted = muted;
|
||||
el.playbackRate = SPEECH_RATE;
|
||||
if (!muted && audioSrc && el.paused) {
|
||||
el.play().catch(() => {
|
||||
// autoplay blocked — silent until next interaction
|
||||
@@ -272,11 +268,7 @@ export function PlayCanvas({
|
||||
function handleAudioMetadata() {
|
||||
const el = audioRef.current;
|
||||
if (!el) return;
|
||||
el.playbackRate = SPEECH_RATE;
|
||||
// Effective playback time is shorter once sped up — keep the typewriter in sync.
|
||||
const ms = Number.isFinite(el.duration)
|
||||
? (el.duration * 1000) / SPEECH_RATE
|
||||
: 0;
|
||||
const ms = Number.isFinite(el.duration) ? el.duration * 1000 : 0;
|
||||
setAudioDurationMs(ms > 0 ? ms : 0);
|
||||
if (!muted) {
|
||||
el.play().catch(() => {
|
||||
|
||||
Reference in New Issue
Block a user