sdl2: Use callbacks with ring buffer instead of audio queue.
Better latencies, and also fast-forward (aka Turbo mode) sounds normal this way. Former-commit-id: d1075087e847c765a871157a7973c897575ef4d7
This commit is contained in:
parent
cb54f4d1a3
commit
d8545dd8cd
18
Cargo.lock
generated
18
Cargo.lock
generated
|
@ -212,6 +212,15 @@ dependencies = [
|
||||||
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "debug_stub_derive"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dirs"
|
name = "dirs"
|
||||||
version = "2.0.2"
|
version = "2.0.2"
|
||||||
|
@ -669,6 +678,11 @@ name = "rgb"
|
||||||
version = "0.8.13"
|
version = "0.8.13"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ringbuf"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustboyadvance-ng"
|
name = "rustboyadvance-ng"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
@ -682,12 +696,14 @@ dependencies = [
|
||||||
"clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"colored 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"colored 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ctrlc 3.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ctrlc 3.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"debug_stub_derive 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"enum-primitive-derive 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"enum-primitive-derive 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"hexdump 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"hexdump 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"minifb 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"minifb 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"nom 5.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"nom 5.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"num 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"num 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"ringbuf 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rustyline 5.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rustyline 5.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"sdl2 0.32.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"sdl2 0.32.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"spin_sleep 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
"spin_sleep 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -1007,6 +1023,7 @@ dependencies = [
|
||||||
"checksum constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8ff012e225ce166d4422e0e78419d901719760f62ae2b7969ca6b564d1b54a9e"
|
"checksum constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8ff012e225ce166d4422e0e78419d901719760f62ae2b7969ca6b564d1b54a9e"
|
||||||
"checksum crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1"
|
"checksum crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1"
|
||||||
"checksum ctrlc 3.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c7dfd2d8b4c82121dfdff120f818e09fc4380b0b7e17a742081a89b94853e87f"
|
"checksum ctrlc 3.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c7dfd2d8b4c82121dfdff120f818e09fc4380b0b7e17a742081a89b94853e87f"
|
||||||
|
"checksum debug_stub_derive 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "496b7f8a2f853313c3ca370641d7ff3e42c32974fdccda8f0684599ed0a3ff6b"
|
||||||
"checksum dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3"
|
"checksum dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3"
|
||||||
"checksum dirs-sys 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "afa0b23de8fd801745c471deffa6e12d248f962c9fd4b4c33787b055599bde7b"
|
"checksum dirs-sys 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "afa0b23de8fd801745c471deffa6e12d248f962c9fd4b4c33787b055599bde7b"
|
||||||
"checksum enum-primitive-derive 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2b90e520ec62c1864c8c78d637acbfe8baf5f63240f2fb8165b8325c07812dd"
|
"checksum enum-primitive-derive 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2b90e520ec62c1864c8c78d637acbfe8baf5f63240f2fb8165b8325c07812dd"
|
||||||
|
@ -1060,6 +1077,7 @@ dependencies = [
|
||||||
"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84"
|
"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84"
|
||||||
"checksum redox_users 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3fe5204c3a17e97dde73f285d49be585df59ed84b50a872baf416e73b62c3828"
|
"checksum redox_users 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3fe5204c3a17e97dde73f285d49be585df59ed84b50a872baf416e73b62c3828"
|
||||||
"checksum rgb 0.8.13 (registry+https://github.com/rust-lang/crates.io-index)" = "4f089652ca87f5a82a62935ec6172a534066c7b97be003cc8f702ee9a7a59c92"
|
"checksum rgb 0.8.13 (registry+https://github.com/rust-lang/crates.io-index)" = "4f089652ca87f5a82a62935ec6172a534066c7b97be003cc8f702ee9a7a59c92"
|
||||||
|
"checksum ringbuf 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7c2b29d87cfbdce39849012bb5020fff88b8f01f4f5b55846a0b6ef360774eae"
|
||||||
"checksum rustc-demangle 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "a7f4dccf6f4891ebcc0c39f9b6eb1a83b9bf5d747cb439ec6fba4f3b977038af"
|
"checksum rustc-demangle 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "a7f4dccf6f4891ebcc0c39f9b6eb1a83b9bf5d747cb439ec6fba4f3b977038af"
|
||||||
"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
||||||
"checksum rustyline 5.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "67e12e40e0240de07f0dab4f4dd01bdb15d74dc977026d4ba91666c41c679ade"
|
"checksum rustyline 5.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "67e12e40e0240de07f0dab4f4dd01bdb15d74dc977026d4ba91666c41c679ade"
|
||||||
|
|
|
@ -26,6 +26,8 @@ zip = "0.5.3"
|
||||||
ctrlc = "3.1.3"
|
ctrlc = "3.1.3"
|
||||||
spin_sleep="0.3.7"
|
spin_sleep="0.3.7"
|
||||||
bit-set = "0.5.1"
|
bit-set = "0.5.1"
|
||||||
|
ringbuf = "0.2.1"
|
||||||
|
debug_stub_derive = "0.3.0"
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "rba-sdl2"
|
name = "rba-sdl2"
|
||||||
|
|
|
@ -467,12 +467,7 @@ impl Gpu {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the new gpu state
|
// Returns the new gpu state
|
||||||
pub fn step(
|
pub fn step(&mut self, cycles: usize, sb: &mut SysBus, irqs: &mut IrqBitmask) {
|
||||||
&mut self,
|
|
||||||
cycles: usize,
|
|
||||||
sb: &mut SysBus,
|
|
||||||
irqs: &mut IrqBitmask,
|
|
||||||
) {
|
|
||||||
self.cycles += cycles;
|
self.cycles += cycles;
|
||||||
|
|
||||||
match self.state {
|
match self.state {
|
||||||
|
|
|
@ -1,37 +1,34 @@
|
||||||
pub type Sample = (i16, i16);
|
use crate::{AudioInterface, StereoSample};
|
||||||
|
|
||||||
const PI: f32 = std::f32::consts::PI;
|
const PI: f32 = std::f32::consts::PI;
|
||||||
|
|
||||||
pub trait Resampler {
|
pub trait Resampler {
|
||||||
fn push_sample(&mut self, s: Sample, output: &mut Vec<i16>);
|
fn push_sample(&mut self, s: StereoSample, audio: &mut dyn AudioInterface);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct CosineResampler {
|
pub struct CosineResampler {
|
||||||
last_in_sample: Sample,
|
last_in_sample: StereoSample,
|
||||||
phase: f32,
|
phase: f32,
|
||||||
pub in_freq: f32,
|
pub in_freq: f32,
|
||||||
out_freq: f32,
|
out_freq: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cosine_interpolation(y1: Sample, y2: Sample, phase: f32) -> Sample {
|
fn cosine_interpolation(y1: i16, y2: i16, phase: f32) -> i16 {
|
||||||
let y1_left = y1.0 as f32;
|
let y1 = y1 as i32 as f32;
|
||||||
let y1_right = y1.1 as f32;
|
let y2 = y2 as i32 as f32;
|
||||||
let y2_left = y2.0 as f32;
|
|
||||||
let y2_right = y2.1 as f32;
|
|
||||||
|
|
||||||
let mu2 = (1.0 - (PI * phase).cos()) / 2.0;
|
let mu2 = (1.0 - (PI * phase).cos()) / 2.0;
|
||||||
|
|
||||||
(
|
(y2 * (1.0 - mu2) + y1 * mu2) as i16
|
||||||
(y2_left * (1.0 - mu2) + y1_left * mu2) as i16,
|
|
||||||
(y2_right * (1.0 - mu2) + y1_right * mu2) as i16,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Resampler for CosineResampler {
|
impl Resampler for CosineResampler {
|
||||||
fn push_sample(&mut self, s: Sample, output: &mut Vec<i16>) {
|
fn push_sample(&mut self, s: StereoSample, audio: &mut dyn AudioInterface) {
|
||||||
while self.phase < 1.0 {
|
while self.phase < 1.0 {
|
||||||
let x = cosine_interpolation(self.last_in_sample, s, self.phase);
|
let left = cosine_interpolation(self.last_in_sample.0, s.0, self.phase);
|
||||||
output.push(x.0);
|
let right = cosine_interpolation(self.last_in_sample.1, s.1, self.phase);
|
||||||
output.push(x.1);
|
audio.push_sample((left, right));
|
||||||
self.phase += self.in_freq / self.out_freq;
|
self.phase += self.in_freq / self.out_freq;
|
||||||
}
|
}
|
||||||
self.phase = self.phase - 1.0;
|
self.phase = self.phase - 1.0;
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
// TODO write tests or replace with a crate
|
// TODO write tests or replace with a crate
|
||||||
|
|
||||||
const SOUND_FIFO_CAPACITY: usize = 32;
|
const SOUND_FIFO_CAPACITY: usize = 32;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
|
@ -31,10 +31,20 @@ struct DmaSoundChannel {
|
||||||
fifo: SoundFifo,
|
fifo: SoundFifo,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl DmaSoundChannel {
|
||||||
|
fn is_stereo_channel_enabled(&self, channel: usize) -> bool {
|
||||||
|
match channel {
|
||||||
|
0 => self.enable_left,
|
||||||
|
1 => self.enable_right,
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Default for DmaSoundChannel {
|
impl Default for DmaSoundChannel {
|
||||||
fn default() -> DmaSoundChannel {
|
fn default() -> DmaSoundChannel {
|
||||||
DmaSoundChannel {
|
DmaSoundChannel {
|
||||||
volume_shift: 1,
|
volume_shift: 0,
|
||||||
value: 0,
|
value: 0,
|
||||||
enable_right: false,
|
enable_right: false,
|
||||||
enable_left: false,
|
enable_left: false,
|
||||||
|
@ -50,8 +60,12 @@ const REG_FIFO_A_H: u32 = REG_FIFO_A + 2;
|
||||||
const REG_FIFO_B_L: u32 = REG_FIFO_B;
|
const REG_FIFO_B_L: u32 = REG_FIFO_B;
|
||||||
const REG_FIFO_B_H: u32 = REG_FIFO_B + 2;
|
const REG_FIFO_B_H: u32 = REG_FIFO_B + 2;
|
||||||
|
|
||||||
|
type AudioDeviceRcRefCell = Rc<RefCell<dyn AudioInterface>>;
|
||||||
|
|
||||||
|
#[derive(DebugStub)]
|
||||||
pub struct SoundController {
|
pub struct SoundController {
|
||||||
audio_device: Rc<RefCell<dyn AudioInterface>>,
|
#[debug_stub = "AudioDeviceRcRefCell"]
|
||||||
|
audio_device: AudioDeviceRcRefCell,
|
||||||
|
|
||||||
sample_rate_to_cpu_freq: usize, // how many "cycles" are a sample?
|
sample_rate_to_cpu_freq: usize, // how many "cycles" are a sample?
|
||||||
last_sample_cycles: usize, // cycles count when we last provided a new sample.
|
last_sample_cycles: usize, // cycles count when we last provided a new sample.
|
||||||
|
@ -90,7 +104,7 @@ pub struct SoundController {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SoundController {
|
impl SoundController {
|
||||||
pub fn new(audio_device: Rc<RefCell<dyn AudioInterface>>) -> SoundController {
|
pub fn new(audio_device: AudioDeviceRcRefCell) -> SoundController {
|
||||||
let resampler =
|
let resampler =
|
||||||
CosineResampler::new(32768_f32, audio_device.borrow().get_sample_rate() as f32);
|
CosineResampler::new(32768_f32, audio_device.borrow().get_sample_rate() as f32);
|
||||||
SoundController {
|
SoundController {
|
||||||
|
@ -250,13 +264,13 @@ impl SoundController {
|
||||||
}
|
}
|
||||||
|
|
||||||
REG_FIFO_A_L | REG_FIFO_A_H => {
|
REG_FIFO_A_L | REG_FIFO_A_H => {
|
||||||
self.dma_sound[0].fifo.write(((value >> 8) & 0xff) as i8);
|
|
||||||
self.dma_sound[0].fifo.write((value & 0xff) as i8);
|
self.dma_sound[0].fifo.write((value & 0xff) as i8);
|
||||||
|
self.dma_sound[0].fifo.write(((value >> 8) & 0xff) as i8);
|
||||||
}
|
}
|
||||||
|
|
||||||
REG_FIFO_B_L | REG_FIFO_B_H => {
|
REG_FIFO_B_L | REG_FIFO_B_H => {
|
||||||
self.dma_sound[1].fifo.write(((value >> 8) & 0xff) as i8);
|
|
||||||
self.dma_sound[1].fifo.write((value & 0xff) as i8);
|
self.dma_sound[1].fifo.write((value & 0xff) as i8);
|
||||||
|
self.dma_sound[1].fifo.write(((value >> 8) & 0xff) as i8);
|
||||||
}
|
}
|
||||||
|
|
||||||
REG_SOUNDBIAS => self.sound_bias = value & 0xc3fe,
|
REG_SOUNDBIAS => self.sound_bias = value & 0xc3fe,
|
||||||
|
@ -283,11 +297,11 @@ impl SoundController {
|
||||||
|
|
||||||
const FIFO_INDEX_TO_REG: [u32; 2] = [REG_FIFO_A, REG_FIFO_B];
|
const FIFO_INDEX_TO_REG: [u32; 2] = [REG_FIFO_A, REG_FIFO_B];
|
||||||
for fifo in 0..2 {
|
for fifo in 0..2 {
|
||||||
let channel = &mut self.dma_sound[fifo];
|
let dma = &mut self.dma_sound[fifo];
|
||||||
|
|
||||||
if timer_id == channel.timer_select {
|
if timer_id == dma.timer_select {
|
||||||
channel.value = channel.fifo.read();
|
dma.value = dma.fifo.read();
|
||||||
if channel.fifo.count() <= 16 {
|
if dma.fifo.count() <= 16 {
|
||||||
dmac.notify_sound_fifo(FIFO_INDEX_TO_REG[fifo]);
|
dmac.notify_sound_fifo(FIFO_INDEX_TO_REG[fifo]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -310,22 +324,19 @@ impl SoundController {
|
||||||
|
|
||||||
// time to push a new sample!
|
// time to push a new sample!
|
||||||
|
|
||||||
let mut sample = (0i16, 0i16);
|
let mut sample = [0; 2];
|
||||||
for i in 0..2 {
|
|
||||||
let channel = &self.dma_sound[i];
|
for channel in 0..=1 {
|
||||||
if channel.enable_left {
|
for dma in &mut self.dma_sound {
|
||||||
sample.0 += ((channel.value as i16) << 8) >> channel.volume_shift;
|
if dma.is_stereo_channel_enabled(channel) {
|
||||||
|
sample[channel] += dma.value as i16;
|
||||||
}
|
}
|
||||||
if channel.enable_right {
|
|
||||||
sample.1 += ((channel.value as i16) << 8) >> channel.volume_shift;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.resampler.push_sample(sample, &mut self.output_buffer);
|
let mut audio = self.audio_device.borrow_mut();
|
||||||
if self.output_buffer.len() >= 10000 {
|
self.resampler
|
||||||
self.audio_device.borrow_mut().play(&self.output_buffer);
|
.push_sample((sample[0], sample[1]), &mut *audio);
|
||||||
self.output_buffer.clear();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,9 @@
|
||||||
#![feature(core_intrinsics)]
|
#![feature(core_intrinsics)]
|
||||||
#![feature(exclusive_range_pattern)]
|
#![feature(exclusive_range_pattern)]
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate debug_stub_derive;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate enum_primitive_derive;
|
extern crate enum_primitive_derive;
|
||||||
extern crate num;
|
extern crate num;
|
||||||
|
@ -34,11 +37,13 @@ pub trait VideoInterface {
|
||||||
fn render(&mut self, buffer: &[u32]);
|
fn render(&mut self, buffer: &[u32]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub type StereoSample = (i16, i16);
|
||||||
|
|
||||||
pub trait AudioInterface {
|
pub trait AudioInterface {
|
||||||
fn get_sample_rate(&self) -> i32;
|
fn get_sample_rate(&self) -> i32;
|
||||||
|
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
fn play(&mut self, samples: &[i16]) {}
|
fn push_sample(&mut self, samples: StereoSample) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait InputInterface {
|
pub trait InputInterface {
|
||||||
|
|
|
@ -1,40 +1,89 @@
|
||||||
use sdl2;
|
use sdl2;
|
||||||
use sdl2::audio::{AudioQueue, AudioSpecDesired};
|
use sdl2::audio::{AudioCallback, AudioDevice, AudioSpec, AudioSpecDesired};
|
||||||
|
|
||||||
use rustboyadvance_ng::AudioInterface;
|
use rustboyadvance_ng::{AudioInterface, StereoSample};
|
||||||
|
|
||||||
|
extern crate ringbuf;
|
||||||
|
use ringbuf::{Consumer, Producer, RingBuffer};
|
||||||
|
|
||||||
|
struct GbaAudioCallback {
|
||||||
|
consumer: Consumer<StereoSample>,
|
||||||
|
spec: AudioSpec,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Sdl2AudioPlayer {
|
pub struct Sdl2AudioPlayer {
|
||||||
pub device: AudioQueue<i16>,
|
_device: AudioDevice<GbaAudioCallback>,
|
||||||
|
producer: Producer<StereoSample>,
|
||||||
freq: i32,
|
freq: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl AudioCallback for GbaAudioCallback {
|
||||||
|
type Channel = i16;
|
||||||
|
|
||||||
|
fn callback(&mut self, out_samples: &mut [i16]) {
|
||||||
|
let sample_count = out_samples.len() / 2;
|
||||||
|
|
||||||
|
for i in 0..sample_count {
|
||||||
|
if let Some((left, right)) = self.consumer.pop() {
|
||||||
|
out_samples[2 * i] = left * (1 << 4);
|
||||||
|
out_samples[2 * i + 1] = right * (1 << 4);
|
||||||
|
} else {
|
||||||
|
out_samples[2 * i] = self.spec.silence as i16;
|
||||||
|
out_samples[2 * i + 1] = self.spec.silence as i16;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl AudioInterface for Sdl2AudioPlayer {
|
impl AudioInterface for Sdl2AudioPlayer {
|
||||||
fn get_sample_rate(&self) -> i32 {
|
fn get_sample_rate(&self) -> i32 {
|
||||||
self.freq
|
self.freq
|
||||||
}
|
}
|
||||||
|
|
||||||
fn play(&mut self, samples: &[i16]) {
|
fn push_sample(&mut self, sample: StereoSample) {
|
||||||
self.device.queue(&samples);
|
#![allow(unused_must_use)]
|
||||||
|
self.producer.push(sample);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_audio_player(sdl: &sdl2::Sdl) -> Sdl2AudioPlayer {
|
pub fn create_audio_player(sdl: &sdl2::Sdl) -> Sdl2AudioPlayer {
|
||||||
let audio_subsystem = sdl.audio().unwrap();
|
|
||||||
|
|
||||||
let desired_spec = AudioSpecDesired {
|
let desired_spec = AudioSpecDesired {
|
||||||
freq: Some(44_100),
|
freq: Some(44_100),
|
||||||
channels: Some(2), // stereo
|
channels: Some(2), // stereo
|
||||||
samples: None,
|
samples: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let audio_subsystem = sdl.audio().unwrap();
|
||||||
|
|
||||||
|
let mut freq = 0;
|
||||||
|
|
||||||
|
let mut producer: Option<Producer<StereoSample>> = None;
|
||||||
|
|
||||||
let device = audio_subsystem
|
let device = audio_subsystem
|
||||||
.open_queue::<i16, _>(None, &desired_spec)
|
.open_playback(None, &desired_spec, |spec| {
|
||||||
|
println!("Found audio device: {:?}", spec);
|
||||||
|
freq = spec.freq;
|
||||||
|
|
||||||
|
// Create a thread-safe SPSC fifo
|
||||||
|
let ringbuf_size = (spec.samples as usize) * 2;
|
||||||
|
let rb = RingBuffer::<StereoSample>::new(ringbuf_size);
|
||||||
|
let (prod, cons) = rb.split();
|
||||||
|
|
||||||
|
// move producer to the outer scope
|
||||||
|
producer = Some(prod);
|
||||||
|
|
||||||
|
GbaAudioCallback {
|
||||||
|
consumer: cons,
|
||||||
|
spec,
|
||||||
|
}
|
||||||
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
println!("Found audio device: {:?}", device.spec());
|
|
||||||
|
|
||||||
let freq = device.spec().freq;
|
|
||||||
device.resume();
|
device.resume();
|
||||||
|
|
||||||
Sdl2AudioPlayer { device, freq }
|
Sdl2AudioPlayer {
|
||||||
|
_device: device,
|
||||||
|
freq,
|
||||||
|
producer: producer.unwrap(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,9 +17,9 @@ mod audio;
|
||||||
mod input;
|
mod input;
|
||||||
mod video;
|
mod video;
|
||||||
|
|
||||||
use audio::{create_audio_player, Sdl2AudioPlayer};
|
use audio::create_audio_player;
|
||||||
use input::{create_input, Sdl2Input};
|
use input::create_input;
|
||||||
use video::{create_video_interface, Sdl2Video};
|
use video::create_video_interface;
|
||||||
|
|
||||||
extern crate rustboyadvance_ng;
|
extern crate rustboyadvance_ng;
|
||||||
use rustboyadvance_ng::prelude::*;
|
use rustboyadvance_ng::prelude::*;
|
||||||
|
@ -85,7 +85,6 @@ fn main() {
|
||||||
keycode: Some(Keycode::Space),
|
keycode: Some(Keycode::Space),
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
audio.borrow_mut().device.clear(); // clear audio queue
|
|
||||||
frame_limiter = true;
|
frame_limiter = true;
|
||||||
}
|
}
|
||||||
Event::KeyDown {
|
Event::KeyDown {
|
||||||
|
|
Reference in a new issue