audiocpp

package module
v0.1.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 4, 2026 License: MIT Imports: 11 Imported by: 0

README

audiocpp-go

Go bindings for audio.cpp — text-to-speech and audio inference on ggml — via purego (no cgo).

audio.cpp exposes no C API of its own, so this project provides one: a thin extern "C" shim over its C++ engine::runtime facade, compiled into a self-contained shared library that Go loads at runtime.

How it works

  • shim/audiocpp_c.{h,cpp} — the extern "C" surface (audiocpp_load / audiocpp_synthesize / audiocpp_free_samples / audiocpp_free / audiocpp_version). It is pointer-only, so it binds cleanly with purego. The shim wraps make_default_registry → load → create_task_session → prepare → run → AudioBuffer; the session is created per synthesis and keyed on task (tts / voice_design / voice_clone), so preset voices, voice design and cloning share one loaded model. C++ exceptions never cross the boundary — failures return a status code plus a message string.
  • CMakeLists.txt — builds audio.cpp's static engine_runtime and the shim into a single SHARED libaudiocpp with global PIC and hidden visibility, exporting only the five audiocpp_* symbols (-exported_symbols_list on macOS, --exclude-libs,ALL + a version script on Linux) so nothing from ggml/sentencepiece/etc. leaks.
  • audiocpp.go + binding.go + library_{unix,windows}.go — the Go binding (package audiocpp): Load(libDir), New(ModelParams), (*Model).Synthesize(SynthParams) → *Audio, Close(), Version(), EncodeWAV(), and a typed Error{Code, Msg}. purego RegisterLibFunc binds the five symbols; native PCM is copied into Go memory and then freed, so callers never hold C-owned buffers.

Usage

import audiocpp "github.com/Pendra-Cloud/audiocpp-go"

// Point Load at a directory containing the prebuilt libaudiocpp for your platform.
if err := audiocpp.Load(libDir); err != nil { log.Fatal(err) }

m, err := audiocpp.New(audiocpp.ModelParams{
    ModelPath:  "/path/to/model.gguf",
    FamilyHint: "qwen3_tts",
    Backend:    "metal", // "cpu" | "cuda" | "vulkan" | "hip" | "metal" | "best"
})
if err != nil { log.Fatal(err) }
defer m.Close()

audio, err := m.Synthesize(audiocpp.SynthParams{
    Text:    "Hello from audio dot cpp, bound to Go.",
    VoiceID: "ryan",
})
if err != nil { log.Fatal(err) }

os.WriteFile("out.wav", audiocpp.EncodeWAV(audio), 0o644)

A Model is not safe for concurrent use — synthesise serially.

See examples/tts for a complete runnable example.

Building the shared library

# Clone the pinned upstream (into upstream/audio.cpp):
scripts/clone-upstream.sh

cmake -S . -B build -DCMAKE_BUILD_TYPE=Release \
  -DAUDIOCPP_SRC=upstream/audio.cpp -DAUDIOCPP_BACKEND=metal
cmake --build build --target audiocpp --parallel

# Verify only the shim's symbols are exported (fail-closed):
scripts/check-symbols.sh build/libaudiocpp.dylib

# Go end-to-end against the local build (needs a model GGUF):
AUDIOCPP_GO_E2E=1 go test ./...

AUDIOCPP_BACKEND selects the ggml backend (cpu / cuda / vulkan / hip / metal). AUDIOCPP_MODEL_SET defaults to a small set for fast local builds; release builds pass -DAUDIOCPP_MODEL_SET=full.

Prebuilt libraries & releases

.github/workflows/build-libs.yml builds a self-contained libaudiocpp for each variant — linux amd64 (cpu / cuda / vulkan), linux arm64 (cpu), darwin arm64 (metal), windows amd64 (cpu) — runs the fail-closed symbol gate on each, and publishes one vX.Y.Z release carrying the module tag (so go get github.com/Pendra-Cloud/audiocpp-go@vX.Y.Z resolves) alongside per-variant audiocpp-libs-<os>-<arch>-<backend>.tar.gz archives and checksums.txt. GPU and Windows legs are build/link/symbol-check only (no GPU CI runners) and best-effort, so a toolchain mismatch never blocks a release. Consumers extract an archive and pass its directory to Load(libDir).

