1#![allow(dead_code)]
8
9use alloc::{collections::BTreeMap, string::String};
10use core::sync::atomic::{AtomicU32, Ordering};
11
12use crate::{
13 audio::{buffer::SharedAudioBuffer, AudioConfig},
14 error::KernelError,
15};
16
17const DEFAULT_BUFFER_FRAMES: u32 = 4096;
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
26pub struct AudioStreamId(pub u32);
27
28impl AudioStreamId {
29 pub fn as_u32(&self) -> u32 {
31 self.0
32 }
33}
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq)]
37pub enum StreamState {
38 Stopped,
40 Playing,
42 Paused,
44}
45
46pub struct AudioStream {
48 pub id: AudioStreamId,
50 pub config: AudioConfig,
52 pub buffer: SharedAudioBuffer,
54 pub mixer_channel_id: u16,
56 pub state: StreamState,
58 pub name: String,
60}
61
62pub struct AudioClient {
68 streams: BTreeMap<u32, AudioStream>,
70 next_id: AtomicU32,
72}
73
74impl AudioClient {
75 fn new() -> Self {
77 Self {
78 streams: BTreeMap::new(),
79 next_id: AtomicU32::new(1),
80 }
81 }
82
83 pub fn create_stream(
88 &mut self,
89 name: &str,
90 config: AudioConfig,
91 ) -> Result<AudioStreamId, KernelError> {
92 let id = self.next_id.fetch_add(1, Ordering::Relaxed);
93 let stream_id = AudioStreamId(id);
94
95 let buffer_frames = if config.buffer_frames > 0 {
97 config.buffer_frames
98 } else {
99 DEFAULT_BUFFER_FRAMES
100 };
101 let buffer = SharedAudioBuffer::new(buffer_frames, config);
102
103 let mixer_channel_id = crate::audio::mixer::with_mixer(|mixer| mixer.add_channel(name))?;
105
106 let stream = AudioStream {
107 id: stream_id,
108 config,
109 buffer,
110 mixer_channel_id,
111 state: StreamState::Stopped,
112 name: String::from(name),
113 };
114
115 self.streams.insert(id, stream);
116 println!("[AUDIO] Created stream '{}' (id={})", name, id);
117
118 Ok(stream_id)
119 }
120
121 pub fn destroy_stream(&mut self, id: AudioStreamId) -> Result<(), KernelError> {
123 let stream = self.streams.remove(&id.0).ok_or(KernelError::NotFound {
124 resource: "audio stream",
125 id: id.0 as u64,
126 })?;
127
128 let _ = crate::audio::mixer::with_mixer(|mixer| {
130 mixer.remove_channel(stream.mixer_channel_id);
131 });
132
133 println!("[AUDIO] Destroyed stream '{}' (id={})", stream.name, id.0);
134 Ok(())
135 }
136
137 pub fn write_samples(
142 &mut self,
143 id: AudioStreamId,
144 samples: &[i16],
145 ) -> Result<usize, KernelError> {
146 let stream = self.streams.get_mut(&id.0).ok_or(KernelError::NotFound {
147 resource: "audio stream",
148 id: id.0 as u64,
149 })?;
150
151 let written = stream.buffer.write_samples(samples);
152
153 let channel_id = stream.mixer_channel_id;
155 let _ = crate::audio::mixer::with_mixer(|mixer| {
156 mixer.write_channel_samples(channel_id, samples);
157 });
158
159 Ok(written)
160 }
161
162 pub fn play(&mut self, id: AudioStreamId) -> Result<(), KernelError> {
164 let stream = self.streams.get_mut(&id.0).ok_or(KernelError::NotFound {
165 resource: "audio stream",
166 id: id.0 as u64,
167 })?;
168
169 stream.state = StreamState::Playing;
170 println!("[AUDIO] Stream '{}' (id={}) -> Playing", stream.name, id.0);
171 Ok(())
172 }
173
174 pub fn pause(&mut self, id: AudioStreamId) -> Result<(), KernelError> {
176 let stream = self.streams.get_mut(&id.0).ok_or(KernelError::NotFound {
177 resource: "audio stream",
178 id: id.0 as u64,
179 })?;
180
181 if stream.state != StreamState::Playing {
182 return Err(KernelError::InvalidState {
183 expected: "Playing",
184 actual: "not Playing",
185 });
186 }
187
188 stream.state = StreamState::Paused;
189 println!("[AUDIO] Stream '{}' (id={}) -> Paused", stream.name, id.0);
190 Ok(())
191 }
192
193 pub fn stop(&mut self, id: AudioStreamId) -> Result<(), KernelError> {
195 let stream = self.streams.get_mut(&id.0).ok_or(KernelError::NotFound {
196 resource: "audio stream",
197 id: id.0 as u64,
198 })?;
199
200 stream.state = StreamState::Stopped;
201 println!("[AUDIO] Stream '{}' (id={}) -> Stopped", stream.name, id.0);
202 Ok(())
203 }
204
205 pub fn set_volume(&self, id: AudioStreamId, volume: u16) -> Result<(), KernelError> {
207 let stream = self.streams.get(&id.0).ok_or(KernelError::NotFound {
208 resource: "audio stream",
209 id: id.0 as u64,
210 })?;
211
212 let channel_id = stream.mixer_channel_id;
213 crate::audio::mixer::with_mixer(|mixer| {
214 mixer.set_volume(channel_id, volume);
215 })?;
216
217 Ok(())
218 }
219
220 pub fn get_state(&self, id: AudioStreamId) -> Result<StreamState, KernelError> {
222 let stream = self.streams.get(&id.0).ok_or(KernelError::NotFound {
223 resource: "audio stream",
224 id: id.0 as u64,
225 })?;
226 Ok(stream.state)
227 }
228
229 pub fn close_stream(&mut self, id: AudioStreamId) -> Result<(), KernelError> {
231 self.streams.remove(&id.0).ok_or(KernelError::NotFound {
232 resource: "audio stream",
233 id: id.0 as u64,
234 })?;
235 println!("[AUDIO] Closed stream {}", id.0);
236 Ok(())
237 }
238
239 pub fn stream_count(&self) -> usize {
241 self.streams.len()
242 }
243
244 pub fn default_sample_rate(&self) -> u32 {
246 48000
247 }
248
249 pub fn default_channels(&self) -> u8 {
251 2
252 }
253}
254
255static CLIENT: spin::Mutex<Option<AudioClient>> = spin::Mutex::new(None);
260
261pub fn init() {
263 let mut client = CLIENT.lock();
264 *client = Some(AudioClient::new());
265 println!("[AUDIO] Client manager initialized");
266}
267
268pub fn with_client<R, F: FnOnce(&mut AudioClient) -> R>(f: F) -> Result<R, KernelError> {
270 let mut guard = CLIENT.lock();
271 match guard.as_mut() {
272 Some(client) => Ok(f(client)),
273 None => Err(KernelError::NotInitialized {
274 subsystem: "audio client",
275 }),
276 }
277}
278
279#[cfg(test)]
284mod tests {
285 use super::*;
286 use crate::audio::SampleFormat;
287
288 fn test_config() -> AudioConfig {
289 AudioConfig {
290 sample_rate: 48000,
291 channels: 2,
292 format: SampleFormat::S16Le,
293 buffer_frames: 1024,
294 }
295 }
296
297 #[test]
298 fn test_audio_stream_id() {
299 let id = AudioStreamId(42);
300 assert_eq!(id.as_u32(), 42);
301 }
302
303 #[test]
304 fn test_stream_state() {
305 assert_eq!(StreamState::Stopped, StreamState::Stopped);
306 assert_ne!(StreamState::Playing, StreamState::Paused);
307 }
308}