Whisper GGML
On-device speech-to-text for Flutter, powered by whisper.cpp v1.9.1.
Transcribe audio files or transcribe live while the user speaks — fully on-device, no server, no API keys.
Highlights
- 🎙 Live transcription — partial transcripts stream in while recording, refined as more audio arrives. The model loads once per session and an adaptive energy gate keeps silence from producing hallucinated text.
- 📄 File transcription — one call to transcribe a recording.
- 📦 Offline-first — models download once and are cached, or ship them in your app's assets for fully offline use (see the example app).
- 🌍 99 languages — pick one (
'en','fr','de', …) or use'auto'to detect. - 🎛 Decoding controls — vocabulary biasing, context conditioning, and non-speech token suppression exposed from whisper.cpp.
- ⚡ Fast — whisper.cpp v1.9.1 with Accelerate on Apple platforms;
an 11-second clip transcribes in ~0.4 s with the
basemodel on an Apple Silicon Mac.
Supported platforms
| Platform | Minimum version |
|---|---|
| Android | API 21 |
| iOS | 15.6 |
| macOS | 10.15 |
Installation
dependencies:
whisper_ggml: ^2.0.0
Quick start
import 'package:whisper_ggml/whisper_ggml.dart';
final controller = WhisperController();
final result = await controller.transcribe(
model: WhisperModel.tiny,
audioPath: '/path/to/audio.wav',
lang: 'en',
);
print(result?.transcription.text);
The model is downloaded automatically on first use. Non-WAV input is converted with the bundled FFmpeg.
Live (streaming) transcription
transcribeLive accepts any stream of 16 kHz mono little-endian PCM16
audio and emits progressively refined transcripts while the audio flows.
With the record package:
final pcmStream = await recorder.startStream(const RecordConfig(
encoder: AudioEncoder.pcm16bits,
sampleRate: 16000,
numChannels: 1,
));
final session = await controller.transcribeLive(
model: WhisperModel.base,
pcm16Stream: pcmStream,
lang: 'en',
);
session.partials.listen((text) {
print(text); // full transcript so far, not a delta
});
// Later — stop recording, finalize, and free the model:
await recorder.stop();
final finalText = await session.stop();
Good to know:
- The model stays loaded for the whole session; inference runs on a background isolate and never blocks the UI.
- An adaptive energy gate keeps silence away from the decoder, which
otherwise hallucinates on silent audio. For unusually loud rooms or quiet
speakers, tune
gateNoiseFloorCap,gateVoiceRatio, andgateRmsMin. - Only one live session can run at a time.
- Real non-speech sounds (knocks, clicks) may transcribe as bracketed
annotations like
[door slams].
Models
| Model | Multilingual | English-only |
|---|---|---|
| tiny | WhisperModel.tiny |
WhisperModel.tinyEn |
| base | WhisperModel.base |
WhisperModel.baseEn |
| small | WhisperModel.small |
WhisperModel.smallEn |
| medium | WhisperModel.medium |
WhisperModel.mediumEn |
| large-v3 | WhisperModel.large |
— |
Smaller models are faster; larger models are more accurate. tiny and
base are good defaults for live transcription; small is a strong
accuracy/speed balance for file transcription on modern phones.
Decoding options
Available on both transcribe and transcribeLive:
| Option | Default | What it does |
|---|---|---|
initialPrompt |
null |
Biases decoding toward the vocabulary, names, and punctuation it contains — useful for domain-specific terms that otherwise get misrecognised. Decoding also mimics the prompt's style: an unpunctuated prompt tends to produce unpunctuated output. |
noContext |
false |
Stops whisper from conditioning on prior-segment transcripts (like Python whisper's condition_on_previous_text=False). Helps against hallucinated repetition on short, independent utterances. |
suppressNonSpeechTokens |
false |
Suppresses bracketed annotations such as [BLANK_AUDIO] or [music]. Side effect: real sounds may decode as plausible-looking words instead, which is why the example keeps it off. |
Performance notes
- The native engine is compiled with
-O3on all platforms, including debug builds on iOS/macOS — transcription speed there is close to release. - Android debug builds run the Dart layer in JIT mode; use
--releasefor representative performance. - The bundled whisper.cpp v1.9.1 is roughly 15× faster than the engine in versions before 2.0.0.