Friday fun: live speech to text from the terminal
Friday fun: I got tired of recording test audio files, uploading them, and waiting for a response before I could see if my STT integration was working. There had to be a faster loop.
Turns out there is. Two CLI tools and a pipe operator give you a live speech-to-text pipeline that prints transcripts to the terminal as you speak.
rec -q -t raw -r 16000 -e signed -b 16 -c 1 - | websocat wss://api.deepgram.com/v1/listen?encoding=linear16&sample_rate=16000
Speak into your mic. Watch the transcript appear.
How it works
rec (part of the sox suite) captures audio from the default microphone. The flags tell it exactly what format to use: raw PCM at 16kHz, signed 16-bit samples, single channel. The - at the end means write to stdout instead of a file.
That raw audio stream gets piped directly into websocat, a command-line WebSocket client. It opens a persistent connection to the STT API’s streaming endpoint and forwards every byte it receives on stdin as a binary WebSocket message.
The STT API processes the audio in real time and sends back JSON transcription results. Those print to your terminal as they arrive. No files, no uploads, no clicking around a dashboard.
Why this matters for SDK work
I maintain SDKs for this kind of API. The debugging loop for a streaming integration usually goes: write code, build, run, speak, check the output, edit the code, repeat. That loop takes a couple of minutes per iteration.
The rec + websocat pipeline collapses it to zero lines of SDK code. You can test whether the API accepts your audio format before you write a single line of integration code. You can check latency by watching how fast words appear after you say them. You can verify that reconnection logic works by killing the websocat process and watching the API surface handle the dropped connection.
It is the fastest possible way to answer “does my audio format work with this API”.
What I learned building it
The raw PCM format flag order matters. sox expects the encoding before the bit depth. -e signed -b 16 is the correct order. Swap them and you get a silent error because sox parses -b as a command, not a format flag.
The sample rate needs to match what the API expects. Most streaming STT APIs accept 8kHz and 16kHz. 16kHz gives better accuracy for speech. The -r 16000 flag matches the default rate for most voice APIs.
Binary WebSocket messages are more efficient than base64-encoded ones. websocat detects raw binary input and sends it as binary frames automatically. No encoding overhead.
FAQ
Do I need to install anything?
rec comes with sox (brew install sox). websocat is cargo install websocat or brew install websocat. Both are small, fast, and have no dependencies beyond what the package manager handles.
Can I change the microphone source?
rec picks the default system input. On macOS you can override it with rec -t coreaudio "Built-in Microphone". On Linux use rec -t alsa "hw:0,0". Check your available devices with rec -D.
What happens if the WebSocket drops?
The pipe breaks and both processes exit. The STT API treats the dropped connection as an end of stream. Any transcription results that were in flight when the connection dropped are lost. For a testing pipeline this is fine. Just run the command again.
Can I use this with a TTS API instead?
You would need a different approach. TTS receives text and outputs audio. You would pipe text into websocat and capture the binary audio output into a file or speaker. The pattern is similar but the direction reverses.