use crate::utils::OutOfDomainError;
use core::mem;
#[derive(PartialEq, Eq)]
pub struct Audio(pub(crate) ());
impl Audio {
pub fn tone(&self, frequency: LinearFrequency, duration: Duration, volume: u32, flags: Flags) {
unsafe { wasm4_sys::tone(frequency.inner(), duration.inner(), volume, flags.inner()) }
}
pub fn leak(self) -> &'static Self {
&Audio(())
}
}
#[derive(Clone, Copy)]
pub struct Flags(u32);
impl Flags {
pub const fn new(channel: Channel, mode: Mode) -> Self {
Flags(mode as u32 | channel as u32)
}
pub const fn inner(self) -> u32 {
self.0
}
pub const fn channel(self) -> Channel {
unsafe { mem::transmute(self.0 & 0b0011) }
}
pub const fn mode(self) -> Mode {
unsafe { mem::transmute(self.0 & 0b1100) }
}
pub const fn with_channel(self, value: Channel) -> Self {
Flags(self.0 & !0b0011 | value as u32)
}
pub const fn with_mode(self, mode: Mode) -> Self {
Flags(self.0 & !0b1100 | mode as u32)
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
#[repr(u32)]
pub enum Channel {
Pulse1,
Pulse2,
Triangle,
Noise,
}
#[derive(Clone, Copy, PartialEq, Eq)]
#[repr(u32)]
pub enum Mode {
N1D8 = 0b0000,
N1D4 = 0b0100,
N1D2 = 0b1000,
N3D4 = 0b1100,
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct LinearFrequency(pub u32);
impl LinearFrequency {
pub fn constant(freq: Frequency) -> Self {
LinearFrequency(freq.0)
}
pub fn linear(start: Frequency, end: Frequency) -> Self {
LinearFrequency(start.0 | end.0 << 16)
}
pub fn inner(self) -> u32 {
self.0
}
pub fn start(self) -> Frequency {
Frequency(self.0 & 0x0000ffff)
}
pub fn end(self) -> Frequency {
Frequency(self.0 >> 16)
}
pub fn with_start(self, freq: Frequency) -> Self {
LinearFrequency(self.0 & !0x0000ffff | freq.0)
}
pub fn with_end(self, freq: Frequency) -> Self {
LinearFrequency(self.0 & !0xffff0000 | freq.0 << 16)
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct Frequency(u32);
impl From<u16> for Frequency {
fn from(value: u16) -> Self {
Frequency(u32::from(value))
}
}
impl TryFrom<u32> for Frequency {
type Error = OutOfDomainError;
fn try_from(value: u32) -> Result<Self, Self::Error> {
if value < 0x10000 {
Ok(Frequency(value))
} else {
Err(OutOfDomainError(()))
}
}
}
impl From<Frequency> for u32 {
fn from(value: Frequency) -> Self {
value.0
}
}
impl From<Frequency> for u16 {
fn from(value: Frequency) -> Self {
value.0 as u16
}
}
#[derive(Clone, Copy, PartialEq, Eq, Default)]
pub struct Duration(pub u32);
impl Duration {
pub fn inner(self) -> u32 {
self.0
}
pub fn sustain(self) -> Frames {
Frames(self.0 & 0xff)
}
pub fn release(self) -> Frames {
Frames(self.0 >> 8 & 0xff)
}
pub fn decay(self) -> Frames {
Frames(self.0 >> 16 & 0xff)
}
pub fn attack(&self) -> Frames {
Frames(self.0 >> 24)
}
pub fn with_sustain(self, value: Frames) -> Self {
Duration(self.0 & !0x000000ff | value.0)
}
pub fn with_release(self, value: Frames) -> Self {
Duration(self.0 & !0x0000ff00 | value.0 << 8)
}
pub fn with_decay(self, value: Frames) -> Self {
Duration(self.0 & !0x00ff0000 | value.0 << 16)
}
pub fn with_attack(self, value: Frames) -> Self {
Duration(self.0 & !0xff000000 | value.0 << 24)
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct Frames(u32);
impl Frames {
pub fn inner(self) -> u32 {
self.0
}
}
impl From<u8> for Frames {
fn from(value: u8) -> Self {
Frames(u32::from(value))
}
}
impl TryFrom<u32> for Frames {
type Error = OutOfDomainError;
fn try_from(value: u32) -> Result<Self, Self::Error> {
if value < 0x100 {
Ok(Frames(value))
} else {
Err(OutOfDomainError(()))
}
}
}
impl From<Frames> for u32 {
fn from(value: Frames) -> Self {
value.0
}
}
impl From<Frames> for u8 {
fn from(value: Frames) -> Self {
value.0 as u8
}
}