32768 example

This commit is contained in:
Gwilym Kuiper 2022-06-11 15:46:10 +01:00
parent a4c696ad33
commit 7d8890b03e
4 changed files with 108 additions and 24 deletions

View file

@ -32,3 +32,7 @@ rustc-hash = { version = "1", default-features = false }
[package.metadata.docs.rs] [package.metadata.docs.rs]
default-target = "thumbv6m-none-eabi" default-target = "thumbv6m-none-eabi"
targets = [] targets = []
[[example]]
name = "mixer_32768"
required-features = ["freq32768"]

Binary file not shown.

View file

@ -0,0 +1,47 @@
#![no_std]
#![no_main]
use agb::sound::mixer::SoundChannel;
use agb::{include_wav, Gba};
// Music - "Let it in" by Josh Woodward, free download at http://joshwoodward.com
const LET_IT_IN: &[u8] = include_wav!("examples/JoshWoodward-CrazyGlue.wav");
#[agb::entry]
fn main(mut gba: Gba) -> ! {
let vblank_provider = agb::interrupt::VBlank::get();
let timer_controller = gba.timers.timers();
let mut timer = timer_controller.timer2;
timer.set_enabled(true);
let mut mixer = gba.mixer.mixer();
mixer.enable();
let mut channel = SoundChannel::new(LET_IT_IN);
channel.stereo();
mixer.play_sound(channel).unwrap();
let _interrupt = mixer.setup_interrupt_handler();
let mut frame_counter = 0i32;
loop {
vblank_provider.wait_for_vblank();
let before_mixing_cycles = timer.value();
mixer.frame();
let after_mixing_cycles = timer.value();
frame_counter = frame_counter.wrapping_add(1);
if frame_counter % 128 == 0 {
let total_cycles = after_mixing_cycles.wrapping_sub(before_mixing_cycles) as u32;
let percent = (total_cycles * 100) / 280896;
agb::println!(
"Took {} cycles to calculate mixer ~= {}% of total frame",
total_cycles,
percent
);
}
}
}

View file

