rolled my own downsampler
This commit is contained in:
parent
0459390d78
commit
28240c80c3
|
@ -132,15 +132,15 @@ fn create_audio_output() -> (AudioOutput, Stream) {
|
|||
|
||||
let sample_rate = config.sample_rate().0;
|
||||
|
||||
let (output, mut rx) = AudioOutput::new(sample_rate);
|
||||
let (output, mut rx) = AudioOutput::new(sample_rate as f32);
|
||||
|
||||
let stream = device
|
||||
.build_output_stream(
|
||||
&config.config(),
|
||||
move |data: &mut [f32], _info: &cpal::OutputCallbackInfo| {
|
||||
for v in data {
|
||||
for v in data.chunks_exact_mut(2) {
|
||||
match executor::block_on(rx.pop()) {
|
||||
Some(a) => *v = a,
|
||||
Some(a) => v.copy_from_slice(&a),
|
||||
None => panic!("Audio queue disconnected!"),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
rand = "0.8.5"
|
||||
samplerate = "0.2.4"
|
||||
async-ringbuf = "0.1.2"
|
||||
futures = "0.3.26"
|
||||
once_cell = "1.17.1"
|
||||
|
|
|
@ -24,18 +24,18 @@ pub trait Renderer {
|
|||
}
|
||||
|
||||
pub struct AudioOutput {
|
||||
pub sample_rate: u32,
|
||||
pub send_rb: AsyncHeapProducer<f32>,
|
||||
pub sample_rate: f32,
|
||||
pub send_rb: AsyncHeapProducer<[f32; 2]>,
|
||||
}
|
||||
|
||||
impl AudioOutput {
|
||||
pub fn new(sample_rate: u32) -> (Self, AsyncHeapConsumer<f32>) {
|
||||
let rb_len = sample_rate as usize / 60;
|
||||
pub fn new(sample_rate: f32) -> (Self, AsyncHeapConsumer<[f32; 2]>) {
|
||||
let rb_len = sample_rate as usize / (60 * 2);
|
||||
|
||||
let rb = AsyncHeapRb::<f32>::new(rb_len);
|
||||
let rb = AsyncHeapRb::<[f32; 2]>::new(rb_len);
|
||||
let (mut send_rb, rx) = rb.split();
|
||||
|
||||
executor::block_on(send_rb.push_iter(vec![0.; rb_len - 1].into_iter())).unwrap();
|
||||
executor::block_on(send_rb.push_iter(vec![[0.; 2]; rb_len - 1].into_iter())).unwrap();
|
||||
|
||||
(
|
||||
Self {
|
||||
|
|
|
@ -1,19 +1,21 @@
|
|||
use self::types::{Channels, DacSample, Mixer, VinEnable, Volume};
|
||||
use self::{
|
||||
downsampler::Downsampler,
|
||||
types::{Channels, DacSample, Mixer, VinEnable, Volume},
|
||||
};
|
||||
use crate::{
|
||||
connect::AudioOutput,
|
||||
constants::CLOCK_SPEED,
|
||||
processor::memory::Address,
|
||||
util::{get_bit, set_or_clear_bit},
|
||||
};
|
||||
use futures::executor;
|
||||
use itertools::izip;
|
||||
use samplerate::{ConverterType, Samplerate};
|
||||
|
||||
mod channels;
|
||||
mod downsampler;
|
||||
mod types;
|
||||
|
||||
impl DacSample {
|
||||
fn mixed(&self, mixer: &Mixer) -> Vec<f32> {
|
||||
fn mixed(&self, mixer: &Mixer) -> [f32; 2] {
|
||||
let left = (self.one * mixer.ch1.left.scale())
|
||||
+ (self.two * mixer.ch2.left.scale())
|
||||
+ (self.three * mixer.ch3.left.scale())
|
||||
|
@ -22,7 +24,7 @@ impl DacSample {
|
|||
+ (self.two * mixer.ch2.right.scale())
|
||||
+ (self.three * mixer.ch3.right.scale())
|
||||
+ (self.four * mixer.ch4.right.scale());
|
||||
vec![
|
||||
[
|
||||
self.mix_channel(left, mixer.vol_left),
|
||||
self.mix_channel(right, mixer.vol_right),
|
||||
]
|
||||
|
@ -40,7 +42,7 @@ pub struct Apu {
|
|||
mixer: Mixer,
|
||||
div_apu: u8,
|
||||
buffer: Vec<DacSample>,
|
||||
converter: Samplerate,
|
||||
converter: Downsampler,
|
||||
output: AudioOutput,
|
||||
}
|
||||
|
||||
|
@ -48,14 +50,6 @@ const CYCLES_PER_FRAME: usize = 70224;
|
|||
|
||||
impl Apu {
|
||||
pub fn new(output: AudioOutput) -> Self {
|
||||
let converter = Samplerate::new(
|
||||
ConverterType::ZeroOrderHold,
|
||||
CLOCK_SPEED as u32,
|
||||
output.sample_rate,
|
||||
2,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
Self {
|
||||
apu_enable: true,
|
||||
channels: Channels::default(),
|
||||
|
@ -63,7 +57,7 @@ impl Apu {
|
|||
mixer: Mixer::default(),
|
||||
div_apu: 0,
|
||||
buffer: vec![],
|
||||
converter,
|
||||
converter: Downsampler::new(output.sample_rate),
|
||||
output,
|
||||
}
|
||||
}
|
||||
|
@ -111,16 +105,12 @@ impl Apu {
|
|||
}
|
||||
|
||||
fn next_audio(&mut self) {
|
||||
let converted = self
|
||||
.converter
|
||||
.process(
|
||||
&self
|
||||
.buffer
|
||||
let converted = self.converter.process(
|
||||
self.buffer
|
||||
.drain(..)
|
||||
.flat_map(|v| v.mixed(&self.mixer))
|
||||
.collect::<Vec<f32>>(),
|
||||
)
|
||||
.unwrap();
|
||||
.map(|v| v.mixed(&self.mixer))
|
||||
.collect::<Vec<[f32; 2]>>(),
|
||||
);
|
||||
executor::block_on(self.output.send_rb.push_slice(&converted)).unwrap();
|
||||
}
|
||||
|
||||
|
|
29
lib/src/processor/memory/mmio/apu/downsampler.rs
Normal file
29
lib/src/processor/memory/mmio/apu/downsampler.rs
Normal file
|
@ -0,0 +1,29 @@
|
|||
use crate::constants::CLOCK_SPEED;
|
||||
|
||||
const TIME_PER_CYCLE: f32 = 1. / CLOCK_SPEED as f32;
|
||||
|
||||
pub(super) struct Downsampler {
|
||||
ratio: f32,
|
||||
time_accum: f32,
|
||||
}
|
||||
|
||||
impl Downsampler {
|
||||
pub fn new(sample_rate: f32) -> Self {
|
||||
Self {
|
||||
ratio: 1. / sample_rate,
|
||||
time_accum: 0.,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn process(&mut self, signal: Vec<[f32; 2]>) -> Vec<[f32; 2]> {
|
||||
let mut output = vec![];
|
||||
for val in signal {
|
||||
self.time_accum += TIME_PER_CYCLE;
|
||||
if self.time_accum >= self.ratio {
|
||||
self.time_accum = 0.;
|
||||
output.push(val);
|
||||
}
|
||||
}
|
||||
output
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue