diff --git a/src/dac.rs b/src/dac.rs index 32ead8f..f1efe6d 100644 --- a/src/dac.rs +++ b/src/dac.rs @@ -1,4 +1,38 @@ use crate::interface::DacInterface; + +pub enum Direction { + Up, + Down, +} + + +pub struct DpcmSample { + direction: Direction, + // only 3 bits + step_count: u8, +} + +impl DpcmSample { + pub fn new(byte: u8, sub_index: usize) -> Self { + let data = if sub_index == 0 { + byte & 0x0F + } else { + byte >> 4 + }; + let direction = match data & 0x1 { + 0x1 => Direction::Up, + 0x0 => Direction::Down, + _ => Direction::Up, + }; + let step_count = data >> 1; + + Self { + direction, + step_count, + } + } +} + pub struct Dac<'a, T: DacInterface> { output: T, data: Option<&'a [u8]>, @@ -30,4 +64,71 @@ impl<'a, T: DacInterface> Dac<'a, T> { // self.output.write_amplitude(100); } } +} + +pub struct DpcmDac<'a, T: DacInterface> { + output: T, + data: Option<&'a [u8]>, + index: usize, + prev_amplitude: usize, + step_size: usize, + min: usize, + max: usize, +} + +impl<'a, T: DacInterface> DpcmDac<'a, T> { + pub fn new(output: T) -> Self { + Self { + output, + data: None, + index: 0, + prev_amplitude: 0x80, + step_size: 0x3, + min: 0x19, + max: 0xe6, + } + } + pub fn load_data(&mut self, data: &'a [u8]) { + self.data = Some(data); + } + pub fn seek_to_sample(&mut self, index: usize) { + self.index = index; + } + + // output the next sampe. returns true if there are samples remaining, false if there are no samples remaining. + pub fn output_next(&mut self) -> bool { + if let Some(data) = self.data { + let sub_index = 1 - self.index % 2; + + let sample = DpcmSample::new(data[self.index / 2], sub_index); + let new_amplitude = match sample.direction { + Direction::Down => self.prev_amplitude.saturating_sub(sample.step_count as usize * self.step_size).max(self.min), + Direction ::Up => (self.prev_amplitude + (sample.step_count as usize * self.step_size)).min(self.max), + }; + + // calculate normalized amplitude and write out + let normalized_amplitude = (new_amplitude - self.min) * 100 / (self.max - self.min); + self.output.write_amplitude(normalized_amplitude as u8); + self.prev_amplitude = new_amplitude; + + + // increment the sample index + let mut samples_remaining = true; + self.index = self.index + 1; + + // reset the index to 0 if we roll over + if (self.index >= data.len() * 2) { + self.index = 0; + self.prev_amplitude = 0x74; + samples_remaining = false; + } + + // return whether or not we have samples remaining + samples_remaining + } + else { + // if the sample failed to load, we (duh) have no more samples remaining + false + } + } } \ No newline at end of file