How to Create SRT Subtitles from an Audio File on Mac
Create SRT or VTT subtitles from an audio file on a Mac with ffmpeg and whisper.cpp, fix the timing and line breaks, then get the same file in one step.
You can turn any audio file on a Mac into a timed SRT or VTT subtitle file without uploading it anywhere. This guide walks through the free Terminal route (ffmpeg plus whisper.cpp), shows how to fix timing and line length when the first result is not quite right, and ends with a one-step option for when you would rather not maintain the setup. By the end you will have a subtitle file you can load into a video editor, a media player, or a web page. If your source is a video rather than an audio file, the shorter path is to transcribe video to text and export captions in a single pass.
What an SRT file actually is
SRT is plain text. Each subtitle is a block: a sequence number, a start and end time, one or two lines of text, and a blank line.
1
00:00:01,200 --> 00:00:04,000
Welcome back, and thanks for joining us today.
2
00:00:04,200 --> 00:00:07,500
Let's start with the numbers from last quarter.
VTT (WebVTT) is nearly the same format with a WEBVTT header and full stops instead of commas in the timestamps. Browsers use VTT for the HTML <track> element; most desktop players and video editors accept both. Because both are plain text, you can open them in any editor and fix a word or a timestamp by hand.
Route 1: ffmpeg and whisper.cpp in Terminal
whisper.cpp is an open-source implementation of OpenAI's Whisper speech model that runs well on Apple Silicon. It can write SRT and VTT directly, so the whole pipeline is: convert the audio, run the model, open the file. If you want the longer background on installing and tuning whisper.cpp, read Run Whisper locally on a Mac; the steps below are the shortest path to a subtitle file.
Step 1: Install Homebrew, ffmpeg and whisper.cpp
If you do not have Homebrew yet, install it from brew.sh by pasting the one-line command from that page into Terminal. Then:
brew install ffmpeg
brew install whisper-cpp
As of writing the Homebrew formula installs a command called whisper-cli. Older versions used the name whisper-cpp. If one name is not found, try the other, or run brew info whisper-cpp to see what was installed.
Step 2: Download a model
whisper.cpp needs a model file in its ggml format. The whisper.cpp repository lists the available models and a download script. A quick way is to fetch one directly into a folder you will remember:
mkdir -p ~/whisper-models
cd ~/whisper-models
curl -L -o ggml-base.en.bin \
https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-base.en.bin
base.en is small and quick, good enough for clear English speech. For other languages or for better accuracy on difficult audio, pick a small, medium or large model from the same list. Larger models are slower and use more memory, so start small and move up only if the transcript needs it.
Step 3: Convert the audio to 16 kHz mono WAV
whisper.cpp expects 16 kHz, 16-bit, mono WAV. ffmpeg does the conversion from almost any input (MP3, M4A, WAV, FLAC, OGG, or the audio track of a video file):
ffmpeg -i input.mp3 -ar 16000 -ac 1 -c:a pcm_s16le input.wav
Replace input.mp3 with your file. If you are starting from a video, use the video file as the input; ffmpeg will take the audio track and ignore the picture.
Step 4: Generate the SRT and VTT
Run the model on the WAV and ask for subtitle output:
whisper-cli -m ~/whisper-models/ggml-base.en.bin -f input.wav -osrt -ovtt
-osrt writes input.wav.srt and -ovtt writes input.wav.vtt next to the audio. Add -l de (or another language code) if the speech is not English and you are using a multilingual model. The flag names come from the whisper.cpp README at the time of writing; whisper-cli --help shows the current list if anything differs.
Step 5: Check the result
Open the SRT in a text editor and skim it. Then load it in a player that supports external subtitles alongside the original audio or video, and watch the first minute and a random minute in the middle. You are checking three things: the words are right, each subtitle appears when the words are spoken, and no line is so long it runs off the screen.
Fixing timing and line length
The first output is usually close but rarely perfect. These are the fixes that cover most cases.
Subtitles are consistently early or late
If every subtitle is off by the same amount, the audio you transcribed does not start at the same moment as the video you are pairing it with (a trimmed intro, for example). whisper.cpp has an offset option that shifts the starting point in milliseconds; check --help for its current name. Alternatively, most subtitle editors and many video players can shift an entire file by a fixed amount. Editing by hand also works for a short file: the timestamps are plain text.
Lines are too long
Whisper tends to produce segments that are one full sentence, which can be 15 words or more. whisper.cpp has an option to cap the maximum segment length in characters, and a related option to split on word boundaries rather than mid-word. Something in the region of 42 characters per line is a common subtitling convention. Re-run with those options rather than fixing lines one by one.
A few segments are wrong
Fix them in the text editor. For a 30-minute recording, expect a handful of corrections with a base model on clean audio, and considerably more if the audio is noisy, has several speakers, or contains names and jargon. If you are making many corrections, try a larger model once before hand-editing the whole file.
Long silences produce odd segments
Whisper can generate a repeated phrase or a stray line during long silences or music. Trim leading silence with ffmpeg before transcribing, and delete stray blocks afterwards. Renumber the sequence numbers if you delete blocks; some players are strict about them, and a small script or a subtitle editor can renumber for you.
Burning the subtitles into a video
If you need a single video file with the captions rendered into the picture (for platforms that do not accept a separate subtitle file), ffmpeg can do that too:
ffmpeg -i video.mp4 -vf subtitles=input.wav.srt output.mp4
Keep the SRT as a separate file as well. Burned-in captions cannot be switched off, edited, or translated later, whereas the SRT can. If you are starting from the video rather than from an audio file, transcribe a video to text covers the import side; for why the captions matter to your audience, see live captions and transcripts as an accessibility tool. A recorded talk with a single voice is the easiest material there is, and transcribing a sermon works one through from recording to subtitles.
What this route costs you
Once it is set up, the Terminal route is free and fully private: nothing leaves your Mac. The costs are time and upkeep. The first setup takes half an hour if everything goes smoothly, longer if Homebrew or the model download misbehaves. Every new file means a conversion step, a model run, and a check in an editor. Model names, flags and binary names change between releases, so a script that worked in the spring may need a small fix in the autumn. For a one-off job that is fine. If you produce subtitles every week, the overhead adds up. Weekly output usually means a show, and podcast transcription and show notes covers the rest of that workflow, from the transcript to the chapter markers.
The one-step alternative
Frequently asked questions
Can I create subtitles from an MP3 without converting it first?
Not with whisper.cpp, which expects a 16 kHz mono WAV. The ffmpeg command above takes seconds and works on MP3, M4A, FLAC, OGG and video files. Some other tools accept MP3 directly, but converting first is a reliable habit because it also lets you trim silence or normalise volume in the same step.
What is the difference between SRT and VTT?
Both are plain-text subtitle formats with the same idea: numbered blocks with a start time, an end time and the text. VTT starts with a WEBVTT header, uses full stops in timestamps instead of commas, and supports some styling and positioning that SRT does not. Use VTT for web video and SRT for desktop players and editors. Converting between them is trivial and most tools can write both.
Why are my subtitles out of sync?
The usual causes are a fixed offset (the audio and video start at different points), a variable-frame-rate video that plays slightly faster or slower than the audio, or a transcription that drifted during a long silence. A fixed offset is fixed by shifting the whole file. Drift is fixed by re-encoding the video at a constant frame rate with ffmpeg, then transcribing that file's audio.
Does whisper.cpp work on Apple Silicon?
Yes. Apple Silicon is one of the platforms it is developed on, and recent builds can use the Neural Engine and GPU through Core ML and Metal. Check the project's README for the current build options; the Homebrew formula gives you a working default without any configuration.