experimenting with high- and low-pass filters. not sure if they're right.
This commit is contained in:
parent
e36bfd1821
commit
dbccee8edd
113
src/audio.rs
113
src/audio.rs
|
@ -1,3 +1,20 @@
|
|||
/*
|
||||
First order low-pass filter equation: H(s)=1/(τs+1). H(s) is output,
|
||||
|
||||
low pass filter:
|
||||
y = (1 - gamma) * y + gamma * x
|
||||
|
||||
high pass filter:
|
||||
y[i] := gamma * y[i−1] + gamma * (x[i] − x[i−1])
|
||||
|
||||
fc = 44100 = sample frequency
|
||||
Ts = 1/44100 = sample period
|
||||
fc = 14000 = cutoff frequency
|
||||
gamma = 1 - (e ^ (-2pi * fc / fs))
|
||||
*/
|
||||
|
||||
use std::f32::consts::{E, PI};
|
||||
|
||||
extern crate sdl2;
|
||||
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
@ -10,11 +27,62 @@ const SDL_SAMPLE_RATE: i32 = 44_100;
|
|||
// devices and then sleeping. So the audio device is set to play 44,100 samples per second, and grab them in 60 intervals over the course of that second.
|
||||
const SAMPLES_PER_FRAME: u16 = SDL_SAMPLE_RATE as u16/60;
|
||||
|
||||
// struct LowPass {
|
||||
// cutoff_freq: f32,
|
||||
// gamma: f32,
|
||||
// previous_input: f32,
|
||||
// previous_out: f32,
|
||||
// }
|
||||
|
||||
// struct HighPass {
|
||||
// cutoff_freq: f32,
|
||||
// gamma: f32,
|
||||
// previous_input: f32,
|
||||
// previous_out: f32,
|
||||
// }
|
||||
|
||||
// impl HighPass {
|
||||
// fn filter(&self, sample: f32) -> f32
|
||||
// }
|
||||
|
||||
fn get_gamma(cutoff_freq: f32) -> f32 {
|
||||
1. - (E.powf(-2. * PI * cutoff_freq / (SDL_SAMPLE_RATE as f32)))
|
||||
}
|
||||
|
||||
pub struct ApuSampler {
|
||||
// This buffer receives all of the raw audio produced by the APU.
|
||||
// The callback will take what it needs when it needs it and truncate the buffer for smooth audio output.
|
||||
buffer: Arc<Mutex<Vec<f32>>>,
|
||||
sample_ratio: f32,
|
||||
|
||||
prev_input_90Hz: f32,
|
||||
prev_output_90Hz: f32,
|
||||
gamma_90Hz: f32,
|
||||
|
||||
prev_input_440Hz: f32,
|
||||
prev_output_440Hz: f32,
|
||||
gamma_440Hz: f32,
|
||||
|
||||
prev_input_14kHz: f32,
|
||||
prev_output_14kHz: f32,
|
||||
gamma_14kHz: f32,
|
||||
}
|
||||
|
||||
impl ApuSampler {
|
||||
|
||||
fn high_pass_90Hz(&self, sample: f32) -> f32 {
|
||||
// y[i] := α × y[i−1] + α × (x[i] − x[i−1])
|
||||
(self.gamma_90Hz * self.prev_output_90Hz) + (sample - self.prev_input_90Hz)
|
||||
}
|
||||
|
||||
fn high_pass_440Hz(&self, sample: f32) -> f32 {
|
||||
(self.gamma_440Hz * self.prev_output_440Hz) + (sample - self.prev_input_440Hz)
|
||||
}
|
||||
|
||||
fn low_pass_14kHz(&self, sample: f32) -> f32 {
|
||||
((1. - self.gamma_14kHz) * self.prev_output_14kHz) + (self.gamma_14kHz * sample)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl AudioCallback for ApuSampler {
|
||||
|
@ -28,7 +96,24 @@ impl AudioCallback for ApuSampler {
|
|||
for (i, x) in out.iter_mut().enumerate() {
|
||||
let sample_idx = ((i as f32) * self.sample_ratio) as usize;
|
||||
if sample_idx < b.len() {
|
||||
*x = b[sample_idx];
|
||||
let sample = b[sample_idx];
|
||||
|
||||
let filtered_90Hz = self.high_pass_90Hz(sample);
|
||||
self.prev_input_90Hz = sample;
|
||||
self.prev_output_90Hz = filtered_90Hz;
|
||||
// *x = filtered_90Hz;
|
||||
|
||||
let filtered_440Hz = self.high_pass_440Hz(filtered_90Hz);
|
||||
self.prev_input_440Hz = filtered_90Hz;
|
||||
self.prev_output_440Hz = filtered_440Hz;
|
||||
// *x = filtered_440Hz;
|
||||
|
||||
let filtered_14kHz = self.low_pass_14kHz(filtered_440Hz);
|
||||
self.prev_input_14kHz = filtered_440Hz;
|
||||
self.prev_output_14kHz = filtered_14kHz;
|
||||
*x = filtered_14kHz;
|
||||
|
||||
// *x = sample;
|
||||
}
|
||||
}
|
||||
let l = b.len();
|
||||
|
@ -53,6 +138,30 @@ pub fn initialize(sdl_context: &Sdl, buffer: Arc<Mutex<Vec<f32>>>)
|
|||
};
|
||||
audio_subsystem.open_playback(None, &desired_spec, |_spec| {
|
||||
// println!("{:?}", _spec);
|
||||
ApuSampler{buffer, sample_ratio: APU_SAMPLE_RATE / (SDL_SAMPLE_RATE as f32)}
|
||||
ApuSampler{
|
||||
buffer,
|
||||
sample_ratio: APU_SAMPLE_RATE / (SDL_SAMPLE_RATE as f32),
|
||||
|
||||
prev_input_90Hz: 0.,
|
||||
prev_output_90Hz: 0.,
|
||||
gamma_90Hz: 1.-get_gamma(90.),
|
||||
prev_input_440Hz: 0.,
|
||||
prev_output_440Hz: 0.,
|
||||
gamma_440Hz: 1.-get_gamma(440.),
|
||||
prev_input_14kHz: 0.,
|
||||
prev_output_14kHz: 0.,
|
||||
gamma_14kHz: get_gamma(14_000.),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::get_gamma;
|
||||
#[test]
|
||||
fn show_gamma_values() {
|
||||
for i in [0, 100, 1000, 10000, 100000].iter() {
|
||||
println!("gamma for cutoff frequency {}: {}", i, get_gamma(*i as f32));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue