From 28240c80c32009f4a69d535dbd8dc386badda729 Mon Sep 17 00:00:00 2001 From: Alex Janka Date: Mon, 6 Mar 2023 21:10:52 +1100 Subject: [PATCH] rolled my own downsampler --- gb-emu/src/main.rs | 6 +-- lib/Cargo.toml | 1 - lib/src/connect/mod.rs | 12 +++--- lib/src/processor/memory/mmio/apu.rs | 40 +++++++------------ .../processor/memory/mmio/apu/downsampler.rs | 29 ++++++++++++++ 5 files changed, 53 insertions(+), 35 deletions(-) create mode 100644 lib/src/processor/memory/mmio/apu/downsampler.rs diff --git a/gb-emu/src/main.rs b/gb-emu/src/main.rs index 5d37b25..e610ca0 100644 --- a/gb-emu/src/main.rs +++ b/gb-emu/src/main.rs @@ -132,15 +132,15 @@ fn create_audio_output() -> (AudioOutput, Stream) { let sample_rate = config.sample_rate().0; - let (output, mut rx) = AudioOutput::new(sample_rate); + let (output, mut rx) = AudioOutput::new(sample_rate as f32); let stream = device .build_output_stream( &config.config(), move |data: &mut [f32], _info: &cpal::OutputCallbackInfo| { - for v in data { + for v in data.chunks_exact_mut(2) { match executor::block_on(rx.pop()) { - Some(a) => *v = a, + Some(a) => v.copy_from_slice(&a), None => panic!("Audio queue disconnected!"), } } diff --git a/lib/Cargo.toml b/lib/Cargo.toml index 8ed0a83..52c959f 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -7,7 +7,6 @@ edition = "2021" [dependencies] rand = "0.8.5" -samplerate = "0.2.4" async-ringbuf = "0.1.2" futures = "0.3.26" once_cell = "1.17.1" diff --git a/lib/src/connect/mod.rs b/lib/src/connect/mod.rs index d94027f..b04088f 100644 --- a/lib/src/connect/mod.rs +++ b/lib/src/connect/mod.rs @@ -24,18 +24,18 @@ pub trait Renderer { } pub struct AudioOutput { - pub sample_rate: u32, - pub send_rb: AsyncHeapProducer, + pub sample_rate: f32, + pub send_rb: AsyncHeapProducer<[f32; 2]>, } impl AudioOutput { - pub fn new(sample_rate: u32) -> (Self, AsyncHeapConsumer) { - let rb_len = sample_rate as usize / 60; + pub fn new(sample_rate: f32) -> (Self, AsyncHeapConsumer<[f32; 2]>) { + let rb_len = sample_rate as usize / (60 * 2); - let rb = AsyncHeapRb::::new(rb_len); + let rb = AsyncHeapRb::<[f32; 2]>::new(rb_len); let (mut send_rb, rx) = rb.split(); - executor::block_on(send_rb.push_iter(vec![0.; rb_len - 1].into_iter())).unwrap(); + executor::block_on(send_rb.push_iter(vec![[0.; 2]; rb_len - 1].into_iter())).unwrap(); ( Self { diff --git a/lib/src/processor/memory/mmio/apu.rs b/lib/src/processor/memory/mmio/apu.rs index 2ce94b8..7f1be29 100644 --- a/lib/src/processor/memory/mmio/apu.rs +++ b/lib/src/processor/memory/mmio/apu.rs @@ -1,19 +1,21 @@ -use self::types::{Channels, DacSample, Mixer, VinEnable, Volume}; +use self::{ + downsampler::Downsampler, + types::{Channels, DacSample, Mixer, VinEnable, Volume}, +}; use crate::{ connect::AudioOutput, - constants::CLOCK_SPEED, processor::memory::Address, util::{get_bit, set_or_clear_bit}, }; use futures::executor; use itertools::izip; -use samplerate::{ConverterType, Samplerate}; mod channels; +mod downsampler; mod types; impl DacSample { - fn mixed(&self, mixer: &Mixer) -> Vec { + fn mixed(&self, mixer: &Mixer) -> [f32; 2] { let left = (self.one * mixer.ch1.left.scale()) + (self.two * mixer.ch2.left.scale()) + (self.three * mixer.ch3.left.scale()) @@ -22,7 +24,7 @@ impl DacSample { + (self.two * mixer.ch2.right.scale()) + (self.three * mixer.ch3.right.scale()) + (self.four * mixer.ch4.right.scale()); - vec![ + [ self.mix_channel(left, mixer.vol_left), self.mix_channel(right, mixer.vol_right), ] @@ -40,7 +42,7 @@ pub struct Apu { mixer: Mixer, div_apu: u8, buffer: Vec, - converter: Samplerate, + converter: Downsampler, output: AudioOutput, } @@ -48,14 +50,6 @@ const CYCLES_PER_FRAME: usize = 70224; impl Apu { pub fn new(output: AudioOutput) -> Self { - let converter = Samplerate::new( - ConverterType::ZeroOrderHold, - CLOCK_SPEED as u32, - output.sample_rate, - 2, - ) - .unwrap(); - Self { apu_enable: true, channels: Channels::default(), @@ -63,7 +57,7 @@ impl Apu { mixer: Mixer::default(), div_apu: 0, buffer: vec![], - converter, + converter: Downsampler::new(output.sample_rate), output, } } @@ -111,16 +105,12 @@ impl Apu { } fn next_audio(&mut self) { - let converted = self - .converter - .process( - &self - .buffer - .drain(..) - .flat_map(|v| v.mixed(&self.mixer)) - .collect::>(), - ) - .unwrap(); + let converted = self.converter.process( + self.buffer + .drain(..) + .map(|v| v.mixed(&self.mixer)) + .collect::>(), + ); executor::block_on(self.output.send_rb.push_slice(&converted)).unwrap(); } diff --git a/lib/src/processor/memory/mmio/apu/downsampler.rs b/lib/src/processor/memory/mmio/apu/downsampler.rs new file mode 100644 index 0000000..7365c5f --- /dev/null +++ b/lib/src/processor/memory/mmio/apu/downsampler.rs @@ -0,0 +1,29 @@ +use crate::constants::CLOCK_SPEED; + +const TIME_PER_CYCLE: f32 = 1. / CLOCK_SPEED as f32; + +pub(super) struct Downsampler { + ratio: f32, + time_accum: f32, +} + +impl Downsampler { + pub fn new(sample_rate: f32) -> Self { + Self { + ratio: 1. / sample_rate, + time_accum: 0., + } + } + + pub fn process(&mut self, signal: Vec<[f32; 2]>) -> Vec<[f32; 2]> { + let mut output = vec![]; + for val in signal { + self.time_accum += TIME_PER_CYCLE; + if self.time_accum >= self.ratio { + self.time_accum = 0.; + output.push(val); + } + } + output + } +}