A huge amount of organizational knowledge isn’t written down — it’s spoken. Podcast back catalogs, recorded meetings, webinars, support calls, sermon archives, conference talks. All of it is invisible to search, because search engines only read text.
ext-whisper fixes that for PHP applications.
It’s a PHP 8.3+ extension that runs OpenAI’s Whisper speech-to-text models inside the PHP process via whisper.cpp (through the whisper-rs Rust bindings) — the same architecture as ext-infer, pointed at a different model family. Audio goes in, text and timestamped segments come out. Nothing touches a network.
The shape of the API
Load a GGUF-format Whisper model, hand it 16kHz mono WAV audio, get a transcript:
<?php
use Displace\Whisper\Model;
$model = Model::load('models/ggml-base.en.gguf');
$result = $model->transcribe('episode-042.wav');
echo $result->text(); // full transcript
foreach ($result->segments() as $segment) {
printf("[%.1fs – %.1fs] %s\n",
$segment['start'],
$segment['end'],
$segment['text'],
);
}
A flat transcript makes audio searchable; timestamped segments make it navigable. When search matches a segment, you know the exact offset — so the result can deep-link to 14:32 of the recording instead of dumping the user at the start of an hour-long file. Segments are also natural chunk boundaries for generating embeddings.
The 16kHz contract
v0.1 accepts exactly one input format: 16kHz, mono, 16-bit WAV — which is what Whisper models consume natively. Rather than bundling a half-baked audio decoder, the extension documents a one-liner to handle converting other audio formats.
ffmpeg -i episode-042.mp3 -ar 16000 -ac 1 -c:a pcm_s16le episode-042.wav
ffmpeg already decodes every format on earth. The contract-first v0.1 of ext-whisper keeps the extension small and the behavior predictable.
Model selection
Whisper comes in sizes from tiny (~75 MB) through large (~3 GB), with English-only variants that outperform multilingual siblings at the same size. Practical guidance:
base.en— fast, light, solid for clear single-speaker audio. Start here.small.en/medium.en— the accuracy sweet spot for podcasts and recorded talks.large— for multilingual audio or difficult recordings, when you can afford the compute.
Transcription is batch-natured — a queued worker chewing through an archive overnight is the natural deployment shape, and it’s the workload where local processing crushes cloud economics. Per-minute transcription pricing on an archive of hundreds of hours is real money; a local model is basically just the cost of electricity you were already paying for.
For audio with privacy weight — internal meetings, support calls, anything under attorney-client or healthcare constraints — local-only processing isn’t just a nice-to-have.
The stack is complete
With ext-whisper, the three native pillars for local AI are in place:
- ext-infer — generation and embeddings
- ext-turbovec — vector indexing and search
- ext-whisper — speech to text
Transcribe → embed → index → search → generate, every stage in-process, every byte on hardware you control. What’s missing is the connective tissue — the interfaces that let these pieces compose with each other and with the wider PHP ecosystem without coupling anyone to anyone.
We’ll tackle that tomorrow.
