use std::{fmt, rc::Rc}; 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; #[derive(Debug, Clone, PartialEq, Eq)] pub struct Track { pub ins: Vec, } impl Track { pub fn parse(src: &str) -> parse::Result { let ins = parse::parse(src)?; Ok(Self { ins }) } #[must_use] 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 { ins: res } } } impl fmt::Display for Track { 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(()) } } #[derive(Debug, Clone, PartialEq, Eq)] pub struct Program { track: Rc, priority: u32, looping: bool, pc: usize, pause_cnt: usize, pause_len: usize, channels: Channels, } impl Program { pub const fn new(track: Rc, priority: u32, looping: bool) -> Self { Self { track, priority, looping, pc: 0, pause_cnt: 0, pause_len: 4, channels: Channels::new(), } } const fn exec_ins(&mut self, 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; match chan_spec { C::PulseA => self.channels.pulse_a.volume = volume, C::PulseB => self.channels.pulse_b.volume = volume, C::Triangle => self.channels.triangle.volume = volume, C::Noise => self.channels.noise.volume = volume, } } I::SetPitch(chan_spec, pitch) => { // set the pitch (freq) on a given channel use ChanSpec as C; match chan_spec { C::PulseA => self.channels.pulse_a.pitch = pitch, C::PulseB => self.channels.pulse_b.pitch = pitch, C::Triangle => self.channels.triangle.pitch = pitch, C::Noise => self.channels.noise.pitch = pitch, } } I::SetPulseDutyA(dc) => { if let ChannelKind::Pulse { duty_cycle } = &mut self.channels.pulse_a.kind { *duty_cycle = dc; } } I::SetPulseDutyB(dc) => { if let ChannelKind::Pulse { duty_cycle } = &mut self.channels.pulse_b.kind { *duty_cycle = dc; } } I::SetNoiseMode(m) => { if let ChannelKind::Noise { mode, .. } = &mut self.channels.noise.kind { *mode = m; } } } } pub fn exec(&mut self) -> Channels { if self.pause_cnt > 0 { self.pause_cnt -= 1; } loop { if self.is_finished() { if self.looping { self.pc = 0; } else { break; } } if self.pause_cnt > 0 { break; } let ins = self.track.ins[self.pc]; self.exec_ins(ins); self.pc += 1; } self.channels } pub fn is_finished(&self) -> bool { self.pc >= self.track.ins.len() } } impl PartialOrd for Program { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } impl Ord for Program { fn cmp(&self, other: &Self) -> std::cmp::Ordering { self.priority.cmp(&other.priority) } }