A small local voice-to-text starter that can:
- Record from a selected Windows capture device (WASAPI)
- Transcribe with Whisper via
Whisper.net(GPU via CUDA runtime if available) - Emit a Markdown file with structure
- Optionally polish the Markdown using a local LLM via Ollama
- Windows 10/11 (recording uses WASAPI)
- .NET 10 SDK installed
- If you want LLM formatting: Ollama running locally
- Install Ollama and pull a model, e.g.:
ollama pull llama3.2
- Install Ollama and pull a model, e.g.:
- If you want cloud formatting via Hugging Face + Semantic Kernel:
- Create a Hugging Face token and set
HF_TOKEN - Example (PowerShell):
$env:HF_TOKEN = "hf_xxx"
- Create a Hugging Face token and set
From the repo root:
cd src\LocalTranscriber.Cli
dotnet restore
dotnet run -- devices
dotnet run -- record --device 0 --out ..\..\output\note.wav
dotnet run -- transcribe --in ..\..\output\note.wav --out ..\..\output\note.md --model SmallEn --format-provider auto
# Optional: use Semantic Kernel + Hugging Face instead of Ollama
dotnet run -- transcribe --in ..\..\output\note.wav --out ..\..\output\note.md --format-provider huggingface --hf-endpoint https://router.huggingface.co --hf-model openai/gpt-oss-20b:groq
# Optional: enable speaker labels quickly
dotnet run -- transcribe --in ..\..\output\note.wav --out ..\..\output\note.md --format-provider local --speakers 2dotnet run -- record-and-transcribe --device 0 --wav ..\..\output\meeting.wav --out ..\..\output\meeting.md --model SmallEncd src\LocalTranscriber.Web
dotnet runThen open the app URL (for example http://localhost:5078) and use the recording panel.
- In the Blazor UI, enable
Run In Browser (Experimental)when the capability badge reports support. - Browser mode runs Whisper in-browser (Transformers.js) and can optionally run markdown cleanup via WebLLM.
- Browser mode requires a modern browser with
WebGPU,AudioContext, andMediaRecorder. - WebLLM/Transformers models are fetched by the browser at runtime, so first run can take time and requires internet access.
- Model download: the first transcription downloads the Whisper GGML model into:
%LOCALAPPDATA%\LocalTranscriber\models
- Accuracy vs speed:
SmallEnis a very good default.MediumEn/LargeV3are more accurate but heavier.
- Segment limit option:
- Use
--max-seg-length <int>to cap segment length (legacy--max-seg-secondsis still accepted).
- Use
- Convenience flags:
- Use
--format-provider auto|local|ollama|huggingfaceinstead of juggling multiple boolean switches. - Use
--speakers <int>as a shortcut for speaker labeling.
- Use
- Speaker labeling:
- If a speaker count is not supplied by the caller, the app now auto-estimates speaker count and labels speaker swaps heuristically.
- Speaker labeling is now tunable:
--speaker-sensitivity 0-100(low = conservative, high = aggressive)--speaker-min-score-gain--speaker-max-switch-rate--speaker-min-separation--speaker-min-cluster-size--speaker-max-auto--speaker-global-variance-gate--speaker-short-run-merge-seconds
- In the Blazor Studio UI, these are exposed in Settings as sliders/advanced numeric controls.
- Formatting:
--format-provider autotries Ollama first, then Hugging Face (ifHF_TOKEN/--hf-api-keyis configured), then local formatting.--format-provider ollamaor--format-provider huggingfaceforce a provider, with local fallback if unavailable.- Formatting is tunable:
--format-sensitivity 0-100--format-strict-transcript true|false--format-overlap-threshold--format-summary-min/--format-summary-max--format-include-action-items true|false--format-temperature/--format-max-tokens--format-local-big-gap/--format-local-small-gap
- In the Blazor Studio UI, formatter tuning is exposed via slider/toggles and advanced numeric fields.
- If not, you still get a usable Markdown transcript via a deterministic local formatter.
- CLI output:
- After transcription, the CLI also prints the transcript text to the terminal for quick verification.
- Web API caching (checksum/signature dedupe):
- The Blazor server endpoint now computes a SHA-256 checksum for uploaded audio.
- It also computes a request signature from
audio checksum + normalized transcription/format/speaker settings. - Matching submissions are served from persisted cache instead of re-running normalization/transcription/formatting.
- Cache entries are stored under
src/LocalTranscriber.Web/output/cache.
Run LocalTranscriber anywhere with Docker:
# Start web UI with Ollama
docker compose up web
# Or use GPU acceleration (requires NVIDIA Docker runtime)
docker compose --profile cuda up web-cuda# Transcribe a file
docker compose run --rm cli transcribe --in /app/input/meeting.wav --out /app/output/meeting.md
# With GPU
docker compose --profile cli-cuda run --rm cli-cuda transcribe --in /app/input/meeting.wav --out /app/output/meeting.md| Image | Description |
|---|---|
local-transcriber:web |
Web UI, CPU-only |
local-transcriber:web-cuda |
Web UI with CUDA GPU support |
local-transcriber:cli |
CLI, CPU-only |
local-transcriber:cli-cuda |
CLI with CUDA GPU support |
./models→/app/models— Whisper model cache./output→/app/output— Transcription output./input→/app/input— Input files (CLI only)
# CPU version
docker build --target web -t local-transcriber:web .
# CUDA version
docker build --target web --build-arg VARIANT=cuda -t local-transcriber:web-cuda .Whisper.net is a .NET wrapper around whisper.cpp and supports multiple runtimes (CPU, CUDA, etc.) and a built-in GGML model downloader.
This repo includes baseline GitHub project automation:
- CI build workflow:
.github/workflows/ci.yml - Dependency review on PRs:
.github/workflows/dependency-review.yml - PR auto-labeling:
.github/workflows/pr-labeler.yml+.github/labeler.yml - Label synchronization:
.github/workflows/label-sync.yml+.github/labels.yml - Release pipeline (tag-driven):
.github/workflows/release.yml - Dependabot updates:
.github/dependabot.yml - Issue and PR templates under
.github/
Project governance/docs:
CONTRIBUTING.mdCODE_OF_CONDUCT.mdSECURITY.mdSUPPORT.mdCHANGELOG.mdLICENSE
- Update
.github/ISSUE_TEMPLATE/config.ymldiscussion URL (<owner>/<repo>placeholder). - Update
CODEOWNERSwith real usernames/teams. - Push
main, then runLabel Syncworkflow once to seed labels. - Create a tag (for example
v0.1.0) to validate the release workflow.