Ownaudio.Native is the PRIMARY audio engine for OwnAudioSharp v3.x, providing cross-platform audio playback and recording using PortAudio (when available) or MiniAudio (bundled fallback).
This engine represents a hybrid approach that prioritizes PortAudio for better audio quality and system integration, while ensuring MiniAudio is always available as a reliable fallback.
The engine uses an intelligent backend selection strategy:
-
Try PortAudio first (all platforms):
- Checks bundled library in
runtimes/{rid}/native/ - Checks system-installed PortAudio:
- macOS: Homebrew paths (
/opt/homebrewfor ARM64,/usr/localfor x64) - Linux: System library paths (
/usr/lib/{arch}-linux-gnu/) - Windows: Bundled
libportaudio.dllfor x64
- macOS: Homebrew paths (
- Checks bundled library in
-
Fallback to MiniAudio (always bundled):
- Used when PortAudio is not found or fails to initialize
- Bundled for all platforms: Windows, Linux, macOS, Android, iOS
- ✅ Zero external dependencies - MiniAudio is always bundled
- ✅ System integration - Automatic detection of system-installed PortAudio
- ✅ Cross-platform - Works on Windows, Linux, macOS, Android, iOS
- ✅ Zero-allocation design - Lock-free ring buffers for RT-safe audio processing
- ✅ IAudioEngine compliant - Implements Ownaudio.Core.IAudioEngine interface
- ✅ Automatic fallback - Gracefully falls back if PortAudio unavailable
Ownaudio.Native/
├── Utils/
│ └── LibraryLoader.cs # Cross-platform native library loader
├── PortAudio/
│ ├── PaBinding.cs # Main P/Invoke wrapper
│ ├── PaBinding.Enums.cs # PortAudio enumerations
│ ├── PaBinding.Structs.cs # PortAudio structures
│ └── PaBinding.Delegates.cs # Function delegates
├── MiniAudio/
│ ├── MaBinding.cs # Main P/Invoke wrapper
│ ├── MaBinding.Enums.cs # MiniAudio enumerations
│ ├── MaBinding.Structs.cs # MiniAudio structures
│ └── MaBinding.Delegates.cs # Function delegates
├── NativeAudioEngine.cs # Main engine implementation
├── runtimes/ # Native libraries
│ ├── win-x64/native/
│ │ ├── libportaudio.dll # PortAudio for Windows x64
│ │ └── libminiaudio.dll # MiniAudio for Windows x64
│ ├── linux-x64/native/
│ │ └── libminiaudio.so # MiniAudio for Linux x64
│ ├── osx-x64/native/
│ │ └── libminiaudio.dylib # MiniAudio for macOS x64
│ └── ... # Other platforms
└── Ownaudio.Native.csproj
The LibraryLoader class uses .NET's built-in NativeLibrary API (introduced in .NET Core 3.0) for reliable cross-platform library loading.
- Application's
runtimes/{rid}/native/folder (bundled libraries) - Application's output directory
- System-specific paths (for PortAudio):
- macOS ARM64:
/opt/homebrew/opt/portaudio/lib/ - macOS x64:
/usr/local/opt/portaudio/lib/ - Linux:
/usr/lib/{arch}-linux-gnu/
- macOS ARM64:
- System library path (LD_LIBRARY_PATH / DYLD_LIBRARY_PATH)
To use system-installed PortAudio for better performance:
macOS (Homebrew):
brew install portaudioLinux (Debian/Ubuntu):
sudo apt-get install portaudio19-dev libportaudio2Linux (Fedora):
sudo dnf install portaudio portaudio-develLinux (Arch):
sudo pacman -S portaudio✅ Completed:
- Backend detection and selection logic (PortAudio preferred, MiniAudio fallback)
- PortAudio initialization and device enumeration
- PortAudio playback callback with lock-free ring buffers
- PortAudio input (recording) support
- MiniAudio backend implementation (callback and initialization)
- Device listing (input/output devices)
- Device switching (SetOutputDeviceByName/Index)
- Start/Stop control
- Proper resource disposal
using Ownaudio.Core;
using Ownaudio.Native;
// Create and initialize engine
var engine = new NativeAudioEngine();
var config = AudioConfig.Default; // 48kHz, 2 channels, 512 frames/buffer
int result = engine.Initialize(config);
if (result != 0)
{
Console.WriteLine($"Failed to initialize: {result}");
return;
}
// Start playback
engine.Start();
// Send audio data (non-blocking)
float[] audioSamples = new float[config.FramesPerBuffer * config.Channels];
// ... fill audioSamples ...
engine.Send(audioSamples);
// Stop and cleanup
engine.Stop();
engine.Dispose();The engine follows OwnAudioSharp's zero-allocation and real-time safety principles:
- Lock-free ring buffers (
LockFreeRingBuffer<float>) for cross-thread communication - Zero allocations in audio callback path
- Pre-allocated buffers for audio processing
- Non-blocking Send() - writes to ring buffer without waiting
- Real-time safe - suitable for professional audio applications
User Thread Audio RT Thread
│ │
├─ Send(samples) ──────► Ring Buffer ──────► Callback reads
│ (non-blocking) │ │
│ │ ├─ Processes audio
│ │ └─ Outputs to device
│ │
└─ Receives(Span) ◄─────── Ring Buffer ◄────── Callback writes
(zero-allocation) (input mode)
AudioEngineFactory.CreateDefault() automatically instantiates NativeAudioEngine on all platforms (Windows, Linux, macOS, Android, iOS). No manual wiring is required.
// AudioEngineFactory selects NativeAudioEngine automatically on all platforms
var engine = AudioEngineFactory.CreateDefault();
await engine.InitializeAsync(AudioConfig.Default);
engine.Start();The engine will use MiniAudio's decoder for all audio formats:
- MP3: Frame-based decoding with layer 1/2/3 support
- WAV: PCM and IEEE float formats
- FLAC: Lossless compression
Decoder implementation location: Ownaudio.Core/Decoders/MiniAudio/
- .NET 10.0+ SDK
- Platform-specific notes:
- Windows: No additional dependencies
- Linux: Optional
libportaudio2for system PortAudio - macOS: Optional
brew install portaudiofor system PortAudio
Test project location: OwnAudioTests/Ownaudio.EngineTest/
Recommended tests:
- Backend selection logic (PortAudio vs MiniAudio)
- Device enumeration
- Playback functionality
- Recording functionality
- Ring buffer performance
- Thread safety
When contributing to the native engine:
- Maintain zero-allocation design in audio callback paths
- Use lock-free structures for cross-thread communication
- Test on all supported platforms
- Follow OwnAudioSharp coding standards
- Update this README with any architectural changes
Copyright © 2025 Ownaudio Team Part of the OwnAudioSharp project