For my application, I need to decode an MP3 file that is stored in an NSData object.
For security reasons, it is not advisable to write the NSData object to disk and reopen it using a link to the system URL, even if it is only stored locally for several seconds.
I would like to use the advanced audio file services (or audio files) to do this, but itβs hard for me to get an NSData view that exists only in the memory that can be read by this Service audio file.
Edit: I want to decode MP3 data so that I can access linear PCM audio samples for manipulation. Playback from an NSData object is not a problem.
My code is as follows:
decryptedData;
Both attempts to open the URL result in OSStatus 43 code, which is "file not found".
I checked that my pointer points to the correct memory address for NSData and that the bytes can be read correctly.
Are there any restrictions for advanced audio file services that prohibit references to bytes stored in memory?
Thanks for any help you can provide.
Edit: I figured out how to do this using the Sbooth suggestion. Code below: This function accepts an NSData object containing an mp3 representation of the audio file. It decodes it as linear PCM, so you can get samples and then transcode it as AAC. I donβt think MP3 encoding is available in CoreAudio on all platforms (mobile / work). This code was tested on my Mac and did the job.
-(void) audioFileReaderWithData: (NSData *) audioData { AudioFileID refAudioFileID; ExtAudioFileRef inputFileID; ExtAudioFileRef outputFileID; OSStatus result = AudioFileOpenWithCallbacks(audioData, readProc, 0, getSizeProc, 0, kAudioFileMP3Type, &refAudioFileID); if(result != noErr){ NSLog(@"problem in theAudioFileReaderWithData function: result code %i \n", result); } result = ExtAudioFileWrapAudioFileID(refAudioFileID, false, &inputFileID); if (result != noErr){ NSLog(@"problem in theAudioFileReaderWithData function Wraping the audio FileID: result code %i \n", result); } // Client Audio Format Description AudioStreamBasicDescription clientFormat; memset(&clientFormat, 0, sizeof(clientFormat)); clientFormat.mFormatID = kAudioFormatLinearPCM; clientFormat.mFramesPerPacket = 1; clientFormat.mChannelsPerFrame = 2; clientFormat.mBitsPerChannel = 32; clientFormat.mBytesPerPacket = clientFormat.mBytesPerFrame = 4 * clientFormat.mChannelsPerFrame; clientFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked; clientFormat.mSampleRate = 44100; //Output Audio Format Description AudioStreamBasicDescription outputFormat; memset(&outputFormat, 0, sizeof(outputFormat)); outputFormat.mChannelsPerFrame = 2; outputFormat.mSampleRate = 44100; outputFormat.mFormatID = kAudioFormatMPEG4AAC; outputFormat.mFormatFlags = kMPEG4Object_AAC_Main; outputFormat.mBitsPerChannel = 0; outputFormat.mBytesPerFrame = 0; outputFormat.mBytesPerPacket = 0; outputFormat.mFramesPerPacket = 1024; // create the outputFile that we're writing to here.... UInt32 outputFormatSize = sizeof(outputFormat); result = 0; result = AudioFormatGetProperty(kAudioFormatProperty_FormatInfo, 0, NULL, &outputFormatSize, &outputFormat); if(result != noErr) NSLog(@"could not set the output format with status code %i \n",result); NSMutableString *outputFilePath = [NSMutableString stringWithCapacity: 100]; [outputFilePath setString:@"/Users/You/Desktop/testAudio.m4a"]; NSURL *sourceURL = [NSURL fileURLWithPath:outputFilePath]; result = 0; result = ExtAudioFileCreateWithURL((CFURLRef)sourceURL, kAudioFileM4AType, &outputFormat, NULL, kAudioFileFlags_EraseFile, &outputFileID); if(result != noErr){ NSLog(@"ExtAudioFileCreateWithURL failed for outputFileID with status %i \n", result); } int size = sizeof(clientFormat); result = 0; result = ExtAudioFileSetProperty(inputFileID, kExtAudioFileProperty_ClientDataFormat, size, &clientFormat); if(result != noErr) NSLog(@"error on ExtAudioFileSetProperty for input File with result code %i \n", result); size = sizeof(clientFormat); result = 0; result = ExtAudioFileSetProperty(outputFileID, kExtAudioFileProperty_ClientDataFormat, size, &clientFormat); if(result != noErr) NSLog(@"error on ExtAudioFileSetProperty for output File with result code %i \n", result); int totalFrames = 0; UInt32 outputFilePacketPosition = 0; //in bytes UInt32 encodedBytes = 0; while (1) { UInt32 bufferByteSize = 22050 * 4 * 2; char srcBuffer[bufferByteSize]; UInt32 numFrames = (bufferByteSize/clientFormat.mBytesPerFrame); AudioBufferList fillBufList; fillBufList.mNumberBuffers = 1; fillBufList.mBuffers[0].mNumberChannels = clientFormat.mChannelsPerFrame; fillBufList.mBuffers[0].mDataByteSize = bufferByteSize; fillBufList.mBuffers[0].mData = srcBuffer; result = 0; result = ExtAudioFileRead(inputFileID, &numFrames, &fillBufList); if (result != noErr) { NSLog(@"Error on ExtAudioFileRead with result code %i \n", result); totalFrames = 0; break; } if (!numFrames) break; totalFrames = totalFrames + numFrames; result = 0; result = ExtAudioFileWrite(outputFileID, numFrames, &fillBufList); if(result!= noErr){ NSLog(@"ExtAudioFileWrite failed with code %i \n", result); } encodedBytes += numFrames * clientFormat.mBytesPerFrame; } //Clean up ExtAudioFileDispose(inputFileID); ExtAudioFileDispose(outputFileID); AudioFileClose(refAudioFileID); }
And you will also need these features ...
static OSStatus readProc(void* clientData, SInt64 position, UInt32 requestCount, void* buffer, UInt32* actualCount) { NSData *inAudioData = (NSData *) clientData; size_t dataSize = inAudioData.length; size_t bytesToRead = 0; if(position < dataSize) { size_t bytesAvailable = dataSize - position; bytesToRead = requestCount <= bytesAvailable ? requestCount : bytesAvailable; [inAudioData getBytes: buffer range:NSMakeRange(position, bytesToRead)]; } else { NSLog(@"data was not read \n"); bytesToRead = 0; } if(actualCount) *actualCount = bytesToRead; return noErr; } static SInt64 getSizeProc(void* clientData) { NSData *inAudioData = (NSData *) clientData; size_t dataSize = inAudioData.length; return dataSize; }