use crate::core::error::Result;
use crate::core::state::{Sink, Source};
pub const LENGTH_TABLE: [u8; 32] = [
10, 254, 20, 2, 40, 4, 80, 6, 160, 8, 60, 10, 14, 12, 26, 14, 12, 16, 24, 18, 48, 20, 96, 22, 192, 24, 72, 26, 16, 28, 32, 30,
];
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct Envelope {
start: bool,
divider: u8,
decay: u8,
period: u8,
loop_flag: bool,
constant: bool,
}
impl Envelope {
pub const fn new() -> Envelope {
Envelope {
start: false,
divider: 0,
decay: 0,
period: 0,
loop_flag: false,
constant: false,
}
}
pub fn write_control(&mut self, value: u8) {
self.loop_flag = value & 0x20 != 0;
self.constant = value & 0x10 != 0;
self.period = value & 0x0F;
}
#[inline]
pub const fn loop_flag(&self) -> bool {
self.loop_flag
}
pub fn restart(&mut self) {
self.start = true;
}
pub fn clock(&mut self) {
if self.start {
self.start = false;
self.decay = 15;
self.divider = self.period;
} else if self.divider == 0 {
self.divider = self.period;
if self.decay > 0 {
self.decay -= 1;
} else if self.loop_flag {
self.decay = 15;
}
} else {
self.divider -= 1;
}
}
#[inline]
pub const fn volume(&self) -> u8 {
if self.constant {
self.period
} else {
self.decay
}
}
pub fn save(&self, w: &mut dyn Sink) -> Result<()> {
w.write_bool(self.start)?;
w.write_u8(self.divider)?;
w.write_u8(self.decay)?;
w.write_u8(self.period)?;
w.write_bool(self.loop_flag)?;
w.write_bool(self.constant)
}
pub fn load<'a>(&mut self, r: &mut dyn Source<'a>) -> Result<()> {
self.start = r.read_bool()?;
self.divider = r.read_u8()?;
self.decay = r.read_u8()?;
self.period = r.read_u8()?;
self.loop_flag = r.read_bool()?;
self.constant = r.read_bool()?;
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct LengthCounter {
value: u8,
halt: bool,
enabled: bool,
}
impl LengthCounter {
pub const fn new() -> LengthCounter {
LengthCounter {
value: 0,
halt: false,
enabled: false,
}
}
#[inline]
pub const fn value(&self) -> u8 {
self.value
}
#[inline]
pub const fn active(&self) -> bool {
self.value > 0
}
#[inline]
pub const fn halted(&self) -> bool {
self.halt
}
pub fn set_halt(&mut self, halt: bool) {
self.halt = halt;
}
pub fn set_enabled(&mut self, enabled: bool) {
self.enabled = enabled;
if !enabled {
self.value = 0;
}
}
#[inline]
pub const fn enabled(&self) -> bool {
self.enabled
}
pub fn load(&mut self, value: u8) {
if self.enabled {
self.value = LENGTH_TABLE[usize::from(value >> 3)];
}
}
pub fn clock(&mut self) {
if !self.halt && self.value > 0 {
self.value -= 1;
}
}
pub fn save(&self, w: &mut dyn Sink) -> Result<()> {
w.write_u8(self.value)?;
w.write_bool(self.halt)?;
w.write_bool(self.enabled)
}
pub fn load_state<'a>(&mut self, r: &mut dyn Source<'a>) -> Result<()> {
self.value = r.read_u8()?;
self.halt = r.read_bool()?;
self.enabled = r.read_bool()?;
Ok(())
}
}