Ios 如何将AUGraph的输出写入文件?
我正在尝试编写(应该是什么)一个简单的应用程序,它在一个曲线图中有一系列音频单元,然后将输出写入一个文件。我使用AUGraphAddRenderNotify添加了一个回调。这是我的回调函数:Ios 如何将AUGraph的输出写入文件?,ios,core-audio,Ios,Core Audio,我正在尝试编写(应该是什么)一个简单的应用程序,它在一个曲线图中有一系列音频单元,然后将输出写入一个文件。我使用AUGraphAddRenderNotify添加了一个回调。这是我的回调函数: OSStatus MyAURenderCallback(void *inRefCon, AudioUnitRenderActionFlags *actionFlags, const AudioTimeStamp
OSStatus MyAURenderCallback(void *inRefCon,
AudioUnitRenderActionFlags *actionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData) {
if (*actionFlags & kAudioUnitRenderAction_PostRender) {
ExtAudioFileRef outputFile = (ExtAudioFileRef)inRefCon;
ExtAudioFileWriteAsync(outputFile, inNumberFrames, ioData);
}
}
这类工作。该文件是可播放的,我可以听到我录制的内容,但有大量的静电,使它几乎听不见
有人知道这是怎么回事吗?或者有人知道更好的方法来将螺旋图输出记录到文件中吗
谢谢你的帮助。也许试试这个。将音频单元回调中的数据复制到长缓冲区。播放缓冲区以进行测试,然后在验证整个缓冲区是否正常后将整个缓冲区写入文件。以下是Apple提供的一些示例代码(该项目是PlaySequence,但不是特定于MIDI的),可能会有所帮助:
{
CAStreamBasicDescription clientFormat = CAStreamBasicDescription();
ca_require_noerr (result = AudioUnitGetProperty(outputUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Output, 0,
&clientFormat, &size), fail);
size = sizeof(clientFormat);
ca_require_noerr (result = ExtAudioFileSetProperty(outfile, kExtAudioFileProperty_ClientDataFormat, size, &clientFormat), fail);
{
MusicTimeStamp currentTime;
AUOutputBL outputBuffer (clientFormat, numFrames);
AudioTimeStamp tStamp;
memset (&tStamp, 0, sizeof(AudioTimeStamp));
tStamp.mFlags = kAudioTimeStampSampleTimeValid;
int i = 0;
int numTimesFor10Secs = (int)(10. / (numFrames / srate));
do {
outputBuffer.Prepare();
AudioUnitRenderActionFlags actionFlags = 0;
ca_require_noerr (result = AudioUnitRender (outputUnit, &actionFlags, &tStamp, 0, numFrames, outputBuffer.ABL()), fail);
tStamp.mSampleTime += numFrames;
ca_require_noerr (result = ExtAudioFileWrite(outfile, numFrames, outputBuffer.ABL()), fail);
ca_require_noerr (result = MusicPlayerGetTime (player, ¤tTime), fail);
if (shouldPrint && (++i % numTimesFor10Secs == 0))
printf ("current time: %6.2f beats\n", currentTime);
} while (currentTime < sequenceLength);
}
}
{
CAStreamBasicDescription clientFormat=CAStreamBasicDescription();
ca\u require\u noerr(结果=AudioUnitGetProperty(输出单位,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_输出,0,
&客户端格式和大小),失败);
size=sizeof(clientFormat);
ca_require_noerr(结果=ExtAudioFileSetProperty(outfile、kExtAudioFileProperty_ClientDataFormat、大小和clientFormat),失败);
{
音乐时间;
AUOutputBL outputBuffer(clientFormat,numFrames);
音频时间戳;
memset(&tStamp,0,sizeof(AudioTimeStamp));
tStamp.mFlags=kAudioTimeStampSampleTimeValid;
int i=0;
int numTimesFor10Secs=(int)(10./(numFrames/srate));
做{
outputBuffer.Prepare();
AudioUnitRenderActionFlags actionFlags=0;
ca_require_noerr(结果=AudioUnitRender(outputUnit,&actionFlags,&tStamp,0,numFrames,outputBuffer.ABL()),失败);
tStamp.mSampleTime+=numFrames;
ca_require_noerr(结果=ExtAudioFileWrite(outfile,numFrames,outputBuffer.ABL()),失败);
ca_require_noerr(结果=MusicLayerGetTime(播放器和当前时间),失败);
如果(应打印和(++i%numTimesFor10Secs==0))
printf(“当前时间:%6.2f拍\n”,当前时间);
}while(currentTime
刚才我对音频单元有了顿悟,这帮助我解决了自己的问题。我对音频单元连接和渲染回调的工作方式有一个误解。我以为它们是完全不同的东西,但事实证明,连接只是渲染回调的简写
执行从音频单元A的输出到音频单元B的输入的kAudioUnitProperty_MakeConnection与对单元B的输入执行kAudioUnitProperty_SetRenderCallback以及对音频单元A的输出调用AudioUnitRender的回调函数相同
在设置渲染回调后,我通过建立连接进行了测试,渲染回调不再被调用
因此,我能够通过以下方式解决我的问题:
AURenderCallbackStruct callbackStruct = {0};
callbackStruct.inputProc = MyAURenderCallback;
callbackStruct.inputProcRefCon = mixerUnit;
AudioUnitSetProperty(ioUnit,
kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Input,
0,
&callbackStruct,
sizeof(callbackStruct));
我的回调函数做了如下操作:
OSStatus MyAURenderCallback(void *inRefCon,
AudioUnitRenderActionFlags *actionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData) {
AudioUnit mixerUnit = (AudioUnit)inRefCon;
AudioUnitRender(mixerUnit,
actionFlags,
inTimeStamp,
0,
inNumberFrames,
ioData);
ExtAudioFileWriteAsync(outputFile,
inNumberFrames,
ioData);
return noErr;
}
这可能对我来说应该是显而易见的,但既然不是这样,我敢打赌还有其他人也以同样的方式感到困惑,所以希望这对他们也有帮助
我仍然不知道为什么我在使用AUGraphAddRenderNotify回调时遇到问题。稍后我将对此进行更深入的研究,但目前我找到了一个似乎有效的解决方案。您的AUGraph和ExtAudioFile(客户端数据格式)的格式匹配吗?还有,你需要实时记录吗?我想他们是匹配的。我使用相同的AudioStreamBasicDescription配置所有音频单元并创建文件。但我不知道如何真正检查。你知道怎么做吗?我想我会调查的。我想我不需要实时录制,但还有其他方法吗?如果我不将数据实时保存到某个位置,则数据将丢失。我相信您不需要调用
AUGraphStart
而是要在图表的开头重复调用AudioUnitRender
。我认为实时操作的问题在于,ExtAudioFile
的内部环形缓冲区填满并丢失数据。哦,这就是你所说的非实时。我想我确实需要它是实时的,因为我正在从麦克风录音,同时也将音频传送到扬声器。上面的代码真的有效吗?我认为它会抛出一个错误,因为您将refCon设置为“self”,然后在回调中将其转换为AUMixer。您是正确的。我稍微简化了项目中的代码。我传入了self,然后通过一个属性访问了mixerUnit。我更新了我的帖子以传入mixerUnit,它是输出单元之前的单元,我想在将其输出传递到输出单元之前将其写入文件。Ok-我想知道。您可能对我的将图形输出保存到文件的解决方案感兴趣—将renderCallback放回remoteIO实例,然后在“post_Render”阶段获取渲染数据。我不知道这是否是一个“好”的方式,但它的工作。还可以将实时保存为ACC压缩格式(耶!):您的答案肯定有效,但实际上是以破坏AUGraph
API为代价的。请看一看,作为回答,我使用AUGraphAddRenderNotify()
提供了一个有效的解决方案。