ios – AVAudioEngine多个AVAudioInputNodes不能完美同步

ios – AVAudioEngine多个AVAudioInputNodes不能完美同步,第1张

概述我一直在尝试使用AVAudioEngine来安排多个音频文件以完美的同步方式播放,但是在收听输出时,输入节点之间似乎有一点点延迟.音频引擎使用以下图表实现: ////AVAudioPlayerNode1 -->//AVAudioPlayerNode2 -->//AVAudioPlayerNode3 --> AVAudioMixerNode --> AVAudioUnitVarispeed - 我一直在尝试使用AVAudioEngine来安排多个音频文件以完美的同步方式播放,但是在收听输出时,输入节点之间似乎有一点点延迟.音频引擎使用以下图表实现:

////AVAudioPlayerNode1 -->//AVAudioPlayerNode2 -->//AVAudioPlayerNode3 --> AVAudiomixerNode --> AVAudioUnitvarispeed ---> AvAudioOutputNode//AVAudioPlayerNode4 -->                                            |//AVAudioPlayerNode5 -->                                        AudioTap//      |                                                         //AVAudioPCMBuffers    //

我正在使用以下代码加载示例并同时安排它们:

- (voID)scheduleInitialAudioBlock:(SBScheduledAudioBlock *)block {    for (int i = 0; i < 5; i++) {        Nsstring *path = [self assetPathForChannel:i trackItem:block.trackItem]; //this fetches the right audio file path to be played        AVAudioPCMBuffer *buffer = [self bufferFromfile:path];        [block.buffers addobject:buffer];    }    AVAudioTime *time = [[AVAudioTime alloc] initWithSampleTime:0 atRate:1.0];    for (int i = 0; i < 5; i++) {        [inputNodes[i] scheduleBuffer:block.buffers[i]                                   atTime:time                                  options:AVAudioPlayerNodeBufferInterrupts                        completionHandler:nil];    }}- (AVAudioPCMBuffer *)bufferFromfile:(Nsstring *)filePath {    NSURL *fileURl = [NSURL fileURLWithPath:filePath];    NSError *error;    AVAudiofile *audiofile = [[AVAudiofile alloc] initForReading:fileURl commonFormat:AVAudioPCMFormatfloat32 interleaved:NO error:&error];    if (error) {        return nil;    }    AVAudioPCMBuffer *buffer = [[AVAudioPCMBuffer alloc] initWithPCMFormat:audiofile.processingFormat frameCapacity:audiofile.length];    [audiofile readIntoBuffer:buffer frameCount:audiofile.length error:&error];    if (error) {        return nil;    }    return buffer;}

我注意到这个问题只能在设备上看到,我正在测试iPhone5s,但我无法弄清楚为什么音频文件播放不同步,任何帮助都将非常感激.

**答案**

我们最终使用以下代码对问题进行排序:

AVAudioTime *startTime = nil;for (AVAudioPlayerNode *node in inputNodes) {    if(startTime == nil) {        const float kStartDelayTime = 0.1; // sec        AVAudioFormat *outputFormat = [node outputFormatForBus:0];        AVAudioFrameposition startSampleTime = node.lastRenderTime.sampleTime + kStartDelayTime * outputFormat.sampleRate;        startTime = [AVAudioTime timeWithSampleTime:startSampleTime atRate:outputFormat.sampleRate];    }    [node playAtTime:startTime];}

这为每个AVAudioinputNode提供了足够的时间来加载缓冲区并修复了我们所有的音频同步问题.希望这有助于他人!

解决方法 问题:

好吧,问题是你在playAt之前的for循环的每次运行中检索你的player.lastRenderTime:

So,you’ll actually get a different Now-time for every player!

你这样做的方式你也可以通过play来启动循环中的所有玩家:或者playAtTime:nil !!!失去同步会导致相同的结果……

出于同样的原因,你的播放器在不同的设备上以不同的方式不同步,取决于机器的速度;-)你现在的时间是随机的魔术数字 – 所以,不要认为它们总是会工作如果他们恰巧在你的场景中工作.即使是由于繁忙的运行循环或cpu导致的最小延迟也会让您再次失去同步…

解:

你真正需要做的是在循环之前获得Now = player.lastRenderTime的一个离散快照,并使用这个非常相同的锚点,以获得所有玩家的批量同步开始.

这样你甚至不需要延迟你的玩家的开始.不可否认,该系统将剪切一些领先的框架 – (但当然每个玩家的数量相同;-) – 以弥补您最近设置的(实际上已经过去和已经过去)与实际之间的差异playTime(在不久的将来仍然处于领先地位),但最终会使你的所有玩家完全同步,就好像你现在真的已经开始了它们一样.这些剪裁的框架几乎从不会引人注目,您可以放心地回应…

如果您碰巧需要这些帧 – 因为文件/段/缓冲区启动时发出咔嗒声或瑕疵 – 现在,通过延迟启动播放器,将您现在转移到未来.但是当然你在按下开始按钮后会有一点滞后 – 虽然当然仍然完美同步……

结论:

这里的重点是现在为所有玩家提供一个单一参考,并在捕获此现在参考后尽快调用playAtTime:Now方法.间隙越大,剪裁的前导框架部分越大 – 除非您提供合理的启动延迟并将其添加到您现在的时间,这当然会在按下您的开始按钮后导致延迟启动时无响应.

并且始终要注意这样一个事实 – 无论设备是由音频缓冲机制产生的任何延迟 – 它都不会影响任何数量的播放器的同步性,如果以正确的,上述方式完成!它也不会延迟你的音频!实际上让您听到音频的窗口会在稍后的时间点打开…

请注意:

>如果您选择未延迟(超响应)启动选项
无论出于何种原因,都会产生很大的延迟
捕获现在和你的玩家的实际开始),你会的
截断你的一个大的领先部分(最多约300毫秒/ 0.3秒)
音频.这意味着当您启动播放器时,它将立即开始
但不是从你最近暂停它的位置恢复而是
(最多约300毫秒)后面的音频.所以声学感知就是这样
暂停播放可以随时随地切断音频的一部分,尽管一切都完全同步.
>作为playAtTime中提供的开始延迟:现在
myProvIDedDelay方法调用是一个固定的常量值(没有得到
动态调整以适应缓冲延迟或其他变化
系统负载较重时的参数)甚至可以选择延迟选项
提供的延迟时间小于约300ms可能会导致a
如果依赖于设备的准备,则剪切领先的音频样本
时间超过了您提供的延迟时间.
>最大削波量(按设计)不会大于
这些~300ms.要获得证明只是强制控制(样本准确)剪辑
例如,领先的帧到目前为止添加一个负延迟时间
你会通过增加这个来感知不断增长的剪辑音频部分
负值.每个负值大于300毫秒
整理到~300ms.因此提供30秒的负延迟
导致与负值10,6,3或1相同的行为
秒,当然还包括负0.8秒,0.5秒
到~0.3

此示例非常适用于演示目的,但不应在生产代码中使用负延迟值.

注意:

在多人游戏设置中最重要的是保持你的播放器.暂停同步.截至2016年6月,AVAudioPlayerNode中仍然没有同步退出策略.

只需要一个小方法查找或在两个播放器之间向控制台注销一些内容.暂停调用可能会强制后者执行一个甚至更多的帧/样本,而不是前一个.所以你的玩家实际上不会及时停在相同的相对位置.最重要的是 – 不同的设备会产生不同的行为……

如果您现在以上述(同步)方式启动它们,那么您在上次暂停时的这些不同步的当前玩家位置肯定会在每次playAtTime强制同步到您现在的新位置: – 基本上意味着您每次重新启动播放器时都会将丢失的样本/帧传播到未来.这当然会增加每个新的开始/暂停周期并扩大差距.做这五十或一百次,你已经得到了一个很好的延迟效果,而不使用效果音频单元;-)

因为我们没有(通过系统提供的)控制这个因素唯一的补救措施是把所有的调用都放在播放器上.暂停一个接一个地按顺序排列,而不是它们之间的任何东西,就像你可以看到的那样以下示例.不要将它们放入for循环或任何类似的东西 – 这将保证在播放器的下一个暂停/开始时结束不同步…

是否将这些呼叫保持在一起是100%完美的解决方案,或者任何大cpu负载下的运行循环可能会偶然干扰并强制分离暂停呼叫,导致帧丢失 – 我不知道 – 至少在几周内搞乱AVAudioNode API我绝不会强迫我的多人游戏设备失去同步 – 但是,我仍然感觉不舒服或安全这种非同步,随机魔术数暂停解…

代码示例和替代方案:

如果您的引擎已经运行,您在AVAudioNode中获得了@property lastRenderTime – 您的播放器的超类 – 这是您获得100%样本帧精确同步的门票…

AVAudioFormat *outputFormat = [playerA outputFormatForBus:0];const float kStartDelayTime = 0.0; // seconds - in case you wanna delay the startAVAudioFrameposition Now = playerA.lastRenderTime.sampleTime;AVAudioTime *startTime = [AVAudioTime timeWithSampleTime:(Now + (kStartDelayTime * outputFormat.sampleRate)) atRate:outputFormat.sampleRate];[playerA playAtTime: startTime];[playerB playAtTime: startTime];[playerC playAtTime: startTime];[playerD playAtTime: startTime];[player...

顺便说一句 – 您可以使用AVAudioPlayer / AVAudioRecorder类实现相同的100%样本帧精确结果…

NSTimeInterval startDelayTime = 0.0; // seconds - in case you wanna delay the startNSTimeInterval Now = playerA.deviceCurrentTime;NSTimeIntervall startTime = Now + startDelayTime;[playerA playAtTime: startTime];[playerB playAtTime: startTime];[playerC playAtTime: startTime];[playerD playAtTime: startTime];[player...

没有startDelayTime,所有玩家的前100-200秒将被截断,因为启动命令实际上花费时间到运行循环,尽管玩家已经开始(好,已经安排)100%同步.但是使用startDelayTime = 0.25你很高兴.并且永远不要忘记准备好提前播放你的玩家,以便在开始时不需要额外的缓冲或设置 – 只需启动他们;-)

总结

以上是内存溢出为你收集整理的ios – AVAudioEngine多个AVAudioInputNodes不能完美同步全部内容,希望文章能够帮你解决ios – AVAudioEngine多个AVAudioInputNodes不能完美同步所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/web/1085928.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-27
下一篇 2022-05-27

发表评论

登录后才能评论

评论列表(0条)

保存