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/instruments.rs | 485 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 485 insertions(+) create mode 100644 src/instruments.rs (limited to 'src/instruments.rs') diff --git a/src/instruments.rs b/src/instruments.rs new file mode 100644 index 0000000..15ac7d4 --- /dev/null +++ b/src/instruments.rs @@ -0,0 +1,485 @@ +//! This file implements an interface for instruments. +//! +//! An instrument is a type that can produce a `Wave` when given a +//! note in semitone, a sample rate, and a duration in beats. + +use super::*; +use std::{ + f64::consts::PI, + fmt::{self, Display, Formatter}, +}; + +/// The interface of an instrument. +pub trait Instrument { + /// Produce pulses for a given note, sample rate, and duration. + /// + /// This will be separately modified to account for the effect of + /// attack-decay-sustain-release. See + /// [Wikipedia](). + fn play(&mut self, note: T, rate: U, bpm: S, duration: V, volume: R) -> Wave + where + T: Into, + S: Into, + U: Into, + V: Into, + R: Into; +} + +// Some instruments + +#[derive(Default)] +pub struct Sinus {} + +impl Display for Sinus { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "the pure sine instrument") + } +} + +impl Instrument for Sinus { + fn play(&mut self, note: T, rate: U, bpm: S, duration: V, volume: R) -> Wave + where + T: Into, + S: Into, + U: Into, + V: Into, + R: Into, + { + let note = note.into(); + let rate = rate.into(); + let duration = duration.into(); + let volume = volume.into(); + + let bpm = bpm.into(); + let duration_per_beat: Seconds = bpm.into(); + let duration: Seconds = (*duration * *duration_per_beat).into(); + + let nb_samples = (*duration * *rate).floor() as usize; + + let note_h: Hertz = note.into(); + + let step = *note_h * 2f64 * PI / *rate; + + let mut theta = 0f64; + + let mut result: Vec = vec![]; + + let mut current_adsr = 0f64; + + for i in 0..nb_samples { + current_adsr = adsr(i, nb_samples, current_adsr); + + let y: f64 = (*volume * f64::sin(theta)) * current_adsr; + + let y = if y >= 1f64 { + 1f64 + } else if y <= -1f64 { + -1f64 + } else { + y + }; + + result.push(y.into()); + + theta += step; + } + + Wave::new(result) + } +} + +// Other instruments: tangent, and variations of piano. + +// Tangent + +#[derive(Default)] +pub struct Tangent {} + +impl Display for Tangent { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "the pure tangent instrument") + } +} + +impl Instrument for Tangent { + fn play(&mut self, note: T, rate: U, bpm: S, duration: V, volume: R) -> Wave + where + T: Into, + S: Into, + U: Into, + V: Into, + R: Into, + { + let note = note.into(); + let rate = rate.into(); + let duration = duration.into(); + let volume = volume.into(); + + let bpm = bpm.into(); + let duration_per_beat: Seconds = bpm.into(); + let duration: Seconds = (*duration * *duration_per_beat).into(); + + let nb_samples = (*duration * *rate).floor() as usize; + + let note_h: Hertz = note.into(); + + let step = *note_h * 2f64 * PI / *rate; + + let mut theta = 0f64; + + let mut result: Vec = vec![]; + + let mut current_adsr = 0f64; + + for i in 0..nb_samples { + current_adsr = adsr(i, nb_samples, current_adsr); + + let y: f64 = (*volume * f64::tan(theta)) * current_adsr; + + let y = if y >= 1f64 { + 1f64 + } else if y <= -1f64 { + -1f64 + } else { + y + }; + + result.push(y.into()); + + theta += step; + } + + Wave::new(result) + } +} + +// Piano Without overtones + +#[derive(Default)] +pub struct PianoWOver {} + +impl Display for PianoWOver { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "the piano instrument without overtones") + } +} + +impl Instrument for PianoWOver { + fn play(&mut self, note: T, rate: U, bpm: S, duration: V, volume: R) -> Wave + where + T: Into, + S: Into, + U: Into, + V: Into, + R: Into, + { + let note = note.into(); + let rate = rate.into(); + let duration = duration.into(); + let volume = volume.into(); + + let bpm = bpm.into(); + let duration_per_beat: Seconds = bpm.into(); + let duration: Seconds = (*duration * *duration_per_beat).into(); + + let nb_samples = (*duration * *rate).floor() as usize; + + let note_h: Hertz = note.into(); + + let step = *note_h * 2f64 * PI / *rate; + + let mut theta = 0f64; + + let mut result: Vec = vec![]; + + for i in 0..nb_samples { + let mut y = f64::sin(theta); + // let mut y = 0.6f64 * f64::sin(theta) + 0.4f64 * f64::sin(2f64 * theta); + y *= f64::exp((-0.0035f64) * theta); + y += y * y * y; + y *= 1f64 + 16f64 * i as f64 * f64::exp((-6f64) * i as f64); + + y *= *volume; + + result.push(y.into()); + + theta += step; + } + + Wave::new(result) + } +} + +#[derive(Default)] +pub struct PianoVideo {} + +impl Display for PianoVideo { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "the piano instrument that I obtained from a video") + } +} + +impl Instrument for PianoVideo { + fn play(&mut self, note: T, rate: U, bpm: S, duration: V, volume: R) -> Wave + where + T: Into, + S: Into, + U: Into, + V: Into, + R: Into, + { + let note = note.into(); + let rate = rate.into(); + let duration = duration.into(); + let volume = volume.into(); + + let bpm = bpm.into(); + let duration_per_beat: Seconds = bpm.into(); + let duration: Seconds = (*duration * *duration_per_beat).into(); + + let nb_samples = (*duration * *rate).floor() as usize; + + let note_h: Hertz = note.into(); + + let step = *note_h * 2f64 * PI / *rate; + + let mut theta = 0f64; + + let mut result: Vec = vec![]; + + for i in 0..nb_samples { + let mut y = 0.6f64 * f64::sin(theta) + 0.4f64 * f64::sin(2f64 * theta); + y *= f64::exp((-0.0015f64) * theta); + y += y * y * y; + y *= 1f64 + 16f64 * i as f64 * f64::exp((-6f64) * i as f64); + + y *= *volume; + + result.push(y.into()); + + theta += step; + } + + Wave::new(result) + } +} + +#[derive(Default)] +pub struct PianoStackoverflow {} + +impl Display for PianoStackoverflow { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!( + f, + "the piano instrument that I obtained from a stackoverflow answer." + ) + } +} + +impl Instrument for PianoStackoverflow { + fn play(&mut self, note: T, rate: U, bpm: S, duration: V, volume: R) -> Wave + where + T: Into, + S: Into, + U: Into, + V: Into, + R: Into, + { + let note = note.into(); + let rate = rate.into(); + let duration = duration.into(); + let volume = volume.into(); + + let bpm = bpm.into(); + let duration_per_beat: Seconds = bpm.into(); + let duration: Seconds = (*duration * *duration_per_beat).into(); + + let nb_samples = (*duration * *rate).floor() as usize; + + let note_h: Hertz = note.into(); + + // dbg!(note); + // dbg!(note_h); + + let step = *note_h * 2f64 * PI / *rate; + + let mut theta = 0f64; + + let mut result: Vec = vec![]; + + for i in 0..nb_samples { + let mut y = f64::sin(theta) + + (1f64 / 2f64) * f64::sin(2f64 * theta) + + (1f64 / 4f64) * f64::sin(3f64 * theta) + + (1f64 / 8f64) * f64::sin(4f64 * theta) + + (1f64 / 16f64) * f64::sin(5f64 * theta) + + (1f64 / 32f64) * f64::sin(6f64 * theta); + y *= f64::exp((-0.0015f64) * theta); + y += y * y * y; + y *= 1f64 + 16f64 * i as f64 * f64::exp((-6f64) * i as f64); + + y *= *volume; + + result.push(y.into()); + + theta += step; + } + + Wave::new(result) + } +} + +#[allow(dead_code)] +fn adsr(step: usize, total: usize, current: f64) -> f64 { + if step < 4320 { + if current <= 1f64 { + current + 0.0002314814814814815f64 + } else { + 1f64 + } + } else if total - step < 4320 { + if current >= 0f64 { + current - 0.0002314814814814815f64 + } else { + 0f64 + } + } else { + current + } +} + +#[derive(Default)] +pub struct PianoFromC {} + +impl Display for PianoFromC { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "the piano instrument from the last project in C.") + } +} + +impl Instrument for PianoFromC { + fn play(&mut self, note: T, rate: U, bpm: S, duration: V, volume: R) -> Wave + where + T: Into, + S: Into, + U: Into, + V: Into, + R: Into, + { + let note = note.into(); + let rate = rate.into(); + let duration = duration.into(); + let volume = volume.into(); + + let bpm = bpm.into(); + let duration_per_beat: Seconds = bpm.into(); + let duration: Seconds = (*duration * *duration_per_beat).into(); + + let nb_samples = (*duration * *rate).floor() as usize; + + let note_h: Hertz = note.into(); + + // dbg!(note); + // dbg!(note_h); + + let step = *note_h * 2f64 * PI / *rate; + + let mut theta = 0f64; + + let mut result: Vec = vec![]; + + let mut adsr_factor = 1f64; + + #[allow(unused_variables)] + for i in 0..nb_samples { + let mut y = 0.65f64 * f64::sin(theta) + + 0.475f64 * f64::sin(2f64 * theta) + + 0.05f64 * f64::sin(3f64 * theta); + y *= f64::exp((-0.0015f64) * theta); + y += y * y * y; + y *= 1f64 + 16f64 * i as f64 * f64::exp((-6f64) * i as f64); + + adsr_factor = adsr(i, nb_samples, adsr_factor); + y *= adsr_factor; + + y *= *volume; + + // if y >= 1f64 { + // y = 1f64; + // } else if y <= -1f64 { + // y = -1f64; + // } + + result.push(y.into()); + + theta += step; + } + + Wave::new(result) + } +} + +#[derive(Default)] +pub struct Violin {} + +impl Display for Violin { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "an attempt at the violin instrument") + } +} + +impl Instrument for Violin { + fn play(&mut self, note: T, rate: U, bpm: S, duration: V, volume: R) -> Wave + where + T: Into, + S: Into, + U: Into, + V: Into, + R: Into, + { + let note = note.into(); + let rate = rate.into(); + let duration = duration.into(); + let volume = volume.into(); + + let bpm = bpm.into(); + let duration_per_beat: Seconds = bpm.into(); + let duration: Seconds = (*duration * *duration_per_beat).into(); + + let nb_samples = (*duration * *rate).floor() as usize; + + let note_h: Hertz = note.into(); + + let step = *note_h * 2f64 * PI / *rate; + + let mut theta = 0f64; + + let mut result: Vec = vec![]; + + for i in 0..nb_samples { + let mut y = f64::sin(theta) + + f64::sin(2f64 * theta) + + (3f64 / 4f64) * f64::sin(3f64 * theta) + + (1f64 / 8f64) * f64::sin(4f64 * theta) + + (1f64 / 16f64) * f64::sin(5f64 * theta) + + (1f64 / 32f64) * f64::sin(6f64 * theta) + + (1f64 / 64f64) * f64::sin(7f64 * theta) + + (10f64 / 128f64) * f64::sin(8f64 * theta) + + (1f64 / 256f64) * f64::sin(9f64 * theta) + + (1f64 / 512f64) * f64::sin(10f64 * theta) + + (1f64 / 1024f64) * f64::sin(11f64 * theta) + + (1f64 / 2048f64) * f64::sin(12f64 * theta); + y *= f64::exp((-0.00051f64) * theta); + // y += y * y * y; + y *= 1f64 + 16f64 * i as f64 * f64::exp((-6f64) * i as f64); + + y *= *volume; + + result.push(y.into()); + + theta += step; + } + + Wave::new(result) + } +} -- cgit v1.2.3-18-g5258