The C++ Basic TTS Sample

The C++ TTS sample, nvigi.basic.tts.cxx, demonstrates text-to-speech synthesis using the NVIGI SDK with modern C++ interfaces. It uses the Chatterbox TTS plugin and showcases speaker-embedding voice cloning, real-time audio playback, and both synchronous and asynchronous generation modes with support for multiple backends.

NOTE: This sample supports multiple backends (D3D12, CUDA, and Vulkan), allowing it to run on various hardware configurations.

For the full Chatterbox API and behavior reference, see the TTS Chatterbox Programming Guide.

Features

  • Voice Cloning: Use any voice via a speaker embedding JSON

  • Real-Time Playback: Play generated audio in real-time using DirectSound (Windows)

  • WAV File Output: Save generated speech to WAV files

  • Async/Polling API: Non-blocking operations perfect for game loops

  • Multiple Backends: D3D12, CUDA, or Vulkan backends

  • Two Model Types: Chatterbox Turbo (English) and Chatterbox Multilingual (23 languages)

Download Required Models

The TTS sample requires one of the following Chatterbox models:

Plugin

Model Name

GUID

nvigi.plugin.tts.chatterbox-ggml.*

Chatterbox Turbo (English)

019BD494-0D97-7223-B9D5-C9286933B8B7

nvigi.plugin.tts.chatterbox-ggml.*

Chatterbox Multilingual (23 langs)

A60EB5CF-9551-4B86-865B-CDC3CDBE61C4

Important: You also need a speaker embedding JSON file. The SDK test data includes sample embeddings in <SDK_TEST>/nvigi.tts/chatterbox/spk_emb/. Turbo speakers (e.g. aaron_turbo.json) must be used with the Turbo model, and multilingual speakers (e.g. french.json) with the Multilingual model — mismatches are rejected at instance creation.

See the top-level documentation that shipped with your development pack for information on how to download these models and test data.

These instructions use the following path placeholders, which refer to locations that differ between a binary developer pack and a GitHub source tree:

  • <SDK_ROOT>: the root of the SDK Plugins tree, which contains the bin directory for the SDK.

  • <SDK_MODELS>: the root of the models tree for the SDK plugins, normally data/nvigi.models

  • <SDK_TEST>: the root of the test data tree for the SDK plugins, normally data/nvigi.test

  • <SDK_PLATFORM>: the target CPU architecture. See Platform Support for information on supported platforms.

How to Use the TTS Sample

Basic Speech Generation

  1. Open a command prompt in <SDK_ROOT>

  2. Run the command:

bin\<SDK_PLATFORM>\<Configuration>\nvigi.basic.tts.cxx.exe --models <SDK_MODELS> --sdk bin\<SDK_PLATFORM>\<Configuration> --speaker <SDK_TEST>/nvigi.tts/chatterbox/spk_emb/aaron_turbo.json
  1. In a standard layout binary development pack or GitHub source tree:

bin\<SDK_PLATFORM>\<Configuration>\nvigi.basic.tts.cxx.exe --models data/nvigi.models --sdk bin\<SDK_PLATFORM>\<Configuration> --speaker data/nvigi.test/nvigi.tts/chatterbox/spk_emb/aaron_turbo.json
  1. The sample will generate speech for the default text and save it to output.wav

  2. You can play the generated WAV file to hear the synthesized speech

Real-Time Playback Mode

To hear the generated speech in real-time as it’s being generated:

bin\<SDK_PLATFORM>\<Configuration>\nvigi.basic.tts.cxx.exe --models data/nvigi.models --sdk bin\<SDK_PLATFORM>\<Configuration> --speaker data/nvigi.test/nvigi.tts/chatterbox/spk_emb/aaron_turbo.json --play

Async Mode (Non-Blocking)

For game integration or when you need to continue other processing:

bin\<SDK_PLATFORM>\<Configuration>\nvigi.basic.tts.cxx.exe --models data/nvigi.models --sdk bin\<SDK_PLATFORM>\<Configuration> --speaker data/nvigi.test/nvigi.tts/chatterbox/spk_emb/aaron_turbo.json --async --play

Command Line Options

