This was seriously way more work than I anticipated. In the end, it really wasn’t that complicated, but just took a while to put all the pieces together. Having to wrap the ExtAudioFileRef around an AudioFileID and then having to setup callback functions to get the AudioFileID from memory felt like a bit of overkill… Here is the code I added to CDOpenALSupport.m. I’m sure there is a better solution, but this works so I’m gonna stick with it for now ^_^.
//
// desc: for storing the decrypted audio file data in memory
//
typedef struct {
unsigned char *data; // pointer to audio data
SInt64 dataLength; // length of audio data
} AudioFileMemory;
//
// desc: callback for reading the audio data,
// check AudioFileOpenWithCallbacks doc for more details
//
OSStatus AudioFileReadProc(void *inClientData, SInt64 inPosition, UInt32 requestCount, void *buffer, UInt32 *actualCount)
{
// check parameters
if (!inClientData || !buffer || !actualCount) {
return EINVAL;
}
AudioFileMemory *audioFileMemory = (AudioFileMemory *)inClientData;
// make sure position is within bounds
if (inPosition < 0 || inPosition >= audioFileMemory->dataLength) {
*actualCount = 0; // don't read anything and tell them everything is just friggin fine,
// this is called passive aggressive error handling ^_^
return noErr;
}
// see if we need to cap requested length
*actualCount = requestCount;
SInt64 endPosition = inPosition + requestCount;
if (endPosition >= audioFileMemory->dataLength) {
*actualCount = requestCount - (endPosition - audioFileMemory->dataLength);
}
memcpy(buffer, audioFileMemory->data + inPosition, *actualCount);
return noErr;
}
//
// desc: callback for getting size of audio data
// check AudioFileOpenWithCallbacks doc for more details
//
SInt64 AudioFileGetSizeProc(void *inClientData) {
if (!inClientData) {
return EINVAL;
}
AudioFileMemory *audioFileMemory = (AudioFileMemory *)inClientData;
return audioFileMemory->dataLength;
}
//
// desc: assumes this is encrypted or packaged file and that
// the file naming convention is filename.xxx.enc or filename.xxx.tar, etc...
// determines type based off xxx, only coded for checking for wave and mp3
// cause that's all I care about right now
//
// params: inFileURL[in] - file url to get audio file type on
//
// returns: audio file type id for file if successful
// returns 0 if file type is unknown
//
AudioFileTypeID GetAudioFileTypeId(CFURLRef inFileURL)
{
CFURLRef pathWithoutEncExtension = CFURLCreateCopyDeletingPathExtension(kCFAllocatorDefault, inFileURL);
CFStringRef extension = CFURLCopyPathExtension(pathWithoutEncExtension);
AudioFileTypeID audioFileTypeId = 0;
if (CFStringCompare(extension, (CFStringRef)@"wav", kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
audioFileTypeId = kAudioFileWAVEType;
}
else if (CFStringCompare(extension, (CFStringRef)@"mp3", kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
audioFileTypeId = kAudioFileMP3Type;
}
CFRelease(pathWithoutEncExtension);
CFRelease(extension);
return audioFileTypeId;
}
//
// desc: creates an audio file id, checks to see if file type is encrypted.
// if it's not encrypted, then it loads it like normal and returns audioFileId.
// if is its encrypted, it will decrypt the file in memory and load it into a AudioFileId.
// if it decrypts, then it will return pointer to the audio data in audioFileMemory. when
// you close the audioFileId, be sure to free() the data pointer inside audioFileMemory.
// I'm not really sure if we need to keep the data around, but I'm doing it to be safe.
//
// params: inFileURL[in] - url of file to load
// audioFileId[out] - returns audio file id if file was successfully loaded
// audioFileMemory[out] - returns pointer to audio memory if the audio file was decrpyted and loaded from mem
// BE SURE TO CALL free() ON THE DATA POINTER WHEN YOU ARE DONE WITH THE AUDIO FILE ID
//
// returns: returns 0 if opened file like normal (not encrypted)
// returns 1 if dectypred file and loaded it from memory
// returns -1 if failed to open unencrypted file
// returns -2 if failed to open encrypted file
// returns -3 if failed to decrypt file
// returns -4 if failed to load audio file id with decrypted memory
//
int CreateAudioFileId(CFURLRef inFileURL, AudioFileID *audioFileId, AudioFileMemory *audioFileMemory)
{
CFStringRef extension = CFURLCopyPathExtension(inFileURL);
OSStatus err = noErr;
// reset audioFileMemory
memset(audioFileMemory, 0, sizeof(AudioFileMemory));
// if not an encrypted file, then get audio id like always
if (CFStringCompare(extension, (CFStringRef)@"enc", kCFCompareCaseInsensitive) != kCFCompareEqualTo) {
CFRelease(extension);
err = AudioFileOpenURL(inFileURL, kAudioFileReadPermission, 0, audioFileId);
if (err) {
CDLOG(@"CreateAudioFileId: AudioFileOpenURL FAILED, Error = %ld\n", err);
return -1;
}
return 0;
}
CFRelease(extension);
// else we are dealing with encrypted file, so decrypt it
@autoreleasepool {
NSData *encData = [NSData dataWithContentsOfURL:(NSURL *)inFileURL];
if (!encData) {
CDLOG(@"CreateAudioFileId: Failed to load %@, could not find file.", CFURLGetString(inFileURL));
return -2;
}
audioFileMemory->dataLength = CCDecryptMemory((unsigned char *)[encData bytes], [encData length], &audioFileMemory->data);
if (!audioFileMemory->data) {
CDLOG(@"CreateAudioFileId: Failed to load %@, failed to decrypt.", CFURLGetString(inFileURL));
return -3;
}
}
err = AudioFileOpenWithCallbacks(audioFileMemory,
AudioFileReadProc, NULL,
AudioFileGetSizeProc, NULL,
GetAudioFileTypeId(inFileURL), audioFileId);
if (err) {
char bytes[4];
bytes[0] = (err >> 24) & 0xFF;
bytes[1] = (err >> 16) & 0xFF;
bytes[2] = (err >> 8) & 0xFF;
bytes[3] = err & 0xFF;
CDLOG(@"CreateAudioFileId: AudioFileOpenWithCallbacks FAILED, Error = %ld, %c%c%c%c\n", err, bytes[0], bytes[1], bytes[2], bytes[3]);
return -4;
}
return 1;
}
And after adding all that, just had to change CDloadWaveAudioData() and CDloadCafAudioData to use the new CreateAudioFileId() calls.
CDloadWaveAudioData() change:
//Taken from oalTouch MyOpenALSupport 1.1
void* CDloadWaveAudioData(CFURLRef inFileURL, ALsizei *outDataSize, ALenum *outDataFormat, ALsizei* outSampleRate)
{
OSStatus err = noErr;
UInt64 fileDataSize = 0;
AudioStreamBasicDescription theFileFormat;
UInt32 thePropertySize = sizeof(theFileFormat);
AudioFileID afid = 0;
void* theData = NULL;
AudioFileMemory audioFileMemory;
// Open a file with ExtAudioFileOpen()
if (CreateAudioFileId(inFileURL, &afid, &audioFileMemory) < 0) {
goto Exit;
}
CDloadCafAudioData change:
//Taken from oalTouch MyOpenALSupport 1.4
void* CDloadCafAudioData(CFURLRef inFileURL, ALsizei *outDataSize, ALenum *outDataFormat, ALsizei* outSampleRate)
{
OSStatus status = noErr;
BOOL abort = NO;
SInt64 theFileLengthInFrames = 0;
AudioStreamBasicDescription theFileFormat;
UInt32 thePropertySize = sizeof(theFileFormat);
ExtAudioFileRef extRef = NULL;
void* theData = NULL;
AudioStreamBasicDescription theOutputFormat;
UInt32 dataSize = 0;
AudioFileID audioFileId = 0;
AudioFileMemory audioFileMemory;
// create audio file id
if (CreateAudioFileId(inFileURL, &audioFileId, &audioFileMemory) < 0) {
goto Exit;
}
// Open a file with ExtAudioFileOpen()
status = ExtAudioFileWrapAudioFileID(audioFileId, false, &extRef);
if (status != noErr)
{
CDLOG(@"MyGetOpenALAudioData: ExtAudioFileOpenURL FAILED, Error = %ld\n", status);
abort = YES;
}
if (abort)
goto Exit;






[...] Nice solution for loading sound from memory into cocos2d’s SimpleAudioEngine – LINK [...]
I got a error when implement your code. im using cocos2d 1.0.1.
Implicit declaration of function 'CCDecryptMemory' is invalid in C99
CCDecryptMemory() is my decryption function. You will want to implement your own encryption/decryption methods. I used sample code from CryptUtils.cpp which you can find in the project here. The link also has information on encrypting/decryption textures. My CCDecrypt() takes the encData as input, and outputs the decrypted data into the audioFileMemory->data and the size of the decrypted data into audioFileMemory->dataLength. Hope that helps.