---
title: "Speech recognition for .NET"
url: https://recursiveai.org/onespeech/docs/overview/
description: "OneSpeech transcribes audio, times words, labels speakers and supports live sessions in managed .NET 10. Open-weight models run on OneModel; OneAudio…"
version: "1.0.0"
source: https://github.com/recursive-ai-oss/onespeech-csharp/blob/63002de486786163a0a7800a51811e76d511fae6/README.md
---

# Speech recognition for .NET

OneSpeech transcribes audio, times words, labels speakers and supports live sessions in managed .NET 10. Open-weight models run on OneModel; OneAudio decodes and resamples audio. No native inference runtime is required.

OneSpeech is unaffiliated with other products or companies named OneSpeech.

See [measured results and limitations](https://recursiveai.org/onespeech/docs/results/) before choosing a model. Licensed under [MIT](https://github.com/recursive-ai-oss/onespeech-csharp/tree/63002de486786163a0a7800a51811e76d511fae6/LICENSE-MIT) OR [Apache-2.0](https://github.com/recursive-ai-oss/onespeech-csharp/tree/63002de486786163a0a7800a51811e76d511fae6/LICENSE-APACHE).

## Transcribe, stream and align

```csharp
using OneSpeech;

Transcript transcript = Speech.Transcribe("meeting.m4a", language: "en");
Console.WriteLine(transcript.Text);
File.WriteAllText("meeting.json", transcript.ToJson());

await foreach (Segment segment in Speech.TranscribeStream("lecture.wav"))
    Console.WriteLine(segment.Text);

Transcript aligned = Speech.Align("short.wav", "The words that were spoken.", language: "en");
```

`Transcribe` returns a timed, diarized, language-tagged `Transcript`: inspect `Words`, `Segments`, `Turns` and `Models`. Failed word timing is **null**, never invented. A speaker label is anonymous and local to the document, not a person's identity. `TranscribeStream` yields final segments in bounded chunks; it does **not** perform whole-file speaker assignment. Standalone `Align` accepts at most 60 seconds. All verbs take an optional final `cancellationToken`; path and encoded-stream I/O have `*Async` forms. CPU inference remains synchronous within those calls. Language tags are case-insensitive: `en-US`, `EN`, `eng` and `English` resolve to `en`; unsupported hints throw `CapabilityNotSupportedException`.

`Speech.Listen` consumes caller-supplied mono 16 kHz PCM as an `IAsyncEnumerable<AudioBuffer>`. It emits replacing partials, immutable finals, endpoints and speaker turns on one audio clock. It does not capture a microphone. See [live sessions](https://recursiveai.org/onespeech/docs/live-sessions/).

For speaker recognition, use `Speech.Enroll("alice", "enrollment.wav")`, `Speech.Identify("query.wav")` and `Speech.Verify("first.wav", "second.wav")`; encoded streams have `*Async` twins. Enrollments live only in process memory: `Speech.ClearSpeakers()` removes them and releases speaker models. File transcription adds matching enrolled names to speaker labels without changing their document-local IDs; unknown clusters keep anonymous labels. Obtain consent and check local biometric law before using these verbs. See [speaker recognition](https://recursiveai.org/onespeech/docs/speaker-recognition/) for calibrated thresholds, limitations and the low-level registry.

### Overlapping speech

**The default clustering diarizer does not detect overlapping speech.** For an overlap-aware upgrade (up to eight speakers), load the named tier-2 diarizer in one call: `await NemotronDiarizer.LoadAsync()`, then pass it to your transcription pipeline. This does not separate simultaneous voices. See the [complete replacement example](https://recursiveai.org/onespeech/docs/diarization/#voice-clustering-and-overlap-limitation) and [Nemotron guide](https://recursiveai.org/onespeech/docs/nemotron-diarization/). It is never downloaded as a default.

## First-use downloads and offline preparation

The `model` argument selects a role, **not the entire pipeline**. Transcription also needs a speaker embedder and, without native timings, companion aligners. Even Whisper tiny can require several GB on first use. Silero VAD is embedded. See the [per-verb download table](https://recursiveai.org/onespeech/docs/model-downloads/) for exact IDs, sizes and conditional dependencies.

`await Speech.PrefetchAsync(language: "en", model: "openai/whisper-tiny");` prepares the complete companion set, including timing fallbacks, without running inference. For live use, pass `model: "moonshine-ai/moonshine-streaming-tiny"`. Every downloadable family exposes `Load(ResolvedModel, …)` and `LoadAsync(store, model, …)`; [composition examples](https://recursiveai.org/onespeech/docs/loading/) use documented role types.

## Contextual biasing

Biasing is a low-level request; `Speech.*` stays unchanged. A loaded recognizer
returns a borrowed wrapper with its own immutable phrase list:

```csharp
using var model = OneSpeech.Whisper.WhisperRecognizer.Load(modelDirectory);
SpeechRecognizer biased = model.WithPhrases([new Phrase("Acme Photonics"), new Phrase("Marisol")]);
Transcript result = biased.Transcribe(audio, "en", cancellationToken);
// The same biased recognizer can be passed to VadTranscriptionPipeline.
```

Keep the owning model alive until the wrappers and their sessions are finished.
Wrappers do not own or dispose it; the model's concurrency limits still apply.
Calling `WithPhrases` again replaces the list; an empty list returns the original
unbiased recognizer. The original recognizer is never modified.

Whisper conditions each window with ordinary prompt tokens. Qwen3-ASR appends
phrases to its existing system context. These prompt methods refuse explicit
numeric weights rather than pretending to apply a score bonus. Parakeet TDT
uses token-level shallow fusion; `new Phrase("Acme", 0.5)` supplies a natural-log
bonus per token (null defaults to 1). Custom CTC/transducer graphs use the same
fusion after the caller supplies `TokenVocabulary.WithPhraseEncoder` for their
exact tokenizer. CTC biasing uses prefix beam search, even if the unbiased
recognizer is greedy. Moonshine does not support biasing: a nonempty request
throws `CapabilityNotSupportedException`, as declared by `Capabilities.Biasing`.

Biasing is not a spelling replacement or a guarantee that a term will be spoken.
It can introduce false alarms. Whisper prompt conditioning in particular can
substantially increase overall errors and repeat absent terms even when recall
of requested terms improves; it is not a safe default. Check both phrase recall and overall WER, with
absent-phrase controls, on your audio. See [contextual scoring and bounds](https://recursiveai.org/onespeech/docs/contextual-biasing/)
for prompt budgets, weights, decoder semantics and the repeatable evaluation.

## Packages

| Package | Purpose |
|---|---|
| `RecursiveAI.OneSpeech` | Recognition, alignment, diarization, speakers, live sessions, algorithms and JSON |
| `RecursiveAI.OneSpeech.Media` | Container input, SRT/VTT/ASS/TTML and caption muxing using OneMedia |
| `RecursiveAI.OneSpeech.Models.CamPlusEnglish` | Optional offline-at-restore CAM++ English weights |
| `RecursiveAI.OneSpeech.Models.MoonshineTiny`, `.MoonshineBase`, `.MoonshineStreamingTiny` | Optional offline-at-restore Moonshine weights |
| `RecursiveAI.OneSpeech.Cli` | The `onespeech` dotnet tool |

Models not supplied by a package download on first use; CPU runtime graphs are built and verified in the per-user cache. An offline model package covers only that model, not every role used by `Transcribe`. See [cache, pre-fetch and offline use](https://recursiveai.org/onespeech/docs/models/) and [CLI and model packages](https://recursiveai.org/onespeech/docs/cli-and-model-packages/).

## Models by role

| Role | Models | Tier / licence | Selection |
|---|---|---|---|
| File recognition | Whisper tiny/base/small/large-v3; large-v3-turbo | 1 / Apache-2.0; turbo MIT | **turbo default** |
| File recognition | Qwen3-ASR 0.6B / 1.7B | 1 / Apache-2.0 | By name; 1.7B accuracy option |
| Live / edge recognition | Moonshine English tiny/base and streaming tiny/small/medium | 1 / MIT | **streaming-tiny live default** |
| Recognition | Parakeet-TDT-0.6B-v3 | 2 / CC-BY-4.0 | By name only |
| Timing | Qwen3-ForcedAligner-0.6B; omniASR-CTC-300M | 1 / Apache-2.0 | Timing policy; see [word timing](https://recursiveai.org/onespeech/docs/word-timing/) |
| Voice activity | Silero VAD 6.2.3 | 1 / MIT | Embedded |
| Diarization | Silero + CAM++ + spectral/AHC clustering | 1 / MIT + Apache-2.0 | Default; no overlap detection |
| Diarization | Nemotron 3 Diarization | 2 / OpenMDW-1.1 | Explicit overlap-aware upgrade |
| Segmentation | pyannote segmentation-3.0 / community-1 | 2 / MIT gated / CC-BY-4.0 gated | Local segmentation-3.0 + our overlap-aware clustering; community-1 reference only |
| Speaker embeddings | CAM++ English/Chinese; ECAPA-TDNN | 1 / Apache-2.0 | CAM++ English default; Chinese for Chinese speech |

Tier 1 is ungated MIT/Apache-2.0/BSD and may be redistributed and selected by default. Tier 2 loads only from pinned publisher files, never from our mirror or model packages, never by default. Local conversion stays in your cache. Licence, revision, file digests and attribution travel in `ModelInfo`. Only the listed licence-cleared Moonshine checkpoints are supported; Community License checkpoints are excluded. See [gated models](https://recursiveai.org/onespeech/docs/tier-2-models/) and [third-party notices](https://recursiveai.org/onespeech/docs/third-party-notices/).

## Privacy and consent

Speaker embeddings are biometric data. Obtain informed consent, limit retention and access, and check applicable law, including **Illinois BIPA** and **GDPR Article 9**, before enrollment, identification or verification. The library does not persist embeddings; applications remain responsible for audio, transcripts, logs and any copies they retain. This is not legal advice.

## Guides

- [Long files](https://recursiveai.org/onespeech/docs/long-files/), [live events](https://recursiveai.org/onespeech/docs/live-sessions/), [word timing](https://recursiveai.org/onespeech/docs/word-timing/)
- [Diarization](https://recursiveai.org/onespeech/docs/diarization/), [speaker recognition](https://recursiveai.org/onespeech/docs/speaker-recognition/), [biasing](https://recursiveai.org/onespeech/docs/biasing/)
- [Custom CTC/transducer graphs](https://recursiveai.org/onespeech/docs/custom-models/), [model cache](https://recursiveai.org/onespeech/docs/models/), [tier-2 access](https://recursiveai.org/onespeech/docs/tier-2-models/)
- [Transcript JSON schema v1](https://recursiveai.org/onespeech/docs/transcript-json/), [containers and captions](https://recursiveai.org/onespeech/docs/media/), [CLI](https://recursiveai.org/onespeech/docs/cli-and-model-packages/)
- Migration: [Whisper.net](https://recursiveai.org/onespeech/docs/migrate-whisper-net/), [sherpa-onnx .NET](https://recursiveai.org/onespeech/docs/migrate-sherpa-onnx/), [Azure Speech SDK](https://recursiveai.org/onespeech/docs/migrate-azure-speech/), [WhisperX concepts](https://recursiveai.org/onespeech/docs/migrate-whisperx/)

## Build and sample checks

Set `ONESPEECH_ARTIFACTS` to a directory outside the checkout. Run `dotnet restore`, then `dotnet build --no-restore`. Run `dotnet run --project tests/DocsSamples --no-build -- .` to exercise the no-download document examples and check local documentation links. Model-backed examples are compile-checked without downloading weights; run them with your own consented audio and fetched models.
