Сохраняйте несколько AVAudioPlayers синхронизированными после взаимодействия с пользователем - PullRequest
0 голосов
/ 28 ноября 2018

Я работаю над приложением для iOS, которое позволяет пользователям воспроизводить песню с несколькими треками.Каждый трек воспроизводится одновременно, и громкость каждого трека может управляться независимо.Задержка между всеми игроками хорошая, когда песня воспроизводится впервые в течение жизненного цикла приложения.Однако задержка между всеми игроками увеличивается, когда пользователь останавливает воспроизведение всех дорожек и затем возобновляет его.

Я создаю AVAudioPlayer для каждой дорожки в моем классе MusicPlayer:

    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:

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 приостанавливает все проигрыватель установкойих громкость до 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()
}

Для записи времени проигрывания я написал метод, который возвращает все значения времени проигрывания и разницу максимального значения иминимальное значение этого массива.Он работает со смещением по времени, чтобы получить точные значения для времен игрока:

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

Все журналы были созданы на iPhone X с последней версией iOS.

После загрузки новой песни жизненный цикл перезапускается, и яимеют низкую задержку, пока пользователь не остановит песню в первый раз.Мне не нравится идея создания новых AVAudioPlayers после каждого взаимодействия, но я пробовал много вещей, таких как вычисление задержки, как я делал в функции регистрации, или асинхронный вызов экземпляров проигрывателя.Однако в большинстве случаев это даже увеличивало задержку.Кто-нибудь знает способ правильной синхронизации проигрывателей после возобновления песни?

...