Использование ExtAudioFileWriteAsync () в функции обратного вызова. Не могу бежать

Похоже, что в Core Audio не так уж и далеко. Моя цель - записать захваченные аудиоданные с приборного блока в файл. Я настроил вызов функции обратного вызова на приборной панели следующим образом:

CheckError(AudioUnitAddRenderNotify(player->instrumentUnit,
                                    MyRenderProc,
                                    &player),
           "AudioUnitAddRenderNotify Failed");

Я настроил файл и AudioStreamBasicDescription с этим:

#define FILENAME @"output_IV.aif"

NSString *fileName = FILENAME; // [NSString stringWithFormat:FILENAME_FORMAT, hz];
NSString *filePath = [[[NSFileManager defaultManager] currentDirectoryPath] stringByAppendingPathComponent: fileName];
NSURL *fileURL = [NSURL fileURLWithPath: filePath];
NSLog (@"path: %@", fileURL);

AudioStreamBasicDescription asbd;
memset(&asbd, 0, sizeof(asbd));
asbd.mSampleRate = 44100.0;
asbd.mFormatID = kAudioFormatLinearPCM;
asbd.mFormatFlags = kAudioFormatFlagIsBigEndian | kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
asbd.mChannelsPerFrame = 2; // CHANGED FROM 1 (STEREO)
asbd.mFramesPerPacket = 1;
asbd.mBitsPerChannel = 16;
asbd.mBytesPerFrame = 4;
asbd.mBytesPerPacket = 4;

CheckError(ExtAudioFileCreateWithURL((__bridge CFURLRef)fileURL, kAudioFileAIFFType, &asbd, NULL, kAudioFileFlags_EraseFile, &testRecordFile), "ExtAudioFileCreateWithURL failed"); 
CheckError(ExtAudioFileSetProperty(testRecordFile, kExtAudioFileProperty_ClientDataFormat, (UInt32)sizeof(asbd), &asbd), "ExtAudioFileSetProperty failed"); 
CheckError(ExtAudioFileWriteAsync(testRecordFile, 0, NULL), "ExtAudioFileWriteAsync 1st time failed");

Я проверил, что файл действительно создан. testRecordFile определен глобально (это единственный способ, которым я мог заставить вещи работать в данный момент):

ExtAudioFileRef testRecordFile;

Моя функция обратного вызова:

OSStatus MyRenderProc(void *inRefCon,
                  AudioUnitRenderActionFlags *ioActionFlags,
                  const AudioTimeStamp *inTimeStamp,
                  UInt32 inBusNumber,
                  UInt32 inNumberFrames,
                  AudioBufferList * ioData)
{
    if (*ioActionFlags & kAudioUnitRenderAction_PostRender){
    static int TEMP_kAudioUnitRenderAction_PostRenderError = (1 << 8);
        if (!(*ioActionFlags & TEMP_kAudioUnitRenderAction_PostRenderError)){
            CheckError(ExtAudioFileWriteAsync(testRecordFile, inNumberFrames, ioData), "ExtAudioFileWriteAsync failed");
        }
    }
return noErr;
}

Когда я запускаю это, программа вращается и переходит в режим отладчика (lldb) при вызове ExtAudioFileWriteAsync. inNumberFrames = 512, и я подтвердил, что получаю стереоканалы аудиоданных Float32 в ioData.

Что мне здесь не хватает?

Ответы на вопрос(2)

Решение Вопроса

ваш код все еще немного сложен и включает некоторые «темные углы» CoreAudio и Obj-C. Лучше сделать ставку, сначала убедившись, что все работает так, как задумано в обычном C, в потоке реального времени. Как только вы отладите эту часть кода, вы можете легко добавить столько элегантности Obj-C, сколько потребуется.

Если для простоты игнорировать возможные проблемы с порядком байтов и преобразованием формата файла,одна проблема Вам нужно разрешить автоматически, используя утилиты API, или «вручную»:

AFAIK, формат данных дляExtAudioFileWriteAsync() должен чередоваться, в то время как формат потока для вашего AUGraphне, Предполагая, что здесь мы не имеем дело с endiannes и преобразованием формата, вы можете исправить это вручную (пример с одним каналом). Если вашasbd Формат потока не является чередующимся стерео, вы чередуете данные в своем буфере следующим образом: LRLRLRLRLR ...

