Transcribe sample.flac (in the zip). First use downloads several GB of models.
dotnet add package RecursiveAI.OneSpeechRequires .NET 10 (net10.0); .NET 9 and earlier aren’t supported.
using OneSpeech; var transcript = Speech.Transcribe( "sample.flac", language: "en"); Console.WriteLine(transcript.Text);
Unzip the example, then dotnet run.
Speech.Listen doesn’t capture a microphone; your app supplies audio (see real-time speech recognition).
Prints:
stuffered into you, his belly counseled him.This is the model’s real output on a real recording, mistake included; word.Text.Trim() removes a recognized word’s leading space.
The default recognizer is Whisper large-v3-turbo: its publisher weights are 1,617.8 MB (about 1.62 GB). This English run also loads Qwen3-ForcedAligner (1,835.5 MB of publisher weights) to time words and CAM++ English (29.4 MB) to group speakers; the built-in Silero detector finds speech. Converted runtime files take additional disk space. By default, models are cached in %LOCALAPPDATA%/OneSpeech/models on Windows, ~/Library/Caches/OneSpeech/models on macOS, or $XDG_CACHE_HOME/onespeech/models on Linux (falling back to ~/.cache/onespeech/models). ONESPEECH_MODEL_CACHE overrides that location. See Model files and pre-fetch and the download sizes.
The full program
The snippet at the top prints the recognized text. The full program also reports language, audio duration and the number of recognized words.
The input sample.flac is the unmodified LibriSpeech test-clean recording 1089-134686-0001: “stuff it into you his belly counselled him”. It ships in the zip under CC BY 4.0, credited to Vassil Panayotov, Guoguo Chen, Daniel Povey and Sanjeev Khudanpur (LibriSpeech).
using OneSpeech; Transcript transcript = Speech.Transcribe("sample.flac", language: "en"); Console.WriteLine(transcript.Text); Console.WriteLine($"{transcript.Language}: {transcript.Duration.TotalSeconds:0.00} seconds"); Console.WriteLine($"{transcript.Words.Count()} words");
dotnet run prints:
stuffered into you, his belly counseled him. en: 3.28 seconds 7 words
This is the recognizer’s actual output, including its “stuffered” mistake and leading spaces. Missing models download on first use; subsequent runs reuse verified cache files.
Invalid encoded audio throws UnsupportedAudioException; model download or integrity failures are reported as typed model errors.
Variations
Save structured JSON. Replace Program.cs with this to keep word timing and speaker turns as well as text. Use the same sample.flac from the zip.
using OneSpeech; Transcript transcript = Speech.Transcribe("sample.flac", language: "en"); File.WriteAllText("transcript.json", transcript.ToJson()); Console.WriteLine($"Saved {transcript.Words.Count()} words");
Saved 7 wordsRelated recipes
- Transcribe an audio file in C#
- Run Whisper in C# without native libraries
- Generate SRT subtitles from audio in C#
- Get speech-to-text word timestamps in C#
The short answer
For speech to text in C#, install RecursiveAI.OneSpeech, pass a recording to Speech.Transcribe, and read Transcript.Text. OneSpeech runs recognition in your .NET process using open-weight models. The file default is Whisper large-v3-turbo. The result also holds segments, timed words and anonymous speaker turns; you don’t need to construct those from the text yourself.
This page starts with a recorded file rather than a microphone so you can reproduce the result. The sample’s recognition error is visible: it hears “stuff it” as “stuffered”. Review a transcript against its recording before publishing it. A successful call means that transcription completed, not that every word is correct.
Is speech to text free in C#?
Yes. OneSpeech is free and open source under MIT or Apache-2.0, at your choice. The core NuGet package uses OneAudio for decoding and resampling and OneModel for inference. There is no speech-service account or API key to configure for this example. The model licenses are separate from the C# library’s license: the default recognizer is MIT, while the English models for word timing and speaker grouping used in this run are Apache-2.0. The model table lists those licenses by task.
Installing the library is not the same as installing all its models. The small example download contains the program and audio, not the weights. Budget disk space and memory for those as well as for your application. Even selecting a smaller recognizer does not eliminate its extra models for word timing and speaker grouping.
Can C# speech recognition work offline?
Yes, once all the models needed for the call are present. OneSpeech checks cached model files against the pinned catalog and reuses verified files without network requests. Missing model files download on first use. Initial setup can also convert verified publisher weights locally, using additional temporary disk space. The first transcription can therefore take appreciably longer than later ones.
Prepare the recognizer and the extra models for word timing and speaker grouping before disconnecting a machine. The ONESPEECH_MODEL_CACHE environment variable selects a different cache location; use a trusted per-user directory. Copying only the application executable is not enough for an offline installation. The first-use documentation explains which models each operation can download.
How do I add speech recognition to a C# application?
Use the file call above when a recording is already available. It returns a complete document and owns and disposes its models for that call. Save Text for a human-readable transcript or use the JSON variation when another part of your application needs word timing and speaker information too. JSON preserves the document structure instead of flattening it to a string.
For live input, OneSpeech has a different entry point, Speech.Listen. It consumes mono 16 kHz PCM supplied by your application; it does not capture a microphone. A desktop or mobile app must provide capture separately. Don’t put a synchronous file transcription on a UI event handler and expect the interface to stay responsive: CPU inference is synchronous, and missing-model acquisition can block the file call. Choose the recorded-file or live path according to what your app actually receives, not according to the file extension of a temporary buffer.