---
title: "Real-time speech recognition in C#"
url: https://recursiveai.org/onespeech/cookbook/real-time-speech-recognition/
description: "Recognize English speech from PCM blocks in C# with OneSpeech live sessions. Free, open source, with a runnable file-replay example."
version: "1.0.0"
---

# Real-time speech recognition in C#

**OneSpeech** is a free, open-source speech recognition library for .NET from Recursive AI. MIT or Apache-2.0, no native dependencies.

[NuGet](https://www.nuget.org/packages/RecursiveAI.OneSpeech) · [GitHub](https://github.com/recursive-ai-oss/onespeech-csharp) · [Docs](https://recursiveai.org/onespeech/docs/) · [Download example (.zip)](https://recursiveai.org/onespeech/cookbook/real-time-speech-recognition/real-time-speech-recognition.zip)

Recognize speech as PCM audio blocks arrive. OneSpeech accepts your audio stream; it does not capture a microphone. The example replays a short English recording through a live session.

```shell
dotnet add package RecursiveAI.OneSpeech
dotnet add package RecursiveAI.OneSpeech.Models.MoonshineStreamingTiny
dotnet add package RecursiveAI.OneAudio
```

Requires [.NET 10](https://dotnet.microsoft.com/download/dotnet/10.0) (net10.0); .NET 9 and earlier aren’t supported.

`sample.flac` is included in the zip; you can substitute your own 16 kHz mono recording. VAD (voice activity detection) finds the speech parts, Silero is the VAD model, and Moonshine is the English recognition model.

```csharp
using OneSpeech;
using OneSpeech.Moonshine;
using OneSpeech.Vad;
var clip = OneAudio.Audio.Load("sample.flac");
using var recognizer = await MoonshineRecognizer.LoadAsync();
using var vad = new SileroVad();
using var session = recognizer.CreateLiveSession(vad, "en");
var updates = session.Push(new AudioBuffer(clip.Samples, 16000));
foreach (var final in updates.Concat(session.Complete()).OfType<FinalEvent>())
    Console.WriteLine(final.Segment.Text);
```

Prints:

```text
Stuff it into you, his belly counseled him.
```

Unzip [the example](https://recursiveai.org/onespeech/cookbook/real-time-speech-recognition/real-time-speech-recognition.zip), then `dotnet run`.

## Full program

The zip includes `sample.flac`: a short English reading, “Stuff it into you, his belly counseled him.” Source: [LibriSpeech test-clean utterance 1089-134686-0001](https://www.openslr.org/12), Vassil Panayotov, Guoguo Chen, Daniel Povey and Sanjeev Khudanpur (2015), [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/). The FLAC is unmodified, mono, 16 kHz.

The snippet at the top uses `Push` and `Complete` to submit the whole short clip; the full program uses `Speech.Listen` to consume successive PCM blocks and complete automatically at end-of-input. Both use the same live-session machinery and print only final updates.

For a microphone, follow [Record microphone audio in C#](https://recursiveai.org/oneaudio/cookbook/record-microphone/) with OneAudio’s `DeviceStreamOptions` set to `SampleRate = 16000` and `Channels = 1`. Its captured float PCM can feed `AudioBuffer`: in an application-owned polling loop, read the available frames, wrap only those samples in a 16 kHz mono buffer, and push them into the session. Drain capture consistently and perform recognition outside the device callback; real capture needs an available device and operating-system permissions.

```csharp
using OneSpeech;
using OneSpeech.Audio;

await foreach (var update in Speech.Listen(Blocks(), language: "en"))
    if (update is FinalEvent final)
        Console.WriteLine(final.Segment.Text);

static async IAsyncEnumerable<AudioBuffer> Blocks()
{
    using var input = AudioFileSource.Open("sample.flac", CancellationToken.None);
    var buffer = new float[1600]; // The sample is mono; 100 ms blocks.
    int count;
    while ((count = input.Read(buffer, CancellationToken.None)) != 0)
    {
        yield return new AudioBuffer(buffer.AsMemory(0, count), 16000);
        await Task.Yield();
    }
}
```

Output:

```text
Stuff it into you, his belly counseled him.
```

Unsupported or corrupt input throws `UnsupportedAudioException`; file-system errors remain I/O errors.

## Variations

### Mark final text

Inside the `FinalEvent` branch, prefix committed text:

```csharp
Console.WriteLine($"Final: {final.Segment.Text}");
```

Output:

```text
Final: Stuff it into you, his belly counseled him.
```

## Related recipes

- [Label speakers in audio with C#](https://recursiveai.org/onespeech/cookbook/speaker-diarization/)
- [Transcribe an MP4 video in C#](https://recursiveai.org/onespeech/cookbook/transcribe-video/)
- [Offline speech recognition in C#](https://recursiveai.org/onespeech/cookbook/offline-speech-recognition/)

## The short answer

Create a live recognition session, push mono 16 kHz PCM blocks into it and consume its updates. Call `Complete` at normal end-of-input to flush trailing speech. The downloadable program uses `Speech.Listen`, which performs that completion automatically when its input sequence ends normally. It replays a real file so you can run it without a microphone API.

## How do I do real-time speech recognition in C#?

Separate audio capture from recognition. Your application opens the microphone, converts its samples to mono 16 kHz PCM and supplies blocks. OneSpeech’s [live session API](https://recursiveai.org/onespeech/docs/live-sessions/) accepts those blocks; it does not choose an audio device or request operating-system microphone permission.

The program’s `Blocks` iterator shows the input boundary: each yielded `AudioBuffer` contains the next portion of the recording. Replace that file reader with your capture source when integrating it into an app. The file iterator yields without wall-clock pacing, so this example is a replay of the live API, not a microphone demo or a latency measurement.

Recognition is synchronous CPU work inside the session. An async input sequence lets the library await audio arrival; it does not turn inference into a background task. Arrange your application’s audio and UI work accordingly. Sessions are single-use and not thread-safe, so do not interleave a push loop with another consumer of the same session.

## Is real-time speech recognition free?

OneSpeech is free and open source under MIT or Apache-2.0. The selected Moonshine streaming tiny weights are a tier-1 model; the [model table](https://recursiveai.org/onespeech/docs/overview/#models-by-role) lists the model licenses separately from the library license. The example does not call a paid recognition endpoint or require an API key.

“Streaming” describes the input and update behavior, not a promise that your machine will always keep up. Test the model with the block size and hardware you will actually deploy. The model package makes weights available during restore; it does not remove local model conversion and loading work on first use.

## What models support real-time speech to text?

The example selects Moonshine streaming tiny through the library’s live default. Streaming checkpoints keep session-local encoder caches and emit replacing partials as well as final segments. The [Moonshine guide](https://recursiveai.org/onespeech/docs/moonshine/) describes the supported checkpoint families. These checkpoints recognize English; choose a model with the language capabilities your application needs rather than passing an unsupported hint.

An offline-family recognizer can also create a VAD-endpointed live session, but that path emits finals only. Do not build an interface that assumes every recognizer produces partials.

## How do I display streaming speech-to-text results?

The sample prints only `FinalEvent` text so its output is short and reproducible. A partial is a draft that replaces the earlier draft for the same utterance, not new text to append. A final commits an immutable segment. Keep committed text and the current draft separate in your UI.

All event times are relative to accepted audio, not wall time. A large push can make several updates available at the same audio time. Neither that timestamp nor a VAD endpoint invents a word boundary. Cancellation and early iterator disposal do not flush pending speech; normal completion does. Dispose the session before the borrowed recognizer and detector.
