Merge pull request #123 from gwilymk/stereo-sound

Stereo sound
This commit is contained in:
Corwin 2021-10-27 18:30:37 +01:00 committed by GitHub
commit b7734726a6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 124 additions and 13 deletions

View file

@ -66,9 +66,12 @@ pub fn include_wav(input: TokenStream) -> TokenStream {
let result = quote! {
{
#[repr(align(4))]
struct AlignmentWrapper<const N: usize>([u8; N]);
const _: &[u8] = include_bytes!(#include_path);
include_bytes!(#out_file_path_include)
&AlignmentWrapper(*include_bytes!(#out_file_path_include)).0
}
};

Binary file not shown.

View file

@ -0,0 +1,28 @@
#![no_std]
#![no_main]
extern crate agb;
use agb::sound::mixer::SoundChannel;
use agb::{include_wav, Gba};
// Music - "Let it in" by Josh Woodward, free download at http://joshwoodward.com
const LET_IT_IN: &[u8] = include_wav!("examples/JoshWoodward-LetItIn.wav");
#[agb::entry]
fn main() -> ! {
let mut gba = Gba::new();
let vblank_provider = agb::interrupt::VBlank::get();
let mut mixer = gba.mixer.mixer();
mixer.enable();
let mut channel = SoundChannel::new(LET_IT_IN);
channel.stereo();
mixer.play_sound(channel).unwrap();
loop {
vblank_provider.wait_for_vblank();
mixer.vblank();
}
}

View file

@ -93,6 +93,60 @@ same_modification:
agb_arm_end agb_rs__mixer_add
agb_arm_func agb_rs__mixer_add_stereo
@ Arguments
@ r0 - pointer to the data to be copied (u8 array)
@ r1 - pointer to the sound buffer (i16 array which will alternate left and right channels, 32-bit aligned)
@
@ The sound buffer must be SOUND_BUFFER_SIZE * 2 in size = 176 * 2
push {r4-r8}
ldr r5, =0x00000FFF
.macro mixer_add_loop_simple_stereo
ldrsh r6, [r0], #2 @ load the current sound sample to r6
ldr r4, [r1] @ read the current value
@ This is slightly convoluted, but is mainly done for performance reasons. It is better
@ to hit ROM just once and then do 3 really simple instructions then do 2 ldrsbs however annoying
@ this is. Also, since all this code is in IWRAM and we never hit ROM otherwise, all accesses
@ are sequential and exactly the size of the bus to ROM (16 bits), so hopefully this will be super fast.
@
@ The next 3 instructions set up the current value in r6 to be in the expected format
@ 1 = 2s complement marks (so if negative, these are all 1s, if positive these are 0s)
@ L = the left sample
@ R = the right sample
@ 0 = all zeros
@ Split into bytes
@
@ At this point
@ r6 = | 1 | 1 | L | R | where the upper bytes are 1s if L is negative. No care about R
@ asr #8 | 1 | 1 | 1 | L | drop R off the right hand side
and r7, r5, r6, asr #8 @ r7 = | 0 | 0 | 1 | L | exactly what we want this to be. The mask puts the 1 as 00001111 ready for the shift later
lsl r6, r6, #24 @ r6 = | R | 0 | 0 | 0 | drop everything except the right sample
orr r6, r7, r6, asr #8 @ r6 = | 1 | R | 1 | L | now we have it perfectly set up
add r4, r4, r6, lsl #4 @ r4 += r6 << 4 (calculating both the left and right samples together)
str r4, [r1], #4 @ store the new value, and increment the pointer
.endm
mov r8, #SOUND_BUFFER_SIZE
1:
mixer_add_loop_simple_stereo
mixer_add_loop_simple_stereo
mixer_add_loop_simple_stereo
mixer_add_loop_simple_stereo
subs r8, r8, #4 @ loop counter
bne 1b @ jump back if we're done with the loop
pop {r4-r8}
bx lr
agb_arm_end agb_rs__mixer_add_stereo
.macro clamp_s8 reg:req
cmn \reg, #127
mvnlt \reg, #127

View file

@ -35,6 +35,8 @@ pub struct SoundChannel {
panning: Num<i16, 4>, // between -1 and 1
is_done: bool,
is_stereo: bool,
priority: SoundPriority,
}
@ -49,6 +51,7 @@ impl SoundChannel {
is_done: false,
priority: SoundPriority::Low,
volume: 1.into(),
is_stereo: false,
}
}
@ -62,6 +65,7 @@ impl SoundChannel {
is_done: false,
priority: SoundPriority::High,
volume: 1.into(),
is_stereo: false,
}
}
@ -91,6 +95,12 @@ impl SoundChannel {
self
}
pub fn stereo(&mut self) -> &mut Self {
self.is_stereo = true;
self
}
pub fn stop(&mut self) {
self.is_done = true
}

View file

@ -13,6 +13,8 @@ extern "C" {
right_amount: Num<i16, 4>,
);
fn agb_rs__mixer_add_stereo(sound_data: *const u8, sound_buffer: *mut Num<i16, 4>);
fn agb_rs__mixer_collapse(sound_buffer: *mut i8, input_buffer: *const Num<i16, 4>);
}
@ -133,12 +135,16 @@ impl MixerBuffer {
continue;
}
let playback_speed = if channel.is_stereo {
2.into()
} else {
channel.playback_speed
};
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).floor()
>= channel.data.len()
{
if (channel.pos + 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 {
channel.pos -= channel.data.len();
@ -148,16 +154,26 @@ impl MixerBuffer {
}
}
unsafe {
agb_rs__mixer_add(
channel.data.as_ptr().add(channel.pos.floor()),
buffer.as_mut_ptr(),
channel.playback_speed,
left_amount,
right_amount,
);
if channel.is_stereo {
unsafe {
agb_rs__mixer_add_stereo(
channel.data.as_ptr().add(channel.pos.floor()),
buffer.as_mut_ptr(),
);
}
} else {
unsafe {
agb_rs__mixer_add(
channel.data.as_ptr().add(channel.pos.floor()),
buffer.as_mut_ptr(),
playback_speed,
left_amount,
right_amount,
);
}
}
channel.pos += channel.playback_speed * SOUND_BUFFER_SIZE;
channel.pos += playback_speed * SOUND_BUFFER_SIZE;
}
let write_buffer = self.get_write_buffer();