audio kinda works
This commit is contained in:
parent
e60426fe0f
commit
7a28655604
31
Cargo.lock
generated
31
Cargo.lock
generated
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in a new issue