Several fixes and new example
This commit is contained in:
199
src/sequencer.rs
199
src/sequencer.rs
@ -4,7 +4,12 @@
|
||||
//! handle timing, and coordinate multiple tracks and patterns.
|
||||
|
||||
use crate::bpm_to_samples_per_beat;
|
||||
use crate::core::{Composition, InstrumentType};
|
||||
use crate::config::EffectConfig;
|
||||
use crate::core::Composition;
|
||||
use crate::effects::{
|
||||
AudioEffect, BitCrusher, Chorus, Delay, Distortion, EffectsChain, HighPassFilter,
|
||||
LowPassFilter, Reverb, TapeSaturation, VinylCrackle,
|
||||
};
|
||||
use crate::patterns::MelodicPattern;
|
||||
use crate::synthesis::{PolySynth, Waveform};
|
||||
use std::collections::VecDeque;
|
||||
@ -56,17 +61,193 @@ struct TrackState {
|
||||
pub volume: f32,
|
||||
pub is_muted: bool,
|
||||
pub current_notes: Vec<u8>, // Currently playing MIDI notes
|
||||
pub effects: EffectsChain,
|
||||
}
|
||||
|
||||
impl TrackState {
|
||||
fn new(waveform: Waveform, max_tracks: usize) -> Self {
|
||||
fn new(waveform: Waveform, max_tracks: usize, effect_configs: &[EffectConfig]) -> Self {
|
||||
let effects =
|
||||
Self::create_effects_chain(effect_configs).unwrap_or_else(|_| EffectsChain::new());
|
||||
Self {
|
||||
synth: PolySynth::new(max_tracks, waveform),
|
||||
volume: 1.0,
|
||||
is_muted: false,
|
||||
current_notes: Vec::new(),
|
||||
effects,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create an effects chain from effect configurations
|
||||
fn create_effects_chain(effect_configs: &[EffectConfig]) -> Result<EffectsChain, String> {
|
||||
let mut effects_chain = EffectsChain::new();
|
||||
|
||||
for effect_config in effect_configs {
|
||||
let effect: Box<dyn AudioEffect> = match effect_config.effect_type.as_str() {
|
||||
"lowpass" => {
|
||||
let cutoff = effect_config
|
||||
.params
|
||||
.get("cutoff")
|
||||
.and_then(|v| v.as_f64())
|
||||
.unwrap_or(1000.0) as f32;
|
||||
let resonance = effect_config
|
||||
.params
|
||||
.get("resonance")
|
||||
.and_then(|v| v.as_f64())
|
||||
.unwrap_or(1.0) as f32;
|
||||
Box::new(LowPassFilter::new(cutoff, resonance))
|
||||
}
|
||||
"highpass" => {
|
||||
let cutoff = effect_config
|
||||
.params
|
||||
.get("cutoff")
|
||||
.and_then(|v| v.as_f64())
|
||||
.unwrap_or(1000.0) as f32;
|
||||
let resonance = effect_config
|
||||
.params
|
||||
.get("resonance")
|
||||
.and_then(|v| v.as_f64())
|
||||
.unwrap_or(1.0) as f32;
|
||||
Box::new(HighPassFilter::new(cutoff, resonance))
|
||||
}
|
||||
"delay" => {
|
||||
let time = effect_config
|
||||
.params
|
||||
.get("time")
|
||||
.and_then(|v| v.as_f64())
|
||||
.unwrap_or(0.3) as f32;
|
||||
let feedback = effect_config
|
||||
.params
|
||||
.get("feedback")
|
||||
.and_then(|v| v.as_f64())
|
||||
.unwrap_or(0.3) as f32;
|
||||
let mix = effect_config
|
||||
.params
|
||||
.get("mix")
|
||||
.and_then(|v| v.as_f64())
|
||||
.unwrap_or(0.3) as f32;
|
||||
Box::new(Delay::new(time, feedback, mix))
|
||||
}
|
||||
"reverb" => {
|
||||
let room_size = effect_config
|
||||
.params
|
||||
.get("room_size")
|
||||
.and_then(|v| v.as_f64())
|
||||
.unwrap_or(0.5) as f32;
|
||||
let damping = effect_config
|
||||
.params
|
||||
.get("damping")
|
||||
.and_then(|v| v.as_f64())
|
||||
.unwrap_or(0.5) as f32;
|
||||
let mix = effect_config
|
||||
.params
|
||||
.get("mix")
|
||||
.and_then(|v| v.as_f64())
|
||||
.unwrap_or(0.3) as f32;
|
||||
Box::new(Reverb::new(room_size, damping, mix))
|
||||
}
|
||||
"distortion" => {
|
||||
let drive = effect_config
|
||||
.params
|
||||
.get("drive")
|
||||
.and_then(|v| v.as_f64())
|
||||
.unwrap_or(2.0) as f32;
|
||||
let tone = effect_config
|
||||
.params
|
||||
.get("tone")
|
||||
.and_then(|v| v.as_f64())
|
||||
.unwrap_or(0.5) as f32;
|
||||
Box::new(Distortion::new(drive, tone))
|
||||
}
|
||||
"chorus" => {
|
||||
let rate = effect_config
|
||||
.params
|
||||
.get("rate")
|
||||
.and_then(|v| v.as_f64())
|
||||
.unwrap_or(1.0) as f32;
|
||||
let depth = effect_config
|
||||
.params
|
||||
.get("depth")
|
||||
.and_then(|v| v.as_f64())
|
||||
.unwrap_or(0.5) as f32;
|
||||
let mix = effect_config
|
||||
.params
|
||||
.get("mix")
|
||||
.and_then(|v| v.as_f64())
|
||||
.unwrap_or(0.3) as f32;
|
||||
let layers = effect_config
|
||||
.params
|
||||
.get("layers")
|
||||
.and_then(|v| v.as_u64())
|
||||
.unwrap_or(3) as usize;
|
||||
Box::new(Chorus::new(rate, depth, mix, layers))
|
||||
}
|
||||
"vinyl" => {
|
||||
let intensity = effect_config
|
||||
.params
|
||||
.get("intensity")
|
||||
.and_then(|v| v.as_f64())
|
||||
.unwrap_or(0.5) as f32;
|
||||
let frequency = effect_config
|
||||
.params
|
||||
.get("frequency")
|
||||
.and_then(|v| v.as_f64())
|
||||
.unwrap_or(0.5) as f32;
|
||||
let mix = effect_config
|
||||
.params
|
||||
.get("mix")
|
||||
.and_then(|v| v.as_f64())
|
||||
.unwrap_or(0.3) as f32;
|
||||
Box::new(VinylCrackle::new(intensity, frequency, mix))
|
||||
}
|
||||
"tape" => {
|
||||
let drive = effect_config
|
||||
.params
|
||||
.get("drive")
|
||||
.and_then(|v| v.as_f64())
|
||||
.unwrap_or(2.0) as f32;
|
||||
let warmth = effect_config
|
||||
.params
|
||||
.get("warmth")
|
||||
.and_then(|v| v.as_f64())
|
||||
.unwrap_or(0.7) as f32;
|
||||
let mix = effect_config
|
||||
.params
|
||||
.get("mix")
|
||||
.and_then(|v| v.as_f64())
|
||||
.unwrap_or(0.5) as f32;
|
||||
Box::new(TapeSaturation::new(drive, warmth, mix))
|
||||
}
|
||||
"bitcrusher" => {
|
||||
let bit_depth = effect_config
|
||||
.params
|
||||
.get("bit_depth")
|
||||
.and_then(|v| v.as_f64())
|
||||
.unwrap_or(8.0) as f32;
|
||||
let sample_rate_reduction = effect_config
|
||||
.params
|
||||
.get("sample_rate_reduction")
|
||||
.and_then(|v| v.as_f64())
|
||||
.unwrap_or(2.0) as f32;
|
||||
let mix = effect_config
|
||||
.params
|
||||
.get("mix")
|
||||
.and_then(|v| v.as_f64())
|
||||
.unwrap_or(0.5) as f32;
|
||||
Box::new(BitCrusher::new(bit_depth, sample_rate_reduction, mix))
|
||||
}
|
||||
_ => {
|
||||
return Err(format!(
|
||||
"Unknown effect type: {}",
|
||||
effect_config.effect_type
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
effects_chain.add_effect(effect);
|
||||
}
|
||||
|
||||
Ok(effects_chain)
|
||||
}
|
||||
}
|
||||
|
||||
impl Sequencer {
|
||||
@ -98,16 +279,9 @@ impl Sequencer {
|
||||
|
||||
// Create track states for each track in the composition
|
||||
for track in &composition.tracks {
|
||||
let waveform = match track.instrument_type {
|
||||
InstrumentType::Lead => Waveform::Sawtooth,
|
||||
InstrumentType::Bass => Waveform::Square,
|
||||
InstrumentType::Pad => Waveform::Sine,
|
||||
InstrumentType::Arp => Waveform::Triangle,
|
||||
InstrumentType::Percussion => Waveform::Noise,
|
||||
InstrumentType::Drone => Waveform::Sine,
|
||||
};
|
||||
let waveform = track.waveform;
|
||||
|
||||
let mut track_state = TrackState::new(waveform, 8);
|
||||
let mut track_state = TrackState::new(waveform, 8, &track.effect_configs);
|
||||
track_state.volume = track.volume;
|
||||
self.tracks.push(track_state);
|
||||
}
|
||||
@ -258,7 +432,8 @@ impl Sequencer {
|
||||
for track in &mut self.tracks {
|
||||
if !track.is_muted {
|
||||
let track_sample = track.synth.next_sample();
|
||||
sample += track_sample * track.volume;
|
||||
let processed_sample = track.effects.process_sample(track_sample);
|
||||
sample += processed_sample * track.volume;
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user