Install
| Product | Versions |
|---|---|
| .NET |
README
ElBruno.Whisper
Run local Whisper speech-to-text in .NET π€
Transcribe audio to text in .NET using OpenAI's Whisper model. Powered by ONNX Runtime with automatic model download from HuggingFace.
Features
- π¦ Automatic model download β models are fetched from HuggingFace on first use
- π Multiple model sizes β tiny β base β small β medium β large (pick your speed/accuracy tradeoff)
- π Zero friction β works out of the box with sensible defaults (tiny.en)
- π Multilingual support β transcribe 99+ languages with multilingual models
- π DI-friendly β register with
AddWhisper()in ASP.NET Core - π§΅ Concurrent transcription β share one client with configurable queueing and pooled inference sessions
- π Progress reporting β track model downloads with real-time callbacks
- π― English-optimized models β dedicated
.envariants for best accuracy on English audio - β±οΈ Timestamp-aware results β opt into segment and word timings for subtitle and caption workflows
Installation
dotnet add package ElBruno.Whisper
Quick Start
using ElBruno.Whisper;
// Create client (downloads tiny.en model on first run)
using var client = await WhisperClient.CreateAsync();
var result = await client.TranscribeAsync("audio.wav");
Console.WriteLine(result.Text);
First Run
The first time you create a WhisperClient, the model is downloaded from HuggingFace to your local cache directory (~75 MB - 3 GB depending on model size). This typically takes 10-60 seconds depending on your internet connection and chosen model.
Track download progress:
using var client = await WhisperClient.CreateAsync(
progress: new Progress<ElBruno.HuggingFace.DownloadProgress>(p =>
{
if (p.Stage == ElBruno.HuggingFace.DownloadStage.Downloading)
Console.WriteLine($"{p.CurrentFile}: {p.PercentComplete:F0}%");
else
Console.WriteLine($"{p.Stage}: {p.Message}");
})
);
Subsequent runs load instantly from cache (%LOCALAPPDATA%/ElBruno/Whisper/models).
Model Selection
Whisper offers various model sizes. English-optimized models (.en suffix) are smaller and faster for English audio:
using var client = await WhisperClient.CreateAsync(new WhisperOptions
{
Model = KnownWhisperModels.WhisperSmallEn
});
var result = await client.TranscribeAsync("english-audio.wav");
Console.WriteLine(result.Text);
Available Models
| Size | English | Multilingual | Parameters | Approx Size | Speed |
|---|---|---|---|---|---|
| tiny | tiny.en | tiny | 39M | 75 MB | β‘β‘β‘β‘β‘ |
| base | base.en | base | 74M | 140 MB | β‘β‘β‘β‘ |
| small | small.en | small | 244M | 460 MB | β‘β‘β‘ |
| medium | medium.en | medium | 769M | 1.5 GB | β‘β‘ |
| large | β | large | 1550M | 3.0 GB | β‘ |
Use English-optimized (.en) models for:
- English audio only (slightly smaller, faster, better accuracy on English)
Use Multilingual models for:
- Non-English audio
- Mixed-language content
- Language auto-detection
Progress Tracking
Monitor both file downloads and transcription progress:
var downloadProgress = new Progress<ElBruno.HuggingFace.DownloadProgress>(p =>
{
if (p.Stage == ElBruno.HuggingFace.DownloadStage.Downloading)
Console.Write($"\rβ¬οΈ {p.PercentComplete:F0}%");
else
Console.WriteLine($"\nβ {p.Message}");
});
using var client = await WhisperClient.CreateAsync(progress: downloadProgress);
var result = await client.TranscribeAsync("audio.wav");
Console.WriteLine($"β Transcribed: {result.Text}");
Dependency Injection
Register Whisper options in ASP.NET Core or other DI-enabled applications:
builder.Services.AddWhisper(options =>
{
options.Model = KnownWhisperModels.WhisperBaseEn;
options.Concurrency.MaximumConcurrentRequests = 2;
});
AddWhisper() registers WhisperOptions. Create and share a WhisperClient yourself where you control startup and disposal.
Thread Safety and Concurrency
WhisperClient can now be shared across concurrent callers. By default it still processes one transcription at a time. Raise the concurrency limit to allow parallel work and reuse pooled ONNX sessions:
using var client = await WhisperClient.CreateAsync(new WhisperOptions
{
Model = KnownWhisperModels.WhisperTinyEn,
Concurrency = new WhisperConcurrencyOptions
{
MaximumConcurrentRequests = 2,
QueueTimeout = TimeSpan.FromSeconds(15),
EnableSessionPooling = true
}
});
var results = await Task.WhenAll(
client.TranscribeAsync("audio-1.wav"),
client.TranscribeAsync("audio-2.wav"));
If all inference slots are busy longer than QueueTimeout, TranscribeAsync throws TimeoutException. Cancelling the request also aborts queue waiting and the next safe decode checkpoint.
Transcription Result
The TranscriptionResult includes:
var result = await client.TranscribeAsync("audio.wav");
Console.WriteLine(result.Text); // Transcribed text
Console.WriteLine(result.DetectedLanguage); // Detected language (for multilingual models)
Console.WriteLine(result.Duration); // Audio duration
Enable timestamps to access both segment-level and word-level timing metadata:
using var client = await WhisperClient.CreateAsync(new WhisperOptions
{
EnableTimestamps = true
});
var result = await client.TranscribeAsync("audio.wav");
foreach (var segment in result.Segments ?? [])
{
Console.WriteLine($"[{segment.Start:mm\\:ss\\.ff} - {segment.End:mm\\:ss\\.ff}] {segment.Text}");
foreach (var word in segment.Words)
{
Console.WriteLine($" {word.Start:mm\\:ss\\.ff} - {word.End:mm\\:ss\\.ff}: {word.Text}");
}
}
Word timings are derived from the timestamped transcript spans produced by Whisper. If a model returns text without explicit spans, the library falls back to a single full-duration segment and derives word timings within that range.
Troubleshooting
Model download fails?
- Check your internet connection
- For private HuggingFace models, set the
HF_TOKENenvironment variable
Out of memory?
- Use a smaller model (tiny or base instead of medium/large)
- Transcribe shorter audio files in chunks
For detailed troubleshooting, see docs.
Samples
| Sample | Description |
|---|---|
| HelloWhisper | Minimal console transcription |
| BlazorWhisper | Blazor app with audio recording and real-time transcription |
Documentation
- Getting Started β installation, first steps, configuration
- API Reference β full API documentation
- Architecture β design decisions and internal structure
- Testing Guide β running tests, test organization, CI/CD pipeline
- Test Audio Files β audio resources for testing and transcription validation
- Image Prompts β prompts for generating blog and social media images
- Publishing β NuGet package publishing with OIDC
Building from Source
git clone https://github.com/elbruno/ElBruno.Whisper
cd ElBruno.Whisper
dotnet build ElBruno.Whisper.slnx
dotnet test ElBruno.Whisper.slnx --filter "Category!=Integration"
Testing
The repository includes comprehensive unit and integration tests:
Quick test run (unit tests, no model download):
dotnet test ElBruno.Whisper.slnx --filter "Category!=Integration"
Full test run (includes integration with real models):
dotnet test ElBruno.Whisper.slnx
Test audio files are provided in testdata/audio/ for validation and transcription testing. For details, see the Testing Guide.
π€ Contributing
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
π License
This project is licensed under the MIT License β see the LICENSE file for details.
Related Projects
- ElBruno.LocalLLMs β Run local LLMs in .NET
- ElBruno.HuggingFace β HuggingFace model utilities for .NET
π Acknowledgments
- ONNX Runtime β inference engine
- OpenAI Whisper β speech-to-text model
- Hugging Face β model hosting and community
- ONNX Community β ONNX model conversions
π About the Author
Hi! I'm ElBruno π§‘, a passionate developer and content creator exploring AI, .NET, and modern development practices.
Made with β€οΈ by ElBruno
If you like this project, consider following my work across platforms:
- π» Podcast: No Tienen Nombre β Spanish-language episodes on AI, development, and tech culture
- π» Blog: ElBruno.com β Deep dives on embeddings, RAG, .NET, and local AI
- πΊ YouTube: youtube.com/elbruno β Demos, tutorials, and live coding
- π LinkedIn: @elbruno β Professional updates and insights
- π Twitter: @elbruno β Quick tips, releases, and tech news
Changes from Previous Version
Adoption Assessment
Assess this package against its supported frameworks, license, maintenance signals and dependency graph before making it a project standard.
Related Guides & Workflows
Learn how to evaluate, integrate, or audit this package in your .NET project.