Use asm for the tight loops in the sound mixer

This commit is contained in:
Gwilym Kuiper 2021-07-31 21:47:05 +01:00
parent e372123ba1
commit f5d771d349
6 changed files with 128 additions and 11 deletions

View file

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

View file

@ -63,3 +63,4 @@ b .Initialise_mb
.pool
.include "interrupt_simple.s"
.include "src/sound/mixer/mixer.s"

View file

@ -3,10 +3,10 @@
extern crate agb;
use agb::input::{Button, ButtonController, Tri};
use agb::number::Num;
use agb::sound::mixer::SoundChannel;
use agb::Gba;
use agb::input::{ButtonController, Tri, Button};
use agb::number::Num;
// Music - "I will not let you let me down" by Josh Woodward, free download at http://joshwoodward.com
const I_WILL_NOT_LET_YOU_LET_ME_DOWN: &[u8] = include_bytes!("i-will-not-let-you-let-me-down.raw");

View file

@ -91,6 +91,7 @@ fixed_width_unsigned_integer_impl!(usize);
fixed_width_signed_integer_impl!(i16);
fixed_width_signed_integer_impl!(i32);
#[repr(C)]
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct Num<I: FixedWidthUnsignedInteger, const N: usize>(I);

View file

@ -0,0 +1,98 @@
.arm
.global agb_rs__mixer_add
.section .iwram, "ax"
.align
agb_rs__mixer_add:
@ Arguments
@ r0 - pointer to the data to be copied (u8 array)
@ r1 - pointer to the sound buffer (i16 array)
@ r2 - playback speed (usize fixnum with 8 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)
@
@ The sound buffer must be SOUND_BUFFER_SIZE * 2 in size = 176 * 2
@ lr = amount to modify right channel by
push {r4-r10, lr}
@ load the right channel modification amount into lr
ldr lr, [sp, #32]
@ current write offset into the resulting buffer
mov r12, #0
mov r8, #352
@ current index
mov r5, #0
1:
@ load the current sound buffer location
mov r6, r1
@ calculate the address of the next read form the sound buffer
add r4, r0, r5, asr #8
@ calculate the position to read the next step from
add r5, r5, r2
@ load the current buffer value (r6 being the current location, r12 being the offset)
@ but pre-increment r6 by r12
ldrh r9, [r6, r12]!
@ load the current value we want to read
ldrsb r10, [r4]
@ increment the current write offset in the resulting buffer
add r12, r12, #2
@ check if we're done
cmp r12, #352
@ r7 = r10 * r3 + r9 = current sound value * left amount + previous buffer value
mla r7, r10, r3, r9
@ *(r6 + r8) = r7, r8 = 352 = offset for the right hand side
strh r7, [r6], r8
@ same for the left hand side (slightly confused here, but this is what was generated)
ldrh r7, [r6]
mla r4, r10, lr, r7
strh r4, [r6]
bne 1b
pop {r4-r10, lr}
bx lr
.pool
.arm
.global agb_rs__mixer_collapse
.section .iwram
.align
agb_rs__mixer_collapse:
@ Arguments:
@ r0 = target buffer (i8)
@ r1 = input buffer (i16) of fixnums with 4 bits of precision
mov r2, #0
1:
@ r12 = *r1; r1++
ldrsh r12, [r1], #2
lsr r3, r12, #4 @ r3 = r12 >> 4
cmn r12, #2048 @ compare r12 against -2048
mvnlt r3, #127 @ r3 = -127 if r12 <= 2048
cmp r12, #2048 @ compare r12 against 2048
movge r3, #127 @ r3 = 127 if r12 >= 2048
strb r3, [r0, -r2] @ r2 counts down, so need a negative offset
sub r2, r2, #1 @ r2 -= 1
cmn r2, #352 @ compare r2 against -352
bne 1b @ loop if not equal
bx lr

View file

@ -3,6 +3,19 @@ use super::hw::LeftOrRight;
use super::{SoundChannel, SoundPriority};
use crate::number::Num;
// Defined in mixer.s
extern "C" {
fn agb_rs__mixer_add(
sound_data: *const u8,
sound_buffer: *mut Num<i16, 4>,
playback_speed: Num<usize, 8>,
left_amount: Num<i16, 4>,
right_amount: Num<i16, 4>,
);
fn agb_rs__mixer_collapse(sound_buffer: *mut i8, input_buffer: *const Num<i16, 4>);
}
pub struct Mixer {
buffer: MixerBuffer,
channels: [Option<SoundChannel>; 16],
@ -131,18 +144,21 @@ impl MixerBuffer {
}
}
for i in 0..SOUND_BUFFER_SIZE {
let v = (channel.data[channel.pos.floor()] as i8) as i16;
channel.pos += channel.playback_speed;
buffer[i] += left_amount * v;
buffer[i + SOUND_BUFFER_SIZE] += right_amount * v;
unsafe {
agb_rs__mixer_add(
channel.data.as_ptr().offset(channel.pos.floor() as isize),
buffer.as_mut_ptr(),
channel.playback_speed,
left_amount,
right_amount,
);
}
channel.pos += channel.playback_speed * SOUND_BUFFER_SIZE;
}
let write_buffer = self.get_write_buffer();
for i in 0..SOUND_BUFFER_SIZE * 2 {
write_buffer[i] = buffer[i].floor().clamp(i8::MIN as i16, i8::MAX as i16) as i8
unsafe {
agb_rs__mixer_collapse(write_buffer.as_mut_ptr(), buffer.as_ptr());
}
}