AUSampler - Adding Instrument Information and Loop Points to Core Audio Files
Q: How can I add Music Instrument Information and Sample Loop Points to Core Audio (.caf) files so I can use them with the AUSampler Audio Unit?
A: The Core Audio File Format Specification provides two chunk types to specify instrument data including looping Information to a .caf file. The Instrument chunk type and the Region chunks work together allowing you to build a list of Region descriptions containing markers annotating position(s) in an audio file which are then referred to by the Instrument chunk to define an instrument.
For more information see the Apple Core Audio Format Specification and AudioFile.h
which is part of the AudioToolbox.framework
.
Listing 1 demonstrates how to add an Instrument chunk with a single sustain loop region as well as specifying a bass, low and high pitch range when provided with an open Core Audio File (.caf) file.
Listing 1
OSStatus SetupInstrument(AudioFileID inFileID, |
UInt64 inFileLength, // length in frames (samples) |
UInt8 inBaseNote, // MIDI note (0-127) |
UInt8 inLowNote, // MIDI note (0-127, <= inBaseNote) |
UInt8 inHighNote) // MIDI note (0-127, >= inBaseNote) |
{ |
const UInt32 numRegionChunks = 1; // For our sustain loop, one region |
const UInt32 numMarkers = 2; // Two markers - One for loop start, one for loop end |
UInt32 totalChunkSize = 0; |
/**** Region Chunk ****/ |
// Add up the total memory size we need to allocate for the variable-size AudioFileRegionList |
totalChunkSize += offsetof(AudioFileRegionList, mRegions); |
totalChunkSize += numRegionChunks * offsetof(AudioFileRegion, mMarkers); |
totalChunkSize += numRegionChunks * numMarkers * sizeof(AudioFileMarker); |
// Allocate the region chunk |
AudioFileRegionList *pRegionList = (AudioFileRegionList *) malloc(totalChunkSize); |
pRegionList->mNumberRegions = numRegionChunks; |
pRegionList->mSMPTE_TimeType = kCAF_SMPTE_TimeTypeNone; // Not used |
// Access the region |
AudioFileRegion *pRegion = &pRegionList->mRegions[0]; |
pRegion->mRegionID = 6789; // Arbitrary, but must match the values used |
// in the Instrument Chunk below |
pRegion->mName = NULL; |
pRegion->mNumberMarkers = numMarkers; // From above |
pRegion->mFlags = kAudioFileRegionFlag_LoopEnable | |
kAudioFileRegionFlag_PlayForward; // enable forward looping for this region |
// Access the marker array |
AudioFileMarker *pMarkers = &pRegion->mMarkers[0]; |
// Set up loop start |
pMarkers[0].mType = kCAFMarkerType_SustainLoopStart; |
memset(&pMarkers[0].mSMPTETime, 0, sizeof(pMarkers[0].mSMPTETime)); // zero out; not used |
pMarkers[0].mFramePosition = 0; // Loop from first sample |
pMarkers[0].mMarkerID = 1; |
pMarkers[0].mName = NULL; // Unused |
pMarkers[0].mChannel = 0; // Unused |
// Set up loop end |
pMarkers[1].mType = kCAFMarkerType_SustainLoopEnd; |
memset(&pMarkers[1].mSMPTETime, 0, sizeof(pMarkers[0].mSMPTETime)); // zero out; not used |
pMarkers[1].mFramePosition = inFileLength - 1; // Loop to the last sample |
pMarkers[1].mMarkerID = 2; |
pMarkers[1].mName = NULL; // Unused |
pMarkers[1].mChannel = 0; // Unused |
// Write out entire AudioFileRegionList into the file |
// Note that we use the full allocated size |
OSStatus err = AudioFileSetProperty(inFileID, kAudioFilePropertyRegionList, totalChunkSize, pRegionList); |
if (err) { free(pRegionList); return err; } |
// Set up Instrument Chunk, referencing the ID for the region chunk that we just wrote to the file |
CAFInstrumentChunk instChunk; |
instChunk.mBaseNote = (Float32) inBaseNote; |
instChunk.mMIDILowNote = inLowNote; |
instChunk.mMIDIHighNote = inHighNote; |
instChunk.mMIDILowVelocity = 0; // For this example, just setting full velocity range |
instChunk.mMIDIHighVelocity = 127; |
instChunk.mdBGain = 0.0f; // 0.0 dB means unity gain |
instChunk.mStartRegionID = 0; // Unused |
instChunk.mSustainRegionID = pRegion->mRegionID; // Set up above |
instChunk.mReleaseRegionID = 0; // Unused |
instChunk.mInstrumentID = 0; |
// CAF Specification requires all chunks to be BigEndian |
MakeCAFInstrumentBigEndian(&instChunk); |
// Write out CAFInstrumentChunk into the file |
err = AudioFileSetUserData(inFileID, kCAF_InstrumentChunkID, 0, sizeof(instChunk), &instChunk); |
free(pRegionList); |
return err; |
} |
#include <stddef.h> |
#include "CAByteOrder.h" // from Core Audio public utilities |
// CAF Files are big-endian, so the chunks contents need to be swapped |
static void MakeCAFInstrumentBigEndian(CAFInstrumentChunk *chunk) |
{ |
chunk->mBaseNote = CASwapFloat32HostToBig(chunk->mBaseNote); |
chunk->mdBGain = CASwapFloat32HostToBig(chunk->mdBGain); |
chunk->mStartRegionID = CFSwapInt32HostToBig(chunk->mStartRegionID); |
chunk->mSustainRegionID = CFSwapInt32HostToBig(chunk->mSustainRegionID); |
chunk->mReleaseRegionID = CFSwapInt32HostToBig(chunk->mReleaseRegionID); |
chunk->mInstrumentID = CFSwapInt32HostToBig(chunk->mInstrumentID); |
} |
Document Revision History
Date | Notes |
---|---|
2013-05-29 | New document that discusses how to add Music Instrument Information and Sample Loop Points to Core Audio (.caf) files so they can be used with the AUSampler. |
Copyright © 2013 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2013-05-29