OSStatus MyRenderProc(void *inRefCon, 
                  AudioUnitRenderActionFlags *ioActionFlags,
                  const AudioTimeStamp *inTimeStamp,
                  UInt32 inBusNumber,
                  UInt32 inNumberFrames,
                  AudioBufferList * ioData) 
{
AudioBufferList bufferList;
Float32 samples[inNumberFrames+inNumberFrames];

bufferList.mNumberBuffers = 1;
bufferList.mBuffers[0].mData = samples;
bufferList.mBuffers[0].mNumberChannels = 1;
bufferList.mBuffers[0].mDataByteSize = (inNumberFrames+inNumberFrames)*sizeof(Float32);
Float32 *data = (Float32 *)ioData->mBuffers[0].mData;

if (*ioActionFlags & kAudioUnitRenderAction_PostRender){
    static int TEMP_kAudioUnitRenderAction_PostRenderError = (1 << 8);
    if (!(*ioActionFlags & TEMP_kAudioUnitRenderAction_PostRenderError) {  
        for(UInt32 i = 0; i < inNumberFrames; i++)
            samples[i+i] = samples [i+i+1] = data[i];//copy buffer[0] to L & R            
        ExtAudioFileWriteAsync(testRecordFile, inNumberFrames, &bufferList);
    }
}
return noErr;
}

Это только один пример, чтобы показать, как это работает. Путем изученияasbd.mFormatFlags и установив правильный формат в:

ExtAudioFileSetProperty(testRecordFile,
                        kExtAudioFileProperty_ClientDataFormat,
                        s,
                        &asbd);

Вы можете достичь этого более элегантно, но это намного превосходит рамки, описанные в этом посте.

 BD Wild08 июн. 2016 г., 00:29
Это сработало! Еще раз большое спасибо. Я надеюсь, что когда-нибудь смогу вернуть карму кому-то, когда у меня будет больше опыта во всем этом. Для дальнейшего использования я приведу рабочий код AIF ниже. Ура!
 user307841407 июн. 2016 г., 19:58
Я проанализировал ваш код - в этом примере я специально использую CAF вместо AIF, потому что я хотел, чтобы этот пример был понятным и полезным для всех. Apple немного запуталась со стандартом AIFF на машинах Intel, объявивродной порядок Линейная PCM-версия AIFF в виде сдвоенных байтов дополняет подтип AIFC (он же SOWT) и дополнительно мистифицирует формат путем объединения стандартов AIFF и AIFC в «AIF». Береги себя.
 user307841408 июн. 2016 г., 09:16
Рад, что это сработало. Спасибо за размещение вашего кода. Пожалуйста, отредактируйте его - первое предложение должно быть конкретным: «Вот рабочий обратный вызов для 16-битного линейного AIF-файла с прямым порядком байтов».
 BD Wild07 июн. 2016 г., 19:40
В этом есть смысл. Я нажился на совпадении между ioData в шаблоне обратного вызова и ExtAudioFileWriteAsync. Мой код будет более обширным, так как я пишу .aif, но я понял идею. Спасибо!

порядком байтов:

OSStatus MyRenderProc(void *inRefCon,
                  AudioUnitRenderActionFlags *ioActionFlags,
                  const AudioTimeStamp *inTimeStamp,
                  UInt32 inBusNumber,
                  UInt32 inNumberFrames,
                  AudioBufferList * ioData)
{
    SInt16 samples[inNumberFrames + inNumberFrames];
    AudioBufferList bufferList;
    bufferList.mNumberBuffers = 1;
    bufferList.mBuffers[0].mData = samples;
    bufferList.mBuffers[0].mNumberChannels = 1;
    bufferList.mBuffers[0].mDataByteSize = (inNumberFrames + inNumberFrames) * sizeof(SInt16);

    Float32 *leftData = (Float32 *)ioData->mBuffers[0].mData;
    Float32 *rightData = (Float32 *)ioData->mBuffers[1].mData;

    if (*ioActionFlags & kAudioUnitRenderAction_PostRender){
        static int TEMP_kAudioUnitRenderAction_PostRenderError = (1 << 8);
        if (!(*ioActionFlags & TEMP_kAudioUnitRenderAction_PostRenderError)){
            for (UInt32 i = 0; i < inNumberFrames; i++) {
                samples[i + i] = CFSwapInt16HostToBig((SInt16) SHRT_MAX * (leftData)[i]);
                samples[i + i + 1] = CFSwapInt16HostToBig((SInt16) SHRT_MAX * (rightData)[i]);
            }
        CheckError(ExtAudioFileWriteAsync(testRecordFile, inNumberFrames, &bufferList), "ExtAudioFileWriteAsync failed");
        }
    }
    return noErr;
}

Ваш ответ на вопрос