More work. DMA sound seems to work, but sounds very crappy.
Former-commit-id: cc9f2877f9ccc1b05163af3fd35ff07efa8e3067
This commit is contained in:
parent
fefeddbc40
commit
398e66b723
|
@ -129,7 +129,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ use bit::BitIndex;
|
||||||
use super::dma::DmaController;
|
use super::dma::DmaController;
|
||||||
use super::iodev::consts::*;
|
use super::iodev::consts::*;
|
||||||
use super::iodev::io_reg_string;
|
use super::iodev::io_reg_string;
|
||||||
|
use crate::AudioInterface;
|
||||||
|
|
||||||
mod fifo;
|
mod fifo;
|
||||||
use fifo::SoundFifo;
|
use fifo::SoundFifo;
|
||||||
|
@ -16,18 +17,20 @@ const DUTY_RATIOS: [f32; 4] = [0.125, 0.25, 0.5, 0.75];
|
||||||
struct NoiseChannel {}
|
struct NoiseChannel {}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct DmaSound {
|
struct DmaSoundChannel {
|
||||||
|
value: i8,
|
||||||
volume: f32,
|
volume: f32,
|
||||||
enable_right: bool,
|
enable_right: bool,
|
||||||
enable_left: bool,
|
enable_left: bool,
|
||||||
timer_select: usize,
|
timer_select: usize,
|
||||||
fifo: SoundFifo<i8>,
|
fifo: SoundFifo,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for DmaSound {
|
impl Default for DmaSoundChannel {
|
||||||
fn default() -> DmaSound {
|
fn default() -> DmaSoundChannel {
|
||||||
DmaSound {
|
DmaSoundChannel {
|
||||||
volume: DMA_RATIOS[0],
|
volume: DMA_RATIOS[0],
|
||||||
|
value: 0,
|
||||||
enable_right: false,
|
enable_right: false,
|
||||||
enable_left: false,
|
enable_left: false,
|
||||||
timer_select: 0,
|
timer_select: 0,
|
||||||
|
@ -74,8 +77,9 @@ pub struct SoundController {
|
||||||
|
|
||||||
sound_bias: u16,
|
sound_bias: u16,
|
||||||
|
|
||||||
sound_a: DmaSound,
|
dma_sound: [DmaSoundChannel; 2],
|
||||||
sound_b: DmaSound,
|
|
||||||
|
pub output_buffer: Vec<i8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SoundController {
|
impl SoundController {
|
||||||
|
@ -104,8 +108,9 @@ impl SoundController {
|
||||||
sqr1_initial_vol: 0,
|
sqr1_initial_vol: 0,
|
||||||
sqr1_cur_vol: 0,
|
sqr1_cur_vol: 0,
|
||||||
sound_bias: 0x200,
|
sound_bias: 0x200,
|
||||||
sound_a: Default::default(),
|
dma_sound: [Default::default(), Default::default()],
|
||||||
sound_b: Default::default(),
|
|
||||||
|
output_buffer: Vec::with_capacity(32),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,18 +137,18 @@ impl SoundController {
|
||||||
.expect("bad dmg_volume_ratio!") as u16
|
.expect("bad dmg_volume_ratio!") as u16
|
||||||
| DMA_RATIOS
|
| DMA_RATIOS
|
||||||
.iter()
|
.iter()
|
||||||
.position(|&f| f == self.sound_a.volume)
|
.position(|&f| f == self.dma_sound[0].volume)
|
||||||
.unwrap() as u16
|
.unwrap() as u16
|
||||||
| DMA_RATIOS
|
| DMA_RATIOS
|
||||||
.iter()
|
.iter()
|
||||||
.position(|&f| f == self.sound_b.volume)
|
.position(|&f| f == self.dma_sound[1].volume)
|
||||||
.unwrap() as u16
|
.unwrap() as u16
|
||||||
| cbit(8, self.sound_a.enable_right)
|
| cbit(8, self.dma_sound[0].enable_right)
|
||||||
| cbit(9, self.sound_a.enable_left)
|
| cbit(9, self.dma_sound[0].enable_left)
|
||||||
| cbit(10, self.sound_a.timer_select != 0)
|
| cbit(10, self.dma_sound[0].timer_select != 0)
|
||||||
| cbit(12, self.sound_b.enable_right)
|
| cbit(12, self.dma_sound[1].enable_right)
|
||||||
| cbit(13, self.sound_b.enable_left)
|
| cbit(13, self.dma_sound[1].enable_left)
|
||||||
| cbit(14, self.sound_b.timer_select != 0)
|
| cbit(14, self.dma_sound[1].timer_select != 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
REG_SOUNDBIAS => self.sound_bias,
|
REG_SOUNDBIAS => self.sound_bias,
|
||||||
|
@ -205,20 +210,20 @@ 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.sound_a.volume = DMA_RATIOS[value.bit(2) as usize];
|
self.dma_sound[0].volume = DMA_RATIOS[value.bit(2) as usize];
|
||||||
self.sound_b.volume = DMA_RATIOS[value.bit(3) as usize];
|
self.dma_sound[1].volume = DMA_RATIOS[value.bit(3) as usize];
|
||||||
self.sound_a.enable_right = value.bit(8);
|
self.dma_sound[0].enable_right = value.bit(8);
|
||||||
self.sound_a.enable_left = value.bit(9);
|
self.dma_sound[0].enable_left = value.bit(9);
|
||||||
self.sound_a.timer_select = DMA_TIMERS[value.bit(10) as usize];
|
self.dma_sound[0].timer_select = DMA_TIMERS[value.bit(10) as usize];
|
||||||
self.sound_b.enable_right = value.bit(12);
|
self.dma_sound[1].enable_right = value.bit(12);
|
||||||
self.sound_b.enable_left = value.bit(13);
|
self.dma_sound[1].enable_left = value.bit(13);
|
||||||
self.sound_b.timer_select = DMA_TIMERS[value.bit(14) as usize];
|
self.dma_sound[1].timer_select = DMA_TIMERS[value.bit(14) as usize];
|
||||||
|
|
||||||
if value.bit(11) {
|
if value.bit(11) {
|
||||||
self.sound_a.fifo.reset();
|
self.dma_sound[0].fifo.reset();
|
||||||
}
|
}
|
||||||
if value.bit(15) {
|
if value.bit(15) {
|
||||||
self.sound_b.fifo.reset();
|
self.dma_sound[1].fifo.reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -239,13 +244,13 @@ impl SoundController {
|
||||||
}
|
}
|
||||||
|
|
||||||
REG_FIFO_A_L | REG_FIFO_A_H => {
|
REG_FIFO_A_L | REG_FIFO_A_H => {
|
||||||
self.sound_a.fifo.write(((value >> 8) & 0xff) as i8);
|
self.dma_sound[0].fifo.write(((value >> 8) & 0xff) as i8);
|
||||||
self.sound_a.fifo.write((value & 0xff) as i8);
|
self.dma_sound[0].fifo.write((value & 0xff) as i8);
|
||||||
}
|
}
|
||||||
|
|
||||||
REG_FIFO_B_L | REG_FIFO_B_H => {
|
REG_FIFO_B_L | REG_FIFO_B_H => {
|
||||||
self.sound_b.fifo.write(((value >> 8) & 0xff) as i8);
|
self.dma_sound[1].fifo.write(((value >> 8) & 0xff) as i8);
|
||||||
self.sound_b.fifo.write((value & 0xff) as i8);
|
self.dma_sound[1].fifo.write((value & 0xff) as i8);
|
||||||
}
|
}
|
||||||
|
|
||||||
REG_SOUNDBIAS => self.sound_bias = value & 0xc3fe,
|
REG_SOUNDBIAS => self.sound_bias = value & 0xc3fe,
|
||||||
|
@ -260,23 +265,53 @@ impl SoundController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_timer_overflow(&mut self, dmac: &mut DmaController, timer_id: usize) {
|
pub fn handle_timer_overflow(
|
||||||
|
&mut self,
|
||||||
|
dmac: &mut DmaController,
|
||||||
|
timer_id: usize,
|
||||||
|
num_overflows: usize,
|
||||||
|
) {
|
||||||
if !self.mse {
|
if !self.mse {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// TODO - play sound ?
|
// TODO - play sound ?
|
||||||
|
|
||||||
if timer_id == self.sound_a.timer_select {
|
const FIFO_INDEX_TO_REG: [u32; 2] = [REG_FIFO_A, REG_FIFO_B];
|
||||||
dmac.notify_sound_fifo(REG_FIFO_A);
|
for fifo in 0..2 {
|
||||||
|
let channel = &mut self.dma_sound[fifo];
|
||||||
|
|
||||||
|
if timer_id == channel.timer_select {
|
||||||
|
channel.value = channel.fifo.read();
|
||||||
|
if channel.fifo.count() <= 16 {
|
||||||
|
dmac.notify_sound_fifo(FIFO_INDEX_TO_REG[fifo]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if timer_id == self.sound_b.timer_select {
|
|
||||||
dmac.notify_sound_fifo(REG_FIFO_B);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(&mut self, cycles: usize) {
|
fn sample_rate(&self) -> i32 {
|
||||||
while cycles - self.last_sample_cycles >= self.sample_rate_to_cpu_freq {
|
let resolution = self.sound_bias.bit_range(14..16) as usize;
|
||||||
self.last_sample_cycles += self.sample_rate_to_cpu_freq;
|
(32768 << resolution) as i32
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(&mut self, cycles: usize, audio_device: &mut dyn AudioInterface) {
|
||||||
|
let resolution = self.sound_bias.bit_range(14..16) as usize;
|
||||||
|
let cycles_per_sample = 512 >> resolution;
|
||||||
|
while cycles - self.last_sample_cycles >= cycles_per_sample {
|
||||||
|
self.last_sample_cycles += cycles_per_sample;
|
||||||
|
|
||||||
|
let mut sample = (0, 0);
|
||||||
|
|
||||||
|
for i in 0..2 {
|
||||||
|
let channel = &self.dma_sound[i];
|
||||||
|
if channel.enable_left {
|
||||||
|
sample.0 += (channel.value as i16) << 8;
|
||||||
|
}
|
||||||
|
if channel.enable_right {
|
||||||
|
sample.1 += (channel.value as i16) << 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
audio_device.play(&[sample.0, sample.1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,7 +67,7 @@ impl Timers {
|
||||||
pub fn new() -> Timers {
|
pub fn new() -> Timers {
|
||||||
Timers {
|
Timers {
|
||||||
timers: [Timer::new(0), Timer::new(1), Timer::new(2), Timer::new(3)],
|
timers: [Timer::new(0), Timer::new(1), Timer::new(2), Timer::new(3)],
|
||||||
trace: true,
|
trace: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,7 +155,9 @@ impl Timers {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if id == 0 || id == 1 {
|
if id == 0 || id == 1 {
|
||||||
sb.io.sound.handle_timer_overflow(&mut sb.io.dmac, id);
|
sb.io
|
||||||
|
.sound
|
||||||
|
.handle_timer_overflow(&mut sb.io.dmac, id, num_overflows);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,10 @@ pub trait VideoInterface {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait AudioInterface {
|
pub trait AudioInterface {
|
||||||
fn get_sample_rate(&self) -> u32;
|
fn get_sample_rate(&self) -> i32;
|
||||||
|
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
fn play(&mut self, samples: &[i16]) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait InputInterface {
|
pub trait InputInterface {
|
||||||
|
|
|
@ -75,7 +75,7 @@ impl InputInterface for MiniFb {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AudioInterface for MiniFb {
|
impl AudioInterface for MiniFb {
|
||||||
fn get_sample_rate(&self) -> u32 {
|
fn get_sample_rate(&self) -> i32 {
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,31 +1,20 @@
|
||||||
use sdl2;
|
use sdl2;
|
||||||
use sdl2::audio::{AudioDevice, AudioSpec, AudioSpecDesired};
|
use sdl2::audio::{AudioDevice, AudioQueue, AudioSpec, AudioSpecDesired, AudioStatus};
|
||||||
|
|
||||||
use rustboyadvance_ng::AudioInterface;
|
use rustboyadvance_ng::AudioInterface;
|
||||||
|
|
||||||
pub struct Sdl2AudioPlayer {
|
pub struct Sdl2AudioPlayer {
|
||||||
device: AudioDevice<GbaAudioCallback>,
|
device: AudioQueue<i16>,
|
||||||
freq: u32,
|
freq: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AudioInterface for Sdl2AudioPlayer {
|
impl AudioInterface for Sdl2AudioPlayer {
|
||||||
fn get_sample_rate(&self) -> u32 {
|
fn get_sample_rate(&self) -> i32 {
|
||||||
self.freq
|
self.freq
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
struct GbaAudioCallback {
|
fn play(&mut self, samples: &[i16]) {
|
||||||
spec: AudioSpec,
|
self.device.queue(&samples);
|
||||||
}
|
|
||||||
|
|
||||||
impl sdl2::audio::AudioCallback for GbaAudioCallback {
|
|
||||||
type Channel = f32;
|
|
||||||
|
|
||||||
fn callback(&mut self, out: &mut [f32]) {
|
|
||||||
// TODO audio
|
|
||||||
for x in out.iter_mut() {
|
|
||||||
*x = 0.0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,19 +22,26 @@ pub fn create_audio_player(sdl: &sdl2::Sdl) -> Sdl2AudioPlayer {
|
||||||
let audio_subsystem = sdl.audio().unwrap();
|
let audio_subsystem = sdl.audio().unwrap();
|
||||||
|
|
||||||
let desired_spec = AudioSpecDesired {
|
let desired_spec = AudioSpecDesired {
|
||||||
freq: Some(44100),
|
freq: Some(44_100),
|
||||||
channels: Some(1), // stereo
|
channels: Some(2), // stereo
|
||||||
samples: None,
|
samples: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut device = audio_subsystem
|
// let mut device = audio_subsystem
|
||||||
.open_playback(None, &desired_spec, |spec| {
|
// .open_playback(None, &desired_spec, |spec| {
|
||||||
println!("Obtained {:?}", spec);
|
// println!("Obtained {:?}", spec);
|
||||||
|
|
||||||
GbaAudioCallback { spec: spec }
|
// GbaAudioCallback { spec: spec }
|
||||||
})
|
// })
|
||||||
|
// .unwrap();
|
||||||
|
|
||||||
|
let mut device = audio_subsystem
|
||||||
|
.open_queue::<i16, _>(None, &desired_spec)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let freq = (*device.lock()).spec.freq as u32;
|
println!("Found audio device: {:?}", device.spec());
|
||||||
|
|
||||||
|
let freq = device.spec().freq;
|
||||||
|
device.resume();
|
||||||
Sdl2AudioPlayer { device, freq }
|
Sdl2AudioPlayer { device, freq }
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue