---
title: "Stream Moonshine speech recognition in C#"
url: https://recursiveai.org/onespeech/cookbook/stream-with-moonshine/
description: "Stream English speech recognition with Moonshine. The snippet streams final text from a file; the full program also displays live partial results from PCM audio blocks."
version: "1.1.0"
---

# Stream Moonshine 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/stream-with-moonshine/stream-with-moonshine.zip)

Stream English text from a recording with Moonshine.

```shell
dotnet add package RecursiveAI.OneSpeech
```

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 example zip.

```csharp
using OneSpeech;

await foreach (var segment in
    Speech.TranscribeStream(
    "sample.flac", model:
    "moonshine-ai/moonshine-streaming-tiny"))
    Console.WriteLine(segment.Text);
```

First use downloads about 0.18 GB of Moonshine streaming-tiny publisher files and caches them and locally converted graphs in `~/.cache/onespeech/models` on Linux ([change the cache location](https://recursiveai.org/onespeech/cookbook/configure-speech-model-cache/)).

The snippet streams final segments; the full program below also prints live partials.

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

Unzip [the example](https://recursiveai.org/onespeech/cookbook/stream-with-moonshine/stream-with-moonshine.zip), then `dotnet run`.

## Full program

The zip includes `sample.flac`, the unmodified mono 16 kHz [LibriSpeech test-clean utterance 1089-134686-0001](https://www.openslr.org/12): “Stuff it into you, his belly counseled him.” Attribution: Vassil Panayotov, Guoguo Chen, Daniel Povey and Sanjeev Khudanpur (2015), [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/). Recognition output can differ from that reference.

The full program feeds the recording through a live session, not a microphone. `AudioBuffer` contains mono float PCM at 16 kHz; the sample is already mono.

For microphone input, see [Real-time speech recognition in C#](https://recursiveai.org/onespeech/cookbook/real-time-speech-recognition/); this example feeds a recording rather than capturing a device.

First use downloads about 0.18 GB of Moonshine streaming-tiny publisher files and caches them and locally converted graphs in `~/.cache/onespeech/models` on Linux ([change the cache location](https://recursiveai.org/onespeech/cookbook/configure-speech-model-cache/)).

```csharp
using OneSpeech;
using OneSpeech.Audio;
using OneSpeech.Catalog;
using OneSpeech.Moonshine;
using OneSpeech.Vad;

var resolved = await ModelStore.Default.PullAsync(
    "moonshine-ai/moonshine-streaming-tiny");
using var recognizer = MoonshineRecognizer.Load(resolved);
using var vad = new SileroVad();
using var live = recognizer.CreateLiveSession(vad, language: "en");
using var input = AudioFileSource.Open("sample.flac", CancellationToken.None);
var buffer = new float[1600];
int count;
while ((count = input.Read(buffer, CancellationToken.None)) != 0)
    Show(live.Push(new AudioBuffer(buffer.AsMemory(0, count), 16000)));
Show(live.Complete());

static void Show(IEnumerable<LiveEvent> updates)
{
    foreach (var update in updates)
        if (update is PartialEvent partial)
            Console.WriteLine("Partial: " + partial.Segment.Text);
        else if (update is FinalEvent final)
            Console.WriteLine("Final: " + final.Segment.Text);
}
```

Output:

```text
Partial: Duff
Partial: Duff it
Partial: Stuff it into
Partial: Stuff it into you.
Partial: Stuff it into you.
Partial: Stuff it into you, his
Partial: Stuff it into you, his belly.
Partial: Stuff it into you, his belly came.
Partial: Duff it into you, his belly counts.
Partial: Stuff it into you, his belly counselled.
Partial: Stuff it into you, his belly counseled him.
Final: Stuff it into you, his belly counseled him.
```

Invalid or corrupt audio is rejected; I/O and model acquisition failures propagate as exceptions.

## Variations

### Print only committed text

Replace the body of `Show` with:

```csharp
foreach (var update in updates)
    if (update is FinalEvent final)
        Console.WriteLine("Final: " + final.Segment.Text);
```

First use downloads about 0.18 GB of Moonshine streaming-tiny publisher files and caches them and locally converted graphs in `~/.cache/onespeech/models` on Linux ([change the cache location](https://recursiveai.org/onespeech/cookbook/configure-speech-model-cache/)).

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

## Related recipes

- [Transcribe an audio file](https://recursiveai.org/onespeech/cookbook/transcribe-audio-file/)
- [Run offline speech recognition](https://recursiveai.org/onespeech/cookbook/offline-speech-recognition/)
- [Get word timestamps](https://recursiveai.org/onespeech/cookbook/word-timestamps/)

## The short answer

Select `moonshine-ai/moonshine-streaming-tiny` for Moonshine live partials. Feed finite mono 16 kHz PCM to a live session, consume replacing `PartialEvent` hypotheses, and retain `FinalEvent` text when an utterance closes. The file-streaming snippet uses the same live controller but returns final segments rather than every intermediate event.

## How do I use Moonshine for live transcription in C#?

Load the streaming checkpoint explicitly, create a detector and a live session, and push the audio blocks in order. The full program reads the supplied mono FLAC in blocks of 1600 samples and wraps each block in `AudioBuffer`. This exercises the PCM input path using a recording; it does not capture a microphone or simulate the timing of a person speaking. For microphone input, your application must capture, downmix and resample before pushing blocks.

Keep the input memory valid until `Push` returns. A session consumes audio memory supplied by your application synchronously and returns immutable event snapshots. The sample reuses its buffer only after the call finishes. Do not push concurrently or interleave `Push` with another enumeration of that session. The recognizer and detector must outlive the session, which borrows them.

### Replace partial text, append final text

A partial is a replacement for the previous hypothesis with the same utterance ID, not another phrase to append. Its revision increases within that utterance. The console example prints every update so you can inspect changes; a caption UI should replace its provisional line instead. A final commits an immutable segment. An endpoint then closes that utterance and resets the revision sequence for the next one.

Call `Complete` on normal end-of-input to flush pending speech. Disposing a session is not a flush, and cancellation or early iterator disposal does not manufacture a final. The full program’s explicit completion is why the last utterance is retained even if the file ends before a long silence. Invalid input and processing failures have different lifecycle consequences: a failed processing step faults the session, so create a new session rather than retrying an ambiguously consumed block.

### Choose a streaming checkpoint

OneSpeech’s registered Moonshine family includes offline tiny/base and streaming tiny/small/medium. These are English models. Offline tiny/base use utterance mode with finals only; they do not gain live partials simply because you call a streaming API. Other languages, contextual biasing, translation and language detection are not supplied by this family.

Streaming state includes encoder caches local to the session. Completed encoder prefixes are retained while only a bounded suffix is re-encoded. A new endpoint resets those caches. The documented live controller has a shared 30-second limit for each continuous stretch of speech; that is separate from the low-level recognizer’s 60-second input limit. For longer recordings, use the bounded pipeline rather than increasing an input buffer without limit.

### Do not treat segment times as word times

Moonshine checkpoints do not produce native word timestamps. Unknown word boundaries and confidence remain null, with `TimingMethod.None`. VAD bounds mark retained speech segments; they are not exact boundaries for every word. Use a separate alignment stage if your application needs word highlighting or word-level subtitles.

The [Moonshine model contract](https://recursiveai.org/onespeech/docs/moonshine/) covers its input and cache bounds. The [live event contract](https://recursiveai.org/onespeech/docs/live-sessions/) explains audio-relative clocks, finalization and endpoint reasons. Neither an event’s arrival nor the block’s accepted-audio time is a wall-clock latency measurement.
