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 AudioDeviceRead in Mac OS 10.4
In order to support a wider variety of data formats, the structure of the data buffers used by the HAL (Hardware Abstraction Layer) and IOAudio-based drivers has changed. As a consequence, code that used the HAL API, AudioDeviceRead
(), and the property kAudioDevicePropertyRegisterBufferList
, needs to be updated to work correctly on Mac OS X Tiger.
Introduction
Prior to Tiger, the AudioBufferList
used with these APIs consisted of AudioBuffers that point to a block of unstructured memory to be used for reading the data. Starting with Tiger, this changes such that each AudioBuffer
in the AudioBufferList
points at an IOAudioBufferDataDescriptor
structure. This structure is defined in <IOKit/audio/IOAudioTypes.h> as follows:
Listing 1 IOAudioBufferDataDescriptor
structure as defined in <IOKit/audio/IOAudioTypes.h>
typedef struct _IOAudioBufferDataDescriptor { |
UInt32 fActualDataByteSize; |
UInt32 fActualNumSampleFrames; |
UInt32 fTotalDataByteSize; |
UInt32 fNominalDataByteSize; |
Byte fData[kVariableLengthArray]; |
} IOAudioBufferDataDescriptor; |
This change impacts registering the AudioBufferList
to be used with an AudioDevice. To register an AudioBufferList
to be used with an AudioDevice, the property kAudioDevicePropertyRegisterBufferList
must be used with the AudioDeviceSetProperty
method. The kAudioDevicePropertyRegisterBufferList
property allows clients to register a fully populated AudioBufferList
that matches the topology described by stream configuration of the audio device.
In addition to setting up the AudioBufferList
, each IOAudioBufferDataDescriptor
has to be fully filled out prior to calling AudioDeviceSetProperty
(..., kAudioDevicePropertyRegisterBufferList
, ...). The fActualDataByteSize
field is set to the number of bytes out of fData that are used for a typical read. The fActualNumSampleFrames
field is set to the number of sample frames used for a typical read. The fTotalDataByteSize
field is set to the total number of bytes in the fData field. The fNominalDataByteSize
field is set to the number of bytes out of fData that are used for a typical read. Note that fActualDataByteSize
and fNominalDataByteSize
are always the same value here. Typically, fTotalDataByteSize
is also the same as fActualDataByteSize
and fNominalDataByteSize
, but can be larger. This is useful in situations where the application wants to read more or less data than typical to facilitate synchronization or reading variable sized data, such as a packet of compressed data.
Creating a AudioBufferList to use with AudioDeviceRead
Listing 2 Registering an AudioBufferList
for AudioDeviceRead
// HAL's main header |
#include <CoreAudio/AudioHardware.h> |
// need this for IOAudioBufferDataDescriptor |
#include <IOKit/audio/IOAudioTypes.h> |
// from /Developer/Examples/CoreAudio/PublicUtility |
#include "CAAudioBufferList.h" |
#include "CADebugMacros.h" |
// need this for memset |
#include <string.h> |
// need to this to use vm_allocate |
#include <mach/mach.h> |
AudioBufferList* MakeABLForAudioDeviceRead(AudioDeviceID inDevice, |
UInt32 inNumberFramesToRead, UInt32 inNumberStreams, |
AudioStreamBasicDescription inStreamFormats[]) |
{ |
// allocate the ABL |
AudioBufferList* theABL = CAAudioBufferList::Create(inNumberStreams); |
theABL->mNumberBuffers = inNumberStreams; |
UInt32 theABLSize = CAAudioBufferList::CalculateByteSize(inNumberStreams); |
// initialize each AudioBuffer in theABL |
for(UInt32 theIndex = 0; theIndex < inNumberStreams; ++theIndex) |
{ |
// we have to calculate how big the fData part of |
// the IOAudioBufferDataDescriptor is |
// (note that this code assumes linear PCM data) |
UInt32 theDataByteSize = inNumberFramesToRead * inStreamFormats[theIndex].mBytesPerPacket; |
// we can now calculate how much memory we need to |
// allocate to hold the entire IOAudioBufferDataDescriptor |
UInt32 theAllocationByteSize = offsetof(IOAudioBufferDataDescriptor, fData) + theDataByteSize; |
// allocate the IOAudioBufferDataDescriptor |
IOAudioBufferDataDescriptor* theDescriptor = NULL; |
kern_return_t theKernelError = vm_allocate(mach_task_self(), |
vm_address_t*)&theDescriptor, |
theAllocationByteSize, |
VM_FLAGS_ANYWHERE); |
AssertNoKernelError(theKernelError, "vm_allocate failed"); |
// the three byte size fields of the IOAudioBufferDataDescriptor |
// refer to the size of the fData field |
theDescriptor->fActualDataByteSize = theDataByteSize; |
theDescriptor->fTotalDataByteSize = theDataByteSize; |
theDescriptor->fNominalDataByteSize = theDataByteSize; |
// the actual number of frames field is the number of frames to read |
theDescriptor->fActualNumSampleFrames = inNumberFramesToRead; |
// clear out fData |
memset(&(theDescriptor->fData[0]), 0, theDataByteSize); |
// start filling the AudioBuffer out with the number of channels |
theABL->mBuffers[theIndex].mNumberChannels = inStreamFormats[theIndex].mChannelsPerFrame; |
// note that the data byte size in the AudioBuffer refers to |
// the entire size of the IOAudioBufferDataDescriptor |
theABL->mBuffers[theIndex].mDataByteSize = theAllocationByteSize; |
// and the data pointer points at the IOAudioBufferDataDescriptor |
theABL->mBuffers[theIndex].mData = theDescriptor; |
} |
// register the ABL with the device |
OSStatus theError = AudioDeviceSetProperty(inDevice, |
NULL, |
0, |
true, |
kAudioDevicePropertyRegisterBufferList, |
theABLSize, |
theABL); |
AssertNoError(theError, "couldn't register the ABL"); |
return theABL; |
} |
Preparing AudioBufferList for AudioDeviceRead
The AudioBufferList
needs to be prepared prior to passing it to AudioDeviceRead
(). Each IOAudioBufferDataDescriptor
in the AudioBufferList
must have its fields properly filled out to indicate how much data is to be read. The fActualDataByteSize
field is set to the number of bytes out of fData that are to be read. The fActualNumSampleFrames
field is set to the number of sample frames that are to be read. The fTotalDataByteSize
field is set to the total number of bytes in the fData field. The fNominalDataByteSize
field is set to the number of bytes out of fData that are used for a typical read (the same as was used for registering the AudioBufferList
). Note that an application can use the fActualDataByteSize
and fActualNumSampleFrames
can be used to vary the amount of data that gets read. When AudioDeviceRead
returns, the data that was read will be in the fData field of each IOAudioBufferDataDescriptor
in the AudioBufferList
.
Listing 3 Preparing AudioBufferList
for AudioDeviceRead
// HAL's main header |
#include <CoreAudio/AudioHardware.h> |
// need this for IOAudioBufferDataDescriptor |
#include <IOKit/audio/IOAudioTypes.h> |
// from /Developer/Examples/CoreAudio/PublicUtility |
#include "CADebugMacros.h" |
OSStatus PrepareABLAndCallAudioDeviceRead(AudioBufferList* inABL, |
AudioTimeStamp* inTimeToReadFrom, |
AudioDeviceID inDevice, |
UInt32 inNumberFramesToRead, |
UInt32 inNumberStreams, |
AudioStreamBasicDescription inStreamFormats[]) |
{ |
// go through each AudioBuffer and update the |
// values in its IOAudioBufferDataDescriptor |
for(UInt32 theIndex = 0; theIndex < inNumberStreams; ++theIndex) |
{ |
// get the IOAudioBufferDataDescriptor |
IOAudioBufferDataDescriptor* theDescriptor = |
(IOAudioBufferDataDescriptor*)inABL->mBuffers[theIndex].mData; |
// adjust the actual size fields |
// (note that this code assumes that the other fields were |
// filled out previously since they don't change) |
theDescriptor->fActualDataByteSize = inNumberFramesToRead * inStreamFormats[theIndex].mBytesPerFrame; |
theDescriptor->fActualNumSampleFrames = inNumberFramesToRead; |
} |
return AudioDeviceRead(inDevice, inTimeToReadFrom, inABL); |
} |
Unregistering an AudioBufferList
To unregister an AudioBufferList
that will no longer be used with AudioDeviceRead
, you must deallocate the IOAudioBufferDataDescriptor
as well at the AudioBufferList
.
Listing 4 Unregistering an AudioBufferList
void DestroyABLForAudioDeviceRead(AudioBufferList* inABL, |
AudioDeviceID inDevice, |
UInt32 inNumberFramesToRead, |
UInt32 inNumberStreams, |
AudioStreamBasicDescription inStreamFormats[]) |
{ |
// this routine assumes that inABL is already filled out |
// with the same values as when it was returned from MakeABLForAudioDeviceRead() |
// unregister the ABL with the HAL |
UInt32 theABLSize = CAAudioBufferList::CalculateByteSize(inNumberStreams); |
OSStatus theError = AudioDeviceGetProperty(inDevice, |
0, |
true, |
kAudioDevicePropertyRegisterBufferList, |
&theABLSize, |
inABL); |
AssertNoError(theError, "couldn't unregister the ABL"); |
// go through each AudioBuffer in theABL |
for(UInt32 theIndex = 0; theIndex < inNumberStreams; ++theIndex) |
{ |
// deallocate the IOAudioBufferDataDescriptor |
vm_deallocate(mach_task_self(), |
(vm_address_t) inABL->mBuffers[theIndex].mData, |
inABL->mBuffers[theIndex].mDataByteSize); |
} |
// deallocate the ABL |
CAAudioBufferList::Destroy(inABL); |
} |
Document Revision History
Date | Notes |
---|---|
2006-11-29 | fixed syntax error in example code |
2005-05-18 | New document that how to use AudioBufferLists with AudioDeviceRead in Tiger and beyond. |
Copyright © 2006 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2006-11-29