Complete AudioMixer Example

This comprehensive example demonstrates the usage of OwnaudioNET with multi-track playback, master effects, and synchronized playback control.

Developer Example This code demonstrates the practical usage of the OwnaudioNET API. All classes and methods used in this example are documented in the API reference.

Overview

This example program demonstrates the following features:

Step 1: Audio Engine Initialization

The first step is to initialize the audio engine with a custom configuration. We configure the sample rate, channel count, and buffer size for optimal performance.

Engine Initialization
using Ownaudio.Core;
using OwnaudioNET.Core;

AudioConfig config = new AudioConfig()
{
    SampleRate = 48000,
    Channels = 2,
    BufferSize = 512
};

OwnaudioNet.Initialize(config);

Console.WriteLine($"Initialized: {OwnaudioNet.IsInitialized}");
Console.WriteLine($"Version: {OwnaudioNet.Version}");
Console.WriteLine($"Sample Rate: {OwnaudioNet.Engine?.Config.SampleRate} Hz");
Console.WriteLine($"Channels: {OwnaudioNet.Engine?.Config.Channels}");
Console.WriteLine($"Buffer Size: {OwnaudioNet.Engine?.FramesPerBuffer} frames");

What happens here:

Step 2: Starting the Audio Engine

After initialization, we start the audio engine to begin processing audio.

Starting Engine
OwnaudioNet.Start();

Console.WriteLine($"Engine running: {OwnaudioNet.IsRunning}");

What happens here:

Step 3: Creating the Audio Mixer

We create an AudioMixer instance with direct engine access for optimal performance, bypassing additional wrapper layers.

Mixer Creation
using OwnaudioNET.Mixing;

var Engine = OwnaudioNet.Engine!.UnderlyingEngine;

mixer = new AudioMixer(Engine, bufferSizeInFrames: 512);

mixer.MasterVolume = 0.8f;

mixer.SourceError += (sender, e) =>
{
    Console.WriteLine($"Source error: {e.Message}");
};

What happens here:

Step 4: Master Effects Configuration

Configure and add master effects that will be applied to the final mixed output. These effects process all sources together.

Equalizer Setup (30-Band)
using OwnaudioNET.Effects;

var _equalizer = new Equalizer30BandEffect();

_equalizer.SetBandGain(band: 0, frequency: 20, q: 0.5f, gainDB: 0.2f);
_equalizer.SetBandGain(band: 1, frequency: 25, q: 0.5f, gainDB: 0.4f);
_equalizer.SetBandGain(band: 2, frequency: 31, q: 0.6f, gainDB: 0.6f);
_equalizer.SetBandGain(band: 3, frequency: 40, q: 0.7f, gainDB: 0.8f);
_equalizer.SetBandGain(band: 4, frequency: 50, q: 0.7f, gainDB: 0.8f);
_equalizer.SetBandGain(band: 5, frequency: 63, q: 0.7f, gainDB: -0.3f);

_equalizer.SetBandGain(band: 6, frequency: 80, q: 0.8f, gainDB: 0.3f);
_equalizer.SetBandGain(band: 7, frequency: 100, q: 0.8f, gainDB: 0.5f);
_equalizer.SetBandGain(band: 8, frequency: 125, q: 0.9f, gainDB: 0.3f);
_equalizer.SetBandGain(band: 9, frequency: 160, q: 0.9f, gainDB: 0.1f);

_equalizer.SetBandGain(band: 10, frequency: 200, q: 1.0f, gainDB: -0.4f);
_equalizer.SetBandGain(band: 11, frequency: 250, q: 1.0f, gainDB: -0.8f);
_equalizer.SetBandGain(band: 12, frequency: 315, q: 1.1f, gainDB: -0.7f);
_equalizer.SetBandGain(band: 13, frequency: 400, q: 1.1f, gainDB: -0.5f);
_equalizer.SetBandGain(band: 14, frequency: 500, q: 1.0f, gainDB: -0.2f);

