////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不能完美同步所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)