Usage: nvigi.basic.tts.cxx [options]

  --sdk                     sdk location, if none provided assuming exe location
  --plugin                  plugin location, if none provided assuming sdk location
  -m, --models              model repo location (REQUIRED)
  -t, --threads             number of threads (default: 8)
  --backend                 backend to use - d3d12, cuda, vulkan (default: d3d12)
  --guid                    TTS model guid in registry format, in quotes (default: "{019BD494-0D97-7223-B9D5-C9286933B8B7}")
  --vram                    the amount of vram to use in MB (default: 4096)
  --log-level               logging level 0-2 (default: 0)
  --text                    text to synthesize (default: "Hello! This is a test of the text to speech system.")
  --speaker                 path to speaker embedding JSON (REQUIRED)
  --output                  output WAV file path (default: output.wav)
  --multilingual            use the multilingual model (default: turbo)
  --language                language code for the multilingual model (en, fr, zh, ja, ko, ...) (default: en)
  --async                   use async mode (polled, non-blocking)
  --play                    play audio in real-time using DirectSound
  --print-system-info       print system information

NOTE: In PowerShell, always quote the --guid value (e.g. "{A60EB5CF-9551-4B86-865B-CDC3CDBE61C4}"); an unquoted {...} is parsed as a script block.

Examples

Generate speech with custom text:

bin\<SDK_PLATFORM>\<Configuration>\nvigi.basic.tts.cxx.exe --models data/nvigi.models --sdk bin\<SDK_PLATFORM>\<Configuration> --speaker data/nvigi.test/nvigi.tts/chatterbox/spk_emb/aaron_turbo.json --text "Welcome to the NVIGI SDK text to speech system."

Use a different voice:

bin\<SDK_PLATFORM>\<Configuration>\nvigi.basic.tts.cxx.exe --models data/nvigi.models --sdk bin\<SDK_PLATFORM>\<Configuration> --speaker data/nvigi.test/nvigi.tts/chatterbox/spk_emb/lucy_turbo.json --text "Hello world!"

Use Vulkan backend:

bin\<SDK_PLATFORM>\<Configuration>\nvigi.basic.tts.cxx.exe --models data/nvigi.models --sdk bin\<SDK_PLATFORM>\<Configuration> --speaker data/nvigi.test/nvigi.tts/chatterbox/spk_emb/aaron_turbo.json --backend vulkan

Use CUDA backend:

bin\<SDK_PLATFORM>\<Configuration>\nvigi.basic.tts.cxx.exe --models data/nvigi.models --sdk bin\<SDK_PLATFORM>\<Configuration> --speaker data/nvigi.test/nvigi.tts/chatterbox/spk_emb/aaron_turbo.json --backend cuda

Multilingual model (French):

The multilingual model requires the multilingual GUID, --multilingual, a --language code, and a matching multilingual speaker JSON. The default --vram 4096 budget is sufficient (Multilingual requires ~3072 MB).

bin\<SDK_PLATFORM>\<Configuration>\nvigi.basic.tts.cxx.exe --models data/nvigi.models --sdk bin\<SDK_PLATFORM>\<Configuration> --guid "{A60EB5CF-9551-4B86-865B-CDC3CDBE61C4}" --multilingual --language fr --speaker data/nvigi.test/nvigi.tts/chatterbox/spk_emb/french.json --text "Bonjour, ceci est un test."

Save to custom output file:

bin\<SDK_PLATFORM>\<Configuration>\nvigi.basic.tts.cxx.exe --models data/nvigi.models --sdk bin\<SDK_PLATFORM>\<Configuration> --speaker data/nvigi.test/nvigi.tts/chatterbox/spk_emb/aaron_turbo.json --output my_speech.wav

Audio Format

The generated audio uses the following format:

  • Sample Rate: 24000 Hz (24 kHz)

  • Bit Depth: 16-bit PCM

  • Channels: Mono

API Patterns Demonstrated

Synchronous (Blocking) Generation

The sample demonstrates a simple blocking pattern where generation blocks until complete:

instance->generate(
    text,
    speaker_path,
    config,
    [&wav_writer](const int16_t* audio, size_t samples, ExecutionState state) -> ExecutionState {
        if (state == ExecutionState::DataPending || state == ExecutionState::Done) {
            // Write audio chunk to file
            wav_writer.write_samples(audio, samples);
            
            // Optionally play in real-time
            AudioPlayer::play_audio(audio, samples);
        }
        return state;  // Continue
    }
);

Asynchronous (Non-Blocking) Generation

The sample also demonstrates a polling-based async pattern perfect for game loops:

auto op = instance->generate_async(text, speaker_path, config);

// Game loop
while (!op.is_complete()) {
    // Try to get results (non-blocking)
    if (auto result = op.try_get_results()) {
        if (!result->audio.empty()) {
            // Write audio chunk to file
            wav_writer.write_samples(result->audio.data(), result->audio.size());
            
            // Play in real-time
            AudioPlayer::play_audio(result->audio.data(), result->audio.size());
            
            if (result->state == ExecutionState::Done) {
                break;
            }
        }
    }
    
    // Continue game logic
    render_frame();
    update_physics();
    process_input();
    
    // Small sleep to avoid busy-wait
    std::this_thread::sleep_for(std::chrono::milliseconds(10));
}

