enum EnvelopeShape {
SlideDown,
SlideUp,
HoldTop,
HoldBottom
}
const ENVELOPE_TABLE: [[EnvelopeShape; 2]; 16] = [
[EnvelopeShape::SlideDown, EnvelopeShape::HoldBottom],
[EnvelopeShape::SlideDown, EnvelopeShape::HoldBottom],
[EnvelopeShape::SlideDown, EnvelopeShape::HoldBottom],
[EnvelopeShape::SlideDown, EnvelopeShape::HoldBottom],
[EnvelopeShape::SlideUp, EnvelopeShape::HoldBottom],
[EnvelopeShape::SlideUp, EnvelopeShape::HoldBottom],
[EnvelopeShape::SlideUp, EnvelopeShape::HoldBottom],
[EnvelopeShape::SlideUp, EnvelopeShape::HoldBottom],
[EnvelopeShape::SlideDown, EnvelopeShape::SlideDown],
[EnvelopeShape::SlideDown, EnvelopeShape::HoldBottom],
[EnvelopeShape::SlideDown, EnvelopeShape::SlideUp],
[EnvelopeShape::SlideDown, EnvelopeShape::HoldTop],
[EnvelopeShape::SlideUp, EnvelopeShape::SlideUp],
[EnvelopeShape::SlideUp, EnvelopeShape::HoldTop],
[EnvelopeShape::SlideUp, EnvelopeShape::SlideDown],
[EnvelopeShape::SlideUp, EnvelopeShape::HoldBottom]
];
pub struct EnvelopeGenerator {
position: u16,
period: u16,
shape: u8,
segment: u8,
value: u8
}
impl EnvelopeGenerator {
pub(crate) fn new() -> Self {
Self {
position: 0,
period: 1,
shape: 0,
segment: 0,
value: 0
}
}
pub(crate) fn render(&mut self) -> u8 {
self.position += 1;
if self.position >= self.period {
self.position = 0;
match ENVELOPE_TABLE[self.shape as usize][self.segment as usize] {
EnvelopeShape::SlideDown => {
if self.value == 0 {
self.segment ^= 1;
self.reset_segment();
} else {
self.value -= 1;
}
}
EnvelopeShape::SlideUp => {
if self.value >= 31 {
self.segment ^= 1;
self.reset_segment();
} else {
self.value += 1;
}
}
_ => ()
}
}
self.value
}
fn reset_segment(&mut self) {
self.value = match ENVELOPE_TABLE[self.shape as usize][self.segment as usize] {
EnvelopeShape::SlideDown | EnvelopeShape::HoldTop => 31,
_ => 0
};
}
pub fn period(&self) -> u16 {
self.period
}
pub fn set_period(&mut self, period: u16) {
self.period = period.max(1);
}
pub fn period_msb(&self) -> u8 {
(self.period >> 8) as u8
}
pub fn set_period_msb(&mut self, period: u8) {
self.period = ((self.period & 0x00ff) | (((period as u16) & 0xff) << 8)).max(1);
}
pub fn period_lsb(&self) -> u8 {
(self.period & 0xff) as u8
}
pub fn set_period_lsb(&mut self, period: u8) {
self.period = ((self.period & 0xff00) | (period as u16)).max(1);
}
pub fn shape(&self) -> u8 {
self.shape
}
pub fn set_shape(&mut self, shape: u8) {
self.shape = shape & 0x0f;
self.position = 0;
self.segment = 0;
self.reset_segment();
}
}