Skip to content

Model capability registry: descriptors, dictation, synthesis hardening#39

Merged
ivan-digital merged 6 commits into
mainfrom
feat/model-capability-registry
Jul 5, 2026
Merged

Model capability registry: descriptors, dictation, synthesis hardening#39
ivan-digital merged 6 commits into
mainfrom
feat/model-capability-registry

Conversation

@ivan-digital

@ivan-digital ivan-digital commented Jul 4, 2026

Copy link
Copy Markdown
Member

Summary

Registry-driven engine rollout, end to end: model-registry.json becomes the single source of truth for engine/ASR capabilities, and the Studio, Rust shell, and Swift sidecar all consume it. On top of that: Parakeet dictation, synthesis output conditioning with onset-artifact removal, Indic-Mio emotion-marker support, and a two-voice Hindi demo.

What changed

  • Registry: capability descriptors (languages, marker behavior, reference requirements, sidecar command, model ids) drive the engine picker, editor hints, and Rust dispatch; per-engine seed ladders, token budgets, and language normalization.
  • Model plumbing: the registry-selected modelId flows through init_model and every synthesize command so sidecar holders reload on artifact switches; VoxCPM2 defaults to the bf16 bundle.
  • Dictation: transcribe_parakeet in the sidecar, save-and-transcribe IPC with its own cache dir, ASR model registry, DictationPanel UI.
  • Onset cleanup: conditionAudioEdges (silent lead-in, edge fades, post-roll) for every engine, plus trimLeadingJunk for a reference-dependent leading codec artifact on Qwen3 ICL (quiet hiss, ~100 ms, Marek reference) and OmniVoice (low hum, ~200 ms, Anna reference). Energy-island analysis removes short, early, gap-separated islands ≥20 dB below the loudest island (or 8–20 dB below with a pure low-frequency ZCR signature); clean renders pass through bit-exact. A fixed pre-trim can't cover this: residue runs past 150 ms on affected renders while unaffected ones start voiced within 25 ms.
  • Indic-Mio markers: inline emotion markers map to the model's closed suffix-tag vocabulary (happy/sad/angry/disgust/fear/surprise); unmapped markers are dropped — the model reads unknown parentheticals aloud.
  • Demo: lazy clip seeding with stable cache ids; corrected Anna reference transcript (a stale transcript leaked prompt words into takes); Qwen single-shot at a fixed seed; Hindi demo gains a second, female FLEURS speaker (the existing reference is the male recording) with lines alternating across both voices.

Test plan

  • cargo test --lib — 38 passed
  • cargo test --test sidecar — 6 fast protocol tests passed (incl. new engine-mismatch / missing-field rejections)
  • cargo test --test sidecar -- --ignored qwen_onset_is_speech_not_junk omnivoice_onset_is_speech_not_junk indic_mio_does_not_speak_emotion_marker — 3 passed against real models: trims fire at 610 ms / 390 ms on the previously affected renders, first island is speech-loud, Parakeet coverage ≥ 60 %, marker never spoken
  • pnpm test — 42 passed; tsc --noEmit clean
  • Manual: English demo regenerated through Qwen3, OmniVoice, Chatterbox, and Indic-Mio; Hindi demo through Indic-Mio with both voices (female clone ~230 Hz vs male reference ~120 Hz); onset artifacts gone on previously affected clips

ivan-digital added 3 commits July 4, 2026 10:02
Ship the remaining pieces on top of the capability registry:

Dictation: Parakeet ASR through the sidecar (transcribe_parakeet), a
save-and-transcribe IPC path with its own cache dir, an ASR model
registry, and a DictationPanel front end.

Model plumbing: pass the registry-selected modelId through init_model
and the synthesize commands so holders reload when the artifact
changes; default VoxCPM2 to the bf16 bundle; registry-driven
per-engine seed ladders, token budgets, and language normalization.

Synthesis output conditioning: every engine's WAV now gets a silent
lead-in, edge fades, and a post-roll (conditionAudioEdges). Qwen3 ICL
and OmniVoice additionally open some renders (reference-dependent)
with a short low-level codec artifact before the first word — quiet
hiss on the Marek reference, a low hum on Anna — followed by a
silence gap. trimLeadingJunk removes it via energy-island analysis:
leading islands that are short, early, gap-separated, and either
≥20 dB below the loudest island or 8-20 dB below with a pure
low-frequency ZCR signature. Clean renders pass through bit-exact.
A fixed pre-trim could not work here: the residue runs past 150 ms
on affected renders while unaffected ones start voiced within 25 ms.

Indic-Mio markers: map the Studio's inline emotion markers onto the
model's closed suffix-tag vocabulary. The model reads unknown
parentheticals aloud, so unmapped markers are dropped rather than
passed through.

Demo: lazy clip seeding with stable cache ids; exact bundled
reference transcripts (a stale Anna transcript leaked prompt words
into takes); Qwen goes single-shot at a fixed seed; the Hindi demo
gains a second, female FLEURS speaker (the existing reference is the
male recording of its sentence) with lines alternating across both
voices.

Tests: sidecar e2e for onset cleanliness on both affected engines and
marker handling on Indic-Mio (ignored, model-backed), fast protocol
tests for engine mismatch and missing fields, and frontend unit tests
for the new synth, clip-audio, media-src, and language helpers.
@ivan-digital ivan-digital changed the title Use TTS capability descriptors Model capability registry: descriptors, dictation, synthesis hardening Jul 5, 2026
ivan-digital added 3 commits July 5, 2026 12:35
Pins buildHindiDemoProject against the two-speaker seed: one track per
voice, alternating lines routed to the matching track. Guards the demo
shape that a stale saved project can otherwise mask during manual
testing.
… editing UX

Synthesis: every engine now renders in a single pass. Removed the seed/cfg
retry ladder and the ASR grading gate entirely — the 16-bit models produce
intelligible speech in one shot (validated by a synth→ASR roundtrip across all
engines), and the macOS grader (Parakeet) could not read non-Latin scripts, so
it scored good Hindi/CJK takes near 0% and burned the whole ladder for nothing.
The seed now varies per synthesis (derived from the per-call invocation salt),
so a Regenerate rolls a fresh take — the manual escape hatch for an unlucky
render, instead of an automatic gate.

Indic-Mio: cache the WavLM speaker embedding per reference so it is computed
once per voice instead of once per attempt; combined with single-shot this
takes warm synthesis from multiple laddered attempts to ~0.7 RTF. Map inline
emotion markers onto the model's closed suffix-tag vocabulary so it stops
reading the marker aloud.

Onset cleanup: a shared conditionSynthOutput helper strips leading
codec/reference-boundary junk (a short sub-speech blip before the first word on
some reference/seed combinations) and edge-conditions, wired into every cloning
engine. Detects genuine junk by energy islands well below the speech body;
passes clean audio through unchanged (never trims a real quiet onset word).

Editing UX: explicit Save button in the top bar (autosave still runs); Add
script line from the track inspector and track list so a clip can be created
without dictation; a microphone in the script editor to dictate a line
directly into the field (shared useDictationRecorder hook with the dictation
panel); responsive top bar that no longer wraps or overlaps at narrow widths.
@ivan-digital ivan-digital marked this pull request as ready for review July 5, 2026 19:23
@ivan-digital ivan-digital merged commit 99e2f05 into main Jul 5, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant