Skip to content

Commit 0d83583

Browse files
Add high-pass filter for sound
1 parent 3bebb7c commit 0d83583

File tree

5 files changed

+37
-8
lines changed

5 files changed

+37
-8
lines changed

core/src/env.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,9 @@ pub trait Peripherals {
1414
/// way to allow fast access.
1515
fn get_pressed_keys(&self) -> Keys;
1616

17-
fn offer_sound_sample(&mut self, f: impl FnOnce() -> f32);
17+
/// Is called regularly by the emulator (without fixed frequency, but on
18+
/// average above 100Mhz) to let the peripherals request an audio sample. It
19+
/// can call `f` at its own sample rate. It has to provide the sample rate
20+
/// to the function for certain audio filters within the emulator.
21+
fn offer_sound_sample(&mut self, f: impl FnOnce(f32) -> f32);
1822
}

core/src/lib.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,9 @@ impl Emulator {
9696
self.machine.dma_step();
9797

9898
self.machine.sound_controller.step();
99-
peripherals.offer_sound_sample(|| self.machine.sound_controller.output());
99+
peripherals.offer_sound_sample(|sample_rate| {
100+
self.machine.sound_controller.output(sample_rate)
101+
});
100102
}
101103

102104
// Handle input

core/src/machine/input.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ mod test {
161161
}
162162

163163
fn write_lcd_line(&mut self, _: u8, _: &[PixelColor; SCREEN_WIDTH]) {}
164-
fn offer_sound_sample(&mut self, _: impl FnOnce() -> f32) {}
164+
fn offer_sound_sample(&mut self, _: impl FnOnce(f32) -> f32) {}
165165
}
166166

167167
#[test]

core/src/machine/sound.rs

+24-3
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ pub(crate) struct SoundController {
3434
/// Hz). As the slowest clock we want to generate is 64Hz, this counter
3535
/// wraps at `1_048_576 / 64 = 16_384`.
3636
frame_sequencer: u32,
37+
38+
// For highpass filter.
39+
last_filtered_out: f32,
40+
last_unfiltered_out: f32,
3741
}
3842

3943
impl SoundController {
@@ -55,6 +59,9 @@ impl SoundController {
5559
square2: SquareChannel2::new(),
5660
wave: WaveChannel::new(),
5761
frame_sequencer: 0,
62+
63+
last_filtered_out: 0.0,
64+
last_unfiltered_out: 0.0,
5865
}
5966
}
6067

@@ -151,9 +158,23 @@ impl SoundController {
151158
self.wave.step();
152159
}
153160

154-
pub(crate) fn output(&self) -> f32 {
155-
// (self.counter as f32 * 2.0 * 3.1415926 / 2u16.pow(13) as f32).sin()
156-
self.wave.output() + self.square2.output()
161+
pub(crate) fn output(&mut self, sample_rate: f32) -> f32 {
162+
// The high-pass filter needs a parameter alpha which determines how
163+
// quickly the existing signal decays. This can be calculated from the
164+
// sample rate and the cutoff frequency. The Gameboy's cutoff frequency
165+
// is 60Hz according to https://github.com/bwhitman/pushpin/blob/master/src/gbsound.txt
166+
//
167+
// Resulting alpha for 60Hz is 0.9915, for 20Hz it's 0.9972.
168+
const CUTOFF: f32 = 60.0;
169+
let alpha = 1.0 / (2.0 * std::f32::consts::PI * 1.0 / sample_rate * CUTOFF + 1.0);
170+
171+
// We use a simple highpass filter to mainly remove the DC component.
172+
let unfiltered_out = self.wave.output() + self.square2.output();
173+
self.last_filtered_out = alpha * self.last_filtered_out
174+
+ alpha * (unfiltered_out - self.last_unfiltered_out);
175+
self.last_unfiltered_out = unfiltered_out;
176+
177+
self.last_filtered_out
157178
}
158179
}
159180

desktop/src/env.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ pub(crate) struct Env {
3939
audio_buffer: AudioBuffer,
4040
cycles_till_next_sample: f64,
4141
_stream: cpal::Stream,
42+
sample_rate: f32,
4243

4344
/// A fixed (set in `new`) value determining how many emulation cycles pass
4445
/// per host audio sample (without turbo mode).
@@ -71,6 +72,7 @@ impl Env {
7172
pixels,
7273
audio_buffer,
7374
_stream: stream,
75+
sample_rate: stream_config.sample_rate.0 as f32,
7476
cycles_till_next_sample,
7577
cycles_per_host_sample,
7678
})
@@ -108,9 +110,9 @@ impl Peripherals for Env {
108110
}
109111
}
110112

111-
fn offer_sound_sample(&mut self, f: impl FnOnce() -> f32) {
113+
fn offer_sound_sample(&mut self, f: impl FnOnce(f32) -> f32) {
112114
if self.cycles_till_next_sample <= 0.0 {
113-
self.audio_buffer.lock().unwrap().push(f());
115+
self.audio_buffer.lock().unwrap().push(f(self.sample_rate));
114116
self.cycles_till_next_sample += self.cycles_per_host_sample;
115117
}
116118
self.cycles_till_next_sample -= 1.0;

0 commit comments

Comments
 (0)