Skip to content

feat(audio): add audio round-trip example pipeline (TTS → Whisper → export)#22

Merged
sarwarbhuiyan merged 1 commit into
mainfrom
feat/audio-pipeline
Jul 23, 2026
Merged

feat(audio): add audio round-trip example pipeline (TTS → Whisper → export)#22
sarwarbhuiyan merged 1 commit into
mainfrom
feat/audio-pipeline

Conversation

@justinrmiller

Copy link
Copy Markdown
Collaborator

Summary

Adds a fourth example modality, audio, alongside images / video / pdf. It's a self-contained text → speech → text round-trip that runs fully offline in local mode with auto-downloading models (no pre-placed model files, no system binaries):

ingest-audio → synthesize-audio → transcribe-audio → export-audio

Seed text prompts → synthesize speech with MMS-TTS → transcribe back with Whisper → export the waveforms to .wav.

Implements the Audio pipeline tickets: GEN-806 (parent scaffolding + ingest), GEN-807 (TTS UDF), GEN-808 (Whisper transcription). The export stage was added on top per follow-up request.

What's included

File Role
examples/audio/ingest.py Seeds an audio table with id + text prompts. No external media — audio is produced downstream, so the whole pipeline runs offline.
examples/audio/tts.py Our own transformers-native MMS-TTS UDF (facebook/mms-tts-eng, VitsModel) → list<float32> @ 16 kHz. Factory + decorated-class shape mirrors _shared/clip.py.
examples/audio/synthesize.py Backfills the audio waveform column from text (mirrors video/frame_embed.py).
examples/audio/transcribe.py Reuses Geneva's shipped WhisperChunkTranscriber via attrs.evolve (like pdf/document.py), rebinding input samplesaudio.
examples/audio/export.py Writes each waveform to <out-dir>/<id>.wav (16-bit PCM, stdlib wave; default /tmp/geneva_audio).

Registered in the three usual points (examples/__init__.py, examples/cli.py, pyproject.toml [project.scripts]), so it appears in both the generated uv run … CLIs and the TUI.

Design notes

  • No model files / pip-only. The requirement was a pipeline someone can run locally with just a pip install. MMS-TTS is transformers-native and auto-downloads via from_pretrained with zero system deps for English — chosen over the shipped ONNX kokoro_base_tts_udf (needs a local model_dir) and the kokoro pip package (needs the espeak-ng system binary). Whisper (transformers) auto-downloads/caches too.
  • Stages chain cleanly. MMS-TTS emits 16 kHz, exactly Whisper's TARGET_SAMPLE_RATE, so the TTS audio column feeds the transcriber directly — no download_audio, no resampling. The rate is recorded in the column's field_metadata.
  • Whisper model. Default is openai/whisper-large-v3-turbo, overridable with --model-id. Note: in testing, model size barely moved round-trip accuracy — the limiter is MMS-TTS pronunciation of uncommon words, not Whisper capacity. whisper-tiny is much lighter (~75 MB vs ~1.6 GB) if a smaller default is preferred later.
  • Runs in both modes unchanged: --mode local (CPU) and enterprise on Cantina (GPU), via the shared resolve_resources/local_or/runtime_session helpers.

Testing

Verified end-to-end locally (--mode local): all 4 stages ran, 5 rows synthesized → transcribed → exported to valid 16-bit mono 16 kHz WAV files. Example round-trip:

text:       The quick brown fox jumps over the lazy dog.
transcript: The quick brown fox jumps over the lazy dog.
  • 250 tests pass, coverage 97.78% (gate 90%); ruff + ty clean.
  • New tests/test_audio.py: UDF factory bindings (input column / data type / metadata / manifest rebind), WAV export round-trip + clipping, ingest/export CLI smoke tests.
  • Updated test_registry.py, test_tui.py, test_udfs.py for the new modality.
  • New run/model bodies added to the coverage omit list per the existing frame_embed/clip precedent (they need Ray + model weights); their factories and the pure WAV helper are unit-tested.

Try it

uv run ingest-audio     --mode local
uv run synthesize-audio --mode local   # downloads facebook/mms-tts-eng on first run
uv run transcribe-audio --mode local   # downloads the whisper model on first run
uv run export-audio     --mode local   # writes /tmp/geneva_audio/*.wav

🤖 Generated with Claude Code

… export)

Add a fourth example modality, `audio`, alongside images/video/pdf. It is a
self-contained text -> speech -> text round-trip that runs fully offline in
local mode with auto-downloading models (no pre-placed model files):

  ingest-audio -> synthesize-audio -> transcribe-audio -> export-audio

- tts.py: our own transformers-native MMS-TTS UDF (facebook/mms-tts-eng,
  VitsModel) emitting list<float32> @ 16 kHz. Chosen over the shipped ONNX
  kokoro UDF (needs a local model_dir) and the kokoro pip package (needs the
  espeak-ng system binary) so the demo installs with pip alone.
- transcribe.py: reuses Geneva's shipped WhisperChunkTranscriber via
  attrs.evolve, rebinding its input column from `samples` to `audio`. MMS-TTS's
  16 kHz output matches whisper's TARGET_SAMPLE_RATE, so it feeds in with no
  download_audio and no resampling. Default openai/whisper-large-v3-turbo,
  overridable with --model-id.
- export.py: writes each waveform to <out-dir>/<id>.wav (16-bit PCM, stdlib
  wave; default /tmp/geneva_audio).

Registered in the three usual points (examples/__init__.py, cli.py,
pyproject.toml scripts) so it appears in both the generated CLIs and the TUI.
New run/model bodies are added to the coverage omit list per the existing
frame_embed/clip precedent; their factories + the WAV helper are unit-tested,
and ingest/export are smoke-tested (tests/test_audio.py). Registry/TUI/UDF
tests updated for the new modality.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@sarwarbhuiyan
sarwarbhuiyan merged commit 5ecbb65 into main Jul 23, 2026
2 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.

2 participants