This commit is contained in:
Thomas Forgione 2026-02-16 00:03:49 +01:00
parent 99b9a79d5e
commit eed4a3494d
2 changed files with 178 additions and 32 deletions

View File

@ -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(())
}

View File

@ -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<NoteEvent>,
}
impl Track {
pub fn new(wave: Wave, amplitude: f64) -> Track {
Track {
wave,
amplitude,
notes: vec![],
}
}
}
pub struct Piece {
pub tracks: Vec<Track>,
pub bpm: u32,
}
impl Piece {
pub fn new(bpm: u32) -> Piece {
Piece { bpm, notes: vec![] }
Piece {
bpm,
tracks: vec![],
}
}
pub fn generate_wav<W: Write>(&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())?;
}