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: Jeff Moore <email@hidden>
- Date: Tue, 08 Jul 2008 12:00:13 -0700
On Jul 8, 2008, at 2:32 AM, Dan Stowell wrote:
Hi -
Thanks for the response, and sorry for my omissions. To clarify:
2008/7/7 Jeff Moore <email@hidden>:
- Is the aggregate device you are creating private?
Yes. Situation doesn't seem to change if I don't make it private.
Interesting. The other folks that have had problems generally had them
with their private devices. It would seem then that your issues may be
something different entirely.
- 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.
Not to belabor the obvious, but something has to be different. Given
everything I've heard so far, figuring this out seems to me to be the
most important thing you can do to learn about this problem. There's
not much more I can do for you until you nail this down I think.
- 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;
}
}
Getting the default input and output devices. Seems fine. About the
only comment I'd make is that you really should stop using the older
APIs and should switch over to using the AudioObject-based APIs. Not
that it will change anything about this problem, but it will help
future-proof your code.
// 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?
The preceding code looks OK, although I'd really advise you to use the
constants in AudioHardware.h for the dictionary keys.
And this is also where you'd put the delay and the re-fetching of the
newly created aggregate device's AudioOjbectID that I mentioned in my
previous email. Note that you'd be using the re-fetched ID from this
point on.
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;
}
Setting the sub-device list can be done in either as part of the
creation dictionary or done after the fact.
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.
As it's name implies, kAudioHardwarePropertyDeviceForUID is a property
of the system object, not the plug-in object. That's why you are
getting the error here.
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