Writing array of floats to audio file

1.1k Views Asked by At

I have an array of Float32 values called _recordingSamples that I wish to write to an audio file. The value of _recordingLength is about 390000. I'm using the following code:

NSString *outputPath = @"/Users/evanjackson/output.caf";
NSFileManager *fileManager = [NSFileManager defaultManager];
[fileManager createFileAtPath:outputPath contents:nil attributes:nil];

AudioStreamBasicDescription _audioFormat;
_audioFormat.mSampleRate = 44100.0;
_audioFormat.mFormatID = kAudioFormatLinearPCM;
_audioFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked;
_audioFormat.mChannelsPerFrame = 1;
_audioFormat.mBitsPerChannel = 32;
_audioFormat.mBytesPerFrame = 4;
_audioFormat.mFramesPerPacket = 1;
_audioFormat.mBytesPerPacket = _audioFormat.mFramesPerPacket * _audioFormat.mBytesPerFrame;

UInt32 lengthPropertySize = sizeof(SInt64);
ExtAudioFileRef filteredAudio;
NSURL *destinationURL = [NSURL URLWithString:outputPath];
OSStatus status = ExtAudioFileCreateWithURL((__bridge CFURLRef)destinationURL, kAudioFileCAFType, &_audioFormat, NULL, kAudioFileFlags_EraseFile, &filteredAudio);
printf("Status: %d\n", status);
//ExtAudioFileOpenURL((__bridge CFURLRef)[NSURL fileURLWithPath:outputPath], &filteredAudio);
const AudioStreamBasicDescription audioFormat = [del audioFormat];
status = ExtAudioFileSetProperty(filteredAudio, kExtAudioFileProperty_ClientDataFormat, sizeof(audioFormat), &audioFormat);
printf("Status: %d\n", status);
status = ExtAudioFileGetProperty(filteredAudio, kExtAudioFileProperty_FileLengthFrames, &lengthPropertySize, &_recordingLength);
printf("Status: %d\n", status);
AudioBufferList *buffers = (AudioBufferList *)malloc(sizeof(AudioBufferList) + sizeof(AudioBuffer));
buffers->mNumberBuffers = 1;
for (int i = 0; i < buffers->mNumberBuffers; i++) {
    buffers->mBuffers[0].mData = _recordingSamples;
    buffers->mBuffers[0].mNumberChannels = 1;
    buffers->mBuffers[0].mDataByteSize = _recordingLength * sizeof(Float32);
}

status = ExtAudioFileWrite(filteredAudio, _recordingLength, buffers);
printf("Status: %d\n", status);

All of the statuses return as 0, the file gets created (4 bytes) but Quicktime will not open it and returns the error -12842, and the compiler delivers the following message:

libc++abi.dylib: terminating with uncaught exception of type std::bad_alloc: std::bad_alloc
(lldb) 

Anybody know what's going wrong? Thanks in advance.

2

There are 2 best solutions below

0
On BEST ANSWER

Scrap the code that I posted in the question. I found the solution here: How to write array of float values to audio file in Core Audio?. Look at the first answer. I went to the given link, downloaded the source code, then added EAFWrite.h and EAFWrite.mm to my project. The class assumes that the audio will be read from multiple buffers (ie: a 2D array), but needed it to work with a 1D array, so I modified the function writeToFloats as follows:

-(OSStatus) writeFloats:(long)numFrames fromArray:(float *)data
{
    OSStatus    err = noErr;

    if (!data)      return -1;
    if (!numFrames) return -1;

    AudioBufferList *abl = AllocateAudioBufferList(mStreamFormat.mChannelsPerFrame, numFrames*sizeof(short));
    if (!abl)       return -1;

    abl->mBuffers[0].mNumberChannels = 1;
    abl->mBuffers[0].mDataByteSize = numFrames*sizeof(short);
    short *buffer = (short*)abl->mBuffers[0].mData;
    for (long v = 0; v < numFrames; v++) {
        if (data[v] > 0.999)
            data[v] = 0.999;
        else if (data[v] < -1)
            data[v] = -1;
        buffer[v] = (short)(data[v]*32768.f);
    }

    abl->mBuffers[0].mData = buffer;

    err = ExtAudioFileWrite(mOutputAudioFile, numFrames, abl);

    DestroyAudioBufferList(abl);

    if(err != noErr)
    {
        char formatID[5];
        *(UInt32 *)formatID = CFSwapInt32HostToBig(err);
        formatID[4] = '\0';
        fprintf(stderr, "ExtAudioFileWrite FAILED! %d '%-4.4s'\n",(int)err, formatID);
        return err;
    }

    return err;

}

And to call this function you need:

NSString *outputPath = @"outputFile.caf"
NSFileManager *fileManager = [NSFileManager defaultManager];
[fileManager createFileAtPath:outputPath contents:nil attributes:nil];
NSURL* fileURL = [NSURL URLWithString:outputPath];

EAFWrite *writer = [[EAFWrite alloc] init];
[writer openFileForWrite:fileURL sr:44100.0 channels:1 wordLength:32 type:kAudioFileCAFType];
[writer writeFloats:_recordingLength fromArray:_recordingSamples];
1
On

That's a bunch of fairly hairy code.

This line:

AudioBufferList *buffers = 
  (AudioBufferList *)malloc(sizeof(AudioBufferList) + sizeof(AudioBuffer));

Looks suspicious to me. It is allocating a buffer with a fixed size, that is the sum of sizeof(AudioBufferList) and sizeof(AudioBuffer)

What are the types of those 2 variables?

Sizeof returns the size of a structure, not the size of the content it points to. I am guessing that you need a buffer that is allocated to the size of a single float times a value that holds the total number of floats in your array. (Is that _recordingLength?)

Where did this code come from? Can you explain what it's supposed to be doing, in detail? Where does your array of floats come from? What tells your code how many bytes there are? Where is the data stored in memory? What writes that buffer of values to disk?

You say your file gets created and is 4 bytes long. That certainly sounds like a problem if your'e trying to store some 390000 floats.