@ -1,8 +1,13 @@
use core::cell::RefCell;
use bare_metal::{CriticalSection, Mutex};
use super::hw; use super::hw;
use super::hw::LeftOrRight; use super::hw::LeftOrRight;
use super::{SoundChannel, SoundPriority}; use super::{SoundChannel, SoundPriority};
use crate::fixnum::Num; use crate::fixnum::Num;
use crate::timer::Timer; use crate::interrupt::{add_interrupt_handler, free, Interrupt, InterruptHandler};
use crate::timer::{Divider, Timer};
// Defined in mixer.s // Defined in mixer.s
extern "C" { extern "C" {
@ -31,7 +36,7 @@ pub struct ChannelId(usize, i32);
impl Mixer { impl Mixer {
pub(super) fn new() -> Self { pub(super) fn new() -> Self {
Mixer { Self {
buffer: MixerBuffer::new(), buffer: MixerBuffer::new(),
channels: Default::default(), channels: Default::default(),
indices: Default::default(), indices: Default::default(),
@ -45,21 +50,25 @@ impl Mixer {
hw::set_sound_control_register_for_mixer(); hw::set_sound_control_register_for_mixer();
} }
#[cfg(not(feature = "freq32768"))]
pub fn frame(&mut self) {
self.buffer.clear();
self.buffer
.write_channels(self.channels.iter_mut().flatten());
}
#[cfg(not(feature = "freq32768"))] #[cfg(not(feature = "freq32768"))]
pub fn after_vblank(&mut self) { pub fn after_vblank(&mut self) {
self.buffer.swap(); free(|cs| self.buffer.swap(cs));
} }
#[cfg(feature = "freq32768")] #[cfg(feature = "freq32768")]
pub fn setup_interrupt_handler(&self) -> InterruptHandler<'_> {
let mut timer1 = unsafe { Timer::new(1) };
timer1.set_cascade(true);
timer1.set_divider(Divider::Divider1);
timer1.set_interrupt(true);
timer1.set_overflow_amount(constants::SOUND_BUFFER_SIZE as u16);
timer1.set_enabled(true);
add_interrupt_handler(Interrupt::Timer1, move |cs| self.buffer.swap(cs))
}
pub fn frame(&mut self) { pub fn frame(&mut self) {
if self.buffer.has_swapped() { if !self.buffer.has_swapped() {
return; return;
} }
@ -126,7 +135,7 @@ mod constants {
#[cfg(feature = "freq32768")] #[cfg(feature = "freq32768")]
mod constants { mod constants {
pub const SOUND_FREQUENCY: i32 = 32768; pub const SOUND_FREQUENCY: i32 = 32768;
pub const SOUND_BUFFER_SIZE: usize = 549; pub const SOUND_BUFFER_SIZE: usize = 560;
} }
fn set_asm_buffer_size() { fn set_asm_buffer_size() {
@ -146,6 +155,10 @@ struct MixerBuffer {
buffer1: SoundBuffer, // alternating bytes left and right channels buffer1: SoundBuffer, // alternating bytes left and right channels
buffer2: SoundBuffer, buffer2: SoundBuffer,
state: Mutex<RefCell<MixerBufferState>>,
}
struct MixerBufferState {
buffer_1_active: bool, buffer_1_active: bool,
swapped: bool, swapped: bool,
} }
@ -158,28 +171,34 @@ impl MixerBuffer {
buffer1: SoundBuffer([0; constants::SOUND_BUFFER_SIZE * 2]), buffer1: SoundBuffer([0; constants::SOUND_BUFFER_SIZE * 2]),
buffer2: SoundBuffer([0; constants::SOUND_BUFFER_SIZE * 2]), buffer2: SoundBuffer([0; constants::SOUND_BUFFER_SIZE * 2]),
state: Mutex::new(RefCell::new(MixerBufferState {
buffer_1_active: true, buffer_1_active: true,
swapped: false, swapped: false,
})),
} }
} }
fn has_swapped(&self) -> bool { fn has_swapped(&self) -> bool {
self.swapped free(|cs| self.state.borrow(*cs).borrow().swapped)
} }
fn swap(&mut self) { fn swap(&self, cs: &CriticalSection) {
let (left_buffer, right_buffer) = let (left_buffer, right_buffer) =
self.write_buffer().split_at(constants::SOUND_BUFFER_SIZE); self.write_buffer(cs).split_at(constants::SOUND_BUFFER_SIZE);
hw::enable_dma_for_sound(left_buffer, LeftOrRight::Left); hw::enable_dma_for_sound(left_buffer, LeftOrRight::Left);
hw::enable_dma_for_sound(right_buffer, LeftOrRight::Right); hw::enable_dma_for_sound(right_buffer, LeftOrRight::Right);
self.buffer_1_active = !self.buffer_1_active; {
self.swapped = true; let mut state = self.state.borrow(*cs).borrow_mut();
state.buffer_1_active = !state.buffer_1_active;
state.swapped = true;
}
} }
fn clear(&mut self) { fn clear(&mut self) {
self.write_buffer().fill(0); self.write_buffer_mut().fill(0);
} }
fn write_channels<'a>(&mut self, channels: impl Iterator<Item = &'a mut SoundChannel>) { fn write_channels<'a>(&mut self, channels: impl Iterator<Item = &'a mut SoundChannel>) {
@ -234,19 +253,33 @@ impl MixerBuffer {
channel.pos += playback_speed * constants::SOUND_BUFFER_SIZE; channel.pos += playback_speed * constants::SOUND_BUFFER_SIZE;
} }
let write_buffer = self.write_buffer(); let write_buffer = self.write_buffer_mut();
unsafe { unsafe {
agb_rs__mixer_collapse(write_buffer.as_mut_ptr(), buffer.as_ptr()); agb_rs__mixer_collapse(write_buffer.as_mut_ptr(), buffer.as_ptr());
} }
self.swapped = false; free(|cs| {
self.state.borrow(*cs).borrow_mut().swapped = false;
});
} }
fn write_buffer(&mut self) -> &mut [i8; constants::SOUND_BUFFER_SIZE * 2] { fn write_buffer_mut(&mut self) -> &mut [i8; constants::SOUND_BUFFER_SIZE * 2] {
if self.buffer_1_active { let buffer_1_active = free(|cs| self.state.borrow(*cs).borrow().buffer_1_active);
if buffer_1_active {
&mut self.buffer2.0 &mut self.buffer2.0
} else { } else {
&mut self.buffer1.0 &mut self.buffer1.0
} }
} }
fn write_buffer(&self, cs: &CriticalSection) -> &[i8; constants::SOUND_BUFFER_SIZE * 2] {
let buffer_1_active = self.state.borrow(*cs).borrow().buffer_1_active;
if buffer_1_active {
&self.buffer2.0
} else {
&self.buffer1.0
}
}
} }