mirror of
https://github.com/italicsjenga/agb.git
synced 2024-12-24 00:31:34 +11:00
commit
b7734726a6
|
@ -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
|
||||
}
|
||||
};
|
||||
|
||||
|
|
BIN
agb/examples/JoshWoodward-LetItIn.wav
Normal file
BIN
agb/examples/JoshWoodward-LetItIn.wav
Normal file
Binary file not shown.
28
agb/examples/stereo_sound.rs
Normal file
28
agb/examples/stereo_sound.rs
Normal 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();
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in a new issue