Skip to main content
Any Framework

Native transcribe.cpp binaries for macOS ARM64 (Metal) Built from upstream source and owned by the transcribe.cpp authors (MIT); this package only redistributes them.

Published
Aug 3, 2026
2mo ago
Total Downloads
7.7K
7,688

Install

dotnet add package TranscribeCppSharp.Native.osx-arm64 --version 0.1.3-preview.99

No framework information available.

README

TranscribeCppSharp

CI CodeQL Code Coverage Quality Gate Status CodeFactor FOSSA Status NuGet Version License: MIT

.NET bindings for transcribe.cpp: load GGUF speech-to-text models and transcribe audio (16 kHz mono float PCM) from C#.

Installation

Add the main wrapper package to your project:

dotnet add package TranscribeCppSharp

To include the native binaries for your platform, add the corresponding runtime package:

  • Linux (x64): TranscribeCppSharp.Native.linux-x64
  • Linux (ARM64): TranscribeCppSharp.Native.linux-arm64
  • Windows (x64): TranscribeCppSharp.Native.win-x64
  • macOS (ARM64): TranscribeCppSharp.Native.osx-arm64
  • macOS (x64): TranscribeCppSharp.Native.osx-x64

Note: For Linux Alpine (musl) or other platforms, please refer to the Building from source section. Like Using CUDA, a custom native build is picked up automatically when placed in the app output directory.

Quick Start

Basic Transcription

using TranscribeCppSharp;

// Load the model (GGUF format)
using var model = Model.Load("whisper-tiny.gguf");

// Create a transcription session
using var session = model.CreateSession();

// Input must be 16kHz mono float PCM
float[] pcm = ...; 

// Run transcription (blocking call)
var transcript = session.Run(pcm);

Console.WriteLine($"Result: {transcript.FullText}");

Selecting Hardware Backend

By default, the wrapper passes BackendAuto and the native library selects the backend it has available. You can force a specific one:

using var model = Model.Load("model.gguf", p => p
    .WithBackend(BackendRequest.BackendVulkan) // or BackendCuda, BackendMetal, etc.
    .WithGpuDevice(0)); // Throws ErrBackend if device index is invalid or unavailable

Batch Processing

Transcribe multiple audio buffers in parallel:

var audios = new float[][] { audio1, audio2, audio3 };
var results = Batch.Run(session, audios);

foreach (var result in results)
{
    Console.WriteLine(result.FullText);
}

Real-Time Streaming

using var stream = session.CreateStream();
stream.Begin(); // Initialize the streaming state

// Feed audio chunks incrementally
while (isRecording)
{
    float[] chunk = GetAudioChunk();
    stream.Feed(chunk);
    
    // Read partial results
    var current = stream.CurrentText;
    Console.Write($"\r{current.FullText}");
}

// Finalize the stream to get the last bits of text
// This must be called BEFORE Dispose() if you want the final results
stream.Finalize();
var final = stream.CurrentText;

Features

  • Multi-Model: Loads GGUF models for the model families supported by transcribe.cpp (Whisper, Moonshine, Parakeet, Voxtral, and others).
  • Hardware Acceleration: The bundled runtimes include CPU, Vulkan (Windows/Linux) and Metal (macOS) backends. See Using CUDA for NVIDIA GPUs.
  • Modern .NET: Uses LibraryImport for interop and SafeHandle for native resource lifetime.
  • Flexible APIs:
    • High-Level Wrapper: Intuitive C# API for rapid development.
    • Low-Level Interop: Direct access to the native C API when needed.
    • Streaming & Batch: Support for incremental streaming transcription and batch processing.
  • Cross-Platform: Pre-compiled native runtimes are packaged for Windows, Linux, and macOS (x64 and ARM64). Only linux-x64 is exercised by CI.

Using CUDA

The NuGet packages do not bundle a CUDA runtime (the bundled binaries are CPU + Vulkan on Windows/Linux and Metal on macOS). The upstream releases do include CUDA archives, but shipping and supporting CUDA builds is out of scope for this packaging layer — so to use an NVIDIA GPU you provide your own CUDA build of transcribe.cpp and place it next to your app; the wrapper prefers native binaries in the app output directory over the packaged ones.

  1. Download the upstream CUDA archive for your platform (this project is bound to transcribe.cpp v0.1.3):

    • Linux x64: transcribe-native-0.1.3-linux-x86_64-cuda.tar.gz
    • Windows x64: transcribe-native-0.1.3-windows-x86_64-cuda.tar.gz

    from the transcribe.cpp v0.1.3 release.

  2. Extract it and copy libtranscribe.so (Linux) or transcribe.dll (Windows) — plus the sibling libggml*.so / ggml*.dll files — into your app's output directory (where your .dll/.exe is produced).

  3. Request the CUDA backend at load time:

    using var model = Model.Load("model.gguf", p => p
        .WithBackend(BackendRequest.BackendCuda)
        .WithGpuDevice(0));
    

    You can verify CUDA is actually available in the current build with BackendAvailable(BackendRequest.BackendCuda). If no CUDA build is installed, that returns false and a BackendCuda request will fail with ErrBackend.

Concurrency Model

All transcription calls (Session.Run, Batch.Run, etc.) are blocking. This mirrors the native library, whose C API is fully synchronous (no async entry points); the wrapper does not add a "fake" async-over-sync layer on top.

Recommended Patterns

  1. Desktop/CLI Apps: Run transcription on a background thread using Task.Run() to keep the UI responsive.
  2. Web APIs (ASP.NET Core): Use a pool of Session objects combined with a SemaphoreSlim to limit concurrent native calls and prevent thread pool starvation.
// Example: Pooling sessions in a service
private readonly SemaphoreSlim _semaphore = new(Environment.ProcessorCount);
public async Task<string> TranscribeAsync(float[] pcm)
{
    await _semaphore.WaitAsync();
    try {
        return await Task.Run(() => _session.Run(pcm).FullText);
    } finally {
        _semaphore.Release();
    }
}

Architecture

The project is divided into several layers, each with a distinct responsibility:

  1. TranscribeCppSharp.Native.* (Runtimes): Platform-specific packages containing the pre-compiled native libtranscribe binaries.
  2. TranscribeCppSharp.Interop (Low-level): Auto-generated P/Invoke declarations using LibraryImport.
  3. TranscribeCppSharp (High-level): Idiomatic C# abstraction layer providing IDisposable resources and typed exceptions.
  4. Generator (Tool): Ensures C# bindings stay in sync with the upstream native API by parsing Rust FFI definitions.

Native Library Loading

A DllImportResolver registered in the Interop layer finds libtranscribe in the app output directory or the NuGet global packages folder, without requiring LD_LIBRARY_PATH. Its libggml* dependencies are loaded from the same directory by the native loader.

Error Handling

The high-level wrapper throws TranscribeException when a native call fails. You can filter by StatusCode to handle specific errors.

try 
{
    using var model = Model.Load("invalid.gguf");
}
catch (TranscribeException ex) when (ex.StatusCode == Status.ErrGguf)
{
    Console.WriteLine("Failed to load model: Check file path and format.");
}

Note: See the Status enum in the TranscribeCppSharp.Interop namespace for the full list of error codes.

Thread Safety

The native library and this wrapper are not thread-safe by default. The relevant rules:

  • Model: believed thread-safe — you can create multiple Session objects from a single Model instance across different threads. This is not covered by concurrency tests yet.
  • Session: Not thread-safe. A session maintains internal state (KV cache) for transcription. For concurrent processing, use multiple sessions or synchronize access.
  • Batch: Not thread-safe. Calls into the provided session internally. Use separate sessions for concurrent batch processing.
  • StreamSession: Not thread-safe. It is a view over a Session and shares its state.

