diff options
Diffstat (limited to 'audio/src/channel.rs')
| -rw-r--r-- | audio/src/channel.rs | 128 |
1 files changed, 85 insertions, 43 deletions
diff --git a/audio/src/channel.rs b/audio/src/channel.rs index 64970f2..48d416b 100644 --- a/audio/src/channel.rs +++ b/audio/src/channel.rs @@ -1,20 +1,35 @@ #![expect(clippy::cast_possible_truncation)] -use std::sync::{Arc, Mutex}; - +use atomic::Atomic; +use bytemuck::NoUninit; use raylib::{ audio::{AudioStream, RaylibAudio}, prelude::audio_stream_callback::set_audio_stream_callback, }; +use std::sync::{Arc, atomic::Ordering}; const SAMPLE_RATE: u32 = 44100; const SAMPLE_SIZE: u32 = i16::BITS; -#[derive(Debug, Clone, Copy)] +fn map_pitch(pitch: u8) -> f32 { + const HALF_STEP: f32 = 1.059_463_1; + HALF_STEP.powf(pitch as f32 - 128.0) +} + +const fn map_volume(volume: u8) -> f32 { + if volume > 100 { + 1.0 + } else { + (volume as f32) / 100.0 + } +} + +#[repr(u8)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, NoUninit)] pub enum ChannelKind { Pulse { duty_cycle: u8 }, - Triangle, - Noise { mode: bool, lsr: i16 }, + Triangle { _pad: u8 }, + Noise { mode: bool }, } impl ChannelKind { pub const fn pulse() -> Self { @@ -22,75 +37,98 @@ impl ChannelKind { } pub const fn triangle() -> Self { - Self::Triangle + Self::Triangle { _pad: 0 } } pub const fn noise() -> Self { - Self::Noise { - mode: false, - lsr: 1, - } + Self::Noise { mode: false } } - fn sample(&mut self, phase: f32) -> i16 { + fn sample(self, phase: f32, lsr_ptr: &mut i16) -> i16 { match self { Self::Pulse { duty_cycle } => { - let duty = *duty_cycle as f32 / 100.0; + let duty = duty_cycle as f32 / 100.0; if phase < duty { i16::MAX } else { i16::MIN } } - Self::Triangle => { + Self::Triangle { .. } => { let steps = 32; let tphase = if phase < 0.5 { phase } else { 1.0 - phase }; let step = (tphase * steps as f32).floor() as u32; let value = ((step as f32 / (steps - 1) as f32) * 4.0) - 1.0; (value * i16::MAX as f32) as i16 } - Self::Noise { mode, lsr } => { - let feedback = if *mode { - (*lsr & 1) ^ ((*lsr >> 1) & 1) + Self::Noise { mode } => { + let mut lsr = *lsr_ptr; + let feedback = if mode { + (lsr & 1) ^ ((lsr >> 1) & 1) } else { - (*lsr & 1) ^ ((*lsr >> 6) & 1) + (lsr & 1) ^ ((lsr >> 6) & 1) }; - *lsr >>= 1; - *lsr |= feedback << 14; - *lsr + lsr >>= 1; + lsr |= feedback << 14; + *lsr_ptr = lsr; + lsr } } } } -#[derive(Debug)] +#[repr(C)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, NoUninit)] pub struct Channel { - pub volume: f32, - pub pitch: f32, + pub volume: u8, + pub pitch: u8, pub kind: ChannelKind, - phase: f32, } impl Channel { const fn new(kind: ChannelKind) -> Self { Self { - volume: 0.0, - pitch: 1.0, + volume: 0, + pitch: 128, kind, - phase: 0.0, } } - fn sample(&mut self, buffer: &mut [i16]) { - let freq = self.pitch * 440.0; + fn sample(self, buffer: &mut [i16], phase_ptr: &mut f32, lsr_ptr: &mut i16) { + let freq = map_pitch(self.pitch) * 440.0; + let volume = map_volume(self.volume); let step = freq / SAMPLE_RATE as f32; + let mut phase = *phase_ptr; for sample in buffer { - self.phase += step; - if self.phase >= 1.0 { - self.phase -= 1.0; + phase += step; + if phase >= 1.0 { + phase -= 1.0; } - let real_note = self.kind.sample(self.phase); - let note = (real_note as f32 * self.volume) as i16; + let real_note = self.kind.sample(phase, lsr_ptr); + let note = (real_note as f32 * volume) as i16; *sample += note / 4; } + *phase_ptr = phase; } } +#[derive(Debug, Clone, Copy)] +struct RuntimeState { + pulse_a_phase: f32, + pulse_b_phase: f32, + triangle_phase: f32, + noise_phase: f32, + noise_lsfr: i16, +} +impl RuntimeState { + const fn new() -> Self { + Self { + pulse_a_phase: 0.0, + pulse_b_phase: 0.0, + triangle_phase: 0.0, + noise_phase: 0.0, + noise_lsfr: 1, + } + } +} + +#[repr(C)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, NoUninit)] pub struct Channels { pub pulse_a: Channel, pub pulse_b: Channel, @@ -98,7 +136,7 @@ pub struct Channels { pub noise: Channel, } impl Channels { - const fn new() -> Self { + pub const fn new() -> Self { let pulse_a = Channel::new(ChannelKind::pulse()); let pulse_b = Channel::new(ChannelKind::pulse()); let triangle = Channel::new(ChannelKind::triangle()); @@ -113,23 +151,27 @@ impl Channels { } pub struct Device<'s> { - pub channels: Arc<Mutex<Channels>>, + pub channels: Arc<Atomic<Channels>>, #[expect(dead_code)] stream: AudioStream<'s>, } -#[expect(clippy::unwrap_used)] impl<'s> Device<'s> { pub(crate) fn load(handle: &'s RaylibAudio) -> crate::Result<Self> { - let channels = Arc::new(Mutex::new(Channels::new())); + let channels = Arc::new(Atomic::new(Channels::new())); let stream = handle.new_audio_stream(SAMPLE_RATE, SAMPLE_SIZE, 1); let cb_data = Arc::clone(&channels); + let mut state = RuntimeState::new(); set_audio_stream_callback(&stream, move |buffer| { - let mut state = cb_data.lock().unwrap(); - state.pulse_a.sample(buffer); - state.pulse_b.sample(buffer); - state.triangle.sample(buffer); - state.noise.sample(buffer); + let chan = cb_data.load(Ordering::Relaxed); + chan.pulse_a + .sample(buffer, &mut state.pulse_a_phase, &mut state.noise_lsfr); + chan.pulse_b + .sample(buffer, &mut state.pulse_b_phase, &mut state.noise_lsfr); + chan.triangle + .sample(buffer, &mut state.triangle_phase, &mut state.noise_lsfr); + chan.noise + .sample(buffer, &mut state.noise_phase, &mut state.noise_lsfr); })?; stream.set_volume(1.0); stream.set_pitch(1.0); |