---
title: "Getting started with OneSpeech"
url: https://recursiveai.org/onespeech/docs/getting-started/
description: "Install OneSpeech for .NET 10, transcribe a recording with timed words and speaker turns, run a live session from packaged models with no network, and use…"
version: "1.0.0"
---

# Getting started with OneSpeech

Install OneSpeech for .NET 10, transcribe a recording with timed words and speaker turns, run a live session from packaged models with no network, and use the `onespeech` command-line tool. Every program below was run on the 1.0.0 packages from NuGet, and the output shown is what it printed.

## Install

OneSpeech targets `net10.0`, so you need the .NET 10 SDK.

```shell
dotnet new console -o SpeechStart
cd SpeechStart
dotnet add package RecursiveAI.OneSpeech
```

The core package depends on `RecursiveAI.OneAudio` (decoding and resampling) and `RecursiveAI.OneModel` (inference) only, and none of the three contains native code. Silero VAD is embedded in it. Every other model either comes in its own package or downloads on first use into a per-user cache, where it is verified against the catalog’s size and SHA-256.

The samples use a LibriSpeech test-clean recording that ships in the OneSpeech repository as `testdata/1089-134686-0001.flac` (CC BY 4.0; about three seconds of one speaker). LibriSpeech’s reference text is “stuff it into you his belly counselled him”.

## Transcribe a file

`Program.cs`:

```csharp
using OneSpeech;

Transcript transcript = Speech.Transcribe("1089-134686-0001.flac", language: "en");

Console.WriteLine(transcript.Text);
Console.WriteLine($"{transcript.Language}, {transcript.Duration.TotalSeconds:0.00} s");

foreach (Word word in transcript.Words.Take(6))
    Console.WriteLine($"{word.Start?.TotalSeconds:0.00}–{word.End?.TotalSeconds:0.00}  {word.Text}  ({word.TimingMethod})");

foreach (Turn turn in transcript.Turns)
    Console.WriteLine($"{turn.Speaker.Id}: {turn.Start.TotalSeconds:0.00}–{turn.End.TotalSeconds:0.00}");

foreach (ModelInfo model in transcript.Models)
    Console.WriteLine($"{model.Id} ({model.License})");

File.WriteAllText("transcript.json", transcript.ToJson());
```

`dotnet run` prints:

```text
 stuffered into you, his belly counseled him.
en, 3.28 s
0.55–0.87   stuffered  (ForcedAligner)
0.87–1.19   into  (ForcedAligner)
1.19–1.43   you,  (ForcedAligner)
1.59–1.75   his  (ForcedAligner)
1.75–2.15   belly  (ForcedAligner)
2.15–2.63   counseled  (ForcedAligner)
speaker-1: 0.20–3.09
silero/vad-v6.2.3 (MIT)
openai/whisper-large-v3-turbo (MIT)
Qwen/Qwen3-ForcedAligner-0.6B (Apache-2.0)
iic/speech_campplus_sv_en_voxceleb_16k (Apache-2.0)
```

This is the default file model’s real output, mistake included: Whisper large-v3-turbo heard “stuff it” as “stuffered”. Word text keeps Whisper’s leading space.

`Models` lists everything that took part. The `model` argument selects one role, not the whole pipeline: turbo recognized the words, Qwen3-ForcedAligner timed them, and CAM++ English labeled the speaker. The first run downloaded and converted all of them, several GB, and took about two minutes; later runs reuse the cache. To prepare a machine ahead of time, call `await Speech.PrefetchAsync(language: "en", model: "openai/whisper-large-v3-turbo")`. [First-use models](https://recursiveai.org/onespeech/docs/model-downloads/) lists every download by verb.

A speaker label such as `speaker-1` is anonymous and local to this transcript, not a person’s identity. Words that can’t be timed have a null `Start` and `End`; a time is never invented. `transcript.json` is the [Transcript JSON schema, version 1](https://recursiveai.org/onespeech/docs/transcript-json/).

## A live session, from packages, offline

`Speech.Listen` reads mono 16 kHz PCM that you supply and returns partials, finals, endpoints and speaker turns on one audio clock. It doesn’t capture a microphone: an app pushes its own capture. Here the blocks come from the same file, 100 ms at a time.

The live default, Moonshine streaming tiny, and the CAM++ English speaker embedder each ship as a NuGet package, so this project needs no download at runtime:

```shell
dotnet new console -o SpeechLive
cd SpeechLive
dotnet add package RecursiveAI.OneSpeech
dotnet add package RecursiveAI.OneSpeech.Models.MoonshineStreamingTiny
dotnet add package RecursiveAI.OneSpeech.Models.CamPlusEnglish
```

`Program.cs`:

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

await foreach (LiveEvent update in Speech.Listen(Blocks("1089-134686-0001.flac"), language: "en"))
{
    switch (update)
    {
        case PartialEvent partial:
            Console.WriteLine($"{partial.AudioTime.TotalSeconds:0.0}s  draft {partial.Revision}: {partial.Segment.Text}");
            break;
        case FinalEvent final:
            Console.WriteLine($"{final.AudioTime.TotalSeconds:0.0}s  final: {final.Segment.Text}");
            break;
        case EndpointEvent endpoint:
            Console.WriteLine($"{endpoint.AudioTime.TotalSeconds:0.0}s  endpoint ({endpoint.Reason})");
            break;
        case SpeakerTurnEvent turn:
            Console.WriteLine($"{turn.AudioTime.TotalSeconds:0.0}s  speaker {turn.Turn.Speaker.Id} {turn.Turn.Start.TotalSeconds:0.00}–{turn.Turn.End.TotalSeconds:0.00}");
            break;
    }
}

static async IAsyncEnumerable<AudioBuffer> Blocks(string path)
{
    using AudioFileSource source = AudioFileSource.Open(path, CancellationToken.None);
    while (true)
    {
        float[] block = new float[1600 * source.Channels]; // 100 ms at 16 kHz
        int read = source.Read(block, CancellationToken.None);
        if (read == 0) yield break;
        yield return new AudioBuffer(block.AsMemory(0, read), AudioFileSource.OutputSampleRate, source.Channels);
        await Task.Yield();
    }
}
```

Run with an empty model cache and no network at all, it prints:

```text
0.9s  draft 1: Duff
1.1s  draft 2: Duff it
1.3s  draft 3: Stuff it into
1.5s  draft 4: Stuff it into you.
1.7s  draft 5: Stuff it into you.
1.9s  draft 6: Stuff it into you, his
2.1s  draft 7: Stuff it into you, his belly.
2.3s  draft 8: Stuff it into you, his belly came.
2.5s  draft 9: Duff it into you, his belly counts.
2.7s  draft 10: Stuff it into you, his belly counselled.
2.9s  draft 11: Stuff it into you, his belly counseled him.
3.1s  speaker speaker-1 0.55–2.05
3.1s  speaker speaker-1 2.05–2.91
3.1s  final: Stuff it into you, his belly counseled him.
3.1s  endpoint (Silence)
```

Each draft replaces the one before it; the final is immutable. `AudioTime` is how much audio had been accepted when the event became available, not a word boundary. On build, the model packages copy their files into `onespeech-models/` beside the executable; keep that directory when you deploy. A model package covers only its own model: `Speech.Transcribe` on files can still need an aligner that isn’t packaged.

## The command-line tool

`RecursiveAI.OneSpeech.Cli` is the `onespeech` .NET tool:

```shell
dotnet new tool-manifest
dotnet tool install RecursiveAI.OneSpeech.Cli
dotnet onespeech transcribe 1089-134686-0001.flac --language en --format srt
```

It prints:

```text
1
00:00:00,546 --> 00:00:02,786
 stuffered into you, his belly counseled
 him.
```

`--format` takes `text`, `json`, `srt` or `vtt`, and `--output` writes a file instead. `onespeech --help` lists `align`, `diarize`, `detect-language`, `models list` and `models pull`, and the speaker commands. For containers such as MP4 and MKV, and for ASS and TTML captions in your own code, add `RecursiveAI.OneSpeech.Media` and call `MediaInput.Register()` once; see [Containers and captions](https://recursiveai.org/onespeech/docs/media/).

## Speakers

The default diarizer labels who spoke when, but it doesn’t detect overlapping speech. `NemotronDiarizer` is an overlap-aware upgrade for up to eight speakers; it is a tier-2 model that loads only from its publisher’s files, by name, never by default. See [Diarization](https://recursiveai.org/onespeech/docs/diarization/).

`Speech.Enroll`, `Speech.Identify` and `Speech.Verify` recognize enrolled voices; enrollments live only in process memory. In the README’s words: “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.” See [Speaker recognition](https://recursiveai.org/onespeech/docs/speaker-recognition/).

## Where next

- [Overview](https://recursiveai.org/onespeech/docs/overview/): the README, with the models by role and their licences.
- [Transcribing files](https://recursiveai.org/onespeech/docs/transcribing-files/) and [Live recognition and file streaming](https://recursiveai.org/onespeech/docs/live-sessions/).
- [Word timing](https://recursiveai.org/onespeech/docs/word-timing/), and [Contextual phrase biasing](https://recursiveai.org/onespeech/docs/biasing/).
- [Model files and pre-fetch](https://recursiveai.org/onespeech/docs/models/) for caches and offline hosts.
- [Measured results and limits](https://recursiveai.org/onespeech/docs/results/): every row, with its reference, interval and verdict.