_equalizer.SetBandGain(band: 15, frequency: 630, q: 1.0f, gainDB: -0.1f);
_equalizer.SetBandGain(band: 16, frequency: 800, q: 1.0f, gainDB: 0.0f);
_equalizer.SetBandGain(band: 17, frequency: 1000, q: 1.0f, gainDB: 0.0f);
_equalizer.SetBandGain(band: 18, frequency: 1250, q: 1.0f, gainDB: 0.0f);
_equalizer.SetBandGain(band: 19, frequency: 1600, q: 1.0f, gainDB: 0.1f);

_equalizer.SetBandGain(band: 20, frequency: 2000, q: 1.0f, gainDB: 0.3f);
_equalizer.SetBandGain(band: 21, frequency: 2500, q: 0.9f, gainDB: 0.5f);
_equalizer.SetBandGain(band: 22, frequency: 3150, q: 0.9f, gainDB: 0.7f);
_equalizer.SetBandGain(band: 23, frequency: 4000, q: 0.8f, gainDB: 0.5f);
_equalizer.SetBandGain(band: 24, frequency: 5000, q: 0.7f, gainDB: 0.3f);

_equalizer.SetBandGain(band: 25, frequency: 6300, q: 0.7f, gainDB: 0.3f);
_equalizer.SetBandGain(band: 26, frequency: 8000, q: 0.6f, gainDB: 0.6f);
_equalizer.SetBandGain(band: 27, frequency: 10000, q: 0.6f, gainDB: 0.8f);
_equalizer.SetBandGain(band: 28, frequency: 12500, q: 0.5f, gainDB: 1.0f);
_equalizer.SetBandGain(band: 29, frequency: 16000, q: 0.5f, gainDB: 1.0f);

What happens here:

Compressor and Dynamic Amp
var _compressor = new CompressorEffect(CompressorPreset.Vintage);

mixer.AddMasterEffect(_equalizer);
mixer.AddMasterEffect(_compressor);
mixer.AddMasterEffect(new DynamicAmpEffect(DynamicAmpPreset.Live));

_equalizer.Enabled = false;
_compressor.Enabled = false;

What happens here:

Step 5: Creating Audio Sources

Create multiple audio sources from WAV files and configure them for synchronized playback.

File Source Creation
using OwnaudioNET.Sources;
using System.Reflection;

string? exePath = Assembly.GetExecutingAssembly().Location;
string? exeDirectory = Path.GetDirectoryName(exePath);

string audioFilePath0 = Path.Combine(exeDirectory, "media", "drums.wav");
string audioFilePath1 = Path.Combine(exeDirectory, "media", "bass.wav");
string audioFilePath2 = Path.Combine(exeDirectory, "media", "other.wav");
string audioFilePath3 = Path.Combine(exeDirectory, "media", "vocals.wav");

int targetSampleRate = OwnaudioNet.Engine!.Config.SampleRate;
int targetChannels = OwnaudioNet.Engine!.Config.Channels;

fileSource0 = new FileSource(audioFilePath0, 8192, targetSampleRate, targetChannels);
fileSource1 = new FileSource(audioFilePath1, 8192, targetSampleRate, targetChannels);
fileSource2 = new FileSource(audioFilePath2, 8192, targetSampleRate, targetChannels);
fileSource3 = new FileSource(audioFilePath3, 8192, targetSampleRate, targetChannels);

fileSource0.Volume = 0.9f;
fileSource1.Volume = 0.85f;
fileSource2.Volume = 0.8f;
fileSource3.Volume = 1.0f;

What happens here:

Step 6: Synchronized Playback Setup

Add sources to the mixer and configure Master Clock synchronization for sample-accurate playback.

Master Clock Synchronization (v2.1.0+) This example uses the new timeline-based Master Clock system for professional DAW-style synchronization.
Mixer Configuration and Master Clock Sync
mixer.AddSource(fileSource0);
mixer.AddSource(fileSource1);
mixer.AddSource(fileSource2);
mixer.AddSource(fileSource3);

// Attach all sources to Master Clock for sample-accurate sync
fileSource0.AttachToClock(mixer.MasterClock);
fileSource1.AttachToClock(mixer.MasterClock);
fileSource2.AttachToClock(mixer.MasterClock);
fileSource3.AttachToClock(mixer.MasterClock);

