在回调函数中使用

在回调函数中使用

本文介绍了在回调函数中使用 ExtAudioFileWriteAsync().无法运行的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在 Core Audio 中似乎走不远.我的目标是将捕获的音频数据从乐器单元写入文件.我已经在仪器单元上设置了对回调函数的调用:

Just can't seem to get very far in Core Audio. My goal is to write captured audio data from an instrument unit to a file. I have set up a call to a callback function on an instrument unit with this:

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

我用这个设置了文件和 AudioStreamBasicDescription:

I set up the file and AudioStreamBasicDescription with this:

#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 是全局定义的(这是我目前可以运行的唯一方法):

I verified that the file does get created. testRecordFile is defined globally (it's the only way I could get things to run at the moment):

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

当我运行这个程序时,程序会在 ExtAudioFileWriteAsync 调用中进入调试器模式 (lldb).inNumberFrames = 512 并且我已经验证我在 ioData 中获得了 Float32 音频数据的立体声通道.

When I run this the program pinwheels and goes into debugger mode (lldb) on the ExtAudioFileWriteAsync call. inNumberFrames = 512 and I have verified that I am getting stereo channels of Float32 audio data in ioData.

我在这里遗漏了什么?

推荐答案

首先,你的代码还是有点复杂,包括 CoreAudio 和 Obj-C 的一些暗角".这是一个更安全的赌注,首先确保一切都在实时线程上按纯 C 语言的预期工作.一旦您调试了该部分代码,您就可以根据需要轻松添加尽可能多的 Obj-C 优雅.

First, your code is still slightly complicated, and including some of "dark corners" of CoreAudio and Obj-C. It is a safer bet first making sure that everything works as intended in plain-C, on the real-time thread. As soon as you have debugged that part of code you can easily add as much Obj-C elegance as needed.

如果为了简单而忽略可能的字节顺序和文件格式转换问题,仍然有一个问题您必须使用 API 实用程序自动解决,或者手动"解决:

If ignoring possible endianness and file format conversion issues for simplicity, there is still one issue you either have to resolve automatically, using API utilities, or "manually":

AFAIK,ExtAudioFileWriteAsync() 的数据格式必须交错,而 AUGraph 的流格式不是.假设我们在这里不处理字节序和格式转换,这就是您可以手动修复它的方法(单通道示例).如果您的 asbd 流格式是非交错立体声,您可以像这样在缓冲区中交错数据:LRLRLRLRLR...

AFAIK, data format for ExtAudioFileWriteAsync() must be interleaved, while the stream format for your AUGraph is not. Assuming we don't deal with endiannes and format conversion here, this is how you can fix it manually (single-channel example). In case your asbd stream format is non-interleaved stereo, you interleave data in your buffer like this: 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 并在:

This is just one example to show how it works. By studying asbd.mFormatFlags and setting the proper format in:

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

你可以更优雅地实现它,但这超出了你这篇文章的范围.

you can achieve it more elegantly, but this exceeds the scope you this post by far.

这篇关于在回调函数中使用 ExtAudioFileWriteAsync().无法运行的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-01 23:12