mirror of
https://github.com/italicsjenga/agb.git
synced 2025-01-11 09:31:34 +11:00
Move old sound code to the dmg module
This commit is contained in:
parent
b0c4a8fd80
commit
c2ba8d835a
|
@ -11,20 +11,20 @@ pub fn main() -> ! {
|
|||
|
||||
gba.sound.enable();
|
||||
|
||||
let sweep_settings = sound::SweepSettings::default();
|
||||
let sweep_settings = sound::dmg::SweepSettings::default();
|
||||
gba.sound.channel1().play_sound(
|
||||
1024,
|
||||
Some(0),
|
||||
&sweep_settings,
|
||||
&sound::EnvelopeSettings::default(),
|
||||
sound::DutyCycle::Half,
|
||||
&sound::dmg::EnvelopeSettings::default(),
|
||||
sound::dmg::DutyCycle::Half,
|
||||
);
|
||||
|
||||
gba.sound.channel2().play_sound(
|
||||
1524,
|
||||
Some(0),
|
||||
&sound::EnvelopeSettings::default(),
|
||||
sound::DutyCycle::Half,
|
||||
&sound::dmg::EnvelopeSettings::default(),
|
||||
sound::dmg::DutyCycle::Half,
|
||||
);
|
||||
|
||||
gba.sound
|
||||
|
|
|
@ -50,7 +50,7 @@ static mut GBASINGLE: single::Singleton<Gba> = single::Singleton::new(unsafe { G
|
|||
|
||||
pub struct Gba {
|
||||
pub display: display::Display,
|
||||
pub sound: sound::Sound,
|
||||
pub sound: sound::dmg::Sound,
|
||||
}
|
||||
|
||||
impl Gba {
|
||||
|
@ -61,7 +61,7 @@ impl Gba {
|
|||
const unsafe fn single_new() -> Self {
|
||||
Self {
|
||||
display: display::Display::new(),
|
||||
sound: sound::Sound::new(),
|
||||
sound: sound::dmg::Sound::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
238
agb/src/sound/dmg.rs
Normal file
238
agb/src/sound/dmg.rs
Normal file
|
@ -0,0 +1,238 @@
|
|||
use crate::memory_mapped::MemoryMapped;
|
||||
|
||||
const CHANNEL_1_SWEEP: MemoryMapped<u16> = unsafe { MemoryMapped::new(0x0400_0060) };
|
||||
const CHANNEL_1_LENGTH_DUTY_ENVELOPE: MemoryMapped<u16> = unsafe { MemoryMapped::new(0x0400_0062) };
|
||||
const CHANNEL_1_FREQUENCY_CONTROL: MemoryMapped<u16> = unsafe { MemoryMapped::new(0x0400_0064) };
|
||||
|
||||
const CHANNEL_2_LENGTH_DUTY_ENVELOPE: MemoryMapped<u16> = unsafe { MemoryMapped::new(0x0400_0068) };
|
||||
const CHANNEL_2_FREQUENCY_CONTROL: MemoryMapped<u16> = unsafe { MemoryMapped::new(0x0400_006c) };
|
||||
|
||||
const CHANNEL_4_LENGTH_ENVELOPE: MemoryMapped<u16> = unsafe { MemoryMapped::new(0x0400_0078) };
|
||||
const CHANNEL_4_FREQUENCY_CONTROL: MemoryMapped<u16> = unsafe { MemoryMapped::new(0x0400_007c) };
|
||||
|
||||
const MASTER_SOUND_VOLUME_ENABLE: MemoryMapped<u16> = unsafe { MemoryMapped::new(0x0400_0080) };
|
||||
const MASTER_SOUND_VOLUME_MIXING: MemoryMapped<u16> = unsafe { MemoryMapped::new(0x0400_0082) };
|
||||
const MASTER_SOUND_STATUS: MemoryMapped<u16> = unsafe { MemoryMapped::new(0x0400_0084) };
|
||||
|
||||
#[non_exhaustive]
|
||||
pub struct Sound {}
|
||||
|
||||
impl Sound {
|
||||
pub(crate) const unsafe fn new() -> Self {
|
||||
Sound {}
|
||||
}
|
||||
|
||||
pub fn channel1(&self) -> Channel1 {
|
||||
Channel1 {}
|
||||
}
|
||||
|
||||
pub fn channel2(&self) -> Channel2 {
|
||||
Channel2 {}
|
||||
}
|
||||
|
||||
pub fn noise(&self) -> Noise {
|
||||
Noise {}
|
||||
}
|
||||
|
||||
pub fn enable(&self) {
|
||||
MASTER_SOUND_STATUS.set_bits(1, 1, 7);
|
||||
|
||||
#[allow(clippy::unusual_byte_groupings)] // I've split these like this for a reason
|
||||
MASTER_SOUND_VOLUME_ENABLE.set(0b1111_1111_0_111_0_111);
|
||||
MASTER_SOUND_VOLUME_MIXING.set(0b10);
|
||||
}
|
||||
}
|
||||
|
||||
#[non_exhaustive]
|
||||
pub struct Channel1 {}
|
||||
|
||||
impl Channel1 {
|
||||
pub fn play_sound(
|
||||
&self,
|
||||
frequency: u16,
|
||||
length: Option<u8>,
|
||||
sweep_settings: &SweepSettings,
|
||||
envelope_settings: &EnvelopeSettings,
|
||||
duty_cycle: DutyCycle,
|
||||
) {
|
||||
CHANNEL_1_SWEEP.set(sweep_settings.as_bits());
|
||||
let length_bits = length.unwrap_or(0) as u16;
|
||||
assert!(length_bits < 64, "Length must be less than 64");
|
||||
|
||||
let length_flag: u16 = length.map(|_| 1 << 14).unwrap_or(0);
|
||||
let initial: u16 = 1 << 15;
|
||||
|
||||
assert!(frequency < 2048, "Frequency must be less than 2048");
|
||||
|
||||
CHANNEL_1_LENGTH_DUTY_ENVELOPE
|
||||
.set(envelope_settings.as_bits() | duty_cycle.as_bits() | length_bits);
|
||||
CHANNEL_1_FREQUENCY_CONTROL.set(frequency | length_flag | initial);
|
||||
}
|
||||
}
|
||||
|
||||
#[non_exhaustive]
|
||||
pub struct Channel2 {}
|
||||
|
||||
impl Channel2 {
|
||||
pub fn play_sound(
|
||||
&self,
|
||||
frequency: u16,
|
||||
length: Option<u8>,
|
||||
envelope_settings: &EnvelopeSettings,
|
||||
duty_cycle: DutyCycle,
|
||||
) {
|
||||
let length_bits = length.unwrap_or(0) as u16;
|
||||
assert!(length_bits < 64, "Length must be less than 64");
|
||||
|
||||
let length_flag: u16 = length.map(|_| 1 << 14).unwrap_or(0);
|
||||
let initial: u16 = 1 << 15;
|
||||
|
||||
assert!(frequency < 2048, "Frequency must be less than 2048");
|
||||
|
||||
CHANNEL_2_LENGTH_DUTY_ENVELOPE
|
||||
.set(envelope_settings.as_bits() | duty_cycle.as_bits() | length_bits);
|
||||
CHANNEL_2_FREQUENCY_CONTROL.set(frequency | length_flag | initial);
|
||||
}
|
||||
}
|
||||
|
||||
#[non_exhaustive]
|
||||
pub struct Noise {}
|
||||
|
||||
impl Noise {
|
||||
pub fn play_sound(
|
||||
&self,
|
||||
length: Option<u8>,
|
||||
envelope_setting: &EnvelopeSettings,
|
||||
frequency_divider: u8,
|
||||
counter_step_width_15: bool,
|
||||
shift_clock_frequency: u8,
|
||||
) {
|
||||
let length_bits = length.unwrap_or(0) as u16;
|
||||
assert!(length_bits < 64, "length must be less than 16");
|
||||
|
||||
assert!(
|
||||
frequency_divider < 8,
|
||||
"frequency divider must be less than 8"
|
||||
);
|
||||
assert!(
|
||||
shift_clock_frequency < 16,
|
||||
"frequency clock divider must be less than 16"
|
||||
);
|
||||
|
||||
let length_flag: u16 = length.map(|_| 1 << 14).unwrap_or(0);
|
||||
let initial: u16 = 1 << 15;
|
||||
|
||||
let counter_step_bit = if counter_step_width_15 { 0 } else { 1 << 3 };
|
||||
|
||||
CHANNEL_4_LENGTH_ENVELOPE.set(length_bits | envelope_setting.as_bits());
|
||||
CHANNEL_4_FREQUENCY_CONTROL.set(
|
||||
(frequency_divider as u16)
|
||||
| counter_step_bit
|
||||
| ((shift_clock_frequency as u16) << 4)
|
||||
| length_flag
|
||||
| initial,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub enum SoundDirection {
|
||||
Increase,
|
||||
Decrease,
|
||||
}
|
||||
|
||||
impl SoundDirection {
|
||||
fn as_bits(&self) -> u16 {
|
||||
match &self {
|
||||
SoundDirection::Increase => 1,
|
||||
SoundDirection::Decrease => 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SweepSettings {
|
||||
number_of_sweep_shifts: u8,
|
||||
sound_direction: SoundDirection,
|
||||
sweep_time: u8,
|
||||
}
|
||||
|
||||
impl SweepSettings {
|
||||
pub fn new(
|
||||
number_of_sweep_shifts: u8,
|
||||
sound_direction: SoundDirection,
|
||||
sweep_time: u8,
|
||||
) -> Self {
|
||||
assert!(
|
||||
number_of_sweep_shifts < 8,
|
||||
"Number of sweep shifts must be less than 8"
|
||||
);
|
||||
assert!(sweep_time < 8, "Sweep time must be less than 8");
|
||||
|
||||
SweepSettings {
|
||||
number_of_sweep_shifts,
|
||||
sound_direction,
|
||||
sweep_time,
|
||||
}
|
||||
}
|
||||
|
||||
fn as_bits(&self) -> u16 {
|
||||
((self.number_of_sweep_shifts as u16) & 0b111)
|
||||
| ((1 - self.sound_direction.as_bits()) << 3) // sweep works backwards
|
||||
| ((self.sweep_time as u16) & 0b111) << 4
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for SweepSettings {
|
||||
fn default() -> Self {
|
||||
SweepSettings::new(0, SoundDirection::Increase, 0)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EnvelopeSettings {
|
||||
step_time: u8,
|
||||
direction: SoundDirection,
|
||||
initial_volume: u8,
|
||||
}
|
||||
|
||||
impl EnvelopeSettings {
|
||||
pub fn new(step_time: u8, direction: SoundDirection, initial_volume: u8) -> Self {
|
||||
assert!(step_time < 8, "Step time must be less than 8");
|
||||
assert!(initial_volume < 16, "Initial volume must be less that 16");
|
||||
EnvelopeSettings {
|
||||
step_time,
|
||||
direction,
|
||||
initial_volume,
|
||||
}
|
||||
}
|
||||
|
||||
fn as_bits(&self) -> u16 {
|
||||
(self.step_time as u16) << 8
|
||||
| (self.direction.as_bits() << 11)
|
||||
| ((self.initial_volume as u16) << 12)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for EnvelopeSettings {
|
||||
fn default() -> Self {
|
||||
EnvelopeSettings::new(0, SoundDirection::Increase, 15)
|
||||
}
|
||||
}
|
||||
|
||||
pub enum DutyCycle {
|
||||
OneEighth,
|
||||
OneQuarter,
|
||||
Half,
|
||||
ThreeQuarters,
|
||||
}
|
||||
|
||||
impl DutyCycle {
|
||||
fn as_bits(&self) -> u16 {
|
||||
use DutyCycle::*;
|
||||
|
||||
match &self {
|
||||
OneEighth => 0,
|
||||
OneQuarter => 1,
|
||||
Half => 2,
|
||||
ThreeQuarters => 3,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,238 +1 @@
|
|||
use crate::memory_mapped::MemoryMapped;
|
||||
|
||||
const CHANNEL_1_SWEEP: MemoryMapped<u16> = unsafe { MemoryMapped::new(0x0400_0060) };
|
||||
const CHANNEL_1_LENGTH_DUTY_ENVELOPE: MemoryMapped<u16> = unsafe { MemoryMapped::new(0x0400_0062) };
|
||||
const CHANNEL_1_FREQUENCY_CONTROL: MemoryMapped<u16> = unsafe { MemoryMapped::new(0x0400_0064) };
|
||||
|
||||
const CHANNEL_2_LENGTH_DUTY_ENVELOPE: MemoryMapped<u16> = unsafe { MemoryMapped::new(0x0400_0068) };
|
||||
const CHANNEL_2_FREQUENCY_CONTROL: MemoryMapped<u16> = unsafe { MemoryMapped::new(0x0400_006c) };
|
||||
|
||||
const CHANNEL_4_LENGTH_ENVELOPE: MemoryMapped<u16> = unsafe { MemoryMapped::new(0x0400_0078) };
|
||||
const CHANNEL_4_FREQUENCY_CONTROL: MemoryMapped<u16> = unsafe { MemoryMapped::new(0x0400_007c) };
|
||||
|
||||
const MASTER_SOUND_VOLUME_ENABLE: MemoryMapped<u16> = unsafe { MemoryMapped::new(0x0400_0080) };
|
||||
const MASTER_SOUND_VOLUME_MIXING: MemoryMapped<u16> = unsafe { MemoryMapped::new(0x0400_0082) };
|
||||
const MASTER_SOUND_STATUS: MemoryMapped<u16> = unsafe { MemoryMapped::new(0x0400_0084) };
|
||||
|
||||
#[non_exhaustive]
|
||||
pub struct Sound {}
|
||||
|
||||
impl Sound {
|
||||
pub(crate) const unsafe fn new() -> Self {
|
||||
Sound {}
|
||||
}
|
||||
|
||||
pub fn channel1(&self) -> Channel1 {
|
||||
Channel1 {}
|
||||
}
|
||||
|
||||
pub fn channel2(&self) -> Channel2 {
|
||||
Channel2 {}
|
||||
}
|
||||
|
||||
pub fn noise(&self) -> Noise {
|
||||
Noise {}
|
||||
}
|
||||
|
||||
pub fn enable(&self) {
|
||||
MASTER_SOUND_STATUS.set_bits(1, 1, 7);
|
||||
|
||||
#[allow(clippy::unusual_byte_groupings)] // I've split these like this for a reason
|
||||
MASTER_SOUND_VOLUME_ENABLE.set(0b1111_1111_0_111_0_111);
|
||||
MASTER_SOUND_VOLUME_MIXING.set(0b10);
|
||||
}
|
||||
}
|
||||
|
||||
#[non_exhaustive]
|
||||
pub struct Channel1 {}
|
||||
|
||||
impl Channel1 {
|
||||
pub fn play_sound(
|
||||
&self,
|
||||
frequency: u16,
|
||||
length: Option<u8>,
|
||||
sweep_settings: &SweepSettings,
|
||||
envelope_settings: &EnvelopeSettings,
|
||||
duty_cycle: DutyCycle,
|
||||
) {
|
||||
CHANNEL_1_SWEEP.set(sweep_settings.as_bits());
|
||||
let length_bits = length.unwrap_or(0) as u16;
|
||||
assert!(length_bits < 64, "Length must be less than 64");
|
||||
|
||||
let length_flag: u16 = length.map(|_| 1 << 14).unwrap_or(0);
|
||||
let initial: u16 = 1 << 15;
|
||||
|
||||
assert!(frequency < 2048, "Frequency must be less than 2048");
|
||||
|
||||
CHANNEL_1_LENGTH_DUTY_ENVELOPE
|
||||
.set(envelope_settings.as_bits() | duty_cycle.as_bits() | length_bits);
|
||||
CHANNEL_1_FREQUENCY_CONTROL.set(frequency | length_flag | initial);
|
||||
}
|
||||
}
|
||||
|
||||
#[non_exhaustive]
|
||||
pub struct Channel2 {}
|
||||
|
||||
impl Channel2 {
|
||||
pub fn play_sound(
|
||||
&self,
|
||||
frequency: u16,
|
||||
length: Option<u8>,
|
||||
envelope_settings: &EnvelopeSettings,
|
||||
duty_cycle: DutyCycle,
|
||||
) {
|
||||
let length_bits = length.unwrap_or(0) as u16;
|
||||
assert!(length_bits < 64, "Length must be less than 64");
|
||||
|
||||
let length_flag: u16 = length.map(|_| 1 << 14).unwrap_or(0);
|
||||
let initial: u16 = 1 << 15;
|
||||
|
||||
assert!(frequency < 2048, "Frequency must be less than 2048");
|
||||
|
||||
CHANNEL_2_LENGTH_DUTY_ENVELOPE
|
||||
.set(envelope_settings.as_bits() | duty_cycle.as_bits() | length_bits);
|
||||
CHANNEL_2_FREQUENCY_CONTROL.set(frequency | length_flag | initial);
|
||||
}
|
||||
}
|
||||
|
||||
#[non_exhaustive]
|
||||
pub struct Noise {}
|
||||
|
||||
impl Noise {
|
||||
pub fn play_sound(
|
||||
&self,
|
||||
length: Option<u8>,
|
||||
envelope_setting: &EnvelopeSettings,
|
||||
frequency_divider: u8,
|
||||
counter_step_width_15: bool,
|
||||
shift_clock_frequency: u8,
|
||||
) {
|
||||
let length_bits = length.unwrap_or(0) as u16;
|
||||
assert!(length_bits < 64, "length must be less than 16");
|
||||
|
||||
assert!(
|
||||
frequency_divider < 8,
|
||||
"frequency divider must be less than 8"
|
||||
);
|
||||
assert!(
|
||||
shift_clock_frequency < 16,
|
||||
"frequency clock divider must be less than 16"
|
||||
);
|
||||
|
||||
let length_flag: u16 = length.map(|_| 1 << 14).unwrap_or(0);
|
||||
let initial: u16 = 1 << 15;
|
||||
|
||||
let counter_step_bit = if counter_step_width_15 { 0 } else { 1 << 3 };
|
||||
|
||||
CHANNEL_4_LENGTH_ENVELOPE.set(length_bits | envelope_setting.as_bits());
|
||||
CHANNEL_4_FREQUENCY_CONTROL.set(
|
||||
(frequency_divider as u16)
|
||||
| counter_step_bit
|
||||
| ((shift_clock_frequency as u16) << 4)
|
||||
| length_flag
|
||||
| initial,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub enum SoundDirection {
|
||||
Increase,
|
||||
Decrease,
|
||||
}
|
||||
|
||||
impl SoundDirection {
|
||||
fn as_bits(&self) -> u16 {
|
||||
match &self {
|
||||
SoundDirection::Increase => 1,
|
||||
SoundDirection::Decrease => 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SweepSettings {
|
||||
number_of_sweep_shifts: u8,
|
||||
sound_direction: SoundDirection,
|
||||
sweep_time: u8,
|
||||
}
|
||||
|
||||
impl SweepSettings {
|
||||
pub fn new(
|
||||
number_of_sweep_shifts: u8,
|
||||
sound_direction: SoundDirection,
|
||||
sweep_time: u8,
|
||||
) -> Self {
|
||||
assert!(
|
||||
number_of_sweep_shifts < 8,
|
||||
"Number of sweep shifts must be less than 8"
|
||||
);
|
||||
assert!(sweep_time < 8, "Sweep time must be less than 8");
|
||||
|
||||
SweepSettings {
|
||||
number_of_sweep_shifts,
|
||||
sound_direction,
|
||||
sweep_time,
|
||||
}
|
||||
}
|
||||
|
||||
fn as_bits(&self) -> u16 {
|
||||
((self.number_of_sweep_shifts as u16) & 0b111)
|
||||
| ((1 - self.sound_direction.as_bits()) << 3) // sweep works backwards
|
||||
| ((self.sweep_time as u16) & 0b111) << 4
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for SweepSettings {
|
||||
fn default() -> Self {
|
||||
SweepSettings::new(0, SoundDirection::Increase, 0)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EnvelopeSettings {
|
||||
step_time: u8,
|
||||
direction: SoundDirection,
|
||||
initial_volume: u8,
|
||||
}
|
||||
|
||||
impl EnvelopeSettings {
|
||||
pub fn new(step_time: u8, direction: SoundDirection, initial_volume: u8) -> Self {
|
||||
assert!(step_time < 8, "Step time must be less than 8");
|
||||
assert!(initial_volume < 16, "Initial volume must be less that 16");
|
||||
EnvelopeSettings {
|
||||
step_time,
|
||||
direction,
|
||||
initial_volume,
|
||||
}
|
||||
}
|
||||
|
||||
fn as_bits(&self) -> u16 {
|
||||
(self.step_time as u16) << 8
|
||||
| (self.direction.as_bits() << 11)
|
||||
| ((self.initial_volume as u16) << 12)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for EnvelopeSettings {
|
||||
fn default() -> Self {
|
||||
EnvelopeSettings::new(0, SoundDirection::Increase, 15)
|
||||
}
|
||||
}
|
||||
|
||||
pub enum DutyCycle {
|
||||
OneEighth,
|
||||
OneQuarter,
|
||||
Half,
|
||||
ThreeQuarters,
|
||||
}
|
||||
|
||||
impl DutyCycle {
|
||||
fn as_bits(&self) -> u16 {
|
||||
use DutyCycle::*;
|
||||
|
||||
match &self {
|
||||
OneEighth => 0,
|
||||
OneQuarter => 1,
|
||||
Half => 2,
|
||||
ThreeQuarters => 3,
|
||||
}
|
||||
}
|
||||
}
|
||||
pub mod dmg;
|
||||
|
|
Loading…
Reference in a new issue