use crate::composition::Composition;
use crate::synthesis::envelope::Envelope;
use crate::synthesis::filter_envelope::FilterEnvelope;
use crate::synthesis::fm_synthesis::FMParams;
use crate::instruments::Instrument;
use crate::composition::timing::Tempo;
use crate::track::{AudioEvent, Track};
use crate::synthesis::waveform::Waveform;
use std::collections::HashMap;
#[derive(Clone, Debug)]
pub struct Section {
pub(crate) name: String,
pub(crate) tracks: HashMap<String, Track>,
pub(crate) duration: f32, }
impl Section {
pub fn new(name: String) -> Self {
Self {
name,
tracks: HashMap::new(),
duration: 0.0,
}
}
pub fn name(&self) -> &str {
&self.name
}
pub fn duration(&self) -> f32 {
self.duration
}
pub(crate) fn clone_with_offset(&self, time_offset: f32) -> HashMap<String, Track> {
self.tracks
.iter()
.map(|(name, track)| {
let mut new_track = track.clone();
for event in &mut new_track.events {
match event {
AudioEvent::Note(note) => {
note.start_time += time_offset;
}
AudioEvent::Drum(drum) => {
drum.start_time += time_offset;
}
AudioEvent::Sample(sample) => {
sample.start_time += time_offset;
}
AudioEvent::TempoChange(tempo) => {
tempo.start_time += time_offset;
}
AudioEvent::TimeSignature(time_sig) => {
time_sig.start_time += time_offset;
}
AudioEvent::KeySignature(key_sig) => {
key_sig.start_time += time_offset;
}
}
}
(name.clone(), new_track)
})
.collect()
}
}
pub struct SectionBuilder<'a> {
composition: &'a mut Composition,
section_name: String,
tempo: Tempo,
}
impl<'a> SectionBuilder<'a> {
pub(crate) fn new(composition: &'a mut Composition, name: String, tempo: Tempo) -> Self {
Self {
composition,
section_name: name,
tempo,
}
}
pub fn track(self, name: &str) -> crate::composition::TrackBuilder<'a> {
crate::composition::TrackBuilder {
composition: self.composition,
context: crate::composition::BuilderContext::Section(self.section_name),
track_name: name.to_string(),
bus_name: "default".to_string(),
cursor: 0.0,
pattern_start: 0.0,
waveform: Waveform::Sine,
envelope: Envelope::default(),
filter_envelope: FilterEnvelope::default(),
fm_params: FMParams::default(),
swing: 0.5,
swing_counter: 0,
pitch_bend: 0.0,
tempo: self.tempo,
custom_wavetable: None,
velocity: 0.8,
spatial_position: None,
last_chord: None,
}
}
pub fn instrument(
self,
name: &str,
instrument: &Instrument,
) -> crate::composition::TrackBuilder<'a> {
let mut builder = crate::composition::TrackBuilder {
composition: self.composition,
context: crate::composition::BuilderContext::Section(self.section_name),
track_name: name.to_string(),
bus_name: "default".to_string(),
cursor: 0.0,
pattern_start: 0.0,
waveform: instrument.waveform,
envelope: instrument.envelope,
filter_envelope: FilterEnvelope::default(),
fm_params: FMParams::default(),
swing: 0.5,
swing_counter: 0,
pitch_bend: 0.0,
tempo: self.tempo,
custom_wavetable: None,
velocity: 0.8,
spatial_position: None,
last_chord: None,
};
builder.get_track_mut().apply_instrument(instrument);
builder
}
}
impl Track {
pub(crate) fn apply_instrument(&mut self, instrument: &Instrument) {
self.volume = instrument.volume;
self.pan = instrument.pan;
self.filter = instrument.filter;
self.effects.delay = instrument.delay.clone();
self.effects.reverb = instrument.reverb.clone();
self.effects.distortion = instrument.distortion.clone();
self.modulation = instrument.modulation.clone();
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::instruments::drums::DrumType;
use crate::consts::notes::*;
#[test]
fn test_create_empty_section() {
let mut comp = Composition::new(Tempo::new(120.0));
comp.section("intro");
assert!(comp.sections.contains_key("intro"));
}
#[test]
fn test_section_duration_tracking() {
let section = Section::new("test".to_string());
assert_eq!(section.duration(), 0.0);
}
#[test]
fn test_section_clone_with_offset() {
let mut section = Section::new("test".to_string());
let mut track = Track::new();
track.add_note(&[C4], 0.0, 1.0);
track.add_note(&[E4], 1.0, 1.0);
section.tracks.insert("melody".to_string(), track);
let offset_tracks = section.clone_with_offset(4.0);
let melody = &offset_tracks["melody"];
if let AudioEvent::Note(note) = &melody.events[0] {
assert_eq!(note.start_time, 4.0);
}
if let AudioEvent::Note(note) = &melody.events[1] {
assert_eq!(note.start_time, 5.0);
}
}
#[test]
fn test_section_with_notes() {
let mut comp = Composition::new(Tempo::new(120.0));
comp.section("verse")
.track("melody")
.notes(&[C4, E4, G4, C5], 0.5);
let section = comp.sections.get("verse").unwrap();
let melody = section.tracks.get("melody").unwrap();
assert_eq!(melody.events.len(), 4);
assert_eq!(section.duration, 2.0); }
#[test]
fn test_section_with_multiple_tracks() {
let mut comp = Composition::new(Tempo::new(120.0));
comp.section("chorus")
.track("melody")
.notes(&[C4, E4], 0.5)
.and()
.track("bass")
.notes(&[C2, G2], 1.0);
let section = comp.sections.get("chorus").unwrap();
assert_eq!(section.tracks.len(), 2);
assert!(section.tracks.contains_key("melody"));
assert!(section.tracks.contains_key("bass"));
assert_eq!(section.duration, 2.0); }
#[test]
fn test_section_with_drums() {
let mut comp = Composition::new(Tempo::new(120.0));
comp.section("beat")
.track("drums")
.drum(DrumType::Kick, 0.5)
.drum(DrumType::Snare, 0.5)
.drum(DrumType::Kick, 0.5)
.drum(DrumType::Snare, 0.0);
let section = comp.sections.get("beat").unwrap();
let drums = section.tracks.get("drums").unwrap();
assert_eq!(drums.events.len(), 4);
}
#[test]
fn test_arrange_single_section() {
let mut comp = Composition::new(Tempo::new(120.0));
comp.section("verse").track("melody").notes(&[C4, E4], 0.5);
comp.arrange(&["verse"]);
let melody = comp.tracks.get("melody").unwrap();
assert_eq!(melody.events.len(), 2);
}
#[test]
fn test_arrange_multiple_sections() {
let mut comp = Composition::new(Tempo::new(120.0));
comp.section("intro").track("melody").note(&[C4], 1.0);
comp.section("verse").track("melody").notes(&[E4, G4], 0.5);
comp.arrange(&["intro", "verse"]);
let melody = comp.tracks.get("melody").unwrap();
assert_eq!(melody.events.len(), 3);
if let AudioEvent::Note(note) = &melody.events[0] {
assert_eq!(note.start_time, 0.0); }
if let AudioEvent::Note(note) = &melody.events[1] {
assert_eq!(note.start_time, 1.0); }
if let AudioEvent::Note(note) = &melody.events[2] {
assert_eq!(note.start_time, 1.5); }
}
#[test]
fn test_arrange_repeated_section() {
let mut comp = Composition::new(Tempo::new(120.0));
comp.section("chorus").track("melody").notes(&[C4, E4], 0.5);
comp.arrange(&["chorus", "chorus"]);
let melody = comp.tracks.get("melody").unwrap();
assert_eq!(melody.events.len(), 4);
if let AudioEvent::Note(note) = &melody.events[0] {
assert_eq!(note.start_time, 0.0);
}
if let AudioEvent::Note(note) = &melody.events[2] {
assert_eq!(note.start_time, 1.0); }
}
#[test]
fn test_section_pattern_repeat() {
let mut comp = Composition::new(Tempo::new(120.0));
comp.section("riff")
.track("guitar")
.pattern_start()
.notes(&[C4, E4], 0.25)
.repeat(2);
let section = comp.sections.get("riff").unwrap();
let guitar = section.tracks.get("guitar").unwrap();
assert_eq!(guitar.events.len(), 6); assert_eq!(section.duration, 1.5); }
#[test]
fn test_complex_arrangement() {
let mut comp = Composition::new(Tempo::new(120.0));
comp.section("intro").track("melody").notes(&[C4], 2.0);
comp.section("verse").track("melody").notes(&[E4, G4], 1.0);
comp.section("chorus")
.track("melody")
.notes(&[C5, B4, A4, G4], 0.5);
comp.arrange(&["intro", "verse", "chorus", "verse"]);
let melody = comp.tracks.get("melody").unwrap();
assert_eq!(melody.events.len(), 9);
if let AudioEvent::Note(note) = &melody.events[8] {
assert_eq!(note.start_time, 7.0); }
}
#[test]
fn test_section_at_positioning() {
let mut comp = Composition::new(Tempo::new(120.0));
comp.section("test")
.track("melody")
.at(1.0)
.note(&[C4], 0.5)
.at(3.0)
.note(&[E4], 0.5);
let section = comp.sections.get("test").unwrap();
let melody = section.tracks.get("melody").unwrap();
if let AudioEvent::Note(note) = &melody.events[0] {
assert_eq!(note.start_time, 1.0);
}
if let AudioEvent::Note(note) = &melody.events[1] {
assert_eq!(note.start_time, 3.0);
}
}
}