Note: These changes are only required for Tiger and later systems (10.4+). These changes will not work for previous releases. IntroductionPrior 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. Back to Top Creating a AudioBufferList to use with AudioDeviceReadListing 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;
}
Back to Top 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);
}
Back to Top Unregistering an AudioBufferListTo 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);
}
Back to Top Document Revision HistoryDate | Notes |
---|
2006-11-29 | fixed syntax error in example code | 2005-05-18 | How to use AudioBufferLists with AudioDeviceRead in Tiger and beyond. |
Posted: 2006-11-29
|