Step 1 of 8

Engine & Configuration

Opening the sound card, picking a device, and knowing what the engine is doing. Namespace: OwnaudioNET

Everything starts here. OwnaudioNet is a static class, there is exactly one engine per application, and nothing else in the library works until it is initialized. Its whole job is three moments: initialize, start, shut down.

🧭

Just want sound? Quick Start covers it in fifteen lines. This page is what you read when the defaults aren't enough — a specific interface, lower latency, more headroom, or a log that tells you what went wrong.

What you can ask it

PropertyTypeDescription
IsInitializedboolWhether the audio system has been initialized.
IsRunningboolWhether the audio engine is actively processing.
EngineAudioEngineWrapper?The active engine wrapper. null before initialization. Access UnderlyingEngine for the IAudioEngine.
InputLatencyFramesintHardware capture latency in frames. 0 before capture starts. See Hardware Latency.
OutputLatencyFramesintHardware playback latency in frames. 0 before playback starts. See Hardware Latency.
VersionVersionLibrary version (4.0.4).

Start & shut down

Two flavours of the same three calls. Use the synchronous ones in console apps and services, the async ones in anything with a window.

âš ī¸

On Linux, Initialize() can block up to 5 seconds waiting for PulseAudio. Always use InitializeAsync() in UI applications.

C# — Synchronous (console apps / services)
// Default config: 48kHz, stereo, 512 frames
OwnaudioNet.Initialize();

// Custom config
OwnaudioNet.Initialize(config);

// Unit testing — no hardware required
OwnaudioNet.Initialize(config, useMockEngine: true);

// Custom config
OwnaudioNet.Initialize(config);

// Start and stop
OwnaudioNet.Start();
OwnaudioNet.Stop();

// Full release of all resources
OwnaudioNet.Shutdown();
C# — Asynchronous (recommended for UI apps)
// Default config
await OwnaudioNet.InitializeAsync();
await OwnaudioNet.InitializeAsync(cancellationToken);

// Custom config
await OwnaudioNet.InitializeAsync(config);
await OwnaudioNet.InitializeAsync(config, useMockEngine: false, cancellationToken: cancellationToken);

// Pre-created engine (custom platform implementations)
await OwnaudioNet.InitializeAsync(engine, config, cancellationToken: cancellationToken);

// Stop / Shutdown
await OwnaudioNet.StopAsync(cancellationToken);
await OwnaudioNet.ShutdownAsync(cancellationToken);
â„šī¸

bufferMultiplier is still accepted, but it no longer changes anything. Playback headroom moved into the engine's native render ring in 4.0 and is not tunable from managed code. For a heavy mix, raise AudioConfig.BufferSize or the mixer's bufferSizeInFrames instead.

Logging — turn this on first when something is wrong

The library reports every initialization, teardown and parameter change to the console, together with any error it would otherwise swallow. It is off by default — pass logLevel to Initialize whenever you want to see what is happening under the hood.

C#
using Logger;

// Silent — this is the default
OwnaudioNet.Initialize(config);

// The full picture: lifecycle, device switches, errors and warnings
OwnaudioNet.Initialize(config, logLevel: Log.Level.Info);

// Only what actually went wrong
await OwnaudioNet.InitializeAsync(config, logLevel: Log.Level.Error);

// Flip it any time while the API is running
Log.LoggerLevel = Log.Level.Info;      // on
Log.LoggerLevel = Log.Level.Disabled;  // off
LevelWhat you get
DisabledNothing at all. The default.
FatalErrorUnrecoverable faults only — device lost, recording aborted, a background loop giving up.
ErrorPlus every failure that would otherwise stay hidden.
WarningPlus recoverable fallbacks — a dropped sync client, a clock tier downgrade, a rejected device.
InfoEverything, including the whole lifecycle: engine start/stop, device switches, source and effect changes.
Sample output — Log.Level.Info
[21:04:12] [INFO] [EngineFactory] Creating RustAudioEngine (cpal): 48000Hz 2ch, buffer 512
[21:04:12] [INFO] [RustEngine] Initialized on CoreAudio: 48000Hz 2ch, out 'MacBook Pro Speakers' in '(none)', latency out/in 512/0 frames
[21:04:12] [INFO] [OwnaudioNet] Initialized
[21:04:13] [INFO] [Mixer] Source 'a3f1c8e2-...' added (1 total)
[21:04:13] [INFO] [Mixer] Started (rust-native), 1 sources
[21:04:31] [ERROR] [RustEngine] No output device named 'Focusrite Scarlett 2i2'
💡

Audio-rate code never logs per buffer. A failing effect or a broken socket reports its first occurrence and then a periodic summary, so the log stays readable while audio keeps flowing.

Three configs you don't have to write

Start from one of these and change what you need — they cover most cases.

C#
// 48kHz ¡ stereo ¡ 512 frames (~10.6ms)
AudioConfig config = OwnaudioNet.CreateDefaultConfig();

// 48kHz ¡ stereo ¡ 128 frames (~2.7ms)
AudioConfig config = OwnaudioNet.CreateLowLatencyConfig();

// 48kHz ¡ stereo ¡ 2048 frames (~42.7ms)
AudioConfig config = OwnaudioNet.CreateHighLatencyConfig();

Choosing a device

Leave OutputDeviceId at null and you get the system default, which is the right answer for most apps. Enumerate when you want to offer the user a choice — see the device picker recipe for the full flow.

C#
// Enumerate devices
List<AudioDeviceInfo> outputs = OwnaudioNet.GetOutputDevices();
List<AudioDeviceInfo> inputs  = OwnaudioNet.GetInputDevices();

// Async variants
List<AudioDeviceInfo> outputs = await OwnaudioNet.GetOutputDevicesAsync();
List<AudioDeviceInfo> inputs  = await OwnaudioNet.GetInputDevicesAsync();

// Use a specific device
var device = outputs.First(d => d.Name.Contains("Focusrite"));
config.OutputDeviceId = device.DeviceId;

// Hot-plug monitoring — pause when opening VST editors
OwnaudioNet.PauseDeviceMonitoring();
// ... open editor ...
OwnaudioNet.ResumeDeviceMonitoring();

AudioDeviceInfo Properties

PropertyTypeDescription
DeviceIdstringUnique identifier — pass to AudioConfig.OutputDeviceId.
NamestringHuman-readable device name.
EngineNamestringBackend: Wasapi, CoreAudio, PulseAudio, etc.
IsInput / IsOutputboolDevice direction.
IsDefaultboolSystem default device for its type.
MaxInputChannels / MaxOutputChannelsintHardware channel limits.

Hardware Latency

The samples you capture were grabbed a bit before the callback saw them, and what you send out won't hit the speaker until a bit after. That gap is the driver's hardware latency. The engine reads it back from the backend once audio is flowing and hands it to you in frames — divide by the sample rate for seconds. It's 0 until the first buffer runs, or when the backend doesn't report one.

C#
// Frames, live once the engine is running
int inFrames  = OwnaudioNet.InputLatencyFrames;
int outFrames = OwnaudioNet.OutputLatencyFrames;

double inMs = inFrames * 1000.0 / config.SampleRate;

// Line a recording up with the real timeline: the buffer you just
// got was actually captured inFrames earlier, so shift it back.
long trueCapturePosition = playbackPosition - inFrames;
💡

Mainly useful when you line captured audio up against a timeline or a backing track — recording and overdub work. Pure playback doesn't need it. On ASIO the driver reports real figures; some WASAPI/CoreAudio setups report 0 when the number isn't available.

Direct Audio I/O

These methods bypass the mixer. Prefer AudioMixer + sources for most use cases.

C#
// Send interleaved samples to output
OwnaudioNet.Send(ReadOnlySpan<float> samples);

// Receive from input — ALWAYS return the buffer!
float[]? buffer = OwnaudioNet.Receive(out int sampleCount);
if (buffer != null)
{
    // process buffer[0..sampleCount-1]
    OwnaudioNet.ReturnInputBuffer(buffer); // returns buffer to pool (zero-allocation)
}
đŸšĢ

Never discard a buffer returned by Receive() without calling ReturnInputBuffer(). The buffer comes from a pool and leaking it causes memory growth.

AudioMixer Registry

The last created AudioMixer is automatically registered for NetworkSync. You can override this:

C#
// Set the primary mixer explicitly (e.g. if you have multiple mixers)
OwnaudioNet.SetPrimaryAudioMixer(mixer);

// Get the currently registered mixer
AudioMixer? active = OwnaudioNet.GetRegisteredAudioMixer();

AudioConfig

The format and the hardware, in one object. You hand it to Initialize and it decides everything downstream — the sample rate every source resamples to, the latency you get, and which device makes noise. Namespace: Ownaudio.Core

PropertyTypeDefaultDescription
SampleRateint48000Sample rate in Hz. Common values: 44100, 48000, 96000.
Channelsint21 = mono, 2 = stereo.
BufferSizeint512Desired frames per buffer. Actual size negotiated with driver.
EnableOutputbooltrueEnable audio output (playback).
EnableInputboolfalseEnable audio input (recording / microphone).
OutputDeviceIdstring?nullOutput device ID from AudioDeviceInfo.DeviceId. null = system default.
InputDeviceIdstring?nullInput device ID. null = system default.
HostTypeEngineHostTypeNoneAudio backend: WASAPI, ASIO, WDMKS, COREAUDIO, ALSA, JACK, AAUDIO, OPENSL, WEBAUDIO. None lets the engine pick — leave it there unless you need ASIO.
FallbackToDefaultOnDisconnectbooltrueDevice unplugged mid-stream: true hops to the system default and hops back when it returns, false parks in DeviceDisconnected and waits.
C# — Common configurations
// Standard stereo output
var config = new AudioConfig { SampleRate = 48000, Channels = 2, BufferSize = 512 };

// Windows WASAPI, specific device, input disabled
var config = new AudioConfig
{
    SampleRate     = 48000,
    Channels       = 2,
    BufferSize     = 512,
    HostType       = EngineHostType.WASAPI,
    OutputDeviceId = myDevice.DeviceId,
    EnableInput    = false
};

// 8-channel multi-output device
var config = new AudioConfig
{
    Channels = 8
};

Next