Transcribe Audio in Python with faster-whisper
A tested faster-whisper script: install, transcribe on a CPU, set the language, word timestamps, write an SRT file and the generator gotcha.
The Scriptorium team ·
Install the package with pip install faster-whisper, create a WhisperModel, call transcribe() and loop over the segments it returns. faster-whisper is an MIT-licensed reimplementation of OpenAI's Whisper built on CTranslate2; its README says it needs Python 3.9 or newer and that FFmpeg does not have to be installed, because audio is decoded with the PyAV library, which bundles the FFmpeg libraries. This is the library our own service runs, so the code below follows our settings.
The basic script
This is the README's example, with the CPU line from the README switched on (the README shows it commented out beside the GPU setup):
from faster_whisper import WhisperModel
model = WhisperModel("small", device="cpu", compute_type="int8")
segments, info = model.transcribe("audio.mp3", beam_size=5)
print("Detected language '%s' with probability %.2f" % (info.language, info.language_probability))
for segment in segments:
print("[%.2fs -> %.2fs] %s" % (segment.start, segment.end, segment.text))
We ran the same call, with the tiny model, on the six-sentence English sample clip on our home page. It printed:
Detected language 'en' with probability 0.99
[0.00s -> 4.40s] Good morning everyone, let's start with the project update.
[4.40s -> 10.00s] The design review is finished and the launch date is now the 12th of March.
[10.00s -> 15.00s] The budget is on track, but we still need approval for the extra servers.
[15.00s -> 18.00s] Maria will send the final numbers by Friday.
[18.00s -> 22.00s] Next week we will test the new export feature.
[22.00s -> 24.00s] Thank you all, and see you on Monday.That clip is a clean synthetic voice, so it says little about real recordings; the leading space on each line is how the library returns text.
Which model name, and how long does it take?
The sizes we load are tiny, base, small, medium, large-v3-turbo and large-v3. The first use of a size downloads it (our app lists approximate sizes of 75 MB for tiny, 480 MB for small and 3 GB for large-v3). On a CPU, compute_type="int8" is the setting we use. The GPU route needs NVIDIA cuBLAS and cuDNN 9 for CUDA 12, according to the README. Speed on a plain CPU is covered in how long an hour of audio takes.
Language, silence, word timing and hints
segments, info = model.transcribe(
"audio.mp3",
language="ar", # set the language instead of letting it be detected
vad_filter=True, # skip stretches with no speech
word_timestamps=True, # each segment then has .words with start, end and word
hotwords="Scriptorium", # words to nudge the model toward; not a guarantee
beam_size=5,
)
- language: set it when you know it. Leave it out and the model detects the language from the start of the file.
- vad_filter: the README says it removes parts of the audio without speech, and by default only silences longer than two seconds. Our service turns it on by default.
- word_timestamps: gives each segment a
wordslist with start, end and word, as in the README's example. - hotwords: the parameter our "Vocabulary hints" field uses. It nudges the model toward spellings and does not guarantee them.
The gotcha: segments is a generator
transcribe() returns almost at once and the real work happens as you loop. We measured it on the sample clip: the call returned in under a second and the loop took a couple of seconds. A second loop over the same object yields nothing. We confirmed that too: it gave zero segments. If you need the segments twice, make a list first: segments = list(segments).
Write an SRT file
def srt_time(t):
ms = round(t * 1000)
h, ms = divmod(ms, 3_600_000)
m, ms = divmod(ms, 60_000)
s, ms = divmod(ms, 1000)
return f"{h:02}:{m:02}:{s:02},{ms:03}"
def write_srt(segments, path):
with open(path, "w", encoding="utf-8") as f:
for i, seg in enumerate(segments, start=1):
f.write(f"{i}\n{srt_time(seg.start)} --> {srt_time(seg.end)}\n{seg.text.strip()}\n\n")
write_srt(segments, "audio.srt")
Run the function once on a list of segments (or on the generator before anything else has consumed it). We checked that its output matches our own SRT export byte for byte, apart from one blank line at the end. For Arabic subtitles, check the file in the player you will publish in: right-to-left punctuation can land on the wrong side (the guide is in Arabic and its script works on any SRT).
When code is not the right tool
A script gives you the text and the timing; it does not give you an editor, speaker labels, search or a shared library. If you only have a few files, the no-account uploader is quicker, and for the trade-offs of running things yourself see faster-whisper vs whisper.cpp vs OpenAI Whisper and transcribing offline.
Checked on 2026-10-26. The faster-whisper README (requirements, the basic example, word timestamps, vad_filter and the CPU int8 line). We ran the basic call with the tiny model and a generator test on our own sample clip, and the SRT function against our exporter. Not tested: GPU use, other versions of the library. Menus, prices and limits change; confirm on the vendor's page before you rely on them.