diff --git a/src/lib.rs b/src/lib.rs index b84fdb7..bdfcfe3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,138 +1,50 @@ pub mod note; pub mod piece; +pub mod technology; +pub mod time; pub mod wav; use std::fs::File; use std::io::{self, BufWriter}; -use note::notes::*; -use piece::{NoteEvent, Piece, Track, Wave}; +use crate::note::notes::*; +use crate::piece::{Piece, Track, Wave}; +use crate::time::Tick; #[rustfmt::skip] pub fn crazy_train() -> Piece { let mut piece = Piece::new(120); let mut main_track = Track::new(Wave::Square, 0.1); - let mut time = 0.0; for _ in 0..4 { - main_track.notes.push(NoteEvent::new(FS3, time, 0.5)); time += 0.5; - main_track.notes.push(NoteEvent::new(FS3, time, 0.5)); time += 0.5; - main_track.notes.push(NoteEvent::new(CS4, time, 0.5)); time += 0.5; - main_track.notes.push(NoteEvent::new(FS3, time, 0.5)); time += 0.5; - main_track.notes.push(NoteEvent::new(D4, time, 0.5)); time += 0.5; - main_track.notes.push(NoteEvent::new(FS3, time, 0.5)); time += 0.5; - main_track.notes.push(NoteEvent::new(CS4, time, 0.5)); time += 0.5; - main_track.notes.push(NoteEvent::new(FS3, time, 0.5)); time += 0.5; - main_track.notes.push(NoteEvent::new(B4, time, 0.5)); time += 0.5; - main_track.notes.push(NoteEvent::new(A4, time, 0.5)); time += 0.5; - main_track.notes.push(NoteEvent::new(GS3, time, 0.5)); time += 0.5; - main_track.notes.push(NoteEvent::new(A4, time, 0.5)); time += 0.5; - main_track.notes.push(NoteEvent::new(B4, time, 0.5)); time += 0.5; - main_track.notes.push(NoteEvent::new(A4, time, 0.5)); time += 0.5; - main_track.notes.push(NoteEvent::new(GS3, time, 0.5)); time += 0.5; - main_track.notes.push(NoteEvent::new(E3, time, 0.5)); time += 0.5; + main_track.add(FS3, Tick::half()); + main_track.add(FS3, Tick::half()); + main_track.add(CS4, Tick::half()); + main_track.add(FS3, Tick::half()); + main_track.add(D4, Tick::half()); + main_track.add(FS3, Tick::half()); + main_track.add(CS4, Tick::half()); + main_track.add(FS3, Tick::half()); + main_track.add(B4, Tick::half()); + main_track.add(A4, Tick::half()); + main_track.add(GS3, Tick::half()); + main_track.add(A4, Tick::half()); + main_track.add(B4, Tick::half()); + main_track.add(A4, Tick::half()); + main_track.add(GS3, Tick::half()); + main_track.add(E3, Tick::half()); } - main_track.notes.push(NoteEvent::new(FS3, time, 0.5)); + main_track.add(FS3, Tick::half()); piece.tracks.push(main_track); piece } -#[rustfmt::skip] -pub fn technology() -> Piece { - let mut piece = Piece::new(100); - - let mut bass_track = Track::new(Wave::Square, 0.05); - let mut time = 0.0; - for _ in 0..2 { - bass_track.notes.push(NoteEvent::new(A1, time, 4.0)); time += 4.0; - bass_track.notes.push(NoteEvent::new(G0, time, 2.0)); time += 2.0; - bass_track.notes.push(NoteEvent::new(A1, time, 2.0)); time += 2.0; - - bass_track.notes.push(NoteEvent::new(F0, time, 4.0)); time += 4.0; - bass_track.notes.push(NoteEvent::new(C1, time, 2.0)); time += 2.0; - bass_track.notes.push(NoteEvent::new(G0, time, 2.0)); time += 2.0; - } - piece.tracks.push(bass_track); - - let mut main_track = Track::new(Wave::Square, 0.1); - let mut time = 0.0; - - // Lick 1 - main_track.notes.push(NoteEvent::new(A4, time, 0.5 )); time += 0.5; - main_track.notes.push(NoteEvent::new(E4, time, 0.25)); time += 0.25; - main_track.notes.push(NoteEvent::new(A4, time, 0.25)); time += 0.25; - main_track.notes.push(NoteEvent::new(G3, time, 0.5 )); time += 0.5; - main_track.notes.push(NoteEvent::new(E4, time, 0.25)); time += 0.25; - main_track.notes.push(NoteEvent::new(G3, time, 0.25)); time += 0.25; - main_track.notes.push(NoteEvent::new(E3, time, 1.0 )); time += 1.0; - - time += 0.5; - main_track.notes.push(NoteEvent::new(A4, time, 1.0 )); time += 1.0; - main_track.notes.push(NoteEvent::new(G3, time, 1.0 )); time += 1.0; - main_track.notes.push(NoteEvent::new(E3, time, 0.5 )); time += 0.5; - main_track.notes.push(NoteEvent::new(E3, time, 0.25)); time += 0.25; - main_track.notes.push(NoteEvent::new(F3, time, 0.25)); time += 0.25; - main_track.notes.push(NoteEvent::new(E3, time, 0.25)); time += 0.25; - main_track.notes.push(NoteEvent::new(D3, time, 0.25)); time += 0.25; - main_track.notes.push(NoteEvent::new(E3, time, 0.5 )); time += 0.5; - - main_track.notes.push(NoteEvent::new(C3, time, 0.5 )); time += 0.5; - main_track.notes.push(NoteEvent::new(E3, time, 1.0 )); time += 1.0; - main_track.notes.push(NoteEvent::new(C3, time, 0.33)); time += 0.33; - main_track.notes.push(NoteEvent::new(D3, time, 0.42)); time += 0.42; - main_track.notes.push(NoteEvent::new(E3, time, 1.75)); time += 1.75; - - main_track.notes.push(NoteEvent::new(C3, time, 0.5 )); time += 0.5; - main_track.notes.push(NoteEvent::new(E3, time, 1.0 )); time += 1.0; - main_track.notes.push(NoteEvent::new(C3, time, 0.33)); time += 0.33; - main_track.notes.push(NoteEvent::new(E2, time, 0.42)); time += 0.42; - main_track.notes.push(NoteEvent::new(D2, time, 1.5 )); time += 1.5; - - main_track.notes.push(NoteEvent::new(G2, time, 0.25)); time += 0.25; - main_track.notes.push(NoteEvent::new(C3, time, 0.25)); time += 0.25; - main_track.notes.push(NoteEvent::new(G3, time, 0.25)); time += 0.25; - - // Lick 2 - main_track.notes.push(NoteEvent::new(A4, time, 0.5 )); time += 0.5; - main_track.notes.push(NoteEvent::new(E4, time, 0.25)); time += 0.25; - main_track.notes.push(NoteEvent::new(A4, time, 0.25)); time += 0.25; - main_track.notes.push(NoteEvent::new(G3, time, 0.5 )); time += 0.5; - main_track.notes.push(NoteEvent::new(E4, time, 0.25)); time += 0.25; - main_track.notes.push(NoteEvent::new(G3, time, 0.25)); time += 0.25; - main_track.notes.push(NoteEvent::new(E3, time, 1.0 )); time += 1.0; - - time += 0.5; - main_track.notes.push(NoteEvent::new(A4, time, 1.0 )); time += 1.0; - main_track.notes.push(NoteEvent::new(G3, time, 1.0 )); time += 1.0; - main_track.notes.push(NoteEvent::new(E3, time, 0.5 )); time += 0.5; - main_track.notes.push(NoteEvent::new(B4, time, 0.25)); time += 0.25; - main_track.notes.push(NoteEvent::new(C4, time, 0.25)); time += 0.25; - main_track.notes.push(NoteEvent::new(B4, time, 0.25)); time += 0.25; - main_track.notes.push(NoteEvent::new(A4, time, 0.25)); time += 0.25; - main_track.notes.push(NoteEvent::new(B4, time, 0.5 )); time += 0.5; - - main_track.notes.push(NoteEvent::new(C3, time, 0.5 )); time += 0.5; - main_track.notes.push(NoteEvent::new(E3, time, 1.0 )); time += 1.0; - main_track.notes.push(NoteEvent::new(C3, time, 0.33)); time += 0.33; - main_track.notes.push(NoteEvent::new(D3, time, 0.42)); time += 0.42; - main_track.notes.push(NoteEvent::new(E3, time, 1.75)); time += 1.75; - - main_track.notes.push(NoteEvent::new(C3, time, 0.5 )); time += 0.5; - main_track.notes.push(NoteEvent::new(E3, time, 1.0 )); time += 1.0; - main_track.notes.push(NoteEvent::new(C3, time, 0.33)); time += 0.33; - main_track.notes.push(NoteEvent::new(A3, time, 0.42)); time += 0.42; - main_track.notes.push(NoteEvent::new(G2, time, 2.25)); time += 2.25; - piece.tracks.push(main_track); - - piece -} - pub fn main() -> io::Result<()> { let file = File::create("output.wav")?; let mut writer = BufWriter::new(file); - technology().generate_wav(&mut writer)?; + crate::technology::technology().generate_wav(&mut writer)?; Ok(()) } diff --git a/src/piece.rs b/src/piece.rs index 9eb7cfc..85d95e6 100644 --- a/src/piece.rs +++ b/src/piece.rs @@ -1,6 +1,7 @@ use std::io::{self, Write}; use crate::note::*; +use crate::time::{Tick, TICKS_PER_BEAT}; use crate::wav; #[derive(Copy, Clone)] @@ -30,12 +31,12 @@ impl Wave { pub struct NoteEvent { pub note: Note, - pub start: f64, - pub duration: f64, + pub start: Tick, + pub duration: Tick, } impl NoteEvent { - pub fn new(note: Note, start: f64, duration: f64) -> NoteEvent { + pub fn new(note: Note, start: Tick, duration: Tick) -> NoteEvent { NoteEvent { note, start, @@ -48,6 +49,7 @@ pub struct Track { pub wave: Wave, pub amplitude: f64, pub notes: Vec, + pub current_tick: Tick, } impl Track { @@ -56,8 +58,19 @@ impl Track { wave, amplitude, notes: vec![], + current_tick: Tick(0), } } + + pub fn add(&mut self, note: Note, duration: Tick) { + self.notes + .push(NoteEvent::new(note, self.current_tick, duration)); + self.current_tick += duration; + } + + pub fn add_silence(&mut self, duration: Tick) { + self.current_tick += duration; + } } pub struct Piece { @@ -73,26 +86,42 @@ impl Piece { } } - pub fn generate_wav(&self, writer: &mut W) -> io::Result<()> { - let amplitude = 0.1; - let seconds_per_beat = 60.0 / self.bpm as f64; - let params = wav::Params::new(); + pub fn add_metronome(&mut self, wave: Wave, amplitude: f64) { + use crate::note::notes::{A3, A4}; - let max_beat = self + let mut metronome = Track::new(wave, amplitude); + for beat in 0..self.max_beat() { + metronome.add(if beat % 4 == 0 { A4 } else { A3 }, Tick::quarter()); + metronome.add_silence(Tick::quarters(3)); + } + + self.tracks.push(metronome); + } + + pub fn max_beat(&self) -> u64 { + let max_tick = self .tracks .iter() .map(|x| { x.notes .iter() .map(|x| x.start + x.duration) - .max_by(|x, y| x.total_cmp(y)) - .unwrap_or(0.0) + .max() + .unwrap_or(Tick(0)) }) - .max_by(|x, y| x.total_cmp(y)) - .unwrap_or(0.0); + .max() + .unwrap_or(Tick(0)); + + max_tick.0 / TICKS_PER_BEAT + } + + pub fn generate_wav(&self, writer: &mut W) -> io::Result<()> { + let seconds_per_beat = 60.0 / self.bpm as f64; + let seconds_per_tick = 60.0 / (self.bpm as u64 * TICKS_PER_BEAT) as f64; + let params = wav::Params::new(); let total_samples = - (max_beat * seconds_per_beat * params.sampling_rate as f64).ceil() as u32; + (self.max_beat() as f64 * seconds_per_beat * params.sampling_rate as f64).ceil() as u32; let data_size = total_samples * params.channels as u32 * params.bytes_per_sample as u32; let total_size = 44 + data_size; @@ -107,8 +136,8 @@ impl Piece { let mut value: f64 = 0.0; for track in &self.tracks { for event in &track.notes { - let start_time = event.start * seconds_per_beat; - let end_time = start_time + event.duration * seconds_per_beat; + let start_time = event.start.0 as f64 * seconds_per_tick; + let end_time = (event.start + event.duration).0 as f64 * seconds_per_tick; if t >= start_time && t < end_time { value += track.amplitude * track.wave.sample(&event.note, t); } diff --git a/src/technology.rs b/src/technology.rs new file mode 100644 index 0000000..5025e63 --- /dev/null +++ b/src/technology.rs @@ -0,0 +1,194 @@ +use crate::note::notes::*; +use crate::piece::{Piece, Track, Wave}; +use crate::time::Tick; + +pub fn technology() -> Piece { + let mut piece = Piece::new(100); + + piece.tracks.push(bass_track()); + piece.tracks.push(chords()); + piece.tracks.push(main_track()); + // piece.add_metronome(Wave::Square, 0.05); + + piece +} + +pub fn bass_track() -> Track { + let mut bass_track = Track::new(Wave::Square, 0.05); + for _ in 0..4 { + bass_track.add(A1, Tick::bar()); + bass_track.add(G0, Tick::beats(2)); + bass_track.add(A1, Tick::beats(2)); + + bass_track.add(F0, Tick::bar()); + bass_track.add(C1, Tick::beats(2)); + bass_track.add(G0, Tick::beats(2)); + } + bass_track +} + +pub fn chords() -> Track { + let mut track = Track::new(Wave::Square, 0.05); + + let add_e_minor = |track: &mut Track| { + track.add(E3, Tick::sixteenth()); + track.add(A4, Tick::sixteenth()); + track.add(C4, Tick::sixteenth()); + track.add(E3, Tick::sixteenth()); + }; + + let add_d_major = |track: &mut Track| { + track.add(D3, Tick::sixteenth()); + track.add(G3, Tick::sixteenth()); + track.add(B4, Tick::sixteenth()); + track.add(D3, Tick::sixteenth()); + }; + + let add_c_major = |track: &mut Track| { + track.add(C3, Tick::sixteenth()); + track.add(F3, Tick::sixteenth()); + track.add(A4, Tick::sixteenth()); + track.add(C3, Tick::sixteenth()); + }; + + let add_c_major_2 = |track: &mut Track| { + track.add(C3, Tick::sixteenth()); + track.add(E3, Tick::sixteenth()); + track.add(G3, Tick::sixteenth()); + track.add(C3, Tick::sixteenth()); + }; + + let add_g_major = |track: &mut Track| { + track.add(B3, Tick::sixteenth()); + track.add(D3, Tick::sixteenth()); + track.add(G3, Tick::sixteenth()); + track.add(B3, Tick::sixteenth()); + }; + + for _ in 0..4 { + track.add_silence(Tick::half()); + add_e_minor(&mut track); + + track.add_silence(Tick::half()); + add_e_minor(&mut track); + + track.add_silence(Tick::half()); + add_e_minor(&mut track); + + track.add_silence(Tick::half() + Tick::quarters(3)); + add_e_minor(&mut track); + + track.add_silence(Tick::quarters(3)); + add_d_major(&mut track); + + track.add_silence(Tick::half()); + add_d_major(&mut track); + + track.add_silence(Tick::half()); + add_e_minor(&mut track); + + track.add_silence(Tick::half() + Tick::quarters(3)); + add_e_minor(&mut track); + + track.add_silence(Tick::quarters(3)); + add_c_major(&mut track); + + track.add_silence(Tick::half()); + add_c_major(&mut track); + + track.add_silence(Tick::half()); + add_c_major(&mut track); + + track.add_silence(Tick::half() + Tick::quarters(3)); + add_c_major(&mut track); + + track.add_silence(Tick::quarters(3)); + add_c_major_2(&mut track); + + track.add_silence(Tick::half()); + add_c_major_2(&mut track); + + track.add_silence(Tick::half()); + add_g_major(&mut track); + + track.add_silence(Tick::half() + Tick::quarters(3)); + add_g_major(&mut track); + + track.add_silence(Tick::quarter()); + } + + track +} + +pub fn main_track() -> Track { + let mut track = Track::new(Wave::Square, 0.1); + track.add_silence(Tick::bars(4)); + + // Lick 1 + track.add(A4, Tick::half()); + track.add(E4, Tick::quarter()); + track.add(A4, Tick::quarter()); + track.add(G3, Tick::half()); + track.add(E4, Tick::quarter()); + track.add(G3, Tick::quarter()); + track.add(E3, Tick::beat()); + + track.add_silence(Tick::half()); + track.add(A4, Tick::beat()); + track.add(G3, Tick::beat()); + track.add(E3, Tick::half()); + track.add(E3, Tick::quarter()); + track.add(F3, Tick::quarter()); + track.add(E3, Tick::quarter()); + track.add(D3, Tick::quarter()); + track.add(E3, Tick::half()); + + track.add(C3, Tick::half()); + track.add(E3, Tick::beat()); + track.add(C3, Tick::half()); // 0.33); + track.add(D3, Tick::half()); // 0.42); + track.add(E3, Tick::beat() + Tick::half()); // 1.75); + + track.add(C3, Tick::half()); + track.add(E3, Tick::beat()); + track.add(C3, Tick::half()); + track.add(E2, Tick::half()); + track.add(D2, Tick::beat() + Tick::quarter()); + + track.add(G2, Tick::quarter()); + track.add(C3, Tick::quarter()); + track.add(G3, Tick::quarter()); + + // Lick 2 + track.add(A4, Tick::half()); + track.add(E4, Tick::quarter()); + track.add(A4, Tick::quarter()); + track.add(G3, Tick::half()); + track.add(E4, Tick::quarter()); + track.add(G3, Tick::quarter()); + track.add(E3, Tick::beat()); + + track.add_silence(Tick::half()); + track.add(A4, Tick::beat()); + track.add(G3, Tick::beat()); + track.add(E3, Tick::half()); + track.add(B4, Tick::quarter()); + track.add(C4, Tick::quarter()); + track.add(B4, Tick::quarter()); + track.add(A4, Tick::quarter()); + track.add(B4, Tick::half()); + + track.add(C3, Tick::half()); + track.add(E3, Tick::beat()); + track.add(C3, Tick::half()); + track.add(D3, Tick::half()); + track.add(E3, Tick::beat() + Tick::half()); + + track.add(C3, Tick::half()); + track.add(E3, Tick::beat()); + track.add(C3, Tick::half()); + track.add(E2, Tick::half()); + track.add(D2, Tick::beats(2)); + + track +} diff --git a/src/time.rs b/src/time.rs new file mode 100644 index 0000000..eb3d207 --- /dev/null +++ b/src/time.rs @@ -0,0 +1,103 @@ +use std::ops::{Add, AddAssign, Mul, MulAssign, Sub, SubAssign}; + +pub const TICKS_PER_BEAT: u64 = 480; + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] +pub struct Tick(pub u64); + +impl Tick { + pub fn bars(b: u64) -> Self { + Tick(b * 4 * TICKS_PER_BEAT) + } + + pub fn bar() -> Self { + Tick(4 * TICKS_PER_BEAT) + } + + pub fn beats(b: u64) -> Self { + Tick(b * TICKS_PER_BEAT) + } + + pub fn beat() -> Self { + Tick(TICKS_PER_BEAT) + } + + pub fn halves(h: u64) -> Self { + Tick(h * TICKS_PER_BEAT / 2) + } + + pub fn half() -> Self { + Tick(TICKS_PER_BEAT / 2) + } + + pub fn triplets(b: u64) -> Self { + Tick(b * TICKS_PER_BEAT / 3) + } + + pub fn triplet() -> Self { + Tick(TICKS_PER_BEAT / 3) + } + + pub fn quarters(b: u64) -> Self { + Tick(b * TICKS_PER_BEAT / 4) + } + + pub fn quarter() -> Self { + Tick(TICKS_PER_BEAT / 4) + } + + pub fn eighths(b: u64) -> Self { + Tick(b * TICKS_PER_BEAT / 8) + } + + pub fn eighth() -> Self { + Tick(TICKS_PER_BEAT / 8) + } + + pub fn sixteenths(b: u64) -> Self { + Tick(b * TICKS_PER_BEAT / 16) + } + + pub fn sixteenth() -> Self { + Tick(TICKS_PER_BEAT / 16) + } +} + +impl Add for Tick { + type Output = Self; + fn add(self, rhs: Self) -> Self { + Tick(self.0 + rhs.0) + } +} + +impl AddAssign for Tick { + fn add_assign(&mut self, rhs: Self) { + self.0 += rhs.0; + } +} + +impl Sub for Tick { + type Output = Self; + fn sub(self, rhs: Self) -> Self { + Tick(self.0 - rhs.0) + } +} + +impl SubAssign for Tick { + fn sub_assign(&mut self, rhs: Self) { + self.0 -= rhs.0; + } +} + +impl Mul for Tick { + type Output = Self; + fn mul(self, rhs: u64) -> Self { + Tick(self.0 * rhs) + } +} + +impl MulAssign for Tick { + fn mul_assign(&mut self, rhs: u64) { + self.0 *= rhs; + } +}