//! This crate implements a simple utility to make music as pure //! numbers. use std::{ fmt::{self, Display, Formatter}, ops::{Deref, DerefMut}, }; macro_rules! deftype { ($($x:ident),*) => { $( #[derive(Debug, Copy, Clone)] pub struct $x(f64); impl From for $x { fn from(n: f64) -> Self { Self(n) } } impl Deref for $x { type Target = f64; fn deref(&self) -> &Self::Target { &self.0 } } impl DerefMut for $x { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } )* }; } deftype!(Volume, Samples, Hertz, Seconds, Pulse, BPM, Beats, Semitones); impl Display for Semitones { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let translated = **self + 9f64; let quotient = translated.div_euclid(12f64) as i64; let octave_string = (quotient + 4).to_string(); let remainder = translated.rem_euclid(12f64) as i64; // Due to round-off error, there is the possibility that the // remainder is equal to the divider's absolute value, // according to the official documentation, so one more // conversion is necessary. let remainder = if remainder == 12i64 { 0i64 } else { remainder }; let note_string = match remainder { 0i64 => String::from("C"), 1i64 => String::from("C#"), 2i64 => String::from("D"), 3i64 => String::from("D#"), 4i64 => String::from("E"), 5i64 => String::from("F"), 6i64 => String::from("F#"), 7i64 => String::from("G"), 8i64 => String::from("G#"), 9i64 => String::from("A"), 10i64 => String::from("A#"), 11i64 => String::from("B"), _ => panic!("remainder not in the range [0, 11]: {remainder}"), }; write!(f, "{note_string}{octave_string}") } } #[allow(clippy::derivable_impls)] impl Default for Pulse { fn default() -> Self { Self(f64::default()) } } #[derive(Debug, Clone, Default)] pub struct Wave { pulses: Vec, } impl Wave { /// Construct a new wave pub fn new(pulses: Vec) -> Self { Self { pulses } } /// Return the vector contained inside it pub fn to_vec(self) -> Vec { self.pulses } } #[allow(dead_code)] fn mix_waves_no_divide(waves: Vec) -> Wave { let mut index = 0; let mut result_pulses = Vec::new(); loop { let mut count = 0usize; let mut local_result = 0f64; for wave in waves.iter() { if index < wave.len() { local_result += *wave[index]; count += 1; } } match count { 0 => { break; } _ => { result_pulses.push(local_result.into()); } } index += 1; } Wave::new(result_pulses) } #[allow(dead_code)] fn mix_waves_exact(waves: Vec) -> Wave { let mut index = 0; let mut result_pulses = Vec::new(); let waves_len = waves.len(); loop { let mut count = 0usize; let mut local_result = 0f64; for wave in waves.iter() { if index < wave.len() { local_result += *wave[index]; count += 1; } } match count { 0 => { break; } _ => { result_pulses.push((local_result / waves_len as f64).into()); } } index += 1; } Wave::new(result_pulses) } #[allow(dead_code)] fn mix_waves(waves: Vec) -> Wave { let mut index = 0; let mut result_pulses = Vec::new(); loop { let mut count = 0usize; let mut local_result = 0f64; for wave in waves.iter() { if index < wave.len() { local_result += *wave[index]; count += 1; } } match count { 0 => { break; } 1 => { result_pulses.push(local_result.into()); } _ => { result_pulses.push((local_result / count as f64).into()); } } index += 1; } Wave::new(result_pulses) } impl Deref for Wave { type Target = [Pulse]; fn deref(&self) -> &Self::Target { &self.pulses } } // The twelveth root of two. const STD_BASE: Hertz = Hertz(1.059463094359295f64); impl Default for Volume { fn default() -> Self { 1f64.into() } } impl Default for BPM { fn default() -> Self { 120f64.into() } } impl Default for Beats { fn default() -> Self { 1f64.into() } } impl From for Seconds { fn from(b: BPM) -> Self { (60f64 / *b).into() } } impl Default for Samples { fn default() -> Self { 48000f64.into() } } impl Default for Hertz { fn default() -> Self { 440f64.into() } } impl From for Hertz { fn from(st: Semitones) -> Self { (*Self::default() * STD_BASE.powf(*st)).into() } } pub mod instruments; pub mod output; pub mod sheet; #[cfg(test)] mod tests { use super::*; #[test] fn test_stoh() { let st: Semitones = 1f64.into(); let hertz: Hertz = st.into(); println!("hertz = {hertz:?}"); } #[test] fn test_btos() { let b = BPM::default(); let s: Seconds = b.into(); println!("second = {s:?}"); } }