diff --git a/agb/build.rs b/agb/build.rs index 217ba164..a2c32735 100644 --- a/agb/build.rs +++ b/agb/build.rs @@ -2,6 +2,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"; diff --git a/agb/crt0.s b/agb/crt0.s index a71b819b..1b51ec83 100644 --- a/agb/crt0.s +++ b/agb/crt0.s @@ -63,3 +63,4 @@ b .Initialise_mb .pool .include "interrupt_simple.s" +.include "src/sound/mixer/mixer.s" diff --git a/agb/examples/mixer_basic.rs b/agb/examples/mixer_basic.rs index 7b288633..c234642f 100644 --- a/agb/examples/mixer_basic.rs +++ b/agb/examples/mixer_basic.rs @@ -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"); @@ -35,7 +35,7 @@ pub fn main() -> ! { Tri::Zero => channel.panning(0.into()), Tri::Positive => channel.panning(half), }; - + match input.y_tri() { Tri::Negative => channel.playback(half_usize.change_base() + 1), Tri::Zero => channel.playback(1.into()), diff --git a/agb/src/number.rs b/agb/src/number.rs index 250b0a50..bf9c57f3 100644 --- a/agb/src/number.rs +++ b/agb/src/number.rs @@ -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); diff --git a/agb/src/sound/mixer/mixer.s b/agb/src/sound/mixer/mixer.s new file mode 100644 index 00000000..8acc9f75 --- /dev/null +++ b/agb/src/sound/mixer/mixer.s @@ -0,0 +1,86 @@ +.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 + + @ r9 = amount to modify right channel by + + push {r4-r10} + + 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 + + @ 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: + add r4, r0, r5, asr #8 @ calculate the address of the next read form the sound buffer + ldrsb r10, [r4] @ load the current value we want to read + add r5, r5, r2 @ calculate the position to read the next step from + + mov r6, r1 @ r6 = current buffer location + + ldrh r4, [r6, r12]! @ load the current buffer value (r12 being the offset) but pre-increment r6 by r12 + 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 + mla r4, r10, r9, r7 + strh r4, [r6] + + add r12, r12, #2 @ increment the current write offset in the resulting buffer + cmp r12, #352 @ check if we're done + bne 1b + + pop {r4-r10} + 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, #352 + +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], #1 @ *r0 = r3; r0++ + + subs r2, r2, #1 @ r2 -= 1 + bne 1b @ loop if not 0 + + bx lr +.pool \ No newline at end of file diff --git a/agb/src/sound/mixer/sw_mixer.rs b/agb/src/sound/mixer/sw_mixer.rs index accc4168..333c4550 100644 --- a/agb/src/sound/mixer/sw_mixer.rs +++ b/agb/src/sound/mixer/sw_mixer.rs @@ -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, + playback_speed: Num, + left_amount: Num, + right_amount: Num, + ); + + fn agb_rs__mixer_collapse(sound_buffer: *mut i8, input_buffer: *const Num); +} + pub struct Mixer { buffer: MixerBuffer, channels: [Option; 16], @@ -120,7 +133,7 @@ impl MixerBuffer { let right_amount = ((channel.panning + 1) / 2) * channel.volume; let left_amount = ((-channel.panning + 1) / 2) * channel.volume; - if channel.pos + channel.playback_speed * SOUND_BUFFER_SIZE >= channel.data.len().into() + if (channel.pos + channel.playback_speed * SOUND_BUFFER_SIZE).floor() >= channel.data.len() { // TODO: This should probably play what's left rather than skip the last bit if channel.should_loop { @@ -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().add(channel.pos.floor()), + 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()); } }