TonnSDK Integration Guide

This guide provides step-by-step instructions for integrating TonnSDK into your audio application or Digital Audio Workstation (DAW).

Table of Contents

Prerequisites

Before integrating TonnSDK, ensure your development environment meets these requirements:

  • C++ Compiler: C++17 compatible compiler
  • Windows: MSVC 2019+
  • macOS: Clang 8+ (Xcode 11+)
  • Linux: GCC 7+ or Clang 8+
  • Build System: CMake 3.15+ recommended (3.14+ minimum)
  • Dependencies:
  • Most dependencies are bundled with TonnSDK
  • libsndfile (for examples and audio I/O)
  • OpenMP (optional, for enhanced performance)
  • nlohmann/json (automatically fetched by CMake when needed)

Installation

CMake Integration

The recommended way to integrate TonnSDK is using CMake:

  1. Add TonnSDK to your project:

```cmake # In your CMakeLists.txt file

# Option 1: If TonnSDK is installed in a standard location find_package(TonnSDK REQUIRED) target_link_libraries(your_target PRIVATE TonnSDK::tonnsdk)

# Option 2: If TonnSDK is in a custom location set(TonnSDK_DIR "/path/to/TonnSDK/cmake") find_package(TonnSDK REQUIRED) target_link_libraries(your_target PRIVATE TonnSDK::tonnsdk) ```

  1. Configure include directories:

cmake target_include_directories(your_target PRIVATE ${TonnSDK_INCLUDE_DIRS})

Manual Integration

For build systems other than CMake:

  1. Include the headers:

Add the TonnSDK include directory to your include path:

cpp #include "TonnSDK.h"

  1. Link against the library:
  • Windows: Link against tonnsdk.lib
  • macOS: Link against libtonnsdk.dylib
  • Linux: Link against libtonnsdk.so
  1. Deploy runtime libraries:
  • Windows: Include tonnsdk.dll with your application
  • macOS: Include libtonnsdk.dylib with your application
  • Linux: Include libtonnsdk.so with your application

Basic Integration Steps

Follow these steps to integrate TonnSDK into your application:

1. Initialize the SDK

#include "TonnSDK.h"
#include "MixTrackSettings.h"

// Initialize with sample rate and musical style
tonn::TonnSDK tonnSDK(44100.0f, tonn::MusicalStyle::POP);

// Or initialize with skip quiet tracks enabled (STABLE in v1.3.1)
// tonn::TonnSDK tonnSDK(44100.0f, tonn::MusicalStyle::POP, true);

// Initialize with license key (required)
if (!tonnSDK.initialize("your-license-key-here")) {
    std::cerr << "SDK initialization failed: " << tonnSDK.getLastErrorMessage() << std::endl;
    // Handle initialization error
    return;
}

// Verify license status (optional)
if (!tonnSDK.isLicenseValid()) {
    std::cerr << "License validation failed: " << tonnSDK.getLicenseStatus() << std::endl;
    return;
}

2. Configure Track Settings

// Create settings for a track using constructor
tonn::MixTrackSettings kickSettings(tonn::GroupType::KICK_GROUP, 
                                   tonn::PresenceSetting::NORMAL, 
                                   tonn::MusicalStyle::POP);

// Optional: Set additional parameters
kickSettings.setPanPreference(tonn::PanPreference::CENTER);  // Center position
kickSettings.setReverbSetting(tonn::ReverbSetting::LOW);     // Low reverb

// You can also create default settings and modify them
tonn::MixTrackSettings bassSettings;
bassSettings.setGroupType(tonn::GroupType::BASS_GROUP);
bassSettings.setPresence(tonn::PresenceSetting::NORMAL);

3. Add Audio Tracks

// Assuming audioBuffer is a multi-channel buffer of your audio data
// [channel][sample] format, as commonly used in DAWs
std::vector<std::vector<float>> kickBuffer = getAudioBufferFromYourSystem();

// Add the track
tonnSDK.addTrackFromBuffer(kickBuffer, kickSettings);

// Add more tracks as needed...

4. Process the Mix

// Process the mix (non-realtime operation)
tonn::MixResult result = tonnSDK.process();

// For faster processing when you only need settings:
// tonn::MixResult settingsResult = tonnSDK.process(true);  // settings-only mode

5. Use the Results

// Get the final stereo mix
auto finalMix = result.audio_x;  // std::pair<std::vector<float>, std::vector<float>>

// Access left and right channels
const std::vector<float>& leftChannel = finalMix.first;
const std::vector<float>& rightChannel = finalMix.second;

// Get processed stems (one for each input track)
auto stems = result.stems;  // std::vector<std::pair<std::vector<float>, std::vector<float>>>

// Access individual stems
for (size_t i = 0; i < stems.size(); ++i) {
    const std::vector<float>& stemLeft = stems[i].first;
    const std::vector<float>& stemRight = stems[i].second;
    std::cout << "Stem " << i << ": " << result.trackNames[i] << std::endl;
}

// Get optimized settings for each track
auto optimizedSettings = result.mixTrackSettings;

// Access optimized parameters
for (size_t i = 0; i < optimizedSettings.size(); ++i) {
    const auto& settings = optimizedSettings[i];
    std::cout << "Track " << i << " - Pan: " << settings.getPanAngle() 
              << "°, Gain: " << settings.getGain() << "x" << std::endl;
}

// Get track duration
float durationSeconds = result.trackLengthInSecs;

// Use these results in your application
// For example, write to disk, play through audio engine, etc.

GPU Integration (v1.6)

Version 1.6 adds GPU-accelerated mixing for faster processing and higher quality mixes. GPU mode is opt-in — the default CPU_STATIC mode requires no GPU hardware.

Requirements

  • NVIDIA GPU with CUDA compute capability 6.0+
  • CUDA Toolkit 12.2 or later
  • NVIDIA Driver compatible with CUDA 12.2+

Enabling GPU Mode

tonn::TonnSDK sdk(44100.0f, tonn::MusicalStyle::ROCK_INDIE);
sdk.initialize(licenseKey);

// Enable GPU-accelerated mixing
sdk.setMixingModel(tonn::MixingModel::GPU_STATIC);

// Process as normal — falls back to CPU if GPU is unavailable
tonn::MixResult result = sdk.process();

Docker with GPU

To run TonnSDK with GPU support in Docker, ensure the NVIDIA Container Toolkit is installed, then use the --gpus flag:

# Run with GPU support
docker run --gpus all -e TONNSDK_LICENSE_KEY="your_key" tonnsdk:1.6.1

# Or with docker-compose — uncomment the GPU section in docker-compose.yml
docker-compose up --build -d
docker-compose exec tonnsdk /bin/bash

GPU Fallback Behavior

If GPU_STATIC is requested but no compatible GPU is available, the SDK automatically falls back to CPU_STATIC processing. No error is thrown — the mix completes using CPU.

Cloud GPU Instances

For production deployments, GPU mode works well with cloud GPU instances: - AWS: g4dn.xlarge or p3.2xlarge instances - GCP: n1-standard with NVIDIA T4 or V100 - Azure: NC or NV series VMs

Stable Features

Skip Quiet Tracks Feature

Version 1.3.1 provides stable and enhanced support for gracefully handling quiet or silent audio tracks. This is particularly useful for batch processing workflows where some tracks might be below threshold.

Usage

// Enable skip quiet tracks for batch processing
tonn::TonnSDK sdk(44100.0f, tonn::MusicalStyle::ROCK_INDIE, true);

// Initialize with license
if (!sdk.initialize("your-license-key")) {
    std::cerr << "Failed to initialize SDK" << std::endl;
    return -1;
}

// Add tracks - quiet tracks will be skipped automatically
sdk.addTrack("quiet_track.wav", settings);   // Skipped with notification
sdk.addTrack("normal_track.wav", settings);  // Processed normally
sdk.addTrack("loud_track.wav", settings);    // Processed normally

// Process normally - quiet tracks are already filtered out
tonn::MixResult result = sdk.process();

When Tracks Are Skipped

Tracks are considered quiet and skipped when: - RMS level is below 0.0001 (extremely quiet) - ITU loudness measurement is below -70 LUFS - Audio contains only silence or near-silence

When tracks are skipped, you'll see clear notifications like:

Info: Skipping quiet track 'quiet_file.wav' - loudness (-85.2 LUFS) is below silence threshold (-70.0 LUFS)

Batch Processing Example

// Process a folder of tracks with skip quiet enabled
tonn::TonnSDK sdk(44100.0f, tonn::MusicalStyle::ROCK_INDIE, true);
sdk.initialize(licenseKey);

std::vector<std::string> trackFiles = getTrackFiles("./audio_folder/");
tonn::MixTrackSettings settings(tonn::GroupType::VOCAL_GROUP, 
                               tonn::PresenceSetting::LEAD, 
                               tonn::MusicalStyle::ROCK_INDIE);

for (const auto& file : trackFiles) {
    try {
        sdk.addTrack(file, settings);  // Quiet tracks automatically skipped
    } catch (const std::exception& e) {
        std::cerr << "Error adding track " << file << ": " << e.what() << std::endl;
    }
}

// Only non-quiet tracks will be processed
tonn::MixResult result = sdk.process();

Pre-gain Preservation Fix

Version 1.3.1 provides stable and enhanced pre-gain preservation functionality, where pre-gain values calculated during AddTracks are properly preserved throughout processing.

What's Fixed

  • Pre-gain values from AddTracks are now accurately preserved
  • Final MixResult contains correct pre-gain information for each track
  • Consistent loudness normalization data throughout the processing pipeline

Accessing Pre-gain Values

tonn::MixResult result = sdk.process();

for (size_t i = 0; i < result.mixTrackSettings.size(); ++i) {
    float preGain = result.mixTrackSettings[i].getPreGain();
    std::cout << "Track " << i << " pre-gain: " << preGain 
              << " dB (" << std::pow(10.0f, preGain / 20.0f) << "x)" << std::endl;
}

AudioReprocessor Integration

The AudioReprocessor feature allows for offline reprocessing of existing stems with modified settings, perfect for iterative mixing workflows and post-processing scenarios.

1. Creating an AudioReprocessor

#include "AudioReprocessor.h"

// Option 1: Create from existing TonnSDK instance (recommended)
tonn::TonnSDK tonnSDK(44100.0f, tonn::MusicalStyle::ROCK_INDIE);
std::unique_ptr<tonn::AudioReprocessor> reprocessor = tonnSDK.createReprocessor();

// Option 2: Create independently
tonn::AudioReprocessor reprocessor(44100.0f, tonn::MusicalStyle::ROCK_INDIE);

2. Loading Original Stems

// Load stems from original audio files
tonn::MixTrackSettings originalVocalSettings(tonn::GroupType::VOCAL_GROUP,
                                           tonn::PresenceSetting::LEAD,
                                           tonn::MusicalStyle::ROCK_INDIE);

if (!reprocessor->loadStem("original_vocals.wav", originalVocalSettings)) {
    std::cerr << "Failed to load vocal stem: " << reprocessor->getLastError() << std::endl;
    return;
}

// Load from buffer (useful for DAW integration)
std::vector<std::vector<float>> drumBuffer = getDrumStemFromDAW();
tonn::MixTrackSettings originalDrumSettings(tonn::GroupType::DRUMS_GROUP,
                                          tonn::PresenceSetting::NORMAL,
                                          tonn::MusicalStyle::ROCK_INDIE);

if (!reprocessor->loadStem(drumBuffer, originalDrumSettings, "Drums")) {
    std::cerr << "Failed to load drum stem: " << reprocessor->getLastError() << std::endl;
    return;
}

3. Modifying Settings and Reprocessing

// Create modified settings for reprocessing
std::vector<tonn::MixTrackSettings> modifiedSettings;

// Modify vocal settings - increase compression and EQ
tonn::MixTrackSettings newVocalSettings = originalVocalSettings;
newVocalSettings.setCompressorRatio(6.0f);  // More compression
newVocalSettings.setCompressorThreshold(-15.0f);  // Lower threshold
newVocalSettings.setEQGains({0, 3, -2, 1, 0, 0});  // EQ boost
modifiedSettings.push_back(newVocalSettings);

