For UI reasons, I'm trying to know what kind of device an output device is. For instance, if the audio is going by default through headphones I need to display a headphone icon.
To get this information, I'm using AudioObjectGetPropertyData()
with kAudioDevicePropertyDataSource
. This returns 'ispk'
for internal speaker, 'hdpn'
for headphones, etc.
The code doesn't work if I use an external USB-C hub to which I connect my headphones. The function calls returns error 2003332927 (i.e. 'who?'
).
The only information I can get is that the UID is AppleUSBAudioEngine:Burr-Brown from TI:USB audio CODEC:14412000:2
, the name is USB audio CODEC
and the manufacturer is Burr-Brown from TI
.
Do you know if I can get out any more useful information?
This is my test code:
static NSString *getDataSourceName(UInt32 dataSource)
{
switch (dataSource)
{
case 'ispk':
return @"internal speaker";
case 'espk':
return @"external speaker";
case 'hdpn':
return @"headphones";
default:
return [NSString stringWithFormat:@"unknown type %d", dataSource];
}
}
static void printDefaultOutputDeviceType()
{
// Get the default output device.
AudioDeviceID deviceID;
UInt32 defaultOutputPropSize = sizeof(AudioDeviceID);
AudioObjectPropertyAddress defaultOutputAddress = {
kAudioHardwarePropertyDefaultOutputDevice,
kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyElementMaster,
};
OSStatus status = AudioObjectGetPropertyData(kAudioObjectSystemObject,
&defaultOutputAddress,
0,
NULL,
&defaultOutputPropSize,
&deviceID);
NSCAssert(status == noErr, @"Cannot get default output device: %d", status);
// Get the data source type for the output device.
AudioObjectPropertyAddress dataSourceAddress = {
kAudioDevicePropertyDataSource,
kAudioObjectPropertyScopeOutput,
kAudioObjectPropertyElementMaster,
};
UInt32 dataSource;
UInt32 dataSourcePropSize = sizeof(dataSource);
status = AudioObjectGetPropertyData(deviceID,
&dataSourceAddress,
0,
NULL,
&dataSourcePropSize,
&dataSource);
if (status == noErr) {
NSLog(@"Audio device with ID %d is: %@",
deviceID,
getDataSourceName(dataSource));
}
else {
NSLog(@"Cannot get type for device with ID %d: %d",
deviceID,
status);
}
}
I found the answer. kAudioDevicePropertyTransportType
is what I wanted.
Cmd-clicking the enum in Xcode was showing me the wrong file AudioHarwareDeprecated.h
instead of the correct AudioHardwareBase.h
, so I could see some enum values, but not the one I wanted.
This is the full program I'm using to inspect devices in case anybody needs some example:
// Compile with: clang -framework Foundation -framework CoreAudio -lobjc
#import <Foundation/Foundation.h>
#import <CoreAudio/CoreAudio.h>
static void logMessage(NSString *format, ...)
{
va_list args;
va_start(args, format);
NSMutableString *formattedString = [[NSMutableString alloc] initWithFormat:format
arguments:args];
va_end(args);
if (![formattedString hasSuffix:@"\n"]) {
[formattedString appendString:@"\n"];
}
NSData *formattedData = [formattedString dataUsingEncoding:NSUTF8StringEncoding];
[NSFileHandle.fileHandleWithStandardOutput writeData:formattedData];
}
/*
* Format a 32-bit code (for instance OSStatus) into a string.
*/
static char *codeToString(UInt32 code)
{
static char str[5] = { '\0' };
UInt32 swapped = CFSwapInt32HostToBig(code);
memcpy(str, &swapped, sizeof(swapped));
return str;
}
static NSString *formatStatusError(OSStatus status)
{
if (status == noErr) {
return [NSString stringWithFormat:@"No error (%d)", status];
}
return [NSString stringWithFormat:@"Error \"%s\" (%d)",
codeToString(status),
status];
}
static void assertStatusSuccess(OSStatus status)
{
if (status != noErr)
{
logMessage(@"Got error %u: '%s'\n", status, codeToString(status));
abort();
}
}
static inline AudioObjectPropertyAddress makeGlobalPropertyAddress(AudioObjectPropertySelector selector)
{
AudioObjectPropertyAddress address = {
selector,
kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyElementMaster,
};
return address;
}
static NSString *getStringProperty(AudioDeviceID deviceID,
AudioObjectPropertySelector selector)
{
AudioObjectPropertyAddress address = makeGlobalPropertyAddress(selector);
CFStringRef prop;
UInt32 propSize = sizeof(prop);
OSStatus status = AudioObjectGetPropertyData(deviceID,
&address,
0,
NULL,
&propSize,
&prop);
if (status != noErr) {
return formatStatusError(status);
}
return (__bridge_transfer NSString *)prop;
}
static NSString *getURLProperty(AudioDeviceID deviceID,
AudioObjectPropertySelector selector)
{
AudioObjectPropertyAddress address = makeGlobalPropertyAddress(selector);
CFURLRef prop;
UInt32 propSize = sizeof(prop);
OSStatus status = AudioObjectGetPropertyData(deviceID,
&address,
0,
NULL,
&propSize,
&prop);
if (status != noErr) {
return formatStatusError(status);
}
NSURL *url = (__bridge_transfer NSURL *)prop;
return url.absoluteString;
}
static NSString *getCodeProperty(AudioDeviceID deviceID,
AudioObjectPropertySelector selector)
{
AudioObjectPropertyAddress address = makeGlobalPropertyAddress(selector);
UInt32 prop;
UInt32 propSize = sizeof(prop);
OSStatus status = AudioObjectGetPropertyData(deviceID,
&address,
0,
NULL,
&propSize,
&prop);
if (status != noErr) {
return formatStatusError(status);
}
return [NSString stringWithFormat:@"%s (%d)",
codeToString(prop),
prop];
}
static NSUInteger getChannelCount(AudioDeviceID deviceID,
AudioObjectPropertyScope scope)
{
AudioObjectPropertyAddress address = {
kAudioDevicePropertyStreamConfiguration,
scope,
kAudioObjectPropertyElementMaster,
};
AudioBufferList streamConfiguration;
UInt32 propSize = sizeof(streamConfiguration);
OSStatus status = AudioObjectGetPropertyData(deviceID,
&address,
0,
NULL,
&propSize,
&streamConfiguration);
assertStatusSuccess(status);
NSUInteger channelCount = 0;
for (NSUInteger i = 0; i < streamConfiguration.mNumberBuffers; i++)
{
channelCount += streamConfiguration.mBuffers[i].mNumberChannels;
}
return channelCount;
}
static NSString *getSourceName(AudioDeviceID deviceID,
AudioObjectPropertyScope scope)
{
AudioObjectPropertyAddress address = {
kAudioDevicePropertyDataSource,
scope,
kAudioObjectPropertyElementMaster,
};
UInt32 sourceCode;
UInt32 propSize = sizeof(sourceCode);
OSStatus status = AudioObjectGetPropertyData(deviceID,
&address,
0,
NULL,
&propSize,
&sourceCode);
if (status != noErr) {
return formatStatusError(status);
}
return [NSString stringWithFormat:@"%s (%d)",
codeToString(sourceCode),
sourceCode];
}
static void inspectDevice(AudioDeviceID deviceID)
{
logMessage(@"Device %d", deviceID);
logMessage(@" - UID: %@", getStringProperty(deviceID, kAudioDevicePropertyDeviceUID));
logMessage(@" - Model UID: %@", getStringProperty(deviceID, kAudioDevicePropertyModelUID));
logMessage(@" - Name: %@", getStringProperty(deviceID, kAudioDevicePropertyDeviceNameCFString));
logMessage(@" - Manufacturer: %@", getStringProperty(deviceID, kAudioDevicePropertyDeviceManufacturerCFString));
logMessage(@" - Input channels: %@", @(getChannelCount(deviceID, kAudioObjectPropertyScopeInput)));
logMessage(@" - Output channels: %@", @(getChannelCount(deviceID, kAudioObjectPropertyScopeOutput)));
logMessage(@" - Input source: %@", getSourceName(deviceID, kAudioObjectPropertyScopeInput));
logMessage(@" - Output source: %@", getSourceName(deviceID, kAudioObjectPropertyScopeOutput));
logMessage(@" - Transport type: %@", getCodeProperty(deviceID, kAudioDevicePropertyTransportType));
logMessage(@" - Icon: %@", getURLProperty(deviceID, kAudioDevicePropertyIcon));
}
static void inspectDeviceForSelector(AudioObjectPropertySelector selector)
{
AudioDeviceID deviceID;
UInt32 propSize = sizeof(AudioDeviceID);
AudioObjectPropertyAddress address = makeGlobalPropertyAddress(selector);
OSStatus status = AudioObjectGetPropertyData(kAudioObjectSystemObject,
&address,
0,
NULL,
&propSize,
&deviceID);
assertStatusSuccess(status);
inspectDevice(deviceID);
}
static void inspectAllDevices()
{
// Check the number of devices.
AudioObjectPropertyAddress address = makeGlobalPropertyAddress(kAudioHardwarePropertyDevices);
UInt32 devicesDataSize;
OSStatus status = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject,
&address,
0,
NULL,
&devicesDataSize);
assertStatusSuccess(status);
// Get the devices.
int count = devicesDataSize / sizeof(AudioDeviceID);
AudioDeviceID deviceIDs[count];
status = AudioObjectGetPropertyData(kAudioObjectSystemObject,
&address,
0,
NULL,
&devicesDataSize,
deviceIDs);
assertStatusSuccess(status);
// Inspect them.
for (UInt32 i = 0; i < count; i++) {
inspectDevice(deviceIDs[i]);
}
}
int main(int argc, const char * argv[])
{
@autoreleasepool {
logMessage(@"==== ALL DEVICES ====");
inspectAllDevices();
logMessage(@"");
logMessage(@"==== DEFAULT INPUT DEVICE ====");
inspectDeviceForSelector(kAudioHardwarePropertyDefaultInputDevice);
logMessage(@"");
logMessage(@"==== DEFAULT OUTPUT DEVICE ====");
inspectDeviceForSelector(kAudioHardwarePropertyDefaultOutputDevice);
logMessage(@"");
}
return 0;
}