Transcribing Long Audio Recordings With Speech-to-Text Models in October 2026: Silence-Based Chunking, Speaker Labels, and Timestamps You Can Trust
Speech-to-text models are good enough now that transcribing a one-hour meeting, podcast, or customer call is mostly an engineering problem, not a model problem. The model handles a clean 30-second clip well. What breaks in production is everything around it: files that are too big to upload, chunks that cut a word in half, invented text during long silences, speaker labels that swap halfway through, and timestamps that drift by several seconds by the end of the recording.
This guide walks through a pipeline that handles long recordings reliably. It is model-agnostic. Whether you call a hosted transcription API or run an open-weight model such as Whisper on your own GPU, the same steps apply.
Step 1: Normalize the Audio Before Anything Else
Recordings arrive in every format: phone M4A files, stereo WAV from a podcast mixer, Opus from a browser, video files with an audio track. Convert everything to one known format first. Most speech models work internally at 16 kHz mono, so converting up front makes files smaller and removes one source of surprises.
ffmpeg -i input.m4a -vn -ac 1 -ar 16000 -c:a pcm_s16le normalized.wav
A few things worth checking at this stage:
- Duration and size. Read the duration with
ffprobeand store it. You will need it later to validate that your timestamps cover the whole file. - Stereo with one speaker per channel. Many call-center and podcast recordings put each participant on a separate channel. If so, do not mix down to mono. Transcribe each channel separately and you get speaker separation for free, which is far more accurate than any diarization model.
- Upload limits. Hosted APIs usually cap file size or duration per request. Check your provider's current limit and plan to chunk anything above it, rather than discovering the cap when a two-hour file fails.
Step 2: Chunk at Silences, Not at Fixed Offsets
The naive approach is to cut the audio every N seconds. That reliably splits words and sentences across chunks, and the model then guesses at the fragments on both sides. The result is dropped or duplicated words at every boundary.
Cut at pauses instead. There are two practical ways to find them.
Option A: ffmpeg silence detection
ffmpeg -i normalized.wav -af silencedetect=noise=-35dB:d=0.5 -f null - 2>&1 | grep silence_
This prints silence_start and silence_end times for every stretch quieter than -35 dB lasting at least half a second. Tune the noise threshold to the recording: a noisy room needs a higher (less negative) threshold, a studio recording a lower one.
Option B: a voice activity detection model
A voice activity detection (VAD) model, such as the open-source Silero VAD, classifies short frames as speech or non-speech. It handles background noise, music, and keyboard clatter better than a plain loudness threshold, and it gives you speech segments directly.
Building the chunks
Once you have pause positions, group speech into chunks with a target length and a hard maximum:
- Walk through the speech segments in order, adding each one to the current chunk.
- When the chunk passes the target length (for example, 60 to 120 seconds), close it at the next pause.
- If no pause appears before the hard maximum, cut at the quietest point in the last few seconds instead of exactly at the limit.
- Record each chunk's start offset in the original file. Every timestamp the model returns is relative to the chunk, so you will add this offset back later.
Some teams also add a short overlap (one or two seconds) between chunks as insurance. If you do, you must de-duplicate the overlapping words when merging, usually by keeping the words from whichever chunk has them farther from its edge. Cutting cleanly at real pauses usually makes overlap unnecessary.
Step 3: Do Not Send Silence to the Model
Speech models trained on large amounts of subtitled audio have a well-known failure: when given long silence, music, or noise, they sometimes produce fluent text that nobody said. Phrases that resemble subtitle credits or "thanks for watching" are common examples, and so are repeated sentences.
The fix is upstream, not in the prompt:
- Drop chunks the VAD marks as having no speech instead of transcribing them.
- Trim long leading and trailing silence from each chunk.
- After transcription, flag any segment whose text repeats the previous segment exactly, or whose words-per-second rate is implausibly high or low for its duration. These are cheap checks that catch most invented text.
- If the API returns per-segment confidence or a no-speech probability, store it and treat low-confidence segments as suspect rather than silently keeping them.
Step 4: Give the Model Context It Cannot Hear
Names, product terms, and acronyms are where transcripts go wrong most visibly. Many transcription APIs accept a short text prompt or a vocabulary hint. Use it for the terms you know will appear:
prompt = "Speakers: Priya Raman, Tomasz Kowalczyk. Terms: Kubernetes, pgvector, SOC 2, Postgres."
Keep it short and factual. For continuity across chunks, some pipelines pass the last sentence of the previous chunk's transcript as part of the next chunk's prompt. That helps with spelling consistency, but it can also carry an error forward, so only pass text from segments that passed your quality checks.
Also set the language explicitly when you know it. Automatic language detection on a chunk that begins with a short greeting or a foreign name can pick the wrong language for the whole chunk.
Step 5: Add Speaker Labels With Diarization
Diarization answers "who spoke when," separately from "what was said." If you do not have one channel per speaker, you need a diarization step. Open-source toolkits such as pyannote.audio produce a list of time ranges with anonymous speaker IDs (SPEAKER_00, SPEAKER_01, and so on). Some hosted transcription APIs include diarization as an option.
The practical problems are in combining the two outputs:
- Run diarization on the whole file, not per chunk. If you diarize each chunk separately, SPEAKER_00 in chunk 3 may be a different person than SPEAKER_00 in chunk 4. A whole-file pass keeps IDs consistent.
- Assign words, not segments. A single transcript segment often spans a speaker change. Assign each word to the speaker whose diarization range overlaps it most. This needs word-level timestamps, which many APIs can return if you ask, and which tools such as WhisperX produce by aligning the transcript against the audio.
- Tell the system how many speakers to expect when you know it. A two-person interview diarized without a speaker count can come back with three or four speakers because of a cough or a change in microphone distance.
- Map anonymous IDs to names last. Let a human or a simple rule (the host introduces themselves first) attach real names. Do not ask an LLM to guess identities from content; it will produce confident wrong labels.
Step 6: Merge and Validate Timestamps
When merging chunk results, convert every timestamp to the original file's timeline by adding the chunk's start offset. Then validate before you store anything:
- Timestamps must increase monotonically across the whole transcript.
- No segment may end after the file's measured duration.
- Gaps between segments should line up with silences you detected. A large gap with no detected silence suggests a chunk was dropped or failed.
- The count of chunks transcribed must equal the count of chunks you created minus those deliberately skipped as silent.
If any check fails, retry the affected chunk rather than the whole file. Store the chunk ID, offset, and raw model output alongside the merged transcript so you can debug a bad timestamp months later.
Step 7: Measure Accuracy on Your Own Audio
Published benchmark numbers are measured on someone else's audio. Your callers, accents, microphones, and jargon are different. Build a small evaluation set: ten to thirty short clips from your real recordings, transcribed carefully by a person.
Measure word error rate (WER), the fraction of words substituted, deleted, or inserted compared with the reference. The Python library jiwer computes it in a few lines:
import jiwer
reference = open("clip01_reference.txt").read()
hypothesis = open("clip01_model.txt").read()
transform = jiwer.Compose([
jiwer.ToLowerCase(),
jiwer.RemovePunctuation(),
jiwer.RemoveMultipleSpaces(),
jiwer.Strip(),
])
print(jiwer.wer(transform(reference), transform(hypothesis)))
Normalize both texts the same way before comparing, or you will measure punctuation style instead of recognition quality. Alongside overall WER, keep a separate list of the names and terms that matter to your business and check how often each is spelled correctly. A transcript with low WER that misspells your product name in every sentence still looks broken to users.
Re-run this set whenever you change the model, the chunk length, the VAD threshold, or the prompt. Small pipeline changes can move accuracy more than switching models.
Step 8: Handle Privacy and Retention Deliberately
Recordings of calls and meetings often contain personal data, and the audio is usually more sensitive than the text. Decide up front:
- Whether participants were told the call is being recorded and transcribed, as your jurisdiction may require.
- How long you keep the original audio once the transcript is validated. Many teams delete it after a short window and keep only the text.
- Whether your transcription provider retains uploaded audio or uses it for training, and what settings or agreements change that.
- Whether transcripts should pass through a redaction step before they reach search indexes, analytics, or an LLM for summarization.
A Checklist for Your Pipeline
- Normalize to 16 kHz mono, unless channels already separate speakers.
- Chunk at detected pauses with a target and a hard maximum length.
- Skip silent chunks and flag repeated or implausible segments.
- Pass known names and terms as a short prompt, and set the language explicitly.
- Diarize the whole file once and assign speakers word by word.
- Shift timestamps by chunk offsets and validate them against the file duration.
- Track WER and key-term accuracy on a small set of your own clips.
- Set a retention policy for audio and transcripts before you go live.
None of these steps is complicated on its own. Together they are the difference between a demo that transcribes one clean clip and a pipeline that turns hundreds of messy hours into transcripts people can search, quote, and trust.
Comments
Post a Comment