// Optional: Set timeline positions (all start at 0.0 by default)
fileSource0.StartOffset = 0.0;  // Drums start immediately
fileSource1.StartOffset = 0.0;  // Bass starts immediately
fileSource2.StartOffset = 0.0;  // Other starts immediately
fileSource3.StartOffset = 0.0;  // Vocals start immediately

// Start mixer
mixer.Start();

// Start all sources for playback
fileSource0.Play();
fileSource1.Play();
fileSource2.Play();
fileSource3.Play();

// All attached sources now play in perfect sync

What happens here:

Master Clock Benefits Unlike legacy sync groups, Master Clock provides:
  • Timeline-based positioning with start offsets
  • Sample-accurate synchronization (not just frame-accurate)
  • Realtime and Offline rendering modes
  • Automatic tempo-independent timing
  • Per-track tempo control with synchronized master timeline

Step 7: Real-time Monitoring and Dynamic Effects

Monitor playback progress and peak levels in real-time, with dynamic effect activation during playback.

Monitoring Loop
DateTime startTime = DateTime.Now;

while (fileSource0.State == SourceState.Playing)
{
    Thread.Sleep(100);

    double position = fileSource0.Position;
    double duration = fileSource0.Duration;
    int progressPercent = (int)((position / duration) * 100);
    
    int barWidth = 40;
    int filledWidth = (int)((position / duration) * barWidth);
    string progressBar = new string('█', filledWidth) + new string('░', barWidth - filledWidth);

    string infoLine = $"Position: {TimeSpan.FromSeconds(position)} / {TimeSpan.FromSeconds(duration)} [{progressBar}] {progressPercent}%";
    string peakLine = $"| Peaks: L={mixer.LeftPeak:F2} R={mixer.RightPeak:F2}";

    Console.Write(infoLine + peakLine);

    if (position > 30 && position < 35)
    {
        _equalizer.Enabled = true;
        _compressor.Enabled = true;
    }
}

What happens here:

Playback Statistics
Console.WriteLine("Playback completed!");

TimeSpan elapsed = DateTime.Now - startTime;
double finalPosition = fileSource0.Position;

Console.WriteLine($"Real-time elapsed: {elapsed.TotalSeconds:F2} seconds");
Console.WriteLine($"Audio position reached: {finalPosition:F2} seconds");

double tempoRatio = finalPosition / elapsed.TotalSeconds;
double tempoError = (tempoRatio - 1.0) * 100.0;

Console.WriteLine($"Tempo ratio: {tempoRatio:F4} (1.0000 = perfect)");
if (Math.Abs(tempoError) < 0.5)
{
    Console.WriteLine($"Tempo accuracy: EXCELLENT ({tempoError:+0.00;-0.00}%)");
}
else if (Math.Abs(tempoError) < 2.0)
{
    Console.WriteLine($"Tempo accuracy: Good ({tempoError:+0.00;-0.00}%)");
}
else
{
    Console.WriteLine($"Tempo accuracy: POOR ({tempoError:+0.00;-0.00}%)");
}

What happens here:

Step 8: Cleanup and Shutdown

Properly dispose of all resources and shut down the audio engine.

Resource Cleanup
Console.WriteLine("=== FINAL STATISTICS ===");
Console.WriteLine($"Total mixed frames: {mixer.TotalMixedFrames}");
Console.WriteLine($"Total underruns: {mixer.TotalUnderruns}");
Console.WriteLine($"Master volume: {mixer.MasterVolume:P0}");
Console.WriteLine($"Source state: {fileSource0.State}");
Console.WriteLine($"Final position: {fileSource0.Position:F2}s / {fileSource0.Duration:F2}s");

Console.WriteLine("=== CLEANUP ===");

mixer.Stop();
mixer.StopSyncGroup("Demo");
mixer.Dispose();

fileSource0.Dispose();
fileSource1.Dispose();
fileSource2.Dispose();
fileSource3.Dispose();

OwnaudioNet.Stop();
OwnaudioNet.Shutdown();

What happens here:

Complete Code

