Compare commits

..

4 Commits

Author SHA1 Message Date
Rekai Nyangadzayi Musuka b13444c885 feat(snd): simplify audio buffer
continuous-integration/drone/push Build is passing Details
The audio buffer now is held by crossbeam. The developers of said
library could write a batter queue than I could anyday
2021-07-11 23:48:16 -05:00
Rekai Nyangadzayi Musuka 3754325e30 chore(cartridge): silence warning 2021-07-11 23:47:05 -05:00
Rekai Nyangadzayi Musuka 3b772c7c49 feat(snd): synchronize audio with rest of system
In it's current form I think this is actually rather inefficient. It's
also not very accurate since I throw away a lot of samples for no real
good reason. More improvements to thiss will be coming henceforth
2021-07-11 23:22:17 -05:00
Rekai Nyangadzayi Musuka 45dc27301c chore: enable/disable gamepad input at compile time 2021-07-11 15:45:37 -05:00
5 changed files with 54 additions and 26 deletions

View File

@ -70,6 +70,10 @@ impl Bus {
self.snd.set_audio_src(sender) self.snd.set_audio_src(sender)
} }
pub(crate) fn is_mpsc_still_full(&mut self) -> bool {
self.snd.is_mpsc_still_full()
}
pub(crate) fn clock(&mut self) { pub(crate) fn clock(&mut self) {
self.ppu.clock(); self.ppu.clock();
self.timer.clock(); self.timer.clock();

View File

@ -183,7 +183,7 @@ impl Mbc1 {
} }
} }
fn mbcm_zero_bank(&self) -> u8 { fn _mbcm_zero_bank(&self) -> u8 {
use BankCount::*; use BankCount::*;
match self.bank_count { match self.bank_count {

View File

@ -126,8 +126,13 @@ impl Cpu {
}; };
let pending: u32 = cycles.into(); let pending: u32 = cycles.into();
for _ in 0..pending { let mut offset = 0;
for _ in 0..(pending + offset) {
if !self.bus.is_mpsc_still_full() {
self.bus.clock(); self.bus.clock();
} else {
offset += 1;
}
} }
self.handle_interrupts(); self.handle_interrupts();

View File

@ -11,6 +11,7 @@ pub const SM83_CYCLE_TIME: Duration = Duration::from_nanos(1_000_000_000 / SM83_
pub const CYCLES_IN_FRAME: Cycle = Cycle::new(456 * 154); // 456 Cycles times 154 scanlines pub const CYCLES_IN_FRAME: Cycle = Cycle::new(456 * 154); // 456 Cycles times 154 scanlines
pub(crate) const SM83_CLOCK_SPEED: u64 = 0x40_0000; // Hz which is 4.194304Mhz pub(crate) const SM83_CLOCK_SPEED: u64 = 0x40_0000; // Hz which is 4.194304Mhz
const DEFAULT_TITLE: &str = "DMG-01 Emulator"; const DEFAULT_TITLE: &str = "DMG-01 Emulator";
const GAMEPAD_ENABLED: bool = false;
pub fn init(boot_path: Option<&str>, rom_path: &str) -> Result<SM83> { pub fn init(boot_path: Option<&str>, rom_path: &str) -> Result<SM83> {
let mut cpu = match boot_path { let mut cpu = match boot_path {
@ -44,12 +45,13 @@ pub fn run(
} }
pub fn run_unsynced(game_boy: &mut SM83, gamepad: &mut Gilrs, input: &WinitInputHelper) -> Cycle { pub fn run_unsynced(game_boy: &mut SM83, gamepad: &mut Gilrs, input: &WinitInputHelper) -> Cycle {
if GAMEPAD_ENABLED {
if let Some(event) = gamepad.next_event() { if let Some(event) = gamepad.next_event() {
joypad::handle_gamepad_input(game_boy.joypad_mut(), event); joypad::handle_gamepad_input(game_boy.joypad_mut(), event);
} else { }
joypad::handle_keyboard_input(game_boy.joypad_mut(), input);
} }
joypad::handle_keyboard_input(game_boy.joypad_mut(), input);
game_boy.step() game_boy.step()
} }

View File

@ -1,14 +1,17 @@
use bitfield::bitfield; use bitfield::bitfield;
use crossbeam_channel::{Receiver, Sender}; use crossbeam_channel::{Receiver, Sender, TrySendError};
use rodio::Source; use rodio::Source;
use crate::emu::SM83_CLOCK_SPEED; use crate::emu::SM83_CLOCK_SPEED;
const WAVE_PATTERN_RAM_LEN: usize = 0x10; const WAVE_PATTERN_RAM_LEN: usize = 0x10;
const SAMPLE_RATE: u32 = 48000; // Hz const SAMPLE_RATE: u32 = 48000; // Hz
const AUDIO_BUFFER_LEN: usize = 1024;
const CHANNEL_COUNT: u16 = 2;
const SAMPLE_INCREMENT: u64 = SAMPLE_RATE as u64; const SAMPLE_INCREMENT: u64 = SAMPLE_RATE as u64;
#[derive(Debug, Clone, Default)] #[derive(Default, Debug, Clone)]
pub(crate) struct Sound { pub(crate) struct Sound {
pub(crate) ctrl: SoundControl, pub(crate) ctrl: SoundControl,
/// Tone & Sweep /// Tone & Sweep
@ -26,6 +29,8 @@ pub(crate) struct Sound {
sender: Option<SampleSender>, sender: Option<SampleSender>,
sample_counter: u64, sample_counter: u64,
is_mpsc_full: bool,
} }
impl Sound { impl Sound {
@ -85,11 +90,16 @@ impl Sound {
let ch4_left = self.ctrl.output.ch4_left() as u8 as f32 * ch4_amplitude; let ch4_left = self.ctrl.output.ch4_left() as u8 as f32 * ch4_amplitude;
let ch4_right = self.ctrl.output.ch4_right() as u8 as f32 * ch4_amplitude; let ch4_right = self.ctrl.output.ch4_right() as u8 as f32 * ch4_amplitude;
let left_sample = (ch1_left + ch2_left + ch3_left + ch4_left) / 4.0; let left = (ch1_left + ch2_left + ch3_left + ch4_left) / 4.0;
let right_sample = (ch1_right + ch2_right + ch3_right + ch4_right) / 4.0; let right = (ch1_right + ch2_right + ch3_right + ch4_right) / 4.0;
if let Some(send) = self.sender.as_ref() { if let Some(s) = self.sender.as_ref() {
send.add_sample(left_sample, right_sample); let _ = s.send_samples(left, right).map_err(|e| match e {
TrySendError::Full(_) => self.is_mpsc_full = true,
TrySendError::Disconnected(_) => {
panic!("Audio Sample MPSC Channel Disconnected")
}
});
} }
} }
} }
@ -98,6 +108,14 @@ impl Sound {
self.sender = Some(sender); self.sender = Some(sender);
} }
pub(crate) fn is_mpsc_still_full(&mut self) -> bool {
if let Some(sender) = self.sender.as_ref() {
self.is_mpsc_full = sender.is_full();
}
self.is_mpsc_full
}
fn clock_length(freq_hi: &FrequencyHigh, length_timer: &mut u16, enabled: &mut bool) { fn clock_length(freq_hi: &FrequencyHigh, length_timer: &mut u16, enabled: &mut bool) {
if freq_hi.idk() && *length_timer > 0 { if freq_hi.idk() && *length_timer > 0 {
*length_timer -= 1; *length_timer -= 1;
@ -1158,31 +1176,30 @@ pub struct AudioSenderReceiver;
impl AudioSenderReceiver { impl AudioSenderReceiver {
pub fn new() -> (SampleSender, SampleReceiver) { pub fn new() -> (SampleSender, SampleReceiver) {
let (send, recv) = crossbeam_channel::unbounded(); let (send, recv) = crossbeam_channel::bounded(AUDIO_BUFFER_LEN * 2);
(SampleSender { send }, SampleReceiver { recv }) (SampleSender { inner: send }, SampleReceiver { inner: recv })
} }
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct SampleSender { pub struct SampleSender {
send: Sender<f32>, inner: Sender<f32>,
} }
impl SampleSender { impl SampleSender {
fn add_sample(&self, left: f32, right: f32) { fn send_samples(&self, left: f32, right: f32) -> Result<(), TrySendError<f32>> {
self.send self.inner.try_send(left).and(self.inner.try_send(right))?;
.send(left) Ok(())
.expect("Send audio sample across threads"); }
self.send fn is_full(&self) -> bool {
.send(right) self.inner.is_full()
.expect("Send audio sample across threads");
} }
} }
pub struct SampleReceiver { pub struct SampleReceiver {
recv: Receiver<f32>, inner: Receiver<f32>,
} }
impl Iterator for SampleReceiver { impl Iterator for SampleReceiver {
@ -1190,7 +1207,7 @@ impl Iterator for SampleReceiver {
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
// TODO: Should this never return none? // TODO: Should this never return none?
self.recv.recv().ok() self.inner.recv().ok()
} }
} }
@ -1204,7 +1221,7 @@ impl Source for SampleReceiver {
fn channels(&self) -> u16 { fn channels(&self) -> u16 {
// The Gameboy supports two channels // The Gameboy supports two channels
2 CHANNEL_COUNT
} }
fn sample_rate(&self) -> u32 { fn sample_rate(&self) -> u32 {