mirror of
https://github.com/italicsjenga/agb.git
synced 2025-01-11 17:41:33 +11:00
Merge pull request #232 from gwilymk/add-32khz-frequency-option
Add 32khz frequency option
This commit is contained in:
commit
46260c085a
|
@ -19,6 +19,7 @@ proc-macro = true
|
|||
|
||||
[features]
|
||||
freq18157 = []
|
||||
freq32768 = []
|
||||
|
||||
[dependencies]
|
||||
hound = "3.4"
|
||||
|
|
|
@ -12,10 +12,14 @@ use std::{
|
|||
};
|
||||
use syn::parse_macro_input;
|
||||
|
||||
#[cfg(not(feature = "freq18157"))]
|
||||
#[cfg(all(not(feature = "freq18157"), not(feature = "freq32768")))]
|
||||
const FREQUENCY: u32 = 10512;
|
||||
#[cfg(feature = "freq18157")]
|
||||
const FREQUENCY: u32 = 18157;
|
||||
#[cfg(feature = "freq32768")]
|
||||
const FREQUENCY: u32 = 32768;
|
||||
#[cfg(all(feature = "freq18157", feature = "freq32768"))]
|
||||
compile_error!("Must have at most one of freq18157 or freq32768 features enabled");
|
||||
|
||||
#[proc_macro]
|
||||
pub fn include_wav(input: TokenStream) -> TokenStream {
|
||||
|
|
|
@ -17,6 +17,7 @@ debug = true
|
|||
[features]
|
||||
default = []
|
||||
freq18157 = ["agb_sound_converter/freq18157"]
|
||||
freq32768 = ["agb_sound_converter/freq32768"]
|
||||
|
||||
[dependencies]
|
||||
bitflags = "1"
|
||||
|
@ -31,3 +32,7 @@ rustc-hash = { version = "1", default-features = false }
|
|||
[package.metadata.docs.rs]
|
||||
default-target = "thumbv6m-none-eabi"
|
||||
targets = []
|
||||
|
||||
[[example]]
|
||||
name = "mixer_32768"
|
||||
required-features = ["freq32768"]
|
BIN
agb/examples/JoshWoodward-CrazyGlue.wav
Normal file
BIN
agb/examples/JoshWoodward-CrazyGlue.wav
Normal file
Binary file not shown.
47
agb/examples/mixer_32768.rs
Normal file
47
agb/examples/mixer_32768.rs
Normal file
|
@ -0,0 +1,47 @@
|
|||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use agb::sound::mixer::SoundChannel;
|
||||
use agb::{include_wav, Gba};
|
||||
|
||||
// Music - "Crazy glue" by Josh Woodward, free download at http://joshwoodward.com
|
||||
const LET_IT_IN: &[u8] = include_wav!("examples/JoshWoodward-CrazyGlue.wav");
|
||||
|
||||
#[agb::entry]
|
||||
fn main(mut gba: Gba) -> ! {
|
||||
let vblank_provider = agb::interrupt::VBlank::get();
|
||||
|
||||
let timer_controller = gba.timers.timers();
|
||||
let mut timer = timer_controller.timer2;
|
||||
timer.set_enabled(true);
|
||||
|
||||
let mut mixer = gba.mixer.mixer();
|
||||
mixer.enable();
|
||||
|
||||
let mut channel = SoundChannel::new(LET_IT_IN);
|
||||
channel.stereo();
|
||||
mixer.play_sound(channel).unwrap();
|
||||
|
||||
let _interrupt = mixer.setup_interrupt_handler();
|
||||
|
||||
let mut frame_counter = 0i32;
|
||||
loop {
|
||||
vblank_provider.wait_for_vblank();
|
||||
let before_mixing_cycles = timer.value();
|
||||
mixer.frame();
|
||||
let after_mixing_cycles = timer.value();
|
||||
|
||||
frame_counter = frame_counter.wrapping_add(1);
|
||||
|
||||
if frame_counter % 128 == 0 {
|
||||
let total_cycles = after_mixing_cycles.wrapping_sub(before_mixing_cycles) as u32;
|
||||
|
||||
let percent = (total_cycles * 100) / 280896;
|
||||
agb::println!(
|
||||
"Took {} cycles to calculate mixer ~= {}% of total frame",
|
||||
total_cycles,
|
||||
percent
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,8 +14,7 @@ fn main(mut gba: Gba) -> ! {
|
|||
let mut input = ButtonController::new();
|
||||
let vblank_provider = agb::interrupt::VBlank::get();
|
||||
|
||||
let mut timers = gba.timers.timers();
|
||||
let mut mixer = gba.mixer.mixer(&mut timers.timer0);
|
||||
let mut mixer = gba.mixer.mixer();
|
||||
mixer.enable();
|
||||
|
||||
let channel = SoundChannel::new(DEAD_CODE);
|
||||
|
|
|
@ -11,11 +11,11 @@ const LET_IT_IN: &[u8] = include_wav!("examples/JoshWoodward-LetItIn.wav");
|
|||
fn main(mut gba: Gba) -> ! {
|
||||
let vblank_provider = agb::interrupt::VBlank::get();
|
||||
|
||||
let mut timer_controller = gba.timers.timers();
|
||||
let mut timer = timer_controller.timer1;
|
||||
let timer_controller = gba.timers.timers();
|
||||
let mut timer = timer_controller.timer2;
|
||||
timer.set_enabled(true);
|
||||
|
||||
let mut mixer = gba.mixer.mixer(&mut timer_controller.timer0);
|
||||
let mut mixer = gba.mixer.mixer();
|
||||
mixer.enable();
|
||||
|
||||
let mut channel = SoundChannel::new(LET_IT_IN);
|
||||
|
|
|
@ -28,6 +28,8 @@ const FIFOB_DEST_ADDR: u32 = 0x0400_00a4;
|
|||
const SOUND_CONTROL: MemoryMapped<u16> = unsafe { MemoryMapped::new(0x0400_0082) };
|
||||
const SOUND_CONTROL_X: MemoryMapped<u16> = unsafe { MemoryMapped::new(0x0400_0084) };
|
||||
|
||||
const SOUND_BIAS: MemoryMapped<u16> = unsafe { MemoryMapped::new(0x0400_0088) };
|
||||
|
||||
const DMA_CONTROL_SETTING_FOR_SOUND: u16 = {
|
||||
let dest_fixed: u16 = 2 << 5; // dest addr control = fixed
|
||||
let repeat: u16 = 1 << 9;
|
||||
|
@ -89,6 +91,9 @@ pub(super) fn set_sound_control_register_for_mixer() {
|
|||
|
||||
// master sound enable
|
||||
SOUND_CONTROL_X.set(1 << 7);
|
||||
|
||||
// Set the sound bias PWM resampling rate to 8bit at 65536Hz (default for most games)
|
||||
SOUND_BIAS.set(SOUND_BIAS.get() | 1 << 14);
|
||||
}
|
||||
|
||||
pub(super) fn set_timer_counter_for_frequency_and_enable(timer: &mut Timer, frequency: i32) {
|
||||
|
|
|
@ -192,6 +192,7 @@ SWAP_SIGN .req r11
|
|||
.endm
|
||||
|
||||
1:
|
||||
.rept 4
|
||||
load_sample r3, r12
|
||||
|
||||
load_sample r5, r6
|
||||
|
@ -211,8 +212,9 @@ SWAP_SIGN .req r11
|
|||
|
||||
str r3, [r0, r4] @ *(r0 + (r4 = SOUND_BUFFER_SIZE)) = r3
|
||||
str r12, [r0], #4 @ *r0 = r12; r0 += 4
|
||||
.endr
|
||||
|
||||
subs r2, r2, #4 @ r2 -= 4
|
||||
subs r2, r2, #16 @ r2 -= 16
|
||||
bne 1b @ loop if not 0
|
||||
|
||||
pop {r4-r11}
|
||||
|
|
|
@ -5,7 +5,6 @@ pub use sw_mixer::ChannelId;
|
|||
pub use sw_mixer::Mixer;
|
||||
|
||||
use crate::fixnum::Num;
|
||||
use crate::timer::Timer;
|
||||
|
||||
#[non_exhaustive]
|
||||
pub struct MixerController {}
|
||||
|
@ -15,8 +14,8 @@ impl MixerController {
|
|||
MixerController {}
|
||||
}
|
||||
|
||||
pub fn mixer<'a>(&mut self, timer: &'a mut Timer) -> Mixer<'a> {
|
||||
Mixer::new(timer)
|
||||
pub fn mixer(&mut self) -> Mixer {
|
||||
Mixer::new()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,18 @@
|
|||
use core::cell::RefCell;
|
||||
|
||||
use bare_metal::{CriticalSection, Mutex};
|
||||
|
||||
use super::hw;
|
||||
use super::hw::LeftOrRight;
|
||||
use super::{SoundChannel, SoundPriority};
|
||||
use crate::fixnum::Num;
|
||||
use crate::timer::Timer;
|
||||
use crate::syscall::cpu_fast_fill_i8;
|
||||
use crate::{
|
||||
fixnum::Num,
|
||||
interrupt::free,
|
||||
interrupt::{add_interrupt_handler, InterruptHandler},
|
||||
timer::Divider,
|
||||
timer::Timer,
|
||||
};
|
||||
|
||||
// Defined in mixer.s
|
||||
extern "C" {
|
||||
|
@ -19,40 +29,58 @@ extern "C" {
|
|||
fn agb_rs__mixer_collapse(sound_buffer: *mut i8, input_buffer: *const Num<i16, 4>);
|
||||
}
|
||||
|
||||
pub struct Mixer<'a> {
|
||||
pub struct Mixer {
|
||||
buffer: MixerBuffer,
|
||||
channels: [Option<SoundChannel>; 8],
|
||||
indices: [i32; 8],
|
||||
|
||||
timer: &'a mut Timer,
|
||||
timer: Timer,
|
||||
}
|
||||
|
||||
pub struct ChannelId(usize, i32);
|
||||
|
||||
impl<'a> Mixer<'a> {
|
||||
pub(super) fn new(timer: &'a mut Timer) -> Self {
|
||||
Mixer {
|
||||
impl Mixer {
|
||||
pub(super) fn new() -> Self {
|
||||
Self {
|
||||
buffer: MixerBuffer::new(),
|
||||
channels: Default::default(),
|
||||
indices: Default::default(),
|
||||
|
||||
timer,
|
||||
timer: unsafe { Timer::new(0) },
|
||||
}
|
||||
}
|
||||
|
||||
pub fn enable(&mut self) {
|
||||
hw::set_timer_counter_for_frequency_and_enable(self.timer, SOUND_FREQUENCY);
|
||||
hw::set_timer_counter_for_frequency_and_enable(&mut self.timer, constants::SOUND_FREQUENCY);
|
||||
hw::set_sound_control_register_for_mixer();
|
||||
}
|
||||
|
||||
pub fn frame(&mut self) {
|
||||
self.buffer.clear();
|
||||
self.buffer
|
||||
.write_channels(self.channels.iter_mut().flatten());
|
||||
#[cfg(not(feature = "freq32768"))]
|
||||
pub fn after_vblank(&mut self) {
|
||||
free(|cs| self.buffer.swap(cs));
|
||||
}
|
||||
|
||||
pub fn after_vblank(&mut self) {
|
||||
self.buffer.swap();
|
||||
/// Note that if you set up an interrupt handler, you should not call `after_vblank` any more
|
||||
/// You are still required to call `frame`
|
||||
pub fn setup_interrupt_handler(&self) -> InterruptHandler<'_> {
|
||||
let mut timer1 = unsafe { Timer::new(1) };
|
||||
timer1
|
||||
.set_cascade(true)
|
||||
.set_divider(Divider::Divider1)
|
||||
.set_interrupt(true)
|
||||
.set_overflow_amount(constants::SOUND_BUFFER_SIZE as u16)
|
||||
.set_enabled(true);
|
||||
|
||||
add_interrupt_handler(timer1.interrupt(), move |cs| self.buffer.swap(cs))
|
||||
}
|
||||
|
||||
pub fn frame(&mut self) {
|
||||
if !self.buffer.should_calculate() {
|
||||
return;
|
||||
}
|
||||
|
||||
self.buffer
|
||||
.write_channels(self.channels.iter_mut().flatten());
|
||||
}
|
||||
|
||||
pub fn play_sound(&mut self, new_channel: SoundChannel) -> Option<ChannelId> {
|
||||
|
@ -96,17 +124,25 @@ impl<'a> Mixer<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
// I've picked one frequency that works nicely. But there are others that work nicely
|
||||
// which we may want to consider in the future: http://deku.gbadev.org/program/sound1.html
|
||||
#[cfg(not(feature = "freq18157"))]
|
||||
const SOUND_FREQUENCY: i32 = 10512;
|
||||
#[cfg(not(feature = "freq18157"))]
|
||||
const SOUND_BUFFER_SIZE: usize = 176;
|
||||
// These work perfectly with swapping the buffers every vblank
|
||||
// list here: http://deku.gbadev.org/program/sound1.html
|
||||
#[cfg(all(not(feature = "freq18157"), not(feature = "freq32768")))]
|
||||
mod constants {
|
||||
pub const SOUND_FREQUENCY: i32 = 10512;
|
||||
pub const SOUND_BUFFER_SIZE: usize = 176;
|
||||
}
|
||||
|
||||
#[cfg(feature = "freq18157")]
|
||||
const SOUND_FREQUENCY: i32 = 18157;
|
||||
#[cfg(feature = "freq18157")]
|
||||
const SOUND_BUFFER_SIZE: usize = 304;
|
||||
mod constants {
|
||||
pub const SOUND_FREQUENCY: i32 = 18157;
|
||||
pub const SOUND_BUFFER_SIZE: usize = 304;
|
||||
}
|
||||
|
||||
#[cfg(feature = "freq32768")]
|
||||
mod constants {
|
||||
pub const SOUND_FREQUENCY: i32 = 32768;
|
||||
pub const SOUND_BUFFER_SIZE: usize = 560;
|
||||
}
|
||||
|
||||
fn set_asm_buffer_size() {
|
||||
extern "C" {
|
||||
|
@ -114,18 +150,55 @@ fn set_asm_buffer_size() {
|
|||
}
|
||||
|
||||
unsafe {
|
||||
agb_rs__buffer_size = SOUND_BUFFER_SIZE;
|
||||
agb_rs__buffer_size = constants::SOUND_BUFFER_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C, align(4))]
|
||||
struct SoundBuffer([i8; SOUND_BUFFER_SIZE * 2]);
|
||||
struct SoundBuffer([i8; constants::SOUND_BUFFER_SIZE * 2]);
|
||||
|
||||
impl Default for SoundBuffer {
|
||||
fn default() -> Self {
|
||||
Self([0; constants::SOUND_BUFFER_SIZE * 2])
|
||||
}
|
||||
}
|
||||
|
||||
struct MixerBuffer {
|
||||
buffer1: SoundBuffer, // alternating bytes left and right channels
|
||||
buffer2: SoundBuffer,
|
||||
buffers: [SoundBuffer; 3],
|
||||
|
||||
buffer_1_active: bool,
|
||||
state: Mutex<RefCell<MixerBufferState>>,
|
||||
}
|
||||
|
||||
struct MixerBufferState {
|
||||
active_buffer: usize,
|
||||
playing_buffer: usize,
|
||||
}
|
||||
|
||||
/// Only returns a valid result if 0 <= x <= 3
|
||||
const fn mod3_estimate(x: usize) -> usize {
|
||||
match x & 0b11 {
|
||||
0 => 0,
|
||||
1 => 1,
|
||||
2 => 2,
|
||||
3 => 0,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
impl MixerBufferState {
|
||||
fn should_calculate(&self) -> bool {
|
||||
mod3_estimate(self.active_buffer + 1) != mod3_estimate(self.playing_buffer)
|
||||
}
|
||||
|
||||
fn playing_advanced(&mut self) -> usize {
|
||||
self.playing_buffer = mod3_estimate(self.playing_buffer + 1);
|
||||
self.playing_buffer
|
||||
}
|
||||
|
||||
fn active_advanced(&mut self) -> usize {
|
||||
self.active_buffer = mod3_estimate(self.active_buffer + 1);
|
||||
self.active_buffer
|
||||
}
|
||||
}
|
||||
|
||||
impl MixerBuffer {
|
||||
|
@ -133,28 +206,33 @@ impl MixerBuffer {
|
|||
set_asm_buffer_size();
|
||||
|
||||
MixerBuffer {
|
||||
buffer1: SoundBuffer([0; SOUND_BUFFER_SIZE * 2]),
|
||||
buffer2: SoundBuffer([0; SOUND_BUFFER_SIZE * 2]),
|
||||
buffers: Default::default(),
|
||||
|
||||
buffer_1_active: true,
|
||||
state: Mutex::new(RefCell::new(MixerBufferState {
|
||||
active_buffer: 0,
|
||||
playing_buffer: 0,
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
fn swap(&mut self) {
|
||||
let (left_buffer, right_buffer) = self.write_buffer().split_at(SOUND_BUFFER_SIZE);
|
||||
fn should_calculate(&self) -> bool {
|
||||
free(|cs| self.state.borrow(*cs).borrow().should_calculate())
|
||||
}
|
||||
|
||||
fn swap(&self, cs: &CriticalSection) {
|
||||
let buffer = self.state.borrow(*cs).borrow_mut().playing_advanced();
|
||||
|
||||
let (left_buffer, right_buffer) = self.buffers[buffer]
|
||||
.0
|
||||
.split_at(constants::SOUND_BUFFER_SIZE);
|
||||
|
||||
hw::enable_dma_for_sound(left_buffer, LeftOrRight::Left);
|
||||
hw::enable_dma_for_sound(right_buffer, LeftOrRight::Right);
|
||||
|
||||
self.buffer_1_active = !self.buffer_1_active;
|
||||
}
|
||||
|
||||
fn clear(&mut self) {
|
||||
self.write_buffer().fill(0);
|
||||
}
|
||||
|
||||
fn write_channels<'a>(&mut self, channels: impl Iterator<Item = &'a mut SoundChannel>) {
|
||||
let mut buffer: [Num<i16, 4>; SOUND_BUFFER_SIZE * 2] = [Num::new(0); SOUND_BUFFER_SIZE * 2];
|
||||
let mut buffer: [Num<i16, 4>; constants::SOUND_BUFFER_SIZE * 2] =
|
||||
[Num::new(0); constants::SOUND_BUFFER_SIZE * 2];
|
||||
|
||||
for channel in channels {
|
||||
if channel.is_done {
|
||||
|
@ -170,7 +248,9 @@ impl MixerBuffer {
|
|||
let right_amount = ((channel.panning + 1) / 2) * channel.volume;
|
||||
let left_amount = ((-channel.panning + 1) / 2) * channel.volume;
|
||||
|
||||
if (channel.pos + playback_speed * SOUND_BUFFER_SIZE).floor() >= channel.data.len() {
|
||||
if (channel.pos + playback_speed * constants::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 = 0.into();
|
||||
|
@ -199,20 +279,16 @@ impl MixerBuffer {
|
|||
}
|
||||
}
|
||||
|
||||
channel.pos += playback_speed * SOUND_BUFFER_SIZE;
|
||||
channel.pos += playback_speed * constants::SOUND_BUFFER_SIZE;
|
||||
}
|
||||
|
||||
let write_buffer = self.write_buffer();
|
||||
let write_buffer_index = free(|cs| self.state.borrow(*cs).borrow_mut().active_advanced());
|
||||
|
||||
let write_buffer = &mut self.buffers[write_buffer_index].0;
|
||||
cpu_fast_fill_i8(write_buffer, 0);
|
||||
|
||||
unsafe {
|
||||
agb_rs__mixer_collapse(write_buffer.as_mut_ptr(), buffer.as_ptr());
|
||||
}
|
||||
}
|
||||
|
||||
fn write_buffer(&mut self) -> &mut [i8; SOUND_BUFFER_SIZE * 2] {
|
||||
if self.buffer_1_active {
|
||||
&mut self.buffer2.0
|
||||
} else {
|
||||
&mut self.buffer1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ use core::arch::asm;
|
|||
|
||||
// use crate::display::object::AffineMatrixAttributes;
|
||||
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
|
||||
pub fn halt() {
|
||||
|
@ -94,6 +93,9 @@ pub fn arc_tan(n: i16) -> i16 {
|
|||
"swi 0x09",
|
||||
in("r0") n,
|
||||
lateout("r0") result,
|
||||
lateout("r1") _,
|
||||
lateout("r2") _,
|
||||
lateout("r3") _
|
||||
);
|
||||
}
|
||||
result
|
||||
|
@ -107,12 +109,36 @@ pub fn arc_tan2(x: i16, y: i32) -> i16 {
|
|||
in("r0") x,
|
||||
in("r1") y,
|
||||
lateout("r0") result,
|
||||
|
||||
lateout("r2") _,
|
||||
lateout("r3") _
|
||||
);
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
pub(crate) fn cpu_fast_fill_i8(input: &mut [i8], new_content: i32) {
|
||||
assert_eq!(
|
||||
input.len() % (4 * 8),
|
||||
0,
|
||||
"Input length must be divisible by 32"
|
||||
);
|
||||
|
||||
let input_ptr = [new_content].as_ptr();
|
||||
let output_ptr = input.as_mut_ptr();
|
||||
let length_mode = (1 << 24) | // copy
|
||||
(input.len() / 4);
|
||||
|
||||
unsafe {
|
||||
asm!(
|
||||
"swi 0x0c",
|
||||
in("r0") input_ptr,
|
||||
in("r1") output_ptr,
|
||||
in("r2") length_mode,
|
||||
lateout("r3") _,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// pub fn affine_matrix(
|
||||
// x_scale: Num<i16, 8>,
|
||||
// y_scale: Num<i16, 8>,
|
||||
|
|
|
@ -40,8 +40,6 @@ pub struct Timer {
|
|||
|
||||
#[non_exhaustive]
|
||||
pub struct Timers {
|
||||
pub timer0: Timer,
|
||||
pub timer1: Timer,
|
||||
pub timer2: Timer,
|
||||
pub timer3: Timer,
|
||||
}
|
||||
|
@ -49,8 +47,6 @@ pub struct Timers {
|
|||
impl Timers {
|
||||
pub(crate) unsafe fn new() -> Self {
|
||||
Self {
|
||||
timer0: Timer::new(0),
|
||||
timer1: Timer::new(1),
|
||||
timer2: Timer::new(2),
|
||||
timer3: Timer::new(3),
|
||||
}
|
||||
|
@ -58,7 +54,7 @@ impl Timers {
|
|||
}
|
||||
|
||||
impl Timer {
|
||||
unsafe fn new(timer_number: u16) -> Self {
|
||||
pub(crate) unsafe fn new(timer_number: u16) -> Self {
|
||||
let new_timer = Self { timer_number };
|
||||
new_timer.data_register().set(0);
|
||||
new_timer.control_register().set(0);
|
||||
|
@ -66,32 +62,37 @@ impl Timer {
|
|||
new_timer
|
||||
}
|
||||
|
||||
pub fn set_overflow_amount(&mut self, n: u16) {
|
||||
pub fn set_overflow_amount(&mut self, n: u16) -> &mut Self {
|
||||
let count_up_value = 0u16.wrapping_sub(n);
|
||||
self.data_register().set(count_up_value);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn value(&self) -> u16 {
|
||||
self.data_register().get()
|
||||
}
|
||||
|
||||
pub fn set_divider(&mut self, divider: Divider) {
|
||||
pub fn set_divider(&mut self, divider: Divider) -> &mut Self {
|
||||
self.control_register().set_bits(divider.as_bits(), 2, 0);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_enabled(&mut self, enabled: bool) {
|
||||
pub fn set_enabled(&mut self, enabled: bool) -> &mut Self {
|
||||
let bit = if enabled { 1 } else { 0 };
|
||||
self.control_register().set_bits(bit, 1, 7);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_cascade(&mut self, cascade: bool) {
|
||||
pub fn set_cascade(&mut self, cascade: bool) -> &mut Self {
|
||||
let bit = if cascade { 1 } else { 0 };
|
||||
self.control_register().set_bits(bit, 1, 2);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_interrupt(&mut self, interrupt: bool) {
|
||||
pub fn set_interrupt(&mut self, interrupt: bool) -> &mut Self {
|
||||
let bit = interrupt as u16;
|
||||
self.control_register().set_bits(bit, 1, 6);
|
||||
self
|
||||
}
|
||||
|
||||
fn data_register(&self) -> MemoryMapped<u16> {
|
||||
|
|
|
@ -809,8 +809,7 @@ fn main(mut agb: agb::Gba) -> ! {
|
|||
vram.set_background_palettes(tile_sheet::background.palettes);
|
||||
|
||||
let object = agb.display.object.get();
|
||||
let mut timer_controller = agb.timers.timers();
|
||||
let mut mixer = agb.mixer.mixer(&mut timer_controller.timer0);
|
||||
let mut mixer = agb.mixer.mixer();
|
||||
|
||||
mixer.enable();
|
||||
let mut music_box = sfx::MusicBox::new();
|
||||
|
|
|
@ -60,13 +60,13 @@ impl MusicBox {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct SfxPlayer<'a, 'b> {
|
||||
mixer: &'a mut Mixer<'b>,
|
||||
pub struct SfxPlayer<'a> {
|
||||
mixer: &'a mut Mixer,
|
||||
frame: i32,
|
||||
}
|
||||
|
||||
impl<'a, 'b> SfxPlayer<'a, 'b> {
|
||||
pub fn new(mixer: &'a mut Mixer<'b>, music_box: &MusicBox) -> Self {
|
||||
impl<'a> SfxPlayer<'a> {
|
||||
pub fn new(mixer: &'a mut Mixer, music_box: &MusicBox) -> Self {
|
||||
SfxPlayer {
|
||||
mixer,
|
||||
frame: music_box.frame,
|
||||
|
|
|
@ -2205,8 +2205,7 @@ fn game_with_level(gba: &mut agb::Gba) {
|
|||
let vblank = agb::interrupt::VBlank::get();
|
||||
vblank.wait_for_vblank();
|
||||
|
||||
let mut timers = gba.timers.timers();
|
||||
let mut mixer = gba.mixer.mixer(&mut timers.timer0);
|
||||
let mut mixer = gba.mixer.mixer();
|
||||
mixer.enable();
|
||||
|
||||
let mut sfx = sfx::Sfx::new(&mut mixer);
|
||||
|
|
|
@ -27,11 +27,11 @@ const BLUE_SPIRIT: &[u8] = agb::include_wav!("sfx/03 - Blue Spirit (Main Loop).w
|
|||
|
||||
pub struct Sfx<'a> {
|
||||
bgm: Option<ChannelId>,
|
||||
mixer: &'a mut Mixer<'a>,
|
||||
mixer: &'a mut Mixer,
|
||||
}
|
||||
|
||||
impl<'a> Sfx<'a> {
|
||||
pub fn new(mixer: &'a mut Mixer<'a>) -> Self {
|
||||
pub fn new(mixer: &'a mut Mixer) -> Self {
|
||||
Self { mixer, bgm: None }
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue