different downsample types
This commit is contained in:
parent
bfb895ef4c
commit
659fc4869a
|
@ -9,7 +9,7 @@ use cpal::{
|
||||||
};
|
};
|
||||||
use futures::executor;
|
use futures::executor;
|
||||||
use gb_emu_lib::{
|
use gb_emu_lib::{
|
||||||
connect::{AudioOutput, EmulatorMessage, JoypadState, Renderer, RomFile},
|
connect::{AudioOutput, DownsampleType, EmulatorMessage, JoypadState, Renderer, RomFile},
|
||||||
util::scale_buffer,
|
util::scale_buffer,
|
||||||
EmulatorCore,
|
EmulatorCore,
|
||||||
};
|
};
|
||||||
|
@ -115,6 +115,9 @@ fn main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const FRAMES_TO_BUFFER: usize = 1;
|
||||||
|
const DOWNSAMPLE_TYPE: DownsampleType = DownsampleType::ZeroOrderHold;
|
||||||
|
|
||||||
fn create_audio_output() -> (AudioOutput, Stream) {
|
fn create_audio_output() -> (AudioOutput, Stream) {
|
||||||
let host = cpal::default_host();
|
let host = cpal::default_host();
|
||||||
|
|
||||||
|
@ -132,7 +135,8 @@ fn create_audio_output() -> (AudioOutput, Stream) {
|
||||||
|
|
||||||
let sample_rate = config.sample_rate().0;
|
let sample_rate = config.sample_rate().0;
|
||||||
|
|
||||||
let (output, mut rx) = AudioOutput::new_unfilled(sample_rate as f32, true, 1);
|
let (output, mut rx) =
|
||||||
|
AudioOutput::new_unfilled(sample_rate as f32, true, FRAMES_TO_BUFFER, DOWNSAMPLE_TYPE);
|
||||||
|
|
||||||
let stream = device
|
let stream = device
|
||||||
.build_output_stream(
|
.build_output_stream(
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use async_ringbuf::AsyncHeapConsumer;
|
use async_ringbuf::AsyncHeapConsumer;
|
||||||
use futures::executor;
|
use futures::executor;
|
||||||
use gb_emu_lib::{
|
use gb_emu_lib::{
|
||||||
connect::{AudioOutput, EmulatorMessage, JoypadButtons, RomFile},
|
connect::{AudioOutput, DownsampleType, EmulatorMessage, JoypadButtons, RomFile},
|
||||||
EmulatorCore,
|
EmulatorCore,
|
||||||
};
|
};
|
||||||
use nih_plug::prelude::*;
|
use nih_plug::prelude::*;
|
||||||
|
@ -34,6 +34,7 @@ type JoypadSender = Mutex<Option<Sender<(JoypadButtons, bool)>>>;
|
||||||
|
|
||||||
const FRAMES_TO_BUFFER: usize = 1;
|
const FRAMES_TO_BUFFER: usize = 1;
|
||||||
const INCLUDE_BOOTROM: bool = false;
|
const INCLUDE_BOOTROM: bool = false;
|
||||||
|
const DOWNSAMPLE_TYPE: DownsampleType = DownsampleType::ZeroOrderHold;
|
||||||
|
|
||||||
impl Plugin for GameboyEmu {
|
impl Plugin for GameboyEmu {
|
||||||
const NAME: &'static str = "Gameboy";
|
const NAME: &'static str = "Gameboy";
|
||||||
|
@ -128,16 +129,24 @@ impl Plugin for GameboyEmu {
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(ref mut vars) = self.vars {
|
if let Some(ref mut vars) = self.vars {
|
||||||
let (output, rx) =
|
let (output, rx) = AudioOutput::new_unfilled(
|
||||||
AudioOutput::new_unfilled(buffer_config.sample_rate, false, FRAMES_TO_BUFFER);
|
buffer_config.sample_rate,
|
||||||
|
false,
|
||||||
|
FRAMES_TO_BUFFER,
|
||||||
|
DOWNSAMPLE_TYPE,
|
||||||
|
);
|
||||||
|
|
||||||
vars.emulator_core.replace_output(output);
|
vars.emulator_core.replace_output(output);
|
||||||
vars.rx = rx;
|
vars.rx = rx;
|
||||||
} else {
|
} else {
|
||||||
let (sender, receiver) = channel::<EmulatorMessage>();
|
let (sender, receiver) = channel::<EmulatorMessage>();
|
||||||
|
|
||||||
let (output, rx) =
|
let (output, rx) = AudioOutput::new_unfilled(
|
||||||
AudioOutput::new_unfilled(buffer_config.sample_rate, false, FRAMES_TO_BUFFER);
|
buffer_config.sample_rate,
|
||||||
|
false,
|
||||||
|
FRAMES_TO_BUFFER,
|
||||||
|
DOWNSAMPLE_TYPE,
|
||||||
|
);
|
||||||
|
|
||||||
let (renderer, frame_receiver, key_handler) = EmulatorRenderer::new();
|
let (renderer, frame_receiver, key_handler) = EmulatorRenderer::new();
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,12 @@ pub enum EmulatorMessage {
|
||||||
Stop,
|
Stop,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum DownsampleType {
|
||||||
|
Linear,
|
||||||
|
ZeroOrderHold,
|
||||||
|
}
|
||||||
|
|
||||||
pub enum RomFile {
|
pub enum RomFile {
|
||||||
Path(String),
|
Path(String),
|
||||||
Raw(Vec<u8>),
|
Raw(Vec<u8>),
|
||||||
|
@ -43,6 +49,7 @@ 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 wait_for_output: bool,
|
||||||
|
pub downsample_type: DownsampleType,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AudioOutput {
|
impl AudioOutput {
|
||||||
|
@ -50,8 +57,14 @@ impl AudioOutput {
|
||||||
sample_rate: f32,
|
sample_rate: f32,
|
||||||
wait_for_output: bool,
|
wait_for_output: bool,
|
||||||
frames_to_buffer: usize,
|
frames_to_buffer: usize,
|
||||||
|
downsample_type: DownsampleType,
|
||||||
) -> (Self, AsyncHeapConsumer<[f32; 2]>) {
|
) -> (Self, AsyncHeapConsumer<[f32; 2]>) {
|
||||||
let (mut output, rx) = Self::new_unfilled(sample_rate, wait_for_output, frames_to_buffer);
|
let (mut output, rx) = Self::new_unfilled(
|
||||||
|
sample_rate,
|
||||||
|
wait_for_output,
|
||||||
|
frames_to_buffer,
|
||||||
|
downsample_type,
|
||||||
|
);
|
||||||
|
|
||||||
executor::block_on(
|
executor::block_on(
|
||||||
output
|
output
|
||||||
|
@ -67,6 +80,7 @@ impl AudioOutput {
|
||||||
sample_rate: f32,
|
sample_rate: f32,
|
||||||
wait_for_output: bool,
|
wait_for_output: bool,
|
||||||
frames_to_buffer: usize,
|
frames_to_buffer: usize,
|
||||||
|
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) * frames_to_buffer;
|
||||||
|
|
||||||
|
@ -78,6 +92,7 @@ impl AudioOutput {
|
||||||
sample_rate,
|
sample_rate,
|
||||||
send_rb,
|
send_rb,
|
||||||
wait_for_output,
|
wait_for_output,
|
||||||
|
downsample_type,
|
||||||
},
|
},
|
||||||
rx,
|
rx,
|
||||||
)
|
)
|
||||||
|
|
|
@ -59,13 +59,13 @@ impl Apu {
|
||||||
div_apu: 0,
|
div_apu: 0,
|
||||||
buffer: vec![],
|
buffer: vec![],
|
||||||
out_buffer: vec![],
|
out_buffer: vec![],
|
||||||
converter: Downsampler::new(output.sample_rate),
|
converter: Downsampler::new(output.sample_rate, output.downsample_type),
|
||||||
output,
|
output,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn replace_output(&mut self, new: AudioOutput) {
|
pub fn replace_output(&mut self, new: AudioOutput) {
|
||||||
self.converter = Downsampler::new(new.sample_rate);
|
self.converter = Downsampler::new(new.sample_rate, new.downsample_type);
|
||||||
self.output = new;
|
self.output = new;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::constants::CLOCK_SPEED;
|
use crate::{connect::DownsampleType, constants::CLOCK_SPEED};
|
||||||
|
|
||||||
const TIME_PER_CYCLE: f32 = 1. / CLOCK_SPEED as f32;
|
const TIME_PER_CYCLE: f32 = 1. / CLOCK_SPEED as f32;
|
||||||
|
|
||||||
|
@ -42,15 +42,18 @@ impl Averager {
|
||||||
pub(super) struct Downsampler {
|
pub(super) struct Downsampler {
|
||||||
ratio: f32,
|
ratio: f32,
|
||||||
time_accum: f32,
|
time_accum: f32,
|
||||||
average: Averager,
|
average: Option<Averager>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Downsampler {
|
impl Downsampler {
|
||||||
pub fn new(sample_rate: f32) -> Self {
|
pub fn new(sample_rate: f32, algo: DownsampleType) -> Self {
|
||||||
Self {
|
Self {
|
||||||
ratio: 1. / sample_rate,
|
ratio: 1. / sample_rate,
|
||||||
time_accum: 0.,
|
time_accum: 0.,
|
||||||
average: Averager::default(),
|
average: match algo {
|
||||||
|
DownsampleType::Linear => Some(Averager::default()),
|
||||||
|
DownsampleType::ZeroOrderHold => None,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,10 +61,16 @@ impl Downsampler {
|
||||||
let mut output = vec![];
|
let mut output = vec![];
|
||||||
for ref val in signal {
|
for ref val in signal {
|
||||||
self.time_accum += TIME_PER_CYCLE;
|
self.time_accum += TIME_PER_CYCLE;
|
||||||
self.average.push(val);
|
if let Some(ref mut averager) = self.average {
|
||||||
|
averager.push(val);
|
||||||
|
}
|
||||||
if self.time_accum >= self.ratio {
|
if self.time_accum >= self.ratio {
|
||||||
self.time_accum = 0.;
|
self.time_accum = 0.;
|
||||||
output.push(self.average.finish());
|
output.push(if let Some(ref mut averager) = self.average {
|
||||||
|
averager.finish()
|
||||||
|
} else {
|
||||||
|
*val
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
output
|
output
|
||||||
|
|
Loading…
Reference in a new issue