Transcribe audio files to text from the command line, with the backend of your choice: whisper.cpp or mlx-whisper running on your own machine, the OpenAI API, or any OpenAI-compatible endpoint such as Groq or a self-hosted Whisper server. Every provider takes the same options and produces the same output (plain text, JSON, SRT or WebVTT), so switching backends is a config change.
aux4 aux4 pkger install aux4/whisper
Requirements depend on the provider you use:
brew install whisper-cpp) and a ggml model file.pip install mlx-whisper.export OPENAI_API_KEY=sk-...
aux4 whisper transcribe meeting.mp3
Welcome everyone, let's start with the quarterly numbers.
Check which providers are usable on this machine:
aux4 whisper providers
local unavailable whisper-cli not found
mlx unavailable mlx_whisper not found (pip install mlx-whisper)
openai ready API key set, model whisper-1
openai-compatible unavailable baseUrl not set
All options can live in a config.yaml section and be selected with --config <name> (use --configFile <file> for a different file). Flags always override config values. A section per backend makes switching a one-word change:
config:
local:
provider: local
binary: /opt/homebrew/bin/whisper-cli # default: whisper-cli (from PATH)
model: /Users/sally/models/ggml-large-v3-turbo.bin
language: en # default: auto-detect
mlx:
provider: mlx
binary: mlx_whisper # default: mlx_whisper, else the Python module
model: mlx-community/whisper-large-v3-turbo # default
openai:
provider: openai
apiKey: secret://1password/Work/openai/credential # default: OPENAI_API_KEY
model: whisper-1 # default: whisper-1
format: srt # default: text
groq:
provider: openai-compatible
baseUrl: https://api.groq.com/openai/v1
apiKey: secret://1password/Work/groq/credential
model: whisper-large-v3-turbo # default: whisper-1
maxUploadSize: 25MB # default: no limit for openai-compatible
speaches:
provider: openai-compatible
baseUrl: http://localhost:8000/v1
model: Systran/faster-whisper-small
prompt: aux4, whisper.cpp, mlx
aux4 whisper transcribe standup.m4a --config local
aux4 whisper transcribe standup.m4a --config groq --format vtt
| Option | Description | Default | |--------|-------------|---------| | provider | local, mlx, openai or openai-compatible | openai | | model | Model name; for local, the path to a ggml model file | provider default | | language | Spoken language (ISO-639-1 code) | auto-detect | | format | text, json, srt or vtt | text | | prompt | Hint text for names, jargon and spelling | none | | apiKey | API key for openai / openai-compatible | OPENAI_API_KEY (openai only) | | baseUrl | API base URL | https://api.openai.com/v1 (openai) | | binary | Provider binary | whisper-cli (local), mlx_whisper (mlx) | | extension | Audio type for stdin or files without a reliable extension | file extension, then content detection | | maxUploadSize | Upload size limit (25MB, 500KB, bytes; 1 MB = 1024 KB) | 25MB (openai, also the maximum), none (openai-compatible) |
Note: keep API keys in config.yaml (plain or as secret:// references, which aux4 resolves through your secret provider) or in OPENAI_API_KEY. A key passed with --apiKey ends up in your shell history and process list.
aux4 whisper transcribe [<file>] [--extension <type>] [--provider <name>] [--model <model>] [--language <code>] [--format <text|json|srt|vtt>] [--prompt <text>] [--config <name>]
Transcribes one audio file, or audio piped to stdin when no file is given, and prints the result to stdout. Supported input: flac, m4a, mp3, mp4, mpeg, mpga, oga, ogg, wav, webm.
Recordings made with the browser MediaRecorder API work as they are: Chrome, Edge and Firefox produce webm (or ogg) with Opus, Safari produces mp4 with AAC. The audio type is taken from --extension when given, otherwise from the file extension, otherwise detected from the content, so uploads saved to a temporary file without a reliable extension still work:
aux4 whisper transcribe /tmp/upload-7f3a --extension webm --config local
cat /tmp/upload-7f3a | aux4 whisper transcribe --config groq
Please call me back after lunch.
text - the transcript as plain text.json - the transcript, detected language, duration and timed segments:{
"text": "Hello from the team. This is the weekly update.",
"language": "english",
"duration": 4.2,
"segments": [
{
"start": 0,
"end": 2,
"text": "Hello from the team."
},
{
"start": 2,
"end": 4.2,
"text": "This is the weekly update."
}
]
}
srt / vtt - subtitles built from the timed segments:aux4 whisper transcribe interview.mp3 --format srt > interview.srt
1
00:00:00,000 --> 00:00:02,000
Hello from the team.
2
00:00:02,000 --> 00:00:04,200
This is the weekly update.
aux4 whisper stream [--input <pcm|wav>] [--provider <name>] [--config <name>] [--silenceMs <ms>] [--maxPhraseSeconds <seconds>] [--partialInterval <ms>] [--threshold <dBFS>]
Live, phrase-by-phrase transcription. Pipe 16 kHz mono 16-bit PCM into stdin (the format a browser AudioWorklet sends) and read one JSON event per line from stdout:
ffmpeg -loglevel error -f avfoundation -i ":0" -ar 16000 -ac 1 -f s16le - | aux4 whisper stream --config local
{"type":"ready"}
{"type":"partial","id":1,"text":"Can you hear","start":0.7,"end":1.74}
{"type":"final","id":1,"text":"Can you hear me now?","start":0.7,"end":2.2,"segments":[{"start":0.7,"end":2.2,"text":"Can you hear me now?"}]}
{"type":"end"}
--silenceMs of silence (default 600) or is cut at --maxPhraseSeconds (default 25). Very short sounds and silence are ignored, and a short pre-roll keeps the first word.partial events re-transcribe the phrase so far every --partialInterval milliseconds of audio (default 1000, 0 turns them off). Partials are skipped while a transcription is still running, so a slow provider never falls behind.final event with the same id replaces the partials. Finals always arrive, in order, and each request gets the previous final as context after your --prompt.error events and the stream continues; configuration problems stop the command before it starts.SIGINT or SIGTERM, the phrase in progress is finalized, an end event is sent and temporary files are removed.--input wav to read a WAV stream, for example straight from aux4/audio:aux4 audio speech-prep --format wav < meeting.webm | aux4 whisper stream --input wav --config groq
All providers and config.yaml sections work as with transcribe. See aux4 aux4 man whisper stream for the full event schema.
aux4 whisper providers [--provider <name>] [--config <name>] [--format <text|json>]
Lists every provider with ready or unavailable and the reason (binary found, model set, API key set, base URL set). Use --config <name> or --provider with --model, --binary, --baseUrl and --apiKey to check a specific setup. API keys are never printed.
Runs fully offline with whisper.cpp. Download a ggml model (for example ggml-base.en.bin or ggml-large-v3-turbo.bin) and point model at it:
aux4 whisper transcribe call.mp3 --provider local --model ~/models/ggml-base.en.bin
Every input is converted to 16 kHz mono wav with aux4 audio speech-prep before transcription. If the aux4/audio package is missing, the command fails with the install command.
Fast local transcription on Apple Silicon with mlx-whisper. Models are Hugging Face repositories and are downloaded on first use:
aux4 whisper transcribe call.mp3 --provider mlx --model mlx-community/whisper-large-v3-turbo
If the mlx_whisper command is not in PATH, the package uses python3 when it can import the mlx_whisper module. mlx-whisper decodes audio with the ffmpeg installed by aux4/audio.
The OpenAI audio transcriptions API. Models: whisper-1 (default, supports every output format), gpt-4o-transcribe and gpt-4o-mini-transcribe (text and json only, since they do not return timestamps).
aux4 whisper transcribe call.mp3 --provider openai --model gpt-4o-transcribe
The API accepts uploads up to 25 MB; bigger files are handled automatically, see Large files.
Any server that implements the OpenAI /audio/transcriptions endpoint, such as Groq or a self-hosted Whisper server. baseUrl is required and apiKey is optional:
aux4 whisper transcribe call.mp3 --provider openai-compatible --baseUrl http://localhost:8000/v1 --model Systran/faster-whisper-small
The OpenAI API accepts uploads up to 25 MB. When a file is bigger, transcribe does not refuse it:
--prompt), so words and names stay consistent across chunk boundaries.json, srt and vtt output look exactly like a single request.Temporary files are always removed, also when a request fails. The same applies to the openai-compatible provider when you set maxUploadSize (for example 25MB for Groq's free tier); without it there is no limit. gpt-4o-transcribe and gpt-4o-mini-transcribe also limit the audio length, so audio longer than 1400 seconds is split into chunks for them even when it is small enough. The local and mlx providers have no limit and never split.
aux4 whisper transcribe all-hands-3h.m4a --format srt > all-hands.srt
aux4 whisper transcribe all-hands-3h.m4a --config groq --maxUploadSize 25MB
| Variable | Description | |----------|-------------| | OPENAI_API_KEY | API key for the openai provider when apiKey is not set |