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,7 +26,11 @@ 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
@ -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,9 +516,10 @@ 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 {
($fn_name:ident) => {
channel.pos = unsafe { channel.pos = unsafe {
agb_rs__mixer_add_mono_loop_first( $fn_name(
channel.data.as_ptr(), channel.data.as_ptr(),
working_buffer_i32.as_mut_ptr(), working_buffer_i32.as_mut_ptr(),
working_buffer_i32.len(), working_buffer_i32.len(),
@ -530,44 +529,20 @@ impl MixerBuffer {
channel.playback_speed, channel.playback_speed,
mul_amount, 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
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;
} }
} }
} }