audio kinda works

This commit is contained in:
Alex Janka 2023-02-17 11:01:38 +11:00
parent e60426fe0f
commit 7a28655604
7 changed files with 73 additions and 63 deletions

31
Cargo.lock generated
View file

@ -24,6 +24,16 @@ dependencies = [
"pkg-config", "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]] [[package]]
name = "autocfg" name = "autocfg"
version = "1.1.0" version = "1.1.0"
@ -233,6 +243,15 @@ dependencies = [
"windows 0.44.0", "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]] [[package]]
name = "cty" name = "cty"
version = "0.2.2" version = "0.2.2"
@ -389,11 +408,14 @@ dependencies = [
name = "gb-emu" name = "gb-emu"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"async-ringbuf",
"clap", "clap",
"cpal", "cpal",
"futures",
"gilrs", "gilrs",
"minifb", "minifb",
"rand", "rand",
"ringbuf",
"samplerate", "samplerate",
"spin_sleep", "spin_sleep",
] ]
@ -1063,6 +1085,15 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "ringbuf"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93ca10b9c9e53ac855a2d6953bce34cef6edbac32c4b13047a4d59d67299420a"
dependencies = [
"crossbeam-utils",
]
[[package]] [[package]]
name = "rustc-hash" name = "rustc-hash"
version = "1.1.0" version = "1.1.0"

View file

@ -13,3 +13,6 @@ rand = "0.8.5"
gilrs = "0.10.1" gilrs = "0.10.1"
samplerate = "0.2.4" samplerate = "0.2.4"
cpal = "0.15.0" cpal = "0.15.0"
ringbuf = "0.3.2"
async-ringbuf = "0.1.2"
futures = "0.3.26"

View file

@ -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 processor;
mod util; mod util;

View file

@ -160,7 +160,6 @@ impl Cpu {
} }
self.gpu.mode = DrawMode::VBlank; self.gpu.mode = DrawMode::VBlank;
self.render_window(); self.render_window();
self.next_audio();
self.memory.set(0xFF0F, set_bit(self.memory.get(0xFF0F), 0)); self.memory.set(0xFF0F, set_bit(self.memory.get(0xFF0F), 0));
} }

View file

@ -10,12 +10,13 @@ use crate::{
}, },
util::{get_bit, set_or_clear_bit}, util::{get_bit, set_or_clear_bit},
}; };
use async_ringbuf::{AsyncHeapProducer, AsyncHeapRb};
use cpal::{ use cpal::{
traits::{DeviceTrait, HostTrait, StreamTrait}, traits::{DeviceTrait, HostTrait, StreamTrait},
Device, Stream, StreamConfig, Device, Stream, StreamConfig,
}; };
use futures::executor;
use samplerate::{ConverterType, Samplerate}; use samplerate::{ConverterType, Samplerate};
use std::sync::mpsc::{channel, Sender};
mod channels; mod channels;
mod types; mod types;
@ -53,7 +54,8 @@ pub struct Apu {
device: Device, device: Device,
config: StreamConfig, config: StreamConfig,
stream: Option<Stream>, stream: Option<Stream>,
send_buffer: Option<Sender<Vec<[f32; 2]>>>, send_rb: Option<AsyncHeapProducer<f32>>,
converter: Samplerate,
} }
impl Default for Apu { impl Default for Apu {
@ -73,6 +75,14 @@ impl Default for Apu {
.with_max_sample_rate() .with_max_sample_rate()
.config(); .config();
let converter = Samplerate::new(
ConverterType::ZeroOrderHold,
CLOCK_SPEED as u32,
config.sample_rate.0,
2,
)
.unwrap();
Self { Self {
mem: [0x0; MEM_SIZE], mem: [0x0; MEM_SIZE],
apu_enable: true, apu_enable: true,
@ -84,58 +94,34 @@ impl Default for Apu {
device, device,
config, config,
stream: None, stream: None,
send_buffer: None, send_rb: None,
converter,
} }
} }
} }
const SCALE: u32 = 4; const CYCLES_PER_FRAME: usize = 70224;
impl Apu { impl Apu {
pub fn init(&mut self) { pub fn init(&mut self) {
let sample_rate = self.config.sample_rate.0; 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::<Vec<[f32; 2]>>(); let rb = AsyncHeapRb::<f32>::new(rb_len);
let other = tx.clone(); let (tx, mut rx) = rb.split();
self.send_buffer = Some(tx);
self.send_rb = Some(tx);
let stream = self let stream = self
.device .device
.build_output_stream( .build_output_stream(
&self.config, &self.config,
move |data: &mut [f32], _info: &cpal::OutputCallbackInfo| { move |data: &mut [f32], _info: &cpal::OutputCallbackInfo| {
let buf_length = data.len() / 2; for v in data {
let gbspeed_buf_length = (ratio * (buf_length as f32)) as usize; if let Some(a) = executor::block_on(rx.pop()) {
*v = a;
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::<Vec<_>>())
.unwrap(),
);
}, },
move |err| { move |err| {
// react to errors here. // react to errors here.
@ -173,17 +159,24 @@ impl Apu {
.map(|(one, two)| DacSample { one, two }) .map(|(one, two)| DacSample { one, two })
.collect(), .collect(),
); );
if self.buffer.len() >= CYCLES_PER_FRAME {
self.next_audio();
}
} }
fn next_audio(&mut self) { fn next_audio(&mut self) {
if let Some(send) = &self.send_buffer { if let Some(ref mut send) = self.send_rb {
send.send( let converted = self
self.buffer .converter
.process(
&self
.buffer
.drain(..) .drain(..)
.map(|v| v.mixed(&self.mixer)) .flat_map(|v| v.mixed(&self.mixer))
.collect(), .collect::<Vec<f32>>(),
) )
.unwrap(); .unwrap();
executor::block_on(send.push_slice(&converted)).unwrap();
} }
} }
@ -306,8 +299,4 @@ impl Cpu {
pub fn advance_apu_clock(&mut self, steps: usize) { pub fn advance_apu_clock(&mut self, steps: usize) {
self.memory.apu.tick(steps); self.memory.apu.tick(steps);
} }
pub fn next_audio(&mut self) {
self.memory.apu.next_audio();
}
} }

View file

@ -72,7 +72,6 @@ impl Cpu {
if self.halted { if self.halted {
self.increment_timers(1); self.increment_timers(1);
self.sleep(interrupt_cycles + 1);
return; return;
} }
@ -96,7 +95,6 @@ impl Cpu {
let cycles = self.run_opcode(opcode); let cycles = self.run_opcode(opcode);
self.memory.user_mode = false; self.memory.user_mode = false;
self.increment_timers(cycles); self.increment_timers(cycles);
self.sleep(interrupt_cycles + cycles);
} }
fn next_opcode(&mut self) -> u8 { fn next_opcode(&mut self) -> u8 {

View file

@ -2,7 +2,6 @@ use crate::{
processor::Cpu, processor::Cpu,
util::{get_bit, set_bit}, util::{get_bit, set_bit},
}; };
use std::time::Duration;
pub(super) struct Timers { pub(super) struct Timers {
div_counter: usize, 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) { fn timer_scale(&self) -> (bool, usize) {
let timer_control = self.memory.get(0xFF07); let timer_control = self.memory.get(0xFF07);
let timer_enable = get_bit(timer_control, 2); let timer_enable = get_bit(timer_control, 2);