From 306be59cdea49c5d96aa6ede598b9e15353f8819 Mon Sep 17 00:00:00 2001 From: Gwilym Inzani Date: Tue, 27 Jun 2023 21:31:06 +0100 Subject: [PATCH 1/7] Add implementation for if this is the first --- agb/src/sound/mixer/mixer.s | 17 +++++++++++++++-- agb/src/sound/mixer/sw_mixer.rs | 8 +++++++- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/agb/src/sound/mixer/mixer.s b/agb/src/sound/mixer/mixer.s index 2299a22b..4fb974c8 100644 --- a/agb/src/sound/mixer/mixer.s +++ b/agb/src/sound/mixer/mixer.s @@ -91,7 +91,8 @@ same_modification: agb_arm_end agb_rs__mixer_add -agb_arm_func agb_rs__mixer_add_stereo +.macro stereo_add_fn fn_name:req is_first:req +agb_arm_func \fn_name @ Arguments @ r0 - pointer to the data to be copied (u8 array) @ r1 - pointer to the sound buffer (i16 array which will alternate left and right channels, 32-bit aligned) @@ -127,16 +128,24 @@ agb_arm_func agb_rs__mixer_add_stereo lsl r6, r6, #24 @ r6 = | R | 0 | 0 | 0 | drop everything except the right sample orr r6, r7, r6, asr #8 @ r6 = | 1 | R | 1 | L | now we have it perfectly set up +.ifc \is_first,true + mul \sample_reg, r6, r2 +.else mla \sample_reg, r6, r2, \sample_reg @ r4 += r6 * r2 (calculating both the left and right samples together) +.endif .endm 1: +.ifc \is_first,true +.else ldmia r1, {{r9-r12}} @ read the current values +.endif add_stereo_sample r9 add_stereo_sample r10 add_stereo_sample r11 add_stereo_sample r12 +.purgem add_stereo_sample stmia r1!, {{r9-r12}} @ store the new value, and increment the pointer @@ -146,7 +155,11 @@ agb_arm_func agb_rs__mixer_add_stereo pop {{r4-r11}} bx lr -agb_arm_end agb_rs__mixer_add_stereo +agb_arm_end \fn_name +.endm + +stereo_add_fn agb_rs__mixer_add_stereo false +stereo_add_fn agb_rs__mixer_add_stereo_first true agb_arm_func agb_rs__mixer_collapse @ Arguments: diff --git a/agb/src/sound/mixer/sw_mixer.rs b/agb/src/sound/mixer/sw_mixer.rs index 387c67fb..efa4ce23 100644 --- a/agb/src/sound/mixer/sw_mixer.rs +++ b/agb/src/sound/mixer/sw_mixer.rs @@ -35,6 +35,12 @@ extern "C" { volume: Num, ); + fn agb_rs__mixer_add_stereo_first( + sound_data: *const u8, + sound_buffer: *mut Num, + volume: Num, + ); + fn agb_rs__mixer_collapse( sound_buffer: *mut i8, input_buffer: *const Num, @@ -406,7 +412,7 @@ impl MixerBuffer { working_buffer: &mut [Num], channels: impl Iterator, ) { - working_buffer.fill(0.into()); + // working_buffer.fill(0.into()); for channel in channels { if channel.is_done { From e1d03929f93ae5e71d4ad4c0f8de32e8706f1e93 Mon Sep 17 00:00:00 2001 From: Gwilym Inzani Date: Tue, 27 Jun 2023 21:33:56 +0100 Subject: [PATCH 2/7] Use local labels --- agb/src/sound/mixer/mixer.s | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/agb/src/sound/mixer/mixer.s b/agb/src/sound/mixer/mixer.s index 4fb974c8..5806373c 100644 --- a/agb/src/sound/mixer/mixer.s +++ b/agb/src/sound/mixer/mixer.s @@ -18,9 +18,9 @@ agb_arm_func agb_rs__mixer_add ldr r7, [sp, #20] @ load the right channel modification amount into r7 cmp r7, r3 @ check if left and right channel need the same modifications - beq same_modification + beq .Lsame_modification -modifications_fallback: +.Lmodifications_fallback: orr r7, r7, r3, lsl #16 @ r7 now is the left channel followed by the right channel modifications. mov r5, #0 @ current index we're reading from @@ -47,13 +47,13 @@ modifications_fallback: pop {{r4-r8}} bx lr -same_modification: +.Lsame_modification: @ check to see if this is a perfect power of 2 @ r5 is a scratch register, r7 = r3 = amount to modify sub r5, r7, #1 ands r5, r5, r7 - bne modifications_fallback @ not 0 means we need to do the full modification + bne .Lmodifications_fallback @ not 0 means we need to do the full modification @ count leading zeros of r7 into r3 mov r3, #0 From f4779208e34d4205ca8f15e503689be3e68464d6 Mon Sep 17 00:00:00 2001 From: Gwilym Inzani Date: Tue, 27 Jun 2023 21:48:01 +0100 Subject: [PATCH 3/7] Implement first cases for the non stereo --- agb/src/sound/mixer/mixer.s | 29 +++++++++++++++++++++-------- agb/src/sound/mixer/sw_mixer.rs | 10 +++++++++- 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/agb/src/sound/mixer/mixer.s b/agb/src/sound/mixer/mixer.s index 5806373c..254699c6 100644 --- a/agb/src/sound/mixer/mixer.s +++ b/agb/src/sound/mixer/mixer.s @@ -4,7 +4,8 @@ agb_rs__buffer_size: .word 0 -agb_arm_func agb_rs__mixer_add +.macro mixer_add fn_name:req is_first:req +agb_arm_func \fn_name @ Arguments @ r0 - pointer to the data to be copied (u8 array) @ r1 - pointer to the sound buffer (i16 array which will alternate left and right channels, 32-bit aligned) @@ -18,9 +19,9 @@ agb_arm_func agb_rs__mixer_add ldr r7, [sp, #20] @ load the right channel modification amount into r7 cmp r7, r3 @ check if left and right channel need the same modifications - beq .Lsame_modification + beq 3f @ same modification -.Lmodifications_fallback: +4: @ modification fallback orr r7, r7, r3, lsl #16 @ r7 now is the left channel followed by the right channel modifications. mov r5, #0 @ current index we're reading from @@ -34,9 +35,12 @@ agb_arm_func agb_rs__mixer_add ldrsb r6, [r4] @ load the current sound sample to r6 add r5, r5, r2 @ calculate the position to read the next sample from +.ifc is_first,true + mul r4, r6, r7 @ r4 = r6 * r7 (calculating both the left and right samples together) +.else ldr r4, [r1] @ read the current value - mla r4, r6, r7, r4 @ r4 += r6 * r7 (calculating both the left and right samples together) +.endif str r4, [r1], #4 @ store the new value, and increment the pointer .endr @@ -47,13 +51,13 @@ agb_arm_func agb_rs__mixer_add pop {{r4-r8}} bx lr -.Lsame_modification: +3: @ same modification @ check to see if this is a perfect power of 2 @ r5 is a scratch register, r7 = r3 = amount to modify sub r5, r7, #1 ands r5, r5, r7 - bne .Lmodifications_fallback @ not 0 means we need to do the full modification + bne 4b @ not 0 means we need to do the full modification, jump to modification fallback @ count leading zeros of r7 into r3 mov r3, #0 @@ -74,11 +78,16 @@ agb_arm_func agb_rs__mixer_add ldrsb r6, [r4] @ load the current sound sample to r6 add r5, r5, r2 @ calculate the position to read the next sample from - ldr r4, [r1] @ read the current value lsl r6, r6, #16 orr r6, r6, lsr #16 + +.ifc is_first,true + mov r4, r6, lsl r3 @ r4 = r6 << r3 +.else + ldr r4, [r1] @ read the current value add r4, r4, r6, lsl r3 @ r4 += r6 << r3 (calculating both the left and right samples together) +.endif str r4, [r1], #4 @ store the new value, and increment the pointer .endr @@ -89,7 +98,11 @@ agb_arm_func agb_rs__mixer_add pop {{r4-r8}} bx lr -agb_arm_end agb_rs__mixer_add +agb_arm_end \fn_name +.endm + +mixer_add agb_rs__mixer_add false +mixer_add agb_rs__mixer_add_first true .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 efa4ce23..ffa637f3 100644 --- a/agb/src/sound/mixer/sw_mixer.rs +++ b/agb/src/sound/mixer/sw_mixer.rs @@ -29,6 +29,14 @@ extern "C" { right_amount: Num, ); + fn agb_rs__mixer_add_first( + sound_data: *const u8, + sound_buffer: *mut Num, + playback_speed: Num, + left_amount: Num, + right_amount: Num, + ); + fn agb_rs__mixer_add_stereo( sound_data: *const u8, sound_buffer: *mut Num, @@ -412,7 +420,7 @@ impl MixerBuffer { working_buffer: &mut [Num], channels: impl Iterator, ) { - // working_buffer.fill(0.into()); + working_buffer.fill(0.into()); for channel in channels { if channel.is_done { From 57f0a8c889953d0b75bc497536a4ebd0294b9ec2 Mon Sep 17 00:00:00 2001 From: Gwilym Inzani Date: Tue, 27 Jun 2023 21:49:30 +0100 Subject: [PATCH 4/7] Remove is_done check in for loop --- agb/src/sound/mixer/sw_mixer.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/agb/src/sound/mixer/sw_mixer.rs b/agb/src/sound/mixer/sw_mixer.rs index ffa637f3..a147fd21 100644 --- a/agb/src/sound/mixer/sw_mixer.rs +++ b/agb/src/sound/mixer/sw_mixer.rs @@ -422,11 +422,7 @@ impl MixerBuffer { ) { working_buffer.fill(0.into()); - for channel in channels { - if channel.is_done { - continue; - } - + for channel in channels.filter(|channel| !channel.is_done) { let playback_speed = if channel.is_stereo { 2.into() } else { From 38868cb269129bcd99e04ea4e1bafb72f7f50b84 Mon Sep 17 00:00:00 2001 From: Gwilym Inzani Date: Tue, 27 Jun 2023 21:50:46 +0100 Subject: [PATCH 5/7] Extract even more from the loop body --- agb/src/sound/mixer/sw_mixer.rs | 40 +++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/agb/src/sound/mixer/sw_mixer.rs b/agb/src/sound/mixer/sw_mixer.rs index a147fd21..5895a08f 100644 --- a/agb/src/sound/mixer/sw_mixer.rs +++ b/agb/src/sound/mixer/sw_mixer.rs @@ -422,25 +422,31 @@ impl MixerBuffer { ) { working_buffer.fill(0.into()); - for channel in channels.filter(|channel| !channel.is_done) { - let playback_speed = if channel.is_stereo { - 2.into() - } else { - channel.playback_speed - }; + for (channel, playback_speed) in + channels + .filter(|channel| !channel.is_done) + .filter_map(|channel| { + let playback_speed = if channel.is_stereo { + 2.into() + } else { + channel.playback_speed + }; - if (channel.pos + playback_speed * self.frequency.buffer_size() as u32).floor() - >= channel.data.len() as u32 - { - // TODO: This should probably play what's left rather than skip the last bit - if channel.should_loop { - channel.pos = 0.into(); - } else { - channel.is_done = true; - continue; - } - } + if (channel.pos + playback_speed * self.frequency.buffer_size() as u32).floor() + >= channel.data.len() as u32 + { + // TODO: This should probably play what's left rather than skip the last bit + if channel.should_loop { + channel.pos = 0.into(); + } else { + channel.is_done = true; + return None; + } + } + Some((channel, playback_speed)) + }) + { if channel.volume != 0.into() { if channel.is_stereo { unsafe { From 3b35061a3a18e5a5e4f375d4e6cc4dca1ddee58d Mon Sep 17 00:00:00 2001 From: Gwilym Inzani Date: Tue, 27 Jun 2023 21:51:59 +0100 Subject: [PATCH 6/7] Extract iterator to a variable --- agb/src/sound/mixer/sw_mixer.rs | 44 ++++++++++++++++----------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/agb/src/sound/mixer/sw_mixer.rs b/agb/src/sound/mixer/sw_mixer.rs index 5895a08f..2cd3257b 100644 --- a/agb/src/sound/mixer/sw_mixer.rs +++ b/agb/src/sound/mixer/sw_mixer.rs @@ -422,31 +422,31 @@ impl MixerBuffer { ) { working_buffer.fill(0.into()); - for (channel, playback_speed) in - channels - .filter(|channel| !channel.is_done) - .filter_map(|channel| { - let playback_speed = if channel.is_stereo { - 2.into() + let channels = channels + .filter(|channel| !channel.is_done) + .filter_map(|channel| { + let playback_speed = if channel.is_stereo { + 2.into() + } else { + channel.playback_speed + }; + + if (channel.pos + playback_speed * self.frequency.buffer_size() as u32).floor() + >= channel.data.len() as u32 + { + // TODO: This should probably play what's left rather than skip the last bit + if channel.should_loop { + channel.pos = 0.into(); } else { - channel.playback_speed - }; - - if (channel.pos + playback_speed * self.frequency.buffer_size() as u32).floor() - >= channel.data.len() as u32 - { - // TODO: This should probably play what's left rather than skip the last bit - if channel.should_loop { - channel.pos = 0.into(); - } else { - channel.is_done = true; - return None; - } + channel.is_done = true; + return None; } + } - Some((channel, playback_speed)) - }) - { + Some((channel, playback_speed)) + }); + + for (channel, playback_speed) in channels { if channel.volume != 0.into() { if channel.is_stereo { unsafe { From 13f5fe01d7d5daae877afcf3c50d682c4889c9c7 Mon Sep 17 00:00:00 2001 From: Gwilym Inzani Date: Tue, 27 Jun 2023 22:34:45 +0100 Subject: [PATCH 7/7] Actually use the new first methods --- agb/src/sound/mixer/mixer.s | 4 ++-- agb/src/sound/mixer/sw_mixer.rs | 37 ++++++++++++++++++++++++++++++--- 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/agb/src/sound/mixer/mixer.s b/agb/src/sound/mixer/mixer.s index 254699c6..1f9db61b 100644 --- a/agb/src/sound/mixer/mixer.s +++ b/agb/src/sound/mixer/mixer.s @@ -35,7 +35,7 @@ agb_arm_func \fn_name ldrsb r6, [r4] @ load the current sound sample to r6 add r5, r5, r2 @ calculate the position to read the next sample from -.ifc is_first,true +.ifc \is_first,true mul r4, r6, r7 @ r4 = r6 * r7 (calculating both the left and right samples together) .else ldr r4, [r1] @ read the current value @@ -82,7 +82,7 @@ agb_arm_func \fn_name lsl r6, r6, #16 orr r6, r6, lsr #16 -.ifc is_first,true +.ifc \is_first,true mov r4, r6, lsl r3 @ r4 = r6 << r3 .else ldr r4, [r1] @ read the current value diff --git a/agb/src/sound/mixer/sw_mixer.rs b/agb/src/sound/mixer/sw_mixer.rs index 2cd3257b..9ae8e3bc 100644 --- a/agb/src/sound/mixer/sw_mixer.rs +++ b/agb/src/sound/mixer/sw_mixer.rs @@ -420,9 +420,7 @@ impl MixerBuffer { working_buffer: &mut [Num], channels: impl Iterator, ) { - working_buffer.fill(0.into()); - - let channels = channels + let mut channels = channels .filter(|channel| !channel.is_done) .filter_map(|channel| { let playback_speed = if channel.is_stereo { @@ -446,6 +444,39 @@ impl MixerBuffer { Some((channel, playback_speed)) }); + if let Some((channel, playback_speed)) = channels.next() { + if channel.volume != 0.into() { + if channel.is_stereo { + unsafe { + agb_rs__mixer_add_stereo_first( + channel.data.as_ptr().add(channel.pos.floor() as usize), + working_buffer.as_mut_ptr(), + channel.volume, + ); + } + } else { + let right_amount = ((channel.panning + 1) / 2) * channel.volume; + let left_amount = ((-channel.panning + 1) / 2) * channel.volume; + + unsafe { + agb_rs__mixer_add_first( + channel.data.as_ptr().add(channel.pos.floor() as usize), + working_buffer.as_mut_ptr(), + playback_speed, + left_amount, + right_amount, + ); + } + } + } else { + working_buffer.fill(0.into()); + } + + channel.pos += playback_speed * self.frequency.buffer_size() as u32; + } else { + working_buffer.fill(0.into()); + } + for (channel, playback_speed) in channels { if channel.volume != 0.into() { if channel.is_stereo {