【问题标题】:AVAudioPlayer loops are not seamlessAVAudioPlayer 循环不是无缝的
【发布时间】:2021-04-04 15:54:09
【问题描述】:

我正在创建和播放一个 AVAudioPlayer,如下所示:

playerOne = try AVAudioPlayer(contentsOf: URL.init(fileURLWithPath: path))
playerOne.numberOfLoops = -1
playerOne.prepareToPlay()

我正在播放 AAC 文件。我正在使用

playerOne.play(atTime: startTime)

安排未来的播放并同步多个 AVAudioPlayer。

一切正常,但我的问题是当声音循环不同步时,这是由于循环不是无缝的。

这里发生的情况是,由于 aac 解码器,我相信解码后的音频数据中会添加一个非常小的静音,这会导致音频播放器之间的同步丢失。我希望这个循环是完美的,从头到尾循环之间的间隙为 0。

如何使用 AVAudioPlayer 实现无缝循环播放?

【问题讨论】:

  • 完全正确!我发现了同样的事情,尽管我的理解是启动/剩余帧应该由 AVAudioPlayer 处理。非常令人沮丧。你找到解决方法了吗?只是要注意;在我的情况下,这不是同步的事情,而只是我听到了循环上的间隙——基本上就像没有处理启动/剩余帧一样。
  • @jbm 我尝试执行以下操作:有两个玩家,第二个玩家在第一个玩家停止时开始玩。然后这不起作用,因为显然 Apple API 从来没有给出 %100 准确的持续时间......我对此感到非常失望。我无法解决我的问题。 AudioKit 进行无缝循环,您可以检查该库,但它也有其局限性,例如我无法设置它的缓冲区大小并且整个文件被加载到 ram 中导致巨大的内存分配和内存浪费......
  • 其实我用的是AudioKit,但是他们的AKPlayer只是封装了AVAudioPlayer。我想我可以在 AudioKit 中寻找替代品...
  • @jbm 与 A​​udioKit 无缝循环,在播放播放器之前,您必须具有以下行 player.buffer = true。我使用了 AudioKit 并对其进行了测试,它可以使循环真正无缝,0 间隙和循环
  • 我正在使用 v4,其中相同的功能是“.buffer = .always”,但显然它实际上不起作用。

标签: ios avaudioplayer


【解决方案1】:

使用AVAudioEngine 会给您带来很大的灵活性,但如果您不需要其他任何东西,只需同步您的曲目,就会产生开销。

在这种情况下,您可以尝试使用包含您所有曲目的AVComposition 的单个播放器,如下所示:

func generateComposition(urls: [URL]) throws -> AVComposition {
    let composition = AVMutableComposition()
    let audioTracks = urls
        .map(AVAsset.init(url:))
        .flatMap { $0.tracks(withMediaType: .audio) }
    
    for audioTrack in audioTracks {
        guard
            let compositionTrack = composition.addMutableTrack(withMediaType: .audio, preferredTrackID: kCMPersistentTrackID_Invalid)
        else { continue }
        
        try compositionTrack.insertTimeRange(
            audioTrack.timeRange,
            of: audioTrack,
            at: .zero
        )
    }
    return composition
}

然后使用AVPlayer播放它:

AVPlayer(playerItem: AVPlayerItem(asset: composition))

【讨论】:

  • 如果我要使用 AVAudioEngine,AVAudioEngine 中的 AVAudioPlayer 用什么替代?
  • 您需要为每个音轨创建 AVAudioPlayerNode,攻击它们到引擎,例如engine.attach(player),然后安排一个文件为每个播放器player.scheduleFile(...) 播放。安排好所有文件后,您可以为每个文件运行 prepare player.prepare(withFrameCount:),所以当您使用 player.play() 启动它时,我会立即启动。然后你等待每个玩家(你可以使用完成scheduleFile 方法)完成并再次执行所有操作以开始播放
【解决方案2】:

由于循环不是无缝的。

我不确定您所说的“不无缝”是什么意思,但如果问题是您遇到延迟,那么使用 AVAudioPlayer 很自然。如果目标是以最小延迟循环,请使用 AVAudioEngine。

【讨论】:

  • 我的意思是我相信编码器在解码文件的最后添加了一个小的静默,因为解码的数据包没有被完全填充。一些解码器会这样做。我也在 ogg 解码器中遇到过这种情况,延迟是什么意思?是音频延迟,也就是音频通过设备并通过扬声器被听到所需的时间!无缝循环是一个循环,其中结尾和开头都有 0 静音,这可能是由编码器或其他原因引起的。无缝循环,循环样本完美,0 间隙,是一个完美循环
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-12-06
  • 2014-01-12
  • 2015-05-03
  • 2019-05-13
  • 1970-01-01
  • 2019-06-20
  • 1970-01-01
相关资源
最近更新 更多