// Keep drum settings mostly the same, but adjust pan
tonn::MixTrackSettings newDrumSettings = originalDrumSettings;
newDrumSettings.setPanAngle(-15.0f);  // Slight left pan
modifiedSettings.push_back(newDrumSettings);

// Reprocess with new settings
tonn::ReprocessResult result = reprocessor->reprocess(modifiedSettings);

if (!result.success) {
    std::cerr << "Reprocessing failed: " << result.errorMessage << std::endl;
    return;
}

4. Using Reprocessed Results

if (result.hasValidAudio()) {
    // Save the new mix
    if (!reprocessor->saveReprocessedMix(result, "new_mix_v2.wav")) {
        std::cerr << "Failed to save reprocessed mix" << std::endl;
    }

    // Save individual reprocessed stems
    for (size_t i = 0; i < result.getStemCount(); ++i) {
        std::string filename = "reprocessed_" + result.trackNames[i] + ".wav";
        if (!reprocessor->saveReprocessedStem(result, i, filename)) {
            std::cerr << "Failed to save stem: " << filename << std::endl;
        }
    }

    // Access audio data directly for playback or further processing
    const auto& leftChannel = result.mixedAudio.first;
    const auto& rightChannel = result.mixedAudio.second;

    // Send to audio playback system
    playAudioInDAW(leftChannel, rightChannel, 44100.0f);
}

5. DAW Integration Pattern

Here's a complete pattern for DAW integration:

class DAWTonnSDKIntegration {
private:
    tonn::TonnSDK sdk_;
    std::unique_ptr<tonn::AudioReprocessor> reprocessor_;
    std::vector<tonn::MixTrackSettings> lastUsedSettings_;

public:
    DAWTonnSDKIntegration(float sampleRate, tonn::MusicalStyle style) 
        : sdk_(sampleRate, style) {
        if (!sdk_.initialize("your-license-key")) {
            throw std::runtime_error("Failed to initialize TonnSDK");
        }
        reprocessor_ = sdk_.createReprocessor();
    }

    // Initial mix processing
    bool processInitialMix(const std::vector<DAWTrack>& tracks) {
        // Add tracks to SDK
        for (const auto& track : tracks) {
            tonn::MixTrackSettings settings = createSettingsFromDAWTrack(track);
            sdk_.addTrackFromBuffer(track.audioBuffer, settings);
        }

        // Process and store results
        tonn::MixResult result = sdk_.process();

        // Store settings for potential reprocessing
        lastUsedSettings_ = result.mixTrackSettings;

        // Save original stems for later reprocessing
        sdk_.saveOriginalStems("./stems_backup/", true);

        return !result.audio_x.first.empty();
    }

    // Reprocess with user modifications
    bool reprocessWithModifications(const std::vector<ParameterChange>& changes) {
        // Load original stems into reprocessor
        auto originalPaths = sdk_.getOriginalFilePaths();
        for (size_t i = 0; i < originalPaths.size() && i < lastUsedSettings_.size(); ++i) {
            if (!reprocessor_->loadStem(originalPaths[i], lastUsedSettings_[i])) {
                return false;
            }
        }

        // Apply user modifications to settings
        std::vector<tonn::MixTrackSettings> modifiedSettings = lastUsedSettings_;
        for (const auto& change : changes) {
            applyParameterChange(modifiedSettings[change.trackIndex], change);
        }

        // Reprocess
        tonn::ReprocessResult result = reprocessor_->reprocess(modifiedSettings);

        if (result.success) {
            // Update DAW with new audio
            updateDAWWithNewMix(result.mixedAudio);
            return true;
        }

        return false;
    }

private:
    void applyParameterChange(tonn::MixTrackSettings& settings, const ParameterChange& change) {
        switch (change.parameter) {
            case ParameterType::GAIN:
                settings.setGain(change.value);
                break;
            case ParameterType::PAN:
                settings.setPanAngle(change.value);
                break;
            case ParameterType::COMPRESSOR_RATIO:
                settings.setCompressorRatio(change.value);
                break;
            // Handle other parameters...
        }
    }
};

6. Real-time Preview Pattern

For applications requiring real-time parameter adjustments:

class RealtimePreviewSystem {
private:
    std::unique_ptr<tonn::AudioReprocessor> reprocessor_;
    std::vector<tonn::MixTrackSettings> baselineSettings_;
    std::vector<std::string> stemPaths_;

public:
    bool setupPreview(const std::vector<std::string>& stemFiles, 
                     const std::vector<tonn::MixTrackSettings>& settings) {
        reprocessor_ = std::make_unique<tonn::AudioReprocessor>(44100.0f);
        baselineSettings_ = settings;
        stemPaths_ = stemFiles;

        // Pre-load all stems
        for (size_t i = 0; i < stemFiles.size() && i < settings.size(); ++i) {
            if (!reprocessor_->loadStem(stemFiles[i], settings[i])) {
                return false;
            }
        }

        return true;
    }

    // Quick preview of parameter changes
    std::pair<std::vector<float>, std::vector<float>> previewChanges(
        int trackIndex, const ParameterModification& mod) {

        // Create modified settings
        std::vector<tonn::MixTrackSettings> previewSettings = baselineSettings_;
        applyModification(previewSettings[trackIndex], mod);

        // Reprocess (this is fast for already-loaded stems)
        tonn::ReprocessResult result = reprocessor_->reprocess(previewSettings);

        if (result.success && result.hasValidAudio()) {
            return result.mixedAudio;
        }

        // Return empty on failure
        return {};
    }
};

7. Workflow Integration Best Practices

  • Cache Original Stems: Use saveOriginalStems() to save stems for later reprocessing
  • Batch Parameter Changes: Group multiple parameter changes into a single reprocess call
  • Error Handling: Always check ReprocessResult.success and handle errors gracefully
  • Resource Management: Clear stems with clearStems() when switching projects
  • Performance: AudioReprocessor is optimized for quick iteration on loaded stems

Platform-Specific Considerations

macOS

On Apple Silicon (arm64) Macs, the SDK provides universal binary support. However, some dependencies like FFTW might require special handling:

# For x86_64 libraries on Apple Silicon
arch -x86_64 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
/usr/local/bin/brew install fftw

In your build configuration:

# For x86_64 target on Apple Silicon
set(CMAKE_OSX_ARCHITECTURES "x86_64")
set(CMAKE_PREFIX_PATH "/usr/local")

Windows

Ensure proper Runtime Library settings:

  • For Debug: /MDd (Dynamic Runtime Library)
  • For Release: /MD (Dynamic Runtime Library)

Linux

Ensure all dependencies are installed:

# Ubuntu/Debian
sudo apt-get install libfftw3-dev libarmadillo-dev libsndfile-dev libomp-dev

Advanced Integration Patterns

Settings-Only Processing for Parameter Analysis

For applications that need to analyze optimal settings without full audio processing:

// Fast parameter calculation (10-50x faster than full processing)
tonn::MixResult settingsResult = tonnSDK.process(true);

// Analyze the optimized parameters
for (size_t i = 0; i < settingsResult.mixTrackSettings.size(); ++i) {
    const auto& settings = settingsResult.mixTrackSettings[i];

    // Extract calculated parameters
    float panAngle = settings.getPanAngle();
    float gain = settings.getGain();
    float compThreshold = settings.getCompressorThreshold();
    float compRatio = settings.getCompressorRatio();

    // Use these parameters in your own processing pipeline
    applyCustomProcessing(i, panAngle, gain, compThreshold, compRatio);
}

Group-Based Processing

For professional workflows, use the subgroup processing approach:

// Process tracks by instrument group
std::map<tonn::GroupType, std::vector<TrackInfo>> trackGroups;

// Group your tracks by GroupType
// ...

// Process each group separately
std::vector<SubmixResult> submixes;
for (const auto& [groupType, tracks] : trackGroups) {
    tonn::TonnSDK groupProcessor(sampleRate, style);

    // Initialize the group processor
    if (!groupProcessor.initialize(licenseKey)) {
        std::cerr << "Failed to initialize group processor" << std::endl;
        continue;
    }

    // Add tracks from this group
    for (const auto& track : tracks) {
        tonn::MixTrackSettings trackSettings(groupType, 
                                           tonn::PresenceSetting::NORMAL, 
                                           style);
        groupProcessor.addTrackFromBuffer(track.buffer, trackSettings);
    }

    // Process this group
    tonn::MixResult groupResult = groupProcessor.process();

    // Store submix
    submixes.push_back({groupType, groupResult.audio_x});
}

// Create final mix from submixes
tonn::TonnSDK finalMixProcessor(sampleRate, style);
if (!finalMixProcessor.initialize(licenseKey)) {
    std::cerr << "Failed to initialize final mix processor" << std::endl;
    return;
}

for (const auto& submix : submixes) {
    tonn::MixTrackSettings submixSettings(submix.groupType, 
                                         tonn::PresenceSetting::NORMAL, 
                                         style);

    // Convert stereo pair to multi-channel buffer format
    std::vector<std::vector<float>> submixBuffer = {submix.audio.first, submix.audio.second};
    finalMixProcessor.addTrackFromBuffer(submixBuffer, submixSettings);
}

// Process final mix
tonn::MixResult finalMixResult = finalMixProcessor.process();

Two-Stage Processing Workflow

Combine settings-only processing with full processing for optimal workflows:

// Stage 1: Fast parameter calculation
tonn::MixResult settingsResult = tonnSDK.process(true);

// Allow user to review/modify settings
if (userWantsToModifySettings(settingsResult.mixTrackSettings)) {
    // User can adjust the calculated parameters
    modifySettingsBasedOnUserInput(settingsResult.mixTrackSettings);
}

// Stage 2: Apply the (possibly modified) settings with full processing
// Note: You would need to create a new SDK instance with the modified settings
// or use the settings to guide your own processing pipeline
tonn::MixResult finalResult = tonnSDK.process(false);  // Full processing

Troubleshooting

Common Issues

  1. SDK Initialization Failure: - Ensure the license key is correct and valid - Check that initialize() is called after the constructor - Verify system date/time is accurate - Use getLastErrorMessage() for detailed error information

  2. License Validation Issues: - Use getLicenseStatus() to get detailed license information - Check getRemainingDays() to verify license expiration - Ensure getLicensee() matches expected customer information

  3. Build Errors: - Verify all dependencies are correctly installed - Ensure compatible compiler version (C++17 required) - Check build configuration matches (Debug/Release) - Verify include paths point to correct TonnSDK headers

  4. Runtime Processing Errors: - Use getLastErrorMessage() to diagnose processing issues - Verify audio buffers are correctly formatted (multi-channel: [channel][sample]) - Ensure sample rates are consistent across all tracks - Check that MixTrackSettings are properly configured

  5. Performance Issues: - Remember TonnSDK is designed for non-realtime processing - Use process(true) for settings-only mode when you only need parameters - Process in a background thread to avoid blocking UI - Consider using the group-based approach for large sessions (20+ tracks)

  6. Memory Issues: - For large sessions, use settings-only mode first to verify feasibility - Process tracks in groups rather than all at once - Ensure proper cleanup of audio buffers after adding to SDK

Debug Information

// Example error handling and diagnostics
tonn::TonnSDK sdk(44100.0f, tonn::MusicalStyle::ROCK_INDIE);

if (!sdk.initialize(licenseKey)) {
    std::cerr << "Initialization failed: " << sdk.getLastErrorMessage() << std::endl;
    std::cerr << "License status: " << sdk.getLicenseStatus() << std::endl;
    return 1;
}

std::cout << "Licensed to: " << sdk.getLicensee() << std::endl;
std::cout << "Days remaining: " << sdk.getRemainingDays() << std::endl;

try {
    tonn::MixResult result = sdk.process();

    if (result.audio_x.first.empty()) {
        std::cerr << "Processing failed: " << sdk.getLastErrorMessage() << std::endl;
        return 1;
    }

    std::cout << "Processing successful. Duration: " 
              << result.trackLengthInSecs << " seconds" << std::endl;

} catch (const std::exception& e) {
    std::cerr << "Exception during processing: " << e.what() << std::endl;
    std::cerr << "Last SDK error: " << sdk.getLastErrorMessage() << std::endl;
    return 1;
}