use alloc::vec::Vec;
pub const SCALE: u32 = 1 << 16;
const fn pulse_level(n: u64) -> u16 {
if n == 0 {
return 0;
}
let num = 9552 * n * SCALE as u64;
let den = 100 * (8128 + 100 * n);
(num / den) as u16
}
const fn tnd_level(n: u64) -> u16 {
if n == 0 {
return 0;
}
let num = 16367 * n * SCALE as u64;
let den = 100 * (24329 + 100 * n);
(num / den) as u16
}
const fn build_pulse_table() -> [u16; 31] {
let mut t = [0u16; 31];
let mut i = 0;
while i < 31 {
t[i] = pulse_level(i as u64);
i += 1;
}
t
}
const fn build_tnd_table() -> [u16; 203] {
let mut t = [0u16; 203];
let mut i = 0;
while i < 203 {
t[i] = tnd_level(i as u64);
i += 1;
}
t
}
pub static PULSE_TABLE: [u16; 31] = build_pulse_table();
pub static TND_TABLE: [u16; 203] = build_tnd_table();
pub fn mix(pulse1: u8, pulse2: u8, triangle: u8, noise: u8, dmc: u8) -> u16 {
let p = usize::from(pulse1.min(15) + pulse2.min(15));
let t = usize::from(triangle.min(15)) * 3 + usize::from(noise.min(15)) * 2 + usize::from(dmc);
let t = t.min(TND_TABLE.len() - 1);
let sum = u32::from(PULSE_TABLE[p]) + u32::from(TND_TABLE[t]);
debug_assert!(sum <= u32::from(u16::MAX), "mixer output overflowed Q16");
sum.min(u32::from(u16::MAX)) as u16
}
#[derive(Debug, Default)]
pub struct SampleRing {
buf: Vec<u16>,
head: usize,
len: usize,
dropped: u64,
}
impl SampleRing {
pub fn with_capacity(capacity: usize) -> SampleRing {
SampleRing {
buf: alloc::vec![0u16; capacity],
head: 0,
len: 0,
dropped: 0,
}
}
pub fn capacity(&self) -> usize {
self.buf.len()
}
pub fn len(&self) -> usize {
self.len
}
pub fn is_empty(&self) -> bool {
self.len == 0
}
pub fn dropped(&self) -> u64 {
self.dropped
}
pub fn push(&mut self, sample: u16) {
let cap = self.buf.len();
if cap == 0 {
return;
}
if self.len == cap {
self.buf[self.head] = sample;
self.head = (self.head + 1) % cap;
self.dropped += 1;
} else {
let index = (self.head + self.len) % cap;
self.buf[index] = sample;
self.len += 1;
}
}
pub fn drain_into(&mut self, out: &mut Vec<u16>) {
let cap = self.buf.len();
if cap == 0 {
return;
}
out.reserve(self.len);
for i in 0..self.len {
out.push(self.buf[(self.head + i) % cap]);
}
self.head = 0;
self.len = 0;
}
pub fn clear(&mut self) {
self.head = 0;
self.len = 0;
self.dropped = 0;
}
}