From b74c5e39c026eb012c77d43ee2600b2b8d772fac Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Fri, 9 Dec 2022 22:27:04 +0000 Subject: [PATCH 01/12] Remove setup_interrupt_handler and after_vblank in the mixer --- agb/src/sound/mixer/sw_mixer.rs | 95 +++++++++++---------------------- 1 file changed, 31 insertions(+), 64 deletions(-) diff --git a/agb/src/sound/mixer/sw_mixer.rs b/agb/src/sound/mixer/sw_mixer.rs index 44c17c4f..46e30daa 100644 --- a/agb/src/sound/mixer/sw_mixer.rs +++ b/agb/src/sound/mixer/sw_mixer.rs @@ -1,4 +1,5 @@ use core::cell::RefCell; +use core::pin::Pin; use alloc::boxed::Box; use alloc::vec::Vec; @@ -79,7 +80,11 @@ extern "C" { /// # } /// ``` pub struct Mixer { - buffer: MixerBuffer, + interrupt_timer: Timer, + // SAFETY: Has to go before buffer because it holds a reference to it + _interrupt_handler: InterruptHandler<'static>, + + buffer: Pin>, channels: [Option; 8], indices: [i32; 8], frequency: Frequency, @@ -112,12 +117,35 @@ pub struct ChannelId(usize, i32); impl Mixer { pub(super) fn new(frequency: Frequency) -> Self { + let buffer = Box::pin(MixerBuffer::new(frequency)); + + // SAFETY: you can only ever have 1 Mixer at a time + let mut interrupt_timer = unsafe { Timer::new(1) }; + interrupt_timer + .set_cascade(true) + .set_divider(Divider::Divider1) + .set_interrupt(true) + .set_overflow_amount(frequency.buffer_size() as u16); + + let buffer_pointer_for_interrupt_handler: &MixerBuffer = &buffer; + + // SAFETY: dropping the lifetime, sound because interrupt handler dropped before the buffer is + // In the case of the mixer being forgotten, both stay alive so okay + let buffer_pointer_for_interrupt_handler: &MixerBuffer = + unsafe { core::mem::transmute(buffer_pointer_for_interrupt_handler) }; + let interrupt_handler = add_interrupt_handler(interrupt_timer.interrupt(), |cs| { + buffer_pointer_for_interrupt_handler.swap(cs); + }); + Self { frequency, - buffer: MixerBuffer::new(frequency), + buffer, channels: Default::default(), indices: Default::default(), + interrupt_timer, + _interrupt_handler: interrupt_handler, + timer: unsafe { Timer::new(0) }, } } @@ -129,69 +157,8 @@ impl Mixer { pub fn enable(&mut self) { hw::set_timer_counter_for_frequency_and_enable(&mut self.timer, self.frequency.frequency()); hw::set_sound_control_register_for_mixer(); - } - /// Do post-vblank work. You can use either this or [`setup_interrupt_handler()`](Mixer::setup_interrupt_handler), - /// but not both. Note that this is not available if using 32768Hz sounds since those require more irregular timings. - /// - /// # Example - /// - /// ```rust,no_run - /// # #![no_std] - /// # #![no_main] - /// # use agb::sound::mixer::*; - /// # use agb::*; - /// # fn foo(gba: &mut Gba) { - /// # let mut mixer = gba.mixer.mixer(agb::sound::mixer::Frequency::Hz10512); - /// # let vblank = agb::interrupt::VBlank::get(); - /// loop { - /// mixer.frame(); - /// vblank.wait_for_vblank(); - /// mixer.after_vblank(); - /// } - /// # } - /// ``` - #[cfg(not(feature = "freq32768"))] - pub fn after_vblank(&mut self) { - free(|cs| self.buffer.swap(cs)); - } - - /// Use timer interrupts to do the timing required for ensuring the music runs smoothly. - /// - /// Note that if you set up an interrupt handler, you should not call [`after_vblank`](Mixer::after_vblank) any more - /// You are still required to call [`frame`](Mixer::frame). - /// - /// This is required if using 32768Hz music, but optional for other frequencies. - /// - /// # Example - /// - /// ```rust,no_run - /// # #![no_std] - /// # #![no_main] - /// # use agb::sound::mixer::*; - /// # use agb::*; - /// # fn foo(gba: &mut Gba) { - /// # let mut mixer = gba.mixer.mixer(agb::sound::mixer::Frequency::Hz10512); - /// # let vblank = agb::interrupt::VBlank::get(); - /// // you must set this to a named variable to ensure that the scope is long enough - /// let _mixer_interrupt = mixer.setup_interrupt_handler(); - /// - /// loop { - /// mixer.frame(); - /// vblank.wait_for_vblank(); - /// } - /// # } - /// ``` - pub fn setup_interrupt_handler(&self) -> InterruptHandler<'_> { - let mut timer1 = unsafe { Timer::new(1) }; - timer1 - .set_cascade(true) - .set_divider(Divider::Divider1) - .set_interrupt(true) - .set_overflow_amount(self.frequency.buffer_size() as u16) - .set_enabled(true); - - add_interrupt_handler(timer1.interrupt(), move |cs| self.buffer.swap(cs)) + self.interrupt_timer.set_enabled(true); } /// Do the CPU intensive mixing for the next frame's worth of data. From 390392a1d85f14d11367d6f47823f57d36ab3e67 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Fri, 9 Dec 2022 22:28:32 +0000 Subject: [PATCH 02/12] Rename timer -> fifo_timer --- agb/src/sound/mixer/sw_mixer.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/agb/src/sound/mixer/sw_mixer.rs b/agb/src/sound/mixer/sw_mixer.rs index 46e30daa..0316f3bb 100644 --- a/agb/src/sound/mixer/sw_mixer.rs +++ b/agb/src/sound/mixer/sw_mixer.rs @@ -75,7 +75,7 @@ extern "C" { /// loop { /// mixer.frame(); /// vblank.wait_for_vblank(); -/// mixer.after_vblank(); +/// mixer.after_vblank(); /// } /// # } /// ``` @@ -89,7 +89,7 @@ pub struct Mixer { indices: [i32; 8], frequency: Frequency, - timer: Timer, + fifo_timer: Timer, } /// A pointer to a currently playing channel. @@ -119,6 +119,9 @@ impl Mixer { pub(super) fn new(frequency: Frequency) -> Self { let buffer = Box::pin(MixerBuffer::new(frequency)); + // SAFETY: you can only ever have 1 Mixer at a time + let fifo_timer = unsafe { Timer::new(0) }; + // SAFETY: you can only ever have 1 Mixer at a time let mut interrupt_timer = unsafe { Timer::new(1) }; interrupt_timer @@ -146,7 +149,7 @@ impl Mixer { interrupt_timer, _interrupt_handler: interrupt_handler, - timer: unsafe { Timer::new(0) }, + fifo_timer, } } @@ -155,7 +158,10 @@ impl Mixer { /// You must call this method in order to start playing sound. You can do as much set up before /// this as you like, but you will not get any sound out of the console until this method is called. pub fn enable(&mut self) { - hw::set_timer_counter_for_frequency_and_enable(&mut self.timer, self.frequency.frequency()); + hw::set_timer_counter_for_frequency_and_enable( + &mut self.fifo_timer, + self.frequency.frequency(), + ); hw::set_sound_control_register_for_mixer(); self.interrupt_timer.set_enabled(true); From 6df06211c63b1c3c907f5a041010c13c833081b7 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Fri, 9 Dec 2022 22:34:16 +0000 Subject: [PATCH 03/12] Fix the examples --- agb/examples/mixer_32768.rs | 1 - agb/examples/mixer_basic.rs | 1 - agb/examples/stereo_sound.rs | 2 -- 3 files changed, 4 deletions(-) diff --git a/agb/examples/mixer_32768.rs b/agb/examples/mixer_32768.rs index a37aca62..6dc338ae 100644 --- a/agb/examples/mixer_32768.rs +++ b/agb/examples/mixer_32768.rs @@ -43,7 +43,6 @@ fn main(mut gba: Gba) -> ! { let mut mixer = gba.mixer.mixer(Frequency::Hz32768); mixer.enable(); - let _interrupt = mixer.setup_interrupt_handler(); let mut channel = SoundChannel::new(CRAZY_GLUE); channel.stereo(); diff --git a/agb/examples/mixer_basic.rs b/agb/examples/mixer_basic.rs index e3fc76f9..80375d77 100644 --- a/agb/examples/mixer_basic.rs +++ b/agb/examples/mixer_basic.rs @@ -51,6 +51,5 @@ fn main(mut gba: Gba) -> ! { mixer.frame(); vblank_provider.wait_for_vblank(); - mixer.after_vblank(); } } diff --git a/agb/examples/stereo_sound.rs b/agb/examples/stereo_sound.rs index 1af3eb6c..9e982aae 100644 --- a/agb/examples/stereo_sound.rs +++ b/agb/examples/stereo_sound.rs @@ -32,7 +32,6 @@ fn main(mut gba: Gba) -> ! { writeln!(&mut writer, "Let it in by Josh Woodward").unwrap(); - writer.commit(); bg.commit(&mut vram); @@ -58,7 +57,6 @@ fn main(mut gba: Gba) -> ! { bg.commit(&mut vram); let before_mixing_cycles = timer.value(); - mixer.after_vblank(); mixer.frame(); let after_mixing_cycles = timer.value(); From 0f5ce17f75bec0331158c02196055c8ab70dddf3 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Fri, 9 Dec 2022 23:00:28 +0000 Subject: [PATCH 04/12] Fix the examples and the games --- agb/Cargo.toml | 3 ++- agb/examples/mixer_32768.rs | 16 +++++++++++++--- examples/hyperspace-roll/src/lib.rs | 1 - examples/the-hat-chooses-the-wizard/src/lib.rs | 7 ------- .../src/splash_screen.rs | 16 ++-------------- examples/the-purple-night/src/lib.rs | 2 -- examples/the-purple-night/src/sfx.rs | 4 ---- 7 files changed, 17 insertions(+), 32 deletions(-) diff --git a/agb/Cargo.toml b/agb/Cargo.toml index 65728a32..b3b2c23d 100644 --- a/agb/Cargo.toml +++ b/agb/Cargo.toml @@ -12,7 +12,8 @@ opt-level = 3 debug = true [profile.release] -lto = true +opt-level = "s" +lto = "thin" debug = true [features] diff --git a/agb/examples/mixer_32768.rs b/agb/examples/mixer_32768.rs index 6dc338ae..19182776 100644 --- a/agb/examples/mixer_32768.rs +++ b/agb/examples/mixer_32768.rs @@ -39,7 +39,9 @@ fn main(mut gba: Gba) -> ! { let timer_controller = gba.timers.timers(); let mut timer = timer_controller.timer2; + let mut timer2 = timer_controller.timer3; timer.set_enabled(true); + timer2.set_cascade(true).set_enabled(true); let mut mixer = gba.mixer.mixer(Frequency::Hz32768); mixer.enable(); @@ -56,14 +58,22 @@ fn main(mut gba: Gba) -> ! { vblank_provider.wait_for_vblank(); bg.commit(&mut vram); - let before_mixing_cycles = timer.value(); + let before_mixing_cycles_high = timer2.value(); + let before_mixing_cycles_low = timer.value(); + mixer.frame(); - let after_mixing_cycles = timer.value(); + + let after_mixing_cycles_low = timer.value(); + let after_mixing_cycles_high = timer2.value(); frame_counter = frame_counter.wrapping_add(1); if frame_counter % 128 == 0 && !has_written_frame_time { - let total_cycles = after_mixing_cycles.wrapping_sub(before_mixing_cycles) as u32; + let before_mixing_cycles = + ((before_mixing_cycles_high as u32) << 16) + before_mixing_cycles_low as u32; + let after_mixing_cycles = + ((after_mixing_cycles_high as u32) << 16) + after_mixing_cycles_low as u32; + let total_cycles = after_mixing_cycles.wrapping_sub(before_mixing_cycles); let percent = (total_cycles * 100) / 280896; diff --git a/examples/hyperspace-roll/src/lib.rs b/examples/hyperspace-roll/src/lib.rs index 269f3231..61506591 100644 --- a/examples/hyperspace-roll/src/lib.rs +++ b/examples/hyperspace-roll/src/lib.rs @@ -142,7 +142,6 @@ pub fn main(mut gba: agb::Gba) -> ! { let mut mixer = gba.mixer.mixer(Frequency::Hz32768); mixer.enable(); - let _interrupt_handler = mixer.setup_interrupt_handler(); let sfx = Sfx::new(&mut mixer); diff --git a/examples/the-hat-chooses-the-wizard/src/lib.rs b/examples/the-hat-chooses-the-wizard/src/lib.rs index 26a8c9ca..d7ddd3b0 100644 --- a/examples/the-hat-chooses-the-wizard/src/lib.rs +++ b/examples/the-hat-chooses-the-wizard/src/lib.rs @@ -832,7 +832,6 @@ pub fn main(mut agb: agb::Gba) -> ! { music_box.before_frame(&mut mixer); mixer.frame(); vblank.wait_for_vblank(); - mixer.after_vblank(); level_display::write_level( &mut world_display, @@ -848,7 +847,6 @@ pub fn main(mut agb: agb::Gba) -> ! { music_box.before_frame(&mut mixer); mixer.frame(); vblank.wait_for_vblank(); - mixer.after_vblank(); let map_current_level = current_level; let mut background = InfiniteScrolledMap::new( @@ -894,21 +892,18 @@ pub fn main(mut agb: agb::Gba) -> ! { music_box.before_frame(&mut mixer); mixer.frame(); vblank.wait_for_vblank(); - mixer.after_vblank(); } while level.background.init_foreground(&mut vram) != PartialUpdateStatus::Done { music_box.before_frame(&mut mixer); mixer.frame(); vblank.wait_for_vblank(); - mixer.after_vblank(); } for _ in 0..20 { music_box.before_frame(&mut mixer); mixer.frame(); vblank.wait_for_vblank(); - mixer.after_vblank(); } object.commit(); @@ -930,7 +925,6 @@ pub fn main(mut agb: agb::Gba) -> ! { music_box.before_frame(&mut mixer); mixer.frame(); vblank.wait_for_vblank(); - mixer.after_vblank(); object.commit(); } break; @@ -944,7 +938,6 @@ pub fn main(mut agb: agb::Gba) -> ! { music_box.before_frame(&mut mixer); mixer.frame(); vblank.wait_for_vblank(); - mixer.after_vblank(); object.commit(); } diff --git a/examples/the-hat-chooses-the-wizard/src/splash_screen.rs b/examples/the-hat-chooses-the-wizard/src/splash_screen.rs index 7be40dda..12991514 100644 --- a/examples/the-hat-chooses-the-wizard/src/splash_screen.rs +++ b/examples/the-hat-chooses-the-wizard/src/splash_screen.rs @@ -41,10 +41,6 @@ pub fn show_splash_screen( vblank.wait_for_vblank(); - if let Some(ref mut mixer) = mixer { - mixer.after_vblank(); - } - for y in 0..20u16 { for x in 0..30u16 { map.set_tile( @@ -63,10 +59,6 @@ pub fn show_splash_screen( } vblank.wait_for_vblank(); - - if let Some(ref mut mixer) = mixer { - mixer.after_vblank(); - } } map.commit(vram); @@ -83,17 +75,13 @@ pub fn show_splash_screen( ) { break; } - if let Some(ref mut mixer) = mixer { - if let Some(ref mut music_box) = music_box { + if let Some(mixer) = &mut mixer { + if let Some(music_box) = &mut music_box { music_box.before_frame(mixer); } mixer.frame(); } vblank.wait_for_vblank(); - - if let Some(ref mut mixer) = mixer { - mixer.after_vblank(); - } } map.hide(); diff --git a/examples/the-purple-night/src/lib.rs b/examples/the-purple-night/src/lib.rs index 1dc5513f..f513effc 100644 --- a/examples/the-purple-night/src/lib.rs +++ b/examples/the-purple-night/src/lib.rs @@ -85,7 +85,6 @@ impl<'a> Level<'a> { let mut between_updates = || { sfx.frame(); vblank.wait_for_vblank(); - sfx.after_vblank(); }; backdrop.init(vram, start_pos, &mut between_updates); @@ -2283,7 +2282,6 @@ fn game_with_level(gba: &mut agb::Gba) { start_at_boss = loop { sfx.frame(); vblank.wait_for_vblank(); - sfx.after_vblank(); object.commit(); match game.advance_frame(&object, &mut vram, &mut sfx) { GameStatus::Continue => {} diff --git a/examples/the-purple-night/src/sfx.rs b/examples/the-purple-night/src/sfx.rs index 08f26800..0e8ac5be 100644 --- a/examples/the-purple-night/src/sfx.rs +++ b/examples/the-purple-night/src/sfx.rs @@ -39,10 +39,6 @@ impl<'a> Sfx<'a> { self.mixer.frame(); } - pub fn after_vblank(&mut self) { - self.mixer.after_vblank(); - } - pub fn stop_music(&mut self) { if let Some(bgm) = &self.bgm { let channel = self.mixer.channel(bgm).unwrap(); From 8efbf87c21ceeb3deeb8096e21767a372479d058 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Fri, 9 Dec 2022 23:09:16 +0000 Subject: [PATCH 05/12] Get rid of references to manual interrupt work --- agb/src/sound/mixer/mod.rs | 38 ++++++--------------------------- agb/src/sound/mixer/sw_mixer.rs | 1 - 2 files changed, 7 insertions(+), 32 deletions(-) diff --git a/agb/src/sound/mixer/mod.rs b/agb/src/sound/mixer/mod.rs index e0f0ca3b..3f43d7b5 100644 --- a/agb/src/sound/mixer/mod.rs +++ b/agb/src/sound/mixer/mod.rs @@ -45,11 +45,13 @@ //! //! ## Doing the per-frame work //! -//! Then, you have a choice of whether you want to use interrupts or do the buffer swapping -//! yourself after a vblank interrupt. If you are using 32768Hz as the frequency of your -//! files, you _must_ use the interrupt version. +//! Despite being high performance, the mixer still takes a sizable portion of CPU time (6-10% +//! depending on number of channels and frequency) to do the per-frame tasks, so should be done +//! towards the end of the frame time (just before waiting for vblank) in order to give as much +//! time during vblank as possible for rendering related tasks. //! -//! Without interrupts: +//! In order to avoid skipping audio, call the [`Mixer::frame()`] function at least once per frame +//! as shown below: //! //! ```rust,no_run //! # #![no_std] @@ -61,35 +63,9 @@ //! // Somewhere in your main loop: //! mixer.frame(); //! vblank.wait_for_vblank(); -//! mixer.after_vblank(); //! # } //! ``` //! -//! Or with interrupts: -//! -//! ```rust,no_run -//! # #![no_std] -//! # #![no_main] -//! use agb::sound::mixer::Frequency; -//! # fn foo(gba: &mut agb::Gba) { -//! let mut mixer = gba.mixer.mixer(agb::sound::mixer::Frequency::Hz32768); -//! let vblank = agb::interrupt::VBlank::get(); -//! // outside your main loop, close to initialisation -//! // you must assign this to a variable (not to _ or ignored) or rust will immediately drop it -//! // and prevent the interrupt handler from firing. -//! let _mixer_interrupt = mixer.setup_interrupt_handler(); -//! -//! // inside your main loop -//! mixer.frame(); -//! vblank.wait_for_vblank(); -//! # } -//! ``` -//! -//! Despite being high performance, the mixer still takes a sizable portion of CPU time (6-10% -//! depending on number of channels and frequency) to do the per-frame tasks, so should be done -//! towards the end of the frame time (just before waiting for vblank) in order to give as much -//! time during vblank as possible for rendering related tasks. -//! //! ## Loading a sample //! //! To load a sample, you must have it in `wav` format (both stereo and mono work) at exactly the @@ -159,7 +135,7 @@ pub enum Frequency { Hz10512, /// 18157Hz Hz18157, - /// 32768Hz - note that this option requires interrupts for buffer swapping + /// 32768Hz Hz32768, } diff --git a/agb/src/sound/mixer/sw_mixer.rs b/agb/src/sound/mixer/sw_mixer.rs index 0316f3bb..cf161a30 100644 --- a/agb/src/sound/mixer/sw_mixer.rs +++ b/agb/src/sound/mixer/sw_mixer.rs @@ -187,7 +187,6 @@ impl Mixer { /// loop { /// mixer.frame(); /// vblank.wait_for_vblank(); - /// mixer.after_vblank(); // optional, only if not using interrupts /// } /// # } /// ``` From 6c27e770b3db9021460646ddd0dc4307d1922b3e Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Fri, 9 Dec 2022 23:10:44 +0000 Subject: [PATCH 06/12] Changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 11497da3..4a9f6955 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Text renderer can now be re-used which is useful for rpg style character/word at a time text boxes. +- Audio now automatically uses interrupts, so you can remove the `setup_interrupt_handler` or `after_vblank` calls to the mixer. ## [0.12.2] - 2022/10/22 From c031e9b002bf2bb2c6b0068e3211519a455973a0 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Fri, 9 Dec 2022 23:33:25 +0000 Subject: [PATCH 07/12] Put the MixerBuffer in IWRAM --- agb/src/sound/mixer/sw_mixer.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/agb/src/sound/mixer/sw_mixer.rs b/agb/src/sound/mixer/sw_mixer.rs index cf161a30..3aac9438 100644 --- a/agb/src/sound/mixer/sw_mixer.rs +++ b/agb/src/sound/mixer/sw_mixer.rs @@ -84,7 +84,7 @@ pub struct Mixer { // SAFETY: Has to go before buffer because it holds a reference to it _interrupt_handler: InterruptHandler<'static>, - buffer: Pin>, + buffer: Pin>, channels: [Option; 8], indices: [i32; 8], frequency: Frequency, @@ -117,7 +117,7 @@ pub struct ChannelId(usize, i32); impl Mixer { pub(super) fn new(frequency: Frequency) -> Self { - let buffer = Box::pin(MixerBuffer::new(frequency)); + let buffer = Box::pin_in(MixerBuffer::new(frequency), InternalAllocator); // SAFETY: you can only ever have 1 Mixer at a time let fifo_timer = unsafe { Timer::new(0) }; From 6f2c1bc6161ad0a97afdce26639f4bf92eed6f7f Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Sat, 10 Dec 2022 00:00:36 +0000 Subject: [PATCH 08/12] Don't need to do the mod3_estimate here too --- agb/src/sound/mixer/sw_mixer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agb/src/sound/mixer/sw_mixer.rs b/agb/src/sound/mixer/sw_mixer.rs index 3aac9438..dc6b3629 100644 --- a/agb/src/sound/mixer/sw_mixer.rs +++ b/agb/src/sound/mixer/sw_mixer.rs @@ -337,7 +337,7 @@ const fn mod3_estimate(x: usize) -> usize { impl MixerBufferState { fn should_calculate(&self) -> bool { - mod3_estimate(self.active_buffer + 1) != mod3_estimate(self.playing_buffer) + mod3_estimate(self.active_buffer + 1) != self.playing_buffer } fn playing_advanced(&mut self) -> usize { From a870a1deda2f4794ac4138660389701668f76b72 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Sat, 10 Dec 2022 00:01:08 +0000 Subject: [PATCH 09/12] Can set the asm buffer size just once --- agb/src/sound/mixer/sw_mixer.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/agb/src/sound/mixer/sw_mixer.rs b/agb/src/sound/mixer/sw_mixer.rs index dc6b3629..af2d68ef 100644 --- a/agb/src/sound/mixer/sw_mixer.rs +++ b/agb/src/sound/mixer/sw_mixer.rs @@ -140,6 +140,8 @@ impl Mixer { buffer_pointer_for_interrupt_handler.swap(cs); }); + set_asm_buffer_size(frequency); + Self { frequency, buffer, @@ -391,8 +393,6 @@ impl MixerBuffer { } fn write_channels<'a>(&mut self, channels: impl Iterator) { - set_asm_buffer_size(self.frequency); - self.working_buffer.fill(0.into()); for channel in channels { From 93d0fe570029bf02c341470226e57cb1fc3eae91 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Sat, 10 Dec 2022 00:19:05 +0000 Subject: [PATCH 10/12] Start refactoring towards needing &self in write_channels --- agb/src/sound/mixer/sw_mixer.rs | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/agb/src/sound/mixer/sw_mixer.rs b/agb/src/sound/mixer/sw_mixer.rs index af2d68ef..285e086b 100644 --- a/agb/src/sound/mixer/sw_mixer.rs +++ b/agb/src/sound/mixer/sw_mixer.rs @@ -89,6 +89,8 @@ pub struct Mixer { indices: [i32; 8], frequency: Frequency, + working_buffer: Box<[Num], InternalAllocator>, + fifo_timer: Timer, } @@ -142,6 +144,10 @@ impl Mixer { set_asm_buffer_size(frequency); + let mut working_buffer = + Vec::with_capacity_in(frequency.buffer_size() * 2, InternalAllocator); + working_buffer.resize(frequency.buffer_size() * 2, 0.into()); + Self { frequency, buffer, @@ -151,6 +157,7 @@ impl Mixer { interrupt_timer, _interrupt_handler: interrupt_handler, + working_buffer: working_buffer.into_boxed_slice(), fifo_timer, } } @@ -198,7 +205,7 @@ impl Mixer { } self.buffer - .write_channels(self.channels.iter_mut().flatten()); + .write_channels(&mut self.working_buffer, self.channels.iter_mut().flatten()); } /// Start playing a given [`SoundChannel`]. @@ -315,7 +322,6 @@ impl SoundBuffer { struct MixerBuffer { buffers: [SoundBuffer; 3], - working_buffer: Box<[Num], InternalAllocator>, frequency: Frequency, state: Mutex>, @@ -355,10 +361,6 @@ impl MixerBufferState { impl MixerBuffer { fn new(frequency: Frequency) -> Self { - let mut working_buffer = - Vec::with_capacity_in(frequency.buffer_size() * 2, InternalAllocator); - working_buffer.resize(frequency.buffer_size() * 2, 0.into()); - MixerBuffer { buffers: [ SoundBuffer::new(frequency), @@ -366,8 +368,6 @@ impl MixerBuffer { SoundBuffer::new(frequency), ], - working_buffer: working_buffer.into_boxed_slice(), - state: Mutex::new(RefCell::new(MixerBufferState { active_buffer: 0, playing_buffer: 0, @@ -392,8 +392,12 @@ impl MixerBuffer { hw::enable_dma_for_sound(right_buffer, LeftOrRight::Right); } - fn write_channels<'a>(&mut self, channels: impl Iterator) { - self.working_buffer.fill(0.into()); + fn write_channels<'a>( + &mut self, + working_buffer: &mut [Num], + channels: impl Iterator, + ) { + working_buffer.fill(0.into()); for channel in channels { if channel.is_done { @@ -422,7 +426,7 @@ impl MixerBuffer { unsafe { agb_rs__mixer_add_stereo( channel.data.as_ptr().add(channel.pos.floor()), - self.working_buffer.as_mut_ptr(), + working_buffer.as_mut_ptr(), channel.volume, ); } @@ -433,7 +437,7 @@ impl MixerBuffer { unsafe { agb_rs__mixer_add( channel.data.as_ptr().add(channel.pos.floor()), - self.working_buffer.as_mut_ptr(), + working_buffer.as_mut_ptr(), playback_speed, left_amount, right_amount, @@ -449,7 +453,7 @@ impl MixerBuffer { let write_buffer = &mut self.buffers[write_buffer_index].0; unsafe { - agb_rs__mixer_collapse(write_buffer.as_mut_ptr(), self.working_buffer.as_ptr()); + agb_rs__mixer_collapse(write_buffer.as_mut_ptr(), working_buffer.as_ptr()); } } } From 5a71db0c1745e2e5c377701981b0076e222bd845 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Sat, 10 Dec 2022 00:28:30 +0000 Subject: [PATCH 11/12] Move buffer management entirely to the mixer state --- agb/src/sound/mixer/hw.rs | 10 +++++----- agb/src/sound/mixer/sw_mixer.rs | 35 +++++++++++++++------------------ 2 files changed, 21 insertions(+), 24 deletions(-) diff --git a/agb/src/sound/mixer/hw.rs b/agb/src/sound/mixer/hw.rs index f2dcf45c..ba6b7ba5 100644 --- a/agb/src/sound/mixer/hw.rs +++ b/agb/src/sound/mixer/hw.rs @@ -46,23 +46,23 @@ pub(super) enum LeftOrRight { Right, } -pub(super) fn enable_dma_for_sound(sound_memory: &[i8], lr: LeftOrRight) { +pub(super) fn enable_dma_for_sound(sound_memory: *const i8, lr: LeftOrRight) { match lr { LeftOrRight::Left => enable_dma1_for_sound(sound_memory), LeftOrRight::Right => enable_dma2_for_sound(sound_memory), } } -fn enable_dma1_for_sound(sound_memory: &[i8]) { +fn enable_dma1_for_sound(sound_memory: *const i8) { DMA1_CONTROL.set(0); - DMA1_SOURCE_ADDR.set(sound_memory.as_ptr() as u32); + DMA1_SOURCE_ADDR.set(sound_memory as u32); DMA1_DEST_ADDR.set(FIFO_A_DEST_ADDR); DMA1_CONTROL.set(DMA_CONTROL_SETTING_FOR_SOUND); } -fn enable_dma2_for_sound(sound_memory: &[i8]) { +fn enable_dma2_for_sound(sound_memory: *const i8) { DMA2_CONTROL.set(0); - DMA2_SOURCE_ADDR.set(sound_memory.as_ptr() as u32); + DMA2_SOURCE_ADDR.set(sound_memory as u32); DMA2_DEST_ADDR.set(FIFO_B_DEST_ADDR); DMA2_CONTROL.set(DMA_CONTROL_SETTING_FOR_SOUND); } diff --git a/agb/src/sound/mixer/sw_mixer.rs b/agb/src/sound/mixer/sw_mixer.rs index 285e086b..370b52aa 100644 --- a/agb/src/sound/mixer/sw_mixer.rs +++ b/agb/src/sound/mixer/sw_mixer.rs @@ -321,7 +321,6 @@ impl SoundBuffer { } struct MixerBuffer { - buffers: [SoundBuffer; 3], frequency: Frequency, state: Mutex>, @@ -330,6 +329,7 @@ struct MixerBuffer { struct MixerBufferState { active_buffer: usize, playing_buffer: usize, + buffers: [SoundBuffer; 3], } /// Only returns a valid result if 0 <= x <= 3 @@ -348,29 +348,28 @@ impl MixerBufferState { mod3_estimate(self.active_buffer + 1) != self.playing_buffer } - fn playing_advanced(&mut self) -> usize { + fn playing_advanced(&mut self) -> *const i8 { self.playing_buffer = mod3_estimate(self.playing_buffer + 1); - self.playing_buffer + self.buffers[self.playing_buffer].0.as_ptr() } - fn active_advanced(&mut self) -> usize { + fn active_advanced(&mut self) -> *mut i8 { self.active_buffer = mod3_estimate(self.active_buffer + 1); - self.active_buffer + self.buffers[self.active_buffer].0.as_mut_ptr() } } impl MixerBuffer { fn new(frequency: Frequency) -> Self { MixerBuffer { - buffers: [ - SoundBuffer::new(frequency), - SoundBuffer::new(frequency), - SoundBuffer::new(frequency), - ], - state: Mutex::new(RefCell::new(MixerBufferState { active_buffer: 0, playing_buffer: 0, + buffers: [ + SoundBuffer::new(frequency), + SoundBuffer::new(frequency), + SoundBuffer::new(frequency), + ], })), frequency, @@ -384,16 +383,16 @@ impl MixerBuffer { fn swap(&self, cs: CriticalSection) { let buffer = self.state.borrow(cs).borrow_mut().playing_advanced(); - let (left_buffer, right_buffer) = self.buffers[buffer] - .0 - .split_at(self.frequency.buffer_size()); + let left_buffer = buffer; + // SAFETY: starting pointer is fine, resulting pointer also fine because buffer has length buffer_size() * 2 by construction + let right_buffer = unsafe { buffer.add(self.frequency.buffer_size()) }; hw::enable_dma_for_sound(left_buffer, LeftOrRight::Left); hw::enable_dma_for_sound(right_buffer, LeftOrRight::Right); } fn write_channels<'a>( - &mut self, + &self, working_buffer: &mut [Num], channels: impl Iterator, ) { @@ -448,12 +447,10 @@ impl MixerBuffer { channel.pos += playback_speed * self.frequency.buffer_size(); } - let write_buffer_index = free(|cs| self.state.borrow(cs).borrow_mut().active_advanced()); - - let write_buffer = &mut self.buffers[write_buffer_index].0; + let write_buffer = free(|cs| self.state.borrow(cs).borrow_mut().active_advanced()); unsafe { - agb_rs__mixer_collapse(write_buffer.as_mut_ptr(), working_buffer.as_ptr()); + agb_rs__mixer_collapse(write_buffer, working_buffer.as_ptr()); } } } From d57a1edbd4e17046d43f86ed75289b1e032ee838 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Sat, 10 Dec 2022 00:32:57 +0000 Subject: [PATCH 12/12] Remove stray reference to after_vblank --- agb/src/sound/mixer/sw_mixer.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/agb/src/sound/mixer/sw_mixer.rs b/agb/src/sound/mixer/sw_mixer.rs index 370b52aa..661ac949 100644 --- a/agb/src/sound/mixer/sw_mixer.rs +++ b/agb/src/sound/mixer/sw_mixer.rs @@ -75,7 +75,6 @@ extern "C" { /// loop { /// mixer.frame(); /// vblank.wait_for_vblank(); -/// mixer.after_vblank(); /// } /// # } /// ```