implment DPCM
This commit is contained in:
parent
8502ed75bf
commit
e6114d2c52
1 changed files with 101 additions and 0 deletions
101
src/dac.rs
101
src/dac.rs
|
|
@ -1,4 +1,38 @@
|
||||||
use crate::interface::DacInterface;
|
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> {
|
pub struct Dac<'a, T: DacInterface> {
|
||||||
output: T,
|
output: T,
|
||||||
data: Option<&'a [u8]>,
|
data: Option<&'a [u8]>,
|
||||||
|
|
@ -31,3 +65,70 @@ impl<'a, T: DacInterface> Dac<'a, T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue