veridian_kernel/audio/mod.rs
1//! Audio subsystem for VeridianOS
2//!
3//! Provides audio mixing, playback, and device management:
4//! - Fixed-point 16.16 audio mixer (no FPU required)
5//! - Ring buffer transport for audio streams
6//! - WAV file parsing (PCM formats)
7//! - Client API for creating and managing audio streams
8//! - Output pipeline with underrun detection
9//! - VirtIO-Sound driver for paravirtualized audio
10
11#![allow(dead_code)]
12
13pub mod alsa;
14pub mod buffer;
15pub mod client;
16pub mod codecs;
17pub mod mixer;
18pub mod pipeline;
19pub mod usb_audio;
20pub(crate) mod virtio_sound;
21pub mod wav;
22
23use alloc::{string::String, vec::Vec};
24
25use crate::error::KernelError;
26
27// ============================================================================
28// Unified Audio Error Type
29// ============================================================================
30
31/// Common audio error type for all audio backends
32#[derive(Debug, Clone, PartialEq)]
33#[allow(dead_code)]
34pub enum AudioError {
35 /// Requested device was not found
36 DeviceNotFound,
37 /// Device is in use by another client
38 DeviceBusy,
39 /// Configuration is invalid
40 InvalidConfig { reason: &'static str },
41 /// Capture buffer overrun -- data was lost
42 BufferOverrun,
43 /// Playback buffer underrun -- device starved
44 BufferUnderrun,
45 /// Device has not been started
46 NotStarted,
47 /// Device is already running
48 AlreadyStarted,
49 /// Requested format is not supported by this device
50 UnsupportedFormat,
51 /// Low-level I/O error communicating with hardware
52 IoError,
53}
54
55impl core::fmt::Display for AudioError {
56 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
57 match self {
58 AudioError::DeviceNotFound => write!(f, "audio device not found"),
59 AudioError::DeviceBusy => write!(f, "audio device busy"),
60 AudioError::InvalidConfig { reason } => {
61 write!(f, "invalid audio config: {}", reason)
62 }
63 AudioError::BufferOverrun => write!(f, "audio buffer overrun"),
64 AudioError::BufferUnderrun => write!(f, "audio buffer underrun"),
65 AudioError::NotStarted => write!(f, "audio device not started"),
66 AudioError::AlreadyStarted => write!(f, "audio device already started"),
67 AudioError::UnsupportedFormat => write!(f, "unsupported audio format"),
68 AudioError::IoError => write!(f, "audio I/O error"),
69 }
70 }
71}
72
73// ============================================================================
74// Device Capabilities
75// ============================================================================
76
77/// Describes the hardware capabilities of an audio device
78#[derive(Debug, Clone)]
79#[allow(dead_code)]
80pub struct AudioDeviceCapabilities {
81 /// Minimum supported sample rate in Hz
82 pub min_sample_rate: u32,
83 /// Maximum supported sample rate in Hz
84 pub max_sample_rate: u32,
85 /// Minimum supported channel count
86 pub min_channels: u8,
87 /// Maximum supported channel count
88 pub max_channels: u8,
89 /// List of supported sample formats
90 pub supported_formats: Vec<SampleFormat>,
91 /// Whether the device supports playback
92 pub playback: bool,
93 /// Whether the device supports capture
94 pub capture: bool,
95}
96
97// ============================================================================
98// Unified Audio Device Trait
99// ============================================================================
100
101/// Unified audio device trait for playback and capture backends
102///
103/// Provides a common interface across ALSA PCM devices, VirtIO Sound streams,
104/// USB Audio Class devices, and any future audio backends. Implementations
105/// adapt existing backend-specific APIs to this shared contract.
106#[allow(dead_code)]
107pub trait AudioDevice {
108 /// Configure the device with desired parameters.
109 ///
110 /// The device may adjust parameters to the nearest supported values.
111 /// Returns the actual configuration that was applied.
112 fn configure(&mut self, config: &AudioConfig) -> Result<AudioConfig, AudioError>;
113
114 /// Start playback or capture.
115 fn start(&mut self) -> Result<(), AudioError>;
116
117 /// Stop playback or capture.
118 fn stop(&mut self) -> Result<(), AudioError>;
119
120 /// Write PCM samples for playback.
121 ///
122 /// Returns the number of frames written. The data must be interleaved
123 /// samples in the format specified by the current configuration.
124 fn write_frames(&mut self, data: &[u8]) -> Result<usize, AudioError>;
125
126 /// Read PCM samples from capture.
127 ///
128 /// Returns the number of frames read. The output buffer is filled with
129 /// interleaved samples in the format specified by the current
130 /// configuration.
131 fn read_frames(&mut self, output: &mut [u8]) -> Result<usize, AudioError>;
132
133 /// Query device capabilities.
134 fn capabilities(&self) -> &AudioDeviceCapabilities;
135
136 /// Human-readable device name.
137 fn name(&self) -> &str;
138
139 /// Returns true if this device supports playback.
140 fn is_playback(&self) -> bool;
141
142 /// Returns true if this device supports capture.
143 fn is_capture(&self) -> bool;
144}
145
146/// Audio sample format
147#[derive(Debug, Clone, Copy, PartialEq, Eq)]
148pub enum SampleFormat {
149 /// Unsigned 8-bit PCM
150 U8,
151 /// Signed 16-bit little-endian PCM
152 S16Le,
153 /// Signed 16-bit big-endian PCM
154 S16Be,
155 /// Signed 24-bit little-endian PCM (packed in 3 bytes)
156 S24Le,
157 /// Signed 32-bit little-endian PCM
158 S32Le,
159 /// 32-bit float stored as i32 fixed-point (16.16 format)
160 F32,
161}
162
163impl SampleFormat {
164 /// Returns the size in bytes of a single sample in this format
165 pub fn bytes_per_sample(&self) -> usize {
166 match self {
167 SampleFormat::U8 => 1,
168 SampleFormat::S16Le | SampleFormat::S16Be => 2,
169 SampleFormat::S24Le => 3,
170 SampleFormat::S32Le | SampleFormat::F32 => 4,
171 }
172 }
173}
174
175/// Audio stream configuration
176#[derive(Debug, Clone, Copy)]
177pub struct AudioConfig {
178 /// Sample rate in Hz (e.g., 44100, 48000)
179 pub sample_rate: u32,
180 /// Number of audio channels (1 = mono, 2 = stereo)
181 pub channels: u8,
182 /// Sample format
183 pub format: SampleFormat,
184 /// Number of frames per buffer period
185 pub buffer_frames: u32,
186}
187
188impl AudioConfig {
189 /// Create a default stereo 16-bit 48kHz configuration
190 pub fn default_config() -> Self {
191 Self {
192 sample_rate: 48000,
193 channels: 2,
194 format: SampleFormat::S16Le,
195 buffer_frames: 1024,
196 }
197 }
198
199 /// Calculate the size of a single frame in bytes (channels * sample_size)
200 pub fn frame_size(&self) -> u16 {
201 (self.channels as u16) * (self.format.bytes_per_sample() as u16)
202 }
203
204 /// Calculate the byte rate (sample_rate * frame_size)
205 pub fn byte_rate(&self) -> u32 {
206 self.sample_rate * self.frame_size() as u32
207 }
208}
209
210/// Audio routing entry connecting a source to a sink
211#[derive(Debug, Clone, Copy)]
212pub struct AudioRoute {
213 /// Source stream/channel identifier
214 pub source_id: u16,
215 /// Sink stream/device identifier
216 pub sink_id: u16,
217 /// Volume level: 0..=65535 maps to 0.0..1.0 in fixed-point
218 pub volume: u16,
219}
220
221/// Information about an audio device
222#[derive(Debug, Clone)]
223pub struct AudioDeviceInfo {
224 /// Unique device identifier
225 pub id: u16,
226 /// Human-readable device name
227 pub name: String,
228 /// Whether this device supports audio output (playback)
229 pub is_output: bool,
230 /// Whether this device supports audio input (capture)
231 pub is_input: bool,
232 /// Current device configuration
233 pub config: AudioConfig,
234}
235
236/// Initialize the audio subsystem
237///
238/// Sets up the mixer, output pipeline, client manager, and probes for
239/// VirtIO-Sound hardware.
240pub fn init() -> Result<(), KernelError> {
241 println!("[AUDIO] Initializing audio subsystem...");
242
243 // Initialize the mixer at 48kHz
244 mixer::init(48000)?;
245
246 // Initialize the output pipeline with default config
247 pipeline::init(AudioConfig::default_config())?;
248
249 // Initialize the client manager
250 client::init();
251
252 // Probe for VirtIO-Sound hardware (non-fatal if absent)
253 if let Err(_e) = virtio_sound::init() {
254 println!("[AUDIO] No VirtIO-Sound device found (non-fatal)");
255 }
256
257 println!("[AUDIO] Audio subsystem initialized");
258 Ok(())
259}
260
261#[cfg(test)]
262mod tests {
263 use super::*;
264
265 #[test]
266 fn test_sample_format_size() {
267 assert_eq!(SampleFormat::U8.bytes_per_sample(), 1);
268 assert_eq!(SampleFormat::S16Le.bytes_per_sample(), 2);
269 assert_eq!(SampleFormat::S16Be.bytes_per_sample(), 2);
270 assert_eq!(SampleFormat::S24Le.bytes_per_sample(), 3);
271 assert_eq!(SampleFormat::S32Le.bytes_per_sample(), 4);
272 assert_eq!(SampleFormat::F32.bytes_per_sample(), 4);
273 }
274
275 #[test]
276 fn test_audio_config_frame_size() {
277 let config = AudioConfig {
278 sample_rate: 44100,
279 channels: 2,
280 format: SampleFormat::S16Le,
281 buffer_frames: 1024,
282 };
283 assert_eq!(config.frame_size(), 4); // 2 channels * 2 bytes
284 assert_eq!(config.byte_rate(), 44100 * 4);
285 }
286
287 #[test]
288 fn test_default_config() {
289 let config = AudioConfig::default_config();
290 assert_eq!(config.sample_rate, 48000);
291 assert_eq!(config.channels, 2);
292 assert_eq!(config.format, SampleFormat::S16Le);
293 assert_eq!(config.buffer_frames, 1024);
294 }
295}