[](https://crates.io/crates/mkaudiolibrary)
[](https://crates.io/crates/mkaudiolibrary)
[](https://docs.rs/mkaudiolibrary/)
# mkaudiolibrary
A Rust library for real-time audio signal processing, featuring analog modeling through numeric functions and circuit simulation via Modified Nodal Analysis (MNA).
## Features
- **Thread-safe buffers** with `RwLock`-based concurrent access
- **Analog modeling** for tube/tape-style saturation with asymmetric parameters
- **Circuit simulation** with real-time transient analysis using MNA
- **DSP primitives** including convolution, compression, limiting, and delay
- **Audio file I/O** for WAV and AIFF formats with Buffer integration
- **Plugin system** via MKAU format for modular processing chains
- **Real-time streaming** via RTAudio-style API (optional `realtime` feature)
- **Zero-copy design** with boxed slices for minimal allocation overhead
## Installation
Add to your `Cargo.toml`:
```toml
[dependencies]
mkaudiolibrary = "1.4"
```
For real-time audio streaming, enable the `realtime` feature:
```toml
[dependencies]
mkaudiolibrary = { version = "1.4", features = ["realtime"] }
```
For MIDI support with mkmidilibrary integration:
```toml
[dependencies]
mkaudiolibrary = { version = "1.4", features = ["midi"] }
```
For GUI support with mkgraphic integration:
```toml
[dependencies]
mkaudiolibrary = { version = "1.4", features = ["gui"] }
```
## Quick Start
```rust
use mkaudiolibrary::audiofile::{AudioFile, FileFormat};
use mkaudiolibrary::dsp::Compression;
use mkaudiolibrary::buffer::Buffer;
// Load an audio file
let mut audio = AudioFile::default();
audio.load("input.wav");
println!("Loaded: {} channels, {} samples, {}Hz",
audio.num_channel(),
audio.num_sample(),
audio.sample_rate()
);
// Convert to thread-safe buffers for processing
let buffers = audio.to_buffers();
// Apply compression to each channel
let mut comp = Compression::new(audio.sample_rate() as f64);
comp.threshold = -12.0;
comp.ratio = 4.0;
for buffer in &buffers {
let output = Buffer::new(buffer.len());
comp.run(buffer, &output);
// ... copy processed output back
}
// Save result
audio.save("output.wav", FileFormat::Wav);
```
## Modules
### buffer
Thread-safe audio buffers designed for concurrent multi-threaded access:
| `Buffer<T>` | General-purpose buffer with read/write locking | Sample storage, inter-thread communication |
| `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 buffers use `Arc<RwLock<...>>` internally, allowing multiple concurrent readers or exclusive write access.
```rust
use mkaudiolibrary::buffer::Buffer;
use std::thread;
let buffer = Buffer::<f64>::new(1024);
let buffer_clone = buffer.clone(); // Shares underlying data
// Writer thread
for i in 0..1024 {
guard[i] = (i as f64 / 1024.0).sin();
}
});
writer.join().unwrap();
// Reader thread
let guard = buffer.read();
println!("First sample: {}", guard[0]);
```
### dsp
Audio processing components organized by category:
#### Utility Functions
```rust
use mkaudiolibrary::dsp::{ratio_to_db, db_to_ratio};
let db = ratio_to_db(2.0); // ~6.02 dB
let ratio = db_to_ratio(-6.0); // ~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
```rust
use mkaudiolibrary::dsp::Saturation;
use mkaudiolibrary::buffer::Buffer;
let sat = Saturation::new(
10.0, 10.0, // alpha_plus, alpha_minus (drive)
1.0, 1.0, // beta_plus, beta_minus (compression)
0.0, // delta (bias)
false // flip polarity
);
// Process single sample
let output = sat.process(0.8);
// Process buffer
let input = Buffer::from_slice(&[0.0, 0.5, 1.0, -0.5, -1.0]);
let output = Buffer::new(5);
sat.run(&input, &output);
```
#### 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
```rust
use mkaudiolibrary::dsp::{Circuit, Resistor, Capacitor, Inductor};
// Create an RC lowpass filter: fc = 1/(2πRC) ≈ 159Hz
let mut circuit = Circuit::new(44100.0, 2);
circuit.add_component(Box::new(Resistor::new(1, 2, 1000.0))); // 1kΩ
circuit.add_component(Box::new(Capacitor::new(2, 0, 1e-6))); // 1µF
// Build Y matrix (call once before processing)
circuit.preprocess(10.0);
// Process samples
for input_sample in audio_input {
let output = circuit.process(input_sample, 2); // Input voltage, probe node 2
}
```
#### Dynamics Processing
```rust
use mkaudiolibrary::dsp::{Compression, Limit};
use mkaudiolibrary::buffer::Buffer;
// Compressor with soft knee
let mut compressor = Compression::new(44100.0);
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 = Limit::new(44100.0);
limiter.gain = 0.0; // dB input gain
limiter.ceiling = -0.1; // dB output ceiling
limiter.release = 100.0; // ms
// Process buffers
let input = Buffer::new(1024);
let output = Buffer::new(1024);
compressor.run(&input, &output);
```
#### Time-Based Effects
```rust
use mkaudiolibrary::dsp::{Convolution, Delay};
use mkaudiolibrary::buffer::Buffer;
// Convolution with impulse response
let impulse_response = vec![1.0, 0.5, 0.25, 0.125];
let conv = Convolution::new(&impulse_response).unwrap();
let input = Buffer::new(1024);
let output = Buffer::new(1024);
conv.run(&input, &output);
// Feedback delay
let mut delay = Delay::new(250.0, 44100.0); // 250ms delay
delay.feedback = 0.5; // 50% feedback
delay.mix = 0.5; // 50% wet
```
### audiofile
Load and save audio files with automatic format detection:
```rust
use mkaudiolibrary::audiofile::{AudioFile, FileFormat};
// Load audio file (WAV or AIFF auto-detected)
let mut audio = AudioFile::default();
audio.load("song.wav");
// Inspect file properties
println!("Format: {:?}", audio.format());
println!("Channels: {}", audio.num_channel());
println!("Samples: {}", audio.num_sample());
println!("Sample rate: {} Hz", audio.sample_rate());
println!("Bit depth: {}", audio.bit_depth());
println!("Duration: {:.2} seconds", audio.length());
// Direct channel access
if let Some(left) = audio.channel(0) {
let peak = left.iter().fold(0.0_f64, |max, &s| max.max(s.abs()));
println!("Peak level: {:.4}", peak);
}
// Modify samples
if let Some(left) = audio.channel_mut(0) {
for sample in left.iter_mut() {
*sample *= 0.5; // Apply -6dB gain
}
}
// Save in different format
audio.set_bit_depth(24);
audio.save("output.aiff", FileFormat::Aiff);
```
#### Buffer Integration
Seamlessly integrate with thread-safe buffers for DSP processing:
```rust
use mkaudiolibrary::audiofile::AudioFile;
use mkaudiolibrary::buffer::Buffer;
let mut audio = AudioFile::default();
audio.load("input.wav");
// Convert to thread-safe buffers
let buffers = audio.to_buffers();
// Process each channel in parallel (example)
// ... parallel processing ...
// Copy results back
audio.from_buffers(&buffers);
```
#### BWF (Broadcast Wave Format)
Support for professional broadcast metadata, markers, and tempo information:
```rust
use mkaudiolibrary::audiofile::{AudioFile, BextChunk, Marker};
let mut audio = AudioFile::default();
audio.load("broadcast.wav");
// Access BWF metadata
if let Some(bext) = audio.bext() {
println!("Description: {}", bext.description);
println!("Originator: {}", bext.originator);
println!("Timecode: {} samples", bext.time_reference);
}
// Work with markers
for marker in audio.markers() {
println!("Marker '{}' at sample {}", marker.label, marker.position);
}
// Add markers
audio.add_marker(Marker::new(44100, "Verse 1"));
audio.add_marker(Marker::new(88200, "Chorus"));
// Set tempo for DAW integration
audio.set_tempo(120.0);
audio.set_tempo_with_time_sig(120.0, 4, 4);
// Set tempo at a specific sample position
audio.set_tempo_at(140.0, 44100 * 30); // 140 BPM starting at 30 seconds
// Set BWF metadata
let mut bext = BextChunk::with_description("Recording session", "Studio A");
bext.set_datetime("2025-01-15", "14:30:00");
audio.set_bext(bext);
// Save as BWF (includes bext chunk)
audio.save_bwf("output_bwf.wav");
```
### processor
MKAU plugin format for modular audio processing chains with thread-safe Buffer-based I/O:
```rust
use mkaudiolibrary::processor::{Processor, AudioIO, load};
// Load a plugin
let plugin = load("/path/to/plugins", "myplugin").expect("Failed to load");
println!("Loaded: {}", plugin.name());
// Prepare for playback
plugin.prepare_to_play(512, 44100);
// Create audio I/O buffers
let mut audio = AudioIO::stereo(512);
// Process audio
plugin.run(&mut audio);
```
#### Creating Plugins
```rust
use mkaudiolibrary::processor::{Processor, AudioIO};
use mkaudiolibrary::buffer::Buffer;
struct GainPlugin {
gain: f64,
}
impl Processor for GainPlugin {
fn init(&mut self) {}
fn name(&self) -> String { String::from("Gain") }
fn get_parameter(&self, _index: usize) -> f64 { self.gain }
fn set_parameter(&mut self, _index: usize, value: f64) { self.gain = value; }
fn get_parameter_name(&self, _index: usize) -> String { String::from("Gain") }
fn prepare_to_play(&mut self, _buffer_size: usize, _sample_rate: usize) {}
#[cfg(feature = "gui")]
fn get_view(&self) -> Option<&View> { None }
#[cfg(feature = "gui")]
fn get_view_mut(&mut self) -> Option<&mut View> { None }
fn run(&self, audio: &mut AudioIO) {
for ch in 0..audio.input.len().min(audio.output.len()) {
let input = audio.input[ch].read();
let mut output = audio.output[ch].write();
for i in 0..input.len() {
output[i] = input[i] * self.gain;
}
}
}
#[cfg(feature = "midi")]
fn run_with_midi(&self, audio: &mut AudioIO, _midi: &mut MidiIO) {
self.run(audio);
}
}
// Export as dynamic library
mkaudiolibrary::declare_plugin!(GainPlugin, GainPlugin::new);
```
#### MIDI Processing (requires `midi` feature)
```rust
#[cfg(feature = "midi")]
use mkaudiolibrary::processor::{Processor, AudioIO, MidiIO, MidiMessage};
// Process with MIDI
let mut audio = AudioIO::stereo(512);
let mut midi = MidiIO::new(512);
// Add MIDI input messages
midi.input[0] = Some(MidiMessage::note_on(0, 60, 100));
// Run processor with MIDI
plugin.run_with_midi(&mut audio, &mut midi);
// Check MIDI output
for msg in midi.output.iter().flatten() {
println!("MIDI out: {:?}", msg);
}
```
### realtime (Optional Feature)
Real-time audio streaming I/O inspired by the C++ RTAudio library. Enable with the `realtime` feature.
#### Supported Backends
| macOS | CoreAudio | `Api::CoreAudio` |
| Windows | WASAPI | `Api::Wasapi` |
| Linux | ALSA | `Api::Alsa` |
#### Basic Usage
```rust
use mkaudiolibrary::realtime::{Realtime, StreamParameters, AudioCallback};
// Create audio interface (auto-detects best API)
let mut audio = Realtime::new(None).unwrap();
// List available devices
for id in audio.get_device_ids() {
if let Ok(info) = audio.get_device_info(id) {
println!("{}: {} (in:{}, out:{})",
info.id, info.name,
info.input_channels, info.output_channels);
}
}
// Define audio callback
let output_params = StreamParameters {
device_id: audio.get_default_output_device(),
num_channels: 2,
first_channel: 0,
};
// Configure input stream
let input_params = StreamParameters {
device_id: audio.get_default_input_device(),
num_channels: 2,
first_channel: 0,
};
// Open duplex stream
audio.open_stream(
Some(&output_params),
Some(&input_params),
44100, // Sample rate
256, // Buffer frames
callback,
None, // Optional stream options
).unwrap();
// Start streaming
audio.start_stream().unwrap();
// ... do work ...
// Stop and cleanup
audio.stop_stream().unwrap();
audio.close_stream();
```
#### Stereo Processing Helper
```rust
use mkaudiolibrary::realtime::{stereo_callback, Realtime, StreamParameters};
// Create callback that works with separate L/R channels
let callback = stereo_callback(|left_in, right_in, left_out, right_out, frames| {
for i in 0..frames {
// Simple stereo processing
left_out[i] = left_in[i] * 0.8;
right_out[i] = right_in[i] * 0.8;
}
});
let mut audio = Realtime::new(None).unwrap();
// ... configure and open stream with callback ...
```
#### Buffer Integration
```rust
use mkaudiolibrary::realtime::{deinterleave, interleave, AudioCallback};
use mkaudiolibrary::buffer::Buffer;
use mkaudiolibrary::dsp::Compression;
use std::sync::{Arc, Mutex};
// Shared DSP processor
let compressor = Arc::new(Mutex::new(Compression::new(44100.0)));
let comp_clone = compressor.clone();
let mut comp = comp_clone.lock().unwrap();
let output_buffers: Vec<Buffer<f64>> = input_buffers.iter()
.map(|buf| {
let out = Buffer::new(frames);
comp.run(buf, &out);
out
})
.collect();
// Interleave back to output
interleave(&output_buffers, output, frames);
0
});
```
## Thread Safety
All buffer types implement `Send + Sync` and use interior mutability with `RwLock`:
| `buffer.read()` | Returns `BufferReadGuard`, multiple allowed |
| `buffer.write()` | Returns `BufferWriteGuard`, exclusive access |
| `buffer.clone()` | Creates new handle to same data (like `Arc`) |
Guards automatically release locks when dropped (RAII pattern).
## Supported Audio Formats
| 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
### 1.4.0
- **GUI support**: New `gui` feature with mkgraphic integration for plugin UI
- Replaced `open_window()`/`close_window()` with `get_view()`, `get_view_mut()`, and `get_preferred_size()` methods
- Re-exports `View`, `Window`, `WindowBuilder`, `Extent`, `Point` from mkgraphic
### 1.3.0
- **Buffer-based Processor I/O**: Changed `Processor::run()` to use `AudioIO` struct with thread-safe `Buffer<f64>` types
- **MIDI support**: New `midi` feature with `MidiIO` struct and `run_with_midi()` method
- **mkmidilibrary integration**: Re-exports `MidiMessage` from mkmidilibrary for MIDI event handling
- `AudioIO` provides `stereo()`, `mono()`, and `new()` constructors for flexible channel configurations
### 1.2.0
- BWF (Broadcast Wave Format) support with `bext` chunk for broadcast metadata
- Markers/cue points with labels via `cue` and `LIST` chunks
- Tempo information via `acid` chunk for DAW integration
### 1.1.0
- Added `realtime` feature with cross-platform audio streaming I/O
- `Realtime` struct providing callback-based audio input/output
- Platform backends: CoreAudio (macOS), WASAPI (Windows), ALSA (Linux)
- Helper functions for buffer interleaving/deinterleaving
- `stereo_callback` wrapper for simplified stereo processing
- Seamless integration with thread-safe `Buffer` types
### 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
This library is dual-licensed:
- **GPL-3.0** for open source projects
- **Commercial license** available for closed source usage
For commercial licensing inquiries, contact: minjaekim@mkaudio.company