3

I've used ExtAudioFileCreateWithURL and ExtAudioFileWrite from the AudioToolBox framework to save my samples to a file. But now I need to save it to an NSFileWrapper since I'm now using NSDocument:

- (NSFileWrapper *)fileWrapperOfType:(NSString *)typeName error:(NSError **)outError 

Is there any way to use the ExtAudioFileXXX functions with an NSMutableData object so I can use NSFileWrapper initRegularFileWithContents:(NSData*)contents method?

Stripped down code for saving to file (which works):

AudioStreamBasicDescription file_desc;
FillOutASBDForLPCM(file_desc, _sample_rate, _channel_count, 32, 32, true, false);
OSStatus rv = ExtAudioFileCreateWithURL(url, kAudioFileWAVEType, &file_desc, NULL, kAudioFileFlags_EraseFile, &fout);
if (rv == noErr){
    int buff_size = sizeof(AudioBufferList) + sizeof(AudioBuffer);
    AudioBufferList* bufferList = (AudioBufferList*)malloc(buff_size);
    bufferList->mNumberBuffers = 1;
    bufferList->mBuffers[0].mData = _samples;
    bufferList->mBuffers[0].mNumberChannels = _channel_count;
    bufferList->mBuffers[0].mDataByteSize = _channel_count * _frame_count * sizeof(float);
    ExtAudioFileWrite(fout, _frame_count, bufferList);
    free(bufferList);
    ExtAudioFileDispose(fout);
}
justin
  • 104,054
  • 14
  • 179
  • 226

1 Answers1

0

Using an external file is the best solution in most scenarios. Note that NSData has initializers for Uncached and Mapped files. You obviously will want to minimize I/O in the majority of cases, while not consuming a ton of memory (and you have to keep an eye on Cached memory growth). One step further, consider how you can represent your file wrapper object another way for these weighty assets (sub-NSFileWrappers, links) because a document's file is likely not a trivially small file (e.g. single cycle).

If memory is not an issue (e.g. you are only dealing with small samples and finite files and documents), then you could use AudioFileInitializeWithCallbacks, which works with Wave (but not all formats). This allows you to handle your own buffer in memory, for contiguous allocations representing the entire file in memory. Then creating the NSData representation is easy -- just monitor your memory usage and ensure exclusive read/write accesses. Note that NSData does not need to create deep copies -- and factor that into your design. You will obviously have a lot more flexibility using this option on OS X. In this case, make sure you are aware of how much you actually write. In all probability, a good disk based solution (above) could save you a lot of writing to disk.

Anyways, this will require more thought than most NSFileWrapper based implementations, because you could possibly exceed 100 MB in audio file assets per document quite quickly.

justin
  • 104,054
  • 14
  • 179
  • 226
  • 1
    Thanks Justin. I had already used child NSFileWrappers to minimize writes since the audio files seldom change. If the fileWrapperOfType already finds a valid NSFileWrapper it skips over it. My final solution was to use the ExtAudioFileCreateWithURL for saving to external files that the user interacts with, but to roll my own saveWAVFileToMemory routine to use when storing the audio in my NSFileWrapper. This gives the user more file format choices during export, but allows me to use NSFileWrappers for internal saving. – Dave Stephenson Nov 05 '12 at 17:34
  • @DaveStephenson explanation of actual implementation appreciated -- cheers. you're welcome. – justin Nov 05 '12 at 20:46