Recursive AI

Generate SRT subtitles from audio in C#

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

Caption sample.flac (in the zip). First use downloads several GB of models.

Terminal
dotnet add package RecursiveAI.OneSpeech.Media

Requires .NET 10 (net10.0); .NET 9 and earlier aren’t supported.

C#
using OneSpeech;
using OneSpeech.Media;
var transcript = Speech.Transcribe(
    "sample.flac", language: "en");
File.WriteAllText(
    "subtitles.srt", transcript.ToSrt());

Unzip the example, then dotnet run.

Writes subtitles.srt with the cue printed below.

The full program

The snippet at the top writes subtitles.srt. The full program prints the exact contents of that file as well.

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).

C#
using OneSpeech;
using OneSpeech.Media;
 
Transcript transcript = Speech.Transcribe("sample.flac", language: "en");
string srt = transcript.ToSrt();
File.WriteAllText("subtitles.srt", srt);
Console.Write(srt);

dotnet run prints:

Text
1
00:00:00,546 --> 00:00:02,786
 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.

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.

If a caption has no known time bounds, ToSrt() throws CapabilityNotSupportedException instead of inventing a duration.

Variations

Remove caption-line padding. Leading spaces are visible text padding, not invalid SRT syntax or changed timing; trim each exported line if you don’t want that padding.

C#
using OneSpeech;
using OneSpeech.Media;
 
var transcript = Speech.Transcribe("sample.flac", language: "en");
string trimmed = string.Join("\n", transcript.ToSrt()
    .Split('\n').Select(line => line.Trim()));
File.WriteAllText("subtitles.srt", trimmed);
Console.Write(trimmed);
Text
1
00:00:00,546 --> 00:00:02,786
stuffered into you, his belly counseled
him.

The short answer

To generate SRT subtitles from audio in C#, transcribe the recording with OneSpeech, then call the ToSrt() extension from RecursiveAI.OneSpeech.Media and save its string. The extension builds timed caption cues from the transcript; you don’t need to format timestamp strings or divide the recording into arbitrary equal-length blocks yourself.

The sample writes a real SRT file and prints its contents. It includes a recognition error from the audio run, not a corrected reference transcript. Proofread the caption text and watch it against the recording before delivering it. You can remove leading caption spaces after export without running recognition a second time.

How do I generate an SRT file from audio in C#?

The full program shows the two stages: recognition produces a transcript; caption export turns it into a subtitle string. For this FLAC input, installing the Media package supplies the caption extensions and the core library transitively. No decoder registration is needed for this FLAC recording or for SRT export.

The caption policy starts with each recognized stretch of speech. When every word has a time, it starts a new caption when the speaker changes, the text reaches 84 character units, or it spans six seconds. It wraps lines at 42 character units without breaking a word. These counts use .NET’s UTF-16 character units. A single long word or a word with a long time interval can exceed those targets. The sample uses these built-in rules without configuring them.

If a segment has an untimed word, it stays one wrapped cue because there is no measured evidence for extra split boundaries. Completely untimed cues make the string export throw CapabilityNotSupportedException. The writer does not fabricate a duration merely to make an SRT file possible. Correct or align the affected transcript before retrying that export.

Can I generate SRT subtitles for free?

Yes. The OneSpeech library and the Media package are open source under MIT or Apache-2.0. The example needs no subscription or speech-service key. Model weights have their own licenses; the models used here are MIT or Apache-2.0, as listed in the overview.

The initial run downloads missing model files and may convert verified publisher weights locally. Plan for several GB of disk use and sufficient memory; the small project zip is not a bundle of the recognition weights. Once verified files are cached, the transcription path can reuse them without network requests. Caption export itself downloads no models. It only consumes the transcript you already have.

How do I convert text to SRT?

Plain text alone lacks the start and end times an SRT cue needs. If you have the original audio, speech recognition supplies timed text, as above. If you already have a corrected transcript and need to locate its words in a recording, forced alignment is the relevant task: it maps supplied text to audio instead of recognizing new wording. OneSpeech has a separate alignment API, whose standalone helper accepts at most 60 seconds and can leave unalignable words untimed.

SRT keeps cue text and cue times, but not per-word times or speaker identities. Use JSON to retain the full transcript for later processing. Subtitle interchange is not lossless transcript storage. Exporting another caption format cannot repair a recognition error or turn uncertain timing into measured ground truth.

All onespeech recipes