Commit Graph
31 Commits
Author SHA1 Message Date
osobhandClaude Opus 4.7 a5cedfb46a rtx-csm: emotional_speech_guide — CREMA-D vs RAVDESS firdhokk verdict
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]>
2026-04-30 00:01:02 -07:00
osobhandClaude Opus 4.7 8045ba79d4 rtx-csm: emotional_speech_guide — firdhokk classifier reveals emotion gap
Wired up firdhokk/speech-emotion-recognition-with-openai-whisper-large-v3
as a working alternative to the broken emotion2vec_plus_base. Sanity
verified on real RAVDESS clips: 3/5 correct, 2/5 near-miss (happy↔
surprised, sad↔fearful). Probabilities are NOT saturated — the
classifier actually distinguishes per-input.

Then scored our 4 decoder-route outputs (Amini context, seed=42,
recipe defaults) and found that **only fearful registers as the
intended class**:

  target    verdict       conf
  happy     neutral       0.80   ✗ (steering produces neutral output)
  angry     happy         0.999  ✗ (high-arousal cross-class)
  fearful   fearful       0.68   ✓
  sad       fearful       0.998  ✗ (sad↔fearful confusion)

Honest framing: the recipe shifts speaker character toward an
expressive-sounding direction (cosine evidence) and preserves text
(decoder vs backbone) but does NOT produce class-distinct emotion.
The metric stack we used through Phase 9 (cosine + WER) couldn't
see this gap because it measures voice fidelity and text rendering,
not emotion class.

Hypothesized fixes (not yet tested):
- CREMA-D extraction (91 actors vs RAVDESS 24) for class-purer
  steering vectors
- Mixed backbone+decoder steering (backbone for prosody)
- EmoNet classifier (TTS-aware, may give different verdicts)

Doc'd in emotional_speech_guide.md as a known limitation. Closes
out an honest scientific picture: today's work successfully ports
the architectural finding (decoder route preserves text), but
class-precise emotion control remains unsolved.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
2026-04-29 22:13:54 -07:00
osobhandClaude Opus 4.7 b9c1e3b300 rtx-csm: quality_eval — emotion2vec metric (with broken-classifier caveat)
Wires emotion2vec into quality_eval so per-row metrics include
target_emotion_prob, top_emotion, top_emotion_prob. Pure inference,
optional via --emotion2vec / --target-emotion flags.

Critical empirical finding documented in code + user guide: the
emotion2vec_plus_base checkpoint classifies every input as
"Surprised" with prob ≥ 0.99, INCLUDING ground-truth RAVDESS clips
with explicit emotion labels. Real angry-RAVDESS → "Surprised"
(0.9999999). Real neutral-RAVDESS → "Surprised" (0.9999996).

The metric implementation is correct (matches the trait's
EmotionDetector::classify code path with same per-utterance zero-
mean unit-variance normalization); the underlying classifier
collapses to a dominant class on most input — likely the same
"9→5 fold collapse" the project already documented in the data-
labeling path.

