Give the same assembly treatment to mono sounds

This commit is contained in:
Gwilym Inzani 2023-07-25 00:18:48 +01:00
parent de666a54f9
commit 49b9a07a87
2 changed files with 83 additions and 73 deletions

View file

@ -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 agb_arm_func \fn_name
@ Arguments @ Arguments
@ r0 - pointer to the sample data from the beginning @ r0 - pointer to the sample data from the beginning
@ -26,11 +26,15 @@ agb_arm_func \fn_name
.irp reg, r7,r8,r9,r10 .irp reg, r7,r8,r9,r10
cmp r4, r5, lsr #8 @ check if we're overflowing 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 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 mov r11, r5, lsr #8 @ calculate the next location to get a value from
ldrsb r11, [r0, r11] @ load a single value 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 mul \reg, r11, r12
.else .else
mla \reg, r11, r12, \reg mla \reg, r11, r12, \reg
@ -44,6 +48,35 @@ agb_arm_func \fn_name
subs r2, r2, #4 subs r2, r2, #4
bne 1b 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 mov r0, r5 @ return the playback position
pop {{r4-r11,lr}} pop {{r4-r11,lr}}
@ -51,8 +84,10 @@ agb_arm_func \fn_name
agb_arm_end \fn_name agb_arm_end \fn_name
.endm .endm
mono_add_fn_loop agb_rs__mixer_add_mono_loop_first true mono_add_fn_loop agb_rs__mixer_add_mono_loop_first true true
mono_add_fn_loop agb_rs__mixer_add_mono_loop false 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 .macro stereo_add_fn fn_name:req is_first:req
agb_arm_func \fn_name agb_arm_func \fn_name

View file

@ -19,6 +19,21 @@ use crate::{
timer::Timer, 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<u32, 8>,
channel_length: usize,
current_pos: Num<u32, 8>,
playback_speed: Num<u32, 8>,
mul_amount: i32,
) -> Num<u32, 8>;
};
}
// Defined in mixer.s // Defined in mixer.s
extern "C" { extern "C" {
fn agb_rs__mixer_add_stereo( fn agb_rs__mixer_add_stereo(
@ -41,27 +56,10 @@ extern "C" {
num_samples: usize, num_samples: usize,
); );
fn agb_rs__mixer_add_mono_loop_first( add_mono_fn!(agb_rs__mixer_add_mono_loop_first);
sample_data: *const u8, add_mono_fn!(agb_rs__mixer_add_mono_loop);
sample_buffer: *mut i32, add_mono_fn!(agb_rs__mixer_add_mono_first);
buffer_size: usize, add_mono_fn!(agb_rs__mixer_add_mono);
restart_amount: Num<u32, 8>,
channel_length: usize,
current_pos: Num<u32, 8>,
playback_speed: Num<u32, 8>,
mul_amount: i32,
) -> Num<u32, 8>;
fn agb_rs__mixer_add_mono_loop(
sample_data: *const u8,
sample_buffer: *mut i32,
buffer_size: usize,
restart_amount: Num<u32, 8>,
channel_length: usize,
current_pos: Num<u32, 8>,
playback_speed: Num<u32, 8>,
mul_amount: i32,
) -> Num<u32, 8>;
} }
/// The main software mixer struct. /// The main software mixer struct.
@ -518,56 +516,33 @@ impl MixerBuffer {
let mul_amount = let mul_amount =
((left_amount.to_raw() as i32) << 16) | (right_amount.to_raw() as i32 & 0x0000ffff); ((left_amount.to_raw() as i32) << 16) | (right_amount.to_raw() as i32 & 0x0000ffff);
if IS_FIRST && channel.should_loop { macro_rules! call_mono_fn {
channel.pos = unsafe { ($fn_name:ident) => {
agb_rs__mixer_add_mono_loop_first( channel.pos = unsafe {
channel.data.as_ptr(), $fn_name(
working_buffer_i32.as_mut_ptr(), channel.data.as_ptr(),
working_buffer_i32.len(), working_buffer_i32.as_mut_ptr(),
channel_len - channel.restart_point, working_buffer_i32.len(),
channel.data.len(), channel_len - channel.restart_point,
channel.pos, channel.data.len(),
channel.playback_speed, channel.pos,
mul_amount, 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;
} }
};
}
// SAFETY: channel.pos < channel_len by the above if statement and the fact we reduce the playback speed match (IS_FIRST, channel.should_loop) {
let value = unsafe { *channel.data.get_unchecked(channel.pos.floor() as usize) } (true, true) => call_mono_fn!(agb_rs__mixer_add_mono_loop_first),
as i8 as i32; (false, true) => call_mono_fn!(agb_rs__mixer_add_mono_loop),
(true, false) => {
// SAFETY: working buffer length = self.frequency.buffer_size() call_mono_fn!(agb_rs__mixer_add_mono_first);
if IS_FIRST { channel.is_done = channel.pos > channel_len;
unsafe { }
*working_buffer_i32.get_unchecked_mut(i) = value.wrapping_mul(mul_amount); (false, false) => {
} call_mono_fn!(agb_rs__mixer_add_mono);
} else { channel.is_done = channel.pos > channel_len;
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;
} }
} }
} }