tags:

views:

31

answers:

2

I have a mixed C++/Objective-C project that uses AudioObjectGetPropertyDataSize to get the number of audio devices (such as USB headsets) plugged in. This API doesn't seem to work under certain conditions. For example, on 10.5 it will work but on 10.6 it won't detect when a new USB headset is plugged in.

I've pared down the problem to a small bit of code that reproduces the problem (it calls AudioObjectGetPropertyDataSize in a loop). The code will work on 10.6 (ie, it will detect when devices are plugged/unplugged) when its only linked against CoreAudio, but once you link against Foundation it will stop working.

I don't understand how linking to a framework can break code that otherwise works.

Here is the code (coreaudio-test.cpp):

#include <stdio.h>
#include <CoreAudio/AudioHardware.h>

int main(int argc, char **argv) {
    printf("Press <enter> to refresh device list> \n");
    while (1) {
        getchar();

        // get device count
        UInt32 dataSize = 0;
        AudioObjectPropertyAddress propertyAddress;
        propertyAddress.mSelector = kAudioHardwarePropertyDevices;
        propertyAddress.mScope    = kAudioObjectPropertyScopeGlobal;
        propertyAddress.mElement  = kAudioObjectPropertyElementMaster;
        OSStatus result =
            AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &dataSize);

        int count = -1;
        if (result == noErr) {
            count = dataSize / sizeof(AudioDeviceID);
        }
        printf("num devices= %d \n", count);
    }

    return 0;
}

And here is the Makefile:

LFLAGS= -framework CoreAudio

all: coreaudio-test coreaudio-test.broken

# create a test that works
coreaudio-test: coreaudio-test.cpp
    g++ -o $@ $^ $(LFLAGS)

# linking to foundation will break the test
coreaudio-test.broken: coreaudio-test.cpp
    g++ -o $@ $^ $(LFLAGS) -framework Foundation

Any thoughts on this bizarre behavior? (btw, I've also posted this question on the CoreAudio list.)

A: 

Related behavior, which is like 9 years ago someone reported: http://lists.apple.com/archives/coreaudio-api/2001/May/msg00021.html

Peter Lee
A: 

The CoreAudio List answered my question. We need to tell CoreAudio to allocate its own event-dispatching thread:

CFRunLoopRef theRunLoop =  NULL;
AudioObjectPropertyAddress theAddress = { kAudioHardwarePropertyRunLoop, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
AudioObjectSetPropertyData(kAudioObjectSystemObject, &theAddress, 0, NULL, sizeof(CFRunLoopRef), &theRunLoop);

I suspect what's happening is that when the program is linked to Foundation, CoreAudio assumes the main thread is acting as an event dispatcher loop (very common since Objective-C is usually used for GUI programs). When not linking against Foundation, I guess it figures that it needs to allocate its own event thread.

paleozogt