输出设备的类型(耳机等)

时间:2017-09-06 12:45:30

标签: objective-c macos cocoa

出于用户界面的原因,我试图了解输出设备是什么类型的设备。例如,如果音频默认通过耳机播放,我需要显示耳机图标。

要获取此信息,我将AudioObjectGetPropertyData()kAudioDevicePropertyDataSource一起使用。这会为内部发言人返回'ispk',为耳机返回'hdpn'

如果我使用连接耳机的外部USB-C集线器,则代码无法正常工作。函数调用返回错误2003332927(即'who?')。

我能获得的唯一信息是UID为AppleUSBAudioEngine:Burr-Brown from TI:USB audio CODEC:14412000:2,名称为USB audio CODEC,制造商为Burr-Brown from TI

你知道我是否可以获得更多有用的信息吗?

这是我的测试代码:

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);
    }
}

1 个答案:

答案 0 :(得分:1)

我找到了答案。 kAudioDevicePropertyTransportType是我想要的。

Cmd点击Xcode中的枚举显示错误的文件AudioHarwareDeprecated.h而不是正确的AudioHardwareBase.h,所以我可以看到一些枚举值,但不是我想要的。

这是我用来检查设备的完整程序,以防任何人需要一些例子:

#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;
}