diff --git a/src/main.rs b/src/main.rs index 9ce4fa5..12fe10a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,4 @@ -#![feature(exclusive_range_pattern, let_chains)] +#![feature(exclusive_range_pattern, let_chains, slice_flatten)] mod processor; mod util; diff --git a/src/processor/memory.rs b/src/processor/memory.rs index 542a2c0..487d685 100644 --- a/src/processor/memory.rs +++ b/src/processor/memory.rs @@ -31,6 +31,8 @@ pub struct Memory { impl Memory { pub fn init(bootrom: Vec, bootrom_enabled: bool, rom: Rom) -> Self { + let mut apu = Apu::default(); + apu.init(); Self { bootrom, bootrom_enabled, @@ -46,7 +48,7 @@ impl Memory { io: [0xFF; 76], user_mode: false, joypad: Joypad::default(), - apu: Apu::default(), + apu, } } diff --git a/src/processor/memory/mmio/apu.rs b/src/processor/memory/mmio/apu.rs index 64bbbf9..b1dd501 100644 --- a/src/processor/memory/mmio/apu.rs +++ b/src/processor/memory/mmio/apu.rs @@ -1,4 +1,4 @@ -use std::sync::RwLock; +use std::sync::mpsc::{channel, Sender}; use crate::{ processor::{ @@ -10,7 +10,7 @@ use crate::{ }; use cpal::{ traits::{DeviceTrait, HostTrait, StreamTrait}, - Stream, + Device, Stream, StreamConfig, }; use samplerate::{ConverterType, Samplerate}; @@ -35,10 +35,10 @@ struct Channels { impl Default for Channels { fn default() -> Self { Self { - one: PwmChannel::new(true, true, true), - two: PwmChannel::new(false, true, true), - three: WaveChannel::new(false, true, false), - four: NoiseChannel::new(false, true, false), + one: PwmChannel::new(true), + two: PwmChannel::new(false), + three: WaveChannel::new(false), + four: NoiseChannel::new(false), } } } @@ -49,20 +49,120 @@ struct VinEnable { right: bool, } +enum Volume { + Muted, + Enabled, +} + +impl Volume { + fn scale(&self) -> f32 { + match self { + Volume::Muted => 0., + Volume::Enabled => 1., + } + } + + fn bool(&self) -> bool { + match self { + Volume::Muted => false, + Volume::Enabled => true, + } + } + + fn from_bool(val: bool) -> Self { + if val { + Self::Enabled + } else { + Self::Muted + } + } +} + +struct ChannelVol { + left: Volume, + right: Volume, +} + +impl Default for ChannelVol { + fn default() -> Self { + Self { + left: Volume::Muted, + right: Volume::Muted, + } + } +} + +struct Mixer { + vol_left: u8, + vol_right: u8, + ch1: ChannelVol, + ch2: ChannelVol, + ch3: ChannelVol, + ch4: ChannelVol, +} + +impl Default for Mixer { + fn default() -> Self { + Self { + vol_left: 7, + vol_right: 7, + ch1: Default::default(), + ch2: Default::default(), + ch3: Default::default(), + ch4: Default::default(), + } + } +} + +// impl Mixer { +// fn mix(&self, sample: DacSample) -> [f32; 2] { +// sample.mixed(self.vol_left, self.vol_right) +// } +// } + +#[derive(Clone, Copy)] +struct DacSample { + one: f32, + two: f32, +} + +impl Default for DacSample { + fn default() -> Self { + Self { one: 0., two: 0. } + } +} + +impl DacSample { + fn mixed(&self, mixer: &Mixer) -> [f32; 2] { + let left = (self.one * mixer.ch1.left.scale()) + (self.two * mixer.ch2.left.scale()); + let right = (self.one * mixer.ch1.right.scale()) + (self.two * mixer.ch2.right.scale()); + [ + self.mix_channel(left, mixer.vol_left), + self.mix_channel(right, mixer.vol_right), + ] + } + + fn mix_channel(&self, sums: f32, volume: u8) -> f32 { + sums * ((volume + 1) as f32 / (8. * 4.)) + } +} + pub struct Apu { mem: [u8; MEM_SIZE], apu_enable: bool, channels: Channels, vin: VinEnable, - vol_left: u8, - vol_right: u8, + mixer: Mixer, div_apu: u8, - buffer: Vec, - converter: Samplerate, - _stream: Stream, + buffer: Vec, + // converter: Samplerate, + device: Device, + config: StreamConfig, + stream: Option, + send_buffer: Option>>, } -static PLAY_BUFFER: RwLock> = RwLock::new(vec![]); +// const TARGET_SAMPLERATE: u32 = 192000; impl Default for Apu { fn default() -> Self { @@ -81,56 +181,97 @@ impl Default for Apu { .with_max_sample_rate() .config(); - let stream = device - .build_output_stream( - &config, - move |data: &mut [f32], _info: &cpal::OutputCallbackInfo| { - // react to stream events and read or write stream data here. - if let Ok(mut audio) = PLAY_BUFFER.try_write() { - if audio.len() >= data.len() { - let b = audio.split_off(data.len()); - data.copy_from_slice(&audio); - *audio = b; - } - } - }, - move |err| { - // react to errors here. - println!("audio error: {err}"); - }, - None, // None=blocking, Some(Duration)=timeout - ) - .unwrap(); - stream.play().unwrap(); + // config.buffer_size = BufferSize::Fixed(2048 * 2); - let converter = Samplerate::new( - ConverterType::Linear, - CLOCK_SPEED as u32, - config.sample_rate.0, - 1, - ) - .unwrap(); + // let converter = Samplerate::new( + // ConverterType::Linear, + // TARGET_SAMPLERATE, + // config.sample_rate.0, + // 2, + // ) + // .unwrap(); Self { mem: [0x0; MEM_SIZE], apu_enable: true, channels: Channels::default(), vin: VinEnable::default(), - vol_left: 7, - vol_right: 7, + mixer: Mixer::default(), div_apu: 0, buffer: vec![], - converter, - _stream: stream, + // converter, + device, + config, + stream: None, + send_buffer: None, } } } +const SCALE: u32 = 4; + impl Apu { + pub fn init(&mut self) { + let sample_rate = self.config.sample_rate.0; + let ratio = (CLOCK_SPEED as f32) / ((sample_rate as f32) * 4.); + + let (tx, rx) = channel::>(); + let other = tx.clone(); + self.send_buffer = Some(tx); + + let stream = self + .device + .build_output_stream( + &self.config, + move |data: &mut [f32], _info: &cpal::OutputCallbackInfo| { + let buf_length = data.len() / 2; + let gbspeed_buf_length = (ratio * (buf_length as f32)) as usize; + + let mut audio = vec![]; + while let Ok(mut e) = rx.recv() { + audio.append(&mut e); + if audio.len() >= gbspeed_buf_length { + break; + } + } + + other.send(audio.split_off(gbspeed_buf_length)).unwrap(); + + let target = sample_rate * SCALE; + let converter = + Samplerate::new(ConverterType::Linear, target, sample_rate, 2).unwrap(); + + let naive_buf_length = buf_length * (SCALE as usize); + + let mut downsampled = vec![[0.; 2]; naive_buf_length]; + let target_step = audio.len() as f64 / naive_buf_length as f64; + let mut index = 0.; + for val in &mut downsampled { + (*val).clone_from(&audio[index as usize]); + index += target_step; + } + data.copy_from_slice( + &converter + .process(&downsampled.into_iter().flatten().collect::>()) + .unwrap(), + ); + }, + move |err| { + // react to errors here. + println!("audio error: {err}"); + }, + None, + ) + .unwrap(); + stream.play().unwrap(); + self.stream = Some(stream); + } + pub fn div_apu_tick(&mut self) { self.div_apu = self.div_apu.wrapping_add(1); if self.div_apu % 8 == 0 { // envelope sweep + self.channels.one.envelope_tick(); } if self.div_apu % 4 == 0 { // ch1 frequency sweep @@ -148,15 +289,20 @@ impl Apu { .tick(steps) .into_iter() .zip(self.channels.two.tick(steps).into_iter()) - .map(|(one, two)| (one + two) / 2.) + .map(|(one, two)| DacSample { one, two }) .collect(), ); } fn next_audio(&mut self) { - if let Ok(mut audio) = PLAY_BUFFER.try_write() { - audio.append(&mut self.converter.process(&self.buffer).unwrap()); - self.buffer.clear(); + if let Some(send) = &self.send_buffer { + send.send( + self.buffer + .drain(..) + .map(|v| v.mixed(&self.mixer)) + .collect(), + ) + .unwrap(); } } @@ -185,7 +331,8 @@ impl Apu { 0xFF19 => self.channels.two.get_control(), 0xFF24 => { // NR50 - Master volume + VIN panning - let mut v = ((self.vol_left << 4) & 0b1110000) | (self.vol_right & 0b111); + let mut v = + ((self.mixer.vol_left << 4) & 0b1110000) | (self.mixer.vol_right & 0b111); v = set_or_clear_bit(v, 7, self.vin.left); v = set_or_clear_bit(v, 3, self.vin.right); v @@ -193,14 +340,14 @@ impl Apu { 0xFF25 => { // NR51 - Panning let mut v = 0; - v = set_or_clear_bit(v, 0, self.channels.one.pan_right); - v = set_or_clear_bit(v, 1, self.channels.two.pan_right); - v = set_or_clear_bit(v, 2, self.channels.three.pan_right); - v = set_or_clear_bit(v, 3, self.channels.four.pan_right); - v = set_or_clear_bit(v, 4, self.channels.one.pan_left); - v = set_or_clear_bit(v, 5, self.channels.two.pan_left); - v = set_or_clear_bit(v, 6, self.channels.three.pan_left); - v = set_or_clear_bit(v, 7, self.channels.four.pan_left); + v = set_or_clear_bit(v, 0, self.mixer.ch1.right.bool()); + v = set_or_clear_bit(v, 1, self.mixer.ch2.right.bool()); + v = set_or_clear_bit(v, 2, self.mixer.ch3.right.bool()); + v = set_or_clear_bit(v, 3, self.mixer.ch4.right.bool()); + v = set_or_clear_bit(v, 4, self.mixer.ch1.left.bool()); + v = set_or_clear_bit(v, 5, self.mixer.ch2.left.bool()); + v = set_or_clear_bit(v, 6, self.mixer.ch3.left.bool()); + v = set_or_clear_bit(v, 7, self.mixer.ch4.left.bool()); v } 0xFF26 => { @@ -250,18 +397,18 @@ impl Apu { 0xFF24 => { self.vin.left = get_bit(data, 7); self.vin.right = get_bit(data, 3); - self.vol_left = (data & 0b1110000) >> 4; - self.vol_right = data & 0b111; + self.mixer.vol_left = (data & 0b1110000) >> 4; + self.mixer.vol_right = data & 0b111; } 0xFF25 => { - self.channels.one.pan_right = get_bit(data, 0); - self.channels.two.pan_right = get_bit(data, 1); - self.channels.three.pan_right = get_bit(data, 2); - self.channels.four.pan_right = get_bit(data, 3); - self.channels.one.pan_left = get_bit(data, 4); - self.channels.two.pan_left = get_bit(data, 5); - self.channels.three.pan_left = get_bit(data, 6); - self.channels.four.pan_left = get_bit(data, 7); + self.mixer.ch1.right = Volume::from_bool(get_bit(data, 0)); + self.mixer.ch2.right = Volume::from_bool(get_bit(data, 1)); + self.mixer.ch3.right = Volume::from_bool(get_bit(data, 2)); + self.mixer.ch4.right = Volume::from_bool(get_bit(data, 3)); + self.mixer.ch1.left = Volume::from_bool(get_bit(data, 4)); + self.mixer.ch2.left = Volume::from_bool(get_bit(data, 5)); + self.mixer.ch3.left = Volume::from_bool(get_bit(data, 6)); + self.mixer.ch4.left = Volume::from_bool(get_bit(data, 7)); } 0xFF26 => self.apu_enable = (1 << 7) == (data & 0b10000000), 0xFF11..0xFF1A | 0xFF1B | 0xFF1D..0xFF23 | 0xFF24..0xFF40 => self.mem[reg(addr)] = data, diff --git a/src/processor/memory/mmio/apu/channels.rs b/src/processor/memory/mmio/apu/channels.rs index 0e5c9be..c4249ea 100644 --- a/src/processor/memory/mmio/apu/channels.rs +++ b/src/processor/memory/mmio/apu/channels.rs @@ -25,17 +25,45 @@ impl Default for Sweep { #[derive(Clone, Copy, PartialEq)] struct Envelope { initial_volume: u8, + current_volume: u8, mode: EnvelopeMode, rate: u8, + counter: u8, +} + +impl Envelope { + fn new(initial_volume: u8, mode: EnvelopeMode, rate: u8) -> Self { + Self { + initial_volume, + current_volume: initial_volume, + mode, + rate, + counter: 0, + } + } + fn tick(&mut self) { + if self.rate == 0 { + return; + } + self.counter += 1; + if self.counter % self.rate == 0 { + self.counter = 0; + match self.mode { + EnvelopeMode::Increase => { + self.current_volume = self.current_volume.saturating_add(1) + } + EnvelopeMode::Decrease => { + self.current_volume = self.current_volume.saturating_sub(1) + } + } + } + } } impl Default for Envelope { fn default() -> Self { - Self { - initial_volume: 0xF, - mode: EnvelopeMode::Decrease, - rate: 0x3, - } + Self::new(0x0, EnvelopeMode::Decrease, 0x0) + // Self::new(0xF, EnvelopeMode::Decrease, 0x3) } } @@ -84,8 +112,6 @@ impl DutyCycle { pub(super) struct PwmChannel { pub(super) enabled: bool, - pub(super) pan_left: bool, - pub(super) pan_right: bool, sweep: Sweep, duty_cycle: DutyCycle, length_enable: bool, @@ -98,12 +124,10 @@ pub(super) struct PwmChannel { } impl PwmChannel { - pub(super) fn new(enabled: bool, pan_left: bool, pan_right: bool) -> Self { + pub(super) fn new(enabled: bool) -> Self { let wavelength = 0x700; Self { enabled, - pan_left, - pan_right, sweep: Sweep::default(), duty_cycle: DutyCycle::Fifty, length_enable: false, @@ -139,7 +163,11 @@ impl PwmChannel { } fn dac(&self, digital: u8) -> f32 { - ((digital as f32) * (-2.)) + 1. + (((digital as f32) * (-2.)) + 1.) * ((self.envelope.current_volume as f32) / 15.) + } + + pub(super) fn envelope_tick(&mut self) { + self.envelope.tick(); } pub(super) fn update_sweep(&mut self, pace: u8, mode: EnvelopeMode, slope: u8) { @@ -166,15 +194,15 @@ impl PwmChannel { } pub(super) fn update_volume_and_envelope(&mut self, data: u8) { - self.queued_envelope = Envelope { - initial_volume: (data & 0b11110000) >> 4, - mode: if (data & 0b1000) == 0b1000 { + self.queued_envelope = Envelope::new( + (data & 0b11110000) >> 4, + if (data & 0b1000) == 0b1000 { EnvelopeMode::Increase } else { EnvelopeMode::Decrease }, - rate: data & 0b111, - }; + data & 0b111, + ); } pub(super) fn get_volume_and_envelope(&self) -> u8 { @@ -204,37 +232,26 @@ impl PwmChannel { } fn set_wave_timer(wavelength: u16) -> u16 { - (2048 - wavelength) * 4 + 2048 - wavelength + // (2048 - wavelength) * 4 } pub(super) struct WaveChannel { pub(super) enabled: bool, - pub(super) pan_left: bool, - pub(super) pan_right: bool, } impl WaveChannel { - pub(super) fn new(enabled: bool, pan_left: bool, pan_right: bool) -> Self { - Self { - enabled, - pan_left, - pan_right, - } + pub(super) fn new(enabled: bool) -> Self { + Self { enabled } } } pub(super) struct NoiseChannel { pub(super) enabled: bool, - pub(super) pan_left: bool, - pub(super) pan_right: bool, } impl NoiseChannel { - pub(super) fn new(enabled: bool, pan_left: bool, pan_right: bool) -> Self { - Self { - enabled, - pan_left, - pan_right, - } + pub(super) fn new(enabled: bool) -> Self { + Self { enabled } } } diff --git a/src/processor/timer.rs b/src/processor/timer.rs index b6d9c0c..dfaa1b0 100644 --- a/src/processor/timer.rs +++ b/src/processor/timer.rs @@ -30,7 +30,7 @@ impl Cpu { let clock_cycles = (machine_cycles as usize) * 4; self.advance_gpu_clock(clock_cycles); - self.advance_apu_clock(clock_cycles); + self.advance_apu_clock(machine_cycles as usize); self.timers.div_counter += clock_cycles; let mut div_diff = (self.timers.div_counter / 256) as u8;