Several fixes and new example

This commit is contained in:
2025-06-23 23:26:42 -06:00
parent 1434b0d0ed
commit e695c67363
11 changed files with 1341 additions and 92 deletions

View File

@ -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;
}
}