From e954df3f896bd18494cd27d77b26bbb2005de8a7 Mon Sep 17 00:00:00 2001 From: JSDurand Date: Wed, 24 Aug 2022 23:54:13 +0800 Subject: First commit Now the project is in a somewhat complete state, ready for future enhancements. --- src/sheet/play.rs | 314 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 314 insertions(+) create mode 100644 src/sheet/play.rs (limited to 'src/sheet/play.rs') diff --git a/src/sheet/play.rs b/src/sheet/play.rs new file mode 100644 index 0000000..c2a632d --- /dev/null +++ b/src/sheet/play.rs @@ -0,0 +1,314 @@ +#![warn(missing_docs)] +//! This file implements the interface to turn a parsed representation +//! of a sheet into pulses. + +use super::*; + +#[allow(unused_imports)] +use crate::instruments::{self, Instrument as IInstrument}; + +#[derive(Debug, Clone, Copy)] +/// The environment when generating waves from a sheet. +struct SheetEnv { + volume: Volume, + bpm: BPM, + instrument: Instrument, + duration: Beats, + octave: usize, + rate: Samples, +} + +impl SheetEnv { + fn set_volume(&mut self, volume: Volume) { + self.volume = volume; + } + fn set_bpm(&mut self, bpm: BPM) { + self.bpm = bpm; + } + fn set_instrument(&mut self, instrument: Instrument) { + self.instrument = instrument; + } + fn set_duration(&mut self, duration: Beats) { + self.duration = duration; + } + fn set_octave(&mut self, octave: usize) { + self.octave = octave; + } +} + +impl Default for SheetEnv { + fn default() -> Self { + let volume = Volume::default(); + let bpm = BPM::default(); + let instrument = Instrument::default(); + let duration = Beats::default(); + let octave = 4usize; + let rate = Samples::default(); + + Self { + volume, + bpm, + instrument, + duration, + octave, + rate, + } + } +} + +impl From<(&SheetDuration, Beats)> for Beats { + fn from((duration, env_duration): (&SheetDuration, Beats)) -> Self { + let duration_base: Beats = if let Some(base) = duration.beats { + base + } else { + env_duration + }; + + let mut final_duration = duration_base; + + for i in (0..duration.dots).map(|n| n + 1) { + let power = 2usize.pow(i as u32) as f64; + + final_duration = (*final_duration + *duration_base / power).into(); + } + + final_duration + } +} + +impl From<(&SheetUnit, &mut SheetEnv)> for Wave { + fn from((unit, env): (&SheetUnit, &mut SheetEnv)) -> Self { + let mut result = Vec::new(); + + // Unfortunately our trait is wrongly designed and that is not + // dynamically dispatchable, so we need separate variables for + // each instrument. + let mut piano = instruments::PianoFromC::default(); + let mut violin = instruments::Violin::default(); + let mut sinus = instruments::Sinus::default(); + let mut tangent = instruments::Tangent::default(); + + match unit { + SheetUnit::Tone(chord, duration) => { + let mut local_waves = Vec::new(); + + for tone in chord.0.iter() { + let octave = if let Some(abs_or_rel) = tone.octave { + match abs_or_rel { + SheetOctave::Absolute(absolute_octave) => absolute_octave, + SheetOctave::Relative(relative_octave) => { + let result: isize = relative_octave + env.octave as isize; + + assert!(!result.is_negative()); + + result as usize + } + } + } else { + env.octave + }; + + env.set_octave(octave); + + let semitone: Semitones = (octave, tone.semitone).into(); + + let final_duration: Beats = (duration, env.duration).into(); + + env.set_duration(final_duration); + + let wave = match env.instrument { + Instrument::Piano => { + piano.play(semitone, env.rate, env.bpm, final_duration, env.volume) + } + Instrument::Violin => { + violin.play(semitone, env.rate, env.bpm, final_duration, env.volume) + } + Instrument::Sinus => { + sinus.play(semitone, env.rate, env.bpm, final_duration, env.volume) + } + Instrument::Tangent => { + tangent.play(semitone, env.rate, env.bpm, final_duration, env.volume) + } + }; + + local_waves.push(wave); + } + + let local_result = mix_waves_exact(local_waves); + + result.append(&mut local_result.to_vec()); + } + SheetUnit::Silence(duration) => { + let duration_per_beat: Seconds = env.bpm.into(); + + let final_duration: Beats = (duration, env.duration).into(); + + env.set_duration(final_duration); + + let duration: Seconds = (*final_duration * *duration_per_beat).into(); + let nb_samples = (*duration * *env.rate).floor() as usize; + + result.append(&mut vec![0f64.into(); nb_samples]); + } + SheetUnit::Instruction(instruction) => match instruction { + Instruction::VolumeTo(volume) => { + env.set_volume(*volume); + } + Instruction::BPMTo(bpm) => { + env.set_bpm(*bpm); + } + Instruction::InstrumentTo(instrument) => { + env.set_instrument(*instrument); + } + }, + } + + Wave::new(result) + } +} + +impl From<(&mut SheetGroup, &mut SheetEnv)> for Wave { + fn from((group, env): (&mut SheetGroup, &mut SheetEnv)) -> Self { + group.do_cram(&mut env.duration); + + // if group.nodes.len() == 19 { + // println!("group={group:?}"); + // } + + // After we crammed the expressions, we are sure that all + // tones have the correct durations, so we can safely ignore + // all cram expressions afterwards. + + let mut stack = vec![0usize]; + let mut seen = vec![false; group.nodes.len()]; + let mut stack_args: Vec = vec![]; + + while let Some(top) = stack.pop() { + match &group.nodes[top] { + SheetGroupNode::Intermediate(repetition, _) if *repetition != 0 => { + if !seen[top] { + stack.push(top); + + stack.append( + &mut group.edges[top] + .iter() + .copied() + .rev() + .filter(|child| match group.nodes[*child] { + SheetGroupNode::Intermediate(child_repetition, _) + if child_repetition != 0 => + { + true + } + SheetGroupNode::Unit(_) => true, + _ => false, + }) + .collect(), + ); + } else { + match repetition { + 0 => { + unreachable!() + } + // 1 => { + // for _ in 0..group.edges[top] + // .iter() + // .filter(|child| match group.nodes[**child] { + // SheetGroupNode::Intermediate(child_repetition, _) + // if child_repetition != 0 => + // { + // true + // } + // SheetGroupNode::Unit(_) => true, + // _ => false, + // }) + // .count() + // { + // result.append(&mut stack_args.pop().unwrap().to_vec()); + // } + // } + _ => { + let mut local_stack_args = vec![]; + let mut local_results = vec![]; + let mut final_local_result = vec![]; + + for _ in 0..group.edges[top] + .iter() + .filter(|child| match group.nodes[**child] { + SheetGroupNode::Intermediate(child_repetition, _) + if child_repetition != 0 => + { + true + } + SheetGroupNode::Unit(_) => true, + _ => false, + }) + .count() + { + local_stack_args.push(stack_args.pop().unwrap()); + } + + for local_wave in local_stack_args.into_iter().rev() { + local_results.append(&mut local_wave.to_vec()); + } + + for _ in 0..(repetition - 1) { + final_local_result.append(&mut local_results.clone()); + } + + final_local_result.append(&mut local_results); + + stack_args.push(Wave::new(final_local_result)); + } + } + } + } + SheetGroupNode::Unit(unit) => { + stack_args.push((unit, &mut *env).into()); + } + _ => { + continue; + } + } + + seen[top] = true; + } + + let result = stack_args + .iter() + .flat_map(|vec| vec.iter()) + .copied() + .collect(); + + Wave::new(result) + } +} + +// Note: The rest are but ordinary house-keeping codes to glue +// everything together. + +impl From<&mut SheetBlock> for Wave { + fn from(block: &mut SheetBlock) -> Self { + let mut result = Vec::new(); + let mut env = SheetEnv::default(); + + for group in block.groups.iter_mut() { + let wave: Wave = (group, &mut env).into(); + + result.append(&mut wave.to_vec()); + } + + Self::new(result) + } +} + +impl From<&mut Sheet> for Wave { + fn from(sheet: &mut Sheet) -> Self { + mix_waves_exact(sheet.blocks.iter_mut().map(Into::::into).collect()) + } +} + +#[cfg(test)] +mod tests { + // use super::*; +} -- cgit v1.2.3-18-g5258