Запись массива с плавающей точкой в ​​аудиофайл

У меня есть массив значений Float32 с именем _recordingSamples, которые я хочу записать в аудиофайл. Значение _recordingLength составляет около 390000. Я использую следующий код:

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);

Все состояния возвращаются как 0, файл создается (4 байта), но Quicktime не открывает его и возвращает ошибку -12842, а компилятор выдает следующее сообщение:

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

Кто-нибудь знает, что идет не так? Заранее спасибо.

2 ответа

Решение

Соскрести код, который я разместил в вопросе. Я нашел решение здесь: Как записать массив значений с плавающей точкой в ​​аудиофайл в Core Audio?, Посмотрите на первый ответ. Я пошел по указанной ссылке, скачал исходный код, затем добавил EAFWrite.h и EAFWrite.mm в мой проект. Класс предполагает, что аудио будет считываться из нескольких буферов (то есть: двумерный массив), но он нужен для работы с одномерным массивом, поэтому я изменил функцию writeToFloats следующим образом:

-(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;

}

И для вызова этой функции вам нужно:

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];

Это куча довольно волосатого кода.

Эта строка:

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

Выглядит подозрительно для меня. Он выделяет буфер с фиксированным размером, который является суммой sizeof(AudioBufferList) и sizeof(AudioBuffer)

Какие типы этих 2 переменных?

Sizeof возвращает размер структуры, а не размер содержимого, на которое она указывает. Я предполагаю, что вам нужен буфер, который выделяется под размер одного числа с плавающей запятой, умноженного на значение, которое содержит общее число чисел с плавающей запятой в вашем массиве. (Это _recordingLength?)

Откуда этот код? Можете ли вы объяснить, что он должен делать, в деталях? Откуда ваш массив поплавков? Что говорит вашему коду, сколько байтов там? Где данные хранятся в памяти? Что пишет этот буфер значений на диск?

Вы говорите, что ваш файл создан и имеет длину 4 байта. Это, безусловно, звучит как проблема, если вы пытаетесь сохранить 390000 поплавков.

Другие вопросы по тегам