Compare commits
No commits in common. "main" and "entomologist-data" have entirely different histories.
main
...
entomologi
9 changed files with 4 additions and 251 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1 +0,0 @@
|
||||||
/target
|
|
||||||
7
Cargo.lock
generated
7
Cargo.lock
generated
|
|
@ -1,7 +0,0 @@
|
||||||
# This file is automatically @generated by Cargo.
|
|
||||||
# It is not intended for manual editing.
|
|
||||||
version = 4
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "adpcm-pwm-dac"
|
|
||||||
version = "0.1.0"
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "adpcm-pwm-dac"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2024"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
1
README.md
Normal file
1
README.md
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
This branch is used by entomologist to track issues.
|
||||||
3
e4c811653781e69e40b63fd27a8c1e20/description
Normal file
3
e4c811653781e69e40b63fd27a8c1e20/description
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
fix scaling on DpcmDecoder::output_next()
|
||||||
|
|
||||||
|
currently it scales to u8, but pwm outputs can use more! use usize!
|
||||||
8
notes.md
8
notes.md
|
|
@ -1,8 +0,0 @@
|
||||||
# ADPCM CODEC
|
|
||||||
ref: 1, 2
|
|
||||||
|
|
||||||
|
|
||||||
# RESOURCES
|
|
||||||
1. [wikipedia: adpcm](https://en.wikipedia.org/wiki/Adaptive_differential_pulse-code_modulation)
|
|
||||||
2. [st-micro: an3413](https://www.st.com/resource/en/application_note/an3143-audio-software-codec-for-the-stm8s-stmicroelectronics.pdf)
|
|
||||||
3. [st-micro: an4453](file:///home/lex/Downloads/an4453-implementing-the-adpcm-algorithm-in-stm32l1xx-microcontrollers-stmicroelectronics.pdf)
|
|
||||||
221
src/dac.rs
221
src/dac.rs
|
|
@ -1,221 +0,0 @@
|
||||||
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]>,
|
|
||||||
index: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T: DacInterface> Dac<'a, T> {
|
|
||||||
pub fn new(output: T) -> Self {
|
|
||||||
Self {
|
|
||||||
output,
|
|
||||||
data: None,
|
|
||||||
index: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
pub fn output_next(&mut self) {
|
|
||||||
if let Some(data) = self.data {
|
|
||||||
self.index = self.index + 1;
|
|
||||||
// reset the index to 0 if we roll over
|
|
||||||
if (self.index >= data.len()) {
|
|
||||||
self.index = 0;
|
|
||||||
}
|
|
||||||
self.output.write_amplitude(data[self.index]);
|
|
||||||
// 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: 0x25,
|
|
||||||
max: 0xc8,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn disable_output(&mut self) {
|
|
||||||
self.output.disable();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct DpcmDecoder<'a> {
|
|
||||||
data: Option<&'a [u8]>,
|
|
||||||
index: usize,
|
|
||||||
prev_amplitude: usize,
|
|
||||||
step_size: usize,
|
|
||||||
min: usize,
|
|
||||||
max: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> DpcmDecoder<'a> {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
data: None,
|
|
||||||
index: 0,
|
|
||||||
prev_amplitude: 0x0,
|
|
||||||
step_size: 0x3,
|
|
||||||
min: 0x50,
|
|
||||||
max: 0xaa,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_done(&self) -> bool {
|
|
||||||
if let Some(data) = self.data {
|
|
||||||
self.index >= (data.len() * 2) - 1
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// output the next sample's amplitude
|
|
||||||
pub fn output_next(&mut self) -> usize {
|
|
||||||
if let Some(data) = self.data {
|
|
||||||
if self.index >= (data.len() * 2) - 10 {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
// TODO: e4c811653781e69e40b63fd27a8c1e20
|
|
||||||
let normalized_amplitude =
|
|
||||||
(new_amplitude.saturating_sub(self.min)) * 100 / (self.max - self.min);
|
|
||||||
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 the normalized amplitude
|
|
||||||
return normalized_amplitude;
|
|
||||||
} else {
|
|
||||||
// otherwise just output 0
|
|
||||||
0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
pub trait DacInterface {
|
|
||||||
// write the amplitude (0->100%)
|
|
||||||
fn disable(&mut self);
|
|
||||||
fn write_amplitude(&mut self, amplitude: u8);
|
|
||||||
}
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
#![no_std]
|
|
||||||
pub mod interface;
|
|
||||||
pub mod dac;
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue