2

I've spent the last week on an unplanned excursion into the depths of the Macintosh sound system after NSSound proved to be unequal to the task.. I finally got my file playing with Audio Queue Services and now there's only one little thing left to do: switch output devices.

Unfortunately, it seems that I'm either doing something wrong or the device UID CFStringRef that you're supposed to pass is not the one that Core Audio gives out..

The piece of code below retrieves the standard output device (to which the Audio Queue will be playing by default anyway, but it refuses to change devices:

UInt32 thePropSize;
AudioDeviceID defaultAudioDevice;

OSStatus result = noErr;

// get the device list  
AudioObjectPropertyAddress thePropertyAddress = { kAudioHardwarePropertyDefaultOutputDevice, kAudioObjectPropertyScopeGlobal, 
    kAudioObjectPropertyElementMaster };

result = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &thePropertyAddress, 0, NULL, &thePropSize);

result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &thePropertyAddress, 0, NULL, &thePropSize, &defaultAudioDevice);

CFStringRef theDeviceName;      

// get the device name
thePropSize = sizeof(CFStringRef);
thePropertyAddress.mSelector = kAudioObjectPropertyName;
thePropertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
thePropertyAddress.mElement = kAudioObjectPropertyElementMaster;

// get the name of the device
result = AudioObjectGetPropertyData( (AudioObjectID)defaultAudioDevice, 
                                    &thePropertyAddress, 0, NULL, &thePropSize, &theDeviceName);

// get the uid of the device
CFStringRef theDeviceUID;
thePropertyAddress.mSelector = kAudioDevicePropertyDeviceUID;
result = AudioObjectGetPropertyData( (AudioObjectID)defaultAudioDevice, 
                                    &thePropertyAddress, 0, NULL, &thePropSize, &theDeviceUID);


result = AudioQueueSetProperty( playerState.mQueue,
                                        kAudioQueueProperty_CurrentDevice,
                                        &theDeviceUID,
                                        sizeof(theDeviceUID));

If the queue is playing, I get an error kAudioQueueErr_InvalidRunState, telling me that you can't set this property while the queue is playing. If the queue is not playing, I get the -50 parameter error.

I'm I doing something wrong with the pointers? or is there a different device uid somewhere!?

Any help would be greatly appreciated.

Frank R.
  • 2,328
  • 1
  • 24
  • 44
  • What are you trying to do that NSSound couldn't do? And what did AudioObjectGetPropertyData return for the DeviceUID property? – Peter Hosey Aug 27 '10 at 19:45
  • NSSound looping feature doesn't work very well. It inserts a slight but audible pause between each repetition, which in my context isn't acceptable. The device uids on my machine are: "AppleHDAEngineOutput:1B,0,1,3:0", "AppleHDAEngineOutput:1B,0,1,4:1", "AppleHDAEngineOutput:1B,0,1,5:2" These work fine with [NSSound setPlaybackDeviceIdentifier:]. – Frank R. Aug 27 '10 at 21:50
  • Did you look those up in an application somewhere, or did you obtain them by inspecting the return value of AudioObjectGetPropertyData? I ask because it's possible that one of the earlier functions is returning an error and not a useful value, which is a good way to have invalid parameters to the last function. – Peter Hosey Aug 28 '10 at 01:14
  • The code I've posted is a simplified version, all status codes are noErr, the values are inspected and printed out elsewhere. They are the same device ids that work just fine when fed into NSSound. Essentially, either they aren't the right values to pass to the audio queue property or I don't pass them the right way.. unless of course, it's just a buggy API which given the state of the non-documentation is a real possibility.. – Frank R. Aug 28 '10 at 18:02

1 Answers1

3

I've figured out a solution and I'm posting it here for the archives:

Apple Developer Services tested my code in a separate project and it ran just great for them straight away.. the difference was that they set the device uid without all the tedious audio buffers and volume setup, etc.. I moved the device uid change from the end of the queue setup to immediately after the queue is created and bingo! it works just fine.

I'm not 100% certain but I think you just can't change the device after you set the gain for the queue due to some hardware driver restrictions. "parameter error" doesn't seem to quite point in that direction but I think a "too late to change device" error would be more appropriate.

Frank R.
  • 2,328
  • 1
  • 24
  • 44