reznez 0.0.0

The high accuracy NES Emulator
Documentation
use crate::apu::timer::Timer;
use crate::apu::length_counter::LengthCounter;
use crate::util::integer::U7;

const VOLUME_SEQUENCE: [u8; 0x20] = [
    15, 14, 13, 12, 11, 10,  9,  8,  7,  6,  5,  4,  3,  2,  1,  0,
     0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
];

#[derive(Default)]
pub struct TriangleChannel {
    pub(super) enabled: bool,

    counter_control: bool,
    linear_counter_reload_value: U7,
    linear_counter: U7,
    timer: Timer,
    pub(super) length_counter: LengthCounter,

    linear_counter_reload: bool,

    sequence_index: usize,
}

impl TriangleChannel {
    pub fn write_control_byte(&mut self, value: u8) {
        self.counter_control =     (value & 0b1000_0000) != 0;
        self.linear_counter = (value & 0b0111_1111).into();

        self.length_counter.set_halt(self.counter_control);
    }

    pub fn write_timer_low_byte(&mut self, value: u8) {
        self.timer.set_period_low(value);
    }

    pub fn write_length_and_timer_high_byte(&mut self, value: u8) {
        if self.enabled {
            self.length_counter.set_count_from_lookup((value & 0b1111_1000) >> 3);
        }

        self.timer.set_period_high_and_reset_index(value & 0b0000_0111);

        self.linear_counter_reload = true;
    }

    pub(super) fn set_enabled(&mut self, enabled: bool) {
        self.enabled = enabled;
        if !self.enabled {
            self.length_counter.set_to_zero();
        }
    }

    pub(super) fn active(&self) -> bool {
        !self.length_counter.is_zero()
    }

    pub(super) fn off_cycle_step(&mut self) {
        self.advance_timer_and_sequence_index();
    }

    pub(super) fn on_cycle_step(&mut self) {
        self.advance_timer_and_sequence_index();
    }

    pub(super) fn decrement_linear_counter(&mut self) {
        if self.linear_counter_reload {
            self.linear_counter = self.linear_counter_reload_value;
        } else {
            self.linear_counter.decrement_towards_zero();
        }

        if !self.counter_control {
            self.linear_counter_reload = false;
        }
    }

    pub(super) fn sample_volume(&self) -> f32 {
        if !self.length_counter.is_zero() && self.linear_counter != U7::ZERO {
            f32::from(VOLUME_SEQUENCE[self.sequence_index])
        } else {
            0.0
        }
    }

    fn advance_timer_and_sequence_index(&mut self) {
        let wrapped_around = self.timer.tick();
        if wrapped_around && !self.length_counter.is_zero() && self.linear_counter != U7::ZERO {
            self.sequence_index += 1;
            self.sequence_index %= 0x20;
        }
    }
}