voice-ai-engineering
Build and debug end-to-end speech pipelines with Whisper-style models and ffmpeg - ingestion and validation, preprocessing, transcription, speaker diarization, transcript cleanup, SRT/VTT generation,
SKILL.mdskills/voice-ai-engineering/SKILL.md
GitHubVoice AI Integration Engineer
Turn raw audio into clean, structured, time-stamped, speaker-attributed text - and keep the pipeline degrading gracefully on noisy, multi-speaker, or long-form input instead of only working on clean studio recordings.
Pipeline stages
Ingest and validate. Never trust extensions: probe the container with
ffprobe -print_format json -show_streams -show_formatand reject the file if duration is unknown/non-positive/oversized, or no audio stream exists. Record codec, sample rate, channel count for the run log.Preprocess. Whisper-style models want 16 kHz mono unless the model documents otherwise. Extract the audio track explicitly (never assume an
.mp4is audio-only), downmix to mono, decode to 16-bit PCM:ffmpeg -y -i in.mp4 -vn -acodec pcm_s16le -ar 16000 -ac 1 \ -af "loudnorm=I=-16:TP=-1.5:LRA=11" out.wavKeep the original file untouched; write preprocessed audio to a scratch area.
Chunk long audio. Do not rely on the model's maximum input duration - overflow truncates silently. Chunk (30 min is a sane default) with a small overlap (about 30 s) and merge with overlap trimming so no word is lost at boundaries.
Transcribe.
faster-whisper(CTranslate2) for GPU/CPU speed,whisper.cppfor CPU-only boxes, cloud ASR (OpenAI, Deepgram, AssemblyAI...) when accuracy, diarization quality, or volume justifies it and privacy allows. Pick model size (tiny -> large-v3) from the latency/accuracy budget; keepword_timestamps=Truewhen downstream timing matters.Diarize (when speakers matter).
pyannote.audioor ASR-native diarization; merge speaker turns with transcription segments by time overlap. Preserve speaker attribution through every later stage - stripping labels breaks all downstream consumers.Post-process. Never treat model punctuation/capitalization as ground truth - run a normalization pass; do not conflate confidence scores with accuracy (flag low-confidence segments for human review instead of deleting them); format timestamps (word/segment level) and emit SRT/VTT/ASS with sane line lengths and reading speed.
Hand off. Deliver structured JSON (segments with start/end/speaker/text), subtitles, and a Markdown reading copy. Downstream consumers (summarizers, TTS dubbing, CMS, editors) must receive normalized, correctly attributed text - plus a small run report (model, parameters, WER sample if measurable, gaps).
Hard rules
- Never discard timestamps. Regenerating them costs a full re-transcription.
- 16 kHz mono before the model, unless the model documents otherwise; bad or unvalidated input is the leading cause of silent accuracy loss.
- Chunk explicitly - overflow is silent and corrupts output without error.
- Keep raw audio and unredacted transcripts out of logs; redact PII as a named, configurable stage, and honor retention windows.
- Verified means executed (§6): WER, alignment, and loudness claims come from measurements on the actual output, not from the model's reputation.
- Fluency is not accuracy: verify names, numbers, and domain terms against a sample of the audio (spot-check by listening or by waveform-aligned segments).
Debugging quick table
| Symptom | Usual cause |
|---|---|
| Uniform garbage / hallucinated repetitions | wrong sample rate or channels (not 16 kHz mono) |
| Words missing at regular intervals | chunk boundaries without overlap |
| Slow drift over a long file | chunk timestamps not re-anchored on merge |
| One speaker split into several | diarization run on unnormalized audio |
| Punctuation noise / casing chaos | model output taken as ground truth, no normalization pass |
| Trailing silence kills timing | no silence trim before transcription |
Deliverable template
## Transcription run
Input: <file, duration, codec, sample rate/channels probed>
Preprocess: <commands applied>
Model: <engine, model size, device, language, key parameters>
Chunks: <count, overlap, merge strategy>
Diarization: <method, speaker count, merge tolerance>
Outputs: <transcript.json (segments), subtitles.srt/.vtt, reading copy.md>
Verification: <spot-check results, WER sample, timing spot-checks, exit codes>
Known gaps: <low-confidence ranges flagged for review>