1#![allow(dead_code)]
22
23extern crate alloc;
24use alloc::{collections::BTreeMap, string::String, vec, vec::Vec};
25use core::sync::atomic::{AtomicU32, AtomicU64, Ordering};
26
27use super::{AudioConfig, AudioDevice, AudioDeviceCapabilities, AudioError, SampleFormat};
28
29const MAX_PCM_DEVICES: usize = 16;
35
36pub(crate) const SNDRV_PCM_IOCTL_HW_PARAMS: u32 = 0x4111;
42pub(crate) const SNDRV_PCM_IOCTL_SW_PARAMS: u32 = 0x4113;
43pub(crate) const SNDRV_PCM_IOCTL_STATUS: u32 = 0x4120;
44pub(crate) const SNDRV_PCM_IOCTL_PREPARE: u32 = 0x4140;
45pub(crate) const SNDRV_PCM_IOCTL_START: u32 = 0x4142;
46pub(crate) const SNDRV_PCM_IOCTL_STOP: u32 = 0x4143;
47
48#[repr(C)]
50#[derive(Debug, Clone, Copy)]
51pub(crate) struct IoctlHwParams {
52 pub format: u32,
53 pub channels: u32,
54 pub rate: u32,
55 pub period_size: u32,
56 pub buffer_size: u32,
57}
58
59#[repr(C)]
61#[derive(Debug, Clone, Copy)]
62pub(crate) struct IoctlSwParams {
63 pub start_threshold: u32,
64 pub stop_threshold: u32,
65 pub avail_min: u32,
66 pub silence_threshold: u32,
67}
68
69#[repr(C)]
71#[derive(Debug, Clone, Copy)]
72pub(crate) struct IoctlStatus {
73 pub state: u32,
74 pub hw_ptr: u64,
75 pub appl_ptr: u64,
76 pub avail: u32,
77 pub delay: u32,
78}
79
80pub(crate) fn alsa_pcm_ioctl(_fd: i32, cmd: u32, _arg: usize) -> Result<i64, AlsaError> {
85 match cmd {
86 SNDRV_PCM_IOCTL_HW_PARAMS => {
87 Ok(0)
89 }
90 SNDRV_PCM_IOCTL_SW_PARAMS => {
91 Ok(0)
93 }
94 SNDRV_PCM_IOCTL_STATUS => {
95 Ok(0)
97 }
98 SNDRV_PCM_IOCTL_PREPARE => {
99 Ok(0)
101 }
102 SNDRV_PCM_IOCTL_START => {
103 Ok(0)
105 }
106 SNDRV_PCM_IOCTL_STOP => {
107 Ok(0)
109 }
110 _ => Err(AlsaError::InvalidFormat),
111 }
112}
113
114const MAX_MIXER_CONTROLS: usize = 32;
116
117const DEFAULT_BUFFER_FRAMES: u32 = 4096;
119
120const DEFAULT_PERIOD_FRAMES: u32 = 1024;
122
123const DEFAULT_SAMPLE_RATE: u32 = 48000;
125
126const DEFAULT_CHANNELS: u8 = 2;
128
129const FP_SHIFT: u32 = 16;
131
132const FP_ONE: i32 = 1 << FP_SHIFT;
134
135const MAX_CAPTURE_DEVICES: usize = 8;
137
138const CAPTURE_BUFFER_FRAMES: u32 = 8192;
140
141#[derive(Debug, Clone, Copy, PartialEq, Eq)]
147pub enum AlsaError {
148 DeviceNotFound { device_id: u32 },
150 DeviceAlreadyOpen { device_id: u32 },
152 DeviceNotOpen { device_id: u32 },
154 InvalidStateTransition {
156 current: PcmState,
157 requested: PcmState,
158 },
159 HwParamsNotSet,
161 SwParamsNotSet,
163 Overrun { lost_frames: u32 },
165 Underrun { missed_frames: u32 },
167 InvalidFormat,
169 InvalidSampleRate { rate: u32 },
171 InvalidChannels { count: u8 },
173 InvalidBufferSize { requested: u32, max: u32 },
175 InvalidPeriodSize { requested: u32, buffer_size: u32 },
177 BufferFull,
179 BufferEmpty,
181 MixerControlNotFound { id: u32 },
183 MixerValueOutOfRange { value: i32, min: i32, max: i32 },
185 TooManyDevices,
187 WrongDirection,
189 DeviceBusy { device_id: u32 },
191}
192
193impl core::fmt::Display for AlsaError {
194 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
195 match self {
196 AlsaError::DeviceNotFound { device_id } => {
197 write!(f, "ALSA: device {} not found", device_id)
198 }
199 AlsaError::DeviceAlreadyOpen { device_id } => {
200 write!(f, "ALSA: device {} already open", device_id)
201 }
202 AlsaError::DeviceNotOpen { device_id } => {
203 write!(f, "ALSA: device {} not open", device_id)
204 }
205 AlsaError::InvalidStateTransition { current, requested } => {
206 write!(
207 f,
208 "ALSA: invalid state transition {:?} -> {:?}",
209 current, requested
210 )
211 }
212 AlsaError::HwParamsNotSet => write!(f, "ALSA: hardware parameters not configured"),
213 AlsaError::SwParamsNotSet => write!(f, "ALSA: software parameters not configured"),
214 AlsaError::Overrun { lost_frames } => {
215 write!(f, "ALSA: capture overrun, {} frames lost", lost_frames)
216 }
217 AlsaError::Underrun { missed_frames } => {
218 write!(
219 f,
220 "ALSA: playback underrun, {} frames missed",
221 missed_frames
222 )
223 }
224 AlsaError::InvalidFormat => write!(f, "ALSA: invalid sample format"),
225 AlsaError::InvalidSampleRate { rate } => {
226 write!(f, "ALSA: invalid sample rate {}", rate)
227 }
228 AlsaError::InvalidChannels { count } => {
229 write!(f, "ALSA: invalid channel count {}", count)
230 }
231 AlsaError::InvalidBufferSize { requested, max } => {
232 write!(f, "ALSA: invalid buffer size {} (max {})", requested, max)
233 }
234 AlsaError::InvalidPeriodSize {
235 requested,
236 buffer_size,
237 } => {
238 write!(
239 f,
240 "ALSA: period size {} exceeds buffer size {}",
241 requested, buffer_size
242 )
243 }
244 AlsaError::BufferFull => write!(f, "ALSA: buffer full"),
245 AlsaError::BufferEmpty => write!(f, "ALSA: buffer empty"),
246 AlsaError::MixerControlNotFound { id } => {
247 write!(f, "ALSA: mixer control {} not found", id)
248 }
249 AlsaError::MixerValueOutOfRange { value, min, max } => {
250 write!(
251 f,
252 "ALSA: mixer value {} out of range [{}, {}]",
253 value, min, max
254 )
255 }
256 AlsaError::TooManyDevices => write!(f, "ALSA: maximum device limit reached"),
257 AlsaError::WrongDirection => {
258 write!(f, "ALSA: operation not supported for this stream direction")
259 }
260 AlsaError::DeviceBusy { device_id } => {
261 write!(f, "ALSA: device {} is busy", device_id)
262 }
263 }
264 }
265}
266
267#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
273pub enum PcmFormat {
274 U8,
276 #[default]
278 S16Le,
279 S32Le,
281 F32FixedPoint,
283}
284
285impl PcmFormat {
286 pub(crate) fn bytes_per_sample(self) -> u32 {
288 match self {
289 PcmFormat::U8 => 1,
290 PcmFormat::S16Le => 2,
291 PcmFormat::S32Le | PcmFormat::F32FixedPoint => 4,
292 }
293 }
294
295 pub(crate) fn bits_per_sample(self) -> u32 {
297 self.bytes_per_sample() * 8
298 }
299}
300
301#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
307pub enum PcmState {
308 #[default]
310 Open,
311 Setup,
313 Prepared,
315 Running,
317 XRun,
319 Draining,
321 Paused,
323}
324
325impl PcmState {
326 pub(crate) fn can_transition_to(self, target: PcmState) -> bool {
329 match (self, target) {
330 (PcmState::Open, PcmState::Setup) => true,
332 (PcmState::Setup, PcmState::Prepared) => true,
334 (PcmState::Prepared, PcmState::Running) => true,
336 (PcmState::Prepared, PcmState::Setup) => true,
337 (PcmState::Running, PcmState::Paused) => true,
339 (PcmState::Running, PcmState::Draining) => true,
340 (PcmState::Running, PcmState::XRun) => true,
341 (PcmState::Running, PcmState::Prepared) => true,
342 (PcmState::Paused, PcmState::Running) => true,
344 (PcmState::Paused, PcmState::Prepared) => true,
345 (PcmState::XRun, PcmState::Prepared) => true,
347 (PcmState::Draining, PcmState::Prepared) => true,
349 _ => false,
351 }
352 }
353}
354
355#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
361pub enum StreamDirection {
362 #[default]
364 Playback,
365 Capture,
367}
368
369#[derive(Debug, Clone, Copy)]
375pub struct HwParams {
376 pub sample_rate: u32,
378 pub channels: u8,
380 pub format: PcmFormat,
382 pub buffer_size: u32,
384 pub period_size: u32,
386}
387
388impl HwParams {
389 pub fn new() -> Self {
391 Self {
392 sample_rate: DEFAULT_SAMPLE_RATE,
393 channels: DEFAULT_CHANNELS,
394 format: PcmFormat::S16Le,
395 buffer_size: DEFAULT_BUFFER_FRAMES,
396 period_size: DEFAULT_PERIOD_FRAMES,
397 }
398 }
399
400 pub(crate) fn validate(&self) -> Result<(), AlsaError> {
402 match self.sample_rate {
404 8000 | 11025 | 16000 | 22050 | 32000 | 44100 | 48000 | 88200 | 96000 | 176400
405 | 192000 => {}
406 rate => return Err(AlsaError::InvalidSampleRate { rate }),
407 }
408
409 if self.channels == 0 || self.channels > 8 {
411 return Err(AlsaError::InvalidChannels {
412 count: self.channels,
413 });
414 }
415
416 if self.buffer_size == 0 || self.buffer_size > 1_048_576 {
418 return Err(AlsaError::InvalidBufferSize {
419 requested: self.buffer_size,
420 max: 1_048_576,
421 });
422 }
423
424 if self.period_size == 0 || self.period_size > self.buffer_size {
426 return Err(AlsaError::InvalidPeriodSize {
427 requested: self.period_size,
428 buffer_size: self.buffer_size,
429 });
430 }
431
432 Ok(())
433 }
434
435 pub(crate) fn frame_size(&self) -> u32 {
437 self.channels as u32 * self.format.bytes_per_sample()
438 }
439
440 pub(crate) fn byte_rate(&self) -> u32 {
442 self.sample_rate.saturating_mul(self.frame_size())
443 }
444
445 pub(crate) fn buffer_bytes(&self) -> u32 {
447 self.buffer_size.saturating_mul(self.frame_size())
448 }
449
450 pub(crate) fn period_bytes(&self) -> u32 {
452 self.period_size.saturating_mul(self.frame_size())
453 }
454}
455
456impl Default for HwParams {
457 fn default() -> Self {
458 Self::new()
459 }
460}
461
462#[derive(Debug, Clone, Copy)]
468pub struct SwParams {
469 pub avail_min: u32,
471 pub start_threshold: u32,
473 pub stop_threshold: u32,
475 pub silence_threshold: u32,
477 pub silence_size: u32,
479}
480
481impl SwParams {
482 pub fn new(buffer_size: u32) -> Self {
484 Self {
485 avail_min: 1,
486 start_threshold: buffer_size,
487 stop_threshold: buffer_size,
488 silence_threshold: 0,
489 silence_size: 0,
490 }
491 }
492
493 pub(crate) fn validate(&self, hw_params: &HwParams) -> Result<(), AlsaError> {
495 if self.avail_min == 0 || self.avail_min > hw_params.buffer_size {
496 return Err(AlsaError::InvalidBufferSize {
497 requested: self.avail_min,
498 max: hw_params.buffer_size,
499 });
500 }
501 Ok(())
502 }
503}
504
505impl Default for SwParams {
506 fn default() -> Self {
507 Self::new(DEFAULT_BUFFER_FRAMES)
508 }
509}
510
511pub struct PcmDevice {
517 id: u32,
519 name: String,
521 direction: StreamDirection,
523 state: PcmState,
525 hw_params: Option<HwParams>,
527 sw_params: Option<SwParams>,
529 buffer: Vec<u8>,
531 read_pos: u32,
533 write_pos: u32,
535 frames_written: AtomicU64,
537 frames_read: AtomicU64,
539 xrun_count: AtomicU32,
541 is_open: bool,
543 device_capabilities: AudioDeviceCapabilities,
545}
546
547impl PcmDevice {
548 pub fn new(id: u32, name: &str, direction: StreamDirection) -> Self {
550 let is_playback = direction == StreamDirection::Playback;
551 let is_capture = direction == StreamDirection::Capture;
552 Self {
553 id,
554 name: String::from(name),
555 direction,
556 state: PcmState::Open,
557 hw_params: None,
558 sw_params: None,
559 buffer: Vec::new(),
560 read_pos: 0,
561 write_pos: 0,
562 frames_written: AtomicU64::new(0),
563 frames_read: AtomicU64::new(0),
564 xrun_count: AtomicU32::new(0),
565 is_open: false,
566 device_capabilities: AudioDeviceCapabilities {
567 min_sample_rate: 8000,
568 max_sample_rate: 192000,
569 min_channels: 1,
570 max_channels: 8,
571 supported_formats: vec![
572 SampleFormat::U8,
573 SampleFormat::S16Le,
574 SampleFormat::S32Le,
575 SampleFormat::F32,
576 ],
577 playback: is_playback,
578 capture: is_capture,
579 },
580 }
581 }
582
583 pub(crate) fn open(&mut self) -> Result<(), AlsaError> {
585 if self.is_open {
586 return Err(AlsaError::DeviceAlreadyOpen { device_id: self.id });
587 }
588 self.is_open = true;
589 self.state = PcmState::Open;
590 Ok(())
591 }
592
593 pub(crate) fn close(&mut self) -> Result<(), AlsaError> {
595 if !self.is_open {
596 return Err(AlsaError::DeviceNotOpen { device_id: self.id });
597 }
598 self.is_open = false;
599 self.state = PcmState::Open;
600 self.hw_params = None;
601 self.sw_params = None;
602 self.buffer.clear();
603 self.read_pos = 0;
604 self.write_pos = 0;
605 self.frames_written.store(0, Ordering::Relaxed);
606 self.frames_read.store(0, Ordering::Relaxed);
607 Ok(())
608 }
609
610 pub(crate) fn set_hw_params(&mut self, params: HwParams) -> Result<(), AlsaError> {
612 if !self.is_open {
613 return Err(AlsaError::DeviceNotOpen { device_id: self.id });
614 }
615 params.validate()?;
616 self.hw_params = Some(params);
617 self.transition_state(PcmState::Setup)?;
618 Ok(())
619 }
620
621 pub(crate) fn get_hw_params(&self) -> Result<&HwParams, AlsaError> {
623 self.hw_params.as_ref().ok_or(AlsaError::HwParamsNotSet)
624 }
625
626 pub(crate) fn set_sw_params(&mut self, params: SwParams) -> Result<(), AlsaError> {
628 let hw = self.hw_params.as_ref().ok_or(AlsaError::HwParamsNotSet)?;
629 params.validate(hw)?;
630 self.sw_params = Some(params);
631 Ok(())
632 }
633
634 pub(crate) fn get_sw_params(&self) -> Result<&SwParams, AlsaError> {
636 self.sw_params.as_ref().ok_or(AlsaError::SwParamsNotSet)
637 }
638
639 pub(crate) fn prepare(&mut self) -> Result<(), AlsaError> {
641 let hw = self.hw_params.ok_or(AlsaError::HwParamsNotSet)?;
642
643 let buf_bytes = hw.buffer_bytes() as usize;
645 self.buffer = vec![0u8; buf_bytes];
646 self.read_pos = 0;
647 self.write_pos = 0;
648 self.frames_written.store(0, Ordering::Relaxed);
649 self.frames_read.store(0, Ordering::Relaxed);
650
651 if self.sw_params.is_none() {
653 self.sw_params = Some(SwParams::new(hw.buffer_size));
654 }
655
656 self.transition_state(PcmState::Prepared)?;
657 Ok(())
658 }
659
660 pub(crate) fn start(&mut self) -> Result<(), AlsaError> {
662 self.transition_state(PcmState::Running)
663 }
664
665 pub(crate) fn stop(&mut self) -> Result<(), AlsaError> {
667 if self.state == PcmState::Running || self.state == PcmState::Paused {
668 self.state = PcmState::Prepared;
669 Ok(())
670 } else {
671 Err(AlsaError::InvalidStateTransition {
672 current: self.state,
673 requested: PcmState::Prepared,
674 })
675 }
676 }
677
678 pub(crate) fn pause(&mut self) -> Result<(), AlsaError> {
680 self.transition_state(PcmState::Paused)
681 }
682
683 pub(crate) fn resume(&mut self) -> Result<(), AlsaError> {
685 if self.state != PcmState::Paused {
686 return Err(AlsaError::InvalidStateTransition {
687 current: self.state,
688 requested: PcmState::Running,
689 });
690 }
691 self.state = PcmState::Running;
692 Ok(())
693 }
694
695 pub(crate) fn drain(&mut self) -> Result<(), AlsaError> {
697 if self.state == PcmState::Running {
698 self.state = PcmState::Draining;
699 self.state = PcmState::Prepared;
702 Ok(())
703 } else {
704 Err(AlsaError::InvalidStateTransition {
705 current: self.state,
706 requested: PcmState::Draining,
707 })
708 }
709 }
710
711 pub(crate) fn recover_xrun(&mut self) -> Result<(), AlsaError> {
713 if self.state != PcmState::XRun {
714 return Err(AlsaError::InvalidStateTransition {
715 current: self.state,
716 requested: PcmState::Prepared,
717 });
718 }
719 self.read_pos = 0;
720 self.write_pos = 0;
721 self.state = PcmState::Prepared;
722 Ok(())
723 }
724
725 pub(crate) fn write(&mut self, data: &[u8]) -> Result<u32, AlsaError> {
729 if self.direction != StreamDirection::Playback {
730 return Err(AlsaError::WrongDirection);
731 }
732 if self.state != PcmState::Running && self.state != PcmState::Prepared {
733 return Err(AlsaError::InvalidStateTransition {
734 current: self.state,
735 requested: PcmState::Running,
736 });
737 }
738
739 let hw = self.hw_params.ok_or(AlsaError::HwParamsNotSet)?;
740 let frame_size = hw.frame_size();
741 if frame_size == 0 {
742 return Ok(0);
743 }
744 let buf_bytes = self.buffer.len() as u32;
745 if buf_bytes == 0 {
746 return Err(AlsaError::BufferFull);
747 }
748
749 let avail = self.available_write_bytes(buf_bytes);
750 let to_write = (data.len() as u32).min(avail);
751 let to_write = (to_write / frame_size) * frame_size;
753
754 if to_write == 0 {
755 return Err(AlsaError::BufferFull);
756 }
757
758 let wp = self.write_pos as usize;
759 let cap = buf_bytes as usize;
760 let tw = to_write as usize;
761
762 let first_chunk = (cap - wp).min(tw);
763 let second_chunk = tw - first_chunk;
764
765 self.buffer[wp..wp + first_chunk].copy_from_slice(&data[..first_chunk]);
766 if second_chunk > 0 {
767 self.buffer[..second_chunk]
768 .copy_from_slice(&data[first_chunk..first_chunk + second_chunk]);
769 }
770
771 self.write_pos = ((wp + tw) % cap) as u32;
772 let frames = to_write / frame_size;
773 self.frames_written
774 .fetch_add(frames as u64, Ordering::Relaxed);
775
776 Ok(frames)
777 }
778
779 pub(crate) fn read(&mut self, output: &mut [u8]) -> Result<u32, AlsaError> {
783 if self.direction != StreamDirection::Capture {
784 return Err(AlsaError::WrongDirection);
785 }
786 if self.state != PcmState::Running {
787 return Err(AlsaError::InvalidStateTransition {
788 current: self.state,
789 requested: PcmState::Running,
790 });
791 }
792
793 let hw = self.hw_params.ok_or(AlsaError::HwParamsNotSet)?;
794 let frame_size = hw.frame_size();
795 if frame_size == 0 {
796 return Ok(0);
797 }
798 let buf_bytes = self.buffer.len() as u32;
799 if buf_bytes == 0 {
800 return Err(AlsaError::BufferEmpty);
801 }
802
803 let avail = self.available_read_bytes(buf_bytes);
804 let to_read = (output.len() as u32).min(avail);
805 let to_read = (to_read / frame_size) * frame_size;
806
807 if to_read == 0 {
808 return Err(AlsaError::BufferEmpty);
809 }
810
811 let rp = self.read_pos as usize;
812 let cap = buf_bytes as usize;
813 let tr = to_read as usize;
814
815 let first_chunk = (cap - rp).min(tr);
816 let second_chunk = tr - first_chunk;
817
818 output[..first_chunk].copy_from_slice(&self.buffer[rp..rp + first_chunk]);
819 if second_chunk > 0 {
820 output[first_chunk..first_chunk + second_chunk]
821 .copy_from_slice(&self.buffer[..second_chunk]);
822 }
823
824 self.read_pos = ((rp + tr) % cap) as u32;
825 let frames = to_read / frame_size;
826 self.frames_read.fetch_add(frames as u64, Ordering::Relaxed);
827
828 Ok(frames)
829 }
830
831 pub(crate) fn avail_update(&self) -> Result<u32, AlsaError> {
833 let hw = self.hw_params.as_ref().ok_or(AlsaError::HwParamsNotSet)?;
834 let frame_size = hw.frame_size();
835 if frame_size == 0 {
836 return Ok(0);
837 }
838 let buf_bytes = self.buffer.len() as u32;
839 match self.direction {
840 StreamDirection::Playback => Ok(self.available_write_bytes(buf_bytes) / frame_size),
841 StreamDirection::Capture => Ok(self.available_read_bytes(buf_bytes) / frame_size),
842 }
843 }
844
845 pub(crate) fn state(&self) -> PcmState {
847 self.state
848 }
849
850 pub(crate) fn id(&self) -> u32 {
852 self.id
853 }
854
855 pub(crate) fn name(&self) -> &str {
857 &self.name
858 }
859
860 pub(crate) fn direction(&self) -> StreamDirection {
862 self.direction
863 }
864
865 pub(crate) fn total_frames_written(&self) -> u64 {
867 self.frames_written.load(Ordering::Relaxed)
868 }
869
870 pub(crate) fn total_frames_read(&self) -> u64 {
872 self.frames_read.load(Ordering::Relaxed)
873 }
874
875 pub(crate) fn xrun_count(&self) -> u32 {
877 self.xrun_count.load(Ordering::Relaxed)
878 }
879
880 pub(crate) fn is_open(&self) -> bool {
882 self.is_open
883 }
884
885 pub(crate) fn mmap_begin(&self) -> Result<(&[u8], u32, u32), AlsaError> {
890 if self.state != PcmState::Prepared && self.state != PcmState::Running {
891 return Err(AlsaError::InvalidStateTransition {
892 current: self.state,
893 requested: PcmState::Running,
894 });
895 }
896 let hw = self.hw_params.as_ref().ok_or(AlsaError::HwParamsNotSet)?;
897 let frame_size = hw.frame_size();
898 let buf_bytes = self.buffer.len() as u32;
899 let avail = match self.direction {
900 StreamDirection::Playback => self.available_write_bytes(buf_bytes) / frame_size,
901 StreamDirection::Capture => self.available_read_bytes(buf_bytes) / frame_size,
902 };
903 let offset = match self.direction {
904 StreamDirection::Playback => self.write_pos / frame_size,
905 StreamDirection::Capture => self.read_pos / frame_size,
906 };
907 Ok((&self.buffer, offset, avail))
908 }
909
910 pub(crate) fn mmap_commit(&mut self, frames: u32) -> Result<(), AlsaError> {
912 let hw = self.hw_params.ok_or(AlsaError::HwParamsNotSet)?;
913 let frame_size = hw.frame_size();
914 let bytes = frames.saturating_mul(frame_size);
915 let cap = self.buffer.len() as u32;
916 if cap == 0 {
917 return Ok(());
918 }
919
920 match self.direction {
921 StreamDirection::Playback => {
922 self.write_pos = (self.write_pos + bytes) % cap;
923 self.frames_written
924 .fetch_add(frames as u64, Ordering::Relaxed);
925 }
926 StreamDirection::Capture => {
927 self.read_pos = (self.read_pos + bytes) % cap;
928 self.frames_read.fetch_add(frames as u64, Ordering::Relaxed);
929 }
930 }
931 Ok(())
932 }
933
934 fn transition_state(&mut self, target: PcmState) -> Result<(), AlsaError> {
938 if self.state.can_transition_to(target) {
939 self.state = target;
940 Ok(())
941 } else {
942 Err(AlsaError::InvalidStateTransition {
943 current: self.state,
944 requested: target,
945 })
946 }
947 }
948
949 fn available_write_bytes(&self, capacity: u32) -> u32 {
951 if capacity == 0 {
952 return 0;
953 }
954 let used = if self.write_pos >= self.read_pos {
955 self.write_pos - self.read_pos
956 } else {
957 capacity - self.read_pos + self.write_pos
958 };
959 capacity.saturating_sub(used).saturating_sub(1)
960 }
961
962 fn available_read_bytes(&self, capacity: u32) -> u32 {
964 if capacity == 0 {
965 return 0;
966 }
967 if self.write_pos >= self.read_pos {
968 self.write_pos - self.read_pos
969 } else {
970 capacity - self.read_pos + self.write_pos
971 }
972 }
973
974 fn push_capture_data(&mut self, data: &[u8]) -> Result<u32, AlsaError> {
976 let hw = self.hw_params.ok_or(AlsaError::HwParamsNotSet)?;
977 let frame_size = hw.frame_size();
978 if frame_size == 0 {
979 return Ok(0);
980 }
981 let buf_bytes = self.buffer.len() as u32;
982 let avail = self.available_write_bytes(buf_bytes);
983 let to_write = (data.len() as u32).min(avail);
984 let to_write = (to_write / frame_size) * frame_size;
985
986 if to_write == 0 && !data.is_empty() {
987 let lost = data.len() as u32 / frame_size;
989 self.xrun_count.fetch_add(1, Ordering::Relaxed);
990 self.state = PcmState::XRun;
991 return Err(AlsaError::Overrun { lost_frames: lost });
992 }
993
994 let wp = self.write_pos as usize;
995 let cap = buf_bytes as usize;
996 let tw = to_write as usize;
997
998 let first_chunk = (cap - wp).min(tw);
999 let second_chunk = tw - first_chunk;
1000
1001 self.buffer[wp..wp + first_chunk].copy_from_slice(&data[..first_chunk]);
1002 if second_chunk > 0 {
1003 self.buffer[..second_chunk]
1004 .copy_from_slice(&data[first_chunk..first_chunk + second_chunk]);
1005 }
1006
1007 self.write_pos = ((wp + tw) % cap) as u32;
1008 Ok(to_write / frame_size)
1009 }
1010}
1011
1012impl From<AlsaError> for AudioError {
1017 fn from(err: AlsaError) -> Self {
1018 match err {
1019 AlsaError::DeviceNotFound { .. } => AudioError::DeviceNotFound,
1020 AlsaError::DeviceAlreadyOpen { .. } | AlsaError::DeviceBusy { .. } => {
1021 AudioError::DeviceBusy
1022 }
1023 AlsaError::DeviceNotOpen { .. }
1024 | AlsaError::HwParamsNotSet
1025 | AlsaError::SwParamsNotSet => AudioError::InvalidConfig {
1026 reason: "device not configured",
1027 },
1028 AlsaError::InvalidStateTransition { current, .. } => {
1029 if current == PcmState::Running {
1030 AudioError::AlreadyStarted
1031 } else {
1032 AudioError::NotStarted
1033 }
1034 }
1035 AlsaError::Overrun { .. } => AudioError::BufferOverrun,
1036 AlsaError::Underrun { .. } => AudioError::BufferUnderrun,
1037 AlsaError::InvalidFormat => AudioError::UnsupportedFormat,
1038 AlsaError::InvalidSampleRate { .. } => AudioError::InvalidConfig {
1039 reason: "unsupported sample rate",
1040 },
1041 AlsaError::InvalidChannels { .. } => AudioError::InvalidConfig {
1042 reason: "unsupported channel count",
1043 },
1044 AlsaError::InvalidBufferSize { .. } => AudioError::InvalidConfig {
1045 reason: "invalid buffer size",
1046 },
1047 AlsaError::InvalidPeriodSize { .. } => AudioError::InvalidConfig {
1048 reason: "invalid period size",
1049 },
1050 AlsaError::BufferFull => AudioError::BufferUnderrun,
1051 AlsaError::BufferEmpty => AudioError::BufferOverrun,
1052 AlsaError::MixerControlNotFound { .. } | AlsaError::MixerValueOutOfRange { .. } => {
1053 AudioError::InvalidConfig {
1054 reason: "mixer control error",
1055 }
1056 }
1057 AlsaError::TooManyDevices => AudioError::DeviceBusy,
1058 AlsaError::WrongDirection => AudioError::InvalidConfig {
1059 reason: "wrong stream direction",
1060 },
1061 }
1062 }
1063}
1064
1065fn pcm_format_to_sample_format(fmt: PcmFormat) -> SampleFormat {
1071 match fmt {
1072 PcmFormat::U8 => SampleFormat::U8,
1073 PcmFormat::S16Le => SampleFormat::S16Le,
1074 PcmFormat::S32Le => SampleFormat::S32Le,
1075 PcmFormat::F32FixedPoint => SampleFormat::F32,
1076 }
1077}
1078
1079fn sample_format_to_pcm_format(fmt: SampleFormat) -> Result<PcmFormat, AudioError> {
1081 match fmt {
1082 SampleFormat::U8 => Ok(PcmFormat::U8),
1083 SampleFormat::S16Le => Ok(PcmFormat::S16Le),
1084 SampleFormat::S32Le => Ok(PcmFormat::S32Le),
1085 SampleFormat::F32 => Ok(PcmFormat::F32FixedPoint),
1086 SampleFormat::S16Be | SampleFormat::S24Le => Err(AudioError::UnsupportedFormat),
1088 }
1089}
1090
1091impl AudioDevice for PcmDevice {
1092 fn configure(&mut self, config: &AudioConfig) -> Result<AudioConfig, AudioError> {
1093 if !self.is_open {
1095 self.open().map_err(AudioError::from)?;
1096 }
1097
1098 let pcm_format = sample_format_to_pcm_format(config.format)?;
1099
1100 let hw_params = HwParams {
1101 sample_rate: config.sample_rate,
1102 channels: config.channels,
1103 format: pcm_format,
1104 buffer_size: config.buffer_frames,
1105 period_size: config.buffer_frames / 4,
1106 };
1107
1108 self.set_hw_params(hw_params).map_err(AudioError::from)?;
1109 self.prepare().map_err(AudioError::from)?;
1110
1111 Ok(AudioConfig {
1113 sample_rate: hw_params.sample_rate,
1114 channels: hw_params.channels,
1115 format: pcm_format_to_sample_format(hw_params.format),
1116 buffer_frames: hw_params.buffer_size,
1117 })
1118 }
1119
1120 fn start(&mut self) -> Result<(), AudioError> {
1121 PcmDevice::start(self).map_err(AudioError::from)
1122 }
1123
1124 fn stop(&mut self) -> Result<(), AudioError> {
1125 PcmDevice::stop(self).map_err(AudioError::from)
1126 }
1127
1128 fn write_frames(&mut self, data: &[u8]) -> Result<usize, AudioError> {
1129 self.write(data)
1130 .map(|frames| frames as usize)
1131 .map_err(AudioError::from)
1132 }
1133
1134 fn read_frames(&mut self, output: &mut [u8]) -> Result<usize, AudioError> {
1135 self.read(output)
1136 .map(|frames| frames as usize)
1137 .map_err(AudioError::from)
1138 }
1139
1140 fn capabilities(&self) -> &AudioDeviceCapabilities {
1141 &self.device_capabilities
1142 }
1143
1144 fn name(&self) -> &str {
1145 &self.name
1146 }
1147
1148 fn is_playback(&self) -> bool {
1149 self.direction == StreamDirection::Playback
1150 }
1151
1152 fn is_capture(&self) -> bool {
1153 self.direction == StreamDirection::Capture
1154 }
1155}
1156
1157#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1163pub enum MixerControlType {
1164 Integer,
1166 Boolean,
1168 Enumerated,
1170}
1171
1172pub struct MixerControl {
1174 pub id: u32,
1176 pub name: String,
1178 pub control_type: MixerControlType,
1180 value: AtomicU32,
1182 pub min: i32,
1184 pub max: i32,
1186 pub enum_count: u32,
1188 pub enum_names: Vec<String>,
1190}
1191
1192impl MixerControl {
1193 pub(crate) fn new_integer(id: u32, name: &str, min: i32, max: i32, initial: i32) -> Self {
1195 Self {
1196 id,
1197 name: String::from(name),
1198 control_type: MixerControlType::Integer,
1199 value: AtomicU32::new(initial as u32),
1200 min,
1201 max,
1202 enum_count: 0,
1203 enum_names: Vec::new(),
1204 }
1205 }
1206
1207 pub(crate) fn new_boolean(id: u32, name: &str, initial: bool) -> Self {
1209 Self {
1210 id,
1211 name: String::from(name),
1212 control_type: MixerControlType::Boolean,
1213 value: AtomicU32::new(if initial { 1 } else { 0 }),
1214 min: 0,
1215 max: 1,
1216 enum_count: 0,
1217 enum_names: Vec::new(),
1218 }
1219 }
1220
1221 pub(crate) fn new_enumerated(id: u32, name: &str, items: Vec<String>, initial: u32) -> Self {
1223 let count = items.len() as u32;
1224 Self {
1225 id,
1226 name: String::from(name),
1227 control_type: MixerControlType::Enumerated,
1228 value: AtomicU32::new(initial),
1229 min: 0,
1230 max: count.saturating_sub(1) as i32,
1231 enum_count: count,
1232 enum_names: items,
1233 }
1234 }
1235
1236 pub(crate) fn get_value(&self) -> i32 {
1238 self.value.load(Ordering::Relaxed) as i32
1239 }
1240
1241 pub(crate) fn set_value(&self, val: i32) -> Result<(), AlsaError> {
1243 if val < self.min || val > self.max {
1244 return Err(AlsaError::MixerValueOutOfRange {
1245 value: val,
1246 min: self.min,
1247 max: self.max,
1248 });
1249 }
1250 self.value.store(val as u32, Ordering::Relaxed);
1251 Ok(())
1252 }
1253
1254 pub(crate) fn get_bool(&self) -> bool {
1256 self.value.load(Ordering::Relaxed) != 0
1257 }
1258
1259 pub(crate) fn set_bool(&self, val: bool) {
1261 self.value.store(if val { 1 } else { 0 }, Ordering::Relaxed);
1262 }
1263
1264 pub(crate) fn get_enum_name(&self) -> Option<&str> {
1266 let idx = self.value.load(Ordering::Relaxed) as usize;
1267 self.enum_names.get(idx).map(|s| s.as_str())
1268 }
1269}
1270
1271pub const MIXER_MASTER_VOLUME: u32 = 1;
1277pub const MIXER_PCM_VOLUME: u32 = 2;
1279pub const MIXER_CAPTURE_VOLUME: u32 = 3;
1281pub const MIXER_MASTER_SWITCH: u32 = 4;
1283pub const MIXER_CAPTURE_SWITCH: u32 = 5;
1285
1286pub struct AlsaMixer {
1288 controls: BTreeMap<u32, MixerControl>,
1290 next_id: u32,
1292}
1293
1294impl AlsaMixer {
1295 pub fn new() -> Self {
1297 let mut mixer = Self {
1298 controls: BTreeMap::new(),
1299 next_id: 10, };
1301
1302 mixer.controls.insert(
1304 MIXER_MASTER_VOLUME,
1305 MixerControl::new_integer(MIXER_MASTER_VOLUME, "Master Playback Volume", 0, 100, 80),
1306 );
1307 mixer.controls.insert(
1308 MIXER_PCM_VOLUME,
1309 MixerControl::new_integer(MIXER_PCM_VOLUME, "PCM Playback Volume", 0, 100, 100),
1310 );
1311 mixer.controls.insert(
1312 MIXER_CAPTURE_VOLUME,
1313 MixerControl::new_integer(MIXER_CAPTURE_VOLUME, "Capture Volume", 0, 100, 80),
1314 );
1315 mixer.controls.insert(
1316 MIXER_MASTER_SWITCH,
1317 MixerControl::new_boolean(MIXER_MASTER_SWITCH, "Master Playback Switch", true),
1318 );
1319 mixer.controls.insert(
1320 MIXER_CAPTURE_SWITCH,
1321 MixerControl::new_boolean(MIXER_CAPTURE_SWITCH, "Capture Switch", true),
1322 );
1323
1324 mixer
1325 }
1326
1327 pub(crate) fn add_control(&mut self, control: MixerControl) -> u32 {
1329 let id = control.id;
1330 self.controls.insert(id, control);
1331 id
1332 }
1333
1334 pub(crate) fn alloc_control_id(&mut self) -> u32 {
1336 let id = self.next_id;
1337 self.next_id = self.next_id.wrapping_add(1);
1338 id
1339 }
1340
1341 pub(crate) fn get_control(&self, id: u32) -> Result<&MixerControl, AlsaError> {
1343 self.controls
1344 .get(&id)
1345 .ok_or(AlsaError::MixerControlNotFound { id })
1346 }
1347
1348 pub(crate) fn set_volume(&self, control_id: u32, volume: i32) -> Result<(), AlsaError> {
1350 let control = self
1351 .controls
1352 .get(&control_id)
1353 .ok_or(AlsaError::MixerControlNotFound { id: control_id })?;
1354 control.set_value(volume)
1355 }
1356
1357 pub(crate) fn get_volume(&self, control_id: u32) -> Result<i32, AlsaError> {
1359 let control = self
1360 .controls
1361 .get(&control_id)
1362 .ok_or(AlsaError::MixerControlNotFound { id: control_id })?;
1363 Ok(control.get_value())
1364 }
1365
1366 pub(crate) fn control_count(&self) -> usize {
1368 self.controls.len()
1369 }
1370
1371 pub(crate) fn list_control_ids(&self) -> Vec<u32> {
1373 self.controls.keys().copied().collect()
1374 }
1375}
1376
1377impl Default for AlsaMixer {
1378 fn default() -> Self {
1379 Self::new()
1380 }
1381}
1382
1383#[inline]
1392pub(crate) fn convert_u8_to_s16(sample: u8) -> i16 {
1393 ((sample as i16) - 128) * 256
1395}
1396
1397#[inline]
1399pub(crate) fn convert_s16_to_u8(sample: i16) -> u8 {
1400 ((sample / 256) + 128) as u8
1402}
1403
1404#[inline]
1406pub(crate) fn convert_s16_to_s32(sample: i16) -> i32 {
1407 (sample as i32) << 16
1408}
1409
1410#[inline]
1412pub(crate) fn convert_s32_to_s16(sample: i32) -> i16 {
1413 let shifted = sample >> 16;
1414 if shifted > i16::MAX as i32 {
1415 i16::MAX
1416 } else if shifted < i16::MIN as i32 {
1417 i16::MIN
1418 } else {
1419 shifted as i16
1420 }
1421}
1422
1423#[inline]
1425pub(crate) fn convert_u8_to_s32(sample: u8) -> i32 {
1426 convert_s16_to_s32(convert_u8_to_s16(sample))
1427}
1428
1429#[inline]
1431pub(crate) fn convert_s32_to_u8(sample: i32) -> u8 {
1432 convert_s16_to_u8(convert_s32_to_s16(sample))
1433}
1434
1435pub(crate) fn convert_buffer(
1437 input: &[u8],
1438 src_format: PcmFormat,
1439 dst_format: PcmFormat,
1440 output: &mut Vec<u8>,
1441) {
1442 output.clear();
1443
1444 if src_format == dst_format {
1445 output.extend_from_slice(input);
1446 return;
1447 }
1448
1449 match (src_format, dst_format) {
1450 (PcmFormat::U8, PcmFormat::S16Le) => {
1451 output.reserve(input.len() * 2);
1452 for &sample in input {
1453 let converted = convert_u8_to_s16(sample);
1454 output.extend_from_slice(&converted.to_le_bytes());
1455 }
1456 }
1457 (PcmFormat::S16Le, PcmFormat::U8) => {
1458 output.reserve(input.len() / 2);
1459 for chunk in input.chunks_exact(2) {
1460 let sample = i16::from_le_bytes([chunk[0], chunk[1]]);
1461 output.push(convert_s16_to_u8(sample));
1462 }
1463 }
1464 (PcmFormat::S16Le, PcmFormat::S32Le) => {
1465 output.reserve(input.len() * 2);
1466 for chunk in input.chunks_exact(2) {
1467 let sample = i16::from_le_bytes([chunk[0], chunk[1]]);
1468 let converted = convert_s16_to_s32(sample);
1469 output.extend_from_slice(&converted.to_le_bytes());
1470 }
1471 }
1472 (PcmFormat::S32Le, PcmFormat::S16Le) => {
1473 output.reserve(input.len() / 2);
1474 for chunk in input.chunks_exact(4) {
1475 let sample = i32::from_le_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]);
1476 let converted = convert_s32_to_s16(sample);
1477 output.extend_from_slice(&converted.to_le_bytes());
1478 }
1479 }
1480 (PcmFormat::U8, PcmFormat::S32Le) => {
1481 output.reserve(input.len() * 4);
1482 for &sample in input {
1483 let converted = convert_u8_to_s32(sample);
1484 output.extend_from_slice(&converted.to_le_bytes());
1485 }
1486 }
1487 (PcmFormat::S32Le, PcmFormat::U8) => {
1488 output.reserve(input.len() / 4);
1489 for chunk in input.chunks_exact(4) {
1490 let sample = i32::from_le_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]);
1491 output.push(convert_s32_to_u8(sample));
1492 }
1493 }
1494 (PcmFormat::F32FixedPoint, dst) => {
1496 convert_buffer(input, PcmFormat::S32Le, dst, output);
1497 }
1498 (src, PcmFormat::F32FixedPoint) => {
1499 convert_buffer(input, src, PcmFormat::S32Le, output);
1500 }
1501 _ => {
1503 output.extend_from_slice(input);
1504 }
1505 }
1506}
1507
1508#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1517pub struct GainFactor(pub i32);
1518
1519impl GainFactor {
1520 pub const UNITY: GainFactor = GainFactor(FP_ONE);
1522
1523 pub const MUTE: GainFactor = GainFactor(0);
1525
1526 #[inline]
1528 pub(crate) fn apply_s16(self, sample: i16) -> i16 {
1529 let wide = (sample as i32 as i64) * (self.0 as i64);
1530 let result = wide >> FP_SHIFT;
1531 if result > i16::MAX as i64 {
1532 i16::MAX
1533 } else if result < i16::MIN as i64 {
1534 i16::MIN
1535 } else {
1536 result as i16
1537 }
1538 }
1539
1540 #[inline]
1542 pub(crate) fn apply_s32(self, sample: i32) -> i32 {
1543 let wide = (sample as i64) * (self.0 as i64);
1544 let result = wide >> FP_SHIFT;
1545 if result > i32::MAX as i64 {
1546 i32::MAX
1547 } else if result < i32::MIN as i64 {
1548 i32::MIN
1549 } else {
1550 result as i32
1551 }
1552 }
1553
1554 pub(crate) fn raw(self) -> i32 {
1556 self.0
1557 }
1558}
1559
1560pub(crate) fn gain_from_db_tenths(db_tenths: i32) -> GainFactor {
1567 if db_tenths <= -600 {
1569 return GainFactor::MUTE;
1570 }
1571
1572 static DB_TABLE: [i32; 21] = [
1596 65, 92, 131, 185, 261, 369, 520, 735, 1038, 1467, 2073, 4135, 8250, 16462, 32845, FP_ONE, 130762, 260921, 520570, 655360, 655360, ];
1618
1619 struct DbGainEntry {
1622 db_tenths: i32,
1623 gain: i32,
1624 }
1625
1626 static ENTRIES: [DbGainEntry; 13] = [
1627 DbGainEntry {
1628 db_tenths: -600,
1629 gain: 65,
1630 },
1631 DbGainEntry {
1632 db_tenths: -540,
1633 gain: 131,
1634 },
1635 DbGainEntry {
1636 db_tenths: -480,
1637 gain: 261,
1638 },
1639 DbGainEntry {
1640 db_tenths: -420,
1641 gain: 520,
1642 },
1643 DbGainEntry {
1644 db_tenths: -360,
1645 gain: 1038,
1646 },
1647 DbGainEntry {
1648 db_tenths: -300,
1649 gain: 2073,
1650 },
1651 DbGainEntry {
1652 db_tenths: -240,
1653 gain: 4135,
1654 },
1655 DbGainEntry {
1656 db_tenths: -180,
1657 gain: 8250,
1658 },
1659 DbGainEntry {
1660 db_tenths: -120,
1661 gain: 16462,
1662 },
1663 DbGainEntry {
1664 db_tenths: -60,
1665 gain: 32845,
1666 },
1667 DbGainEntry {
1668 db_tenths: 0,
1669 gain: FP_ONE,
1670 },
1671 DbGainEntry {
1672 db_tenths: 120,
1673 gain: 260921,
1674 },
1675 DbGainEntry {
1676 db_tenths: 200,
1677 gain: 655360,
1678 },
1679 ];
1680
1681 let db = if db_tenths > 200 { 200 } else { db_tenths };
1683
1684 let mut i = 0;
1686 while i < ENTRIES.len() - 1 {
1687 if db <= ENTRIES[i + 1].db_tenths {
1688 break;
1689 }
1690 i += 1;
1691 }
1692 if i >= ENTRIES.len() - 1 {
1693 return GainFactor(ENTRIES[ENTRIES.len() - 1].gain);
1694 }
1695
1696 let lo = &ENTRIES[i];
1697 let hi = &ENTRIES[i + 1];
1698 let range = hi.db_tenths - lo.db_tenths;
1699 if range == 0 {
1700 return GainFactor(lo.gain);
1701 }
1702
1703 let frac_num = db - lo.db_tenths;
1706 let gain_diff = hi.gain as i64 - lo.gain as i64;
1707 let interpolated = lo.gain as i64 + (gain_diff * frac_num as i64) / range as i64;
1708
1709 GainFactor(interpolated as i32)
1710}
1711
1712pub(crate) fn gain_from_percent(percent: u32) -> GainFactor {
1716 if percent == 0 {
1717 return GainFactor::MUTE;
1718 }
1719 if percent >= 100 {
1720 return GainFactor::UNITY;
1721 }
1722
1723 let inv = (100 - percent) as i64;
1730 let db_tenths = -((600 * inv * inv) / 10000) as i32;
1731
1732 gain_from_db_tenths(db_tenths)
1733}
1734
1735#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
1741pub enum CaptureState {
1742 #[default]
1744 Idle,
1745 Recording,
1747 Paused,
1749}
1750
1751#[derive(Debug, Clone, Copy, Default)]
1753pub struct CaptureStats {
1754 pub frames_captured: u64,
1756 pub overruns: u64,
1758 pub buffer_fill: u32,
1760 pub buffer_capacity: u32,
1762}
1763
1764pub struct CaptureDevice {
1766 pub id: u32,
1768 pub name: String,
1770 state: CaptureState,
1772 hw_params: HwParams,
1774 buffer: Vec<u8>,
1776 read_pos: u32,
1778 write_pos: u32,
1780 frames_captured: AtomicU64,
1782 overruns: AtomicU64,
1784 gain: GainFactor,
1786}
1787
1788impl CaptureDevice {
1789 pub fn new(id: u32, name: &str, hw_params: HwParams) -> Self {
1791 let buf_bytes =
1792 (CAPTURE_BUFFER_FRAMES as usize).saturating_mul(hw_params.frame_size() as usize);
1793 Self {
1794 id,
1795 name: String::from(name),
1796 state: CaptureState::Idle,
1797 hw_params,
1798 buffer: vec![0u8; buf_bytes],
1799 read_pos: 0,
1800 write_pos: 0,
1801 frames_captured: AtomicU64::new(0),
1802 overruns: AtomicU64::new(0),
1803 gain: GainFactor::UNITY,
1804 }
1805 }
1806
1807 pub(crate) fn start(&mut self) -> Result<(), AlsaError> {
1809 if self.state != CaptureState::Idle && self.state != CaptureState::Paused {
1810 return Err(AlsaError::InvalidStateTransition {
1811 current: PcmState::Running,
1812 requested: PcmState::Running,
1813 });
1814 }
1815 self.state = CaptureState::Recording;
1816 Ok(())
1817 }
1818
1819 pub(crate) fn stop(&mut self) {
1821 self.state = CaptureState::Idle;
1822 self.read_pos = 0;
1823 self.write_pos = 0;
1824 }
1825
1826 pub(crate) fn pause(&mut self) {
1828 if self.state == CaptureState::Recording {
1829 self.state = CaptureState::Paused;
1830 }
1831 }
1832
1833 pub(crate) fn resume(&mut self) {
1835 if self.state == CaptureState::Paused {
1836 self.state = CaptureState::Recording;
1837 }
1838 }
1839
1840 pub(crate) fn push_data(&mut self, data: &[u8]) -> Result<u32, AlsaError> {
1845 if self.state != CaptureState::Recording {
1846 return Ok(0);
1847 }
1848
1849 let frame_size = self.hw_params.frame_size();
1850 if frame_size == 0 {
1851 return Ok(0);
1852 }
1853
1854 let cap = self.buffer.len() as u32;
1855 let avail = self.available_write(cap);
1856 let to_write = (data.len() as u32).min(avail);
1857 let to_write = (to_write / frame_size) * frame_size;
1858
1859 if to_write == 0 && !data.is_empty() {
1860 let lost = data.len() as u32 / frame_size;
1862 self.overruns.fetch_add(1, Ordering::Relaxed);
1863 return Err(AlsaError::Overrun { lost_frames: lost });
1864 }
1865
1866 if self.gain != GainFactor::UNITY && self.gain != GainFactor::MUTE {
1868 if self.hw_params.format == PcmFormat::S16Le {
1870 let tw = to_write as usize;
1871 let wp = self.write_pos as usize;
1872 let c = cap as usize;
1873
1874 let mut src_offset = 0;
1876 let mut dst_offset = wp;
1877 let mut remaining = tw;
1878
1879 while remaining >= 2 {
1880 let sample = i16::from_le_bytes([data[src_offset], data[src_offset + 1]]);
1881 let gained = self.gain.apply_s16(sample);
1882 let bytes = gained.to_le_bytes();
1883 self.buffer[dst_offset % c] = bytes[0];
1884 self.buffer[(dst_offset + 1) % c] = bytes[1];
1885 src_offset += 2;
1886 dst_offset += 2;
1887 remaining -= 2;
1888 }
1889
1890 self.write_pos = (dst_offset % c) as u32;
1891 } else {
1892 self.copy_to_ring(data, to_write);
1894 }
1895 } else if self.gain == GainFactor::MUTE {
1896 let tw = to_write as usize;
1898 let wp = self.write_pos as usize;
1899 let c = cap as usize;
1900 let first = (c - wp).min(tw);
1901 let second = tw - first;
1902 for b in &mut self.buffer[wp..wp + first] {
1903 *b = 0;
1904 }
1905 if second > 0 {
1906 for b in &mut self.buffer[..second] {
1907 *b = 0;
1908 }
1909 }
1910 self.write_pos = ((wp + tw) % c) as u32;
1911 } else {
1912 self.copy_to_ring(data, to_write);
1913 }
1914
1915 let frames = to_write / frame_size;
1916 self.frames_captured
1917 .fetch_add(frames as u64, Ordering::Relaxed);
1918 Ok(frames)
1919 }
1920
1921 pub(crate) fn read_data(&mut self, output: &mut [u8]) -> u32 {
1925 let frame_size = self.hw_params.frame_size();
1926 if frame_size == 0 {
1927 return 0;
1928 }
1929
1930 let cap = self.buffer.len() as u32;
1931 let avail = self.available_read(cap);
1932 let to_read = (output.len() as u32).min(avail);
1933 let to_read = (to_read / frame_size) * frame_size;
1934
1935 if to_read == 0 {
1936 return 0;
1937 }
1938
1939 let rp = self.read_pos as usize;
1940 let c = cap as usize;
1941 let tr = to_read as usize;
1942
1943 let first = (c - rp).min(tr);
1944 let second = tr - first;
1945
1946 output[..first].copy_from_slice(&self.buffer[rp..rp + first]);
1947 if second > 0 {
1948 output[first..first + second].copy_from_slice(&self.buffer[..second]);
1949 }
1950
1951 self.read_pos = ((rp + tr) % c) as u32;
1952 to_read / frame_size
1953 }
1954
1955 pub(crate) fn stats(&self) -> CaptureStats {
1957 let cap = self.buffer.len() as u32;
1958 let frame_size = self.hw_params.frame_size();
1959 let fill_bytes = self.available_read(cap);
1960 let fill_frames = if frame_size > 0 {
1961 fill_bytes / frame_size
1962 } else {
1963 0
1964 };
1965 let cap_frames = if frame_size > 0 { cap / frame_size } else { 0 };
1966 CaptureStats {
1967 frames_captured: self.frames_captured.load(Ordering::Relaxed),
1968 overruns: self.overruns.load(Ordering::Relaxed),
1969 buffer_fill: fill_frames,
1970 buffer_capacity: cap_frames,
1971 }
1972 }
1973
1974 pub(crate) fn state(&self) -> CaptureState {
1976 self.state
1977 }
1978
1979 pub(crate) fn set_gain(&mut self, gain: GainFactor) {
1981 self.gain = gain;
1982 }
1983
1984 pub(crate) fn gain(&self) -> GainFactor {
1986 self.gain
1987 }
1988
1989 pub(crate) fn hw_params(&self) -> &HwParams {
1991 &self.hw_params
1992 }
1993
1994 fn available_write(&self, capacity: u32) -> u32 {
1997 if capacity == 0 {
1998 return 0;
1999 }
2000 let used = if self.write_pos >= self.read_pos {
2001 self.write_pos - self.read_pos
2002 } else {
2003 capacity - self.read_pos + self.write_pos
2004 };
2005 capacity.saturating_sub(used).saturating_sub(1)
2006 }
2007
2008 fn available_read(&self, capacity: u32) -> u32 {
2009 if capacity == 0 {
2010 return 0;
2011 }
2012 if self.write_pos >= self.read_pos {
2013 self.write_pos - self.read_pos
2014 } else {
2015 capacity - self.read_pos + self.write_pos
2016 }
2017 }
2018
2019 fn copy_to_ring(&mut self, data: &[u8], to_write: u32) {
2020 let wp = self.write_pos as usize;
2021 let c = self.buffer.len();
2022 let tw = to_write as usize;
2023 let first = (c - wp).min(tw);
2024 let second = tw - first;
2025 self.buffer[wp..wp + first].copy_from_slice(&data[..first]);
2026 if second > 0 {
2027 self.buffer[..second].copy_from_slice(&data[first..first + second]);
2028 }
2029 self.write_pos = ((wp + tw) % c) as u32;
2030 }
2031}
2032
2033#[derive(Debug, Clone)]
2039pub struct DeviceInfo {
2040 pub id: u32,
2042 pub name: String,
2044 pub playback: bool,
2046 pub capture: bool,
2048 pub max_sample_rate: u32,
2050 pub max_channels: u8,
2052 pub formats: Vec<PcmFormat>,
2054}
2055
2056pub struct DeviceRegistry {
2058 devices: BTreeMap<u32, DeviceInfo>,
2060 next_id: AtomicU32,
2062}
2063
2064impl DeviceRegistry {
2065 pub fn new() -> Self {
2067 Self {
2068 devices: BTreeMap::new(),
2069 next_id: AtomicU32::new(1),
2070 }
2071 }
2072
2073 pub(crate) fn register(&mut self, info: DeviceInfo) -> Result<u32, AlsaError> {
2075 if self.devices.len() >= MAX_PCM_DEVICES {
2076 return Err(AlsaError::TooManyDevices);
2077 }
2078 let id = info.id;
2079 self.devices.insert(id, info);
2080 Ok(id)
2081 }
2082
2083 pub(crate) fn unregister(&mut self, id: u32) -> Result<(), AlsaError> {
2085 self.devices
2086 .remove(&id)
2087 .map(|_| ())
2088 .ok_or(AlsaError::DeviceNotFound { device_id: id })
2089 }
2090
2091 pub(crate) fn get(&self, id: u32) -> Result<&DeviceInfo, AlsaError> {
2093 self.devices
2094 .get(&id)
2095 .ok_or(AlsaError::DeviceNotFound { device_id: id })
2096 }
2097
2098 pub(crate) fn list(&self) -> Vec<&DeviceInfo> {
2100 self.devices.values().collect()
2101 }
2102
2103 pub(crate) fn list_playback(&self) -> Vec<&DeviceInfo> {
2105 self.devices.values().filter(|d| d.playback).collect()
2106 }
2107
2108 pub(crate) fn list_capture(&self) -> Vec<&DeviceInfo> {
2110 self.devices.values().filter(|d| d.capture).collect()
2111 }
2112
2113 pub(crate) fn device_count(&self) -> usize {
2115 self.devices.len()
2116 }
2117
2118 pub(crate) fn alloc_id(&self) -> u32 {
2120 self.next_id.fetch_add(1, Ordering::Relaxed)
2121 }
2122}
2123
2124impl Default for DeviceRegistry {
2125 fn default() -> Self {
2126 Self::new()
2127 }
2128}
2129
2130static ALSA_MIXER: spin::Mutex<Option<AlsaMixer>> = spin::Mutex::new(None);
2135static DEVICE_REGISTRY: spin::Mutex<Option<DeviceRegistry>> = spin::Mutex::new(None);
2136
2137pub fn init() {
2139 {
2140 let mut mixer = ALSA_MIXER.lock();
2141 *mixer = Some(AlsaMixer::new());
2142 }
2143 {
2144 let mut registry = DEVICE_REGISTRY.lock();
2145 let mut reg = DeviceRegistry::new();
2146
2147 let _ = reg.register(DeviceInfo {
2149 id: 0,
2150 name: String::from("default"),
2151 playback: true,
2152 capture: true,
2153 max_sample_rate: 192000,
2154 max_channels: 8,
2155 formats: vec![PcmFormat::U8, PcmFormat::S16Le, PcmFormat::S32Le],
2156 });
2157
2158 *registry = Some(reg);
2159 }
2160}
2161
2162pub fn with_alsa_mixer<R, F: FnOnce(&AlsaMixer) -> R>(f: F) -> Option<R> {
2164 let guard = ALSA_MIXER.lock();
2165 guard.as_ref().map(f)
2166}
2167
2168pub fn with_alsa_mixer_mut<R, F: FnOnce(&mut AlsaMixer) -> R>(f: F) -> Option<R> {
2170 let mut guard = ALSA_MIXER.lock();
2171 guard.as_mut().map(f)
2172}
2173
2174pub fn with_registry<R, F: FnOnce(&DeviceRegistry) -> R>(f: F) -> Option<R> {
2176 let guard = DEVICE_REGISTRY.lock();
2177 guard.as_ref().map(f)
2178}
2179
2180pub fn with_registry_mut<R, F: FnOnce(&mut DeviceRegistry) -> R>(f: F) -> Option<R> {
2182 let mut guard = DEVICE_REGISTRY.lock();
2183 guard.as_mut().map(f)
2184}
2185
2186#[cfg(test)]
2191mod tests {
2192 #[allow(unused_imports)]
2193 use alloc::vec;
2194
2195 use super::*;
2196
2197 #[test]
2200 fn test_pcm_state_valid_transitions() {
2201 assert!(PcmState::Open.can_transition_to(PcmState::Setup));
2202 assert!(PcmState::Setup.can_transition_to(PcmState::Prepared));
2203 assert!(PcmState::Prepared.can_transition_to(PcmState::Running));
2204 assert!(PcmState::Running.can_transition_to(PcmState::Paused));
2205 assert!(PcmState::Running.can_transition_to(PcmState::Draining));
2206 assert!(PcmState::Running.can_transition_to(PcmState::XRun));
2207 assert!(PcmState::Paused.can_transition_to(PcmState::Running));
2208 assert!(PcmState::XRun.can_transition_to(PcmState::Prepared));
2209 assert!(PcmState::Draining.can_transition_to(PcmState::Prepared));
2210 }
2211
2212 #[test]
2213 fn test_pcm_state_invalid_transitions() {
2214 assert!(!PcmState::Open.can_transition_to(PcmState::Running));
2215 assert!(!PcmState::Open.can_transition_to(PcmState::Paused));
2216 assert!(!PcmState::Setup.can_transition_to(PcmState::Running));
2217 assert!(!PcmState::XRun.can_transition_to(PcmState::Running));
2218 assert!(!PcmState::Draining.can_transition_to(PcmState::Running));
2219 }
2220
2221 #[test]
2224 fn test_hw_params_defaults() {
2225 let params = HwParams::new();
2226 assert_eq!(params.sample_rate, DEFAULT_SAMPLE_RATE);
2227 assert_eq!(params.channels, DEFAULT_CHANNELS);
2228 assert_eq!(params.format, PcmFormat::S16Le);
2229 assert_eq!(params.buffer_size, DEFAULT_BUFFER_FRAMES);
2230 assert_eq!(params.period_size, DEFAULT_PERIOD_FRAMES);
2231 }
2232
2233 #[test]
2234 fn test_hw_params_validation_valid() {
2235 let params = HwParams::new();
2236 assert!(params.validate().is_ok());
2237 }
2238
2239 #[test]
2240 fn test_hw_params_validation_invalid_rate() {
2241 let params = HwParams {
2242 sample_rate: 12345,
2243 ..HwParams::new()
2244 };
2245 assert_eq!(
2246 params.validate(),
2247 Err(AlsaError::InvalidSampleRate { rate: 12345 })
2248 );
2249 }
2250
2251 #[test]
2252 fn test_hw_params_validation_invalid_channels() {
2253 let params = HwParams {
2254 channels: 0,
2255 ..HwParams::new()
2256 };
2257 assert_eq!(
2258 params.validate(),
2259 Err(AlsaError::InvalidChannels { count: 0 })
2260 );
2261
2262 let params2 = HwParams {
2263 channels: 9,
2264 ..HwParams::new()
2265 };
2266 assert_eq!(
2267 params2.validate(),
2268 Err(AlsaError::InvalidChannels { count: 9 })
2269 );
2270 }
2271
2272 #[test]
2273 fn test_hw_params_validation_period_exceeds_buffer() {
2274 let params = HwParams {
2275 buffer_size: 1024,
2276 period_size: 2048,
2277 ..HwParams::new()
2278 };
2279 assert_eq!(
2280 params.validate(),
2281 Err(AlsaError::InvalidPeriodSize {
2282 requested: 2048,
2283 buffer_size: 1024,
2284 })
2285 );
2286 }
2287
2288 #[test]
2289 fn test_hw_params_frame_size() {
2290 let params = HwParams {
2291 channels: 2,
2292 format: PcmFormat::S16Le,
2293 ..HwParams::new()
2294 };
2295 assert_eq!(params.frame_size(), 4); let params32 = HwParams {
2298 channels: 2,
2299 format: PcmFormat::S32Le,
2300 ..HwParams::new()
2301 };
2302 assert_eq!(params32.frame_size(), 8); }
2304
2305 #[test]
2306 fn test_hw_params_byte_rate() {
2307 let params = HwParams {
2308 sample_rate: 48000,
2309 channels: 2,
2310 format: PcmFormat::S16Le,
2311 ..HwParams::new()
2312 };
2313 assert_eq!(params.byte_rate(), 48000 * 4);
2314 }
2315
2316 #[test]
2319 fn test_pcm_device_open_close() {
2320 let mut dev = PcmDevice::new(0, "test", StreamDirection::Playback);
2321 assert!(!dev.is_open());
2322
2323 assert!(dev.open().is_ok());
2324 assert!(dev.is_open());
2325
2326 assert_eq!(
2328 dev.open(),
2329 Err(AlsaError::DeviceAlreadyOpen { device_id: 0 })
2330 );
2331
2332 assert!(dev.close().is_ok());
2333 assert!(!dev.is_open());
2334
2335 assert_eq!(dev.close(), Err(AlsaError::DeviceNotOpen { device_id: 0 }));
2337 }
2338
2339 #[test]
2340 fn test_pcm_device_lifecycle() {
2341 let mut dev = PcmDevice::new(0, "test", StreamDirection::Playback);
2342 dev.open().unwrap();
2343 assert_eq!(dev.state(), PcmState::Open);
2344
2345 dev.set_hw_params(HwParams::new()).unwrap();
2346 assert_eq!(dev.state(), PcmState::Setup);
2347
2348 dev.prepare().unwrap();
2349 assert_eq!(dev.state(), PcmState::Prepared);
2350
2351 dev.start().unwrap();
2352 assert_eq!(dev.state(), PcmState::Running);
2353
2354 dev.pause().unwrap();
2355 assert_eq!(dev.state(), PcmState::Paused);
2356
2357 dev.resume().unwrap();
2358 assert_eq!(dev.state(), PcmState::Running);
2359
2360 dev.stop().unwrap();
2361 assert_eq!(dev.state(), PcmState::Prepared);
2362 }
2363
2364 #[test]
2365 fn test_pcm_device_write() {
2366 let mut dev = PcmDevice::new(0, "test", StreamDirection::Playback);
2367 dev.open().unwrap();
2368 dev.set_hw_params(HwParams {
2369 channels: 1,
2370 format: PcmFormat::S16Le,
2371 buffer_size: 64,
2372 period_size: 16,
2373 ..HwParams::new()
2374 })
2375 .unwrap();
2376 dev.prepare().unwrap();
2377 dev.start().unwrap();
2378
2379 let data = [0u8, 0, 1, 0, 2, 0, 3, 0];
2381 let frames = dev.write(&data).unwrap();
2382 assert_eq!(frames, 4);
2383 assert_eq!(dev.total_frames_written(), 4);
2384 }
2385
2386 #[test]
2387 fn test_pcm_device_write_wrong_direction() {
2388 let mut dev = PcmDevice::new(0, "test", StreamDirection::Capture);
2389 dev.open().unwrap();
2390 dev.set_hw_params(HwParams::new()).unwrap();
2391 dev.prepare().unwrap();
2392 dev.start().unwrap();
2393
2394 let data = [0u8; 8];
2395 assert_eq!(dev.write(&data), Err(AlsaError::WrongDirection));
2396 }
2397
2398 #[test]
2399 fn test_pcm_device_read_capture() {
2400 let mut dev = PcmDevice::new(0, "test", StreamDirection::Capture);
2401 dev.open().unwrap();
2402 let hw = HwParams {
2403 channels: 1,
2404 format: PcmFormat::S16Le,
2405 buffer_size: 64,
2406 period_size: 16,
2407 ..HwParams::new()
2408 };
2409 dev.set_hw_params(hw).unwrap();
2410 dev.prepare().unwrap();
2411 dev.start().unwrap();
2412
2413 let input = [10u8, 0, 20, 0, 30, 0, 40, 0];
2415 let pushed = dev.push_capture_data(&input).unwrap();
2416 assert_eq!(pushed, 4); let mut output = [0u8; 8];
2420 let frames = dev.read(&mut output).unwrap();
2421 assert_eq!(frames, 4);
2422 assert_eq!(output, input);
2423 }
2424
2425 #[test]
2428 fn test_convert_u8_to_s16() {
2429 assert_eq!(convert_u8_to_s16(128), 0); assert_eq!(convert_u8_to_s16(0), -32768); assert_eq!(convert_u8_to_s16(255), 32512); }
2433
2434 #[test]
2435 fn test_convert_s16_to_u8() {
2436 assert_eq!(convert_s16_to_u8(0), 128); assert_eq!(convert_s16_to_u8(-32768), 0); assert_eq!(convert_s16_to_u8(32512), 255); }
2441
2442 #[test]
2443 fn test_convert_s16_s32_roundtrip() {
2444 let original: i16 = 1234;
2445 let s32 = convert_s16_to_s32(original);
2446 let back = convert_s32_to_s16(s32);
2447 assert_eq!(back, original);
2448 }
2449
2450 #[test]
2451 fn test_convert_s32_to_s16_saturation() {
2452 assert_eq!(convert_s32_to_s16(i32::MAX), i16::MAX);
2453 assert_eq!(convert_s32_to_s16(i32::MIN), i16::MIN);
2454 }
2455
2456 #[test]
2457 fn test_convert_buffer_s16_to_s32() {
2458 let input_sample: i16 = 1000;
2459 let input = input_sample.to_le_bytes();
2460 let mut output = Vec::new();
2461
2462 convert_buffer(&input, PcmFormat::S16Le, PcmFormat::S32Le, &mut output);
2463 assert_eq!(output.len(), 4);
2464
2465 let result = i32::from_le_bytes([output[0], output[1], output[2], output[3]]);
2466 assert_eq!(result, convert_s16_to_s32(1000));
2467 }
2468
2469 #[test]
2470 fn test_convert_buffer_same_format() {
2471 let input = [1u8, 2, 3, 4];
2472 let mut output = Vec::new();
2473 convert_buffer(&input, PcmFormat::S16Le, PcmFormat::S16Le, &mut output);
2474 assert_eq!(output, input);
2475 }
2476
2477 #[test]
2480 fn test_mixer_default_controls() {
2481 let mixer = AlsaMixer::new();
2482 assert_eq!(mixer.control_count(), 5);
2483
2484 let master = mixer.get_control(MIXER_MASTER_VOLUME).unwrap();
2486 assert_eq!(master.get_value(), 80);
2487 assert_eq!(master.min, 0);
2488 assert_eq!(master.max, 100);
2489
2490 let pcm = mixer.get_control(MIXER_PCM_VOLUME).unwrap();
2492 assert_eq!(pcm.get_value(), 100);
2493 }
2494
2495 #[test]
2496 fn test_mixer_set_volume() {
2497 let mixer = AlsaMixer::new();
2498 assert!(mixer.set_volume(MIXER_MASTER_VOLUME, 50).is_ok());
2499 assert_eq!(mixer.get_volume(MIXER_MASTER_VOLUME).unwrap(), 50);
2500 }
2501
2502 #[test]
2503 fn test_mixer_volume_out_of_range() {
2504 let mixer = AlsaMixer::new();
2505 assert_eq!(
2506 mixer.set_volume(MIXER_MASTER_VOLUME, 101),
2507 Err(AlsaError::MixerValueOutOfRange {
2508 value: 101,
2509 min: 0,
2510 max: 100,
2511 })
2512 );
2513 }
2514
2515 #[test]
2516 fn test_mixer_boolean_control() {
2517 let mixer = AlsaMixer::new();
2518 let switch = mixer.get_control(MIXER_MASTER_SWITCH).unwrap();
2519 assert!(switch.get_bool()); switch.set_bool(false);
2522 assert!(!switch.get_bool());
2523
2524 switch.set_bool(true);
2525 assert!(switch.get_bool());
2526 }
2527
2528 #[test]
2529 fn test_mixer_control_not_found() {
2530 let mixer = AlsaMixer::new();
2531 assert!(matches!(
2532 mixer.get_control(999),
2533 Err(AlsaError::MixerControlNotFound { id: 999 })
2534 ));
2535 }
2536
2537 #[test]
2540 fn test_gain_unity() {
2541 assert_eq!(GainFactor::UNITY.raw(), FP_ONE);
2542 }
2543
2544 #[test]
2545 fn test_gain_mute() {
2546 assert_eq!(GainFactor::MUTE.raw(), 0);
2547 }
2548
2549 #[test]
2550 fn test_gain_apply_s16_unity() {
2551 let sample: i16 = 16384;
2552 let result = GainFactor::UNITY.apply_s16(sample);
2553 assert_eq!(result, sample);
2554 }
2555
2556 #[test]
2557 fn test_gain_apply_s16_mute() {
2558 let sample: i16 = 16384;
2559 let result = GainFactor::MUTE.apply_s16(sample);
2560 assert_eq!(result, 0);
2561 }
2562
2563 #[test]
2564 fn test_gain_from_db_tenths_zero() {
2565 let gain = gain_from_db_tenths(0);
2566 assert_eq!(gain.raw(), FP_ONE);
2567 }
2568
2569 #[test]
2570 fn test_gain_from_db_tenths_mute() {
2571 let gain = gain_from_db_tenths(-700);
2572 assert_eq!(gain, GainFactor::MUTE);
2573 }
2574
2575 #[test]
2576 fn test_gain_from_percent_boundaries() {
2577 let mute = gain_from_percent(0);
2578 assert_eq!(mute, GainFactor::MUTE);
2579
2580 let full = gain_from_percent(100);
2581 assert_eq!(full, GainFactor::UNITY);
2582 }
2583
2584 #[test]
2585 fn test_gain_from_percent_mid() {
2586 let mid = gain_from_percent(50);
2587 assert!(mid.raw() > 0);
2589 assert!(mid.raw() < FP_ONE);
2590 }
2591
2592 #[test]
2595 fn test_capture_device_lifecycle() {
2596 let hw = HwParams {
2597 channels: 1,
2598 format: PcmFormat::S16Le,
2599 ..HwParams::new()
2600 };
2601 let mut cap = CaptureDevice::new(0, "mic", hw);
2602 assert_eq!(cap.state(), CaptureState::Idle);
2603
2604 cap.start().unwrap();
2605 assert_eq!(cap.state(), CaptureState::Recording);
2606
2607 cap.pause();
2608 assert_eq!(cap.state(), CaptureState::Paused);
2609
2610 cap.resume();
2611 assert_eq!(cap.state(), CaptureState::Recording);
2612
2613 cap.stop();
2614 assert_eq!(cap.state(), CaptureState::Idle);
2615 }
2616
2617 #[test]
2618 fn test_capture_device_push_read() {
2619 let hw = HwParams {
2620 channels: 1,
2621 format: PcmFormat::S16Le,
2622 ..HwParams::new()
2623 };
2624 let mut cap = CaptureDevice::new(0, "mic", hw);
2625 cap.start().unwrap();
2626
2627 let input = [10u8, 0, 20, 0, 30, 0, 40, 0];
2629 let frames = cap.push_data(&input).unwrap();
2630 assert_eq!(frames, 4);
2631
2632 let mut output = [0u8; 8];
2634 let read = cap.read_data(&mut output);
2635 assert_eq!(read, 4);
2636 assert_eq!(output, input);
2637 }
2638
2639 #[test]
2640 fn test_capture_device_stats() {
2641 let hw = HwParams {
2642 channels: 1,
2643 format: PcmFormat::S16Le,
2644 ..HwParams::new()
2645 };
2646 let mut cap = CaptureDevice::new(0, "mic", hw);
2647 cap.start().unwrap();
2648
2649 let input = [0u8; 8]; let _ = cap.push_data(&input);
2651
2652 let stats = cap.stats();
2653 assert_eq!(stats.frames_captured, 4);
2654 assert_eq!(stats.overruns, 0);
2655 assert!(stats.buffer_capacity > 0);
2656 }
2657
2658 #[test]
2659 fn test_capture_device_gain() {
2660 let hw = HwParams {
2661 channels: 1,
2662 format: PcmFormat::S16Le,
2663 ..HwParams::new()
2664 };
2665 let mut cap = CaptureDevice::new(0, "mic", hw);
2666 assert_eq!(cap.gain(), GainFactor::UNITY);
2667
2668 cap.set_gain(GainFactor::MUTE);
2669 assert_eq!(cap.gain(), GainFactor::MUTE);
2670 }
2671
2672 #[test]
2675 fn test_device_registry_register() {
2676 let mut reg = DeviceRegistry::new();
2677 let info = DeviceInfo {
2678 id: 1,
2679 name: String::from("test"),
2680 playback: true,
2681 capture: false,
2682 max_sample_rate: 48000,
2683 max_channels: 2,
2684 formats: vec![PcmFormat::S16Le],
2685 };
2686 let id = reg.register(info).unwrap();
2687 assert_eq!(id, 1);
2688 assert_eq!(reg.device_count(), 1);
2689 }
2690
2691 #[test]
2692 fn test_device_registry_list_filtered() {
2693 let mut reg = DeviceRegistry::new();
2694 reg.register(DeviceInfo {
2695 id: 1,
2696 name: String::from("playback"),
2697 playback: true,
2698 capture: false,
2699 max_sample_rate: 48000,
2700 max_channels: 2,
2701 formats: vec![PcmFormat::S16Le],
2702 })
2703 .unwrap();
2704 reg.register(DeviceInfo {
2705 id: 2,
2706 name: String::from("capture"),
2707 playback: false,
2708 capture: true,
2709 max_sample_rate: 48000,
2710 max_channels: 1,
2711 formats: vec![PcmFormat::S16Le],
2712 })
2713 .unwrap();
2714
2715 assert_eq!(reg.list_playback().len(), 1);
2716 assert_eq!(reg.list_capture().len(), 1);
2717 assert_eq!(reg.list().len(), 2);
2718 }
2719
2720 #[test]
2721 fn test_device_registry_unregister() {
2722 let mut reg = DeviceRegistry::new();
2723 reg.register(DeviceInfo {
2724 id: 1,
2725 name: String::from("test"),
2726 playback: true,
2727 capture: false,
2728 max_sample_rate: 48000,
2729 max_channels: 2,
2730 formats: vec![PcmFormat::S16Le],
2731 })
2732 .unwrap();
2733 assert_eq!(reg.device_count(), 1);
2734
2735 reg.unregister(1).unwrap();
2736 assert_eq!(reg.device_count(), 0);
2737
2738 assert_eq!(
2739 reg.unregister(1),
2740 Err(AlsaError::DeviceNotFound { device_id: 1 })
2741 );
2742 }
2743
2744 #[test]
2747 fn test_pcm_format_sizes() {
2748 assert_eq!(PcmFormat::U8.bytes_per_sample(), 1);
2749 assert_eq!(PcmFormat::S16Le.bytes_per_sample(), 2);
2750 assert_eq!(PcmFormat::S32Le.bytes_per_sample(), 4);
2751 assert_eq!(PcmFormat::F32FixedPoint.bytes_per_sample(), 4);
2752 }
2753
2754 #[test]
2755 fn test_pcm_format_bits() {
2756 assert_eq!(PcmFormat::U8.bits_per_sample(), 8);
2757 assert_eq!(PcmFormat::S16Le.bits_per_sample(), 16);
2758 assert_eq!(PcmFormat::S32Le.bits_per_sample(), 32);
2759 }
2760
2761 #[test]
2764 fn test_sw_params_defaults() {
2765 let sw = SwParams::new(4096);
2766 assert_eq!(sw.avail_min, 1);
2767 assert_eq!(sw.start_threshold, 4096);
2768 assert_eq!(sw.stop_threshold, 4096);
2769 }
2770
2771 #[test]
2772 fn test_sw_params_validation() {
2773 let hw = HwParams::new();
2774 let sw = SwParams::new(hw.buffer_size);
2775 assert!(sw.validate(&hw).is_ok());
2776
2777 let bad_sw = SwParams {
2778 avail_min: 0,
2779 ..SwParams::new(hw.buffer_size)
2780 };
2781 assert!(bad_sw.validate(&hw).is_err());
2782 }
2783
2784 #[test]
2787 fn test_pcm_device_xrun_recovery() {
2788 let mut dev = PcmDevice::new(0, "test", StreamDirection::Playback);
2789 dev.open().unwrap();
2790 dev.set_hw_params(HwParams::new()).unwrap();
2791 dev.prepare().unwrap();
2792 dev.start().unwrap();
2793
2794 dev.state = PcmState::XRun;
2796 assert_eq!(dev.state(), PcmState::XRun);
2797
2798 dev.recover_xrun().unwrap();
2799 assert_eq!(dev.state(), PcmState::Prepared);
2800 }
2801
2802 #[test]
2805 fn test_pcm_device_mmap() {
2806 let mut dev = PcmDevice::new(0, "test", StreamDirection::Playback);
2807 dev.open().unwrap();
2808 dev.set_hw_params(HwParams {
2809 channels: 1,
2810 format: PcmFormat::S16Le,
2811 buffer_size: 64,
2812 period_size: 16,
2813 ..HwParams::new()
2814 })
2815 .unwrap();
2816 dev.prepare().unwrap();
2817
2818 let (buf, offset, avail) = dev.mmap_begin().unwrap();
2819 assert!(!buf.is_empty());
2820 assert_eq!(offset, 0);
2821 assert!(avail > 0);
2822
2823 dev.mmap_commit(4).unwrap();
2825 assert_eq!(dev.total_frames_written(), 4);
2826 }
2827
2828 #[test]
2831 fn test_enumerated_mixer_control() {
2832 let items = vec![
2833 String::from("Input 1"),
2834 String::from("Input 2"),
2835 String::from("Input 3"),
2836 ];
2837 let ctrl = MixerControl::new_enumerated(10, "Capture Source", items, 0);
2838 assert_eq!(ctrl.control_type, MixerControlType::Enumerated);
2839 assert_eq!(ctrl.get_enum_name(), Some("Input 1"));
2840
2841 ctrl.set_value(2).unwrap();
2842 assert_eq!(ctrl.get_enum_name(), Some("Input 3"));
2843
2844 assert_eq!(
2845 ctrl.set_value(3),
2846 Err(AlsaError::MixerValueOutOfRange {
2847 value: 3,
2848 min: 0,
2849 max: 2,
2850 })
2851 );
2852 }
2853}