diff --git a/src/lib.rs b/src/lib.rs index 51de56a..b84fdb7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,37 +6,133 @@ use std::fs::File; use std::io::{self, BufWriter}; use note::notes::*; -use piece::{NoteEvent, Piece}; +use piece::{NoteEvent, Piece, Track, Wave}; #[rustfmt::skip] -pub fn main() -> io::Result<()> { +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 { - piece.notes.push(NoteEvent::new(FS3, time, 0.5)); time += 0.5; - piece.notes.push(NoteEvent::new(FS3, time, 0.5)); time += 0.5; - piece.notes.push(NoteEvent::new(CS4, time, 0.5)); time += 0.5; - piece.notes.push(NoteEvent::new(FS3, time, 0.5)); time += 0.5; - piece.notes.push(NoteEvent::new(D4, time, 0.5)); time += 0.5; - piece.notes.push(NoteEvent::new(FS3, time, 0.5)); time += 0.5; - piece.notes.push(NoteEvent::new(CS4, time, 0.5)); time += 0.5; - piece.notes.push(NoteEvent::new(FS3, time, 0.5)); time += 0.5; - piece.notes.push(NoteEvent::new(B4, time, 0.5)); time += 0.5; - piece.notes.push(NoteEvent::new(A4, time, 0.5)); time += 0.5; - piece.notes.push(NoteEvent::new(GS3, time, 0.5)); time += 0.5; - piece.notes.push(NoteEvent::new(A4, time, 0.5)); time += 0.5; - piece.notes.push(NoteEvent::new(B4, time, 0.5)); time += 0.5; - piece.notes.push(NoteEvent::new(A4, time, 0.5)); time += 0.5; - piece.notes.push(NoteEvent::new(GS3, time, 0.5)); time += 0.5; - piece.notes.push(NoteEvent::new(E3, 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(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; } - piece.notes.push(NoteEvent::new(FS3, time, 0.5)); + main_track.notes.push(NoteEvent::new(FS3, time, 0.5)); + 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); - piece.generate_wav(&mut writer)?; + technology().generate_wav(&mut writer)?; Ok(()) } diff --git a/src/piece.rs b/src/piece.rs index 7de789d..9eb7cfc 100644 --- a/src/piece.rs +++ b/src/piece.rs @@ -3,6 +3,31 @@ use std::io::{self, Write}; use crate::note::*; use crate::wav; +#[derive(Copy, Clone)] +pub enum Wave { + Square, + Triangle, +} + +impl Wave { + pub fn sample(self, note: &Note, t: f64) -> f64 { + let freq = note.freq(); + match self { + Wave::Square => { + if (2.0 * std::f64::consts::PI * freq * t).sin() >= 0.0 { + 1.0 + } else { + -1.0 + } + } + Wave::Triangle => { + let phase = (freq * t) % 1.0; + 4.0 * (phase - 0.5).abs() - 1.0 + } + } + } +} + pub struct NoteEvent { pub note: Note, pub start: f64, @@ -19,14 +44,33 @@ impl NoteEvent { } } -pub struct Piece { +pub struct Track { + pub wave: Wave, + pub amplitude: f64, pub notes: Vec, +} + +impl Track { + pub fn new(wave: Wave, amplitude: f64) -> Track { + Track { + wave, + amplitude, + notes: vec![], + } + } +} + +pub struct Piece { + pub tracks: Vec, pub bpm: u32, } impl Piece { pub fn new(bpm: u32) -> Piece { - Piece { bpm, notes: vec![] } + Piece { + bpm, + tracks: vec![], + } } pub fn generate_wav(&self, writer: &mut W) -> io::Result<()> { @@ -35,9 +79,15 @@ impl Piece { let params = wav::Params::new(); let max_beat = self - .notes + .tracks .iter() - .map(|x| x.start + x.duration) + .map(|x| { + x.notes + .iter() + .map(|x| x.start + x.duration) + .max_by(|x, y| x.total_cmp(y)) + .unwrap_or(0.0) + }) .max_by(|x, y| x.total_cmp(y)) .unwrap_or(0.0); @@ -55,17 +105,17 @@ impl Piece { for i in 0..total_samples { let t = i as f64 / params.sampling_rate as f64; let mut value: f64 = 0.0; - for event in &self.notes { - let start_time = event.start * seconds_per_beat; - let end_time = start_time + event.duration * seconds_per_beat; - if t >= start_time && t < end_time { - let freq = event.note.freq(); - let sin = (std::f64::consts::TAU * freq * (t - start_time)).sin(); - value += if sin >= 0.0 { 1.0 } else { -1.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; + if t >= start_time && t < end_time { + value += track.amplitude * track.wave.sample(&event.note, t); + } } } - let sample = (value * (i16::MAX as f64) * amplitude).round() as i16; + let sample = (value * (i16::MAX as f64)).round() as i16; writer.write_all(&sample.to_le_bytes())?; }