Merge pull request #106 from gwilymk/half-multiplications-in-sound-mixing

Halve the number multiplications in sound mixing
This commit is contained in:
Gwilym Kuiper 2021-08-15 23:36:21 +01:00 committed by GitHub
commit 1b40fe2b03
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 72 additions and 58 deletions

View file

@ -2,6 +2,7 @@ fn main() {
println!("cargo:rerun-if-changed=crt0.s"); println!("cargo:rerun-if-changed=crt0.s");
println!("cargo:rerun-if-changed=gba_mb.ld"); println!("cargo:rerun-if-changed=gba_mb.ld");
println!("cargo:rerun-if-changed=src/sound/mixer/mixer.s"); println!("cargo:rerun-if-changed=src/sound/mixer/mixer.s");
println!("cargo:rerun-if-changed=src/asm_include.s");
println!("cargo:rerun-if-changed=interrupt_handler.s"); println!("cargo:rerun-if-changed=interrupt_handler.s");
println!("cargo:rerun-if-changed=gfx/test_logo.png"); println!("cargo:rerun-if-changed=gfx/test_logo.png");

View file

@ -1,3 +1,5 @@
.include "src/asm_include.s"
.arm .arm
.global __start .global __start
__start: __start:

15
agb/src/asm_include.s Normal file
View file

@ -0,0 +1,15 @@
.macro agb_arm_func functionName:req
.section .iwram, "ax", %progbits @ "ax" = allocatable and executable, %progbits = contains data
.arm
.align 2
.global \functionName
.type \functionName, %function
.func \functionName
\functionName:
.endm
.macro agb_arm_end functionName:req
.pool
.size \functionName,.-\functionName
.endfunc
.endm

View file

@ -1,86 +1,79 @@
.arm .equ SOUND_BUFFER_SIZE, 176
.global agb_rs__mixer_add
.section .iwram, "ax" agb_arm_func agb_rs__mixer_add
.align
agb_rs__mixer_add:
@ Arguments @ Arguments
@ r0 - pointer to the data to be copied (u8 array) @ r0 - pointer to the data to be copied (u8 array)
@ r1 - pointer to the sound buffer (i16 array) @ r1 - pointer to the sound buffer (i16 array which will alternate left and right channels, 32-bit aligned)
@ r2 - playback speed (usize fixnum with 8 bits) @ r2 - playback speed (usize fixnum with 8 bits)
@ r3 - amount to modify the left channel by (u16 fixnum with 4 bits) @ r3 - amount to modify the left channel by (u16 fixnum with 4 bits)
@ stack position 1 - amount to modify the right channel by (u16 fixnum with 4 bits) @ stack position 1 - amount to modify the right channel by (u16 fixnum with 4 bits)
@ @
@ The sound buffer must be SOUND_BUFFER_SIZE * 2 in size = 176 * 2 @ The sound buffer must be SOUND_BUFFER_SIZE * 2 in size = 176 * 2
push {r4-r8}
@ r9 = amount to modify right channel by ldr r7, [sp, #20] @ load the right channel modification amount into r7
push {r4-r10} orr r7, r7, r3, lsl #16 @ r7 now is the left channel followed by the right channel modifications.
ldr r9, [sp, #28] @ load the right channel modification amount into r9
mov r12, #0 @ current write offset into the resulting buffer
mov r8, #352 @ the offset for writing to the resulting buffer between left and right channels
mov r5, #0 @ current index we're reading from mov r5, #0 @ current index we're reading from
mov r8, #SOUND_BUFFER_SIZE @ the number of steps left
@ kept between iterations:
@ r12 - current write offset into the output buffer (r1)
@ r9 - the amount to modify the right channel by
@ r8 - the constant 352
@ r5 - the current index from the input buffer we're reading from (r0)
@ the provided arguments are all unmodified
@ all other registers are temporary
1: 1:
add r4, r0, r5, asr #8 @ calculate the address of the next read form the sound buffer .macro mixer_add_loop
ldrsb r10, [r4] @ load the current value we want to read add r4, r0, r5, asr #8 @ calculate the address of the next read from the sound buffer
add r5, r5, r2 @ calculate the position to read the next step from ldrsb r6, [r4] @ load the current sound sample to r6
add r5, r5, r2 @ calculate the position to read the next sample from
mov r6, r1 @ r6 = current buffer location ldr r4, [r1] @ read the current value
ldrh r4, [r6, r12]! @ load the current buffer value (r12 being the offset) but pre-increment r6 by r12 mla r4, r6, r7, r4 @ r4 += r6 * r7 (calculating both the left and right samples together)
mla r7, r10, r3, r4 @ r7 = r10 * r3 + r9 = current sound value * left amount + previous buffer value
strh r7, [r6], r8 @ *r6 = r7, r6 += r8 where r8 = 352 = offset for the right hand side
ldrh r7, [r6] @ same for the right hand side, r6 now points to the right hand side location str r4, [r1], #4 @ store the new value, and increment the pointer
mla r4, r10, r9, r7 .endm
strh r4, [r6]
add r12, r12, #2 @ increment the current write offset in the resulting buffer mixer_add_loop
cmp r12, #352 @ check if we're done mixer_add_loop
bne 1b mixer_add_loop
mixer_add_loop
pop {r4-r10} subs r8, r8, #4 @ loop counter
bne 1b @ jump back if we're done with the loop
pop {r4-r8}
bx lr bx lr
.pool agb_arm_end agb_rs__mixer_add
.arm .macro clamp_s8 reg:req
.global agb_rs__mixer_collapse cmn \reg, #127
.section .iwram mvnlt \reg, #127
.align
agb_rs__mixer_collapse: cmp \reg, #128
movgt \reg, #128
.endm
agb_arm_func agb_rs__mixer_collapse
@ Arguments: @ Arguments:
@ r0 = target buffer (i8) @ r0 = target buffer (i8)
@ r1 = input buffer (i16) of fixnums with 4 bits of precision @ r1 = input buffer (i16) of fixnums with 4 bits of precision (read in sets of i16 in an i32)
mov r2, #352 mov r2, #SOUND_BUFFER_SIZE @ loop counter
1: 1:
@ r12 = *r1; r1++ @ r12 = *r1; r1++
ldrsh r12, [r1], #2 ldr r12, [r1], #4
lsr r3, r12, #4 @ r3 = r12 >> 4 lsl r3, r12, #16 @ r3 is going to be the right sample, push r12 left 16 bits first
asr r3, r3, #20 @ move r3 back to being the correct value
mov r12, r12, asr #20 @ r12 = left sample
cmn r12, #2048 @ compare r12 against -2048 clamp_s8 r12 @ clamp the audio to 8 bit values
mvnlt r3, #127 @ r3 = -127 if r12 <= 2048 clamp_s8 r3
cmp r12, #2048 @ compare r12 against 2048 strb r3, [r0, #SOUND_BUFFER_SIZE] @ *(r0 + SOUND_BUFFER_SIZE) = r3
movge r3, #127 @ r3 = 127 if r12 >= 2048 strb r12, [r0], #1 @ *r0 = r12; r0++
strb r3, [r0], #1 @ *r0 = r3; r0++
subs r2, r2, #1 @ r2 -= 1 subs r2, r2, #1 @ r2 -= 1
bne 1b @ loop if not 0 bne 1b @ loop if not 0
bx lr bx lr
.pool agb_arm_end agb_rs__mixer_collapse

View file

@ -92,9 +92,12 @@ impl Mixer {
const SOUND_FREQUENCY: i32 = 10512; const SOUND_FREQUENCY: i32 = 10512;
const SOUND_BUFFER_SIZE: usize = 176; const SOUND_BUFFER_SIZE: usize = 176;
#[repr(C, align(4))]
struct SoundBuffer([i8; SOUND_BUFFER_SIZE * 2]);
struct MixerBuffer { struct MixerBuffer {
buffer1: [i8; SOUND_BUFFER_SIZE * 2], // first half is left, second is right buffer1: SoundBuffer, // alternating bytes left and right channels
buffer2: [i8; SOUND_BUFFER_SIZE * 2], buffer2: SoundBuffer,
buffer_1_active: bool, buffer_1_active: bool,
} }
@ -102,8 +105,8 @@ struct MixerBuffer {
impl MixerBuffer { impl MixerBuffer {
fn new() -> Self { fn new() -> Self {
MixerBuffer { MixerBuffer {
buffer1: [0; SOUND_BUFFER_SIZE * 2], buffer1: SoundBuffer([0; SOUND_BUFFER_SIZE * 2]),
buffer2: [0; SOUND_BUFFER_SIZE * 2], buffer2: SoundBuffer([0; SOUND_BUFFER_SIZE * 2]),
buffer_1_active: true, buffer_1_active: true,
} }
@ -164,9 +167,9 @@ impl MixerBuffer {
fn get_write_buffer(&mut self) -> &mut [i8; SOUND_BUFFER_SIZE * 2] { fn get_write_buffer(&mut self) -> &mut [i8; SOUND_BUFFER_SIZE * 2] {
if self.buffer_1_active { if self.buffer_1_active {
&mut self.buffer2 &mut self.buffer2.0
} else { } else {
&mut self.buffer1 &mut self.buffer1.0
} }
} }
} }