This commit is contained in:
Thomas Forgione 2026-02-16 19:24:40 +01:00
parent eed4a3494d
commit bf8f7f397e
4 changed files with 364 additions and 126 deletions

View File

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

View File

@ -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<NoteEvent>,
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<W: Write>(&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<W: Write>(&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);
}

194
src/technology.rs Normal file
View File

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

103
src/time.rs Normal file
View File

@ -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<u64> for Tick {
type Output = Self;
fn mul(self, rhs: u64) -> Self {
Tick(self.0 * rhs)
}
}
impl MulAssign<u64> for Tick {
fn mul_assign(&mut self, rhs: u64) {
self.0 *= rhs;
}
}