From 49b9a07a87ec2915fd0048c51186224e7d093904 Mon Sep 17 00:00:00 2001 From: Gwilym Inzani Date: Tue, 25 Jul 2023 00:18:48 +0100 Subject: [PATCH] Give the same assembly treatment to mono sounds --- agb/src/sound/mixer/mixer.s | 43 ++++++++++-- agb/src/sound/mixer/sw_mixer.rs | 113 +++++++++++++------------------- 2 files changed, 83 insertions(+), 73 deletions(-) diff --git a/agb/src/sound/mixer/mixer.s b/agb/src/sound/mixer/mixer.s index e6cbe929..71f3e87b 100644 --- a/agb/src/sound/mixer/mixer.s +++ b/agb/src/sound/mixer/mixer.s @@ -1,4 +1,4 @@ -.macro mono_add_fn_loop fn_name:req is_first:req +.macro mono_add_fn_loop fn_name:req is_first:req is_loop:req agb_arm_func \fn_name @ Arguments @ r0 - pointer to the sample data from the beginning @@ -26,11 +26,15 @@ agb_arm_func \fn_name .irp reg, r7,r8,r9,r10 cmp r4, r5, lsr #8 @ check if we're overflowing +.ifc \is_loop,true suble r5, r5, r3 @ if we are, subtract the overflow amount +.else + ble 2f @ if we are, zero the rest of the buffer +.endif mov r11, r5, lsr #8 @ calculate the next location to get a value from ldrsb r11, [r0, r11] @ load a single value -.ifc \is_first,true @ multiply the sample value, but only add if not the first call +.ifc \is_first,true @ multiply the sample value, but only add if not the first call mul \reg, r11, r12 .else mla \reg, r11, r12, \reg @@ -44,6 +48,35 @@ agb_arm_func \fn_name subs r2, r2, #4 bne 1b +.ifc \is_loop,false + b 3f + +2: +.ifc \is_first,true @ zero the rest of the buffer as this sample has ended + ands r7, r2, #3 + sub r2, r2, r7 + beq 5f + +4: + mov r8, #0 +4: + stmia r1!, {{r8}} + subs r7, r7, #1 + bne 4b + +5: +.irp reg, r7,r8,r9,r10 + mov \reg, #0 +.endr +5: + stmia r1!, {{r7-r10}} + subs r2, r2, #4 + bne 5b +.endif +3: +.endif + + mov r0, r5 @ return the playback position pop {{r4-r11,lr}} @@ -51,8 +84,10 @@ agb_arm_func \fn_name agb_arm_end \fn_name .endm -mono_add_fn_loop agb_rs__mixer_add_mono_loop_first true -mono_add_fn_loop agb_rs__mixer_add_mono_loop false +mono_add_fn_loop agb_rs__mixer_add_mono_loop_first true true +mono_add_fn_loop agb_rs__mixer_add_mono_loop false true +mono_add_fn_loop agb_rs__mixer_add_mono_first true false +mono_add_fn_loop agb_rs__mixer_add_mono false false .macro stereo_add_fn fn_name:req is_first:req agb_arm_func \fn_name diff --git a/agb/src/sound/mixer/sw_mixer.rs b/agb/src/sound/mixer/sw_mixer.rs index f572fb6b..9ad50bf0 100644 --- a/agb/src/sound/mixer/sw_mixer.rs +++ b/agb/src/sound/mixer/sw_mixer.rs @@ -19,6 +19,21 @@ use crate::{ timer::Timer, }; +macro_rules! add_mono_fn { + ($name:ident) => { + fn $name( + sample_data: *const u8, + sample_buffer: *mut i32, + buffer_size: usize, + restart_amount: Num, + channel_length: usize, + current_pos: Num, + playback_speed: Num, + mul_amount: i32, + ) -> Num; + }; +} + // Defined in mixer.s extern "C" { fn agb_rs__mixer_add_stereo( @@ -41,27 +56,10 @@ extern "C" { num_samples: usize, ); - fn agb_rs__mixer_add_mono_loop_first( - sample_data: *const u8, - sample_buffer: *mut i32, - buffer_size: usize, - restart_amount: Num, - channel_length: usize, - current_pos: Num, - playback_speed: Num, - mul_amount: i32, - ) -> Num; - - fn agb_rs__mixer_add_mono_loop( - sample_data: *const u8, - sample_buffer: *mut i32, - buffer_size: usize, - restart_amount: Num, - channel_length: usize, - current_pos: Num, - playback_speed: Num, - mul_amount: i32, - ) -> Num; + add_mono_fn!(agb_rs__mixer_add_mono_loop_first); + add_mono_fn!(agb_rs__mixer_add_mono_loop); + add_mono_fn!(agb_rs__mixer_add_mono_first); + add_mono_fn!(agb_rs__mixer_add_mono); } /// The main software mixer struct. @@ -518,56 +516,33 @@ impl MixerBuffer { let mul_amount = ((left_amount.to_raw() as i32) << 16) | (right_amount.to_raw() as i32 & 0x0000ffff); - if IS_FIRST && channel.should_loop { - channel.pos = unsafe { - agb_rs__mixer_add_mono_loop_first( - channel.data.as_ptr(), - working_buffer_i32.as_mut_ptr(), - working_buffer_i32.len(), - channel_len - channel.restart_point, - channel.data.len(), - channel.pos, - channel.playback_speed, - mul_amount, - ) - }; - } else if !IS_FIRST && channel.should_loop { - channel.pos = unsafe { - agb_rs__mixer_add_mono_loop( - channel.data.as_ptr(), - working_buffer_i32.as_mut_ptr(), - working_buffer_i32.len(), - channel_len - channel.restart_point, - channel.data.len(), - channel.pos, - channel.playback_speed, - mul_amount, - ) - }; - } else { - for i in 0..self.frequency.buffer_size() { - if channel.pos >= channel_len { - channel.is_done = true; - - break; + macro_rules! call_mono_fn { + ($fn_name:ident) => { + channel.pos = unsafe { + $fn_name( + channel.data.as_ptr(), + working_buffer_i32.as_mut_ptr(), + working_buffer_i32.len(), + channel_len - channel.restart_point, + channel.data.len(), + channel.pos, + channel.playback_speed, + mul_amount, + ) } + }; + } - // SAFETY: channel.pos < channel_len by the above if statement and the fact we reduce the playback speed - let value = unsafe { *channel.data.get_unchecked(channel.pos.floor() as usize) } - as i8 as i32; - - // SAFETY: working buffer length = self.frequency.buffer_size() - if IS_FIRST { - unsafe { - *working_buffer_i32.get_unchecked_mut(i) = value.wrapping_mul(mul_amount); - } - } else { - unsafe { - let value_ref = working_buffer_i32.get_unchecked_mut(i); - *value_ref = value_ref.wrapping_add(value.wrapping_mul(mul_amount)); - }; - } - channel.pos += playback_speed; + match (IS_FIRST, channel.should_loop) { + (true, true) => call_mono_fn!(agb_rs__mixer_add_mono_loop_first), + (false, true) => call_mono_fn!(agb_rs__mixer_add_mono_loop), + (true, false) => { + call_mono_fn!(agb_rs__mixer_add_mono_first); + channel.is_done = channel.pos > channel_len; + } + (false, false) => { + call_mono_fn!(agb_rs__mixer_add_mono); + channel.is_done = channel.pos > channel_len; } } }