use std::fmt; use crate::{ channel::{ChannelKind, Channels}, parse, }; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ChanSpec { PulseA, PulseB, Triangle, Noise, } impl fmt::Display for ChanSpec { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::PulseA => f.write_str("pulsea"), Self::PulseB => f.write_str("pulseb"), Self::Triangle => f.write_str("triangle"), Self::Noise => f.write_str("noise"), } } } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Instruction { Pause(u16), PauseLen(u16), Jump(u16), SetVolume(ChanSpec, u8), SetPitch(ChanSpec, u8), SetPulseDutyA(u8), SetPulseDutyB(u8), SetNoiseMode(bool), } impl fmt::Display for Instruction { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Pause(len) => write!(f, "pause {len}"), Self::PauseLen(len) => write!(f, "pause_len {len}"), Self::Jump(pc) => write!(f, "jump {pc}"), Self::SetVolume(chan, v) => write!(f, "{chan} volume {v}"), Self::SetPitch(chan, p) => write!(f, "{chan} pitch {p}"), Self::SetPulseDutyA(dc) => write!(f, "{} duty_cycle {dc}", ChanSpec::PulseA), Self::SetPulseDutyB(dc) => write!(f, "{} duty_cycle {dc}", ChanSpec::PulseB), Self::SetNoiseMode(mode) => write!(f, "{} mode {mode}", ChanSpec::Noise), } } } use Instruction as I; fn map_pitch(pitch: u8) -> f32 { const HALF_STEP: f32 = 1.059_463_1; HALF_STEP.powf(pitch as f32 - 128.0) } const fn map_volume(volume: u8) -> f32 { if volume > 100 { 1.0 } else { (volume as f32) / 100.0 } } #[derive(Debug, Clone)] pub struct Program { ins: Vec, pc: usize, pause_cnt: usize, pause_len: usize, looping: bool, playing: bool, } impl Program { pub const fn new(ins: Vec, looping: bool) -> Self { Self { ins, pc: 0, pause_cnt: 0, pause_len: 4, looping, playing: false, } } pub fn parse(src: &str, looping: bool) -> parse::Result { let ins = parse::parse(src)?; Ok(Self::new(ins, looping)) } pub fn merge(self, other: Self) -> Self { let mut res = vec![]; let mut l = 0; let mut r = 0; let mut l_pause = 0; let mut r_pause = 0; let l_ins = self.ins; let r_ins = other.ins; loop { if l >= l_ins.len() && r >= r_ins.len() { // were done here break; } while l < l_ins.len() { if l_pause > 0 { break; } let ins = l_ins[l]; l += 1; if let I::Pause(cnt) = ins { l_pause = cnt; break; } res.push(ins); } while r < r_ins.len() { if r_pause > 0 { break; } let ins = r_ins[r]; r += 1; if let I::Pause(cnt) = ins { r_pause = cnt; break; } res.push(ins); } if l_pause > 0 { if r_pause > 0 && l_pause >= r_pause { l_pause -= r_pause; res.push(I::Pause(r_pause)); r_pause = 0; } else if r_pause == 0 { res.push(I::Pause(l_pause)); l_pause = 0; } } if r_pause > 0 { if l_pause > 0 && r_pause >= l_pause { r_pause -= l_pause; res.push(I::Pause(l_pause)); l_pause = 0; } else if l_pause == 0 { res.push(I::Pause(r_pause)); r_pause = 0; } } } Self::new(res, self.looping) } fn exec_ins(&mut self, channels: &mut Channels, ins: Instruction) { use Instruction as I; match ins { I::Pause(cnt) => { self.pause_cnt = self.pause_len * (cnt as usize); } I::PauseLen(pause_len) => { self.pause_len = pause_len as usize; } I::Jump(pc) => { self.pc = pc as usize; } I::SetVolume(chan_spec, volume) => { // set the volume (amplitude) on a given channel use ChanSpec as C; let v = map_volume(volume); match chan_spec { C::PulseA => channels.pulse_a.volume = v, C::PulseB => channels.pulse_b.volume = v, C::Triangle => channels.triangle.volume = v, C::Noise => channels.noise.volume = v, } } I::SetPitch(chan_spec, pitch) => { // set the pitch (freq) on a given channel use ChanSpec as C; let p = map_pitch(pitch); match chan_spec { C::PulseA => channels.pulse_a.pitch = p, C::PulseB => channels.pulse_b.pitch = p, C::Triangle => channels.triangle.pitch = p, C::Noise => channels.noise.pitch = p, } } I::SetPulseDutyA(dc) => { if let ChannelKind::Pulse { duty_cycle } = &mut channels.pulse_a.kind { *duty_cycle = dc; } } I::SetPulseDutyB(dc) => { if let ChannelKind::Pulse { duty_cycle } = &mut channels.pulse_b.kind { *duty_cycle = dc; } } I::SetNoiseMode(m) => { if let ChannelKind::Noise { mode, .. } = &mut channels.noise.kind { *mode = m; } } } } pub fn exec(&mut self, channels: &mut Channels) { if !self.playing { return; } if self.pause_cnt > 0 { self.pause_cnt -= 1; } loop { if self.pause_cnt > 0 { break; } if self.is_finished() { if self.looping { self.pc = 0; } else { self.playing = false; break; } } let ins = self.ins[self.pc]; self.exec_ins(channels, ins); self.pc += 1; } } pub const fn is_finished(&self) -> bool { self.pc >= self.ins.len() } pub const fn is_playing(&self) -> bool { self.playing } pub const fn play(&mut self) { self.playing = true; self.pc = 0; } pub const fn set_looping(&mut self, looping: bool) { self.looping = looping; } } impl fmt::Display for Program { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { for (idx, ins) in self.ins.iter().enumerate() { if idx != 0 { writeln!(f)?; } write!(f, "{ins}")?; } Ok(()) } }