mkaudiolibrary
A Rust library for real-time audio signal processing, featuring analog modeling through numeric functions and circuit simulation via Modified Nodal Analysis (MNA).
Every audio-sample-carrying API in this crate operates on plain f32 - matching VST3/AU/MKAP plugin hosting's native sample format - passed as &[f32]/&mut [f32] slices or the unlocked Buffer<f32> wrapper. None of this crate's own processors share buffers across threads internally, so nothing pays for locking with no reader on the other end; wrap a buffer yourself (Arc<Mutex<_>>, a lock-free ring buffer, etc.) if you need to hand it to another thread.
Features
- Analog modeling - asymmetric log-curve saturation, and (via the
simfeature) physically-modeled vacuum tube saturation - Circuit simulation - real-time MNA solver for reactive circuits (
dsp::Circuit), plus a full tube/diode/transistor + Wave Digital Filter circuit modeling toolkit (simfeature, merged in from libmksim) - DSP primitives - convolution, IIR (biquad/Butterworth) and FIR (windowed-sinc) filtering, compression/limiting/gating, delay, integer oversampling, and FFT-based sample-rate conversion, with pre-allocated scratch buffers so steady-state processing never allocates
- SIMD acceleration (optional
simdfeature) for hot per-sample loops: AVX2+FMA/SSE2 onx86_64, NEON onaarch64, scalar fallback otherwise - Time-frequency analysis (
tfmodule) - DFT, FFT (radix-2 + Bluestein for arbitrary lengths), DCT, STFT/multi-resolution STFT, CWT, CQT, and mel spectrograms - Audio file I/O for WAV, BWF, and AIFF formats with Buffer integration
- Plugin hosting (
hostmodule) for MKAP (native), VST3, and AUv2 (macOS) - load and run third-party plugins through oneHostedPlugintrait - MKAP plugin system for building your own modular processing chains
- Real-time streaming via an RTAudio-style API (optional
realtimefeature) with real hardware-clocked backends: CoreAudio (macOS), WASAPI (Windows), ALSA (Linux)
Installation
Add to your Cargo.toml:
[]
= "2.0.0"
For real-time audio streaming (real CoreAudio/WASAPI/ALSA backends), enable the realtime feature:
[]
= { = "2.0.0", = ["realtime"] }
For SIMD-accelerated DSP/TF hot paths:
[]
= { = "2.0.0", = ["simd"] }
For physically-modeled analog circuit simulation (tubes, diodes, transistors, WDF networks):
[]
= { = "2.0.0", = ["sim"] }
# or with SIMD backends for sim's internal math: "sim-avx2" / "sim-avx512" / "sim-neon"
For hosting third-party VST3 plugins:
[]
= { = "2.0.0", = ["vst3"] }
For hosting Audio Units (macOS only):
[]
= { = "2.0.0", = ["au"] }
For MIDI support with mkmidilibrary integration:
[]
= { = "2.0.0", = ["midi"] }
For plugin GUI support with mkapk integration (mkapk-core + mkapk-host):
[]
= { = "2.0.0", = ["gui"] }
Quick Start
use ;
use Compression;
use Buffer;
// Load an audio file
let mut audio = default;
audio.load;
println!;
// Convert to buffers for processing
let mut buffers = audio.to_buffers;
// Apply compression to each channel
let mut comp = new;
comp.threshold = -12.0;
comp.ratio = 4.0;
for buffer in &mut buffers
// Save result
audio.save;
Modules
buffer
Plain (unlocked) audio sample containers, single-owner - use &mut per audio-processing thread rather than sharing one instance across threads:
| Type | Description | Use Case |
|---|---|---|
Buffer<T> |
Resizable, owned block of samples (Box<[T]> wrapper) |
Owning your own sample storage (AudioIO/MidiIO themselves just borrow &[T]/&mut [T], not Buffer) |
PushBuffer<T> |
FIFO buffer that shifts samples on push | FIR filters, convolution |
CircularBuffer<T> |
Ring buffer with power-of-2 sizing | Delay lines, lookahead buffers |
All three implement Deref/DerefMut<Target = [T]>, so they can be indexed or passed anywhere a slice is expected.
use Buffer;
let mut buffer = new;
for i in 0..1024
println!;
dsp
Audio processing components organized by category. run() methods take &[f32] input and &mut [f32] output directly.
Utility Functions
use ;
let db = ratio_to_db; // ~6.02 dB
let ratio = db_to_ratio; // ~0.5
Saturation (Analog Modeling)
Asymmetric logarithmic saturation model for analog-style harmonic generation:
- Alpha parameters - Independent drive/knee control for positive and negative signals
- Beta parameters - Separate compression/gain characteristics per polarity
- Delta parameter - DC bias offset for curve positioning
- Gamma parameter - Boolean polarity inversion
use Saturation;
use Buffer;
let sat = new;
// Process single sample
let output = sat.process;
// Process buffer
let input = from_slice;
let mut output = new;
sat.run;
For a physically-modeled alternative driven by an actual vacuum tube circuit (requires the sim feature):
Circuit Simulation (Modified Nodal Analysis)
Sample-by-sample circuit analysis for real-time filtering:
- Component library - Resistors, capacitors, and inductors with companion model discretization
- Gaussian elimination solver with partial pivoting
- Single preprocessing step builds the static admittance matrix
- Per-sample updates for reactive element state
use ;
// Create an RC lowpass filter: fc = 1/(2πRC) ≈ 159Hz
let mut circuit = new;
circuit.add_component; // 1kΩ
circuit.add_component; // 1µF
// Build Y matrix (call once before processing)
circuit.preprocess;
// Process samples
let output = circuit.process; // Input voltage, probe node 2
IIR/FIR Filtering
RBJ biquad sections (with Butterworth cascade helper) and windowed-sinc FIR design:
use ;
use FirFilter;
// Single biquad section
let mut lowpass = new;
let y = lowpass.process;
// 4th-order Butterworth lowpass (two cascaded biquads)
let mut butterworth = butterworth;
let y2 = butterworth.process;
// Windowed-sinc FIR lowpass, 101 taps
let mut fir_lp = lowpass;
let y3 = fir_lp.process;
Dynamics Processing
use ;
use Buffer;
// Compressor with soft knee
let mut compressor = new;
compressor.threshold = -20.0; // dB
compressor.ratio = 4.0; // 4:1
compressor.attack = 10.0; // ms
compressor.release = 100.0; // ms
compressor.makeup = 6.0; // dB
compressor.knee = 6.0; // dB (soft knee width)
// Brickwall limiter
let mut limiter = new;
limiter.gain = 0.0; // dB input gain
limiter.ceiling = -0.1; // dB output ceiling
limiter.release = 100.0; // ms
// Downward-expanding noise gate with hold time
let mut gate = new;
gate.threshold = -40.0; // dB
gate.hold = 50.0; // ms
gate.range = -60.0; // dB attenuation when fully closed
// Process buffers
let input = new;
let mut output = new;
compressor.run;
Sample-Rate Conversion
use ;
// Integer oversampling around a nonlinear stage (reduces aliasing)
let mut os = new;
let sat = new;
let wet = os.process;
// Whole-buffer FFT-based resampling (offline/analysis use)
let input = vec!;
let output = resample;
Time-Based Effects
use ;
use Buffer;
// Convolution with impulse response
let impulse_response = vec!;
let mut conv = new;
let input = new;
let mut output = new;
conv.run;
// Feedback delay
let mut delay = new; // 250ms delay
delay.feedback = 0.5; // 50% feedback
delay.mix = 0.5; // 50% wet
audiofile
Load and save audio files with automatic format detection:
use ;
// Load audio file (WAV or AIFF auto-detected)
let mut audio = default;
audio.load;
// Inspect file properties
println!;
println!;
println!;
println!;
println!;
println!;
// Direct channel access
if let Some = audio.channel
// Modify samples
if let Some = audio.channel_mut
// Save in different format
audio.set_bit_depth;
audio.save;
Buffer Integration
use AudioFile;
let mut audio = default;
audio.load;
// Convert to buffers for processing
let buffers = audio.to_buffers;
// ... process each channel ...
// Copy results back
audio.from_buffers;
BWF (Broadcast Wave Format)
Support for professional broadcast metadata, markers, and tempo information:
use ;
let mut audio = default;
audio.load;
// Access BWF metadata
if let Some = audio.bext
// Work with markers
for marker in audio.markers
// Add markers
audio.add_marker;
audio.add_marker;
// Set tempo for DAW integration
audio.set_tempo;
audio.set_tempo_with_time_sig;
// Set tempo at a specific sample position
audio.set_tempo_at; // 140 BPM starting at 30 seconds
// Set BWF metadata
let mut bext = with_description;
bext.set_datetime;
audio.set_bext;
// Save as BWF (includes bext chunk)
audio.save_bwf;
processor
MKAU plugin format for modular audio processing chains. AudioIO is a
thin, non-owning view: it borrows per-channel slices from storage the
caller owns for the life of the stream, rather than allocating its own
(matching how VST3/CoreAudio hand a plugin pointers into host-owned
memory). input/sidechain_in/sidechain_out are Option since a
generator plugin may have no input and sidechain busses are often absent;
output is always present.
use ;
// Load a plugin
let plugin = load.expect;
println!;
// Prepare for playback
plugin.prepare_to_play;
// Own the actual sample storage for the stream's lifetime...
let input_storage = vec!;
let mut output_storage = vec!;
// ...and borrow an AudioIO view into it for each block.
let input: = input_storage.iter.map.collect;
let mut output: = output_storage.iter_mut.map.collect;
let mut audio = new;
// Process audio
plugin.run;
Creating Plugins
use ;
// Export as dynamic library
declare_plugin!;
MIDI Processing (requires midi feature)
MidiIO follows the same non-owning pattern: input is always present,
output is Option (a plugin that only consumes MIDI has nowhere to write
outgoing messages).
use ;
// Process with MIDI
let mut midi_input = vec!;
let mut midi_output = vec!;
let mut midi = new;
// Add MIDI input messages
// Run processor with MIDI
plugin.run_with_midi;
// Check MIDI output
if let Some = midi.output.as_deref
host (Plugin Hosting)
Load and run third-party plugins - MKAP (always available), VST3 (vst3 feature), and AUv2 (au feature, macOS only) - through one HostedPlugin trait:
use ;
use AudioIO;
use Path;
// Scan a directory for VST3 plugins
let found = scan_vst3;
for d in &found
// Load and run one
let mut plugin = load.expect;
plugin.prepare.expect;
plugin.set_active.expect;
println!;
for i in 0..plugin.num_parameters
let input_storage = vec!;
let mut output_storage = vec!;
let input: = input_storage.iter.map.collect;
let mut output: = output_storage.iter_mut.map.collect;
let mut audio = new;
plugin.process;
The VST3 backend talks directly to a plugin's IComponent/IAudioProcessor/IEditController COM-style interfaces using hand-written vtables matching Steinberg's public ABI - no vendored SDK or C++ toolchain required. Both the VST3 and AUv2 backends negotiate 32-bit float first (this library's own native sample format, and what every VST3 plugin and most third-party AUs support), falling back to 64-bit float with a per-block conversion scratch buffer only for the plugins that require it.
sim (Analog Circuit Simulation, optional feature)
Physically-modeled vacuum tubes, diodes, transistors, op-amps, potentiometers, switches, and passive/RLC filters, merged in from libmksim. Linear passive networks use Wave Digital Filters (series/parallel adaptor trees); nonlinear devices use local Newton-Raphson solvers over the Koren/Shockley/Ebers-Moll/square-law equations. Enable with the sim feature; sim-avx2/sim-avx512/sim-neon additionally enable SIMD backends for its internal fast-math.
dsp::TubeSaturation builds on this module to provide a physically-modeled alternative to dsp::Saturation.
tf (Time-Frequency Analysis)
DFT, FFT, DCT, STFT/multi-resolution STFT, CWT, CQT, and mel spectrograms. Hot inner loops go through the same SIMD dot-product primitives as dsp.
use ;
// FFT of a real signal (any length - radix-2 or Bluestein's algorithm as needed)
let spectrum = rfft;
// STFT
let stft_config = new;
let frames = stft;
// Mel spectrogram
let mel_config = MelConfig ;
let mel_frames = mel_spectrogram;
For Cohen's class of bilinear time-frequency distributions (Wigner-Ville, Choi-Williams, Rihaczek, ...), see the sibling bilinear_tf crate - tf covers the standard linear transforms.
realtime (Optional Feature)
Real-time audio streaming I/O inspired by the C++ RTAudio library, with real hardware-clocked backends (not a simulated/dummy stream) per platform. Enable with the realtime feature.
Supported Backends
| Platform | Backend | API |
|---|---|---|
| macOS | CoreAudio | Api::CoreAudio |
| Windows | WASAPI | Api::Wasapi |
| Linux | ALSA | Api::Alsa |
Basic Usage
use ;
// Create audio interface (auto-detects best API)
let mut audio = new.unwrap;
// List available devices
for id in audio.get_device_ids
// Define audio callback
let callback: AudioCallback = Boxnew;
// Configure output stream
let output_params = StreamParameters ;
// Configure input stream
let input_params = StreamParameters ;
// Open duplex stream
audio.open_stream.unwrap;
// Start streaming
audio.start_stream.unwrap;
// ... do work ...
// Stop and cleanup
audio.stop_stream.unwrap;
audio.close_stream;
Stereo Processing Helper
use ;
// Create callback that works with separate L/R channels
let callback = stereo_callback;
let mut audio = new.unwrap;
// ... configure and open stream with callback ...
Buffer Integration
use ;
use Buffer;
use Compression;
use ;
// Shared DSP processor
let compressor = new;
let comp_clone = compressor.clone;
let callback: AudioCallback = Boxnew;
Supported Audio Formats
| Format | Extension | Read | Write | Notes |
|---|---|---|---|---|
| WAV | .wav |
Yes | Yes | PCM, IEEE Float |
| BWF | .wav |
Yes | Yes | WAV with bext chunk, markers, tempo |
| AIFF | .aiff, .aif |
Yes | Yes | Uncompressed, AIFC |
Supported bit depths: 8, 16, 24, 32-bit
Changelog
2.1.0
f32throughout:dsp,processor::AudioIO,audiofile,buffer, and the plugin hosting backends now operate onf32(previouslyf64), matching VST3/AU/MKAP's native sample format andsim's own circuit models - removes a redundant conversion at every plugin-hosting andsimboundary- Unlocked buffers:
Buffer/PushBuffer/CircularBufferdropped their internalArc<RwLock<...>>- they're plain owned containers now (Deref/DerefMut<Target = [T]>, no.read()/.write()guards), since nothing in this crate's own processing graph shared them across threads without its own synchronization dspAPI:run()methods now take&[f32]/&mut [f32]directly instead of&Buffer<f64>- New analog circuit simulation:
simmodule (feature-gated) merged in from libmksim - vacuum tubes, diodes, transistors, op-amps, potentiometers, switches, and passive/RLC filters via Wave Digital Filters and Newton-Raphson solvers;dsp::TubeSaturationbuilds on it for a physically-modeled saturation alternative - Expanded
dsp: newdsp::iir(RBJ biquad + Butterworth cascades),dsp::fir(windowed-sinc design),dsp::Gate(noise gate),dsp::Oversampler(integer oversampling), anddsp::resampling(FFT-based whole-buffer resampling) - SIMD independence:
crate::simdsplit into per-backend files (scalar/x86_64/aarch64), withdot/mul_elementwise/mix_scalaras the single canonicalf32primitives shared bydspandtf(previously duplicated under_f32-suffixed names during thef64->f32transition);sim's ownf32-lane trait-based SIMD abstraction now lives atcrate::simd::generic, independent of thesimdfeature - VST3/AUv2 hosting now prefers 32-bit float negotiation (matching this library's native format) with a 64-bit float fallback, inverted from the previous
f64-native preference guifeature: now backed by mkapk'smkapk-core/mkapk-host(a plugin-editor GUI framework: widget tree, geometry, paint commands, andPluginEditor/EditorHostparent-window-embedding traits) instead ofmkgraphic- a better fit for hosted plugin editors than a general-purpose windowing crate, and pure Rust with no platform-specific dependencies, so it's now verified on Windows/Linux too, not just macOS.Processor::get_view()/get_view_mut()/get_preferred_size()were replaced by a singleeditor() -> Option<&mut dyn PluginEditor>(defaultNone)AudioIO/MidiIOare non-owning views now: both gained a lifetime parameter and their fields became borrowed slices (&[&[f32]]/&mut [&mut [f32]],&mut [Option<MidiMessage>]) instead of ownedVec<Buffer<f32>>/Box<[Option<MidiMessage>]>- the caller owns the actual sample/message storage for the stream's lifetime and borrows a view into it per block, avoiding the allocate-and-copyAudioIO::new(channel_count, buffer_size)used to do.AudioIO::input/sidechain_in/sidechain_outandMidiIO::outputareOption(generator plugins have no input, sidechain busses are often absent, and not every plugin produces MIDI output);AudioIO::outputandMidiIO::inputare always present.AudioIO::set_channel/resizeandMidiIO::resizewere removed along with the owned storage they resized
2.0.0
- Real realtime backends:
realtimemodule restructured into per-platform backends (mirroringmkmidilibrary's design) - CoreAudio (AUHAL), WASAPI (event-drivenIAudioClient), and ALSA (blockingsnd_pcm) now drive real, hardware-clocked audio I/O instead of a simulated dummy stream - Plugin hosting: new
hostmodule with a unifiedHostedPlugintrait - MKAP (always available), VST3 (vst3feature, hand-written COM/vtable FFI, no vendored SDK needed), and AUv2 (aufeature, macOS) - Time-frequency analysis: new
tfmodule - DFT, FFT (radix-2 + Bluestein), DCT, STFT/multi-resolution STFT, CWT, CQT, mel spectrograms - SIMD: new
simdfeature with AVX2+FMA/SSE2 (x86_64) and NEON (aarch64) dot-product/elementwise-multiply/mix primitives, used bydspandtf's hot loops - No-allocation steady state:
Compression,Limit, andDelaynow use pre-allocated scratch buffers instead of allocating perrun()call - Dependency updates:
libloading0.9,no_denormals0.3 (nowunsafe fn),windows0.62,alsa0.9 (pinned to matchmkmidilibrary's native-link constraint),mkmidilibrary0.2,mkgraphic0.4 Processortrait gained anum_parameters()method (default0) so hosts can enumerate MKAP plugin parameters
1.4.0
- GUI support: New
guifeature with mkgraphic integration for plugin UI - Replaced
open_window()/close_window()withget_view(),get_view_mut(), andget_preferred_size()methods - Re-exports
View,Window,WindowBuilder,Extent,Pointfrom mkgraphic
1.3.0
- Buffer-based Processor I/O: Changed
Processor::run()to useAudioIOstruct withBuffertypes - MIDI support: New
midifeature withMidiIOstruct andrun_with_midi()method - mkmidilibrary integration: Re-exports
MidiMessagefrom mkmidilibrary for MIDI event handling AudioIOprovidesnew()andset_channel()constructors for flexible channel configurations
1.2.0
- BWF (Broadcast Wave Format) support with
bextchunk for broadcast metadata - Markers/cue points with labels via
cueandLISTchunks - Tempo information via
acidchunk for DAW integration
1.1.0
- Added
realtimefeature with cross-platform audio streaming I/O Realtimestruct providing callback-based audio input/output- Platform backends: CoreAudio (macOS), WASAPI (Windows), ALSA (Linux)
- Helper functions for buffer interleaving/deinterleaving
stereo_callbackwrapper for simplified stereo processing
1.0.0
- Major update with thread-safe buffers using
RwLock - New saturation model with asymmetric numeric modeling
- Circuit simulation with MNA solver
- Refined compression and limiting with proper envelope detection
- Enhanced audiofile module with Buffer integration
- Comprehensive documentation for all modules
0.3.0
- Reconstructed sized buffer, used slice instead of buffer for plugins
0.2.x
- Added audiofile module
- Lock/unlock mechanisms for data safety
- Updated processor loader and documentation
0.1.x
- Initial development versions
- Buffer implementations with reference counting
- Basic DSP components
License
MIT License