我正在开发一个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)
以下是暂停前后的延迟列表(最大值-播放时间的负值):
以下是暂停前后一本日志的播放时间:
所有日志都是在装有最新iOS的iPhone X上创建的。
加载新歌曲后,生命周期将重新开始,并且在用户首次暂停歌曲之前,我的等待时间很短。我不喜欢在每次交互后创建新的AVAudioPlayers的想法,但是我尝试了很多事情,例如像在日志记录函数中那样计算延迟或异步调用播放器实例。但是,在大多数情况下,这甚至会增加延迟。没有人知道恢复歌曲后正确同步播放器的方法吗?