AVAudioEngine:立即启动一组AVAudioPlayerNodes

时间:2016-03-10 09:13:49

标签: ios core-audio avaudioengine

我正在开发一个同时启动多个循环的应用程序,并且应该保持同步。

使用以前的,天真的问题解决方法(不使用AVAudioEngine),我发现按顺序以编程方式启动多个音频播放器会在调用之间产生足够的延迟,从而使结果无效;节拍听起来不同步。

我可以使用AVAudioEngine实现这种功能吗?

目前我正在将AVAudioPlayerNodes连接到调音台,我已经将缓冲区连接到它们并从那里控制输入。但我能同时开始吗?

似乎节点在我调用播放之前不会开始产生声音,而且在引擎启动之前无法完成...

3 个答案:

答案 0 :(得分:4)

有关更深入的解释,请查看我在

中的答案

AVAudioEngine multiple AVAudioInputNodes do not play in perfect sync

简而言之:

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

AVAudioFormat *outputFormat = [playerA outputFormatForBus:0];

const float kStartDelayTime = 0.0; // seconds - in case you wanna delay the start

AVAudioFramePosition startSampleTime = playerA.lastRenderTime.sampleTime;

AVAudioTime *startTime = [AVAudioTime timeWithSampleTime:(startSampleTime + (kStartDelayTime * outputFormat.sampleRate)) atRate:outputFormat.sampleRate];

[playerA playAtTime: startTime];
[playerB playAtTime: startTime];
[playerC playAtTime: startTime];
[playerD playAtTime: startTime];

[player...

顺便说一下 - 您可以使用AVAudioPlayer类获得相同的100%样本帧精确结果......

NSTimeInterval startDelayTime = 0.0; // seconds - in case you wanna delay the start
NSTimeInterval 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 ,你很高兴。并且永远不要忘记提前 prepareToPlay 你的玩家,以便在开始时不需要进行额外的缓冲或设置 - 只需启动他们; - )

答案 1 :(得分:1)

正确的方法是使用调音台音频设备。最低限度你会得到一个带调音台和remoteIO的图表。

创建一个带两个输入的混音器。 iOS音频系统的pull架构将同时播放两个音频缓冲区。

答案 2 :(得分:0)

我通过循环遍历需要同步的AVAudioPlayerNode实例并为每一个实现了我想要的效果:

  1. 致电'play'
  2. 安排适当的缓冲区以便在将来的某个时间开始
  3. 代码看起来像这样:

        for (AVAudioPlayerNode *playerNode in playerNodes) {
            [playerNode play];
            [playerNode scheduleBuffer:loopBufferForThisPlayer atTime:startTime options:AVAudioPlayerNodeBufferLoops completionHandler:nil];
        }
    

    startTime中指定的时间是近期(300ms左右)的时间,以确保所有播放器节点都可以播放。 startTime必须基于主机时间;将时间基于您的样本,事情将不同步。

    所有这些节点都附加到AVAudioMixerNode,如问题中所述。

    我并不热衷于以这种方式使用延迟;我更愿意,如果我的节点在他们准备好的时候告诉我......但是这样可行。