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 gb_emu_lib::connect::{AudioOutput, DownsampleType};
|
||||
|
||||
const FRAMES_TO_BUFFER: usize = 1;
|
||||
const BUFFERS_PER_FRAME: usize = 100;
|
||||
const DOWNSAMPLE_TYPE: DownsampleType = DownsampleType::ZeroOrderHold;
|
||||
|
||||
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 (output, mut rx) =
|
||||
AudioOutput::new(sample_rate as f32, true, FRAMES_TO_BUFFER, DOWNSAMPLE_TYPE);
|
||||
let (output, mut rx) = AudioOutput::new(sample_rate as f32, BUFFERS_PER_FRAME, DOWNSAMPLE_TYPE);
|
||||
|
||||
let stream = if muted {
|
||||
device
|
||||
|
|
|
@ -67,8 +67,8 @@ pub struct GameboyEmu {
|
|||
type FrameReceiver = Mutex<Option<Receiver<Vec<[u8; 4]>>>>;
|
||||
type JoypadSender = Mutex<Option<Sender<(JoypadButtons, bool)>>>;
|
||||
|
||||
const FRAMES_TO_BUFFER: usize = 1;
|
||||
const DOWNSAMPLE_TYPE: DownsampleType = DownsampleType::ZeroOrderHold;
|
||||
const BUFFERS_PER_FRAME: usize = 1;
|
||||
const DOWNSAMPLE_TYPE: DownsampleType = DownsampleType::Linear;
|
||||
|
||||
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 {
|
||||
let (output, rx) = AudioOutput::new(
|
||||
buffer_config.sample_rate,
|
||||
false,
|
||||
FRAMES_TO_BUFFER,
|
||||
BUFFERS_PER_FRAME,
|
||||
DOWNSAMPLE_TYPE,
|
||||
);
|
||||
|
||||
|
@ -232,8 +231,7 @@ impl Plugin for GameboyEmu {
|
|||
|
||||
let (output, rx) = AudioOutput::new(
|
||||
buffer_config.sample_rate,
|
||||
false,
|
||||
FRAMES_TO_BUFFER,
|
||||
BUFFERS_PER_FRAME,
|
||||
DOWNSAMPLE_TYPE,
|
||||
);
|
||||
|
||||
|
|
|
@ -42,18 +42,16 @@ pub trait Renderer<Format: From<Colour>> {
|
|||
pub struct AudioOutput {
|
||||
pub sample_rate: f32,
|
||||
pub send_rb: AsyncHeapProducer<[f32; 2]>,
|
||||
pub wait_for_output: bool,
|
||||
pub downsample_type: DownsampleType,
|
||||
}
|
||||
|
||||
impl AudioOutput {
|
||||
pub fn new(
|
||||
sample_rate: f32,
|
||||
wait_for_output: bool,
|
||||
frames_to_buffer: usize,
|
||||
buffers_per_frame: usize,
|
||||
downsample_type: DownsampleType,
|
||||
) -> (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 (send_rb, rx) = rb.split();
|
||||
|
@ -62,7 +60,6 @@ impl AudioOutput {
|
|||
Self {
|
||||
sample_rate,
|
||||
send_rb,
|
||||
wait_for_output,
|
||||
downsample_type,
|
||||
},
|
||||
rx,
|
||||
|
|
|
@ -42,8 +42,6 @@ pub struct Apu {
|
|||
vin: VinEnable,
|
||||
mixer: Mixer,
|
||||
div_apu: u8,
|
||||
buffer: Vec<DacSample>,
|
||||
out_buffer: Vec<[f32; 2]>,
|
||||
converter: Downsampler,
|
||||
output: AudioOutput,
|
||||
}
|
||||
|
@ -55,8 +53,6 @@ pub struct ApuSaveState {
|
|||
vin: VinEnable,
|
||||
mixer: Mixer,
|
||||
div_apu: u8,
|
||||
buffer: Vec<DacSample>,
|
||||
out_buffer: Vec<[f32; 2]>,
|
||||
}
|
||||
|
||||
impl ApuSaveState {
|
||||
|
@ -67,14 +63,10 @@ impl ApuSaveState {
|
|||
vin: apu.vin,
|
||||
mixer: apu.mixer,
|
||||
div_apu: apu.div_apu,
|
||||
buffer: apu.buffer.clone(),
|
||||
out_buffer: apu.out_buffer.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const CYCLES_PER_FRAME: usize = 70224;
|
||||
|
||||
impl Apu {
|
||||
pub fn new(output: AudioOutput) -> Self {
|
||||
Self {
|
||||
|
@ -83,8 +75,6 @@ impl Apu {
|
|||
vin: VinEnable::default(),
|
||||
mixer: Mixer::default(),
|
||||
div_apu: 0,
|
||||
buffer: vec![],
|
||||
out_buffer: vec![],
|
||||
converter: Downsampler::new(output.sample_rate, output.downsample_type),
|
||||
output,
|
||||
}
|
||||
|
@ -97,8 +87,6 @@ impl Apu {
|
|||
vin: state.vin,
|
||||
mixer: state.mixer,
|
||||
div_apu: state.div_apu,
|
||||
buffer: state.buffer,
|
||||
out_buffer: state.out_buffer,
|
||||
converter: Downsampler::new(output.sample_rate, output.downsample_type),
|
||||
output,
|
||||
}
|
||||
|
@ -132,8 +120,7 @@ impl Apu {
|
|||
|
||||
pub fn tick(&mut self, steps: usize, output: bool) {
|
||||
if output {
|
||||
self.buffer.append(
|
||||
&mut izip!(
|
||||
for s in izip!(
|
||||
self.channels.one.tick(steps).into_iter(),
|
||||
self.channels.two.tick(steps).into_iter(),
|
||||
self.channels.three.tick(steps).into_iter(),
|
||||
|
@ -144,13 +131,10 @@ impl Apu {
|
|||
two,
|
||||
three,
|
||||
four,
|
||||
})
|
||||
.collect(),
|
||||
);
|
||||
if self.buffer.len() >= CYCLES_PER_FRAME {
|
||||
self.next_audio();
|
||||
} else if !self.out_buffer.is_empty() {
|
||||
self.push_audio();
|
||||
}) {
|
||||
if let Some(next) = self.converter.push(s.mixed(&self.mixer)) {
|
||||
executor::block_on(self.output.send_rb.push(next)).unwrap();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.channels.one.tick(steps);
|
||||
|
@ -168,34 +152,6 @@ impl Apu {
|
|||
(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 {
|
||||
self.output.send_rb.is_full()
|
||||
}
|
||||
|
|
|
@ -55,22 +55,20 @@ impl Downsampler {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn process(&mut self, signal: Vec<[f32; 2]>) -> Vec<[f32; 2]> {
|
||||
let mut output = vec![];
|
||||
for ref val in signal {
|
||||
pub fn push(&mut self, signal: [f32; 2]) -> Option<[f32; 2]> {
|
||||
self.time_accum += 1.;
|
||||
if let Some(ref mut averager) = self.average {
|
||||
averager.push(val);
|
||||
averager.push(&signal);
|
||||
}
|
||||
if self.time_accum >= self.ratio {
|
||||
self.time_accum -= self.ratio;
|
||||
output.push(if let Some(ref mut averager) = self.average {
|
||||
Some(if let Some(ref mut averager) = self.average {
|
||||
averager.finish()
|
||||
} else {
|
||||
*val
|
||||
});
|
||||
signal
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
output
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue