TonnSDK Best Practices¶
This document outlines best practices and recommendations for getting optimal results with TonnSDK in your applications.
Table of Contents¶
- Audio Input Quality
- Track Organization
- GPU Mode Selection (v1.6)
- Creative Compression
- FX Chain JSON Export
- Settings-Only Processing
- AudioReprocessor Best Practices
- Performance Optimization
- Error Handling
- Memory Management
- Cross-Platform Considerations
- Advanced Workflow Strategies
Audio Input Quality¶
The quality of the final mix heavily depends on the input audio quality. Follow these recommendations:
Sample Rate and Bit Depth¶
- Use Consistent Sample Rates: Ensure all tracks have the same sample rate
- Recommended Sample Rates: 44.1 kHz or 48 kHz for standard projects
- Bit Depth: 32-bit floating point is optimal for internal processing
Gain Staging¶
- Avoid Clipping: Input tracks should not be clipped (exceeding ±1.0)
Audio Content Quality¶
- Minimize Bleed: Use tracks with minimal bleed from other instruments
- Clean Recordings: Minimize background noise and unwanted artifacts
- Phase Coherence: Check multi-miked sources for phase issues before processing
GPU Mode Selection (v1.6)¶
Version 1.6 introduces GPU-accelerated mixing for faster processing and higher quality mixes. Choose the appropriate mode based on your hardware and requirements.
When to Use Each Mode¶
Use CPU_STATIC (default) when:
- No NVIDIA GPU is available
- Running in environments without CUDA support (e.g., CI/CD, lightweight containers)
- Broad compatibility is needed across different hardware
- Processing small or simple mixes where GPU overhead isn't justified
Use GPU_STATIC when:
- An NVIDIA GPU with CUDA 12.2+ is available
- You want faster processing times and higher quality mixes
- Running in a production environment with GPU hardware (e.g., cloud instances with GPUs)
- Processing complex mixes with many tracks
GPU Mode Example¶
tonn::TonnSDK sdk(44100.0f, tonn::MusicalStyle::ROCK_INDIE);
sdk.initialize(licenseKey);
// Check hardware and select mode
sdk.setMixingModel(tonn::MixingModel::GPU_STATIC);
// Add tracks and process as normal
sdk.addTrack("drums.wav", drumSettings);
sdk.addTrack("bass.wav", bassSettings);
tonn::MixResult result = sdk.process();
// If GPU is unavailable, the SDK falls back to CPU automatically
Docker GPU Setup¶
For GPU mode in Docker, use the --gpus flag:
docker run --gpus all -e TONNSDK_LICENSE_KEY="your_key" tonnsdk:1.6.1
Creative Compression¶
Genre-specific stylized compression automatically applies professional compression presets based on musical style and instrument type.
When to Use Creative Compression¶
Enable Creative Compression (default) when: - You want genre-appropriate compression character (punchy drums for rock, tight bass for electronic, etc.) - Processing complete productions where consistent compression style is desired - Working with specific genres that benefit from stylized compression - Creating final mixes that need professional compression polish
Disable Creative Compression when: - You want a more transparent, natural sound without genre-specific coloring - The source material already has compression applied - You plan to apply your own compression in a DAW after exporting - Working with orchestral or acoustic material where transparency is preferred
Creative Compression Example¶
tonn::TonnSDK sdk(44100.0f, tonn::MusicalStyle::ROCK_INDIE);
sdk.setLicenseKey(licenseKey);
// Creative compression is ON by default
std::cout << "Creative compression: "
<< (sdk.isCreativeCompressionEnabled() ? "ON" : "OFF") << std::endl;
// Disable for transparent processing
sdk.setCreativeCompressionEnabled(false);
// Or explicitly enable for stylized compression
sdk.setCreativeCompressionEnabled(true);
// Process tracks - compression will be applied based on setting
tonn::MixResult result = sdk.process();
Instrument-Specific Compression¶
Creative compression applies different presets based on instrument type:
| Instrument Group | Compression Character |
|---|---|
| VOCAL_GROUP | Medium ratio, moderate attack for presence and clarity |
| DRUMS_GROUP, KICK_GROUP, SNARE_GROUP | Fast attack, punchy release for impact |
| BASS_GROUP | Tight control, consistent low-end |
| E_GUITAR_GROUP, ACOUSTIC_GUITAR_GROUP | Musical compression for sustain |
| SYNTH_GROUP, KEYS_GROUP | Subtle compression, preserve dynamics |
| PERCS_GROUP | Fast, transparent limiting |
Best Practice: Check FX Chain JSON¶
Use the FX Chain JSON to verify what compression was applied:
tonn::MixResult result = sdk.process();
// Parse FX chain to see compression settings
std::cout << result.fxChainsJson << std::endl;
// Look for "type": "COMPRESSOR", "subtype": "CREATIVE"
FX Chain JSON Export¶
The new fxChainsJson field in MixResult provides a complete ordered export of all effects applied to each track.
When to Use FX Chain Export¶
Use FX Chain JSON for: - DAW integration: Import exact settings into your DAW - Mix analysis: Understand what processing was applied - Preset generation: Save and reuse effect chains - A/B comparison: Compare settings between different mixes - Documentation: Record exactly how a mix was processed - Future bidirectional import: Foundation for importing settings back
FX Chain JSON Example¶
tonn::MixResult result = sdk.process();
// Check if FX chain data is available
if (!result.fxChainsJson.empty()) {
// Parse and display
json fxChains = json::parse(result.fxChainsJson);
std::cout << fxChains.dump(2) << std::endl; // Pretty print
// Save to file
std::ofstream fxFile("fx_chain_settings.json");
fxFile << fxChains.dump(2);
fxFile.close();
}
Understanding the FX Chain Structure¶
Each track's FX chain contains 9 ordered slots:
- LOW_CUT (order: 0) - High-pass filter applied based on instrument type
- GAIN (PRE_GAIN) (order: 1) - Loudness normalization before processing
- EQ (order: 2) - 6-band parametric EQ
- COMPRESSOR (CORRECTIVE) (order: 3) - Masking minimization compression
- COMPRESSOR (CREATIVE) (order: 4) - Genre-specific stylized compression
- PAN (order: 5) - Stereo positioning
- GAIN (POST_GAIN) (order: 6) - Final level adjustment
- REVERB (order: 7) - Reverb send amount
- SIDECHAIN_COMPRESSOR (order: 8) - Sidechain ducking
Best Practice: Track Names¶
Always pass track names for clearer FX chain output:
// Good: Pass the actual track name
sdk.addTrackFromBuffer(audioBuffer, settings, "lead_vocals.wav");
// Result in FX chain JSON: "trackName": "lead_vocals.wav"
// Avoid: Using default (results in "trackName": "buffer_track")
sdk.addTrackFromBuffer(audioBuffer, settings);
Skip Quiet Tracks¶
The skipQuietTracks parameter provides graceful handling of quiet or silent audio tracks, perfect for batch processing workflows.
When to Enable Skip Quiet Tracks¶
Enable skipQuietTracks (true) when: - Processing large collections of tracks where some might be silent or extremely quiet - Doing batch processing where you want to skip problematic tracks automatically - Working with user-generated content that might include silent/empty files - Processing exported tracks from DAWs where some tracks might be muted/empty
Keep skipQuietTracks disabled (false) when: - You need to guarantee every track is processed or get an explicit error - Working with curated content where all tracks should be valid - You want to handle quiet tracks with custom logic - Maximum control over error handling is required
Example: Content Processing Pipeline¶
// For user-uploaded content - enable skip quiet tracks
tonn::TonnSDK sdk(44100.0f, tonn::MusicalStyle::POP, true);
std::vector<std::string> userFiles = getUserUploadedFiles();
int processedCount = 0;
int skippedCount = 0;
for (const auto& file : userFiles) {
size_t tracksBefore = sdk.getTrackCount();
try {
sdk.addTrack(file, getSettingsForFile(file));
if (sdk.getTrackCount() > tracksBefore) {
processedCount++;
} else {
skippedCount++; // Track was skipped due to being quiet
}
} catch (const std::exception& e) {
// Handle other errors (file not found, format issues, etc.)
std::cerr << "Error processing " << file << ": " << e.what() << std::endl;
}
}
std::cout << "Processed: " << processedCount << " tracks, Skipped: " << skippedCount << " quiet tracks" << std::endl;
Pre-gain Information Access¶
Pre-gain preservation functionality makes loudness normalization data accessible in results.
Using Pre-gain Information¶
tonn::MixResult result = sdk.process();
// Log pre-gain information for each track
for (size_t i = 0; i < result.mixTrackSettings.size(); ++i) {
float preGainDb = result.mixTrackSettings[i].getPreGain();
float preGainLinear = std::pow(10.0f, preGainDb / 20.0f);
std::cout << "Track " << i << ":" << std::endl;
std::cout << " Pre-gain: " << preGainDb << " dB (" << preGainLinear << "x)" << std::endl;
std::cout << " Loudness normalization applied: " << (preGainDb != 0.0f ? "Yes" : "No") << std::endl;
}
Best Practices for Pre-gain Data¶
- Save pre-gain values for reprocessing workflows to maintain consistent loudness
- Use pre-gain for metering - tracks with large pre-gain adjustments may need source-level attention
- Apply pre-gain in stem exports to maintain the normalized loudness in individual stems
- Monitor extreme pre-gain values (> ±12 dB) as they may indicate source material issues
Track Organization¶
Proper track organization is critical for optimal results:
Group Types¶
- Accurate Classification: Always assign the most appropriate
GroupTypefor each track - Subdivide Instruments: Use specific group types (e.g.,
KICK_GROUP,SNARE_GROUP) for more control - Group Similar Instruments: When using subgroup mixing, group similar instruments together
Presence Settings¶
- Lead Elements: Use
PresenceSetting::LEADfor main vocal or solo instruments - Background Elements: Use
PresenceSetting::BACKGROUNDfor ambience, pads, or backing elements - Most Instruments: Use
PresenceSetting::NORMALfor most typical instruments
Example Organization Strategy¶
// Drums
kickSettings.setGroupType(tonn::GroupType::KICK_GROUP);
snareSettings.setGroupType(tonn::GroupType::SNARE_GROUP);
overheadsSettings.setGroupType(tonn::GroupType::CYMBALS_GROUP);
roomSettings.setGroupType(tonn::GroupType::DRUMS_GROUP);
// Bass
bassSettings.setGroupType(tonn::GroupType::BASS_GROUP);
// Guitars
rhythmGuitarSettings.setGroupType(tonn::GroupType::E_GUITAR_GROUP);
leadGuitarSettings.setGroupType(tonn::GroupType::E_GUITAR_GROUP);
leadGuitarSettings.setPresence(tonn::PresenceSetting::LEAD);
// Vocals
leadVocalSettings.setGroupType(tonn::GroupType::VOCAL_GROUP);
leadVocalSettings.setPresence(tonn::PresenceSetting::LEAD);
backingVocalSettings.setGroupType(tonn::GroupType::BACKING_VOX_GROUP);
Settings-Only Processing¶
The TonnSDK provides a powerful optimization mode through the settingsOnly parameter in the process() method. This feature can dramatically improve performance for specific workflows.
When to Use Settings-Only Mode¶
Use settingsOnly=true when you:
- Need fast parameter calculation: 10-50x faster execution than full processing
- Want to generate presets: Calculate optimized gain, EQ, compression, and panning settings
- Are working in memory-constrained environments: Significantly lower memory usage
- Need parameter inspection: Analyze what settings the SDK would apply
- Are building two-stage workflows: Calculate settings first, apply manually later
Settings-Only vs Full Processing¶
// Settings-only mode: Fast parameter calculation
tonn::MixResult settingsResult = tonnSDK.process(true);
// Returns optimized settings but no audio data
// - settingsResult.mixTrackSettings contains all optimized parameters
// - settingsResult.audio_x and stems are empty
// - settingsResult.trackLengthInSecs is still calculated
// Full processing mode: Complete audio processing
tonn::MixResult fullResult = tonnSDK.process(false); // or process()
// Returns complete results including processed audio
// - All audio processing applied
// - Final mix and stems generated
// - Optimized settings included
What's Included in Settings-Only Mode¶
When settingsOnly=true, the SDK calculates and returns:
- Optimized EQ parameters: Frequency bands, gains, and Q factors
- Compression settings: Threshold, ratio, attack, and release times
- Panning angles: Calculated stereo positioning
- Loudness normalization gains: Level adjustments for each track
- Track length: Duration information
- Track names: Corresponding to the input tracks
Example Workflow: Two-Stage Processing¶
// Stage 1: Fast settings calculation
std::cout << "Calculating optimized parameters..." << std::endl;
tonn::MixResult settingsResult = tonnSDK.process(true);
// Inspect or modify the calculated settings
for (size_t i = 0; i < settingsResult.mixTrackSettings.size(); ++i) {
auto& settings = settingsResult.mixTrackSettings[i];
// Log the optimized parameters
std::cout << "Track " << i << " optimized settings:" << std::endl;
std::cout << " Pan angle: " << settings.getPanAngle() << " degrees" << std::endl;
std::cout << " Gain: " << settings.getGain() << "x" << std::endl;
std::cout << " Compressor threshold: " << settings.getCompressorThreshold() << " dB" << std::endl;
}
// Stage 2: Apply settings manually or use them in another processing pipeline
// ... custom processing using the optimized parameters ...
Command-Line Integration¶
For applications with command-line interfaces, provide easy access to settings-only mode:
// Example command-line argument parsing
bool settingsOnly = false;
for (int i = 1; i < argc; ++i) {
std::string arg = argv[i];
if (arg == "--settings-only" || arg == "-s") {
settingsOnly = true;
std::cout << "Settings-only mode: Computing parameters without audio processing" << std::endl;
break;
}
}
tonn::MixResult result = tonnSDK.process(settingsOnly);
Performance Benefits¶
Settings-only mode provides significant advantages:
- Speed: 10-50x faster than full processing
- Memory: Much lower peak memory usage (no audio buffers for final processing)
- CPU: Minimal CPU usage during the settings calculation phase
- Scalability: Handle larger sessions that might not fit in memory for full processing
Use Cases¶
Preset Generation Systems:
// Generate mixing presets for different genres
for (const auto& genre : genres) {
tonnSDK.setMusicalStyle(genre);
auto presetSettings = tonnSDK.process(true);
savePreset(genre, presetSettings.mixTrackSettings);
}
Interactive Mixing Applications:
// Quick parameter preview before full processing
auto quickSettings = tonnSDK.process(true);
if (userApprovesSettings(quickSettings)) {
auto finalMix = tonnSDK.process(false); // Full processing
saveMix(finalMix);
}
Batch Analysis:
// Analyze multiple projects quickly
for (const auto& project : projects) {
loadProject(project, tonnSDK);
auto analysis = tonnSDK.process(true);
logProjectAnalysis(project, analysis.mixTrackSettings);
}
AudioReprocessor Best Practices¶
The AudioReprocessor feature provides powerful offline reprocessing capabilities. Follow these best practices to get optimal results:
1. Stem Management¶
Preserve Original Quality:
// Always load stems at their original quality
// Avoid re-encoding or quality loss before loading
bool loadOriginalStem(const std::string& stemPath) {
// Load at original sample rate and bit depth
return reprocessor.loadStem(stemPath, originalSettings);
}
Organize Stem Storage:
// Use organized directory structure for stems
struct StemLibrary {
std::string projectDir;
std::string stemsDir;
std::string settingsDir;
StemLibrary(const std::string& project)
: projectDir(project) {
stemsDir = projectDir + "/stems/";
settingsDir = projectDir + "/settings/";
// Create directories if they don't exist
createDirectoryIfNotExists(stemsDir);
createDirectoryIfNotExists(settingsDir);
}
void saveStems(const tonn::TonnSDK& sdk) {
sdk.saveOriginalStems(stemsDir, true);
}
};
2. Settings Preservation and Versioning¶
Save Original Settings:
// Always preserve original settings for reference
struct SettingsVersion {
std::vector<tonn::MixTrackSettings> settings;
std::string version;
std::string timestamp;
std::string description;
};
class SettingsManager {
private:
std::vector<SettingsVersion> settingsHistory;
public:
void saveSettingsVersion(const std::vector<tonn::MixTrackSettings>& settings,
const std::string& description) {
SettingsVersion version;
version.settings = settings;
version.version = "v" + std::to_string(settingsHistory.size() + 1);
version.timestamp = getCurrentTimestamp();
version.description = description;
settingsHistory.push_back(version);
// Save to JSON for persistence
saveSettingsToFile(version);
}
std::vector<tonn::MixTrackSettings> getSettingsVersion(const std::string& version) {
auto it = std::find_if(settingsHistory.begin(), settingsHistory.end(),
[&version](const SettingsVersion& sv) { return sv.version == version; });
if (it != settingsHistory.end()) {
return it->settings;
}
return {}; // Return empty if not found
}
};
3. Parameter Modification Strategies¶
Incremental Changes:
// Make incremental changes for better control
class ParameterAdjuster {
public:
static tonn::MixTrackSettings adjustCompression(const tonn::MixTrackSettings& base,
float ratioMultiplier,
float thresholdOffset) {
tonn::MixTrackSettings adjusted = base;
// Apply incremental changes
float currentRatio = base.getCompressorRatio();
float currentThreshold = base.getCompressorThreshold();
adjusted.setCompressorRatio(currentRatio * ratioMultiplier);
adjusted.setCompressorThreshold(currentThreshold + thresholdOffset);
return adjusted;
}
static tonn::MixTrackSettings adjustEQ(const tonn::MixTrackSettings& base,
int bandIndex,
float gainChange) {
tonn::MixTrackSettings adjusted = base;
auto currentGains = base.getEQGains();
if (bandIndex < currentGains.size()) {
currentGains[bandIndex] += gainChange;
adjusted.setEQGains(currentGains);
}
return adjusted;
}
};
Batch Parameter Changes:
// Group related parameter changes together
struct ParameterBatch {
std::vector<std::pair<size_t, tonn::MixTrackSettings>> changes;
std::string description;
void addChange(size_t trackIndex, const tonn::MixTrackSettings& newSettings) {
changes.emplace_back(trackIndex, newSettings);
}
};
class BatchProcessor {
public:
tonn::ReprocessResult applyBatch(tonn::AudioReprocessor& reprocessor,
const std::vector<tonn::MixTrackSettings>& baseSettings,
const ParameterBatch& batch) {
// Start with base settings
std::vector<tonn::MixTrackSettings> modifiedSettings = baseSettings;
// Apply all changes in the batch
for (const auto& [trackIndex, newSettings] : batch.changes) {
if (trackIndex < modifiedSettings.size()) {
modifiedSettings[trackIndex] = newSettings;
}
}
return reprocessor.reprocess(modifiedSettings);
}
};
4. Error Handling and Validation¶
Comprehensive Validation:
class ReprocessValidator {
public:
struct ValidationResult {
bool isValid = true;
std::vector<std::string> warnings;
std::vector<std::string> errors;
};
static ValidationResult validateReprocessSetup(const tonn::AudioReprocessor& reprocessor,
const std::vector<tonn::MixTrackSettings>& settings) {
ValidationResult result;
// Check stem count matches settings count
if (reprocessor.getStemCount() != settings.size()) {
result.isValid = false;
result.errors.push_back("Stem count doesn't match settings count");
}
// Validate each setting
for (size_t i = 0; i < settings.size(); ++i) {
validateSingleSetting(settings[i], result, i);
}
return result;
}
private:
static void validateSingleSetting(const tonn::MixTrackSettings& setting,
ValidationResult& result,
size_t index) {
// Check for extreme parameter values
if (setting.getCompressorRatio() > 20.0f) {
result.warnings.push_back("Track " + std::to_string(index) +
": Very high compression ratio");
}
if (std::abs(setting.getPanAngle()) > 90.0f) {
result.errors.push_back("Track " + std::to_string(index) +
": Pan angle out of range");
result.isValid = false;
}
}
};
5. Performance Optimization¶
Stem Caching:
class StemCache {
private:
std::map<std::string, std::pair<std::vector<std::vector<float>>, tonn::MixTrackSettings>> cache;
size_t maxCacheSize = 10; // Limit cache size
public:
bool loadStemFromCache(tonn::AudioReprocessor& reprocessor, const std::string& stemPath) {
auto it = cache.find(stemPath);
if (it != cache.end()) {
return reprocessor.loadStem(it->second.first, it->second.second,
getTrackNameFromPath(stemPath));
}
return false;
}
void cacheStem(const std::string& stemPath,
const std::vector<std::vector<float>>& audioBuffer,
const tonn::MixTrackSettings& settings) {
// Implement LRU cache logic
if (cache.size() >= maxCacheSize) {
cache.erase(cache.begin()); // Simple eviction
}
cache[stemPath] = {audioBuffer, settings};
}
};
Efficient Workflow:
// Reuse AudioReprocessor instances when possible
class EfficientReprocessor {
private:
std::unique_ptr<tonn::AudioReprocessor> reprocessor;
std::vector<std::string> loadedStems;
public:
bool setupForProject(const std::vector<std::string>& stemPaths,
const std::vector<tonn::MixTrackSettings>& settings) {
// Only recreate if necessary
if (!reprocessor || stemPaths != loadedStems) {
reprocessor = std::make_unique<tonn::AudioReprocessor>(44100.0f);
// Clear and reload stems
reprocessor->clearStems();
for (size_t i = 0; i < stemPaths.size() && i < settings.size(); ++i) {
if (!reprocessor->loadStem(stemPaths[i], settings[i])) {
return false;
}
}
loadedStems = stemPaths;
}
return true;
}
tonn::ReprocessResult quickReprocess(const std::vector<tonn::MixTrackSettings>& newSettings) {
return reprocessor->reprocess(newSettings);
}
};
6. Quality Control¶
A/B Comparison Framework:
class QualityController {
public:
struct ComparisonResult {
std::pair<std::vector<float>, std::vector<float>> originalMix;
std::pair<std::vector<float>, std::vector<float>> reprocessedMix;
float lufsOriginal;
float lufsReprocessed;
std::string analysisReport;
};
ComparisonResult compareVersions(const tonn::ReprocessResult& original,
const tonn::ReprocessResult& reprocessed) {
ComparisonResult result;
result.originalMix = original.mixedAudio;
result.reprocessedMix = reprocessed.mixedAudio;
// Analyze loudness (simplified)
result.lufsOriginal = calculateLUFS(original.mixedAudio);
result.lufsReprocessed = calculateLUFS(reprocessed.mixedAudio);
// Generate analysis report
result.analysisReport = generateAnalysisReport(result);
return result;
}
private:
float calculateLUFS(const std::pair<std::vector<float>, std::vector<float>>& audio) {
// Implement LUFS calculation
// This is a simplified placeholder
return -23.0f; // LUFS
}
std::string generateAnalysisReport(const ComparisonResult& result) {
std::stringstream report;
report << "LUFS Comparison:\n";
report << "Original: " << result.lufsOriginal << " LUFS\n";
report << "Reprocessed: " << result.lufsReprocessed << " LUFS\n";
report << "Difference: " << (result.lufsReprocessed - result.lufsOriginal) << " LUFS\n";
return report.str();
}
};
7. Workflow Integration¶
Template System:
class MixTemplate {
public:
struct Template {
std::string name;
std::string genre;
std::map<tonn::GroupType, tonn::MixTrackSettings> defaultSettings;
std::string description;
};
static Template createRockTemplate() {
Template rockTemplate;
rockTemplate.name = "Modern Rock";
rockTemplate.genre = "Rock";
rockTemplate.description = "Settings optimized for modern rock productions";
// Define default settings for each instrument group
tonn::MixTrackSettings drumSettings(tonn::GroupType::DRUMS_GROUP,
tonn::PresenceSetting::NORMAL,
tonn::MusicalStyle::ROCK_INDIE);
drumSettings.setCompressorRatio(4.0f);
drumSettings.setCompressorThreshold(-12.0f);
rockTemplate.defaultSettings[tonn::GroupType::DRUMS_GROUP] = drumSettings;
// Add other instrument groups...
return rockTemplate;
}
std::vector<tonn::MixTrackSettings> applyTemplate(const Template& tmpl,
const std::vector<tonn::GroupType>& trackGroups) {
std::vector<tonn::MixTrackSettings> settings;
for (const auto& groupType : trackGroups) {
auto it = tmpl.defaultSettings.find(groupType);
if (it != tmpl.defaultSettings.end()) {
settings.push_back(it->second);
} else {
// Use default settings for unknown groups
settings.emplace_back(groupType, tonn::PresenceSetting::NORMAL,
tonn::MusicalStyle::ROCK_INDIE);
}
}
return settings;
}
};
8. Best Practice Summary¶
- Always preserve original stems and settings for reference and rollback capability
- Use incremental parameter changes rather than extreme adjustments
- Implement comprehensive error handling and validation
- Cache stems when possible to improve performance for iterative workflows
- Group related parameter changes into batches for efficiency
- Validate parameter ranges before reprocessing to avoid processing failures
- Implement A/B comparison tools for quality control
- Use template systems for consistent results across projects
- Monitor resource usage when processing large numbers of stems
- Document all parameter changes for collaborative workflows
Performance Optimization¶
TonnSDK is designed for non-realtime processing, but performance is still important:
Primary Performance Strategy: Settings-Only Mode¶
Start with Settings-Only Processing: For maximum performance, use the settingsOnly=true parameter described in the Settings-Only Processing section. This provides 10-50x faster execution and is ideal for:
- Parameter calculation and inspection
- Preset generation workflows
- Memory-constrained environments
- Two-stage processing pipelines
Processing Strategy¶
- Background Processing: Run TonnSDK in a background thread to avoid blocking UI
- Progress Reporting: Implement progress reporting for long processing operations
- Caching Strategy: Cache processed results for previously processed content
Memory Efficiency¶
- Buffer Management: Release audio buffers after adding them to the SDK
- Batch Processing: For large projects, process tracks in batches if memory is constrained
- Release Resources: Call destructors properly to release SDK resources when done
Processing Large Sessions¶
- Subgroup Approach: Use the advanced subgroup mixing approach for large sessions
- Incremental Processing: Process subgroups one at a time and save stems
- Parallel Processing: Consider multi-threading for processing multiple subgroups in parallel
// Example of multi-threaded subgroup processing
std::vector<std::future<SubmixResult>> futures;
for (const auto& [groupType, tracks] : trackGroups) {
futures.push_back(std::async(std::launch::async, [&]() {
// Process group in separate thread
tonn::TonnSDK groupProcessor(static_cast<float>(sampleRate), style);
// ... add tracks and process ...
return SubmixResult{...};
}));
}
// Collect results when complete
std::vector<SubmixResult> submixes;
for (auto& future : futures) {
submixes.push_back(future.get());
}
Error Handling¶
Robust error handling is essential for professional applications:
Error Detection¶
- Check Return Values: Always check return values from SDK methods
- License Validation: Verify license validity before processing
- Input Validation: Validate audio input parameters before processing
Graceful Recovery¶
- Detailed Error Messages: Use
getLastErrorMessage()to get detailed error information - Fallback Strategies: Implement fallback processing if SDK operations fail
- User Feedback: Provide clear error messages to users with suggestion for resolution
Example Error Handling Approach¶
try {
// Attempt to process the mix
tonn::MixResult result = tonnSDK.process();
// Check if processing succeeded
if (result.audio_x.first.empty() || result.audio_x.second.empty()) {
std::cerr << "Processing failed: " << tonnSDK.getLastErrorMessage() << std::endl;
// Try fallback strategy or inform user
}
} catch (const std::exception& e) {
// Handle unexpected exceptions
std::cerr << "Critical error during processing: " << e.what() << std::endl;
// Log detailed information
logError("TonnSDK processing failed", e.what());
// Inform user and suggest recovery actions
notifyUser("Mix processing failed", "Please try with fewer tracks or contact support");
}
Memory Management¶
Efficient memory management is critical when working with audio data:
Resource Lifecycle¶
- Clean Initialization: Properly initialize all SDK objects
- Proper Cleanup: Destroy SDK instances when no longer needed
- Scope Management: Use smart pointers and RAII principles where applicable
Large Buffer Handling¶
- Buffer Reuse: Reuse buffers when possible to avoid repeated allocations
- Buffer Format: Use the most efficient buffer format for your application
// Example of reusing buffers
std::vector<std::vector<float>> reusableBuffer;
for (const auto& track : tracks) {
// Clear the buffer but retain capacity
for (auto& channel : reusableBuffer) {
channel.clear();
}
// Load new audio data into existing buffer
loadAudioIntoBuffer(track.filePath, reusableBuffer);
// Use the buffer
tonnSDK.addTrackFromBuffer(reusableBuffer, track.settings);
}
Cross-Platform Considerations¶
TonnSDK is cross-platform, but some platform-specific optimizations may be needed:
macOS¶
- Universal Binary Support: TonnSDK supports both Intel and Apple Silicon
- Architecture Selection: Request the appropriate build for your target architecture
- Dependency Management: Install x86_64 dependencies specifically when targeting Intel
# For macOS with both architectures
set(CMAKE_OSX_ARCHITECTURES "x86_64;arm64")
Windows¶
- Runtime Libraries: Match the runtime library setting with the SDK (typically /MD)
- Path Handling: Use proper path handling for Windows filesystem
- DLL Distribution: Ensure TonnSDK DLLs are properly distributed with your application
Linux¶
- Distribution Compatibility: Test with multiple Linux distributions
- Dependency Management: Use standard package managers or bundle dependencies
- Audio System Integration: Consider JACK, ALSA, or PulseAudio integration needs
Advanced Workflow Strategies¶
For professional applications, consider these advanced strategies:
DAW Integration Patterns¶
- Processing Module: Implement TonnSDK as a processing module or plugin
- Offline Rendering: Provide an offline rendering option for time-consuming mixes
- Preset Management: Allow saving and loading of processing presets
Track Analysis¶
- Pre-analysis: Analyze tracks before mixing to determine optimal settings
- Intelligent Grouping: Group tracks automatically based on audio content
- Metadata Use: Leverage track metadata to inform automatic classification
Mixing Template Approaches¶
Create mixing templates for common scenarios:
// Example mixing template for a rock song
void applyRockMixTemplate(tonn::TonnSDK& sdk) {
// Load template settings from configuration
json template = loadJsonConfig("templates/rock_mix.json");
// Apply template settings to current tracks
for (size_t i = 0; i < trackCount; ++i) {
if (trackGroups[i] == tonn::GroupType::DRUMS_GROUP) {
// Apply drum-specific template settings
applyTemplateToTrack(sdk, i, template["drums"]);
}
// Handle other instrument groups...
}
}
Real-world Production Workflows¶
- Stem Printing: Generate processed stems for further manual tweaking
- Two-stage Approach: Use TonnSDK for initial mix, then fine-tune manually
- A/B Comparison: Provide tools to compare TonnSDK output with reference mixes
By following these best practices, you can achieve optimal results with TonnSDK in your applications, ensuring reliable, high-quality audio processing across a wide range of scenarios.