use std::io::Write;
use byteorder::{LittleEndian, WriteBytesExt};
use wasm4_sx::Tone;
#[derive(Debug, Clone, Copy)]
pub enum NotePitchKey {
C,
Cs,
Db,
D,
Ds,
Eb,
E,
F,
Fs,
Gb,
G,
Gs,
Ab,
A,
As,
Bb,
B,
}
impl NotePitchKey {
fn semitones_above_c(&self) -> u8 {
match self {
Self::C => 0,
Self::Cs | Self::Db => 1,
Self::D => 2,
Self::Ds | Self::Eb => 3,
Self::E => 4,
Self::F => 5,
Self::Fs | Self::Gb => 6,
Self::G => 7,
Self::Gs | Self::Ab => 8,
Self::A => 9,
Self::As | Self::Bb => 10,
Self::B => 11,
}
}
}
#[derive(Debug)]
pub struct NotePitch {
pub key: NotePitchKey,
pub octave: i8,
}
impl NotePitch {
pub fn new(key: NotePitchKey, octave: i8) -> Self {
Self { key, octave }
}
fn semitones_above_c4(&self) -> i16 {
let octave_length = 12;
(self.octave as i16 - 4) * octave_length + self.key.semitones_above_c() as i16
}
pub fn as_frequency(&self) -> u16 {
let semitones = self.semitones_above_c4();
let frequency = 440.0 * 2.0_f32.powf((semitones as f32 - 9.0) / 12.0);
frequency as u16
}
}
#[derive(Debug)]
pub struct Track {
pub notes: Vec<Note>,
}
impl Track {
pub fn write<W: Write>(&self, writer: &mut W) -> std::io::Result<()> {
for note in &self.notes {
note.write(writer)?;
}
if let Some(last_frame) = self.get_last_frame() {
writer.write_u16::<LittleEndian>(last_frame)?;
}
Ok(())
}
pub fn print(&self) {
for note in &self.notes {
println!("[{}] Voices: {}", note.frame, note.voices.len());
}
}
fn get_last_frame(&self) -> Option<u16> {
self.notes.iter().max_by_key(|x| x.frame).map(|x| x.frame)
}
}
#[derive(Debug, Clone)]
pub struct Note {
pub frame: u16,
pub voices: Vec<Tone>,
}
impl Note {
pub fn new(frame: u16, voices: Vec<Tone>) -> Self {
Self { frame, voices }
}
pub fn write<W: Write>(&self, writer: &mut W) -> std::io::Result<()> {
writer.write_u16::<LittleEndian>(self.frame)?;
writer.write_u16::<LittleEndian>(self.voices.len() as u16)?;
for tone in &self.voices {
let (freq, dur, vol, flags) = tone.to_binary();
writer.write_u32::<LittleEndian>(freq)?;
writer.write_u32::<LittleEndian>(dur)?;
writer.write_u16::<LittleEndian>(vol)?;
writer.write_u16::<LittleEndian>(flags)?;
}
Ok(())
}
}