mirror of
https://github.com/italicsjenga/agb.git
synced 2024-12-23 16:21:33 +11:00
32768 example
This commit is contained in:
parent
a4c696ad33
commit
7d8890b03e
|
@ -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"]
|
BIN
agb/examples/JoshWoodward-CrazyGlue.wav
Normal file
BIN
agb/examples/JoshWoodward-CrazyGlue.wav
Normal file
Binary file not shown.
47
agb/examples/mixer_32768.rs
Normal file
47
agb/examples/mixer_32768.rs
Normal 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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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]),
|
||||||
|
|
||||||
buffer_1_active: true,
|
state: Mutex::new(RefCell::new(MixerBufferState {
|
||||||
swapped: false,
|
buffer_1_active: true,
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue