⚠️ VeridianOS Kernel Documentation - This is low-level kernel code. All functions are unsafe unless explicitly marked otherwise. no_std

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}