1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
|
#![expect(clippy::cast_possible_truncation)]
use std::sync::{Arc, Mutex};
use raylib::{
audio::{AudioStream, RaylibAudio},
prelude::audio_stream_callback::set_audio_stream_callback,
};
const SAMPLE_RATE: u32 = 44100;
const SAMPLE_SIZE: u32 = i16::BITS;
#[derive(Debug, Clone, Copy)]
pub enum ChannelKind {
Pulse { duty_cycle: u8 },
Triangle,
Noise { mode: bool, lsr: i16 },
}
impl ChannelKind {
pub const fn pulse() -> Self {
Self::Pulse { duty_cycle: 50 }
}
pub const fn triangle() -> Self {
Self::Triangle
}
pub const fn noise() -> Self {
Self::Noise {
mode: false,
lsr: 1,
}
}
fn sample(&mut self, phase: f32) -> i16 {
match self {
Self::Pulse { duty_cycle } => {
let duty = *duty_cycle as f32 / 100.0;
if phase < duty { i16::MAX } else { i16::MIN }
}
Self::Triangle => {
let steps = 32;
let step = (phase * steps as f32).floor() as u32;
let value = (((step as f32 / (steps - 1) as f32) * 2.0) - 0.5) * 2.0;
(value * i16::MAX as f32) as i16
}
Self::Noise { mode, lsr } => {
let feedback = if *mode {
(*lsr & 1) ^ ((*lsr >> 1) & 1)
} else {
(*lsr & 1) ^ ((*lsr >> 6) & 1)
};
*lsr >>= 1;
*lsr |= feedback << 14;
*lsr
}
}
}
}
#[derive(Debug)]
pub struct Channel {
pub volume: f32,
pub pitch: f32,
pub kind: ChannelKind,
phase: f32,
}
impl Channel {
const fn new(kind: ChannelKind) -> Self {
Self {
volume: 0.0,
pitch: 1.0,
kind,
phase: 0.0,
}
}
fn sample(&mut self, buffer: &mut [i16]) {
let freq = self.pitch * 440.0;
let step = freq / SAMPLE_RATE as f32;
for sample in buffer {
self.phase += step;
if self.phase >= 1.0 {
self.phase -= 1.0;
}
let real_note = self.kind.sample(self.phase);
let note = (real_note as f32 * self.volume) as i16;
*sample += note / 4;
}
}
}
pub struct Channels {
pub pulse_a: Channel,
pub pulse_b: Channel,
pub triangle: Channel,
pub noise: Channel,
}
impl Channels {
const fn new() -> Self {
let pulse_a = Channel::new(ChannelKind::pulse());
let pulse_b = Channel::new(ChannelKind::pulse());
let triangle = Channel::new(ChannelKind::triangle());
let noise = Channel::new(ChannelKind::noise());
Self {
pulse_a,
pulse_b,
triangle,
noise,
}
}
}
pub struct Device<'s> {
pub channels: Arc<Mutex<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 stream = handle.new_audio_stream(SAMPLE_RATE, SAMPLE_SIZE, 1);
let cb_data = Arc::clone(&channels);
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);
})?;
stream.set_volume(1.0);
stream.set_pitch(1.0);
stream.play();
Ok(Self { channels, stream })
}
}
|