Voice Cloning

Voice cloning is achieved using speaker embedding JSON files. These files contain the speaker conditioning data for the voice you want to reproduce. The SDK test data includes several sample voices in <SDK_TEST>/nvigi.tts/chatterbox/spk_emb/:

  • Turbo (English): aaron_turbo.json, lucy_turbo.json

  • Multilingual (per-language): french.json, german.json, spanish.json, chinese.json, japanese.json, … and additional voices such as ethan_multilingual.json, meera_multilingual.json.

To use a different voice, change the --speaker parameter to point to a different embedding JSON. Multilingual voices require a multilingual speaker JSON (not interchangeable with Turbo speakers).

To clone your own voice, extract a speaker embedding from a short reference recording using the bundled toolkit at data/nvigi.test/nvigi.tts/chatterbox/voice_cloning/ (run setup_venv.ps1, then get_voice_embeddings.py). See Creating Custom Speaker Embeddings in the programming guide.

IMPORTANT: The speaker JSON must match the selected model. A Turbo speaker with --multilingual (or a multilingual speaker without it) is rejected with kResultInvalidParameter at instance creation.

Model Selection: Turbo vs Multilingual

  • Turbo (default, GUID {019BD494-...}): English-only, ~2x faster, supports paralinguistic tags ([laugh], [sigh], [gasp], …). --language and CFG weight are ignored.

  • Multilingual (--multilingual, GUID {A60EB5CF-...}): 23 languages with always-on CFG-based speech pacing and language-specific preprocessing. --language is required. Does not support paralinguistic tags.

See the Model Feature Comparison for the full breakdown.

Troubleshooting

“Failed to create TTS instance”

  • Verify the model GUID exists in your models directory

  • Check that you have sufficient VRAM. Multilingual needs ~3072 MB; if --vram is set below the model’s declared requirement the model is filtered out and creation fails. The default --vram 4096 is sufficient for both models.

  • Ensure the backend is properly installed (e.g., D3D12 requires Windows 10+)

Speaker / model mismatch

  • A Turbo speaker JSON used with --multilingual (or a multilingual speaker used without it) is rejected. Use a speaker JSON that matches the selected model.

Missing speaker JSON

  • Verify the path to the speaker JSON is correct

  • Check that the test data has been downloaded

  • Use an absolute path if relative paths aren’t working

No audio playback with --play

  • Real-time playback only works on Windows with DirectSound

  • Check that your audio output device is working

  • Try without --play and verify the WAV file is generated correctly

Out of memory errors

  • Close other GPU-intensive applications

  • Try a smaller text input

  • Note: reducing --vram below a model’s requirement will prevent it from loading

Poor audio quality

  • Provide complete, grammatically correct sentences with standard punctuation (see the programming guide’s input-quality guidance)

  • For the multilingual model, ensure --language matches your text

  • Very short inputs (1-2 words) are more likely to produce artifacts

Programming Notes

This sample demonstrates:

  • Modern C++ wrapper interfaces (nvigi::tts::Instance)

  • Blocking generation with streaming callbacks

  • Non-blocking async operations with polling (perfect for games)

  • Real-time audio playback using DirectSound

  • WAV file writing with proper headers

  • Builder pattern for runtime configuration

  • RAII-based resource management

  • std::expected for error handling

Building the Sample

The sample is built as part of the SDK build process. For full build instructions, see Building and Debugging. After building, stage the binaries:

copy_sdk_binaries.bat [-x64|-arm64] [Release|Debug|Production]

This ensures all DLLs and the executable are in the same directory (bin\<SDK_PLATFORM>\<Configuration>, where <SDK_PLATFORM> is the target CPU architecture and is the selected build configuration; see Platform Support for information on supported platforms).

Run in Debugger

For the general procedure and background, see the Building and Debugging guide. The settings specific to this sample are:

Property

Value

VS Project

nvigi/samples/nvigi.basic.cxx/tts

Command

<SDK_ROOT>\bin\<SDK_PLATFORM>\<Configuration>\nvigi.basic.tts.cxx.exe

Command Arguments

--models data\nvigi.models --sdk bin\<SDK_PLATFORM>\<Configuration> --speaker data\nvigi.test\nvigi.tts\chatterbox\spk_emb\aaron_turbo.json

Working Directory

<SDK_ROOT>\bin\<SDK_PLATFORM>\<Configuration>