8-gen bench (4 emotions × 2 corpora) at seed=42 against firdhokk
Whisper-LV3:
target RAVDESS CREMA-D
happy happy (0.999) ✓ happy (0.999) ✓
angry neutral (0.92) sad (0.99)
fearful happy (0.998) fearful (0.984) ✓
sad angry (0.99) fearful (0.99)
CREMA-D 2/4 vs RAVDESS 1/4. Larger / more naturalistic corpus
produces more class-pure fearful direction. Neither corpus solves
angry or sad — recipe shifts into 'vague expressivity' rather than
class-specific corners.
Practical: prefer CREMA-D when available; A/B both per emotion if
class precision matters.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Phase 6c.3c verification: extended converse_client with
--barge-in-after-ms flag that injects a 200 ms audio frame N ms after
the assistant starts speaking, then watches for the
{"event":"barge_in"} server response.
Verified end-to-end on Metal:
Input: 10.43s LibriSpeech FLAC
STT transcript: matched correctly
Mock LLM streamed 4-sentence response with 50ms/token delays
Client injected barge-in 100 ms into TTS streaming
Server log: "barge-in detected (4800 samples carried over)"
Client log: "[server] barge_in event received -- TTS cancelled"
WAV file: 1.60s of TTS captured before cutoff
Mock LLM upgraded to multi-sentence with tokio::time::sleep(50ms)
between chunks — exercises the streaming pipeline long enough for
barge-in tests to fire mid-response.
The full Rust Unmute conversational stack is now feature-verified:
voice-in, voice-out, interruptible, VAD-driven, authed, metrics-
instrumented. Strategic Phase 6 deliverable shipped end-to-end.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Production hardening pass on the Rust Unmute MVP:
6c.3a: streaming audio output. Replaced the "collect-then-send" loop
with a tokio mpsc channel + tokio::join! between conv.run and the
WS sender. Audio chunks are forwarded to the client AS each sentence
completes TTS, instead of waiting for the full assistant response.
Drop the channel sender at end of conv.run to signal the pump exit.
6d.auth: Bearer token auth. --auth-token flag (or RTX_AUTH_TOKEN env)
on the server requires an Authorization: Bearer <token> header on the
WebSocket upgrade. Rejected upgrades return 401. Server logs a warn
if no token is configured (open dev mode). converse_client gains a
matching --auth-token flag.
6d.shutdown: Graceful SIGINT/SIGTERM. tokio::signal handlers wired
into axum::serve.with_graceful_shutdown(). Verified: SIGINT log line
"received SIGINT, shutting down gracefully" + clean exit 0.
6d.metrics: /metrics Prometheus-style endpoint. Counters
(turns_total, errors_total, connections_total) + gauges
(connections_active, stt/tts/e2e_first_audio latency averages).
Verified end-to-end: rtx_csm_turns_total 1 / errors_total 3 (from
earlier 401 attempts) / e2e_first_audio_ms_avg 21295 / etc.
Verified all four together: 401 on bad/missing auth, 200 + WS upgrade
on correct auth, full round-trip metrics, clean SIGINT exit.
Phase 6 status:
6a STT: working
6b LLM client: working
6c.1 text->LLM->TTS: working
6c.2 WebSocket duplex MVP: working
6c.3a streaming TTS chunks: working (this commit)
6c.3b semantic VAD / barge-in: deferred
6d.{auth,shutdown,metrics}: shipped (this commit)
6d.rate-limit: deferred
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Full-stack pure-Rust voice conversation server + CLI client:
Client -> Server binary frames: 16-bit LE PCM @ 24 kHz mono
Client -> Server text "EOT": signal end-of-turn
Server: STT (Kyutai 1B en/fr) -> transcript
LLM (OpenAI-compatible OR mock echo) -> token stream
Converse: sentence buffer -> CSM TTS -> 16-bit LE PCM
Server -> Client text {"event":"transcript","text":"..."}
Server -> Client binary frames: assistant audio
Server -> Client text {"event":"done","assistant":"..."}
Per-connection chat history; multiple turns supported per socket.
--mock-llm mode for testing without API keys (echoes user transcript).
examples/converse_server.rs: axum WebSocket server.
examples/converse_client.rs: CLI; streams WAV in as user turn, saves
response audio out.
Verified end-to-end on Metal:
Input: 10.43s LibriSpeech FLAC ("He hoped there would be stew...")
STT transcript: matched (full sentence captured by 23/25 words)
Mock LLM: "I heard you say: <transcript>."
CSM TTS: response audio streamed back via WebSocket
Round-trip wall-clock: 6.86s (TTFA on first audio chunk: 6.86s; the
pipeline is sequential per turn — Phase 6c.3 would pipeline LLM
tokens with TTS to get TTFA much lower).
This is the Rust Unmute MVP: PCM in, voice out, no Python in the
runtime path. Strategic Phase 6 deliverable.
Phase 6 status:
6a STT: working
6b LLM client: working
6c.1 text->LLM->TTS: working
6c.2 WebSocket duplex MVP: working (this commit)
6c.3 streaming pipeline + auto-EOT + barge-in: deferred
6d productionization: deferred
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>