用户互动后保持多个AVAudioPlayers同步

时间:2018-11-28 19:11:00

标签: ios swift avaudioplayer avaudiosession

我正在开发一个iOS应用,该应用可以让用户播放多首歌曲。每个曲目同时播放,每个曲目的音量可以独立管理。在应用程序的生命周期内首次播放歌曲时,所有播放器之间的等待时间都很好。但是,当用户停止播放所有曲目并随后恢复播放时,所有播放器之间的等待时间都会增加。

我为MusicPlayer-Class中的每个音轨创建一个AVAudioPlayer:

    do {
        let data = try Data.init(contentsOf: url, options: NSData.ReadingOptions.alwaysMapped )
        let player = try AVAudioPlayer(data: data)
        player.prepareToPlay()

        self.audioPlayers.append(player)
    }

在加载播放器之后,通过playSong-Method开始播放歌曲:

playerSemaphore.wait()

    NotificationCenter.default.post(playerDidReceiveCommand.getNotification())

    for item in self.audioPlayers {
        item.prepareToPlay()
    }


    let time = self.audioPlayers[0].deviceCurrentTime + 0.2
    for item in self.audioPlayers {
        item.play(atTime: time)
    }

    DispatchQueue.main.async {
        NotificationCenter.default.post(playerFinishedCommand.getNotification())
    }


    playerSemaphore.signal()

当用户决定暂停歌曲或AudioSession被中断时,pauseSong-Method通过将其播放器的音量设置为0,暂停播放器,同步播放时间并重置适当的音量来暂停所有播放器:

func pauseSong() {
    playerSemaphore.wait()

    NotificationCenter.default.post(playerDidReceiveCommand.getNotification())

    DispatchQueue.global().async {

        // Set volumes of all audio tracks to 0, so delay on pause is not noticed
        for audioTrackPlayer in self.audioPlayers {
            audioTrackPlayer.volume = 0
        }

        // Update Times and reset the volumes now the tracks are paused
        for (index, audioTrackPlayer) in self.audioPlayers.enumerated() {
            audioTrackPlayer.pause()
            audioTrackPlayer.currentTime = self.audioPlayers[0].currentTime
            if let currentSongID = self.loadedSongID, let currentVolume = sharedSongManager.getVolumeOfTrackOfSongID(id: currentSongID, track: index), let currentActivation = sharedSongManager.getActivationStatusOfTrackOfSongID(id: currentSongID, track: index) {
                if currentActivation == true {
                    audioTrackPlayer.volume = currentVolume
                }
            }
        }

        DispatchQueue.main.async {
            NotificationCenter.default.post(playerFinishedCommand.getNotification())
        }
    }

    playerSemaphore.signal()
}

为了记录玩家时间,我编写了一个方法,该方法返回所有玩家时间以及该数组的max-Value和min-Value之差。它与时间偏移配合使用以获取玩家时间的准确值:

let startTime = CACurrentMediaTime()
    var playerTimes = [TimeInterval]()
    var delay: Double?

    for item in self.audioPlayers.enumerated() {
        let playerTime = item.element.currentTime
        let currentTime = CACurrentMediaTime()
        let timeOffset = currentTime - startTime
        let correctedPlayerTime = playerTime - timeOffset
        playerTimes.append(correctedPlayerTime)
    }

    if let maxTime = playerTimes.max(), let minTime = playerTimes.min() {
        delay = Double(maxTime) - Double(minTime)
    }

    return (delay, playerTimes)

以下是暂停前后的延迟列表(最大值-播放时间的负值):

delay before pausing delay after pausing

以下是暂停前后一本日志的播放时间:

playertimes before pausing playertimes after pausing

所有日志都是在装有最新iOS的iPhone X上创建的。

加载新歌曲后,生命周期将重新开始,并且在用户首次暂停歌曲之前,我的等待时间很短。我不喜欢在每次交互后创建新的AVAudioPlayers的想法,但是我尝试了很多事情,例如像在日志记录函数中那样计算延迟或异步调用播放器实例。但是,在大多数情况下,这甚至会增加延迟。没有人知道恢复歌曲后正确同步播放器的方法吗?

0 个答案:

没有答案