#![warn(missing_docs)]
mod synth;
use std::time::Duration;
use synth::Samples;
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialOrd, PartialEq)]
pub struct Pitch(pub i32);
impl Pitch {
pub fn new(name: Name, octave: u8) -> Self {
Self((i32::from(octave) - 4) * 12 + name as i32 - 9)
}
pub fn new_from_freq(freq: f64) -> Self {
assert!(freq > 0.0, "Frequency must be greater than 0");
Self((12.0 * (freq / 440.0).log2()).round() as i32)
}
pub fn freq(self) -> f64 {
let b = 2f64.powf(12f64.recip());
440.0 * b.powi(self.0)
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct Chord {
pitches: Vec<Pitch>,
length: Length,
volume: f64,
}
impl Chord {
pub fn new(pitches: Vec<Pitch>, length: Length, volume: f64) -> Self {
Self {
pitches,
length,
volume,
}
}
pub fn new_major(root: Pitch, length: Length, volume: f64) -> Self {
Self::new(
vec![root, Pitch(root.0 + 4), Pitch(root.0 + 7)],
length,
volume,
)
}
pub fn pitches(&self) -> &[Pitch] {
&self.pitches
}
pub fn pitches_mut(&mut self) -> &mut Vec<Pitch> {
&mut self.pitches
}
pub fn samples(&self, bpm: f64, rate: u32) -> Samples<'_> {
let rate = f64::from(rate);
Samples::new(
0,
(self.length.duration(bpm).as_secs_f64() * rate).round() as u32,
&self.pitches,
rate,
self.volume * 8_192.0,
)
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialOrd, PartialEq)]
#[repr(i32)]
pub enum Length {
Sixteenth = 2,
Eigth = 1,
Quarter = 0,
Half = -1,
Whole = -2,
}
impl Length {
pub fn duration(self, bpm: f64) -> Duration {
Duration::from_secs_f64(60.0 / (bpm * 2f64.powi(self as i32)))
}
}
#[allow(missing_docs)]
#[derive(Clone, Copy)]
#[repr(i32)]
pub enum Name {
A = 9,
AS = 10,
B = 11,
C = 0,
CS = 1,
D = 2,
DS = 3,
E = 4,
F = 5,
FS = 6,
G = 7,
GS = 8,
}