screencapturekit/
audio_devices.rs

1//! Audio input device enumeration using `AVFoundation`.
2//!
3//! This module provides access to available microphone devices on macOS.
4
5use crate::utils::ffi_string::{ffi_string_from_buffer, SMALL_BUFFER_SIZE};
6
7/// Represents an audio input device (microphone).
8#[derive(Debug, Clone, PartialEq, Eq)]
9pub struct AudioInputDevice {
10    /// The unique device ID used with `SCStreamConfiguration::with_microphone_capture_device_id`
11    pub id: String,
12    /// Human-readable device name
13    pub name: String,
14    /// Whether this is the system default audio input device
15    pub is_default: bool,
16}
17
18impl AudioInputDevice {
19    /// List all available audio input devices.
20    ///
21    /// # Example
22    ///
23    /// ```no_run
24    /// use screencapturekit::audio_devices::AudioInputDevice;
25    ///
26    /// let devices = AudioInputDevice::list();
27    /// for device in &devices {
28    ///     println!("{}: {} {}", device.id, device.name,
29    ///         if device.is_default { "(default)" } else { "" });
30    /// }
31    /// ```
32    #[allow(clippy::cast_possible_wrap, clippy::cast_sign_loss)]
33    pub fn list() -> Vec<Self> {
34        let count = unsafe { crate::ffi::sc_audio_get_input_device_count() };
35        let mut devices = Vec::with_capacity(count as usize);
36
37        for i in 0..count {
38            let id = unsafe {
39                ffi_string_from_buffer(SMALL_BUFFER_SIZE, |buf, len| {
40                    crate::ffi::sc_audio_get_input_device_id(i, buf, len)
41                })
42            };
43            let name = unsafe {
44                ffi_string_from_buffer(SMALL_BUFFER_SIZE, |buf, len| {
45                    crate::ffi::sc_audio_get_input_device_name(i, buf, len)
46                })
47            };
48            let is_default = unsafe { crate::ffi::sc_audio_is_default_input_device(i) };
49
50            if let (Some(id), Some(name)) = (id, name) {
51                devices.push(Self {
52                    id,
53                    name,
54                    is_default,
55                });
56            }
57        }
58
59        devices
60    }
61
62    /// Get the default audio input device, if any.
63    ///
64    /// # Example
65    ///
66    /// ```no_run
67    /// use screencapturekit::audio_devices::AudioInputDevice;
68    ///
69    /// if let Some(device) = AudioInputDevice::default_device() {
70    ///     println!("Default microphone: {}", device.name);
71    /// }
72    /// ```
73    pub fn default_device() -> Option<Self> {
74        let id = unsafe {
75            ffi_string_from_buffer(SMALL_BUFFER_SIZE, |buf, len| {
76                crate::ffi::sc_audio_get_default_input_device_id(buf, len)
77            })
78        };
79        let name = unsafe {
80            ffi_string_from_buffer(SMALL_BUFFER_SIZE, |buf, len| {
81                crate::ffi::sc_audio_get_default_input_device_name(buf, len)
82            })
83        };
84
85        match (id, name) {
86            (Some(id), Some(name)) => Some(Self {
87                id,
88                name,
89                is_default: true,
90            }),
91            _ => None,
92        }
93    }
94}