Retired Document
Important: This document may not represent best practices for current development. Links to downloads and other resources may no longer be valid.
Using NSSound with CoreAudio on Mac OS 10.3.x
Q:
I am using NSSound
with CoreAudio on Mac OS 10.3.x, why am I getting NULL data in my IOProc callback?
A: Some applications might find it useful to use Application Kit framework's NSSound
and Core Audio within the same application. A developer must take notice that NSSound
might interfere with Core Audio's HAL (Hardware Abstraction Layer) IOProc.
On MacOS 10.3.x systems, using the NSSound
class in the same process as a Core Audio framework IOProc callback can yield NULL input buffers in the IOProc callback (rdar://4106975). This bug affects applications that add a Core Audio IOProc on the same device that NSSound
is using for its sound output: the Default Output Audio Device. Applications that make calls to NSSound
before adding a Core Audio IOProc to the audio device, will get NULL input buffers in the Core Audio IOProc.
To workaround this problem, the process needs to make its calls to AudioDeviceAddIOProc
before making any calls to NSSound
.
Listing 1 Sample problem
#import <Cocoa/Cocoa.h> |
#import <CoreAudio/CoreAudio.h> |
OSStatus recordIOProc ( AudioDeviceID inDevice, |
const AudioTimeStamp* inNow, |
const AudioBufferList* inInputData, |
const AudioTimeStamp* inInputTime, |
AudioBufferList* outOutputData, |
const AudioTimeStamp* inOutputTime, |
void* inClientData ) |
{ |
if ( inInputData ) |
{ |
if ( inInputData->mBuffers[0].mData == NULL ) |
NSLog (@"CoreAudio IOProc is being given NULL input buffers"); |
} |
return noErr; |
} |
int main() |
{ |
NSAutoreleasePool * pool = [NSAutoreleasePool new]; |
AudioDeviceID device; |
UInt32 theSize = sizeof(AudioDeviceID); |
verify_noerr( AudioHardwareGetProperty ( kAudioHardwarePropertyDefaultInputDevice, &theSize, & device ) ); |
NSSound *sound = [[NSSound alloc] initWithContentsOfFile: |
@"/System/Library/Sounds/Submarine.aiff" byReference:NO]; |
[sound play]; |
verify_noerr( AudioDeviceAddIOProc ( device, recordIOProc, NULL ) ); |
verify_noerr( AudioDeviceStart ( device, recordIOProc ) ); |
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:2]]; |
verify_noerr( AudioDeviceStop ( device, recordIOProc ) ); |
verify_noerr( AudioDeviceRemoveIOProc ( device, recordIOProc ) ); |
[sound release]; |
[pool release]; |
return 0; |
} |
When running the above sample code, you will see "CoreAudio IOProc is being given NULL input buffers" printed many many times in the program's standard output (stdout).
To workaround this problem, the process needs to make its calls to AudioDeviceAddIOProc
before making any calls to NSSound
. The following modified version of the above sample code shows this:
Listing 2 Workaround
#import <Cocoa/Cocoa.h> |
#import <CoreAudio/CoreAudio.h> |
OSStatus recordIOProc ( AudioDeviceID inDevice, |
const AudioTimeStamp* inNow, |
const AudioBufferList* inInputData, |
const AudioTimeStamp* inInputTime, |
AudioBufferList* outOutputData, |
const AudioTimeStamp* inOutputTime, |
void* inClientData ) |
{ |
if ( inInputData ) |
{ |
if ( inInputData->mBuffers[0].mData == NULL ) |
NSLog (@"CoreAudio IOProc is being given NULL input buffers"); |
} |
return noErr; |
} |
int main() |
{ |
NSAutoreleasePool * pool = [NSAutoreleasePool new]; |
AudioDeviceID device; |
UInt32 theSize = sizeof(AudioDeviceID); |
verify_noerr( AudioHardwareGetProperty ( kAudioHardwarePropertyDefaultInputDevice, &theSize, & device ) ); |
//Call this *before* using NSSound |
verify_noerr( AudioDeviceAddIOProc ( device, recordIOProc, NULL ) ); |
NSSound *sound = [[NSSound alloc] initWithContentsOfFile: |
@"/System/Library/Sounds/Submarine.aiff" byReference:NO]; |
[sound play]; |
verify_noerr( AudioDeviceStart ( device, recordIOProc ) ); |
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:2]]; |
verify_noerr( AudioDeviceStop ( device, recordIOProc ) ); |
verify_noerr( AudioDeviceRemoveIOProc ( device, recordIOProc ) ); |
[sound release]; |
[pool release]; |
return 0; |
} |
Notice that the only change that's been made here is that we've moved the AudioDeviceAddIOProc
call to before the NSSound
calls. When running this sample code, recordIOProc will not print out the string "CoreAudio IOProc is being given NULL input buffers", indicating that it is receiving valid input buffers.
Note that it is not necessary to call AudioDeviceStart
before any calls to NSSound
. It is only necessary to call AudioDeviceAddIOProc
.
Document Revision History
Date | Notes |
---|---|
2006-03-29 | Formatting changes only. |
2005-06-30 | New document that workaround the NULL input buffers in a Core Audio IOProc callback when using NSSound by using AudioDeviceAddIOProc |
Copyright © 2006 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2006-03-29