summaryrefslogtreecommitdiff
path: root/src/sheet/play.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/sheet/play.rs')
-rw-r--r--src/sheet/play.rs314
1 files changed, 314 insertions, 0 deletions
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<Wave> = 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::<Wave>::into).collect())
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ // use super::*;
+}