我想从麦克风实时输入波形显示。 我已经使用installTapOnBus:bufferSize:format:block:来实现,这个函数在一秒钟内被调用三次。 我想将此函数设置为每秒调用20次。 我在哪里可以设置?
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
NSError* error = nil;
if (audioSession.isInputAvailable) [audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:&error];
if(error){
return;
}
[audioSession setActive:YES error:&error];
if(error){
retur;
}
self.engine = [[[AVAudioEngine alloc] init] autorelease];
AVAudioMixerNode* mixer = [self.engine mainMixerNode];
AVAudioInputNode* input = [self.engine inputNode];
[self.engine connect:input to:mixer format:[input inputFormatForBus:0]];
// tap ... 1 call in 16537Frames
// It does not change even if you change the bufferSize
[input installTapOnBus:0 bufferSize:4096 format:[input inputFormatForBus:0] block:^(AVAudioPCMBuffer* buffer, AVAudioTime* when) {
for (UInt32 i = 0; i < buffer.audioBufferList->mNumberBuffers; i++) {
Float32 *data = buffer.audioBufferList->mBuffers[i].mData;
UInt32 frames = buffer.audioBufferList->mBuffers[i].mDataByteSize / sizeof(Float32);
// create waveform
...
}
}];
[self.engine startAndReturnError:&error];
if (error) {
return;
}
答案 0 :(得分:12)
是的,目前我们内部有一个固定的分接缓冲区大小(0.375s), 并且客户端指定的tap的缓冲区大小没有生效。
但有人调整缓冲区大小并获得40毫秒 https://devforums.apple.com/thread/249510?tstart=0
无法检查,在ObjC中:(
UPD 它有效!只是单行:
[input installTapOnBus:0 bufferSize:1024 format:[mixer outputFormatForBus:0] block:^(AVAudioPCMBuffer *buffer, AVAudioTime *when) {
buffer.frameLength = 1024; //here
答案 1 :(得分:5)
AVAudioNode类引用指出实现可能会选择一个缓冲区大小而不是你提供的缓冲区大小,所以据我所知,我们会遇到非常大的缓冲区大小。这很不幸,因为AVAudioEngine是一款优秀的Core Audio包装器。由于我也需要使用输入分接头来进行录制以外的其他操作,我正在研究The Amazing Audio Engine,以及Core Audio C API(请参阅iBook Learning Core Audio以获得优秀的教程)作为替代方案。
***更新:事实证明您可以访问AVAudioInputNode的AudioUnit并在其上安装渲染回调。通过AVAudioSession,您可以设置音频会话所需的缓冲区大小(不保证,但肯定比节点点击更好)。到目前为止,我已经使用这种方法获得了低至64个样本的缓冲区大小。一旦我有机会对此进行测试,我将在此处发布代码。
答案 2 :(得分:2)
从2019年的iOS 13开始,有AVAudioSinkNode,它可以更好地完成您要寻找的内容。虽然您还可以创建常规的AVAudioUnit / Node并将其附加到输入/输出,但与AVAudioSinkNode的区别在于不需要输出。这使它更像是轻按,并且规避了使用常规音频单元/节点时可能发生的链不完整的问题。
有关更多信息:
相关的Swift代码在会话PDF的第10页上(下面已纠正了一个小错误)。
// Create Engine
let engine = AVAudioEngine()
// Create and Attach AVAudioSinkNode
let sinkNode = AVAudioSinkNode() { (timeStamp, frames, audioBufferList) ->
OSStatus in
…
}
engine.attach(sinkNode)
我想在使用此功能时,您仍然必须遵循典型的实时音频规则(例如,没有分配/释放内存,没有ObjC调用,没有锁定或等待锁定等)。环形缓冲区在这里可能仍然有用。
答案 3 :(得分:0)
您可以使用CADisplayLink
来实现此目的。每次屏幕刷新时,CADisplayLink
都会给你一个回调,这通常会超过每秒20次(因此可能需要额外的逻辑来限制或限制你的方法在你的情况下执行的次数)。
这显然是一个与音频工作完全不同的解决方案,并且在您需要反映会话的解决方案的范围内,它可能无效。但是当我们需要在iOS上频繁重复回调时,这通常是首选方法,所以这是一个想法。
答案 4 :(得分:0)
不知道为什么,或者即使这个有效,只是尝试了一些事情。但是肯定NSLogs指示一个21毫秒的间隔,每个缓冲区有1024个样本......
AVAudioEngine* sEngine = NULL;
- (void)applicationDidBecomeActive:(UIApplication *)application
{
/*
Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
*/
[glView startAnimation];
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
NSError* error = nil;
if (audioSession.isInputAvailable) [audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:&error];
if(error){
return;
}
[audioSession setActive:YES error:&error];
if(error){
return;
}
sEngine = [[AVAudioEngine alloc] init];
AVAudioMixerNode* mixer = [sEngine mainMixerNode];
AVAudioInputNode* input = [sEngine inputNode];
[sEngine connect:input to:mixer format:[input inputFormatForBus:0]];
__block NSTimeInterval start = 0.0;
// tap ... 1 call in 16537Frames
// It does not change even if you change the bufferSize
[input installTapOnBus:0 bufferSize:1024 format:[input inputFormatForBus:0] block:^(AVAudioPCMBuffer* buffer, AVAudioTime* when) {
if (start == 0.0)
start = [AVAudioTime secondsForHostTime:[when hostTime]];
// why does this work? because perhaps the smaller buffer is reused by the audioengine, with the code to dump new data into the block just using the block size as set here?
// I am not sure that this is supported by apple?
NSLog(@"buffer frame length %d", (int)buffer.frameLength);
buffer.frameLength = 1024;
UInt32 frames = 0;
for (UInt32 i = 0; i < buffer.audioBufferList->mNumberBuffers; i++) {
Float32 *data = buffer.audioBufferList->mBuffers[i].mData;
frames = buffer.audioBufferList->mBuffers[i].mDataByteSize / sizeof(Float32);
// create waveform
///
}
NSLog(@"%d frames are sent at %lf", (int) frames, [AVAudioTime secondsForHostTime:[when hostTime]] - start);
}];
[sEngine startAndReturnError:&error];
if (error) {
return;
}
}