Practical implication: target_emotion_prob is near-zero for almost
every (target, output) that isn't "surprised", so it can't be used
as a picker score. The emotion2vec metric still works as a
diagnostic ("did the model produce something that classifies as
audio at all?") but not as a generation-quality validator.

Doc'd in:
- examples/quality_eval.rs CLI doc (caveat block on --emotion2vec)
- docs/emotional_speech_guide.md (Known limitations section with
  full sanity-check table)

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
2026-04-29 21:43:42 -07:00
osobhandClaude Opus 4.7 cc4c84f2cd rtx-csm: emotional_speech_guide — document composite picker score
Brief addition to the recipe section explaining the WER + length
floor scoring used by emotional_speech_n.sh (committed in b7b267b).
Validates that the new scoring preserves canonical winners on
happy and calm while flipping surprised to the long-and-correct
candidate.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
2026-04-29 21:37:23 -07:00
osobhandClaude Opus 4.7 8190b26e93 rtx-csm: emotional_speech_guide — N-seed bench for new 3 emotions
3-seed picker run (7, 42, 100) on calm/disgust/surprised refines the
single-shot characterization:

  calm:      winner seed=100, WER 0.64
             "It's a good reflection. Not that that. I want to
              share somet..."
             (recipe lands the prompt — single-shot at seed 42 only
              produced hesitation markers; the picker found a seed
              with actual content)

  disgust:   no reliable seed
             (all 3 seeds WER ≥ 0.93; likely RAVDESS corpus issue —
              disgust clips are low-energy / acoustically close to
              neutral. Try CREMA-D or ESD for this emotion.)

  surprised: picker chose seed=7 (WER 0.93, short "You can.") over
             seed=100 (WER 1.0, "...Today I want to share something")
             — WER weighting issue: deletions and insertions count
             uniformly, so terse-but-mostly-wrong beat long-and-
             mostly-right. Manual selection or weighting WER less
             heavily would help here.

Updated per-emotion table marks disgust as ✗ (corpus limitation),
surprised as ⚠ (picker scoring artifact), calm as ✓ (works with
N-seed picker).

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
2026-04-29 19:45:15 -07:00
osobhandClaude Opus 4.7 e6f8d77328 rtx-csm: 7-emotion RAVDESS pack — calm/disgust/surprised benched
Extracted decoder steering vectors for the remaining 3 RAVDESS
emotions (calm, disgust, surprised). Single-seed bench at
seed=42 on Amini context, decoder route, recipe defaults:

  emotion     cos_ctx  WER    transcript
  calm        0.70     0.86   "I'm sorry. Um, I don't know."
                              (natural hesitation markers — the
                              recipe produces semantically-emotion-
                              matched content, not just acoustic
                              shift)
  disgust     0.81     0.93   "For that, that..." (truncated)
  surprised   0.95     2.57   "Too couple, sorry, and that's saying,
                              even a premier and super driver..."
                              (long rambling; voice migrates well,
                              text drifts)

All 7 RAVDESS emotions now produce coherent English on the decoder
route — calm is solid first-shot, disgust truncates, surprised
rambles. Roll N seeds via emotional_speech_n.sh for the latter two.

emotional_speech_guide.md updated with the per-emotion table now
covering all 7. Voice character preservation (cos vs context > 0.7)
holds for every emotion in the pack.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
2026-04-29 19:29:47 -07:00
osobhandClaude Opus 4.7 f022e14f34 rtx-csm: docs/emotional_speech_guide.md — user-facing handbook
Phase 9 produced 25 commits and a complex pipeline; perf_history is
the engineering log but new users coming to this cold need a clean
"how do I make CSM speak with emotion" handbook.

Sections:
- What this gets you (single-shot WER 0.07–0.21, voice cosine ≥ 0.95)
- One-liner quickstart (RAVDESS download → extract → use)
- The recipe explained — every flag and why it's there
- Per-emotion notes (works/best-seed/caveats per emotion)
- When it works / when it doesn't
- Troubleshooting (music tokens, premature EOT, repetition, etc.)
- Architecture cheat sheet (backbone=semantic, decoder=acoustic)

References perf_history.md for the full empirical log; this doc is
the user-facing distillation.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
2026-04-29 19:00:31 -07:00
osobhandClaude Opus 4.7 6e22507994 rtx-csm: perf_history — per-emotion seed × context interaction
Cross-emotion × Carlini context at Amini-best seeds reveals the
"magic combo" doesn't fully transfer:

  happy@42    Amini WER 0.21  →  Carlini WER **0.071** (transfers!)
  angry@100   Amini WER 0.93  →  Carlini WER 1.21 (URL drift)
  fearful@7   Amini WER 0.86  →  Carlini WER 1.00 ("Screw it")
  sad@7       Amini WER 0.93  →  Carlini WER 1.00 (no transcript)

Only happy@42 cleanly generalizes across contexts. The previous
"context-robust" claim was too strong — the (emotion, seed, context)
interaction matters. Cosine vs context stays high for angry (0.95)
even when text drifts, so voice character preservation is the more
robust property than text fidelity.

Honest production interface: `emotional_speech_n.sh` rolling 5 seeds
per (context, prompt). The single-shot recipe lands well only when
all dimensions align, but the picker absorbs the variance.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
2026-04-29 15:48:08 -07:00
osobhandClaude Opus 4.7 48c9fdd5f2 rtx-csm: perf_history — cross-context validation, WER 0.071 new best
3-context bench at happy@seed=42 decoder recipe (Amini / MCC /
Carlini). Cosine measured vs the context wav (does recipe preserve
input voice character?), WER vs prompt:

  context   cos_ctx   WER      transcript
  amini     0.972     0.21     "All right, today I want to share
                                something with you that I've been
                                thinking about."
  mcc       0.58      0.93     "You" (sub-speaker mismatch)
  carlini   0.958     0.071  "So today I want to share something
                                with you that I have been thinking
                                about."

Carlini's WER 0.071 is the new single-shot best of Phase 9. Only
prefix "So" added to the verbatim prompt. Cos vs context > 0.95 on
the two working contexts means the recipe preserves speaker
character of the reference — does NOT impose RAVDESS speaker
identity on every output.

The recipe is context-robust on speaker identities the picker
selects correctly. McConaughey failed because we picked the
manifest's spk1 (likely the Oscars announcer), not McConaughey
himself. That's a context-selection issue, not a recipe issue.

Empirical capstone: single-shot near-verbatim emotional speech
with preserved voice character is achievable.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
2026-04-29 15:35:59 -07:00
osobhandClaude Opus 4.7 d0ab272804 rtx-csm: perf_history — cross-prompt validation, WER 0.125 best of day
3-prompt × 2-condition bench at happy@seed=42 decoder recipe vs
no-steering baseline. The recipe holds across prompts and produces
the lowest WER recorded across all Phase 9 experiments:

  prompt                base                  happy decoder
  Today I want to…      0.52 / 0.93 "You"     0.82 / 0.21 
  Have you ever…        0.72 / 1.39 drift     0.77 / 1.00 "With blames"
  Weather has been…     0.73 / 1.88 ♪♪♪       0.69 / 0.125  "That the
                                               weather has been
                                               absolutely beautiful
                                               this mor"

The imperative prompt's WER 0.125 is the lowest recorded.
Improvement vs baseline ranges 1.4× to 15× lower WER. The
no-steering baseline produced literal singing tokens (♪♪) on the
weather prompt, suggesting CSM's CFG-only path is fragile on
prompts the model "dislikes."

Empirical conclusion: decoder route + RAVDESS happy steering at
seed 42, scale 1.0, layers [2,3] is a reproducible recipe, not a
single-prompt anomaly. N-seed picker still the right consumer
interface, but this single configuration alone reaches
near-publishable quality on multiple prompts.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
2026-04-29 15:10:30 -07:00
osobhandClaude Opus 4.7 942de8ef49 rtx-csm: perf_history — decoder 4-emotion × 3-seed capstone
Records the breakthrough single-shot result: happy@seed=42,
decoder route, scale 1.0, layers [2,3] →
"All right, today I want to share something with you tha"
(WER 0.21, cos 0.82). Closest-to-perfect single-condition
result of the entire Phase 9 sprint.

Per-emotion seed winners diverge:
  happy=42, angry=100, fearful=7, sad=7

No universal best seed exists; this validates emotional_speech_n.sh
as the production interface (rolls multiple, picks lowest WER).

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
2026-04-29 15:00:46 -07:00
osobhandClaude Opus 4.7 f642132f4a rtx-csm: --decoder-steering-layers — layer subset fixes repetition
Mirror of --steering-layers for the decoder: comma-separated layer
indices to actually steer (decoder has 4 layers; useful subsets are
[3], [2,3], [1,2]).

Sweep at seed 7, [email protected]:
  [0,1,2,3]  cos 0.86  WER 0.93  "I'm not that tall ×3" ← repetition
  [3]        cos 0.58  WER 0.86  "I'll be off and offense..."
  [2,3]      cos 0.80  WER 1.43  "My daughter, Penny Ryan, and I have…"
  [0,1]      cos 0.82  WER 1.57  "And I'll check on them..."
  [1,2]      cos 0.81  WER 1.43  "I'm going to call him an X-Man..."

The repetition is specific to all-layers-at-once steering. Any 2-layer
subset eliminates it while preserving most of the cosine boost. Same
pattern as the backbone's [8,10,12] finding: partial perturbation
lets the unsteered layers act as a stabilizing prior.

[2,3] (decoder last 2) is the new recommended recipe — best cosine
of the no-repetition subsets and the longest fluent transcript.
Documented in docs/perf_history.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
2026-04-29 14:37:49 -07:00
osobhandClaude Opus 4.7 68ecae1b08 rtx-csm: perf_history — decoder steering empirical characterization
3-seed × 2-condition ([email protected] alone vs +CFG) bench plus a 4-step
scale sweep. Captures the honest tradeoff:

- Backbone steering destroys word content (semantic gibberish).
- Decoder steering preserves coherent English BUT produces
  repetition or premature EOT.

Neither produces single-shot production-quality emotional speech;
emotional_speech_n.sh (N-seed picker, lowest-WER wins) remains the
right consumer interface — it doesn't care which failure mode
generated the bad samples, just discards them by metric.

Decoder vector magnitudes are ~10× smaller than backbone (norm 0.85
at deepest layer vs 14.9), so the apparent useful scale window is
~10× higher (0.5-1.0 instead of 0.2-0.3).

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
2026-04-29 14:21:06 -07:00
osobhandClaude Opus 4.7 406c437d70 rtx-csm: perf_history — recipe seed variance caveat
3-seed × 3-prompt reproducibility bench on [email protected] reveals that the
recipe shifts speaker character reliably but produces high text-
fidelity variance:

  seed 42:  WER 2.00 "The police are, if you're, I can't recite..."
  seed 7:   WER 0.71 "Today, today I want to share..."  ← near-verbatim
  seed 100: 0.32 s premature EOT

Cross-prompt at seed 42 drifts uniformly across 3 prompts. Speaker
cosine is consistently elevated; text content is roll-the-dice.

Documenting this as the honest characterization rather than overclaim
the single-seed Sprint 2 results. Practical recipe: roll N seeds,
pick lowest-WER output.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
2026-04-29 12:16:20 -07:00
osobhandClaude Opus 4.7 2008c9c4a6 rtx-csm: emotional_speech.sh — per-emotion scale defaults
Capstone wrapper for the Phase 9 recipe (Selective CFG + RAVDESS
steering + mid-layer subset). Picks the steering scale automatically
from an empirical per-emotion map:

  - happy:    0.30
  - angry:    0.20
  - fearful:  0.20
  - sad:      0.20 (note: sad is unreliable — see below)

These came from a follow-up sweep after the multi-emotion demo
revealed the recipe is emotion-sensitive: scale 0.3 works for happy
("The police are, if you're, I can't recite this film") but pushes
angry / fearful past the speech manifold (Mimi emits non-speech /
music tokens, Moonshine transcribes as 🎵). Dropping to 0.2 recovers
fluent speech for both:

  - [email protected]:    "The next disorder is completing kashim for more."
  - [email protected]:  "You just heard a little bit about this decision,
                   though."
  - [email protected]:    "The police are, if you're, I can't recite this
                   film. I"

Sad is the outlier — model resists "sad" steering at every scale
between 0.15 and 0.3. Likely a corpus issue (sad RAVDESS clips are
the lowest-energy subset). Documented as a known limitation rather
than worked around.

perf_history.md updated with the per-emotion sensitivity finding.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
2026-04-29 11:30:33 -07:00
osobhandClaude Opus 4.7 29f4b6fc56 rtx-csm: CFG schedule × prompt sweep — robust default is linear
Sweep of 4 schedules × 4 prompts produced concrete data on schedule
sensitivity. step:3.0:1.5:12 (the Sprint 3 winner) catastrophically
fails dense prompts: lecture-style input → 0.08 s of audio (one
frame). step:2.0:1.0:8 produced the best single shot — near-verbatim
question rendering "Well, it had stem-wondered. Have you ever
wondered why we sometimes hear voices the way we do?" — but tanked
the lecture prompt (2.4 s "You").

linear:3.0:1.0:25 is the only schedule that's never the best AND
never the worst — graceful degradation across all four prompt
categories. Updates the recommended recipe in perf_history.md
(formerly step:3.0:1.5:12).

quality_eval: skip WavLM-SV scoring on clips shorter than 0.25 s
(emit -1 sentinel) — WavLM-SV's TDNN front-end requires a few
hundred samples and crashed mid-sweep on the 0.08 s clip. Now the
eval emits a row instead of bailing on the whole batch.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
2026-04-29 11:04:09 -07:00
osobhandClaude Opus 4.7 38f586cfa3 rtx-csm: docs/perf_history — Phase 9 emotion/quality/control summary
Records all of today's three-paper sprint: Sprint 1 (eval foundation),
Sprint 2 A/B + follow-ups (steering apply, extract, layer-subset,
RAVDESS), Sprint 3 (Selective CFG). Captures the composition finding
(step CFG + RAVDESS mid-layer steering @ scale 0.3 → speaker_cosine
0.846, largest cross-character migration we've measured) and the
recommended invocation recipe. Documents what was rejected and why
(TTSDS2 install hell, EmoSteer's flow-matching-specific algorithm).

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
2026-04-29 10:45:55 -07:00
osobhandClaude Opus 4.7 209279c13e rtx-csm: Phase 13.9 — wav2vec2 candle port (slices 1+2+3, real ASR working)
Full port of facebook/wav2vec2-base-960h (94.4 M params, MIT) closing
the WhisperX-class word-alignment gap from the audio-ML survey. Same
staged-scaffolding pattern that worked for emotion2vec — but landed
slices 1+2+3 in one session.

src/wav2vec2.rs ships:
  - Wav2Vec2Config::base_960h
  - FeatureExtractor — 7 Conv1d (1→512, total stride 320). Layer 0
    uses GroupNorm with num_groups=num_channels=512 (HF's wav2vec2
    feat_extract_norm: "group"). Critical: state-dict key is
    layer_norm.* but the OP is GroupNorm — loading as LayerNorm
    produces empty CTC output.
  - FeatureProjection — LayerNorm(512) + Linear(512→768)
  - ConvPosEmbedding — kernel 128 grouped Conv1d, materialized at
    load time from upstream weight_g + weight_v (fairseq's weight_norm
    on dim=2; eps-guarded division for numerical stability)
  - Block — POST-norm transformer with separate Q/K/V (vs emotion2vec's
    fused QKV), uses (B*H, T, D) Metal 3D-matmul workaround from
    Phase 8.8 Moonshine
  - Encoder — pos_conv + initial LayerNorm + 12 Blocks
  - Wav2Vec2 top-level — load_from_safetensors via mmap'd VarBuilder
  - ctc_greedy_decode + VOCAB_960H constant for the 32-char alphabet

examples/wav2vec2_inspect.rs (slice 1): dumps tensor layout + config
examples/wav2vec2_smoke.rs (slice 3): real-weight load + ASR forward

Verified on Metal:
  loaded model in 0.28 s
  forward in 9 ms for 10.42 s audio (~1150× realtime)
  transcript: "HE HOPED THERE WOULD BE STEW FOR DINNER TURNIPS AND
              CARROTS AND BRUISED POTATOES AND FAT MUTTON PIECES TO
              BE LADLED OUT IN THICK PEPPERED FLOWER FAT AND SAUCE"

Numerical parity with upstream Python — the FLOWER-for-FLOUR typo is
the known wav2vec2-base-960h failure mode, matches HF reference exactly.

7 new unit tests; lib suite 127/127 (was 120).

Slice 4 remaining: Viterbi forced alignment given known transcript,
to emit (token, frame_start_ms, frame_end_ms) for word-boundary cuts.
The ASR path itself is now production-ready.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
2026-04-28 03:33:35 -07:00
osobhandClaude Opus 4.7 a77b22d47c rtx-csm: Phase 13.8 — emotion2vec_plus_base port, slice 1 (inspector)
First slice of the multi-session port that will replace the Phase 13.3
prosody-rule SER placeholder with a real classifier via the existing
EmotionDetector trait.

examples/emotion2vec_inspect.rs:
  - downloads model.pt + config.yaml + tokens.txt from
    emotion2vec/emotion2vec_plus_base on HF Hub
  - descends fairseq-style nested checkpoint via --key model
  - dumps all 185 tensors with shapes/dtypes + per-prefix summary
  - uses pickle::read_pth_tensor_info, same pattern as audioseal_inspect

Architecture confirmed (full notes in docs/emotion2vec_port_notes.md):
  - 93 M params, F32 (the 1.12 GB file is mostly optimizer state)
  - local_encoder: 7 Conv1d layers (wav2vec2 feature extractor:
    [(512,10,5)] + [(512,3,2)]×4 + [(512,2,2)]×2, T → T/320)
  - project_features: Linear 512 → 768
  - relative_positional_encoder: 5 Conv1d layers (kernel 19)
  - context_encoder: 4-layer transformer prenet (prenet_depth=4)
  - blocks.0..7: 8-layer main transformer (depth=8, embed_dim=768,
    12 heads, mlp_ratio=4, fused QKV qkv.weight=[2304, 768])
  - proj: Linear 768 → 9 (angry/disgusted/fearful/happy/neutral/other/
    sad/surprised/<unk>)

Slicing plan (remaining):
  Slice 2 (~half-day): candle module scaffolding + from_pickle loaders
  Slice 3 (~half-day): forward pass + shape verification
  Slice 4 (~hour): EmotionDetector impl + swap into audio_to_manifest
                  and converse_server

Lib suite 110/110.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
2026-04-27 23:14:06 -07:00
osobhandClaude Opus 4.7 a0dbe7caf4 rtx-csm: docs — personal-use emotional voice training guide
The hobby-project sibling of maya_finetune_analysis.md. For the case
where you want the most emotionally responsive voice ever, for personal
use only (not distributed, not commercial), using all media formats
legitimately accessible to one person.

Key practical guidance:

  Best ROI sources: audiobooks (200-500 hr easy) > podcasts (100 hr per
  weekend) > anime/game VA reels (the gold mine for extreme emotional
  range) > YouTube > movies. Skip Reddit clips. SKIP TTS-synthesized
  data (mode collapse).

  Data prep pipeline (specific tool choices):
    yt-dlp -> Demucs v4 htdemucs_ft -> Silero V5 VAD (we have the
    candle port already) -> WhisperX (the right answer for
    diarization+ASR+alignment, don't roll your own) -> DNSMOS quality
    gate -> single-speaker filter -> resample 24 kHz -> Mimi tokenize

  Emotion labeling: emotion2vec+ as primary auto-tagger, GPT-4o or
  Claude as LLM-as-judge for the 5-10% you'll actually train on
  (~$50/100hr), hand-label 200 clips for Cohen's kappa validation.
  Plus implicit conditioning on previous-turn audio (what Sesame
  likely did). Do BOTH.

  Training recipe (100-200 hr corpus, single A100/H100):
    - LoRA: extend from q+v to q,k,v,o + MLP gate/up/down. r=32-64.
    - Curriculum: audiobooks (3 ep clean) -> podcasts (1 ep) -> VA/
      movies (1 ep, lower LR). Prevents messy data destabilizing
      acoustic priors.
    - One LoRA, multiple emotion control tokens. Per-emotion LoRAs
      can't switch fast enough at inference.
    - 5-10% mix-in of EmoV-DB/ESD/MEAD/RAVDESS. Not more.

  Reality check:
    60-120 focused hr -> "clearly better than base CSM in your domain"
    300+ hr           -> "genuinely beats Maya for me"

    Biggest trap: spending 80% of time on data, 15% on training infra,
    5% on actually listening critically. Listening is where the model
    gets good. Set a rule: every checkpoint, 20 prompts + notes.

    Second trap: training on TTS-synthesized data. Mode collapse.

    Where motivation dies: hour 40 when WhisperX diarization fails on
    a podcast and you spend a Saturday debugging pyannote.

  Going BEYOND Maya:
    - GoEmotions 28-label taxonomy + V/A continuous (5x5 = 25 pseudo)
    - Multi-persona via 512-d persona embeddings (YourTTS pattern)
    - Reactive emotion: emotion2vec+ on user audio at inference,
      feed as control token. ~50 ms latency. Feasible today.

  Concrete Phase 12 candidates (bounded codable items, NOT the data
  collection itself):
    1. Extend rtx-csm LoRA coverage q+v -> k,o,MLP (~1-2 hr)
    2. Wire WhisperX as scripts/ data-prep step (Python sidecar)
    3. emotion2vec+ via ort sidecar, JSON labels
    4. Emotion control token plumbing in Generator::generate
    5. Curriculum trainer examples/lora_train_emotional.rs

Papers cited: CosyVoice 2, Voicebox, NaturalSpeech 3, emotion2vec+,
Spirit-LM. Tools: yt-dlp, Demucs v4, WhisperX, pyannote 3.x, Silero V5,
DNSMOS, GoEmotions taxonomy.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
2026-04-27 14:42:54 -07:00
osobhandClaude Opus 4.7 fa8d7e9eb2 rtx-csm: research — emotional corpora vs Hollywood for CSM fine-tune
Extended docs/maya_finetune_analysis.md with the user's "train on
Hollywood scenes that denote emotions" angle. The instinct is sound;
the legal pitfalls are severe; legitimate alternatives exist.

Headline:

  DON'T train on Hollywood movies. Copyright + right-of-publicity is
  uninsurable for a shipped product (NYT v OpenAI / Andersen v
  Stability / RIAA v Suno / Johansson v OpenAI "Sky" all 2024-2026).
  Source separation works technically; the law doesn't.

Legitimate corpora that capture the same "actors performing emotion"
property (matrix added to the doc):

  Commercial-clean (use these):
    EmoV-DB           7 hr / 4 spk    CC-BY 4.0   — explicit laughs/yawns
    CREMA-D           5 hr / 91 spk   ODC-By 1.0  — read but emotion-tagged
    DailyTalk        20 hr / 2 spk    CC-BY-SA    — dyadic conversational
    LAION Emo Speech ~5000 hr         CC-BY 4.0   — but provenance risk
    Hume Prosody     proprietary      paid commercial

  Research-only (skip for shipped product):
    Expresso (Meta)  47 hr / 4 spk   CC-BY-NC    — best quality
    IEMOCAP          12 hr / 10 spk  academic    — best emotional range
    MELD (Friends)   13 hr           Warner Bros — audio is copyrighted
    RAVDESS, ESD     small/medium    research

New "Path D" recipe added:

  Stage 1 (~6 hr GPU on H100):
    - EmoV-DB + CREMA-D combined (~12 hr, commercial-clean)
    - LoRA r=8 α=16 on q+v +k+o + decoder cross-attn
    - 3 epochs, lr 1e-4 cosine, bf16

  Stage 2 (~weekend, 5-10 hr recording):
    - One voice actor improvising LLM-prompted dialogue
    - Stage-2 LoRA r=16 on the stage-1 checkpoint

Per the corpus research: gets ~70% of Maya's emotional
expressiveness, ~30% of her personality. The single-speaker stage 2
is the "uncanny news anchor doing feelings" -> "specific persona"
overlay. Crucially this is a WEEKEND with one actor, not the 40-hr
studio sprint Sesame did.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
2026-04-27 14:25:39 -07:00
osobhandClaude Opus 4.7 406780e194 rtx-csm: research — Maya personality fine-tune analysis
Honest research into what Sesame likely did to fine-tune the open
CSM-1B base into the deployed Maya/Miles persona. Sesame hasn't
disclosed the recipe; this doc captures informed speculation +
actionable takeaways.

Key findings:

1. Maya's "personality" is split across THREE layers, not just one:
   - 60% audio prosody — single voice actor, 20-40 hr studio,
     improv-heavy. Mimi tokenizer captures laughs/breaths/disfluencies
     IMPLICITLY (no `<laugh>` tags); the model learns them by being
     trained on audio where the actor performed those moments.
   - 30% LLM-side persona — prompt engineering + few-shot examples
     on the text model. NOT a voice-model property at all.
   - 10% conversational dynamics — VAD + endpointing + barge-in +
     streaming TTS. We're already at parity here.

2. Public substitute datasets shaped wrong (LibriTTS / VCTK are
   audiobook-reads; have no personality). Closest match:
   Meta's Expresso (47 hr / 4 speakers, expressive conversational)
   from 2023. Worth investigating if we ever pursue real Maya-class
   prosody.

3. Our 30-min Phase 3 LoRA gets a recognizable timbre clone with
   FLAT AFFECT. Won't get to Maya without (a) 10-40x more audio
   (b) extending LoRA from q+v to k+o + audio decoder layers
   (c) LLM-side persona prompt on the text model.

Three concrete next chunks captured:
  A. LLM-side persona prompt (~30 min, biggest ROI/minute)
  B. Extended LoRA coverage (~1-2 hours)
  C. Real corpus + audio fine-tune (multi-week, defer)

Verdict: real Maya-class output is 2-4 person-months of product
work + a 5-10 hr studio recording session. The IP gap is real and
not closeable with documentation alone. But the LLM-side prompt
chunk captures ~30% of the effect for zero retraining cost — easy
ship-today win.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
2026-04-27 13:56:18 -07:00
osobhandClaude Opus 4.7 237216a31f rtx-csm: Phase 11 — Silero V5 VAD pure-candle port (closes 8.1.3 deferred)
Phase 8.1.3 deferred Silero V5 VAD because the only Rust crate path
(`voice_activity_detector` via `ort`) collides with `sentencepiece-sys`
on protobuf 3.14 vs 3.21 and panics at process startup. This commit
closes that gap with a NATIVE candle port.

Direct port from `Snakers4/silero-vad/src/silero_vad/tinygrad_model.py`
(71 LOC reference). Architecture:

  stft_conv  Conv1d(1,    258, k=256, s=128)  no bias
  conv1      Conv1d(129,  128, k=3,   p=1)
  conv2      Conv1d(128,   64, k=3,   s=2, p=1)
  conv3      Conv1d(64,    64, k=3,   s=2, p=1)
  conv4      Conv1d(64,   128, k=3,   p=1)
  lstm_cell  LSTMCell(128, 128)
  final_conv Conv1d(128,    1, k=1)

Forward: reflect-pad input by 64, STFT-as-conv1d, sqrt(real² + imag²),
4-layer Conv1d feature stack with ReLU, single LSTM step (state across
chunks), 1x1 conv + sigmoid -> speech probability.

Files added:
  src/silero_vad.rs                       ~310 LOC (incl. LSTM cell + downloader)
  docs/silero_vad_port_notes.md           architecture + port plan
  examples/silero_vad_smoke.rs            real-audio discrimination test

Plus a new `ureq` direct dep (transport already pulled in via hf-hub).

Weights ship via download-on-first-run from the upstream GitHub raw
URL into `~/.cache/rtx-csm/silero_vad_16k.safetensors` (1.24 MB). No
repo bloat; no .gitignore wrestling.

End-to-end smoke (synthetic 50/50 silence/speech WAV at 16 kHz):

  load (cold):     download + parse, < 100 ms after first run
  VAD sweep:       170 ms over 9.99 s of audio = 0.017x realtime (59x faster)
  unit test:       passes (load weights + run one step)

Probability output (per 32 ms chunk):
  0-1.5 s:   p ~ 0.01-0.07   silence
  1.5-5 s:   p ~ 1.000        speech (clean ramp at speech onset)
  5-10 s:    p ~ 0.001        silence

Speech-chunk fraction 33% on the 50/50 layout — matches expected.

Production angle: dramatically better silence/speech discrimination
than the Phase 8.1.3b energy VAD (which only catches obvious silence).
Silero V5 catches whisper-quiet speech, breath/lip noise, music vs
speech distinction. Drop-in candidate for `--vad-gate` in a future
iteration.

The ort/protobuf conflict that blocked this for two months is now
permanently resolved by NOT using ort.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
2026-04-27 13:46:47 -07:00
osobhandClaude Opus 4.7 c92e8f2eef rtx-csm: docs/perf_history — add watermarker backend matrix (SilentCipher row)
Captures the Phase 10 work in the consolidated perf doc. New section
"Watermarker backend matrix" lists AudioSeal (Meta) and SilentCipher
(Sesame's actual) side by side with measured RTF, capacity, conflicts,
and a per-use-case recommendation table.

Headline: SilentCipher is Sesame's literal production watermarker, now
shipping in pure candle with bit-perfect round-trip on real LibriSpeech
audio (15/15 codes, confidence 1.0000) and ~10× smaller than AudioSeal
(~3M params vs ~30M). The "blow them out of the water" item from the
Sesame gap analysis is closed.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
2026-04-27 13:27:28 -07:00
osobhandClaude Opus 4.7 9f5c345b53 rtx-csm: Phase 10.1 — SilentCipher inspector + port notes
Foundation for porting Sesame's actual production watermarker (NOT
AudioSeal — the gap analysis identified this as the literal Sesame
parity item). Same iterative-shipping pattern as Phase 8.4 for
Moonshine.

`docs/silentcipher_port_notes.md`:
  - Full architecture from SesameAILabs/silentcipher/src/.../model.py
    (verified against 95 LOC of source)
  - Three small networks of gated 2D convs on STFT:
      enc_c    3 layers   1 -> 32 channels
      dec_c    4 layers   96 -> 1 channels
      dec_m    10 layers  1 -> 128 -> message_dim, plus Linear
  - Each Layer = Conv2d * sigmoid(Conv2d) + BatchNorm2d
  - Pipeline (encode + decode) walked through step by step
  - 10 ordered porting tasks with hour estimates totaling ~1-2 days
  - Risks flagged: STFT helper needed, BatchNorm running stats loading,
    phase passthrough, message-length differences vs AudioSeal

`examples/silentcipher_inspect`:
  - Downloads sony/silentcipher 16 kHz checkpoint from HuggingFace
  - Dumps hparams.yaml + tensor shapes per .ckpt file
  - Verified output:
        N_FFT 2048   HOP 1024   SR 16000
        message_dim 4  message_len 16  message_band 512
        enc_c     0.17 MB    40 k params
        dec_c     2.01 MB   500 k params
        dec_m_0   9.54 MB  2.38 M params
        Total           ~2.92 M params

That's ~10x smaller than AudioSeal's gen+det combined. Port
estimated 1-2 days.

`.ckpt` files are pickle (PyTorch state_dict) — direct loadable via
candle_core::pickle::read_all, same path as audioseal_convert.rs.
No safetensors conversion needed.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
2026-04-27 12:23:08 -07:00
osobhandClaude Opus 4.7 1760309b39 rtx-csm: gap analysis vs Sesame Labs full voice stack
Researched what Sesame has actually disclosed publicly (vs. marketed)
and compared systematically against rtx-csm's shipped surface.

Key findings:

1. **rtx-csm has shipped a SUPERSET of Sesame's open release.** CSM-1B
   inference + voice cloning + Q8 + production WebSocket server +
   three-backend STT pipeline + per-phase observability — Sesame ships
   inference code only.

2. **Sesame's deployed Maya is a cascaded STT->LLM->TTS pipeline**,
   same architecture pattern as rtx-csm. Their research blog explicitly
   states future work is "fully duplex models" — Maya today isn't
   duplex either. We're structurally equivalent at the pipeline level
   to Kyutai Unmute, Sesame's closest peer.

3. **Marketing latency claims are unverified.** "Sub-200 ms TTFA"
   appears in third-party blogs, not in any Sesame paper. Production
   benchmarks of similar cascaded stacks show 250-300 ms TTFT
   (gpt-realtime, Unmute) — our 280-380 ms TTS-side is competitive.
   Z.AI provider TTFT (~1.3 s) is the dominant cost in our 1.96 s
   end-to-end.

4. **Critical correction**: Sesame ships SilentCipher (their fork of
   Sony's), NOT AudioSeal (which is Meta's). Our Phase 4 AudioSeal
   work is functionally equivalent but isn't the *literal* Sesame
   watermarker. SilentCipher port is ~1-2 days.

5. **What's gated on Sesame**: CSM-3B / CSM-8B variants (trained but
   never released), Maya personality fine-tune dataset, distilled
   wearable variant. The Oct 2025 Series B + smart-glasses pivot
   suggests they're unlikely to release any of these.

Punch-list of remaining gaps captured in the doc with status (Closed/
Partial/Open/N/A) per capability.

Recommended next chunks (prioritized):
  1. SilentCipher port (~1-2 days) — literal Sesame watermarker parity
  2. clawsample-csm integration (~5-10 days) — separate plan exists
  3. Tier 2.1 VoXtream look-ahead (~5-7 days) — diminishing returns
     after Phase 9.2's chunk_frames tuning
  4. Tier 3 Frame-Stacked / VADUSA (training-required, ~3-6 weeks)
  5. Distilled CSM (speculative, wait for product target)

Defensible framing: "rtx-csm is Sesame's open release + voice cloning
+ production HTTP/WS server + Kyutai-style cascaded duplex. Remaining
gap to internal Maya is (a) SilentCipher watermarker, (b) Sesame's
proprietary fine-tune dataset, (c) larger CSM variants Sesame chose
not to release."

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
2026-04-27 12:14:04 -07:00
osobhandClaude Opus 4.7 87fb269a81 rtx-csm: Phase 9.1 spike — VoXtream port notes (Tier 2.1 prep)
Phase 9.1 is the foundation step for the Tier 2.1 (VoXtream-style
look-ahead) optimization from the Phase 8 plan. Same pattern as Phase
8.4: don't attempt the full port in one session; ship the architecture
analysis + ecosystem audit + ordered porting plan so a future session
starts cold from concrete data.

Phonemizer ecosystem audited (results in the doc):

  Crate                       Approach              Latency      Conflicts
  espeak-ng 0.1.1             pure Rust eSpeak NG   1-5 ms/word  none      <- PICK
  voirs-g2p 0.1.0-rc.1        neural, candle 0.9.2  10-50 ms/w   libc dep
  grapheme_to_phoneme 0.1.0   seq2seq RNN ARPAbet   2-8 ms/word  none      (abandoned 2020)
  phonetisaurus-g2p 0.1.1     FST                   sub-ms       none      (no pre-trained FST)

espeak-ng is the right pick: zero C linkage default, no conflicts with
candle 0.9 / sentencepiece-sys / hound / symphonia / ebur128, mature.
Per-word latency fits VoXtream's 102 ms first-packet target with room
to spare (5-10 word lookahead = 5-50 ms total phonemizer cost).

Doc captures:
  - Why this is worth doing (current ~600 ms TTS first chunk vs 102 ms
    claim from arXiv 2509.15969)
  - Three-piece integration architecture (phonemizer service, look-ahead
    window in Converse, optional Generator-side phoneme hint)
  - Three open questions that MUST be resolved before implementing
    (mechanism, CSM training compatibility, empirical win on this HW)
  - Six ordered porting tasks with hour estimates totaling 5-7 days
  - Alternative quick win: drop streaming chunk_frames from 4 to 2 or 1
    first; if that closes most of the gap, full VoXtream port may not
    be worth the complexity

Recommended order of attack written for the next session: try the
chunk_frames tuning before implementing the phonemizer. The cheapest
move is a 1-line config change.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
2026-04-27 11:40:04 -07:00
osobhandClaude Opus 4.7 893e8e6232 rtx-csm: Phase 8.11 — production stack validated against live Z.AI
Updated docs/perf_history.md to reflect the full Phase 8 work:

  - New TL;DR has TWO production configs (English-only with --moonshine,
    multilingual with default Kyutai). The English path is the new
    headline recommendation.
  - Captured live Z.AI bench numbers from the new production stack
    (Q8 + stream + warmup + Moonshine + thinking-disabled): total_turn
    8765 ms vs Phase 6f.q8 era 17145 ms — half the wall-clock latency
    end-to-end.
  - Phase 8 commit table extended to 8.4 through 8.10.
  - Added an STT backend matrix (Kyutai 1B / Whisper-rs / Moonshine)
    with measured RTF, build flags, and per-deploy recommendation rows.

Bench command used (single turn, real Z.AI glm-4.5 thinking-disabled,
10.43 s LibriSpeech /tmp/asr_test.flac):

  target/release/examples/converse_server \\
    --bind 127.0.0.1:18099 \\
    --quantized-gguf /tmp/csm_q8.gguf \\
    --stream-tts --moonshine \\
    --llm-base https://api.z.ai/api/coding/paas/v4 \\
    --llm-model glm-4.5 \\
    --llm-extra-body '{"thinking":{"type":"disabled"}}'

Server-side timing:
  recv_phase            0 ms   (Moonshine batch — no parallel STT)
  stt_post            332 ms   (Moonshine transcribe at EOT)
  llm_to_first_audio  1627 ms  (Z.AI TTFT ~1.3 s + first TTS chunk)
  conv_total          8432 ms
  total_turn          8765 ms

Client TTFA: 1959 ms (vs Phase 6f.q8 era ~2 s — comparable; the dominant
remaining latency is the Z.AI provider TTFT, not anything we control).

Z.AI returned a coherent reply: "He eagerly anticipated a hearty stew
with turnips, carrots, potatoes, and savory mutton pieces for dinner."
matching the LibriSpeech ground-truth meaning.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
2026-04-27 11:30:14 -07:00
osobhandClaude Opus 4.7 c848abef22 rtx-csm: Phase 8.4 spike — Moonshine v2 inspector + port notes
First step on the Tier 2.2 (Moonshine v2 candle port) item from the
Phase 8 plan. Full port is honestly multi-session work (~12-15 hours
of focused implementation across encoder, decoder, generation loop,
tokenizer, weight mapping, smoke test). This commit ships the
foundation so future sessions start from concrete data instead of
arxiv reading.

Two ships:

1. examples/moonshine_inspect — downloads UsefulSensors/moonshine-tiny
   from HF, parses safetensors header, dumps all 160 tensors grouped
   by prefix with shapes + dtypes. Verified output: 27.1 M params,
   108.4 MB safetensors (F32), encoder + decoder layers laid out as
   expected.

2. docs/moonshine_port_notes.md — captures every architectural fact
   established by the inspector + HF config.json:
   - Hyperparameter table (hidden=288, 6+6 layers, vocab=32768,
     partial_rotary=0.9, etc.)
   - Tensor layout per layer (encoder, decoder)
   - Architecture summary (raw waveform input, 3-layer Conv1d stem,
     SwiGLU decoder MLP via fused fc1, tied LM head)
   - Ordered porting tasks with hour estimates totaling ~12-15 h
   - Risks / unknowns (conv strides not in config, tied output head
     question, quality-vs-Kyutai concern)
   - Recommended order of attack for the next session

The full port itself is deferred. Ship the foundation now so the
remaining work has a clean handoff.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
2026-04-27 10:21:30 -07:00
osobhandClaude Opus 4.7 3cf02f3aee rtx-csm: docs/perf_history.md — Phase 6/7/8 consolidated record
Consolidated record of every shipped optimization, every rejected path
with the data behind the rejection, the production-recommended config,
the architectural lessons captured, and the deferred multi-session
work with realistic sizing.

Headline:
  - client first_audio_ms p50 = 529 ms (mock LLM, Q8 + stream + extended warmup + VAD gate)
  - real Z.AI loop: ~2 s TTFA p50
  - boot cost: ~2.7 s (one-time)

Rejected paths captured (so future sessions don't redo the work):
  Q4_K_M (2.85x slower than Q8), whisper-rs linkage (2-3x CSM regression),
  Silero V5 via ort (protobuf 3.14 vs 3.21 conflict), Mimi codec Q8
  (no candle conv-quant path), KV cache reuse (variance is content-
  dependent not state-dependent), rayon for single-connection
  (overhead exceeds gain on <100µs tasks), codec swaps (require
  backbone retrain), custom distillation (no published checkpoint),
  Kyutai 4x flush (hardware-bound on M-series).

Architectural lessons:
  1. In-process linkage of external ML runtimes is a recurring trap;
     default to sidecar-process pattern.
  2. Bench thermals dominate single-machine A/B; 90s cooldown often
     necessary.
  3. First-frame compilation is the dominant cold-start cost — long
     warm-ups are essential.
  4. Conv-phase variance is content-dependent, not state-dependent.
  5. tokio::join! polls cooperatively — spawn separate tasks for real
     concurrency between sync compute and async pump.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
2026-04-27 09:13:23 -07:00
osobhandClaude Opus 4.7 83e80dc721 rtx-csm: add deferred clawsample-csm integration plan
Detailed plan for exposing rtx-csm as a managed service on the
clawsample platform: crate layout (mirrors clawsample-demucs +
clawsample-gen), HTTP routes (/v1/tts, /v1/tts/async, /v1/voice_profile,
WS /v1/converse), DB schema, R2 paths, webhook dispatch, ordered TDD
task breakdown, open decisions, acceptance criteria, reference commits.

Estimated effort: 5-10 days. Trigger to start: a real consumer for the
public TTS API, OR standalone converse_server hits a hard ceiling.
Until then, integration is product/platform work, not ML work.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
2026-04-27 04:50:43 -07:00