Skip to content
Snippets Groups Projects
Verified Commit 2faa3e75 authored by João Magalhães's avatar João Magalhães :rocket:
Browse files

chore: audio support for libretro

parent 90f38b2c
No related branches found
No related tags found
1 merge request!29Support for Libretro
Pipeline #3078 passed
...@@ -9,7 +9,7 @@ use std::{ ...@@ -9,7 +9,7 @@ use std::{
}; };
use boytacean::{ use boytacean::{
gb::GameBoy, gb::{AudioProvider, GameBoy},
pad::PadKey, pad::PadKey,
ppu::{DISPLAY_HEIGHT, DISPLAY_WIDTH, RGB1555_SIZE}, ppu::{DISPLAY_HEIGHT, DISPLAY_WIDTH, RGB1555_SIZE},
rom::Cartridge, rom::Cartridge,
...@@ -288,6 +288,8 @@ pub extern "C" fn retro_set_controller_port_device() { ...@@ -288,6 +288,8 @@ pub extern "C" fn retro_set_controller_port_device() {
#[no_mangle] #[no_mangle]
pub extern "C" fn retro_run() { pub extern "C" fn retro_run() {
let emulator = unsafe { EMULATOR.as_mut().unwrap() }; let emulator = unsafe { EMULATOR.as_mut().unwrap() };
let sample_batch_cb = unsafe { AUDIO_SAMPLE_BATCH_CALLBACK.as_ref().unwrap() };
let channels = emulator.audio_channels();
let mut counter_cycles = 0_u32; let mut counter_cycles = 0_u32;
let cycle_limit = 4194304 / 60; //@TODO this is super tricky let cycle_limit = 4194304 / 60; //@TODO this is super tricky
...@@ -304,6 +306,21 @@ pub extern "C" fn retro_run() { ...@@ -304,6 +306,21 @@ pub extern "C" fn retro_run() {
// include the advance of both the CPU, PPU, APU // include the advance of both the CPU, PPU, APU
// and any other frequency based component of the system // and any other frequency based component of the system
counter_cycles += emulator.clock() as u32; counter_cycles += emulator.clock() as u32;
// obtains the audio buffer reference and queues it
// in a batch manner using the audio callback at the
// the end of the operation clears the buffer
let audio_buffer = emulator
.audio_buffer()
.iter()
.map(|v| *v as i16 * 256)
.collect::<Vec<i16>>();
sample_batch_cb(
audio_buffer.as_ptr(),
audio_buffer.len() / channels as usize,
);
emulator.clear_audio_buffer();
} }
unsafe { unsafe {
......
...@@ -2,8 +2,6 @@ use std::collections::VecDeque; ...@@ -2,8 +2,6 @@ use std::collections::VecDeque;
use crate::{gb::GameBoy, warnln}; use crate::{gb::GameBoy, warnln};
const CHANNELS: u8 = 2;
const DUTY_TABLE: [[u8; 8]; 4] = [ const DUTY_TABLE: [[u8; 8]; 4] = [
[0, 0, 0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 0, 0, 1],
...@@ -93,9 +91,22 @@ pub struct Apu { ...@@ -93,9 +91,22 @@ pub struct Apu {
ch3_out_enabled: bool, ch3_out_enabled: bool,
ch4_out_enabled: bool, ch4_out_enabled: bool,
/// The RAM that is used to sore the wave information
/// to be used in channel 3 audio
wave_ram: [u8; 16], wave_ram: [u8; 16],
/// The rate at which audio samples are going to be
/// taken, ideally this value should be aligned with
/// the sampling rate of the output device. A typical
/// sampling rate would be of 44.1kHz.
sampling_rate: u16, sampling_rate: u16,
/// The number of audion channels that are going to be
/// outputted as part fo the audio buffer)
channels: u8,
/// Internal sequencer counter that runs at 512Hz
/// used for the activation of the tick actions.
sequencer: u16, sequencer: u16,
sequencer_step: u8, sequencer_step: u8,
output_timer: i16, output_timer: i16,
...@@ -106,7 +117,7 @@ pub struct Apu { ...@@ -106,7 +117,7 @@ pub struct Apu {
} }
impl Apu { impl Apu {
pub fn new(sampling_rate: u16, buffer_size: f32, clock_freq: u32) -> Self { pub fn new(sampling_rate: u16, channels: u8, buffer_size: f32, clock_freq: u32) -> Self {
Self { Self {
ch1_timer: 0, ch1_timer: 0,
ch1_sequence: 0, ch1_sequence: 0,
...@@ -180,25 +191,18 @@ impl Apu { ...@@ -180,25 +191,18 @@ impl Apu {
ch3_out_enabled: true, ch3_out_enabled: true,
ch4_out_enabled: true, ch4_out_enabled: true,
/// The RAM that is used to sore the wave information
/// to be used in channel 3 audio
wave_ram: [0u8; 16], wave_ram: [0u8; 16],
/// The rate at which audio samples are going to be
/// taken, ideally this value should be aligned with
/// the sampling rate of the output device. A typical
/// sampling rate would be of 44.1kHz.
sampling_rate, sampling_rate,
channels,
/// Internal sequencer counter that runs at 512Hz
/// used for the activation of the tick actions.
sequencer: 0, sequencer: 0,
sequencer_step: 0, sequencer_step: 0,
output_timer: 0, output_timer: 0,
audio_buffer: VecDeque::with_capacity( audio_buffer: VecDeque::with_capacity(
(sampling_rate as f32 * buffer_size) as usize * CHANNELS as usize, (sampling_rate as f32 * buffer_size) as usize * channels as usize,
), ),
audio_buffer_max: (sampling_rate as f32 * buffer_size) as usize * CHANNELS as usize, audio_buffer_max: (sampling_rate as f32 * buffer_size) as usize * channels as usize,
clock_freq, clock_freq,
} }
} }
...@@ -324,13 +328,14 @@ impl Apu { ...@@ -324,13 +328,14 @@ impl Apu {
// the buffer (avoiding overflow) and then then the new audio // the buffer (avoiding overflow) and then then the new audio
// volume item is added to the queue // volume item is added to the queue
if self.audio_buffer.len() >= self.audio_buffer_max { if self.audio_buffer.len() >= self.audio_buffer_max {
self.audio_buffer.pop_front(); for _ in 0..self.channels {
self.audio_buffer.pop_front(); self.audio_buffer.pop_front();
}
} }
if self.left_enabled { if self.left_enabled {
self.audio_buffer.push_back(self.output()); self.audio_buffer.push_back(self.output());
} }
if self.right_enabled { if self.right_enabled && self.channels > 1 {
self.audio_buffer.push_back(self.output()); self.audio_buffer.push_back(self.output());
} }
...@@ -729,7 +734,7 @@ impl Apu { ...@@ -729,7 +734,7 @@ impl Apu {
} }
pub fn channels(&self) -> u8 { pub fn channels(&self) -> u8 {
CHANNELS self.channels
} }
pub fn audio_buffer(&self) -> &VecDeque<u8> { pub fn audio_buffer(&self) -> &VecDeque<u8> {
...@@ -1061,7 +1066,7 @@ impl Apu { ...@@ -1061,7 +1066,7 @@ impl Apu {
impl Default for Apu { impl Default for Apu {
fn default() -> Self { fn default() -> Self {
Self::new(44100, 1.0, GameBoy::CPU_FREQ) Self::new(44100, 2, 1.0, GameBoy::CPU_FREQ)
} }
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment