· 3 min read

Speech to Text in C#, Offline: Whisper in Your .NET Process

Transcribe audio files from C# with OpenAI's Whisper running locally: no API key, no Python, no whisper.cpp build. One NuGet package, timestamps per segment, and 3 minutes of speech transcribed in 25 seconds on a CPU.

Transcribing audio from C# usually means sending it to a cloud API, or building whisper.cpp and wrapping it yourself. This runs OpenAI's Whisper model inside your .NET process, from one NuGet package, with no network after the first download:

using Kjarni;

using var transcriber = new Transcriber("whisper-small", language: "en", quiet: true);
var transcript = transcriber.TranscribeFile("jfk.wav");
Console.WriteLine(transcript.Text);
 And so my fellow Americans, ask not what your country can do for you, ask what you can do for your country.

Try it

You need the .NET 8 SDK.

dotnet new console -o SpeechToText
cd SpeechToText
dotnet add package Kjarni
curl -LO https://github.com/ggml-org/whisper.cpp/raw/master/samples/jfk.wav

Replace Program.cs with:

using System.Diagnostics;
using Kjarni;

var path = args.Length > 0 ? args[0] : "jfk.wav";

using var transcriber = new Transcriber("whisper-small", language: "en", quiet: true);

var sw = Stopwatch.StartNew();
var transcript = transcriber.TranscribeFile(path);

Console.WriteLine(transcript.Text);
Console.WriteLine();
foreach (var segment in transcript.Segments)
    Console.WriteLine($"[{segment.Start,6:F2} -> {segment.End,6:F2}] {segment.Text}");

Console.Error.WriteLine($"\n{transcript.DurationSecs:F1} s of audio transcribed in {sw.Elapsed.TotalSeconds:F1} s");
dotnet run

The first run downloads whisper-small (920 MB) and caches it. jfk.wav is the 11-second sample from the whisper.cpp repository, a public domain recording of John F. Kennedy.

A longer recording

For something with more than one sentence, here is a 3 minute 19 second public domain address from Wikimedia Commons, converted to WAV with ffmpeg:

curl -L -o columbia.ogg https://upload.wikimedia.org/wikipedia/commons/1/1f/George_W_Bush_Columbia_FINAL.ogg
ffmpeg -i columbia.ogg -ar 16000 -ac 1 columbia.wav
dotnet run -- columbia.wav
 My fellow Americans, this day has brought terrible news and great sadness to our country. At 9 o'clock this morning, ...

[  0.00 ->   9.00]  My fellow Americans, this day has brought terrible news and great sadness to our country.
[  9.00 ->  18.00]  At 9 o'clock this morning, Mission Control in Houston lost contact with our space shuttle Columbia.
[ 18.00 ->  24.00]  A short time later, debris was seen falling from the skies above Texas.
[ 24.00 ->  27.00]  The Columbia's lost.
[ 27.00 ->  29.00]  There are no survivors.
[ 29.54 ->  37.14]  On board was a crew of seven, Colonel Rick Husband, Lieutenant Colonel Michael Anderson,
...
198.7 s of audio transcribed in 25.1 s

Segments carries start and end times in seconds, which is what you need for subtitles, for jumping to a moment in a player, or for searching a recording by what was said.

How fast, and how good

On an Intel i7-13700 (24 threads), with the model already downloaded:

ModelDownload3 min 19 s of speechFaster than real time
whisper-small920 MB25 s8×
whisper-large-v32.9 GB124 s1.6×

whisper-small got almost all of the address right. It dropped one short phrase ("In the words of the prophet Isaiah"), which large-v3 kept, and both models misheard one name: astronaut Kalpana Chawla came out as "Kultna Shavla". That is typical of Whisper: common words are reliable, rare names are not. Check names, numbers and terms of art before you publish a transcript.

Use whisper-small by default, and large-v3 when accuracy matters more than time, for accented speech or for languages other than English.

Formats: WAV in, ffmpeg for the rest

TranscribeFile reads WAV files, at any sample rate, mono or stereo; Kjarni resamples to the 16 kHz mono Whisper expects. Kjarni 0.4.2 does not decode MP3, Ogg or FLAC, so convert those first:

ffmpeg -i meeting.mp3 -ar 16000 -ac 1 meeting.wav

If you already have the audio in memory, from a microphone or a decoder library such as NAudio, pass the samples directly:

float[] samples = ...;   // mono, -1.0 to 1.0
var transcript = transcriber.Transcribe(samples, sampleRate: 16000);

Languages and translation

Whisper is multilingual. Pass the language if you know it, or null to let the model detect it from the first seconds of audio:

using var detect = new Transcriber("whisper-small", language: null, quiet: true);
var t = detect.TranscribeFile("interview.wav");
Console.WriteLine(t.Language);   // e.g. "de"

translate: true turns speech in any supported language into English text in the same pass:

using var toEnglish = new Transcriber("whisper-small", translate: true, quiet: true);

What it does not do

  • No live streaming from a microphone. It transcribes files or sample buffers that you have. For near-live captions, transcribe chunks of a few seconds as they arrive.
  • No speaker labels. Whisper does not say who is speaking.
  • CPU by default. device: "gpu" uses a GPU through Vulkan, DirectX 12 or Metal.

FAQ

How do I convert speech to text in C# offline?

Install the Kjarni NuGet package and call new Transcriber("whisper-small").TranscribeFile("audio.wav"). The Whisper model runs inside your process; after the first download nothing goes over the network.

Does it need Python, whisper.cpp or an API key?

No. The package includes the native library for Windows x64, Linux x64 and arm64, and macOS on Apple Silicon.

Which audio formats does it read?

WAV, at any sample rate, mono or stereo. Convert MP3, Ogg, FLAC and video files to WAV with ffmpeg first.

How accurate is it?

whisper-small gets everyday English nearly word for word and makes mistakes on rare names. whisper-large-v3 is more accurate and about five times slower.

Getting it

Install:  dotnet add package Kjarni
NuGet:    https://www.nuget.org/packages/Kjarni
GitHub:   https://github.com/olafurjohannsson/kjarni

Kjarni runs the same engine on every platform: