@@ -18,11 +18,12 @@ use objc2_core_audio::{
1818 kAudioDevicePropertyAvailableNominalSampleRates, kAudioDevicePropertyDeviceIsAlive,
1919 kAudioDevicePropertyDeviceNameCFString, kAudioDevicePropertyHogMode,
2020 kAudioDevicePropertyNominalSampleRate, kAudioDevicePropertyScopeOutput,
21- kAudioDevicePropertyStreamConfiguration, kAudioHardwareNoError,
22- kAudioHardwarePropertyDefaultInputDevice, kAudioHardwarePropertyDefaultOutputDevice,
23- kAudioHardwarePropertyDevices, kAudioObjectPropertyElementMaster,
24- kAudioObjectPropertyElementWildcard, kAudioObjectPropertyScopeGlobal,
25- kAudioObjectPropertyScopeInput, kAudioObjectPropertyScopeOutput, kAudioObjectSystemObject,
21+ kAudioDevicePropertyStreamConfiguration, kAudioDevicePropertyTransportType,
22+ kAudioHardwareNoError, kAudioHardwarePropertyDefaultInputDevice,
23+ kAudioHardwarePropertyDefaultOutputDevice, kAudioHardwarePropertyDevices,
24+ kAudioObjectPropertyElementMaster, kAudioObjectPropertyElementWildcard,
25+ kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyScopeInput,
26+ kAudioObjectPropertyScopeOutput, kAudioObjectSystemObject,
2627 kAudioStreamPropertyAvailablePhysicalFormats, kAudioStreamPropertyPhysicalFormat,
2728 AudioDeviceID , AudioObjectAddPropertyListener , AudioObjectGetPropertyData ,
2829 AudioObjectGetPropertyDataSize , AudioObjectID , AudioObjectPropertyAddress ,
@@ -188,6 +189,37 @@ fn test_get_audio_device_ids() {
188189 let _ = get_audio_device_ids ( ) . expect ( "Failed to get audio device ids" ) ;
189190}
190191
192+ #[ test]
193+ fn test_get_available_sample_rates ( ) {
194+ if let Some ( device_id) = get_default_device_id ( false ) {
195+ let rates = get_available_sample_rates ( device_id) . expect ( "Failed to get sample rates" ) ;
196+ assert ! (
197+ !rates. is_empty( ) ,
198+ "Default output device should support at least one sample rate"
199+ ) ;
200+ for range in & rates {
201+ assert ! (
202+ range. mMinimum > 0.0 ,
203+ "Sample rate minimum should be positive"
204+ ) ;
205+ assert ! (
206+ range. mMaximum >= range. mMinimum,
207+ "Maximum should be >= minimum"
208+ ) ;
209+ }
210+ }
211+ }
212+
213+ #[ test]
214+ fn test_get_device_transport_type ( ) {
215+ if let Some ( device_id) = get_default_device_id ( false ) {
216+ let transport = get_device_transport_type ( device_id) . expect ( "Failed to get transport type" ) ;
217+ // Transport type should be a non-zero FourCC value (or 0 for unknown)
218+ // The default output device typically has a known transport type
219+ let _ = transport;
220+ }
221+ }
222+
191223#[ test]
192224fn test_get_audio_device_ids_for_scope ( ) {
193225 for scope in & [
@@ -577,6 +609,84 @@ pub fn get_supported_physical_stream_formats(
577609 Ok ( allformats)
578610}
579611
612+ /// Get the available nominal sample rate ranges for a device.
613+ ///
614+ /// Returns a vector of [`AudioValueRange`] where each entry describes a supported range.
615+ /// For devices that support discrete rates, `mMinimum` and `mMaximum` will be equal.
616+ /// For devices that support continuous ranges, they will differ.
617+ pub fn get_available_sample_rates ( device_id : AudioDeviceID ) -> Result < Vec < AudioValueRange > , Error > {
618+ let property_address = AudioObjectPropertyAddress {
619+ mSelector : kAudioDevicePropertyAvailableNominalSampleRates,
620+ mScope : kAudioObjectPropertyScopeGlobal,
621+ mElement : kAudioObjectPropertyElementMaster,
622+ } ;
623+
624+ let mut data_size = 0u32 ;
625+ // SAFETY: AudioObjectGetPropertyDataSize is a CoreAudio C API that reads
626+ // the size of the property data into data_size. All pointers passed are
627+ // valid stack references via NonNull::from.
628+ let status = unsafe {
629+ AudioObjectGetPropertyDataSize (
630+ device_id,
631+ NonNull :: from ( & property_address) ,
632+ 0 ,
633+ null ( ) ,
634+ NonNull :: from ( & mut data_size) ,
635+ )
636+ } ;
637+ Error :: from_os_status ( status) ?;
638+
639+ let n_ranges = data_size as usize / mem:: size_of :: < AudioValueRange > ( ) ;
640+ let mut ranges: Vec < AudioValueRange > = Vec :: with_capacity ( n_ranges) ;
641+ // SAFETY: AudioObjectGetPropertyData writes exactly n_ranges elements
642+ // (validated by data_size from the previous call) into the pre-allocated
643+ // buffer. AudioValueRange is a plain C struct with no drop semantics.
644+ let status = unsafe {
645+ ranges. set_len ( n_ranges) ;
646+ AudioObjectGetPropertyData (
647+ device_id,
648+ NonNull :: from ( & property_address) ,
649+ 0 ,
650+ null ( ) ,
651+ NonNull :: from ( & data_size) ,
652+ NonNull :: new ( ranges. as_mut_ptr ( ) ) . unwrap ( ) . cast ( ) ,
653+ )
654+ } ;
655+ Error :: from_os_status ( status) ?;
656+ Ok ( ranges)
657+ }
658+
659+ /// Get the transport type of a device.
660+ ///
661+ /// The returned value is one of the `kAudioDeviceTransportType*` constants
662+ /// (e.g. `kAudioDeviceTransportTypeUSB`, `kAudioDeviceTransportTypeBuiltIn`).
663+ pub fn get_device_transport_type ( device_id : AudioDeviceID ) -> Result < u32 , Error > {
664+ let property_address = AudioObjectPropertyAddress {
665+ mSelector : kAudioDevicePropertyTransportType,
666+ mScope : kAudioObjectPropertyScopeGlobal,
667+ mElement : kAudioObjectPropertyElementMaster,
668+ } ;
669+
670+ let mut transport_type: u32 = 0 ;
671+ let data_size = mem:: size_of :: < u32 > ( ) as u32 ;
672+ // SAFETY: AudioObjectGetPropertyData is a CoreAudio C API that reads
673+ // the transport type (a u32) into transport_type. All pointers passed
674+ // are valid stack references via NonNull::from. data_size matches the
675+ // size of the output buffer.
676+ let status = unsafe {
677+ AudioObjectGetPropertyData (
678+ device_id,
679+ NonNull :: from ( & property_address) ,
680+ 0 ,
681+ null ( ) ,
682+ NonNull :: from ( & data_size) ,
683+ NonNull :: from ( & mut transport_type) . cast ( ) ,
684+ )
685+ } ;
686+ Error :: from_os_status ( status) ?;
687+ Ok ( transport_type)
688+ }
689+
580690/// Changing the sample rate is an asynchronous process.
581691/// A RateListener can be used to get notified when the rate is changed.
582692pub struct RateListener {
0 commit comments