Here's the complete example with all necessary using statements and error handling:

Complete Program.cs
using Ownaudio.Core;
using OwnaudioNET.Core;
using OwnaudioNET.Effects;
using OwnaudioNET.Mixing;
using OwnaudioNET.Sources;
using System.Reflection;

namespace OwnaudioNET.Test;

public class TestProgram
{
    public static void Main(string[] args)
    {
        AudioMixer? mixer = null;
        FileSource? fileSource0 = null;
        FileSource? fileSource1 = null;
        FileSource? fileSource2 = null;
        FileSource? fileSource3 = null;

        try
        {
            AudioConfig config = new AudioConfig()
            {
                SampleRate = 48000,
                Channels = 2,
                BufferSize = 512
            };
            
            OwnaudioNet.Initialize(config);
            OwnaudioNet.Start();

            var Engine = OwnaudioNet.Engine!.UnderlyingEngine;
            mixer = new AudioMixer(Engine, bufferSizeInFrames: 512);
            mixer.MasterVolume = 0.8f;

            var _equalizer = new Equalizer30BandEffect();
            var _compressor = new CompressorEffect(CompressorPreset.Vintage);
            
            mixer.AddMasterEffect(_equalizer);
            mixer.AddMasterEffect(_compressor);
            mixer.AddMasterEffect(new DynamicAmpEffect(DynamicAmpPreset.Live));
            
            _equalizer.Enabled = false;
            _compressor.Enabled = false;

            string? exeDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
            int targetSampleRate = OwnaudioNet.Engine!.Config.SampleRate;
            int targetChannels = OwnaudioNet.Engine!.Config.Channels;

            fileSource0 = new FileSource(Path.Combine(exeDirectory, "media", "drums.wav"), 8192, targetSampleRate, targetChannels);
            fileSource1 = new FileSource(Path.Combine(exeDirectory, "media", "bass.wav"), 8192, targetSampleRate, targetChannels);
            fileSource2 = new FileSource(Path.Combine(exeDirectory, "media", "other.wav"), 8192, targetSampleRate, targetChannels);
            fileSource3 = new FileSource(Path.Combine(exeDirectory, "media", "vocals.wav"), 8192, targetSampleRate, targetChannels);

            mixer.AddSource(fileSource0);
            mixer.AddSource(fileSource1);
            mixer.AddSource(fileSource2);
            mixer.AddSource(fileSource3);

            var syncGroup = mixer.CreateSyncGroup("Demo", fileSource0, fileSource1, fileSource2, fileSource3);
            mixer.Start();
            mixer.StartSyncGroup("Demo");

            DateTime startTime = DateTime.Now;

            while (fileSource0.State == SourceState.Playing)
            {
                Thread.Sleep(100);
                
                double position = fileSource0.Position;
                double duration = fileSource0.Duration;
                
                if (position > 30 && position < 35)
                {
                    _equalizer.Enabled = true;
                    _compressor.Enabled = true;
                }
            }

            mixer.Stop();
            mixer.StopSyncGroup("Demo");
            mixer.Dispose();

            fileSource0.Dispose();
            fileSource1.Dispose();
            fileSource2.Dispose();
            fileSource3.Dispose();

            OwnaudioNet.Stop();
            OwnaudioNet.Shutdown();
        }
        catch (Exception ex)
        {
            Console.WriteLine($"ERROR: {ex.Message}");
            
            fileSource0?.Dispose();
            fileSource1?.Dispose();
            fileSource2?.Dispose();
            fileSource3?.Dispose();
            mixer?.Dispose();
            OwnaudioNet.Shutdown();
            
            Environment.Exit(1);
        }
    }
}
Key Takeaways This example demonstrates professional audio application development with OwnaudioNET, including multi-track synchronization, real-time effects processing, and proper resource management.

Next Steps

📚 Core API Reference

Learn about low-level audio engine components and direct platform access.

View Core API →

📚 NET API Reference

Explore high-level APIs for mixing, effects, and audio sources.

View NET API →

🎚️ Audio Effects

Discover available effects like EQ, compressor, reverb, and more.

View Effects →