Pin

audio.cpp is pinned in lib/version.txt. The shim compiles against audio.cpp's internal C++ headers, which move quickly, so the pin is intentionally strict.

Licensing

This project (the Go code and the extern "C" shim) is MIT-licensed — see LICENSE.

The prebuilt shared libraries statically link audio.cpp (Apache-2.0), ggml (MIT), and sentencepiece (Apache-2.0). Their licence and NOTICE files are bundled inside each released audiocpp-libs-*.tar.gz.

Documentation

Overview

Package audiocpp is a Go binding for audio.cpp's TTS engine, loaded in-process via purego (no cgo) over the extern "C" shim in shim/audiocpp_c.h. It mirrors a common purego binding approach.

The surface is deliberately small (five C symbols), so — unlike sd-go, which splits a ~50-symbol API into pkg/sd + a root layer — the raw bindings and the ergonomic API live in one package here.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func EncodeWAV

func EncodeWAV(a *Audio) []byte

EncodeWAV encodes an Audio result as a 16-bit PCM WAV file (little-endian). A convenience for callers/examples; the worker ships base64 WAV to the API.

func Load

func Load(libDir string) (err error)

Load resolves and dlopens the audiocpp shared library from libDir (an empty libDir falls back to the OS default search path). It is idempotent, safe for concurrent use, and returns an error — never panics — when the library is absent or a symbol is missing, so importing this package or calling Load with no library present keeps the caller healthy.

func Version

func Version() string

Version returns the shim's version string. Requires Load to have succeeded.

Types

type Audio

type Audio struct {
	Samples    []float32
	SampleRate int
	Channels   int
}

Audio is a synthesis result: interleaved f32 PCM plus format.

type Error

type Error struct {
	Code int
	Msg  string
}

Error is a synthesis or load failure from the engine. Code is the coarse shim status (see the shim's audiocpp_status); Msg is the engine's free-text message. The whole point of the in-process binding over the old HTTP path is that this arrives synchronously — no status code to re-parse off a response.

func (*Error) Error

func (e *Error) Error() string

type Model

type Model struct {
	// contains filtered or unexported fields
}

Model is a loaded TTS model. It is NOT safe for concurrent use — synthesise serially — hold one model behind a single-slot mutex. An internal mutex is kept only to prevent a use-after-free race between Synthesize and Close, not to enable parallelism.

func New

func New(p ModelParams) (m *Model, err error)

New loads a model. The returned Model must be Close()d to free native memory.

func (*Model) Close

func (m *Model) Close() error

Close unloads the model and frees native memory. Idempotent.

func (*Model) Synthesize

func (m *Model) Synthesize(p SynthParams) (a *Audio, err error)

Synthesize turns text into audio. Safe against a concurrent Close (returns an error if the model is closed); concurrent Synthesize calls are serialized.

type ModelParams

type ModelParams struct {
	ModelPath   string            // path to the GGUF (required)
	FamilyHint  string            // audio.cpp family, e.g. "qwen3_tts"
	Backend     string            // "cpu"|"metal"|"cuda"|"vulkan"|"hip"|"best" ("" == cpu)
	Device      int               // GPU device index
	Threads     int               // CPU threads (<=0 -> engine default)
	LoadOptions map[string]string // extra load-time options
}

ModelParams configures a model load.

type SynthParams

type SynthParams struct {
	Task          string            // "tts" (default) | "voice_design" | "voice_clone"
	Text          string            // text to speak (required)
	VoiceID       string            // preset speaker id, e.g. "ryan"
	RefPCM        []float32         // reference-audio voice (mono f32); alternative to VoiceID
	RefSampleRate int               // sample rate of RefPCM
	Options       map[string]string // per-request options (instruct, seed, temperature, ...)
}

SynthParams configures one synthesis call.

Directories

Path Synopsis
examples
tts command
Command tts is a minimal end-to-end example: load a model and synthesise a WAV, exercising the Go -> shim -> engine path end to end.
Command tts is a minimal end-to-end example: load a model and synthesise a WAV, exercising the Go -> shim -> engine path end to end.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL