Newer
Older
//! APU (Audio Processing Unit) functions and structures.
use crate::{
consts::{
NR10_ADDR, NR11_ADDR, NR12_ADDR, NR13_ADDR, NR14_ADDR, NR20_ADDR, NR21_ADDR, NR22_ADDR,
NR23_ADDR, NR24_ADDR, NR30_ADDR, NR31_ADDR, NR32_ADDR, NR33_ADDR, NR34_ADDR, NR40_ADDR,
NR41_ADDR, NR42_ADDR, NR43_ADDR, NR44_ADDR,
},
gb::GameBoy,
mmu::BusComponent,
warnln,
};
const DUTY_TABLE: [[u8; 8]; 4] = [
[0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 1, 1, 1],
[0, 1, 1, 1, 1, 1, 1, 0],
];
const CH4_DIVISORS: [u8; 8] = [8, 16, 32, 48, 64, 80, 96, 112];
pub enum Channel {
Ch1,
Ch2,
Ch3,
Ch4,
}
ch1_envelope_sequence: u8,
ch1_envelope_enabled: bool,
ch1_sweep_slope: u8,
ch1_sweep_increase: bool,
ch1_sweep_pace: u8,
ch1_length_timer: u8,
ch1_wave_duty: u8,
ch1_pace: u8,
ch1_direction: u8,
ch1_volume: u8,
ch1_wave_length: u16,
ch2_envelope_sequence: u8,
ch2_envelope_enabled: bool,
ch2_length_timer: u8,
ch2_wave_duty: u8,
ch2_pace: u8,
ch2_direction: u8,
ch2_volume: u8,
ch2_wave_length: u16,
ch3_position: u8,
ch3_output: u8,
ch3_output_level: u8,
ch3_wave_length: u16,
ch4_envelope_sequence: u8,
ch4_envelope_enabled: bool,
ch4_pace: u8,
ch4_direction: u8,
ch4_volume: u8,
ch4_divisor: u8,
ch4_width_mode: bool,
ch4_clock_shift: u8,
ch4_lfsr: u16,
right_enabled: bool,
left_enabled: bool,
ch1_out_enabled: bool,
ch2_out_enabled: bool,
ch3_out_enabled: bool,
ch4_out_enabled: bool,
/// The RAM that is used to sore the wave information
/// to be used in channel 3 audio
/// The rate at which audio samples are going to be
/// taken, ideally this value should be aligned with
/// the sampling rate of the output device. A typical
/// sampling rate would be of 44.1kHz.
sampling_rate: u16,
/// The number of audion channels that are going to be
channels: u8,
/// Internal sequencer counter that runs at 512Hz
/// used for the activation of the tick actions.
sequencer: u16,
sequencer_step: u8,
audio_buffer: VecDeque<u8>,
audio_buffer_max: usize,
pub fn new(sampling_rate: u16, channels: u8, buffer_size: f32, clock_freq: u32) -> Self {
ch1_timer: 0,
ch1_sequence: 0,
ch1_envelope_sequence: 0,
ch1_envelope_enabled: false,
ch1_sweep_increase: true,
ch1_sweep_pace: 0x0,
ch1_length_timer: 0x0,
ch1_wave_duty: 0x0,
ch1_pace: 0x0,
ch1_direction: 0x0,
ch1_volume: 0x0,
ch1_wave_length: 0x0,
ch1_length_enabled: false,
ch2_timer: 0,
ch2_sequence: 0,
ch2_envelope_sequence: 0,
ch2_envelope_enabled: false,
ch2_length_timer: 0x0,
ch2_wave_duty: 0x0,
ch2_pace: 0x0,
ch2_direction: 0x0,
ch2_volume: 0x0,
ch2_wave_length: 0x0,
ch2_length_enabled: false,
ch3_timer: 0,
ch3_position: 0,
ch3_output: 0,
ch3_dac: false,
ch3_length_timer: 0x0,
ch3_output_level: 0x0,
ch3_wave_length: 0x0,
ch3_length_enabled: false,
ch4_envelope_sequence: 0,
ch4_envelope_enabled: false,
ch4_pace: 0x0,
ch4_direction: 0x0,
ch4_volume: 0x0,
ch4_divisor: 0x0,
ch4_width_mode: false,
ch4_clock_shift: 0x0,
ch4_lfsr: 0x0,
ch4_length_enabled: false,
left_enabled: true,
right_enabled: true,
ch1_out_enabled: true,
ch2_out_enabled: true,
sequencer: 0,
sequencer_step: 0,
(sampling_rate as f32 * buffer_size) as usize * channels as usize,
audio_buffer_max: (sampling_rate as f32 * buffer_size) as usize * channels as usize,
pub fn reset(&mut self) {
self.ch1_timer = 0;
self.ch1_sequence = 0;
self.ch1_envelope_sequence = 0;
self.ch1_envelope_enabled = false;
self.ch1_sweep_sequence = 0;
self.ch1_output = 0;
self.ch1_sweep_increase = true;
self.ch1_sweep_pace = 0x0;
self.ch1_length_timer = 0x0;
self.ch1_wave_duty = 0x0;
self.ch1_pace = 0x0;
self.ch1_direction = 0x0;
self.ch1_volume = 0x0;
self.ch1_wave_length = 0x0;
self.ch1_length_enabled = false;
self.ch1_enabled = false;
self.ch2_timer = 0;
self.ch2_sequence = 0;
self.ch2_envelope_sequence = 0;
self.ch2_envelope_enabled = false;
self.ch2_output = 0;
self.ch2_length_timer = 0x0;
self.ch2_wave_duty = 0x0;
self.ch2_pace = 0x0;
self.ch2_direction = 0x0;
self.ch2_volume = 0x0;
self.ch2_wave_length = 0x0;
self.ch2_length_enabled = false;
self.ch2_enabled = false;
self.ch3_timer = 0;
self.ch3_position = 0;
self.ch3_output = 0;
self.ch3_dac = false;
self.ch3_length_timer = 0x0;
self.ch3_output_level = 0x0;
self.ch3_wave_length = 0x0;
self.ch3_length_enabled = false;
self.ch4_envelope_sequence = 0;
self.ch4_envelope_enabled = false;
self.ch4_pace = 0x0;
self.ch4_direction = 0x0;
self.ch4_volume = 0x0;
self.ch4_divisor = 0x0;
self.ch4_width_mode = false;
self.ch4_clock_shift = 0x0;
self.ch4_lfsr = 0x0;
self.ch4_length_enabled = false;
self.left_enabled = true;
self.right_enabled = true;
self.sequencer = 0;
self.sequencer_step = 0;
self.output_timer = 0;
self.clear_audio_buffer()
}
pub fn clock(&mut self, cycles: u16) {
if !self.sound_enabled {
return;
}
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
if self.sequencer >= 8192 {
// each of these steps runs at 512/8 Hz = 64Hz,
// meaning a complete loop runs at 512 Hz
match self.sequencer_step {
0 => {
self.tick_length_all();
}
1 => (),
2 => {
self.tick_ch1_sweep();
self.tick_length_all();
}
3 => (),
4 => {
self.tick_length_all();
}
5 => (),
6 => {
self.tick_ch1_sweep();
self.tick_length_all();
}
7 => {
self.tick_envelope_all();
}
_ => (),
}
self.sequencer -= 8192;
self.sequencer_step = (self.sequencer_step + 1) & 7;
}
self.tick_ch_all(cycles);
self.output_timer = self.output_timer.saturating_sub(cycles as i16);
if self.output_timer <= 0 {
// verifies if we've reached the maximum allowed size for the
// audio buffer and if that's the case an item is removed from
// the buffer (avoiding overflow) and then then the new audio
// volume item is added to the queue
if self.audio_buffer.len() >= self.audio_buffer_max {
for _ in 0..self.channels {
self.audio_buffer.pop_front();
}
}
if self.left_enabled {
self.audio_buffer.push_back(self.output());
}
if self.right_enabled && self.channels > 1 {
self.audio_buffer.push_back(self.output());
}
// calculates the rate at which a new audio sample should be
// created based on the (base/CPU) clock frequency and the
// sampling rate, this is basically the amount of APU clock
// calls that should be performed until an audio sample is created
self.output_timer += (self.clock_freq as f32 / self.sampling_rate as f32) as i16;
| (if self.ch1_sweep_increase { 0x00 } else { 0x08 })
// 0xFF11 — NR11: Channel 1 length timer & duty cycle
NR11_ADDR => ((self.ch1_wave_duty & 0x03) << 6) | 0x3f,
// 0xFF12 — NR12: Channel 1 volume & envelope
(self.ch1_pace & 0x07)
| ((self.ch1_direction & 0x01) << 3)
| ((self.ch1_volume & 0x0f) << 4)
}
// 0xFF13 — NR13: Channel 1 wavelength low
// 0xFF14 — NR14: Channel 1 wavelength high & control
NR14_ADDR => (if self.ch1_length_enabled { 0x40 } else { 0x00 }) | 0xbf,
// 0xFF15 — NR20: Not used
NR20_ADDR => 0xff,
// 0xFF16 — NR21: Channel 2 length timer & duty cycle
NR21_ADDR => ((self.ch2_wave_duty & 0x03) << 6) | 0x3f,
// 0xFF17 — NR22: Channel 2 volume & envelope
(self.ch2_pace & 0x07)
| ((self.ch2_direction & 0x01) << 3)
| ((self.ch2_volume & 0x0f) << 4)
}
// 0xFF18 — NR23: Channel 2 wavelength low
// 0xFF19 — NR24: Channel 2 wavelength high & control
NR24_ADDR => (if self.ch2_length_enabled { 0x40 } else { 0x00 }) | 0xbf,
NR30_ADDR => (if self.ch3_dac { 0x80 } else { 0x00 }) | 0x7f,
// 0xFF1B — NR31: Channel 3 length timer
// 0xFF1C — NR32: Channel 3 output level
NR32_ADDR => ((self.ch3_output_level & 0x03) << 5) | 0x9f,
// 0xFF1D — NR33: Channel 3 wavelength low
// 0xFF1E — NR34: Channel 3 wavelength high & control
NR34_ADDR => (if self.ch3_length_enabled { 0x40 } else { 0x00 }) | 0xbf,
// 0xFF1F — NR40: Not used
NR40_ADDR => 0xff,
// 0xFF20 — NR41: Channel 4 length timer
// 0xFF21 — NR42: Channel 4 volume & envelope
(self.ch4_pace & 0x07)
| ((self.ch4_direction & 0x01) << 3)
| ((self.ch4_volume & 0x0f) << 4)
}
// 0xFF22 — NR43: Channel 4 frequency & randomness
(self.ch4_divisor & 0x07)
| if self.ch4_width_mode { 0x08 } else { 0x00 }
| ((self.ch4_clock_shift & 0x0f) << 4)
}
// 0xFF23 — NR44: Channel 4 control
NR44_ADDR => (if self.ch4_length_enabled { 0x40 } else { 0x00 }) | 0xbf,
// 0xFF24 — NR50: Master volume & VIN panning
0xff24 => self.master,
// 0xFF25 — NR51: Sound panning
0xff25 => self.glob_panning,
((if self.ch1_enabled && self.ch1_dac {
0x01
} else {
0x00
} | if self.ch2_enabled && self.ch2_dac {
0x02
} else {
0x00
} | if self.ch3_enabled && self.ch3_dac {
0x04
} else {
0x00
} | if self.ch4_enabled && self.ch4_dac {
0x08
} else {
0x00
} | if self.sound_enabled { 0x80 } else { 0x00 })
| 0x70)
// 0xFF30-0xFF3F — Wave pattern RAM
0xff30..=0xff3f => self.wave_ram[addr as usize & 0x000f],
_ => {
warnln!("Reading from unknown APU location 0x{:04x}", addr);
}
pub fn write(&mut self, addr: u16, value: u8) {
// in case the sound is disabled then ignores writes
// to any register aside from the sound on/off
if !self.sound_enabled && addr != 0xff26 {
return;
}
self.ch1_sweep_slope = value & 0x07;
self.ch1_sweep_increase = value & 0x08 == 0x00;
self.ch1_sweep_pace = (value & 0x70) >> 4;
}
// 0xFF11 — NR11: Channel 1 length timer & duty cycle
self.ch1_length_timer = 64 - (value & 0x3f);
self.ch1_wave_duty = (value & 0xc0) >> 6;
}
// 0xFF12 — NR12: Channel 1 volume & envelope
self.ch1_pace = value & 0x07;
self.ch1_direction = (value & 0x08) >> 3;
self.ch1_volume = (value & 0xf0) >> 4;
self.ch1_envelope_enabled = self.ch1_pace > 0;
self.ch1_envelope_sequence = 0;
self.ch1_dac = value & 0xf8 != 0x00;
if !self.ch1_dac {
self.ch1_enabled = false;
}
}
// 0xFF13 — NR13: Channel 1 wavelength low
self.ch1_wave_length = (self.ch1_wave_length & 0xff00) | value as u16;
}
// 0xFF14 — NR14: Channel 1 wavelength high & control
let length_trigger = value & 0x40 == 0x40;
let trigger = value & 0x80 == 0x80;
let length_edge = length_trigger && !self.ch1_length_enabled;
self.ch1_wave_length =
(self.ch1_wave_length & 0x00ff) | (((value & 0x07) as u16) << 8);
self.ch1_length_enabled = value & 0x40 == 0x40;
if length_edge && self.sequencer_step % 2 == 1 {
self.tick_length(Channel::Ch1);
}
if length_trigger && self.ch1_length_timer == 0 {
// 0xFF15 — NR20: Not used
NR20_ADDR => (),
// 0xFF16 — NR21: Channel 2 length timer & duty cycle
self.ch2_length_timer = 64 - (value & 0x3f);
self.ch2_wave_duty = (value & 0xc0) >> 6;
}
// 0xFF17 — NR22: Channel 2 volume & envelope
self.ch2_pace = value & 0x07;
self.ch2_direction = (value & 0x08) >> 3;
self.ch2_volume = (value & 0xf0) >> 4;
self.ch2_envelope_enabled = self.ch2_pace > 0;
self.ch2_envelope_sequence = 0;
self.ch2_dac = value & 0xf8 != 0x00;
if !self.ch2_dac {
self.ch2_enabled = false;
}
}
// 0xFF18 — NR23: Channel 2 wavelength low
self.ch2_wave_length = (self.ch2_wave_length & 0xff00) | value as u16;
}
// 0xFF19 — NR24: Channel 2 wavelength high & control
let length_trigger = value & 0x40 == 0x40;
let trigger = value & 0x80 == 0x80;
let length_edge = length_trigger && !self.ch2_length_enabled;
self.ch2_wave_length =
(self.ch2_wave_length & 0x00ff) | (((value & 0x07) as u16) << 8);
self.ch2_length_enabled = length_trigger;
self.ch2_enabled |= trigger;
if length_edge && self.sequencer_step % 2 == 1 {
self.tick_length(Channel::Ch2);
}
if length_trigger && self.ch2_length_timer == 0 {
// 0xFF1A — NR30: Channel 3 DAC enable
self.ch3_dac = value & 0x80 == 0x80;
if !self.ch3_dac {
self.ch3_enabled = false;
}
}
// 0xFF1B — NR31: Channel 3 length timer
self.ch3_length_timer = 256 - (value as u16);
}
// 0xFF1C — NR32: Channel 3 output level
self.ch3_output_level = (value & 0x60) >> 5;
// 0xFF1D — NR33: Channel 3 wavelength low
self.ch3_wave_length = (self.ch3_wave_length & 0xff00) | value as u16;
}
// 0xFF1E — NR34: Channel 3 wavelength high & control
let length_trigger = value & 0x40 == 0x40;
let trigger = value & 0x80 == 0x80;
let length_edge = length_trigger && !self.ch3_length_enabled;
self.ch3_wave_length =
(self.ch3_wave_length & 0x00ff) | (((value & 0x07) as u16) << 8);
self.ch3_length_enabled = length_trigger;
self.ch3_enabled |= trigger;
if length_edge && self.sequencer_step % 2 == 1 {
self.tick_length(Channel::Ch3);
}
if length_trigger && self.ch3_length_timer == 0 {
// 0xFF20 — NR41: Channel 4 length timer
self.ch4_length_timer = 64 - (value & 0x3f);
}
// 0xFF21 — NR42: Channel 4 volume & envelope
self.ch4_pace = value & 0x07;
self.ch4_direction = (value & 0x08) >> 3;
self.ch4_volume = (value & 0xf0) >> 4;
self.ch4_envelope_enabled = self.ch4_pace > 0;
self.ch4_envelope_sequence = 0;
self.ch4_dac = value & 0xf8 != 0x00;
if !self.ch4_dac {
self.ch4_enabled = false;
}
}
// 0xFF22 — NR43: Channel 4 frequency & randomness
self.ch4_divisor = value & 0x07;
self.ch4_width_mode = value & 0x08 == 0x08;
self.ch4_clock_shift = (value & 0xf0) >> 4;
}
// 0xFF23 — NR44: Channel 4 control
let length_trigger = value & 0x40 == 0x40;
let trigger = value & 0x80 == 0x80;
let length_edge = length_trigger && !self.ch4_length_enabled;
self.ch4_length_enabled = length_trigger;
self.ch4_enabled |= trigger;
if length_edge && self.sequencer_step % 2 == 1 {
self.tick_length(Channel::Ch4);
}
if length_trigger && self.ch4_length_timer == 0 {
// 0xFF24 — NR50: Master volume & VIN panning
0xff24 => {
}
// 0xFF25 — NR51: Sound panning
0xff25 => {
}
// 0xFF26 — NR52: Sound on/off
0xff26 => {
self.sound_enabled = value & 0x80 == 0x80;
if !self.sound_enabled {
self.reset();
self.sound_enabled = false;
}
// 0xFF30-0xFF3F — Wave pattern RAM
0xff30..=0xff3f => self.wave_ram[addr as usize & 0x000f] = value,
_ => warnln!("Writing in unknown APU location 0x{:04x}", addr),
pub fn read_raw(&mut self, addr: u16) -> u8 {
match addr {
// 0xFF11 — NR11: Channel 1 length timer & duty cycle
NR11_ADDR => ((64 - self.ch1_length_timer) & 0x3f) | ((self.ch1_wave_duty & 0x03) << 6),
// 0xFF1B — NR31: Channel 3 length timer
NR31_ADDR => (255 - self.ch3_length_timer as u8).saturating_add(1),
// 0xFF1E — NR34: Channel 3 wavelength high & control
// @TODO: Add the extra VALUES
NR34_ADDR => (if self.ch3_length_enabled { 0x40 } else { 0x00 }) | 0xbf,
// 0xFF20 — NR41: Channel 4 length timer
NR41_ADDR => (64 - self.ch4_length_timer) & 0x3f,
pub fn write_raw(&mut self, addr: u16, value: u8) {
match addr {
// 0xFF26 — NR52: Sound on/off
0xff26 => {
self.ch1_enabled = value & 0x01 == 0x01;
self.ch2_enabled = value & 0x02 == 0x02;
self.ch3_enabled = value & 0x04 == 0x04;
self.ch4_enabled = value & 0x08 == 0x08;
self.sound_enabled = value & 0x80 == 0x80;
self.ch1_dac = self.ch1_enabled;
self.ch2_dac = self.ch2_enabled;
self.ch3_dac = self.ch3_enabled;
self.ch4_dac = self.ch4_enabled;
if !self.sound_enabled {
self.reset();
self.sound_enabled = false;
}
}
_ => self.write(addr, value),
}
pub fn output(&self) -> u8 {
self.ch1_output() + self.ch2_output() + self.ch3_output() + self.ch4_output()
if self.ch1_out_enabled {
self.ch1_output
} else {
0
}
if self.ch2_out_enabled {
self.ch2_output
} else {
0
}
if self.ch3_out_enabled {
self.ch3_output
} else {
0
}
pub fn ch4_output(&self) -> u8 {
if self.ch4_out_enabled {
self.ch4_output
} else {
0
}
pub fn set_ch1_out_enabled(&mut self, enabled: bool) {
pub fn set_ch2_out_enabled(&mut self, enabled: bool) {
pub fn set_ch3_out_enabled(&mut self, enabled: bool) {
pub fn set_ch4_out_enabled(&mut self, enabled: bool) {
pub fn sampling_rate(&self) -> u16 {
self.sampling_rate
}
pub fn channels(&self) -> u8 {
pub fn audio_buffer(&self) -> &VecDeque<u8> {
&self.audio_buffer
}
pub fn audio_buffer_mut(&mut self) -> &mut VecDeque<u8> {
&mut self.audio_buffer
}
pub fn clear_audio_buffer(&mut self) {
self.audio_buffer.clear();
}
pub fn audio_buffer_max(&self) -> usize {
self.audio_buffer_max
}
pub fn clock_freq(&self) -> u32 {
self.clock_freq
}
pub fn set_clock_freq(&mut self, value: u32) {
self.clock_freq = value;
}
fn tick_length_all(&mut self) {
self.tick_length(Channel::Ch1);
self.tick_length(Channel::Ch2);
self.tick_length(Channel::Ch3);
self.tick_length(Channel::Ch4);
fn tick_length(&mut self, channel: Channel) {
match channel {
Channel::Ch1 => {
if !self.ch1_length_enabled || self.ch1_length_timer == 0 {
self.ch1_length_timer = self.ch1_length_timer.saturating_sub(1);
if self.ch1_length_timer == 0 {
self.ch1_enabled = false;
}
}
Channel::Ch2 => {
if !self.ch2_length_enabled || self.ch2_length_timer == 0 {
self.ch2_length_timer = self.ch2_length_timer.saturating_sub(1);
if self.ch2_length_timer == 0 {
self.ch2_enabled = false;
if !self.ch3_length_enabled || self.ch3_length_timer == 0 {
self.ch3_length_timer = self.ch3_length_timer.saturating_sub(1);
if self.ch3_length_timer == 0 {
self.ch3_enabled = false;
if !self.ch4_length_enabled || self.ch4_length_timer == 0 {
self.ch4_length_timer = self.ch4_length_timer.saturating_sub(1);
if self.ch4_length_timer == 0 {
self.ch4_enabled = false;
fn tick_envelope_all(&mut self) {
self.tick_envelope(Channel::Ch1);
self.tick_envelope(Channel::Ch2);
self.tick_envelope(Channel::Ch4);
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
fn tick_envelope(&mut self, channel: Channel) {
match channel {
Channel::Ch1 => {
if !self.ch1_enabled || !self.ch1_envelope_enabled {
return;
}
self.ch1_envelope_sequence += 1;
if self.ch1_envelope_sequence >= self.ch1_pace {
if self.ch1_direction == 0x01 {
self.ch1_volume = self.ch1_volume.saturating_add(1);
} else {
self.ch1_volume = self.ch1_volume.saturating_sub(1);
}
if self.ch1_volume == 0 || self.ch1_volume == 15 {
self.ch1_envelope_enabled = false;
}
self.ch1_envelope_sequence = 0;
}
}
Channel::Ch2 => {
if !self.ch2_enabled || !self.ch2_envelope_enabled {
return;
}
self.ch2_envelope_sequence += 1;
if self.ch2_envelope_sequence >= self.ch2_pace {
if self.ch2_direction == 0x01 {
self.ch2_volume = self.ch2_volume.saturating_add(1);
} else {
self.ch2_volume = self.ch2_volume.saturating_sub(1);
}
if self.ch2_volume == 0 || self.ch2_volume == 15 {
self.ch2_envelope_enabled = false;
}
self.ch2_envelope_sequence = 0;
}
}
Channel::Ch3 => (),
Channel::Ch4 => {
if !self.ch4_enabled || !self.ch4_envelope_enabled {
return;
}
self.ch4_envelope_sequence += 1;
if self.ch4_envelope_sequence >= self.ch4_pace {
if self.ch4_direction == 0x01 {
self.ch4_volume = self.ch4_volume.saturating_add(1);
} else {
self.ch4_volume = self.ch4_volume.saturating_sub(1);
}
if self.ch4_volume == 0 || self.ch4_volume == 15 {
self.ch4_envelope_enabled = false;
}
self.ch4_envelope_sequence = 0;
}
}
fn tick_ch1_sweep(&mut self) {
if self.ch1_sweep_pace == 0x0 {
return;
}
self.ch1_sweep_sequence += 1;
if self.ch1_sweep_sequence >= self.ch1_sweep_pace {
let divisor = 1u16 << self.ch1_sweep_slope as u16;
let delta = (self.ch1_wave_length as f32 / divisor as f32) as u16;
if self.ch1_sweep_increase {
self.ch1_wave_length = self.ch1_wave_length.saturating_add(delta);
} else {
self.ch1_wave_length = self.ch1_wave_length.saturating_sub(delta);
}
if self.ch1_wave_length > 0x07ff {
self.ch1_enabled = false;
self.ch1_wave_length = 0x07ff;
}
self.ch1_sweep_sequence = 0;
}
fn tick_ch_all(&mut self, cycles: u16) {
self.tick_ch1(cycles);
self.tick_ch2(cycles);
self.tick_ch3(cycles);
fn tick_ch1(&mut self, cycles: u16) {
self.ch1_timer = self.ch1_timer.saturating_sub(cycles as i16);
if self.ch1_timer > 0 {
return;
}
if self.ch1_enabled {
self.ch1_output =
if DUTY_TABLE[self.ch1_wave_duty as usize][self.ch1_sequence as usize] == 1 {
self.ch1_volume
} else {
0
};
} else {
self.ch1_output = 0;
}
self.ch1_timer += ((2048 - self.ch1_wave_length) << 2) as i16;
self.ch1_sequence = (self.ch1_sequence + 1) & 7;
}
fn tick_ch2(&mut self, cycles: u16) {
self.ch2_timer = self.ch2_timer.saturating_sub(cycles as i16);
if self.ch2_timer > 0 {
return;
}
if self.ch2_enabled {
self.ch2_output =
if DUTY_TABLE[self.ch2_wave_duty as usize][self.ch2_sequence as usize] == 1 {
self.ch2_volume
} else {
0
};
} else {
self.ch2_output = 0;
}