mirror of
https://github.com/italicsjenga/agb.git
synced 2024-12-25 01:01:34 +11:00
commit
b7734726a6
|
@ -66,9 +66,12 @@ pub fn include_wav(input: TokenStream) -> TokenStream {
|
||||||
|
|
||||||
let result = quote! {
|
let result = quote! {
|
||||||
{
|
{
|
||||||
|
#[repr(align(4))]
|
||||||
|
struct AlignmentWrapper<const N: usize>([u8; N]);
|
||||||
|
|
||||||
const _: &[u8] = include_bytes!(#include_path);
|
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_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
|
.macro clamp_s8 reg:req
|
||||||
cmn \reg, #127
|
cmn \reg, #127
|
||||||
mvnlt \reg, #127
|
mvnlt \reg, #127
|
||||||
|
|
|
@ -35,6 +35,8 @@ pub struct SoundChannel {
|
||||||
panning: Num<i16, 4>, // between -1 and 1
|
panning: Num<i16, 4>, // between -1 and 1
|
||||||
is_done: bool,
|
is_done: bool,
|
||||||
|
|
||||||
|
is_stereo: bool,
|
||||||
|
|
||||||
priority: SoundPriority,
|
priority: SoundPriority,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,6 +51,7 @@ impl SoundChannel {
|
||||||
is_done: false,
|
is_done: false,
|
||||||
priority: SoundPriority::Low,
|
priority: SoundPriority::Low,
|
||||||
volume: 1.into(),
|
volume: 1.into(),
|
||||||
|
is_stereo: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,6 +65,7 @@ impl SoundChannel {
|
||||||
is_done: false,
|
is_done: false,
|
||||||
priority: SoundPriority::High,
|
priority: SoundPriority::High,
|
||||||
volume: 1.into(),
|
volume: 1.into(),
|
||||||
|
is_stereo: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,6 +95,12 @@ impl SoundChannel {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn stereo(&mut self) -> &mut Self {
|
||||||
|
self.is_stereo = true;
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub fn stop(&mut self) {
|
pub fn stop(&mut self) {
|
||||||
self.is_done = true
|
self.is_done = true
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,8 @@ extern "C" {
|
||||||
right_amount: Num<i16, 4>,
|
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>);
|
fn agb_rs__mixer_collapse(sound_buffer: *mut i8, input_buffer: *const Num<i16, 4>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,12 +135,16 @@ impl MixerBuffer {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let playback_speed = if channel.is_stereo {
|
||||||
|
2.into()
|
||||||
|
} else {
|
||||||
|
channel.playback_speed
|
||||||
|
};
|
||||||
|
|
||||||
let right_amount = ((channel.panning + 1) / 2) * channel.volume;
|
let right_amount = ((channel.panning + 1) / 2) * channel.volume;
|
||||||
let left_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()
|
if (channel.pos + playback_speed * SOUND_BUFFER_SIZE).floor() >= channel.data.len() {
|
||||||
>= channel.data.len()
|
|
||||||
{
|
|
||||||
// TODO: This should probably play what's left rather than skip the last bit
|
// TODO: This should probably play what's left rather than skip the last bit
|
||||||
if channel.should_loop {
|
if channel.should_loop {
|
||||||
channel.pos -= channel.data.len();
|
channel.pos -= channel.data.len();
|
||||||
|
@ -148,16 +154,26 @@ impl MixerBuffer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if channel.is_stereo {
|
||||||
|
unsafe {
|
||||||
|
agb_rs__mixer_add_stereo(
|
||||||
|
channel.data.as_ptr().add(channel.pos.floor()),
|
||||||
|
buffer.as_mut_ptr(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
unsafe {
|
unsafe {
|
||||||
agb_rs__mixer_add(
|
agb_rs__mixer_add(
|
||||||
channel.data.as_ptr().add(channel.pos.floor()),
|
channel.data.as_ptr().add(channel.pos.floor()),
|
||||||
buffer.as_mut_ptr(),
|
buffer.as_mut_ptr(),
|
||||||
channel.playback_speed,
|
playback_speed,
|
||||||
left_amount,
|
left_amount,
|
||||||
right_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();
|
let write_buffer = self.get_write_buffer();
|
||||||
|
|
Loading…
Reference in a new issue