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;
