screencapturekit/stream/configuration/
audio.rs

1//! Audio capture configuration
2//!
3//! Methods for configuring audio capture, sample rate, and channel count.
4//!
5//! ## Supported Values
6//!
7//! `ScreenCaptureKit` supports specific sample rates and channel counts:
8//!
9//! | Sample Rate | Description |
10//! |-------------|-------------|
11//! | 8000 Hz | Low quality, telephony |
12//! | 16000 Hz | Speech quality |
13//! | 24000 Hz | Medium quality |
14//! | 48000 Hz | Professional audio (default) |
15//!
16//! | Channel Count | Description |
17//! |---------------|-------------|
18//! | 1 | Mono |
19//! | 2 | Stereo (default) |
20
21use crate::utils::ffi_string::{ffi_string_from_buffer, SMALL_BUFFER_SIZE};
22
23use super::internal::SCStreamConfiguration;
24
25/// Audio sample rate for capture
26///
27/// `ScreenCaptureKit` supports a fixed set of sample rates. Using values outside
28/// this set will result in the system defaulting to 48000 Hz.
29///
30/// # Examples
31///
32/// ```
33/// use screencapturekit::stream::configuration::audio::AudioSampleRate;
34///
35/// // Get the Hz value
36/// assert_eq!(AudioSampleRate::Rate48000.as_hz(), 48000);
37///
38/// // Use default (48000 Hz)
39/// let rate = AudioSampleRate::default();
40/// assert_eq!(rate, AudioSampleRate::Rate48000);
41/// ```
42#[repr(i32)]
43#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
44pub enum AudioSampleRate {
45    /// 8000 Hz - Low quality, suitable for telephony
46    Rate8000 = 8000,
47    /// 16000 Hz - Speech quality
48    Rate16000 = 16000,
49    /// 24000 Hz - Medium quality
50    Rate24000 = 24000,
51    /// 48000 Hz - Professional audio quality (default)
52    #[default]
53    Rate48000 = 48000,
54}
55
56impl AudioSampleRate {
57    /// Get the sample rate in Hz
58    #[must_use]
59    pub const fn as_hz(self) -> i32 {
60        self as i32
61    }
62
63    /// Create from Hz value, returning None if unsupported
64    ///
65    /// # Examples
66    ///
67    /// ```
68    /// use screencapturekit::stream::configuration::audio::AudioSampleRate;
69    ///
70    /// assert_eq!(AudioSampleRate::from_hz(48000), Some(AudioSampleRate::Rate48000));
71    /// assert_eq!(AudioSampleRate::from_hz(44100), None); // Not supported
72    /// ```
73    #[must_use]
74    pub const fn from_hz(hz: i32) -> Option<Self> {
75        match hz {
76            8000 => Some(Self::Rate8000),
77            16000 => Some(Self::Rate16000),
78            24000 => Some(Self::Rate24000),
79            48000 => Some(Self::Rate48000),
80            _ => None,
81        }
82    }
83}
84
85impl std::fmt::Display for AudioSampleRate {
86    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
87        write!(f, "{} Hz", self.as_hz())
88    }
89}
90
91impl From<AudioSampleRate> for i32 {
92    fn from(rate: AudioSampleRate) -> Self {
93        rate.as_hz()
94    }
95}
96
97/// Audio channel configuration for capture
98///
99/// `ScreenCaptureKit` supports mono (1 channel) or stereo (2 channels) audio.
100/// Using other values will result in the system defaulting to stereo.
101///
102/// # Examples
103///
104/// ```
105/// use screencapturekit::stream::configuration::audio::AudioChannelCount;
106///
107/// // Get the channel count
108/// assert_eq!(AudioChannelCount::Stereo.as_count(), 2);
109///
110/// // Use default (stereo)
111/// let channels = AudioChannelCount::default();
112/// assert_eq!(channels, AudioChannelCount::Stereo);
113/// ```
114#[repr(i32)]
115#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
116pub enum AudioChannelCount {
117    /// Mono - single channel audio
118    Mono = 1,
119    /// Stereo - two channel audio (default)
120    #[default]
121    Stereo = 2,
122}
123
124impl AudioChannelCount {
125    /// Get the channel count as an integer
126    #[must_use]
127    pub const fn as_count(self) -> i32 {
128        self as i32
129    }
130
131    /// Create from channel count, returning None if unsupported
132    ///
133    /// # Examples
134    ///
135    /// ```
136    /// use screencapturekit::stream::configuration::audio::AudioChannelCount;
137    ///
138    /// assert_eq!(AudioChannelCount::from_count(2), Some(AudioChannelCount::Stereo));
139    /// assert_eq!(AudioChannelCount::from_count(6), None); // Not supported
140    /// ```
141    #[must_use]
142    pub const fn from_count(count: i32) -> Option<Self> {
143        match count {
144            1 => Some(Self::Mono),
145            2 => Some(Self::Stereo),
146            _ => None,
147        }
148    }
149}
150
151impl std::fmt::Display for AudioChannelCount {
152    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
153        match self {
154            Self::Mono => write!(f, "Mono (1 channel)"),
155            Self::Stereo => write!(f, "Stereo (2 channels)"),
156        }
157    }
158}
159
160impl From<AudioChannelCount> for i32 {
161    fn from(count: AudioChannelCount) -> Self {
162        count.as_count()
163    }
164}
165
166impl SCStreamConfiguration {
167    /// Enable or disable audio capture
168    ///
169    /// # Examples
170    ///
171    /// ```
172    /// use screencapturekit::prelude::*;
173    ///
174    /// let mut config = SCStreamConfiguration::default();
175    /// config.set_captures_audio(true);
176    /// assert!(config.captures_audio());
177    /// ```
178    pub fn set_captures_audio(&mut self, captures_audio: bool) -> &mut Self {
179        unsafe {
180            crate::ffi::sc_stream_configuration_set_captures_audio(self.as_ptr(), captures_audio);
181        }
182        self
183    }
184
185    /// Enable or disable audio capture (builder pattern)
186    #[must_use]
187    pub fn with_captures_audio(mut self, captures_audio: bool) -> Self {
188        self.set_captures_audio(captures_audio);
189        self
190    }
191
192    /// Check if audio capture is enabled
193    pub fn captures_audio(&self) -> bool {
194        unsafe { crate::ffi::sc_stream_configuration_get_captures_audio(self.as_ptr()) }
195    }
196
197    /// Set the audio sample rate
198    ///
199    /// Accepts either an [`AudioSampleRate`] enum or a raw `i32` Hz value.
200    ///
201    /// # Supported Values
202    ///
203    /// `ScreenCaptureKit` supports: 8000, 16000, 24000, 48000 Hz.
204    /// Other values will default to 48000 Hz.
205    ///
206    /// # Examples
207    ///
208    /// ```
209    /// use screencapturekit::prelude::*;
210    /// use screencapturekit::stream::configuration::audio::AudioSampleRate;
211    ///
212    /// // Using the enum (recommended)
213    /// let config = SCStreamConfiguration::new()
214    ///     .with_sample_rate(AudioSampleRate::Rate48000);
215    ///
216    /// // Using raw value (still works)
217    /// let config = SCStreamConfiguration::new()
218    ///     .with_sample_rate(48000);
219    /// ```
220    pub fn set_sample_rate(&mut self, sample_rate: impl Into<i32>) -> &mut Self {
221        unsafe {
222            crate::ffi::sc_stream_configuration_set_sample_rate(
223                self.as_ptr(),
224                sample_rate.into() as isize,
225            );
226        }
227        self
228    }
229
230    /// Set the audio sample rate (builder pattern)
231    #[must_use]
232    pub fn with_sample_rate(mut self, sample_rate: impl Into<i32>) -> Self {
233        self.set_sample_rate(sample_rate);
234        self
235    }
236
237    /// Get the configured audio sample rate in Hz
238    pub fn sample_rate(&self) -> i32 {
239        // FFI returns isize but sample rate fits in i32 (typical values: 44100, 48000)
240        #[allow(clippy::cast_possible_truncation)]
241        unsafe {
242            crate::ffi::sc_stream_configuration_get_sample_rate(self.as_ptr()) as i32
243        }
244    }
245
246    /// Get the configured audio sample rate as an enum
247    ///
248    /// Returns `None` if the current sample rate is not a supported value.
249    pub fn audio_sample_rate(&self) -> Option<AudioSampleRate> {
250        AudioSampleRate::from_hz(self.sample_rate())
251    }
252
253    /// Set the number of audio channels
254    ///
255    /// Accepts either an [`AudioChannelCount`] enum or a raw `i32` value.
256    ///
257    /// # Supported Values
258    ///
259    /// `ScreenCaptureKit` supports: 1 (mono), 2 (stereo).
260    /// Other values will default to stereo.
261    ///
262    /// # Examples
263    ///
264    /// ```
265    /// use screencapturekit::prelude::*;
266    /// use screencapturekit::stream::configuration::audio::AudioChannelCount;
267    ///
268    /// // Using the enum (recommended)
269    /// let config = SCStreamConfiguration::new()
270    ///     .with_channel_count(AudioChannelCount::Stereo);
271    ///
272    /// // Using raw value (still works)
273    /// let config = SCStreamConfiguration::new()
274    ///     .with_channel_count(2);
275    /// ```
276    pub fn set_channel_count(&mut self, channel_count: impl Into<i32>) -> &mut Self {
277        unsafe {
278            crate::ffi::sc_stream_configuration_set_channel_count(
279                self.as_ptr(),
280                channel_count.into() as isize,
281            );
282        }
283        self
284    }
285
286    /// Set the number of audio channels (builder pattern)
287    #[must_use]
288    pub fn with_channel_count(mut self, channel_count: impl Into<i32>) -> Self {
289        self.set_channel_count(channel_count);
290        self
291    }
292
293    /// Get the configured channel count
294    pub fn channel_count(&self) -> i32 {
295        // FFI returns isize but channel count fits in i32 (typical values: 1-8)
296        #[allow(clippy::cast_possible_truncation)]
297        unsafe {
298            crate::ffi::sc_stream_configuration_get_channel_count(self.as_ptr()) as i32
299        }
300    }
301
302    /// Get the configured channel count as an enum
303    ///
304    /// Returns `None` if the current channel count is not a supported value.
305    pub fn audio_channel_count(&self) -> Option<AudioChannelCount> {
306        AudioChannelCount::from_count(self.channel_count())
307    }
308
309    /// Enable microphone capture (macOS 15.0+)
310    ///
311    /// When set to `true`, the stream will capture audio from the microphone
312    /// in addition to system/application audio (if `captures_audio` is also enabled).
313    ///
314    /// **Note**: Requires `NSMicrophoneUsageDescription` in your app's Info.plist
315    /// for microphone access permission.
316    ///
317    /// # Availability
318    /// macOS 15.0+. On earlier versions, this setting has no effect.
319    ///
320    /// # Example
321    /// ```rust,no_run
322    /// use screencapturekit::prelude::*;
323    ///
324    /// let config = SCStreamConfiguration::new()
325    ///     .with_captures_audio(true)       // System audio
326    ///     .with_captures_microphone(true)  // Microphone audio (macOS 15.0+)
327    ///     .with_sample_rate(48000)
328    ///     .with_channel_count(2);
329    /// ```
330    pub fn set_captures_microphone(&mut self, captures_microphone: bool) -> &mut Self {
331        unsafe {
332            crate::ffi::sc_stream_configuration_set_captures_microphone(
333                self.as_ptr(),
334                captures_microphone,
335            );
336        }
337        self
338    }
339
340    /// Enable microphone capture (builder pattern)
341    #[must_use]
342    pub fn with_captures_microphone(mut self, captures_microphone: bool) -> Self {
343        self.set_captures_microphone(captures_microphone);
344        self
345    }
346
347    /// Get whether microphone capture is enabled (macOS 15.0+).
348    pub fn captures_microphone(&self) -> bool {
349        unsafe { crate::ffi::sc_stream_configuration_get_captures_microphone(self.as_ptr()) }
350    }
351
352    /// Exclude current process audio from capture.
353    ///
354    /// When set to `true`, the stream will not capture audio from the current
355    /// process, preventing feedback loops in recording applications.
356    ///
357    /// # Example
358    /// ```rust,no_run
359    /// use screencapturekit::prelude::*;
360    ///
361    /// let config = SCStreamConfiguration::new()
362    ///     .with_captures_audio(true)
363    ///     .with_excludes_current_process_audio(true); // Prevent feedback
364    /// ```
365    pub fn set_excludes_current_process_audio(&mut self, excludes: bool) -> &mut Self {
366        unsafe {
367            crate::ffi::sc_stream_configuration_set_excludes_current_process_audio(
368                self.as_ptr(),
369                excludes,
370            );
371        }
372        self
373    }
374
375    /// Exclude current process audio (builder pattern)
376    #[must_use]
377    pub fn with_excludes_current_process_audio(mut self, excludes: bool) -> Self {
378        self.set_excludes_current_process_audio(excludes);
379        self
380    }
381
382    /// Get whether current process audio is excluded from capture.
383    pub fn excludes_current_process_audio(&self) -> bool {
384        unsafe {
385            crate::ffi::sc_stream_configuration_get_excludes_current_process_audio(self.as_ptr())
386        }
387    }
388
389    /// Set microphone capture device ID (macOS 15.0+).
390    ///
391    /// Specifies which microphone device to capture from.
392    ///
393    /// # Availability
394    /// macOS 15.0+. On earlier versions, this setting has no effect.
395    ///
396    /// # Example
397    /// ```rust,no_run
398    /// use screencapturekit::prelude::*;
399    ///
400    /// let mut config = SCStreamConfiguration::new()
401    ///     .with_captures_microphone(true);
402    /// config.set_microphone_capture_device_id("AppleHDAEngineInput:1B,0,1,0:1");
403    /// ```
404    pub fn set_microphone_capture_device_id(&mut self, device_id: &str) -> &mut Self {
405        unsafe {
406            if let Ok(c_id) = std::ffi::CString::new(device_id) {
407                crate::ffi::sc_stream_configuration_set_microphone_capture_device_id(
408                    self.as_ptr(),
409                    c_id.as_ptr(),
410                );
411            }
412        }
413        self
414    }
415
416    /// Set microphone capture device ID (builder pattern)
417    #[must_use]
418    pub fn with_microphone_capture_device_id(mut self, device_id: &str) -> Self {
419        self.set_microphone_capture_device_id(device_id);
420        self
421    }
422
423    /// Clear microphone capture device ID, reverting to default system microphone
424    pub fn clear_microphone_capture_device_id(&mut self) -> &mut Self {
425        unsafe {
426            crate::ffi::sc_stream_configuration_set_microphone_capture_device_id(
427                self.as_ptr(),
428                std::ptr::null(),
429            );
430        }
431        self
432    }
433
434    /// Get microphone capture device ID (macOS 15.0+).
435    pub fn microphone_capture_device_id(&self) -> Option<String> {
436        unsafe {
437            ffi_string_from_buffer(SMALL_BUFFER_SIZE, |buf, len| {
438                crate::ffi::sc_stream_configuration_get_microphone_capture_device_id(
439                    self.as_ptr(),
440                    buf,
441                    len,
442                )
443            })
444        }
445    }
446}