如何使用AVAudioEngine手动渲染模式处理输入



在处理输入时,AVAudioEngine手动渲染无法正常工作。当没有输入节点并且音频来自播放器节点时,它很容易工作。

这是我得到的代码,可以粘贴到测试中:

- (void)testManualRendering {
auto engine = [[AVAudioEngine alloc] init];
auto format = [[AVAudioFormat alloc] initStandardFormatWithSampleRate:44100.0
channels:2];
auto inputBuffer = [[AVAudioPCMBuffer alloc] initWithPCMFormat:format
frameCapacity:1024];
auto abl = inputBuffer.mutableAudioBufferList;
XCTAssert(abl != NULL);
XCTAssertEqual(abl->mNumberBuffers, 2);
[engine connect:engine.inputNode to:engine.mainMixerNode format:format];
NSError* error;
[engine enableManualRenderingMode:AVAudioEngineManualRenderingModeOffline
format:format
maximumFrameCount:1024 error:&error];
XCTAssertNil(error);
XCTAssert([format isEqual:engine.manualRenderingFormat]);
NSLog(@"manualRenderingFormat: %@", engine.manualRenderingFormat);
auto success = [engine.inputNode setManualRenderingInputPCMFormat:format
inputBlock:^const AudioBufferList * _Nullable(AVAudioFrameCount inNumberOfFrames) {
XCTAssert(abl->mBuffers[0].mDataByteSize <= inNumberOfFrames * sizeof(float));
XCTAssert(abl->mBuffers[1].mDataByteSize <= inNumberOfFrames * sizeof(float));
return abl;
}];
XCTAssert(success);
[engine startAndReturnError:&error];
XCTAssertNil(error);
auto outputBuffer = [[AVAudioPCMBuffer alloc] initWithPCMFormat:format
frameCapacity:1024];
XCTAssertNotNil(outputBuffer);
XCTAssert(engine.isInManualRenderingMode);
auto status = [engine renderOffline:32 toBuffer:outputBuffer error:&error];
if(status == AVAudioEngineManualRenderingStatusInsufficientDataFromInputNode) {
printf("manual rendering failed: AVAudioEngineManualRenderingStatusInsufficientDataFromInputNoden");
}
XCTAssertEqual(status, AVAudioEngineManualRenderingStatusSuccess);
if (error) {
NSLog(@"error: %@", error);
}
XCTAssertNil(error);
}

我得到的是AVAudioEngineManualRenderingStatusInsufficientDataFromInputNode,但输入缓冲区有很多数据。我错过了什么?查找此示例代码时遇到问题。

我已经考虑了几个下午,经过一番讨论,我想我已经为你的问题找到了解决方案,并回答了为什么这没有像你预期的那样奏效。

AVAudioEngineinputNode默认为AVAudioInputNode或系统音频接口的输入。当引擎处于手动渲染模式时,输入和输出节点都会断开与音频接口的连接,但许多设置都保持不变。所以hwFormat.sampleRate必须==format.sampleRate,否则[engine connect]调用将失败。

由于音频接口断开,我们无法从输入中获取数据,因此我们可以使用setManualRenderingInput来确定如何提供数据。我们可以要求[engine renderOffline]呈现一个帧Capacity,但显然,这不能保证是在我们的inputBlock内传递的==inNumberOfFrames值,它显然会根据所选的音频设备及其相应的sampleRate而变化。为了使renderOffline调用成功,AudioBuffer.mDataByteSize必须==inNumberOfFrames * sizeof(float),否则renderOffline调用将抛出不足的数据。由于我们无法在调用renderOffline之前确保inNumberOfFrames的值,因此AudioBuffer的分配必须等到inputBlock

我肯定有一些东西我错过了。。。但无论如何。。。试试下面的更改,看看这是否适合你。

- (void)testManualRendering {
auto engine = [[AVAudioEngine alloc] init];
auto hwFormat = [engine.inputNode inputFormatForBus:0];
NSLog(@"HW Format: %@", hwFormat);

///Note: SampleRate will have to match the systems audio interface input sample rate or engine connect will throw an error
auto format = [[AVAudioFormat alloc] initStandardFormatWithSampleRate:[hwFormat sampleRate] channels:2];

NSLog(@"Input Node: %@", [engine inputNode]);

auto frameCapacity = 1024;

AudioBufferList *abl = (AudioBufferList *)calloc(sizeof(AudioBufferList) + sizeof(AudioBuffer), 1);
XCTAssert(NULL != abl, @"Unable to allocate memory");
abl->mNumberBuffers = [format channelCount];

XCTAssertEqual(abl->mNumberBuffers, [format channelCount]);

[engine connect:engine.inputNode to:engine.mainMixerNode format:format];
NSError* error;
[engine enableManualRenderingMode:AVAudioEngineManualRenderingModeOffline
format:format
maximumFrameCount:frameCapacity error:&error];
XCTAssertNil(error);
XCTAssert([format isEqual:engine.manualRenderingFormat]);
NSLog(@"manualRenderingFormat: %@", engine.manualRenderingFormat);
auto success = [engine.inputNode setManualRenderingInputPCMFormat:format
inputBlock:^const AudioBufferList * _Nullable(AVAudioFrameCount inNumberOfFrames) {

auto byteSize = UInt32((inNumberOfFrames) * sizeof(float));

unsigned i, j;

for(i=0; i<abl->mNumberBuffers; ++i){
abl->mBuffers[i].mData = calloc(sizeof(float), inNumberOfFrames);
XCTAssert(NULL != abl->mBuffers[i].mData, @"Unable to allocate memory.");
abl->mBuffers[i].mDataByteSize = byteSize;
abl->mBuffers[i].mNumberChannels = 1;

XCTAssert( abl->mBuffers[i].mDataByteSize > 0);
XCTAssert( abl->mBuffers[i].mDataByteSize >= inNumberOfFrames * sizeof(float));
float *ptr = (float *)abl->mBuffers[i].mData;

//possibly load in some random data? WHY NOT!
for(j=0; j<inNumberOfFrames; ++j){
*ptr = (rand() / float(RAND_MAX)) * 0.8;
ptr++;
}

}

NSLog(@"Number of input frames: %i, size of frames (bytes): %lu, buffer capacity: %i", inNumberOfFrames, inNumberOfFrames * sizeof(float), abl->mBuffers[0].mDataByteSize);

return abl;
}];
XCTAssert(success);
[engine startAndReturnError:&error];
XCTAssertNil(error);
auto outputBuffer = [[AVAudioPCMBuffer alloc] initWithPCMFormat:format
frameCapacity:frameCapacity];
XCTAssertNotNil(outputBuffer);
XCTAssert(engine.isInManualRenderingMode);
auto status = [engine renderOffline:frameCapacity toBuffer:outputBuffer error:&error];

if(status == AVAudioEngineManualRenderingStatusInsufficientDataFromInputNode) {
printf("manual rendering failed: AVAudioEngineManualRenderingStatusInsufficientDataFromInputNoden");
}
XCTAssertEqual(status, AVAudioEngineManualRenderingStatusSuccess);
if (error) {
NSLog(@"error: %@", error);
}
XCTAssertNil(error);

//free buffers
unsigned i;
for(i=0; i<abl->mNumberBuffers; ++i){
free(abl->mBuffers[i].mData);
}
free(abl);
}

相关内容

  • 没有找到相关文章

最新更新