no triply buffered audio
This commit is contained in:
parent
429e3b4085
commit
fb0dbf2f33
5 changed files with 37 additions and 89 deletions
|
@ -5,7 +5,7 @@ use cpal::{
|
||||||
use futures::executor;
|
use futures::executor;
|
||||||
use gb_emu_lib::connect::{AudioOutput, DownsampleType};
|
use gb_emu_lib::connect::{AudioOutput, DownsampleType};
|
||||||
|
|
||||||
const FRAMES_TO_BUFFER: usize = 1;
|
const BUFFERS_PER_FRAME: usize = 100;
|
||||||
const DOWNSAMPLE_TYPE: DownsampleType = DownsampleType::ZeroOrderHold;
|
const DOWNSAMPLE_TYPE: DownsampleType = DownsampleType::ZeroOrderHold;
|
||||||
|
|
||||||
pub fn create_output(muted: bool) -> (AudioOutput, Stream) {
|
pub fn create_output(muted: bool) -> (AudioOutput, Stream) {
|
||||||
|
@ -25,8 +25,7 @@ pub fn create_output(muted: bool) -> (AudioOutput, Stream) {
|
||||||
|
|
||||||
let sample_rate = config.sample_rate().0;
|
let sample_rate = config.sample_rate().0;
|
||||||
|
|
||||||
let (output, mut rx) =
|
let (output, mut rx) = AudioOutput::new(sample_rate as f32, BUFFERS_PER_FRAME, DOWNSAMPLE_TYPE);
|
||||||
AudioOutput::new(sample_rate as f32, true, FRAMES_TO_BUFFER, DOWNSAMPLE_TYPE);
|
|
||||||
|
|
||||||
let stream = if muted {
|
let stream = if muted {
|
||||||
device
|
device
|
||||||
|
|
|
@ -67,8 +67,8 @@ pub struct GameboyEmu {
|
||||||
type FrameReceiver = Mutex<Option<Receiver<Vec<[u8; 4]>>>>;
|
type FrameReceiver = Mutex<Option<Receiver<Vec<[u8; 4]>>>>;
|
||||||
type JoypadSender = Mutex<Option<Sender<(JoypadButtons, bool)>>>;
|
type JoypadSender = Mutex<Option<Sender<(JoypadButtons, bool)>>>;
|
||||||
|
|
||||||
const FRAMES_TO_BUFFER: usize = 1;
|
const BUFFERS_PER_FRAME: usize = 1;
|
||||||
const DOWNSAMPLE_TYPE: DownsampleType = DownsampleType::ZeroOrderHold;
|
const DOWNSAMPLE_TYPE: DownsampleType = DownsampleType::Linear;
|
||||||
|
|
||||||
const ROM: &[u8; 65536] = include_bytes!("../../test-roms/mGB.gb");
|
const ROM: &[u8; 65536] = include_bytes!("../../test-roms/mGB.gb");
|
||||||
|
|
||||||
|
@ -218,8 +218,7 @@ impl Plugin for GameboyEmu {
|
||||||
if let Some(ref mut vars) = self.vars {
|
if let Some(ref mut vars) = self.vars {
|
||||||
let (output, rx) = AudioOutput::new(
|
let (output, rx) = AudioOutput::new(
|
||||||
buffer_config.sample_rate,
|
buffer_config.sample_rate,
|
||||||
false,
|
BUFFERS_PER_FRAME,
|
||||||
FRAMES_TO_BUFFER,
|
|
||||||
DOWNSAMPLE_TYPE,
|
DOWNSAMPLE_TYPE,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -232,8 +231,7 @@ impl Plugin for GameboyEmu {
|
||||||
|
|
||||||
let (output, rx) = AudioOutput::new(
|
let (output, rx) = AudioOutput::new(
|
||||||
buffer_config.sample_rate,
|
buffer_config.sample_rate,
|
||||||
false,
|
BUFFERS_PER_FRAME,
|
||||||
FRAMES_TO_BUFFER,
|
|
||||||
DOWNSAMPLE_TYPE,
|
DOWNSAMPLE_TYPE,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -42,18 +42,16 @@ pub trait Renderer<Format: From<Colour>> {
|
||||||
pub struct AudioOutput {
|
pub struct AudioOutput {
|
||||||
pub sample_rate: f32,
|
pub sample_rate: f32,
|
||||||
pub send_rb: AsyncHeapProducer<[f32; 2]>,
|
pub send_rb: AsyncHeapProducer<[f32; 2]>,
|
||||||
pub wait_for_output: bool,
|
|
||||||
pub downsample_type: DownsampleType,
|
pub downsample_type: DownsampleType,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AudioOutput {
|
impl AudioOutput {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
sample_rate: f32,
|
sample_rate: f32,
|
||||||
wait_for_output: bool,
|
buffers_per_frame: usize,
|
||||||
frames_to_buffer: usize,
|
|
||||||
downsample_type: DownsampleType,
|
downsample_type: DownsampleType,
|
||||||
) -> (Self, AsyncHeapConsumer<[f32; 2]>) {
|
) -> (Self, AsyncHeapConsumer<[f32; 2]>) {
|
||||||
let rb_len = (sample_rate as usize / 60) * frames_to_buffer;
|
let rb_len = (sample_rate as usize / 60) / buffers_per_frame;
|
||||||
|
|
||||||
let rb = AsyncHeapRb::<[f32; 2]>::new(rb_len);
|
let rb = AsyncHeapRb::<[f32; 2]>::new(rb_len);
|
||||||
let (send_rb, rx) = rb.split();
|
let (send_rb, rx) = rb.split();
|
||||||
|
@ -62,7 +60,6 @@ impl AudioOutput {
|
||||||
Self {
|
Self {
|
||||||
sample_rate,
|
sample_rate,
|
||||||
send_rb,
|
send_rb,
|
||||||
wait_for_output,
|
|
||||||
downsample_type,
|
downsample_type,
|
||||||
},
|
},
|
||||||
rx,
|
rx,
|
||||||
|
|
|
@ -42,8 +42,6 @@ pub struct Apu {
|
||||||
vin: VinEnable,
|
vin: VinEnable,
|
||||||
mixer: Mixer,
|
mixer: Mixer,
|
||||||
div_apu: u8,
|
div_apu: u8,
|
||||||
buffer: Vec<DacSample>,
|
|
||||||
out_buffer: Vec<[f32; 2]>,
|
|
||||||
converter: Downsampler,
|
converter: Downsampler,
|
||||||
output: AudioOutput,
|
output: AudioOutput,
|
||||||
}
|
}
|
||||||
|
@ -55,8 +53,6 @@ pub struct ApuSaveState {
|
||||||
vin: VinEnable,
|
vin: VinEnable,
|
||||||
mixer: Mixer,
|
mixer: Mixer,
|
||||||
div_apu: u8,
|
div_apu: u8,
|
||||||
buffer: Vec<DacSample>,
|
|
||||||
out_buffer: Vec<[f32; 2]>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ApuSaveState {
|
impl ApuSaveState {
|
||||||
|
@ -67,14 +63,10 @@ impl ApuSaveState {
|
||||||
vin: apu.vin,
|
vin: apu.vin,
|
||||||
mixer: apu.mixer,
|
mixer: apu.mixer,
|
||||||
div_apu: apu.div_apu,
|
div_apu: apu.div_apu,
|
||||||
buffer: apu.buffer.clone(),
|
|
||||||
out_buffer: apu.out_buffer.clone(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const CYCLES_PER_FRAME: usize = 70224;
|
|
||||||
|
|
||||||
impl Apu {
|
impl Apu {
|
||||||
pub fn new(output: AudioOutput) -> Self {
|
pub fn new(output: AudioOutput) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
@ -83,8 +75,6 @@ impl Apu {
|
||||||
vin: VinEnable::default(),
|
vin: VinEnable::default(),
|
||||||
mixer: Mixer::default(),
|
mixer: Mixer::default(),
|
||||||
div_apu: 0,
|
div_apu: 0,
|
||||||
buffer: vec![],
|
|
||||||
out_buffer: vec![],
|
|
||||||
converter: Downsampler::new(output.sample_rate, output.downsample_type),
|
converter: Downsampler::new(output.sample_rate, output.downsample_type),
|
||||||
output,
|
output,
|
||||||
}
|
}
|
||||||
|
@ -97,8 +87,6 @@ impl Apu {
|
||||||
vin: state.vin,
|
vin: state.vin,
|
||||||
mixer: state.mixer,
|
mixer: state.mixer,
|
||||||
div_apu: state.div_apu,
|
div_apu: state.div_apu,
|
||||||
buffer: state.buffer,
|
|
||||||
out_buffer: state.out_buffer,
|
|
||||||
converter: Downsampler::new(output.sample_rate, output.downsample_type),
|
converter: Downsampler::new(output.sample_rate, output.downsample_type),
|
||||||
output,
|
output,
|
||||||
}
|
}
|
||||||
|
@ -132,25 +120,21 @@ impl Apu {
|
||||||
|
|
||||||
pub fn tick(&mut self, steps: usize, output: bool) {
|
pub fn tick(&mut self, steps: usize, output: bool) {
|
||||||
if output {
|
if output {
|
||||||
self.buffer.append(
|
for s in izip!(
|
||||||
&mut izip!(
|
self.channels.one.tick(steps).into_iter(),
|
||||||
self.channels.one.tick(steps).into_iter(),
|
self.channels.two.tick(steps).into_iter(),
|
||||||
self.channels.two.tick(steps).into_iter(),
|
self.channels.three.tick(steps).into_iter(),
|
||||||
self.channels.three.tick(steps).into_iter(),
|
self.channels.four.tick(steps).into_iter()
|
||||||
self.channels.four.tick(steps).into_iter()
|
)
|
||||||
)
|
.map(|(one, two, three, four)| DacSample {
|
||||||
.map(|(one, two, three, four)| DacSample {
|
one,
|
||||||
one,
|
two,
|
||||||
two,
|
three,
|
||||||
three,
|
four,
|
||||||
four,
|
}) {
|
||||||
})
|
if let Some(next) = self.converter.push(s.mixed(&self.mixer)) {
|
||||||
.collect(),
|
executor::block_on(self.output.send_rb.push(next)).unwrap();
|
||||||
);
|
}
|
||||||
if self.buffer.len() >= CYCLES_PER_FRAME {
|
|
||||||
self.next_audio();
|
|
||||||
} else if !self.out_buffer.is_empty() {
|
|
||||||
self.push_audio();
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.channels.one.tick(steps);
|
self.channels.one.tick(steps);
|
||||||
|
@ -168,34 +152,6 @@ impl Apu {
|
||||||
(self.channels.one.last & 0xF) | ((self.channels.two.last & 0xF0) << 4)
|
(self.channels.one.last & 0xF) | ((self.channels.two.last & 0xF0) << 4)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn next_audio(&mut self) {
|
|
||||||
self.out_buffer.append(
|
|
||||||
&mut self.converter.process(
|
|
||||||
self.buffer
|
|
||||||
.drain(..)
|
|
||||||
.map(|v| v.mixed(&self.mixer))
|
|
||||||
.collect::<Vec<[f32; 2]>>(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
self.push_audio();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn push_audio(&mut self) {
|
|
||||||
let length = if self.output.wait_for_output {
|
|
||||||
self.out_buffer.len()
|
|
||||||
} else {
|
|
||||||
self.out_buffer.len().min(self.output.send_rb.free_len())
|
|
||||||
};
|
|
||||||
|
|
||||||
executor::block_on(
|
|
||||||
self.output
|
|
||||||
.send_rb
|
|
||||||
.push_slice(&self.out_buffer.drain(..length).collect::<Vec<[f32; 2]>>()),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_buffer_full(&self) -> bool {
|
pub fn is_buffer_full(&self) -> bool {
|
||||||
self.output.send_rb.is_full()
|
self.output.send_rb.is_full()
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,22 +55,20 @@ impl Downsampler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process(&mut self, signal: Vec<[f32; 2]>) -> Vec<[f32; 2]> {
|
pub fn push(&mut self, signal: [f32; 2]) -> Option<[f32; 2]> {
|
||||||
let mut output = vec![];
|
self.time_accum += 1.;
|
||||||
for ref val in signal {
|
if let Some(ref mut averager) = self.average {
|
||||||
self.time_accum += 1.;
|
averager.push(&signal);
|
||||||
if let Some(ref mut averager) = self.average {
|
}
|
||||||
averager.push(val);
|
if self.time_accum >= self.ratio {
|
||||||
}
|
self.time_accum -= self.ratio;
|
||||||
if self.time_accum >= self.ratio {
|
Some(if let Some(ref mut averager) = self.average {
|
||||||
self.time_accum -= self.ratio;
|
averager.finish()
|
||||||
output.push(if let Some(ref mut averager) = self.average {
|
} else {
|
||||||
averager.finish()
|
signal
|
||||||
} else {
|
})
|
||||||
*val
|
} else {
|
||||||
});
|
None
|
||||||
}
|
|
||||||
}
|
}
|
||||||
output
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue