use crate::audio::pitch_table::{midi_inc, MidiNote};
pub const VOICE_BASE: usize = 0x3041;
pub const VOICE_SIZE: usize = 9;
pub const VOICE_COUNT: usize = 7;
pub const WAVETABLE_BASE: usize = 0x3600;
pub const WAVETABLE_SIZE: usize = 256;
pub const WAVETABLE_COUNT: usize = 6;
pub const WAVETABLE: [u16; WAVETABLE_COUNT] = [
0x0600, 0x0700, 0x0800, 0x0900, 0x0A00, 0x0B00,
];
const VOLUME_MAP: [(u16, u8); 17] = [
(0x0500, 4),
(0x0500, 3), (0x0400, 3), (0x0300, 3), (0x0200, 3),
(0x0500, 2), (0x0400, 2), (0x0300, 2), (0x0200, 2),
(0x0500, 1), (0x0400, 1), (0x0300, 1), (0x0200, 1),
(0x0500, 0), (0x0400, 0), (0x0300, 0), (0x0200, 0), ];
#[repr(C, packed)]
pub struct Voice {
phase: u16,
frequency: u16,
wavetable: u16,
volptr: u16,
shift: u8,
}
impl Voice {
#[inline]
pub fn set_note(&mut self, note: MidiNote) {
self.frequency = midi_inc(note);
}
#[inline]
pub fn set_frequency(&mut self, freq_inc: u16) {
self.frequency = freq_inc;
}
#[inline]
pub fn set_volume(&mut self, level: u8) {
let level = level.min(16);
let (volptr, shift) = VOLUME_MAP[level as usize];
self.volptr = volptr;
self.shift = shift;
}
#[inline]
pub fn set_wavetable(&mut self, wavetable_addr: u16) {
self.wavetable = wavetable_addr;
}
#[inline]
pub fn mute(&mut self) {
self.shift = 4; }
#[inline]
pub fn reset_phase(&mut self) {
self.phase = 0;
}
#[inline]
pub fn get_volume(&self) -> u8 {
VOLUME_MAP.iter()
.position(|(ptr, shift)| *ptr == self.volptr && *shift == self.shift)
.unwrap_or(0) as u8
}
}
#[inline]
pub fn voices() -> &'static mut [Voice; VOICE_COUNT] {
unsafe { &mut *(VOICE_BASE as *mut [Voice; VOICE_COUNT]) }
}
#[inline]
pub fn voice(index: usize) -> &'static mut Voice {
assert!(index < VOICE_COUNT, "voice index out of range");
unsafe { &mut *((VOICE_BASE + index * VOICE_SIZE) as *mut Voice) }
}
#[inline]
pub fn mute_all() {
let v = voices();
for voice in v.iter_mut() {
voice.mute();
}
}