Start making the frequency field dynamic rather than a feature flag

This commit is contained in:
Gwilym Kuiper 2022-10-02 21:14:01 +01:00
parent e01a3c41b6
commit 2ffc68c5c1
8 changed files with 82 additions and 76 deletions

View file

@ -18,10 +18,6 @@ debug = true
[lib] [lib]
proc-macro = true proc-macro = true
[features]
freq18157 = []
freq32768 = []
[dependencies] [dependencies]
hound = "3.5" hound = "3.5"
syn = "1" syn = "1"

View file

@ -6,15 +6,6 @@ use quote::{quote, ToTokens};
use std::path::Path; use std::path::Path;
use syn::parse_macro_input; use syn::parse_macro_input;
#[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");
use quote::TokenStreamExt; use quote::TokenStreamExt;
struct ByteString<'a>(&'a [u8]); struct ByteString<'a>(&'a [u8]);
impl ToTokens for ByteString<'_> { impl ToTokens for ByteString<'_> {
@ -37,13 +28,6 @@ pub fn include_wav(input: TokenStream) -> TokenStream {
let wav_reader = hound::WavReader::open(&path) let wav_reader = hound::WavReader::open(&path)
.unwrap_or_else(|_| panic!("Failed to load file {}", include_path)); .unwrap_or_else(|_| panic!("Failed to load file {}", include_path));
assert_eq!(
wav_reader.spec().sample_rate,
FREQUENCY,
"agb currently only supports sample rate of {}Hz",
FREQUENCY
);
let samples: Vec<u8> = samples_from_reader(wav_reader).collect(); let samples: Vec<u8> = samples_from_reader(wav_reader).collect();
let samples = ByteString(&samples); let samples = ByteString(&samples);

View file

@ -17,8 +17,6 @@ debug = true
[features] [features]
default = ["testing"] default = ["testing"]
freq18157 = ["agb_sound_converter/freq18157"]
freq32768 = ["agb_sound_converter/freq32768"]
testing = [] testing = []
[dependencies] [dependencies]
@ -34,7 +32,3 @@ rustc-hash = { version = "1", default-features = false }
[package.metadata.docs.rs] [package.metadata.docs.rs]
default-target = "thumbv6m-none-eabi" default-target = "thumbv6m-none-eabi"
targets = [] targets = []
[[example]]
name = "mixer_32768"
required-features = ["freq32768"]

View file

@ -7,7 +7,7 @@ use agb::{
Font, Priority, Font, Priority,
}, },
include_font, include_wav, include_font, include_wav,
sound::mixer::SoundChannel, sound::mixer::{Frequency, SoundChannel},
Gba, Gba,
}; };
@ -40,7 +40,7 @@ fn main(mut gba: Gba) -> ! {
let mut timer = timer_controller.timer2; let mut timer = timer_controller.timer2;
timer.set_enabled(true); timer.set_enabled(true);
let mut mixer = gba.mixer.mixer(); let mut mixer = gba.mixer.mixer(Frequency::Hz32768);
mixer.enable(); mixer.enable();
let _interrupt = mixer.setup_interrupt_handler(); let _interrupt = mixer.setup_interrupt_handler();

View file

@ -3,7 +3,7 @@
use agb::fixnum::Num; use agb::fixnum::Num;
use agb::input::{Button, ButtonController, Tri}; use agb::input::{Button, ButtonController, Tri};
use agb::sound::mixer::SoundChannel; use agb::sound::mixer::{Frequency, SoundChannel};
use agb::{fixnum::num, include_wav, Gba}; use agb::{fixnum::num, include_wav, Gba};
// Music - "Dead Code" by Josh Woodward, free download at http://joshwoodward.com // Music - "Dead Code" by Josh Woodward, free download at http://joshwoodward.com
@ -14,7 +14,7 @@ fn main(mut gba: Gba) -> ! {
let mut input = ButtonController::new(); let mut input = ButtonController::new();
let vblank_provider = agb::interrupt::VBlank::get(); let vblank_provider = agb::interrupt::VBlank::get();
let mut mixer = gba.mixer.mixer(); let mut mixer = gba.mixer.mixer(Frequency::Hz10512);
mixer.enable(); mixer.enable();
let channel = SoundChannel::new(DEAD_CODE); let channel = SoundChannel::new(DEAD_CODE);

View file

@ -7,7 +7,7 @@ use agb::{
Font, Priority, Font, Priority,
}, },
include_font, include_wav, include_font, include_wav,
sound::mixer::SoundChannel, sound::mixer::{Frequency, SoundChannel},
Gba, Gba,
}; };
@ -40,7 +40,7 @@ fn main(mut gba: Gba) -> ! {
let mut timer = timer_controller.timer2; let mut timer = timer_controller.timer2;
timer.set_enabled(true); timer.set_enabled(true);
let mut mixer = gba.mixer.mixer(); let mut mixer = gba.mixer.mixer(Frequency::Hz10512);
mixer.enable(); mixer.enable();
let mut channel = SoundChannel::new(LET_IT_IN); let mut channel = SoundChannel::new(LET_IT_IN);

View file

@ -138,8 +138,8 @@ impl MixerController {
} }
/// Get a [`Mixer`] in order to start producing sounds. /// Get a [`Mixer`] in order to start producing sounds.
pub fn mixer(&mut self) -> Mixer { pub fn mixer(&mut self, frequency: Frequency) -> Mixer {
Mixer::new() Mixer::new(frequency)
} }
} }
@ -149,6 +149,44 @@ enum SoundPriority {
Low, Low,
} }
/// The supported frequencies within AGB. These are chosen to work well with
/// the hardware. Note that the higher the frequency, the better the quality of
/// the sound but the more CPU time sound mixing will take.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum Frequency {
/// 10512Hz
Hz10512,
/// 18157Hz
Hz18157,
/// 32768Hz - note that this option requires the timer to do the buffer swapping
Hz32768,
}
// These work perfectly with swapping the buffers every vblank
// list here: http://deku.gbadev.org/program/sound1.html
impl Frequency {
pub(crate) fn frequency(self) -> i32 {
use Frequency::*;
match self {
Hz10512 => 10512,
Hz18157 => 18157,
Hz32768 => 32768,
}
}
pub(crate) fn buffer_size(self) -> usize {
use Frequency::*;
match self {
Hz10512 => 176,
Hz18157 => 18157,
Hz32768 => 32768,
}
}
}
/// Describes one sound which should be playing. This could be a sound effect or /// Describes one sound which should be playing. This could be a sound effect or
/// the background music. Use the factory methods on this to modify how it is played. /// the background music. Use the factory methods on this to modify how it is played.
/// ///

View file

@ -1,12 +1,14 @@
use core::cell::RefCell; use core::cell::RefCell;
use core::intrinsics::transmute;
use alloc::boxed::Box;
use alloc::vec::Vec;
use bare_metal::{CriticalSection, Mutex}; use bare_metal::{CriticalSection, Mutex};
use super::hw;
use super::hw::LeftOrRight; use super::hw::LeftOrRight;
use super::{hw, Frequency};
use super::{SoundChannel, SoundPriority}; use super::{SoundChannel, SoundPriority};
use crate::InternalAllocator;
use crate::{ use crate::{
fixnum::Num, fixnum::Num,
interrupt::free, interrupt::free,
@ -80,6 +82,7 @@ pub struct Mixer {
buffer: MixerBuffer, buffer: MixerBuffer,
channels: [Option<SoundChannel>; 8], channels: [Option<SoundChannel>; 8],
indices: [i32; 8], indices: [i32; 8],
frequency: Frequency,
timer: Timer, timer: Timer,
} }
@ -108,9 +111,10 @@ pub struct Mixer {
pub struct ChannelId(usize, i32); pub struct ChannelId(usize, i32);
impl Mixer { impl Mixer {
pub(super) fn new() -> Self { pub(super) fn new(frequency: Frequency) -> Self {
Self { Self {
buffer: MixerBuffer::new(), frequency,
buffer: MixerBuffer::new(frequency),
channels: Default::default(), channels: Default::default(),
indices: Default::default(), indices: Default::default(),
@ -123,7 +127,7 @@ impl Mixer {
/// You must call this method in order to start playing sound. You can do as much set up before /// You must call this method in order to start playing sound. You can do as much set up before
/// this as you like, but you will not get any sound out of the console until this method is called. /// this as you like, but you will not get any sound out of the console until this method is called.
pub fn enable(&mut self) { pub fn enable(&mut self) {
hw::set_timer_counter_for_frequency_and_enable(&mut self.timer, constants::SOUND_FREQUENCY); hw::set_timer_counter_for_frequency_and_enable(&mut self.timer, self.frequency.frequency());
hw::set_sound_control_register_for_mixer(); hw::set_sound_control_register_for_mixer();
} }
@ -184,7 +188,7 @@ impl Mixer {
.set_cascade(true) .set_cascade(true)
.set_divider(Divider::Divider1) .set_divider(Divider::Divider1)
.set_interrupt(true) .set_interrupt(true)
.set_overflow_amount(constants::SOUND_BUFFER_SIZE as u16) .set_overflow_amount(self.frequency.buffer_size() as u16)
.set_enabled(true); .set_enabled(true);
add_interrupt_handler(timer1.interrupt(), move |cs| self.buffer.swap(cs)) add_interrupt_handler(timer1.interrupt(), move |cs| self.buffer.swap(cs))
@ -313,47 +317,31 @@ impl Mixer {
} }
} }
// These work perfectly with swapping the buffers every vblank fn set_asm_buffer_size(frequency: Frequency) {
// 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")]
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" { extern "C" {
static mut agb_rs__buffer_size: usize; static mut agb_rs__buffer_size: usize;
} }
unsafe { unsafe {
agb_rs__buffer_size = constants::SOUND_BUFFER_SIZE; agb_rs__buffer_size = frequency.buffer_size();
} }
} }
#[repr(C, align(4))] struct SoundBuffer(Box<[i8], InternalAllocator>);
struct SoundBuffer([i8; constants::SOUND_BUFFER_SIZE * 2]);
impl Default for SoundBuffer { impl SoundBuffer {
fn default() -> Self { fn new(frequency: Frequency) -> Self {
Self([0; constants::SOUND_BUFFER_SIZE * 2]) let my_size = frequency.buffer_size() * 2;
let mut v = Vec::with_capacity_in(my_size, InternalAllocator);
v.resize(my_size, 0);
SoundBuffer(v.into_boxed_slice())
} }
} }
struct MixerBuffer { struct MixerBuffer {
buffers: [SoundBuffer; 3], buffers: [SoundBuffer; 3],
frequency: Frequency,
state: Mutex<RefCell<MixerBufferState>>, state: Mutex<RefCell<MixerBufferState>>,
} }
@ -391,16 +379,20 @@ impl MixerBufferState {
} }
impl MixerBuffer { impl MixerBuffer {
fn new() -> Self { fn new(frequency: Frequency) -> Self {
set_asm_buffer_size();
MixerBuffer { MixerBuffer {
buffers: Default::default(), buffers: [
SoundBuffer::new(frequency),
SoundBuffer::new(frequency),
SoundBuffer::new(frequency),
],
state: Mutex::new(RefCell::new(MixerBufferState { state: Mutex::new(RefCell::new(MixerBufferState {
active_buffer: 0, active_buffer: 0,
playing_buffer: 0, playing_buffer: 0,
})), })),
frequency,
} }
} }
@ -413,15 +405,17 @@ impl MixerBuffer {
let (left_buffer, right_buffer) = self.buffers[buffer] let (left_buffer, right_buffer) = self.buffers[buffer]
.0 .0
.split_at(constants::SOUND_BUFFER_SIZE); .split_at(self.frequency.buffer_size());
hw::enable_dma_for_sound(left_buffer, LeftOrRight::Left); hw::enable_dma_for_sound(left_buffer, LeftOrRight::Left);
hw::enable_dma_for_sound(right_buffer, LeftOrRight::Right); hw::enable_dma_for_sound(right_buffer, LeftOrRight::Right);
} }
fn write_channels<'a>(&mut self, channels: impl Iterator<Item = &'a mut SoundChannel>) { fn write_channels<'a>(&mut self, channels: impl Iterator<Item = &'a mut SoundChannel>) {
let mut buffer: [Num<i16, 4>; constants::SOUND_BUFFER_SIZE * 2] = set_asm_buffer_size(self.frequency);
unsafe { transmute([0i16; constants::SOUND_BUFFER_SIZE * 2]) };
let mut buffer = Vec::with_capacity_in(self.frequency.buffer_size() * 2, InternalAllocator);
buffer.resize(self.frequency.buffer_size() * 2, 0.into());
for channel in channels { for channel in channels {
if channel.is_done { if channel.is_done {
@ -434,7 +428,7 @@ impl MixerBuffer {
channel.playback_speed channel.playback_speed
}; };
if (channel.pos + playback_speed * constants::SOUND_BUFFER_SIZE).floor() if (channel.pos + playback_speed * self.frequency.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
@ -469,7 +463,7 @@ impl MixerBuffer {
} }
} }
channel.pos += playback_speed * constants::SOUND_BUFFER_SIZE; channel.pos += playback_speed * self.frequency.buffer_size();
} }
let write_buffer_index = free(|cs| self.state.borrow(cs).borrow_mut().active_advanced()); let write_buffer_index = free(|cs| self.state.borrow(cs).borrow_mut().active_advanced());