如何在 AudioEngine 的默认 inputNode 上安装 tap 并写入文件

How to install a tap on the default inputNode of AudioEngine and write to file

我正在尝试做一些非常简单的事情,但我只是断断续续地取得了进展。我现在只想将音频数据从麦克风获取到文件中。一旦我克服了这个障碍,我将对块中的数据进行更多处理。我已经厌倦了我在 AVAudioEngine 中发现的一些想法,我想知道我是否应该回到 AudioUnits,它可能调试得更好(尽管更复杂)。

首先,似乎在inputNode实例化之前无法启动AudioEngine,然后您无法使用其outputFormatForBus获取其格式(采样率为零);你必须使用 inputFormatForBus.

现在我已经可以正常工作了,而且我正在使用似乎是有效数据的块进行调用,我无法在不生成异常的情况下写入文件,并出现错误:

'error writing buffer data to file, The operation couldn’t be completed. (com.apple.coreaudio.avfaudio error 1768846202.)'

('insz') 这似乎表明我提供的格式或块获取的格式存在某种错误。

有什么想法吗?

_engine = [[AVAudioEngine alloc] init];

_outputFileURL = [NSURL URLWithString:[NSTemporaryDirectory() stringByAppendingString:@"tempOutput.caf"]];

AVAudioInputNode *inputNode = [_engine inputNode];
AVAudioFormat *format = [inputNode inputFormatForBus:1];
NSMutableDictionary *recordSettings = format.settings.mutableCopy;
[recordSettings addEntriesFromDictionary:@{
                              AVFormatIDKey : @(kAudioFormatMPEG4AAC),
                              AVEncoderAudioQualityKey : @(AVAudioQualityMedium)
                              }];

AVAudioFile *outputFile = [[AVAudioFile alloc] initForWriting:_outputFileURL settings:recordSettings error:&error];

[inputNode installTapOnBus:1 bufferSize:4096 format:format block:^(AVAudioPCMBuffer *buffer, AVAudioTime *when) {
    NSError *error;

    // as AVAudioPCMBuffer's are delivered this will write sequentially. The buffer's frameLength signifies how much of the buffer is to be written
    // IMPORTANT: The buffer format MUST match the file's processing format which is why outputFormatForBus: was used when creating the AVAudioFile object above
    NSAssert([outputFile writeFromBuffer:buffer error:&error], @"error writing buffer data to file, %@", [error localizedDescription]);
}];
if (!_engine.isRunning) [self startEngine];

谢谢。

根据@matt 的评论更新了代码

这是我解决这个问题的方法,以防其他人遇到同样的问题。

@matt 正确地评论说我应该创建独立的设置,而不是使用与 inputNode 关联的格式。

commonFormat = [[AVAudioFormat alloc] initWithCommonFormat:AVAudioPCMFormatFloat32 sampleRate:44100 channels:2 interleaved:NO];

engine = [[AVAudioEngine alloc] init];
AVAudioInputNode *inputNode = engine.inputNode;

NSError *error;
AVAudioFile *outputFile = [[AVAudioFile alloc] initForWriting:_outputFileURL settings:commonFormat.settings error:&error];

[inputNode installTapOnBus:0 bufferSize:4096 format:commonFormat block:^(AVAudioPCMBuffer *buffer, AVAudioTime *when) {
    NSError *error;

    NSAssert([outputFile writeFromBuffer:buffer error:&error], @"error writing buffer data to file, %@", [error localizedDescription]);
}];