From 7a28655604cbce316043d2ebd39b6f6c53d239ef Mon Sep 17 00:00:00 2001 From: Alex Janka Date: Fri, 17 Feb 2023 11:01:38 +1100 Subject: [PATCH] audio kinda works --- Cargo.lock | 31 ++++++++++++ Cargo.toml | 3 ++ src/main.rs | 2 +- src/processor/gpu.rs | 1 - src/processor/memory/mmio/apu.rs | 87 ++++++++++++++------------------ src/processor/mod.rs | 2 - src/processor/timer.rs | 10 ---- 7 files changed, 73 insertions(+), 63 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4ce2b11..87493da 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -24,6 +24,16 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "async-ringbuf" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85fa5f7f80e4d4356056f1ccf628c57872ea6f7faae4ab9dc7a8208376c461c2" +dependencies = [ + "futures", + "ringbuf", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -233,6 +243,15 @@ dependencies = [ "windows 0.44.0", ] +[[package]] +name = "crossbeam-utils" +version = "0.8.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f" +dependencies = [ + "cfg-if", +] + [[package]] name = "cty" version = "0.2.2" @@ -389,11 +408,14 @@ dependencies = [ name = "gb-emu" version = "0.1.0" dependencies = [ + "async-ringbuf", "clap", "cpal", + "futures", "gilrs", "minifb", "rand", + "ringbuf", "samplerate", "spin_sleep", ] @@ -1063,6 +1085,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "ringbuf" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93ca10b9c9e53ac855a2d6953bce34cef6edbac32c4b13047a4d59d67299420a" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "rustc-hash" version = "1.1.0" diff --git a/Cargo.toml b/Cargo.toml index 7cac5ec..8f5958c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,3 +13,6 @@ rand = "0.8.5" gilrs = "0.10.1" samplerate = "0.2.4" cpal = "0.15.0" +ringbuf = "0.3.2" +async-ringbuf = "0.1.2" +futures = "0.3.26" diff --git a/src/main.rs b/src/main.rs index 12fe10a..6e975a3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,4 @@ -#![feature(exclusive_range_pattern, let_chains, slice_flatten)] +#![feature(exclusive_range_pattern, let_chains, slice_flatten, async_closure)] mod processor; mod util; diff --git a/src/processor/gpu.rs b/src/processor/gpu.rs index c1423c8..237bfb1 100644 --- a/src/processor/gpu.rs +++ b/src/processor/gpu.rs @@ -160,7 +160,6 @@ impl Cpu { } self.gpu.mode = DrawMode::VBlank; self.render_window(); - self.next_audio(); self.memory.set(0xFF0F, set_bit(self.memory.get(0xFF0F), 0)); } diff --git a/src/processor/memory/mmio/apu.rs b/src/processor/memory/mmio/apu.rs index 3f34c40..3085f95 100644 --- a/src/processor/memory/mmio/apu.rs +++ b/src/processor/memory/mmio/apu.rs @@ -10,12 +10,13 @@ use crate::{ }, util::{get_bit, set_or_clear_bit}, }; +use async_ringbuf::{AsyncHeapProducer, AsyncHeapRb}; use cpal::{ traits::{DeviceTrait, HostTrait, StreamTrait}, Device, Stream, StreamConfig, }; +use futures::executor; use samplerate::{ConverterType, Samplerate}; -use std::sync::mpsc::{channel, Sender}; mod channels; mod types; @@ -53,7 +54,8 @@ pub struct Apu { device: Device, config: StreamConfig, stream: Option, - send_buffer: Option>>, + send_rb: Option>, + converter: Samplerate, } impl Default for Apu { @@ -73,6 +75,14 @@ impl Default for Apu { .with_max_sample_rate() .config(); + let converter = Samplerate::new( + ConverterType::ZeroOrderHold, + CLOCK_SPEED as u32, + config.sample_rate.0, + 2, + ) + .unwrap(); + Self { mem: [0x0; MEM_SIZE], apu_enable: true, @@ -84,58 +94,34 @@ impl Default for Apu { device, config, stream: None, - send_buffer: None, + send_rb: None, + converter, } } } -const SCALE: u32 = 4; +const CYCLES_PER_FRAME: usize = 70224; 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 rb_len = sample_rate as usize; - let (tx, rx) = channel::>(); - let other = tx.clone(); - self.send_buffer = Some(tx); + let rb = AsyncHeapRb::::new(rb_len); + let (tx, mut rx) = rb.split(); + + self.send_rb = 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; + for v in data { + if let Some(a) = executor::block_on(rx.pop()) { + *v = a; } } - - 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. @@ -173,17 +159,24 @@ impl Apu { .map(|(one, two)| DacSample { one, two }) .collect(), ); + if self.buffer.len() >= CYCLES_PER_FRAME { + self.next_audio(); + } } fn next_audio(&mut self) { - if let Some(send) = &self.send_buffer { - send.send( - self.buffer - .drain(..) - .map(|v| v.mixed(&self.mixer)) - .collect(), - ) - .unwrap(); + if let Some(ref mut send) = self.send_rb { + let converted = self + .converter + .process( + &self + .buffer + .drain(..) + .flat_map(|v| v.mixed(&self.mixer)) + .collect::>(), + ) + .unwrap(); + executor::block_on(send.push_slice(&converted)).unwrap(); } } @@ -306,8 +299,4 @@ impl Cpu { pub fn advance_apu_clock(&mut self, steps: usize) { self.memory.apu.tick(steps); } - - pub fn next_audio(&mut self) { - self.memory.apu.next_audio(); - } } diff --git a/src/processor/mod.rs b/src/processor/mod.rs index bf5c8b8..c28bc36 100644 --- a/src/processor/mod.rs +++ b/src/processor/mod.rs @@ -72,7 +72,6 @@ impl Cpu { if self.halted { self.increment_timers(1); - self.sleep(interrupt_cycles + 1); return; } @@ -96,7 +95,6 @@ impl Cpu { let cycles = self.run_opcode(opcode); self.memory.user_mode = false; self.increment_timers(cycles); - self.sleep(interrupt_cycles + cycles); } fn next_opcode(&mut self) -> u8 { diff --git a/src/processor/timer.rs b/src/processor/timer.rs index 965e074..6358c0b 100644 --- a/src/processor/timer.rs +++ b/src/processor/timer.rs @@ -2,7 +2,6 @@ use crate::{ processor::Cpu, util::{get_bit, set_bit}, }; -use std::time::Duration; pub(super) struct Timers { div_counter: usize, @@ -67,15 +66,6 @@ impl Cpu { } } - pub(super) fn sleep(&mut self, machine_cycles: u8) { - let secs = ((machine_cycles as f64) * 4.) / CLOCK_SPEED as f64; - if let Some(remaining) = - Duration::from_secs_f64(secs).checked_sub(self.cycle_start.elapsed()) - { - spin_sleep::sleep(remaining); - } - } - fn timer_scale(&self) -> (bool, usize) { let timer_control = self.memory.get(0xFF07); let timer_enable = get_bit(timer_control, 2);