Add simple resampling using cosine interpolation, seems to work~ish.
Still have clicks & delays in my audio output. Former-commit-id: 46707fc773d8083fbba1ec614e3e0a3e53866b98
This commit is contained in:
parent
ac9a17713b
commit
b026ad4ed3
|
@ -7,6 +7,7 @@ use super::cartridge::Cartridge;
|
||||||
use super::gpu::*;
|
use super::gpu::*;
|
||||||
use super::interrupt::*;
|
use super::interrupt::*;
|
||||||
use super::iodev::*;
|
use super::iodev::*;
|
||||||
|
use super::sound::SoundController;
|
||||||
use super::sysbus::SysBus;
|
use super::sysbus::SysBus;
|
||||||
|
|
||||||
use super::super::{AudioInterface, InputInterface, VideoInterface};
|
use super::super::{AudioInterface, InputInterface, VideoInterface};
|
||||||
|
@ -28,7 +29,8 @@ impl GameBoyAdvance {
|
||||||
audio_device: Rc<RefCell<dyn AudioInterface>>,
|
audio_device: Rc<RefCell<dyn AudioInterface>>,
|
||||||
input_device: Rc<RefCell<dyn InputInterface>>,
|
input_device: Rc<RefCell<dyn InputInterface>>,
|
||||||
) -> GameBoyAdvance {
|
) -> GameBoyAdvance {
|
||||||
let io = IoDevices::new();
|
let sound_controller = SoundController::new(audio_device.clone());
|
||||||
|
let io = IoDevices::new(sound_controller);
|
||||||
GameBoyAdvance {
|
GameBoyAdvance {
|
||||||
cpu: cpu,
|
cpu: cpu,
|
||||||
sysbus: Box::new(SysBus::new(io, bios_rom, gamepak)),
|
sysbus: Box::new(SysBus::new(io, bios_rom, gamepak)),
|
||||||
|
@ -119,7 +121,6 @@ impl GameBoyAdvance {
|
||||||
}
|
}
|
||||||
|
|
||||||
io.intc.request_irqs(irqs);
|
io.intc.request_irqs(irqs);
|
||||||
let mut audio_device = self.audio_device.borrow_mut();
|
io.sound.update(self.cpu.cycles);
|
||||||
io.sound.update(self.cpu.cycles, &mut (*audio_device));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@ pub enum HaltState {
|
||||||
Stop, // In Stop mode, most of the hardware including sound and video are paused
|
Stop, // In Stop mode, most of the hardware including sound and video are paused
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct IoDevices {
|
pub struct IoDevices {
|
||||||
pub intc: InterruptController,
|
pub intc: InterruptController,
|
||||||
pub gpu: Gpu,
|
pub gpu: Gpu,
|
||||||
|
@ -33,10 +32,10 @@ pub struct IoDevices {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IoDevices {
|
impl IoDevices {
|
||||||
pub fn new() -> IoDevices {
|
pub fn new(sound_controller: SoundController) -> IoDevices {
|
||||||
IoDevices {
|
IoDevices {
|
||||||
gpu: Gpu::new(),
|
gpu: Gpu::new(),
|
||||||
sound: SoundController::new(),
|
sound: sound_controller,
|
||||||
timers: Timers::new(),
|
timers: Timers::new(),
|
||||||
dmac: DmaController::new(),
|
dmac: DmaController::new(),
|
||||||
intc: InterruptController::new(),
|
intc: InterruptController::new(),
|
||||||
|
|
51
src/core/sound/dsp.rs
Normal file
51
src/core/sound/dsp.rs
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
pub type Sample = (i16, i16);
|
||||||
|
const PI: f32 = std::f32::consts::PI;
|
||||||
|
|
||||||
|
pub trait Resampler {
|
||||||
|
fn push_sample(&mut self, s: Sample, output: &mut Vec<i16>);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CosineResampler {
|
||||||
|
last_in_sample: Sample,
|
||||||
|
phase: f32,
|
||||||
|
pub in_freq: f32,
|
||||||
|
out_freq: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cosine_interpolation(y1: Sample, y2: Sample, phase: f32) -> Sample {
|
||||||
|
let y1_left = y1.0 as f32;
|
||||||
|
let y1_right = y1.1 as f32;
|
||||||
|
let y2_left = y2.0 as f32;
|
||||||
|
let y2_right = y2.1 as f32;
|
||||||
|
|
||||||
|
let mu2 = (1.0 - (PI * phase).cos()) / 2.0;
|
||||||
|
|
||||||
|
(
|
||||||
|
(y2_left * (1.0 - mu2) + y1_left * mu2) as i16,
|
||||||
|
(y2_right * (1.0 - mu2) + y1_right * mu2) as i16,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Resampler for CosineResampler {
|
||||||
|
fn push_sample(&mut self, s: Sample, output: &mut Vec<i16>) {
|
||||||
|
while self.phase < 1.0 {
|
||||||
|
let x = cosine_interpolation(self.last_in_sample, s, self.phase);
|
||||||
|
output.push(x.0);
|
||||||
|
output.push(x.1);
|
||||||
|
self.phase += self.in_freq / self.out_freq;
|
||||||
|
}
|
||||||
|
self.phase = self.phase - 1.0;
|
||||||
|
self.last_in_sample = s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CosineResampler {
|
||||||
|
pub fn new(in_freq: f32, out_freq: f32) -> CosineResampler {
|
||||||
|
CosineResampler {
|
||||||
|
last_in_sample: Default::default(),
|
||||||
|
phase: 0.0,
|
||||||
|
in_freq: in_freq,
|
||||||
|
out_freq: out_freq,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,6 @@
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
use bit::BitIndex;
|
use bit::BitIndex;
|
||||||
|
|
||||||
use super::dma::DmaController;
|
use super::dma::DmaController;
|
||||||
|
@ -8,8 +11,10 @@ use crate::AudioInterface;
|
||||||
mod fifo;
|
mod fifo;
|
||||||
use fifo::SoundFifo;
|
use fifo::SoundFifo;
|
||||||
|
|
||||||
|
mod dsp;
|
||||||
|
use dsp::{CosineResampler, Resampler};
|
||||||
|
|
||||||
const DMG_RATIOS: [f32; 4] = [0.25, 0.5, 1.0, 0.0];
|
const DMG_RATIOS: [f32; 4] = [0.25, 0.5, 1.0, 0.0];
|
||||||
const DMA_RATIOS: [f32; 2] = [0.5, 1.0];
|
|
||||||
const DMA_TIMERS: [usize; 2] = [0, 1];
|
const DMA_TIMERS: [usize; 2] = [0, 1];
|
||||||
const DUTY_RATIOS: [f32; 4] = [0.125, 0.25, 0.5, 0.75];
|
const DUTY_RATIOS: [f32; 4] = [0.125, 0.25, 0.5, 0.75];
|
||||||
|
|
||||||
|
@ -19,7 +24,7 @@ struct NoiseChannel {}
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct DmaSoundChannel {
|
struct DmaSoundChannel {
|
||||||
value: i8,
|
value: i8,
|
||||||
volume: f32,
|
volume_shift: i16,
|
||||||
enable_right: bool,
|
enable_right: bool,
|
||||||
enable_left: bool,
|
enable_left: bool,
|
||||||
timer_select: usize,
|
timer_select: usize,
|
||||||
|
@ -29,7 +34,7 @@ struct DmaSoundChannel {
|
||||||
impl Default for DmaSoundChannel {
|
impl Default for DmaSoundChannel {
|
||||||
fn default() -> DmaSoundChannel {
|
fn default() -> DmaSoundChannel {
|
||||||
DmaSoundChannel {
|
DmaSoundChannel {
|
||||||
volume: DMA_RATIOS[0],
|
volume_shift: 1,
|
||||||
value: 0,
|
value: 0,
|
||||||
enable_right: false,
|
enable_right: false,
|
||||||
enable_left: false,
|
enable_left: false,
|
||||||
|
@ -45,8 +50,9 @@ const REG_FIFO_A_H: u32 = REG_FIFO_A + 2;
|
||||||
const REG_FIFO_B_L: u32 = REG_FIFO_B;
|
const REG_FIFO_B_L: u32 = REG_FIFO_B;
|
||||||
const REG_FIFO_B_H: u32 = REG_FIFO_B + 2;
|
const REG_FIFO_B_H: u32 = REG_FIFO_B + 2;
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct SoundController {
|
pub struct SoundController {
|
||||||
|
audio_device: Rc<RefCell<dyn AudioInterface>>,
|
||||||
|
|
||||||
sample_rate_to_cpu_freq: usize, // how many "cycles" are a sample?
|
sample_rate_to_cpu_freq: usize, // how many "cycles" are a sample?
|
||||||
last_sample_cycles: usize, // cycles count when we last provided a new sample.
|
last_sample_cycles: usize, // cycles count when we last provided a new sample.
|
||||||
|
|
||||||
|
@ -79,12 +85,17 @@ pub struct SoundController {
|
||||||
|
|
||||||
dma_sound: [DmaSoundChannel; 2],
|
dma_sound: [DmaSoundChannel; 2],
|
||||||
|
|
||||||
pub output_buffer: Vec<i8>,
|
resampler: CosineResampler,
|
||||||
|
pub output_buffer: Vec<i16>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SoundController {
|
impl SoundController {
|
||||||
pub fn new() -> SoundController {
|
pub fn new(audio_device: Rc<RefCell<dyn AudioInterface>>) -> SoundController {
|
||||||
|
let resampler =
|
||||||
|
CosineResampler::new(32768_f32, audio_device.borrow().get_sample_rate() as f32);
|
||||||
SoundController {
|
SoundController {
|
||||||
|
audio_device: audio_device,
|
||||||
|
|
||||||
sample_rate_to_cpu_freq: 12345,
|
sample_rate_to_cpu_freq: 12345,
|
||||||
last_sample_cycles: 0,
|
last_sample_cycles: 0,
|
||||||
mse: false,
|
mse: false,
|
||||||
|
@ -110,7 +121,8 @@ impl SoundController {
|
||||||
sound_bias: 0x200,
|
sound_bias: 0x200,
|
||||||
dma_sound: [Default::default(), Default::default()],
|
dma_sound: [Default::default(), Default::default()],
|
||||||
|
|
||||||
output_buffer: Vec::with_capacity(32),
|
resampler: resampler,
|
||||||
|
output_buffer: Vec::with_capacity(10000),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,14 +147,8 @@ impl SoundController {
|
||||||
.iter()
|
.iter()
|
||||||
.position(|&f| f == self.dmg_volume_ratio)
|
.position(|&f| f == self.dmg_volume_ratio)
|
||||||
.expect("bad dmg_volume_ratio!") as u16
|
.expect("bad dmg_volume_ratio!") as u16
|
||||||
| DMA_RATIOS
|
| cbit(2, self.dma_sound[0].volume_shift == 1)
|
||||||
.iter()
|
| cbit(3, self.dma_sound[1].volume_shift == 1)
|
||||||
.position(|&f| f == self.dma_sound[0].volume)
|
|
||||||
.unwrap() as u16
|
|
||||||
| DMA_RATIOS
|
|
||||||
.iter()
|
|
||||||
.position(|&f| f == self.dma_sound[1].volume)
|
|
||||||
.unwrap() as u16
|
|
||||||
| cbit(8, self.dma_sound[0].enable_right)
|
| cbit(8, self.dma_sound[0].enable_right)
|
||||||
| cbit(9, self.dma_sound[0].enable_left)
|
| cbit(9, self.dma_sound[0].enable_left)
|
||||||
| cbit(10, self.dma_sound[0].timer_select != 0)
|
| cbit(10, self.dma_sound[0].timer_select != 0)
|
||||||
|
@ -210,8 +216,8 @@ impl SoundController {
|
||||||
|
|
||||||
REG_SOUNDCNT_H => {
|
REG_SOUNDCNT_H => {
|
||||||
self.dmg_volume_ratio = DMG_RATIOS[value.bit_range(0..1) as usize];
|
self.dmg_volume_ratio = DMG_RATIOS[value.bit_range(0..1) as usize];
|
||||||
self.dma_sound[0].volume = DMA_RATIOS[value.bit(2) as usize];
|
self.dma_sound[0].volume_shift = value.bit(2) as i16;
|
||||||
self.dma_sound[1].volume = DMA_RATIOS[value.bit(3) as usize];
|
self.dma_sound[1].volume_shift = value.bit(3) as i16;
|
||||||
self.dma_sound[0].enable_right = value.bit(8);
|
self.dma_sound[0].enable_right = value.bit(8);
|
||||||
self.dma_sound[0].enable_left = value.bit(9);
|
self.dma_sound[0].enable_left = value.bit(9);
|
||||||
self.dma_sound[0].timer_select = DMA_TIMERS[value.bit(10) as usize];
|
self.dma_sound[0].timer_select = DMA_TIMERS[value.bit(10) as usize];
|
||||||
|
@ -274,7 +280,6 @@ impl SoundController {
|
||||||
if !self.mse {
|
if !self.mse {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// TODO - play sound ?
|
|
||||||
|
|
||||||
const FIFO_INDEX_TO_REG: [u32; 2] = [REG_FIFO_A, REG_FIFO_B];
|
const FIFO_INDEX_TO_REG: [u32; 2] = [REG_FIFO_A, REG_FIFO_B];
|
||||||
for fifo in 0..2 {
|
for fifo in 0..2 {
|
||||||
|
@ -294,24 +299,33 @@ impl SoundController {
|
||||||
(32768 << resolution) as i32
|
(32768 << resolution) as i32
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(&mut self, cycles: usize, audio_device: &mut dyn AudioInterface) {
|
pub fn update(&mut self, cycles: usize) {
|
||||||
let resolution = self.sound_bias.bit_range(14..16) as usize;
|
let resolution = self.sound_bias.bit_range(14..16) as usize;
|
||||||
let cycles_per_sample = 512 >> resolution;
|
let cycles_per_sample = 512 >> resolution;
|
||||||
|
|
||||||
|
self.resampler.in_freq = self.sample_rate() as f32;
|
||||||
|
|
||||||
while cycles - self.last_sample_cycles >= cycles_per_sample {
|
while cycles - self.last_sample_cycles >= cycles_per_sample {
|
||||||
self.last_sample_cycles += cycles_per_sample;
|
self.last_sample_cycles += cycles_per_sample;
|
||||||
|
|
||||||
let mut sample = (0, 0);
|
// time to push a new sample!
|
||||||
|
|
||||||
|
let mut sample = (0i16, 0i16);
|
||||||
for i in 0..2 {
|
for i in 0..2 {
|
||||||
let channel = &self.dma_sound[i];
|
let channel = &self.dma_sound[i];
|
||||||
if channel.enable_left {
|
if channel.enable_left {
|
||||||
sample.0 += (channel.value as i16) << 8;
|
sample.0 += ((channel.value as i16) << 8) >> channel.volume_shift;
|
||||||
}
|
}
|
||||||
if channel.enable_right {
|
if channel.enable_right {
|
||||||
sample.1 += (channel.value as i16) << 8;
|
sample.1 += ((channel.value as i16) << 8) >> channel.volume_shift;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
audio_device.play(&[sample.0, sample.1]);
|
|
||||||
|
self.resampler.push_sample(sample, &mut self.output_buffer);
|
||||||
|
if self.output_buffer.len() >= 10000 {
|
||||||
|
self.audio_device.borrow_mut().play(&self.output_buffer);
|
||||||
|
self.output_buffer.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -138,7 +138,6 @@ impl Bus for DummyBus {
|
||||||
fn write_8(&mut self, _addr: Addr, _value: u8) {}
|
fn write_8(&mut self, _addr: Addr, _value: u8) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct SysBus {
|
pub struct SysBus {
|
||||||
pub io: IoDevices,
|
pub io: IoDevices,
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
use sdl2;
|
use sdl2;
|
||||||
use sdl2::audio::{AudioDevice, AudioQueue, AudioSpec, AudioSpecDesired, AudioStatus};
|
use sdl2::audio::{AudioQueue, AudioSpecDesired};
|
||||||
|
|
||||||
use rustboyadvance_ng::AudioInterface;
|
use rustboyadvance_ng::AudioInterface;
|
||||||
|
|
||||||
pub struct Sdl2AudioPlayer {
|
pub struct Sdl2AudioPlayer {
|
||||||
device: AudioQueue<i16>,
|
pub device: AudioQueue<i16>,
|
||||||
freq: i32,
|
freq: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,15 +27,7 @@ pub fn create_audio_player(sdl: &sdl2::Sdl) -> Sdl2AudioPlayer {
|
||||||
samples: None,
|
samples: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
// let mut device = audio_subsystem
|
let device = audio_subsystem
|
||||||
// .open_playback(None, &desired_spec, |spec| {
|
|
||||||
// println!("Obtained {:?}", spec);
|
|
||||||
|
|
||||||
// GbaAudioCallback { spec: spec }
|
|
||||||
// })
|
|
||||||
// .unwrap();
|
|
||||||
|
|
||||||
let mut device = audio_subsystem
|
|
||||||
.open_queue::<i16, _>(None, &desired_spec)
|
.open_queue::<i16, _>(None, &desired_spec)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
@ -43,5 +35,6 @@ pub fn create_audio_player(sdl: &sdl2::Sdl) -> Sdl2AudioPlayer {
|
||||||
|
|
||||||
let freq = device.spec().freq;
|
let freq = device.spec().freq;
|
||||||
device.resume();
|
device.resume();
|
||||||
|
|
||||||
Sdl2AudioPlayer { device, freq }
|
Sdl2AudioPlayer { device, freq }
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,6 +85,7 @@ fn main() {
|
||||||
keycode: Some(Keycode::Space),
|
keycode: Some(Keycode::Space),
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
|
audio.borrow_mut().device.clear(); // clear audio queue
|
||||||
frame_limiter = true;
|
frame_limiter = true;
|
||||||
}
|
}
|
||||||
Event::KeyDown {
|
Event::KeyDown {
|
||||||
|
|
Reference in a new issue