Memory and disk usage depend on the model file, quantization, and backend you use. These are not documented here; refer to the model documentation and transcribe.cpp for accurate numbers.

Versioning & Compatibility

Two version numbers are in play, decoupled on purpose:

  • TranscribeCppSharp (this wrapper) follows Semantic Versioning (SemVer) for its own C# API. Breaking API changes bump the major/minor version of the wrapper.
  • TranscribeCppSharp.Interop and TranscribeCppSharp.Native.* are versioned to match the upstream transcribe.cpp version they bind to (e.g. 0.1.3 = transcribe.cpp v0.1.3). They track the ABI, not the wrapper's API.

So TranscribeCppSharp 0.1.0 depends on TranscribeCppSharp.Interop 0.1.3; a later upstream release will ship as a new Interop/Native version without necessarily changing the wrapper's own version. The correspondence between a wrapper release and the upstream version it targets is recorded in CHANGELOG.md.

Attribution

This project is a packaging and binding effort only — the underlying library is not my work:

  • The native library (transcribe.cpp) is developed and owned by the transcribe.cpp authors (MIT License).
  • The bundled native components (ggml, etc.) are owned by their respective authors; their MIT license texts are distributed alongside the binaries in the TranscribeCppSharp.Native.* packages.
  • I did not author the native library and claim no credit for it. This repository only adds:
    • A C# interop layer (auto-generated P/Invoke bindings via LibraryImport).
    • A high-level C# wrapper (IDisposable resources, typed exceptions).
    • Pre-built native binaries packaged for .NET consumption.

The transcribe.cpp project is an independent upstream project with its own maintainers and governance; this package is not affiliated with or endorsed by them. Please direct bug reports about the native library itself to the upstream repository.

Development

Prerequisites

  • .NET 10.0 or later.
  • Native libraries (can be fetched using the provided script).

Building and Testing

# Download native libraries for your current platform
./fetch-native.sh

# Run unit and integration tests
./run-integration-tests.sh

# Run the smoke test sample
dotnet run --project samples/SmokeTest -- model.gguf audio.wav

Building from source

The Native.* packages redistribute exactly what upstream transcribe.cpp publishes in its releases — nothing more. This project is a packaging/binding layer, not a binary provider: it does not compile musl, CUDA, or other variant builds. If a variant you need is not in the upstream release, building it yourself is on you.

You need to build from source when:

  • You are using Alpine Linux (which uses musl instead of glibc, making the pre-built Linux binaries incompatible). Upstream transcribe.cpp does not ship musl builds, so there is no Native.* package to install for this case.
  • You need to support a non-standard architecture or custom OS.
  • You want to enable specific hardware optimizations not included in the default build.

Steps:

  1. Clone transcribe.cpp.
  2. Build the native library using cmake (ensure BUILD_SHARED_LIBS=ON). On Alpine, build inside the distro so the resulting library links against musl.
  3. Copy the resulting libtranscribe.so (or .dll/.dylib) — and the sibling libggml*.so files it loads — into your application's output directory. As with Using CUDA, the wrapper prefers native binaries in the app output directory over the packaged ones, so no LD_LIBRARY_PATH is needed. The C# interop contract is unchanged: only the native binaries differ, not the P/Invoke signatures.

Governance

Security

To report a security vulnerability, please use the GitHub Security Advisory feature.

License

This project is licensed under the MIT License (matching transcribe.cpp).

Changes from Previous Version

No structural changes between v0.1.3-preview.98 and v0.1.3-preview.99.

Adoption Assessment

Assess this package against its supported frameworks, license, maintenance signals and dependency graph before making it a project standard.

This version is marked prerelease. Validate its API and upgrade path before production use.No target framework metadata is available. Confirm compatibility in a representative project before standardising on this package.

Related Guides & Workflows

Learn how to evaluate, integrate, or audit this package in your .NET project.