Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
coreaudio
A safe, idiomatic Rust wrapper around the macOS CoreAudio Hardware Abstraction Layer (HAL).
This crate provides typed access to audio devices, streams, and system-level audio objects, with compile-time guarantees around property access permissions and listener support.
Features
- Type-safe object model —
AudioObject<System>,AudioObject<Device>,AudioObject<Stream>,AudioObject<Process>andAudioObject<Tap>expose only the operations valid for each object type. - Compile-time property safety — Properties carry phantom types encoding their value type, owning object, read/write access, and listenability. Attempting to write a read-only property or listen to a non-listenable one is a compile error.
- Property builder methods — Properties that require an element (channel) or qualifier data expose
.for_element(n)and.with_qualifier(value)builder methods. Forgetting to call them is a compile error. - Property listeners — Subscribe to property changes with
add_listener, then poll withlatest(), drain withall_since_last_check(), or block withblock_until_change()/block_for_duration(). - Callback listeners —
add_listener_withhands every change straight to a closure on CoreAudio's notification thread, in order, with nothing blocking a thread to wait for it. - IO Procs — Register audio render callbacks on devices with
add_io_procand control playback withplay()/pause(). - Processes and taps — List the processes using CoreAudio, and (with the
process-tapfeature) tap one app's audio before it's mixed, read through a private aggregate device like any input. - Channel layouts — Read, write and listen to a device's speaker layout (
DEVICE_PREFERRED_CHANNEL_LAYOUT_OUTPUT), expand predefined layouts withlayout_for_tag, and get macOS's names for layouts and speakers. - Structured error handling — All CoreAudio
OSStatuscodes are mapped to a typedErrorKindenum with human-readable four-character-code formatting. - Format support — Rich enums for audio format IDs (Linear PCM, AAC variants, ALAC, AC3, Opus, MP3, etc.), format flags, sample formats, transport types, terminal types, and sample resampling utilities.
Requirements
- macOS (the crate is gated with
#[cfg(target_os = "macos")]) coreaudio-sysfor raw FFI bindingscore-foundationforCFStringhandling- macOS 14.2+ for process objects and taps. The optional
process-tapfeature addsobjc2-core-audioforCATapDescription; a binary built with it won't launch on older macOS
Quick start
use ;
Usage
Querying device properties
use ;
let system = default;
let device = system.current_device?;
let name: String = device.get_property?;
let uid: String = device.get_property?;
let sample_rate: f64 = device.get_property?;
let alive: bool = device.get_property?;
println!;
Setting writable properties
use ;
let system = default;
let device = system.current_device?;
device.set_property?;
device.set_property?;
Listening for property changes
use ;
use Duration;
let system = default;
let device = system.current_device?;
let listener = device.add_listener?;
// Non-blocking — returns the most recent change, or None
if let Some = listener.latest
// Blocking with timeout
match listener.block_for_duration
Calling a closure on every change
add_listener_with skips the channel: every change is handed straight to a closure, in the order CoreAudio reports it, along with any error reading the new value. The closure runs on CoreAudio's notification thread, so keep it short and hand the value on to wherever the work happens. The returned CallbackListener can be moved between threads, and dropping it unregisters the listener.
use ;
use mpsc;
let system = default;
let device = system.current_device?;
let = channel;
let listener = device.add_listener_with?;
// ... later
drop;
Tapping an app's audio
Every process that has used CoreAudio has a process object. With the process-tap feature, ProcessTap captures what a set of processes output, mixed down to stereo, before it reaches any device. A tap has no IO of its own; AggregateDevice::with_tap wraps it in a private aggregate device, which is read with an ordinary IO proc.
use ;
let system = default;
let music: = system
.processes?
.into_iter
.filter
.collect;
// Music goes silent while this is read, and plays normally again once it isn't.
let tap = stereo_mixdown?;
let aggregate = with_tap?;
let mut proc = aggregate.device.add_io_proc?;
proc.play?;
// Drop in this order: the IO proc, the aggregate, then the tap.
drop;
drop;
drop;
# Ok::
The aggregate is always private: only the creating process can see it, and macOS removes it if that process exits. Tapping needs the user's System Audio Recording permission (Privacy & Security). Without it a tap is still created but delivers silence, and a muting tap still mutes, so check the permission first.
Properties that require an element or qualifier
Some properties target a specific channel (element) or need a qualifier value before they can be used. Call .for_element(), .with_qualifier(), or both — in either order — to complete the property. Forgetting is a compile error.
Channel names are usually per direction, so read them with DEVICE_INPUT_ELEMENT_NAME or DEVICE_OUTPUT_ELEMENT_NAME; the global OBJECT_ELEMENT_NAME often reads empty on the same device.
use ;
let system = default;
let device = system.current_device?;
// e.g. "Channel 1" on an iPhone, "Mic/Line 1" on an audio interface
let name: String = device.get_property?;
use ;
let system = default;
let device = system.current_device?;
// Element-only: read the output volume of channel 1
let volume: f32 = device.get_property?;
// Element-only: mute output channel 1
device.set_property?;
// Element-only: read the active data source on channel 1
let source_id: u32 = device.get_property?;
// Element + qualifier: look up the name of that source
let source_name: String = device.get_property?;
println!;
Working with streams
use ;
let system = default;
let device = system.current_device?;
let streams = device.streams_with_scope?;
for stream in &streams
Registering an IO proc (audio callback)
use ;
let system = default;
let device = system.current_device?;
let mut io_proc = device.add_io_proc?;
io_proc.play?;
// ... render audio ...
io_proc.pause?;
io_proc.remove;
Available buffer sizes and sample rates
use ;
let buffer_range = device.get_property?;
println!;
let sample_rates = device.get_property?;
println!;
Property reference
Properties marked element require .for_element(channel) before use.
Properties marked qualifier require .with_qualifier(value) before use.
Properties marked both require both calls (in either order).
Object properties (all object types)
| Constant | Type | Access | Listenable | Extra |
|---|---|---|---|---|
OBJECT_BASE_CLASS |
u32 |
Read | No | — |
OBJECT_CLASS |
u32 |
Read | No | — |
OBJECT_OWNER |
u32 |
Read | No | — |
OBJECT_MODEL_NAME |
String |
Read | No | — |
OBJECT_MANUFACTURER |
String |
Read | No | — |
OBJECT_CREATOR |
String |
Read | No | — |
OBJECT_ELEMENT_NAME |
String |
Read | No | element |
OBJECT_ELEMENT_CATEGORY_NAME |
String |
Read | No | element |
OBJECT_ELEMENT_NUMBER_NAME |
String |
Read | No | element |
OBJECT_OWNED_OBJECTS |
Vec<u32> |
Read | No | qualifier: Vec<u32> |
Device properties
| Constant | Type | Access | Listenable | Extra |
|---|---|---|---|---|
DEVICE_NAME |
String |
Read | Yes | — |
DEVICE_UID |
String |
Read | No | — |
DEVICE_MODEL_UID |
String |
Read | No | — |
DEVICE_CONFIGURATION_APPLICATION |
String |
Read | No | — |
DEVICE_TRANSPORT_TYPE |
TransportType |
Read | No | — |
DEVICE_IS_ALIVE |
bool |
Read | Yes | — |
DEVICE_IS_RUNNING |
bool |
Read | Yes | — |
DEVICE_IS_HIDDEN |
bool |
Read | No | — |
DEVICE_CAN_BE_DEFAULT |
bool |
Read | No | — |
DEVICE_CAN_BE_DEFAULT_SYSTEM |
bool |
Read | No | — |
DEVICE_NOMINAL_SAMPLE_RATE |
f64 |
Read/Write | Yes | — |
DEVICE_AVAILABLE_SAMPLE_RATES |
Vec<SampleRateRange> |
Read | Yes | — |
DEVICE_BUFFER_FRAME_SIZE |
u32 |
Read/Write | Yes | — |
DEVICE_BUFFER_FRAME_SIZE_RANGE |
BufferFrameSizeRange |
Read | No | — |
DEVICE_USES_VARIABLE_BUFFER_FRAME_SIZES |
u32 |
Read | No | — |
DEVICE_INPUT_LATENCY |
u32 |
Read | No | — |
DEVICE_OUTPUT_LATENCY |
u32 |
Read | No | — |
DEVICE_SAFETY_OFFSET |
u32 |
Read | No | — |
DEVICE_CLOCK_DOMAIN |
u32 |
Read | No | — |
DEVICE_HOG_MODE |
HogMode |
Read/Write | Yes | — |
DEVICE_RELATED_DEVICES |
Vec<u32> |
Read | No | — |
DEVICE_PREFERRED_CHANNELS_FOR_STEREO |
ChannelPair |
Read/Write | No | — |
DEVICE_PREFERRED_CHANNEL_LAYOUT_INPUT |
ChannelLayout |
Read/Write | Yes | — |
DEVICE_PREFERRED_CHANNEL_LAYOUT_OUTPUT |
ChannelLayout |
Read/Write | Yes | — |
DEVICE_PROCESSOR_OVERLOAD |
u32 |
Read | Yes | — |
DEVICE_IO_STOPPED_ABNORMALLY |
u32 |
Read | Yes | — |
DEVICE_IO_CYCLE_USAGE |
f32 |
Read/Write | No | — |
DEVICE_CLOCK_SOURCE |
u32 |
Read/Write | Yes | — |
DEVICE_CLOCK_SOURCES |
Vec<u32> |
Read | No | — |
DEVICE_CLOCK_SOURCE_NAME |
String |
Read | No | qualifier: u32 |
DEVICE_PLAY_THRU_DESTINATION |
u32 |
Read/Write | Yes | — |
DEVICE_PLAY_THRU_DESTINATIONS |
Vec<u32> |
Read | No | — |
DEVICE_PLAY_THRU_DESTINATION_NAME |
String |
Read | No | qualifier: u32 |
DEVICE_INPUT_ELEMENT_NAME |
String |
Read | No | element |
DEVICE_OUTPUT_ELEMENT_NAME |
String |
Read | No | element |
DEVICE_GLOBAL_VOLUME_SCALAR |
f32 |
Read/Write | Yes | element |
DEVICE_INPUT_VOLUME_SCALAR |
f32 |
Read/Write | Yes | element |
DEVICE_OUTPUT_VOLUME_SCALAR |
f32 |
Read/Write | Yes | element |
DEVICE_VOLUME_DECIBELS |
f32 |
Read/Write | Yes | element |
DEVICE_VOLUME_RANGE_DECIBELS |
DBRange |
Read | No | element |
DEVICE_VOLUME_SCALAR_TO_DECIBELS |
f32 |
Read | No | element |
DEVICE_VOLUME_DECIBELS_TO_SCALAR |
f32 |
Read | No | element |
DEVICE_SUB_VOLUME_SCALAR |
f32 |
Read/Write | Yes | element |
DEVICE_SUB_VOLUME_DECIBELS |
f32 |
Read/Write | Yes | element |
DEVICE_SUB_VOLUME_RANGE_DECIBELS |
DBRange |
Read | No | element |
DEVICE_SUB_VOLUME_SCALAR_TO_DECIBELS |
f32 |
Read | No | element |
DEVICE_SUB_VOLUME_DECIBELS_TO_SCALAR |
f32 |
Read | No | element |
DEVICE_STEREO_PAN |
f32 |
Read/Write | Yes | element |
DEVICE_STEREO_PAN_CHANNELS |
ChannelPair |
Read | No | element |
DEVICE_GLOBAL_MUTE |
bool |
Read/Write | Yes | element |
DEVICE_INPUT_MUTE |
bool |
Read/Write | Yes | element |
DEVICE_OUTPUT_MUTE |
bool |
Read/Write | Yes | element |
DEVICE_SUB_MUTE |
bool |
Read/Write | Yes | element |
DEVICE_SOLO |
bool |
Read/Write | Yes | element |
DEVICE_PHANTOM_POWER |
bool |
Read/Write | Yes | element |
DEVICE_PHASE_INVERT |
bool |
Read/Write | Yes | element |
DEVICE_CLIP_LIGHT |
bool |
Read/Write | Yes | element |
DEVICE_TALKBACK |
bool |
Read/Write | Yes | element |
DEVICE_LISTENBACK |
bool |
Read/Write | Yes | element |
DEVICE_JACK_IS_CONNECTED |
bool |
Read | Yes | element |
DEVICE_DATA_SOURCE |
u32 |
Read/Write | Yes | element |
DEVICE_DATA_SOURCES |
Vec<u32> |
Read | No | element |
DEVICE_DATA_SOURCE_NAME |
String |
Read | No | element + qualifier: u32 |
DEVICE_CHANNEL_NOMINAL_LINE_LEVEL |
u32 |
Read/Write | Yes | element |
DEVICE_CHANNEL_NOMINAL_LINE_LEVELS |
Vec<u32> |
Read | No | element |
DEVICE_CHANNEL_NOMINAL_LINE_LEVEL_NAME |
String |
Read | No | element + qualifier: u32 |
DEVICE_HIGH_PASS_FILTER_SETTING |
u32 |
Read/Write | Yes | element |
DEVICE_HIGH_PASS_FILTER_SETTINGS |
Vec<u32> |
Read | No | element |
DEVICE_HIGH_PASS_FILTER_SETTING_NAME |
String |
Read | No | element + qualifier: u32 |
Stream properties
| Constant | Type | Access | Listenable | Extra |
|---|---|---|---|---|
STREAM_NAME |
String |
Read | Yes | — |
STREAM_IS_ACTIVE |
bool |
Read | Yes | — |
STREAM_DIRECTION |
Scope |
Read | No | — |
STREAM_LATENCY |
u32 |
Read | Yes | — |
STREAM_VIRTUAL_FORMAT |
StreamDescription |
Read/Write | Yes | — |
STREAM_PHYSICAL_FORMAT |
StreamDescription |
Read/Write | Yes | — |
STREAM_AVAILABLE_VIRTUAL_FORMATS |
Vec<StreamRangedDescription> |
Read | Yes | — |
STREAM_AVAILABLE_PHYSICAL_FORMATS |
Vec<StreamRangedDescription> |
Read | Yes | — |
TERMINAL_TYPE |
TerminalType |
Read | No | — |
STARTING_CHANNEL |
u32 |
Read | No | — |
System properties
| Constant | Type | Access | Listenable | Extra |
|---|---|---|---|---|
SYSTEM_NAME |
String |
Read | No | — |
SYSTEM_DEVICES |
Vec<u32> |
Read | Yes | — |
SYSTEM_DEFAULT_INPUT |
u32 |
Read/Write | Yes | — |
SYSTEM_DEFAULT_OUTPUT |
u32 |
Read/Write | Yes | — |
SYSTEM_IS_INITING_OR_EXITING |
bool |
Read | No | — |
SYSTEM_SLEEPING_IS_ALLOWED |
bool |
Read/Write | Yes | — |
SYSTEM_UNLOADING_IS_ALLOWED |
bool |
Read/Write | No | — |
SYSTEM_HOG_MODE_IS_ALLOWED |
bool |
Read/Write | No | — |
SYSTEM_MIX_STEREO_TO_MONO |
bool |
Read/Write | No | — |
SYSTEM_POWER_HINT |
PowerHint |
Read/Write | No | — |
SYSTEM_PROCESS_IS_AUDIBLE |
bool |
Read/Write | Yes | — |
SYSTEM_PROCESS_IS_MASTER |
bool |
Read | No | — |
SYSTEM_USER_SESSION_IS_ACTIVE_OR_HEADLESS |
bool |
Read | Yes | — |
SYSTEM_USER_ID_CHANGED |
u32 |
Read/Write | Yes | — |
SYSTEM_SERVICE_RESTARTED |
u32 |
Read | Yes | — |
SYSTEM_DEFAULT_SYSTEM_OUTPUT |
u32 |
Read/Write | Yes | — |
SYSTEM_BOX_LIST |
Vec<u32> |
Read | Yes | — |
SYSTEM_CLOCK_DEVICE_LIST |
Vec<u32> |
Read | Yes | — |
SYSTEM_PLUGIN_LIST |
Vec<u32> |
Read | Yes | — |
SYSTEM_TAP_LIST |
Vec<u32> |
Read | Yes | — |
SYSTEM_PROCESS_OBJECT_LIST |
Vec<u32> |
Read | Yes | — |
SYSTEM_TRANSPORT_MANAGER_LIST |
Vec<u32> |
Read | No | — |
SYSTEM_TRANSLATE_UID_TO_DEVICE |
u32 |
Read | No | qualifier: String |
SYSTEM_TRANSLATE_UID_TO_BOX |
u32 |
Read | No | qualifier: String |
SYSTEM_TRANSLATE_UID_TO_CLOCK_DEVICE |
u32 |
Read | No | qualifier: String |
SYSTEM_TRANSLATE_BUNDLE_ID_TO_PLUGIN |
u32 |
Read | No | qualifier: String |
SYSTEM_TRANSLATE_BUNDLE_ID_TO_TRANSPORT_MANAGER |
u32 |
Read | No | qualifier: String |
Process properties
| Constant | Type | Access | Listenable | Extra |
|---|---|---|---|---|
PROCESS_PID |
i32 |
Read | No | — |
PROCESS_BUNDLE_ID |
String |
Read | No | — |
PROCESS_IS_RUNNING |
bool |
Read | Yes | — |
PROCESS_IS_RUNNING_INPUT |
bool |
Read | Yes | — |
PROCESS_IS_RUNNING_OUTPUT |
bool |
Read | Yes | — |
PROCESS_INPUT_DEVICES |
Vec<u32> |
Read | Yes | — |
PROCESS_OUTPUT_DEVICES |
Vec<u32> |
Read | Yes | — |
Tap properties
| Constant | Type | Access | Listenable | Extra |
|---|---|---|---|---|
TAP_UID |
String |
Read | No | — |
TAP_FORMAT |
StreamDescription |
Read | Yes | — |
Error handling
All fallible operations return Result<T, CoreAudioError>. The error type wraps a typed ErrorKind enum and the raw OSStatus code. You can match on the kind or inspect the four-character code string:
use ErrorKind;
match device.get_property
Supported audio formats
The FormatId enum covers Linear PCM, AAC (Standard, HE, HEv2, LD, ELD, ELDv2, ELD+SBR, Spatial), Apple Lossless, AC-3, Enhanced AC-3, APAC, AES3, A-Law, AMR, AMR-WB, Opus, and MP3. Unrecognised format IDs are preserved as FormatId::Unknown(u32).
Roadmap
- Dedicated
AudioObject<Clock>,AudioObject<Box>, andAudioObject<Tap>with unique methods - Add new wrappers and 'multi-properties' that combine multiple properties into one for things that shouldn't have to be seperate calls
- Properties for device sample format
Disclaimer
Apple's documentation on what properties can be listened to is pretty much non existant. Because of this, almost all writable properties have been made listenable and it will return an error if it turns out not to be.
If you know of any documentation or if a specific property is incorrectly set, please make an issue in the repository and I will fix it at my earliest convenience.
License
See LICENSE file for details.