Re: Programmatic Aggregate Device issue on Intel Mac, not always completing initialisation?
Re: Programmatic Aggregate Device issue on Intel Mac, not always completing initialisation?
- Subject: Re: Programmatic Aggregate Device issue on Intel Mac, not always completing initialisation?
- From: Dan <email@hidden>
- Date: Tue, 8 Jul 2008 10:35:16 +0100
Hi -
Thanks for the response, and sorry for my omissions. To clarify:
2008/7/7 Jeff Moore <email@hidden>:
> So you don't say how what devices you are trying to aggregate. It would be
> helpful to know precisely what we are talking about here.
My test was by aggregating the built-in input and built-in output -
these were set as defaults in my Audio Midi Setup, and then were
retrieved via kAudioHardwarePropertyDefaultOutputDevice and
kAudioHardwarePropertyDefaultInputDevice.
> At any rate, I have a few questions:
> - You say that this only happens on Intel hardware, is that because you've
> tested your aggregation code on PPC and found it working fine or that you
> only are doing aggregation on Intel hardware because the PPC hardware is
> already generally full-duplex?
I thought it was the former but I can now correct myself, the same
behaviour actually happens on both. The difference is between the two
ways of launching our audio engine, not between PPC/Intel.
Test on PPC: FastTrack Pro (USB) as default input and builtin as
default output (set using Audio Midi Setup). When audio engine
("scsynth") is launched as separate process, all fine. When launched
within app process, the IOProc callback doesn't come.
Test on Intel: builtin mic as default input, builtin output as default
output. Same as above - as separate process, runs fine; from within
app process, never starts. The same situation occurs if I substitute
FastTrack Pro as either the input or output device.
> - What is the sample rate of the various sub-devices in the aggregation and
> which sub-device has been designated the master?
44.1 kHz for all. The output is set as the master (see code below).
> - Is the aggregate device you are creating private?
Yes. Situation doesn't seem to change if I don't make it private.
> - You say that it works fine in the case where you are using two separate
> processes, but you get the problem when you are using a single process. Can
> you elaborate on what the differences are between the environments?
I wish I could pin down the relevant differences for you. Both
versions use the same code to connect to the audio drivers. The
"separate process" version is a command-line executable so the audio
setup is triggered from the call to main(), while the in-process
version is triggered from some other function called by the user. But
both use the same structs and method calls to initialise, and as far
as I can tell there's no difference in the options received by the
CoreAudio code.
> - Are you carefully checking for errors from each and every API call? Are
> you seeing any?
I believe we're checking them all (code below). Seeing none.
> - Can you walk through how you are creating the aggregate device? The honest
> truth is that I don't have time to wade through your code and debug your
> problems for you so it is always much more helpful if you can boil down your
> code into snippets that you can post.
Here's the main snippet:
count = sizeof(mOutputDevice);
if(mOutputDevice == kAudioDeviceUnknown) {
//get the output device:
err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice,
&count, (void *) & mOutputDevice);
if (err != kAudioHardwareNoError) {
scprintf("get kAudioHardwarePropertyDefaultOutputDevice error
%4.4s\n", (char*)&err);
return false;
}
}
if (mInputDevice == kAudioDeviceUnknown) {
//get the input device
err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice,
&count, (void *) & mInputDevice);
if (err != kAudioHardwareNoError) {
scprintf("get kAudioHardwarePropertyDefaultInputDevice error
%4.4s\n", (char*)&err);
return false;
}
}
// create aggregate from default input and default output
if (mInputDevice!=mOutputDevice)
{
scprintf("creating Aggregate Device : SCAggregate\n");
CFStringRef s = CFStringCreateWithCString(NULL,
"com.apple.audio.CoreAudio", kCFStringEncodingUTF8);
AudioValueTranslation translation = {&s, sizeof(s), &coreaudioPlugin,
sizeof(coreaudioPlugin)};
unsigned long size = sizeof(translation);
err = AudioHardwareGetProperty(kAudioHardwarePropertyPlugInForBundleID,
&size, &translation);
CFRelease(s);
if (err != kAudioHardwareNoError)
{
scprintf("get kAudioHardwarePropertyPlugInForBundleID error
%4.4s\n", (char*)&err);
return false;
}
CFStringRef inputUID;
CFStringRef outputUID;
unsigned long UIDsize = sizeof(inputUID);
AudioDeviceGetProperty(mInputDevice, 0, 0,
kAudioDevicePropertyDeviceUID, &UIDsize, &inputUID);
AudioDeviceGetProperty(mOutputDevice, 0, 0,
kAudioDevicePropertyDeviceUID, &UIDsize, &outputUID);
CFMutableDictionaryRef dict =
CFDictionaryCreateMutable(kCFAllocatorDefault,0,
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFDictionarySetValue(dict, CFSTR("uid"), CFSTR("SCAggregate"));
CFDictionarySetValue(dict, CFSTR("name"), CFSTR("SCAggregate"));
int privateness = 1;
CFNumberRef privatenessRef = CFNumberCreate(kCFAllocatorDefault,
kCFNumberIntType, & privateness);
CFDictionarySetValue(dict, CFSTR("private"), privatenessRef);
CFRelease(privatenessRef);
unsigned long aggregateSize = sizeof(aggregateID);
AudioObjectPropertyAddress address = {
kAudioPlugInCreateAggregateDevice, kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyElementMaster };
err = AudioObjectGetPropertyData(coreaudioPlugin, &address,
sizeof(CFDictionaryRef), &dict, &aggregateSize, &aggregateID);
CFRelease(dict);
if (err != kAudioHardwareNoError)
{
scprintf("get kAudioPlugInCreateAggregateDevice error
%4.4s\n", (char*)&err);
return false;
}
// point at which to add the delay?
address.mSelector = kAudioAggregateDevicePropertyFullSubDeviceList;
CFMutableArrayRef subdevices = CFArrayCreateMutable(NULL,0,NULL);
CFArrayAppendValue(subdevices, (void *) inputUID);
CFArrayAppendValue(subdevices, (void *) outputUID);
err = AudioObjectSetPropertyData(aggregateID, &address, 0, NULL,
sizeof(subdevices), &subdevices);
CFRelease(subdevices);
if (err != kAudioHardwareNoError)
{
scprintf("get
kAudioAggregateDevicePropertyFullSubDeviceList error
%4.4s\n", (char*)&err);
return false;
}
address.mSelector = kAudioAggregateDevicePropertyMasterSubDevice;
err = AudioObjectSetPropertyData(aggregateID, &address, 0, NULL,
sizeof(outputUID), &outputUID);
if (err != kAudioHardwareNoError)
{
scprintf("get kAudioAggregateDevicePropertyMasterSubDevice error
%4.4s\n", (char*)&err);
return false;
}
mOutputDevice = mInputDevice = aggregateID;
}
> Finally, as you say there have been issues in the past with programmatically
> created aggregate devices. The usual way this has manifested is that a newly
> created aggregate device will seem to disappear for a moment after it is
> created. The way I've seen folks work around this issue is to do add some
> code to pause after creating the aggregate and then to look up the
> aggregate's AudioObjectID again using kAudioHardwarePropertyDeviceForUID.
> It's not a cure all by any means but it has helped a few developers.
OK, thanks. I added the following to the above code, at the point
marked "point at which to add the delay":
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, false);
address.mSelector = kAudioHardwarePropertyDeviceForUID;
err = AudioObjectGetPropertyData(coreaudioPlugin, &address,
sizeof(CFStringRef), CFSTR("SCAggregate"), &aggregateSize, &aggregateID);
if (err != kAudioHardwareNoError)
{
scprintf("get kAudioHardwarePropertyDeviceForUID error %s\n",
(char*)&err);
return false;
}
It results in the following error in all configurations:
get kAudioHardwarePropertyDeviceForUID error ?ohw
so I guess I'm doing something wrong...? Thanks for any further advice.
Dan
> At any rate, here's what the pseudo-code would look like:
>
> // create the aggregate device
> <... make a bunch of HAL calls ending with a new aggregate device getting
> created ...>
>
> // task the thread's run loop for 100 milliseconds to make sure that things
> got done
> CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, false);
>
> // use the UID of the aggregate device to look up it's AudioObjectID
> <... make a call to AudioObjectGetPropertyData on the system object using
> kAudioHardwarePropertyDeviceForUID ...>
>
> On Jul 3, 2008, at 7:42 AM, Dan wrote:
>
>> We're trying to add automatic creation of an Aggregate Device to an
>> open-source audio software called SuperCollider. The audio engine has
>> two ways of running: either as a separate process, or launched within
>> the main application process.
>>
>> Now, we have a patch for the Aggregate Device issue, and it works fine
>> in the first case (audio engine is separate). But in the second case
>> (audio engine "internal" to application) there seems to be a problem
>> on Intel Macs, where the IOProc callback for the audio device is never
>> called. It's the same code being called in each case, so maybe
>> something subtle is going on.
>>
>> If using a "real" device, or a user-created aggregate, there is no
>> problem starting the audio callback.
>>
>> I've seen in the archives of this list discussion of the occasional
>> bug in the programmatic interaction with Aggregate Device creation
>> etc. If you can suggest whether this is a bug or our own fault that
>> would be extremely helpful!
>
>
>
> --
>
> Jeff Moore
> Core Audio
> Apple
>
>
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Coreaudio-api mailing list (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden