A Rust library for building synthesizers: envelopes, voices, and polyphony.
| Component | Description |
|---|---|
Envelope |
Trait for envelope generators. LinearAdsr included, more to come. |
Voice |
Combines an oscillator and envelope into a playable voice with MIDI support. |
Polyphony |
Manages multiple voices for chords with configurable voice stealing. |
use soundlab::envelope::{Envelope, LinearAdsr};
let mut env = LinearAdsr::new(44100.0, 0.01, 0.1, 0.7, 0.3);
env.gate_on(); // Note pressed
for _ in 0..44100 {
let amplitude = env.next_sample();
// Multiply your oscillator output by amplitude
}
env.gate_off(); // Note releasedPresets for common sounds:
let pad = LinearAdsr::pad(44100.0); // Slow attack, long release
let pluck = LinearAdsr::pluck(44100.0); // Instant attack, no sustain
let perc = LinearAdsr::percussion(44100.0); // Instant attack, fast decayA voice combines an oscillator with an amplitude envelope:
use soundlab::voice::Voice;
use soundlab::envelope::LinearAdsr;
use oscy::{poly_blep::PolyBlepOsc, Waveform};
let osc = PolyBlepOsc::new(44100.0, 440.0, Waveform::Saw);
let env = LinearAdsr::pad(44100.0);
let mut voice = Voice::new(osc, env);
// MIDI note on (C4, velocity 0.8)
voice.note_on(60, 0.8);
for _ in 0..44100 {
let sample = voice.next_sample();
}
voice.note_off();Manage multiple voices for playing chords:
use soundlab::polyphony::{Polyphony, VoiceStealStrategy};
use soundlab::voice::Voice;
use soundlab::envelope::LinearAdsr;
use oscy::{poly_blep::PolyBlepOsc, Waveform};
// Create 8-voice polyphony
let mut poly = Polyphony::<_, _, 8>::from_factory(
VoiceStealStrategy::Oldest,
|| Voice::new(
PolyBlepOsc::new(44100.0, 440.0, Waveform::Saw),
LinearAdsr::pad(44100.0),
),
);
// Play a chord
poly.note_on(60, 0.8); // C4
poly.note_on(64, 0.8); // E4
poly.note_on(67, 0.8); // G4
for _ in 0..44100 {
let sample = poly.next_sample();
}
// Release
poly.note_off(60);
poly.note_off(64);
poly.note_off(67);fn process(&mut self, buffer: &mut Buffer, context: &mut impl ProcessContext<Self>) {
let mut next_event = context.next_event();
for (sample_idx, channel_samples) in buffer.iter_samples().enumerate() {
// Process events at their exact sample timing
while let Some(event) = next_event {
if event.timing() > sample_idx as u32 {
break;
}
match event {
NoteEvent::NoteOn { note, velocity, .. } => {
self.poly.note_on(note, velocity);
}
NoteEvent::NoteOff { note, .. } => {
self.poly.note_off(note);
}
_ => {}
}
next_event = context.next_event();
}
// Generate audio
let out = self.poly.next_sample();
for sample in channel_samples {
*sample = out;
}
}
}MIT License - see LICENSE for details.