summaryrefslogtreecommitdiff
path: root/src/instruments.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/instruments.rs')
-rw-r--r--src/instruments.rs485
1 files changed, 485 insertions, 0 deletions
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](<https://en.wikipedia.org/wiki/Envelope_(music)>).
+ fn play<T, S, U, V, R>(&mut self, note: T, rate: U, bpm: S, duration: V, volume: R) -> Wave
+ where
+ T: Into<Semitones>,
+ S: Into<BPM>,
+ U: Into<Samples>,
+ V: Into<Beats>,
+ R: Into<Volume>;
+}
+
+// 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<T, S, U, V, R>(&mut self, note: T, rate: U, bpm: S, duration: V, volume: R) -> Wave
+ where
+ T: Into<Semitones>,
+ S: Into<BPM>,
+ U: Into<Samples>,
+ V: Into<Beats>,
+ R: Into<Volume>,
+ {
+ 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<Pulse> = 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<T, S, U, V, R>(&mut self, note: T, rate: U, bpm: S, duration: V, volume: R) -> Wave
+ where
+ T: Into<Semitones>,
+ S: Into<BPM>,
+ U: Into<Samples>,
+ V: Into<Beats>,
+ R: Into<Volume>,
+ {
+ 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<Pulse> = 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<T, S, U, V, R>(&mut self, note: T, rate: U, bpm: S, duration: V, volume: R) -> Wave
+ where
+ T: Into<Semitones>,
+ S: Into<BPM>,
+ U: Into<Samples>,
+ V: Into<Beats>,
+ R: Into<Volume>,
+ {
+ 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<Pulse> = 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<T, S, U, V, R>(&mut self, note: T, rate: U, bpm: S, duration: V, volume: R) -> Wave
+ where
+ T: Into<Semitones>,
+ S: Into<BPM>,
+ U: Into<Samples>,
+ V: Into<Beats>,
+ R: Into<Volume>,
+ {
+ 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<Pulse> = 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<T, S, U, V, R>(&mut self, note: T, rate: U, bpm: S, duration: V, volume: R) -> Wave
+ where
+ T: Into<Semitones>,
+ S: Into<BPM>,
+ U: Into<Samples>,
+ V: Into<Beats>,
+ R: Into<Volume>,
+ {
+ 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<Pulse> = 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<T, S, U, V, R>(&mut self, note: T, rate: U, bpm: S, duration: V, volume: R) -> Wave
+ where
+ T: Into<Semitones>,
+ S: Into<BPM>,
+ U: Into<Samples>,
+ V: Into<Beats>,
+ R: Into<Volume>,
+ {
+ 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<Pulse> = 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<T, S, U, V, R>(&mut self, note: T, rate: U, bpm: S, duration: V, volume: R) -> Wave
+ where
+ T: Into<Semitones>,
+ S: Into<BPM>,
+ U: Into<Samples>,
+ V: Into<Beats>,
+ R: Into<Volume>,
+ {
+ 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<Pulse> = 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)
+ }
+}