# How Moonshine's C++ Core, C API, and Language Bindings (Python, Swift, Java) Are Structured

> Explore Moonshine's layered architecture: C++ inference engine, stable C API, and Python Swift Java bindings. Discover how language bindings interact with the C++ core for efficient inference.

- Repository: [Moonshine AI/moonshine](https://github.com/moonshine-ai/moonshine)
- Tags: internals
- Published: 2026-02-16

---

**Moonshine uses a layered architecture where a modern C++ inference engine exposes a stable C API, which Python, Swift, and Java bindings consume via ctypes, direct module imports, and JNI respectively.**

The `moonshine-ai/moonshine` repository implements a high-performance speech-to-text engine designed for cross-platform deployment. To maximize portability without duplicating logic, the project isolates its ONNX Runtime inference code behind a thin C API layer. This design allows language-specific bindings to interact with the core engine while maintaining a single source of truth for model loading, tokenization, and streaming transcription.

## Architecture Overview

The codebase follows a strict three-tier separation:

1. **C++ Core Engine** – Handles model loading, ONNX Runtime sessions, and streaming state management.
2. **C API Layer** – Provides a stable ABI in [`core/moonshine-c-api.h`](https://github.com/moonshine-ai/moonshine/blob/main/core/moonshine-c-api.h) and [`core/moonshine-c-api.cpp`](https://github.com/moonshine-ai/moonshine/blob/main/core/moonshine-c-api.cpp) that marshals data between C++ objects and C structures.
3. **Language Bindings** – Thin adapters in Python, Swift, and Java that convert native types to C-compatible structures and invoke the C API.

This separation ensures that updates to the inference engine never break the public interface consumed by the language bindings.

## The C++ Core Engine

Located in the `core/` directory, the engine implements the actual speech recognition logic.

* **[`core/moonshine-model.cpp`](https://github.com/moonshine-ai/moonshine/blob/main/core/moonshine-model.cpp)** – Implements the `MoonshineModel` class, which manages ONNX Runtime inference sessions, vocabulary loading, and audio preprocessing.
* **[`core/transcriber.cpp`](https://github.com/moonshine-ai/moonshine/blob/main/core/transcriber.cpp)** – Implements the `Transcriber` class that orchestrates the streaming pipeline, maintaining internal state for partial results.
* **Threading** – The core is thread-safe; multiple threads can create independent transcriber instances, though a single transcriber serializes its internal work queue.

The C++ layer never exposes STL containers or exceptions across the library boundary. Instead, it relies on the C API to translate all data into plain C structs.

## The C API Layer

The C API provides the stable binary interface that all language bindings target.

* **Header** – [`core/moonshine-c-api.h`](https://github.com/moonshine-ai/moonshine/blob/main/core/moonshine-c-api.h) defines exported symbols, data structures (`transcript_t`, `transcriber_option_t`), constants (`MOONSHINE_MODEL_ARCH_*`), and function prototypes.
* **Implementation** – [`core/moonshine-c-api.cpp`](https://github.com/moonshine-ai/moonshine/blob/main/core/moonshine-c-api.cpp) wraps C++ classes (`MoonshineModel`, `Transcriber`) and translates between C structures and C++ objects. All public functions are marked `MOONSHINE_EXPORT` to ensure visibility in the shared library.

### Key Functions

* `moonshine_load_transcriber_from_files` – Initializes a transcriber from model files on disk.
* `moonshine_transcribe_without_streaming` – One-shot transcription of complete audio.
* `moonshine_create_stream`, `moonshine_add_audio_to_stream`, `moonshine_transcribe_stream` – Streaming API for real-time recognition.
* `moonshine_free_transcriber` – Releases model resources.

### Design Highlights

* **Version safety** – Callers pass `MOONSHINE_HEADER_VERSION` when loading a transcriber, allowing the library to emulate older behavior if the shared library is newer than the client headers.
* **ABI stability** – The C API guarantees that structures and function signatures remain compatible across minor versions, preventing binding breakage.

## Language Bindings

Each binding is a thin adapter that marshals data between idiomatic language types and the C API.

### Python Binding

Located in `python/src/moonshine_voice/`, the Python binding uses **ctypes** to load the shared library dynamically.

* **[`moonshine_api.py`](https://github.com/moonshine-ai/moonshine/blob/main/moonshine_api.py)** – Defines `_MoonshineLib`, a singleton that loads `libmoonshine.so`, `libmoonshine.dylib`, or `moonshine.dll` via `ctypes.CDLL`. It maps every C function to a ctypes prototype and declares matching `ctypes.Structure` classes (`TranscriptC`, `TranscriptLineC`, `TranscriberOptionC`) that mirror the C header field order.
* **[`transcriber.py`](https://github.com/moonshine-ai/moonshine/blob/main/transcriber.py)** – Provides the high-level `Transcriber` class that wraps the low-level calls:
  1. `loadTranscriberFromFiles` returns a handle (`int32`).
  2. `transcribeWithoutStreaming` or streaming methods convert Python `list[float]` to C `float*` and wrap the returned `TranscriptC` into Python objects.
  3. `freeTranscriber` releases the handle.

The binding automatically converts Python lists to C arrays and raises `MoonshineError` when the C API returns non-zero error codes.

```python
from moonshine_voice import Transcriber, ModelArch

# Load a transcriber for the base model (English)

transcriber = Transcriber(
    model_path="models/base/en",
    model_arch=ModelArch.BASE,
)

# Non-streaming transcription of a float PCM array (16 kHz)

audio = [...]                     # List[float] with values in [-1.0, 1.0]

transcript = transcriber.transcribe(audio, sample_rate=16000)

for line in transcript.lines:
    print(f"{line.start_time:.2f}s → {line.text}")

# Clean up

transcriber.close()

```

### Swift Binding

The Swift package resides in `swift/Sources/MoonshineVoice/`.

* **[`MoonshineAPI.swift`](https://github.com/moonshine-ai/moonshine/blob/main/MoonshineAPI.swift)** – Defines the `MoonshineAPI` class, a singleton that imports C functions directly via the module map `core/module.modulemap`. The module map exposes the C header symbols to Swift as `extern` functions.
* **Data conversion** – Swift uses the imported C structs (`transcript_t`, `transcript_line_t`) as opaque pointers. The wrapper extracts fields, builds Swift structs `Transcript` and `TranscriptLine`, and copies audio data into Swift arrays after safety checks.
* **Error handling** – Each C call returns an integer error code; the Swift wrapper checks this and throws `MoonshineError` if non-zero.

The Swift binding bypasses JNI or ctypes overhead by linking directly against the compiled `libmoonshine` library, providing near-native performance.

```swift
import MoonshineVoice

do {
    // Load a tiny-streaming model
    let handle = try MoonshineAPI.shared.loadTranscriberFromFiles(
        path: "models/tiny/en",
        modelArch: .tinyStreaming
    )

    // Create a streaming session
    let stream = try MoonshineAPI.shared.createStream(transcriberHandle: handle, flags: 0)
    try MoonshineAPI.shared.startStream(transcriberHandle: handle, streamHandle: stream)

    // Feed audio chunks (Float array) from microphone
    try MoonshineAPI.shared.addAudioToStream(
        transcriberHandle: handle,
        streamHandle: stream,
        audioData: micChunk,
        sampleRate: 16000,
        flags: 0
    )

    // Get partial transcript
    let transcript = try MoonshineAPI.shared.transcribeStream(
        transcriberHandle: handle,
        streamHandle: stream,
        flags: 0
    )
    print(transcript.lines.map { $0.text }.joined(separator: " "))

    // Clean up
    try MoonshineAPI.shared.stopStream(transcriberHandle: handle, streamHandle: stream)
    try MoonshineAPI.shared.freeStream(transcriberHandle: handle, streamHandle: stream)
    MoonshineAPI.shared.freeTranscriber(handle)
} catch {
    print("Moonshine error: \(error)")
}

```

### Java (Android) Binding

The Android binding uses JNI to bridge Java method calls to the C API.

* **[`JNI.java`](https://github.com/moonshine-ai/moonshine/blob/main/JNI.java)** – Located at [`android/java/main/java/ai/moonshine/voice/JNI.java`](https://github.com/moonshine-ai/moonshine/blob/main/android/java/main/java/ai/moonshine/voice/JNI.java), this class declares `native` static methods that correspond one-to-one with the C API (e.g., `moonshineLoadTranscriberFromFiles`, `moonshineTranscribeWithoutStreaming`).
* **[`moonshine-jni.cpp`](https://github.com/moonshine-ai/moonshine/blob/main/moonshine-jni.cpp)** – The native bridge at [`android/moonshine-jni/moonshine-jni.cpp`](https://github.com/moonshine-ai/moonshine/blob/main/android/moonshine-jni/moonshine-jni.cpp) implements each JNI method. It converts Java strings to C strings, Java `float[]` to C `float*`, and invokes the C API. It also marshals the `transcript_t` result back into a Java `Transcript` object (a list of `TranscriptLine` POJOs).
* **Data objects** – `Transcript`, `TranscriptLine`, and `TranscriberOption` are plain Java POJOs used by the application layer.

The Java code simply forwards arguments to the native layer; all heavy lifting (model inference, VAD, tokenization) stays in the C++ core accessed via the C API.

```java
import ai.moonshine.voice.JNI;
import ai.moonshine.voice.Transcript;

public class Demo {
    public static void main(String[] args) {
        // Load the native library (packaged with the app)
        System.loadLibrary("moonshine");

        // Load a base model from assets
        int transcriber = JNI.moonshineLoadTranscriberFromFiles(
                "/android_asset/models/base/en",
                JNI.MOONSHINE_MODEL_ARCH_BASE,
                null // no extra options
        );

        // Non-streaming transcription
        float[] audio = ...; // 16 kHz PCM floats
        Transcript transcript = JNI.moonshineTranscribeWithoutStreaming(
                transcriber,
                audio,
                audio.length,
                16000,
                0 // flags
        );

        for (var line : transcript.getLines()) {
            System.out.printf("[%.2fs] %s%n", line.getStartTime(), line.getText());
        }

        // Clean up
        JNI.moonshineFreeTranscriber(transcriber);
    }
}

```

## Summary

Moonshine’s multi-language architecture relies on strict separation between the inference engine and language wrappers:

* **C++ Core** – Implements model loading, ONNX Runtime inference, and streaming logic in [`core/moonshine-model.cpp`](https://github.com/moonshine-ai/moonshine/blob/main/core/moonshine-model.cpp) and [`core/transcriber.cpp`](https://github.com/moonshine-ai/moonshine/blob/main/core/transcriber.cpp).
* **C API** – Exposes a stable ABI through [`core/moonshine-c-api.h`](https://github.com/moonshine-ai/moonshine/blob/main/core/moonshine-c-api.h) and [`core/moonshine-c-api.cpp`](https://github.com/moonshine-ai/moonshine/blob/main/core/moonshine-c-api.cpp), guaranteeing backward compatibility via header version checks.
* **Python Binding** – Uses **ctypes** in [`python/src/moonshine_voice/moonshine_api.py`](https://github.com/moonshine-ai/moonshine/blob/main/python/src/moonshine_voice/moonshine_api.py) to load the shared library and marshal Python lists to C arrays.
* **Swift Binding** – Imports C symbols directly via `core/module.modulemap` in [`swift/Sources/MoonshineVoice/MoonshineAPI.swift`](https://github.com/moonshine-ai/moonshine/blob/main/swift/Sources/MoonshineVoice/MoonshineAPI.swift), avoiding JNI overhead.
* **Java Binding** – Bridges JVM calls to the C API through JNI using [`android/java/main/java/ai/moonshine/voice/JNI.java`](https://github.com/moonshine-ai/moonshine/blob/main/android/java/main/java/ai/moonshine/voice/JNI.java) and the native bridge [`android/moonshine-jni/moonshine-jni.cpp`](https://github.com/moonshine-ai/moonshine/blob/main/android/moonshine-jni/moonshine-jni.cpp).

## Frequently Asked Questions

### How does Moonshine maintain ABI stability across versions?

The C API in [`core/moonshine-c-api.h`](https://github.com/moonshine-ai/moonshine/blob/main/core/moonshine-c-api.h) uses explicit struct layouts and `MOONSHINE_EXPORT` visibility macros. Callers pass `MOONSHINE_HEADER_VERSION` when initializing a transcriber via `moonshine_load_transcriber_from_files`, allowing the library to emulate older behavior if the shared library is newer than the client headers. This ensures that Python, Swift, and Java bindings compiled against an older SDK continue to work with newer core releases.

### Why does Moonshine use a C API instead of exposing C++ directly?

A C API provides a **stable binary interface** that is not affected by C++ ABI changes, name mangling, or STL container differences across compilers. This allows the same compiled `libmoonshine.so` (or `.dylib`/`.dll`) to be consumed by Python via ctypes, Swift via direct symbol import, and Java via JNI without recompilation or per-language C++ wrappers.

### Can I use the C API directly without the language bindings?

Yes. The C API is fully public and documented in [`core/moonshine-c-api.h`](https://github.com/moonshine-ai/moonshine/blob/main/core/moonshine-c-api.h). You can link against `libmoonshine` directly from C or C++ code, or from any language that supports C FFI (such as Rust, Go, or Node.js N-API). The language bindings provided in the repository are convenience wrappers that handle memory management and type conversion for their respective ecosystems.

### How does streaming transcription work across the different bindings?

All bindings expose the same streaming lifecycle defined in the C API:
1. **Create** a stream handle with `moonshine_create_stream` (or language equivalent).
2. **Add audio** chunks via `moonshine_add_audio_to_stream`.
3. **Transcribe** incrementally with `moonshine_transcribe_stream`, which returns a `transcript_t` containing flags like `is_complete` and `is_updated`.
4. **Free** the stream with `moonshine_free_stream` when done.

The Python, Swift, and Java wrappers map these steps to idiomatic patterns—Python context managers, Swift structured concurrency, or Java try-with-resources—while internally calling the same C functions.