summaryrefslogtreecommitdiff
path: root/audio/src/channel.rs
diff options
context:
space:
mode:
Diffstat (limited to 'audio/src/channel.rs')
-rw-r--r--audio/src/channel.rs128
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);