examples/c++/README.md
This is a minimal, platform independent example of using the C++ interface to the Moonshine Voice Library.
To use this you'll first need to download a prebuilt version of the library, or build it yourself using cmake in core/ if you're on a platform without a prebuilt version.
The easiest way to grab everything you need is the download-library.sh helper in this folder. It detects your platform, downloads the matching prebuilt library archive from our GitHub releases, and always extracts it into a folder named moonshine-voice here (regardless of operating system or architecture, so the build commands below never change). It also fetches the Medium Streaming English model into medium-streaming-en/ and a sample recording (two_cities.wav) so the transcriber example runs out of the box:
cd examples/c++
./download-library.sh
On Linux the extracted moonshine-voice/lib folder is self-contained: it holds libmoonshine.so alongside the libonnxruntime.so.1 it depends on, and the library is built with an $ORIGIN rpath so it finds the ONNX Runtime next to itself with no LD_LIBRARY_PATH needed.
If you would rather download by hand, look for moonshine-voice-<platform>.tar.gz on the releases page and extract it into a moonshine-voice folder in this directory. For example, on MacOS:
cd examples/c++
curl -O -L https://github.com/moonshine-ai/moonshine/releases/download/v0.1.1/moonshine-voice-macos-arm64.tar.gz
mkdir -p moonshine-voice
tar xzf moonshine-voice-macos-arm64.tar.gz -C moonshine-voice --strip-components=1
The archive contains the Moonshine library inside the lib folder: a shared libmoonshine.so on Linux, a static libmoonshine.a on MacOS, or moonshine.lib on Windows. This is what you'll link against. There are also two headers, one for the low-level C API, and another for the higher-level C++ framework that's built on top of it. On Linux the lib folder also contains libonnxruntime.so.1, the ONNX Runtime shared library that libmoonshine.so loads at runtime.
Since this is a generic C++ example, I'll show the simplest possible build command lines on some common platforms. Because download-library.sh always extracts into a folder named moonshine-voice, the exact same command works on both x86_64 and 64-bit ARM within each operating system.
g++ transcriber.cpp \
-Imoonshine-voice/include \
-Lmoonshine-voice/lib \
-lmoonshine \
-Wl,-rpath,'$ORIGIN/moonshine-voice/lib' \
-o transcriber
The -Wl,-rpath,'$ORIGIN/...' flag records a runtime library search path relative to the compiled transcriber binary, so it finds libmoonshine.so (and, through that library's own $ORIGIN rpath, libonnxruntime.so.1) without any LD_LIBRARY_PATH. If you prefer, drop the rpath flag and instead export LD_LIBRARY_PATH=$(pwd)/moonshine-voice/lib before running.
You can build text-to-speech.cpp the same way (swap the source and -o output names). Note that it additionally needs the TTS voice and G2P data, so pass its location with --asset-root (in a checkout of this repo that is ../../core/moonshine-tts/data).
g++ transcriber.cpp \
-Imoonshine-voice/include \
-Lmoonshine-voice/lib \
-lmoonshine \
-o transcriber \
-framework CoreFoundation \
-framework Foundation
g++ text-to-speech.cpp \
-Imoonshine-voice/include \
-Lmoonshine-voice/lib \
-lmoonshine \
-o text-to-speech \
-framework CoreFoundation \
-framework Foundation
You should now have an executable called transcriber in this folder. Run it with:
./transcriber
The C++ binding is header-only and opens no devices and no sockets, which is
what makes it portable to anywhere the library builds. So it has Transcriber,
TextToSpeech, GraphemeToPhonemizer, EmbeddingModel and VoiceClone, but
none of the pieces that need a runtime: there is no MicTranscriber or
AgentFlow (they need a capture device), no say() (it needs an output
device; synthesize() hands you the samples to play yourself), and no
downloading, so nothing takes a load() or reports progress. Feed it paths or
buffers instead.
Fetching models is therefore your job, but working out which models is not. Every manifest the other bindings download from is available as JSON naming the files, their URLs, sizes and checksums:
std::string stt = moonshine::Transcriber::getDependencies("en");
std::string diarization = moonshine::Transcriber::getDiarizationDependencies();
std::string tts = moonshine::TextToSpeech::getDependencies("en_us");
std::string g2p = moonshine::GraphemeToPhonemizer::getDependencies("en_us");
std::string embedding =
moonshine::EmbeddingModel::getDependencies("embeddinggemma-300m");
Transcriber::getCatalog() and EmbeddingModel::getCatalog() list everything
published, if you would rather browse than ask for one language.
Voice cloning does work here, because the voice-activity detector that finds a reference clip is compiled into the library and needs no downloads:
moonshine::TextToSpeech tts("en_us", {{"g2p_root", assetRoot}});
tts.cloneFrom(recording, sampleRate, "what the speaker said");
moonshine::TtsSynthesisResult cloned = tts.synthesize("Hello world!");
Pass a Transcriber in place of the transcript to have Moonshine work out what
was said. To capture the clip from a live stream rather than a finished
recording, use startCloning() and feed addAudio() until isReady().
By default it transcribes the two_cities.wav sample using the Medium Streaming English model that download-library.sh placed in the medium-streaming-en/ folder, so you should see transcription results printed as it goes. You can point it at different models and inputs using --model-path, --model-arch, and --wav-path.