MPMusicPlayerController.systemMusicPlayer setОчередь сложности

У меня проблемы с моим музыкальным приложением. Идея состоит в том, чтобы нажать кнопку и воспроизвести больше песен исполнителя, который играет в данный момент. Когда я нахожусь в приложении и нажимаю следующую песню, все работает нормально. Однако, если я позволю песне закончиться естественным образом, приложение воспроизведет случайную песню. Поэтому я добавил логику, чтобы сказать, что если оставшееся время песен равно 0, тогда воспроизведите следующую песню, используя func медиаплееров, потому что я знаю, что это работает. Это решило проблему, за исключением случаев, когда приложение находится в фоновом режиме. Я пытался сохранить приложение, если оно работает в фоновом режиме, но я думаю, что это не работает.

Каждый раз, когда я думаю, что решил что-то, проблема возвращается.

Я ожидаю, что произойдет блокировка исполнителя, закрытие приложения в фоновом режиме и после окончания песни воспроизведение другой песни исполнителя.

Что на самом деле происходит, так это то, что когда я блокирую исполнителя и закрываю приложение в фоновом режиме, песня заканчивается, и иногда она воспроизводит нужную песню. Иногда этого не будет. ОДНАКО когда воспроизводится не та песня, и я открываю приложение в резервной копии, текущая (неправильная) воспроизводимая песня завершается и начинается воспроизведение песни правильного исполнителя

для ясности, я думаю, что неразумно использовать логику, которая запускается, когда оставшееся время песни равно 0, чтобы перейти к следующей песне, но по какой-то причине мне это нужно

Я установил возможности для фоновой загрузки и воспроизведения аудио и изображения в картинке.

Я пытался опубликовать только соответствующий код по порядку.

у меня есть недвижимость

let mediaPlayer = MPMusicPlayerController.systemMusicPlayer
var backgroundTask: UIBackgroundTaskIdentifier = UIBackgroundTaskInvalid

В моем ViewDidLoad

try? AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategorySoloAmbient)
try? AVAudioSession.sharedInstance().setActive(true)


DispatchQueue.main.async {
  self.clearSongInfo()
  MediaManager.shared.getAllSongs { (songs) in
    guard let theSongs = songs else {
      return
    }
    self.mediaPlayer.nowPlayingItem = nil

    self.newSongs = theSongs.filter({ (item) -> Bool in
      return !MediaManager.shared.playedSongs.contains(item)
    })
    self.aSongIsInChamber = true
    self.mediaPlayer.setQueue(with: MPMediaItemCollection(items: self.newSongs.shuffled())
    )
    self.mediaPlayer.shuffleMode = .off
    self.mediaPlayer.repeatMode = .none

  }
  NotificationCenter.default.addObserver(self, selector: #selector(self.songChanged(_:)), name: NSNotification.Name.MPMusicPlayerControllerNowPlayingItemDidChange, object: self.mediaPlayer)
  self.mediaPlayer.beginGeneratingPlaybackNotifications()
}

У меня есть функция, которая обновляет время воспроизведения и оставшееся время, и в этом у меня есть

if Int(self.songProgressSlider.maximumValue - self.songProgressSlider.value) < 1 {
  print("song ended naturally, skipped")
  mediaPlayer.prepareToPlay(completionHandler: { (error) in
    DispatchQueue.main.async {
      self.mediaPlayer.skipToNextItem()
    }
  })
}

Когда я играю музыку, у меня есть логическое значение isPlaying.

Если это так, то время бежит, и у меня есть это

let app = UIApplication.shared
  var task: UIBackgroundTaskIdentifier?
  task  = app.beginBackgroundTask {
    app.endBackgroundTask(task!)
  }

У меня есть это, чтобы таймер работал в фоновом режиме, поэтому он будет воспроизводить следующую песню, когда оставшееся время равно 0.

Теперь, чтобы заблокировать исполнителя, у меня есть следующий код, который выполняется при нажатии кнопки

let artistPredicate: MPMediaPropertyPredicate  = MPMediaPropertyPredicate(value: nowPlaying.artist, forProperty: MPMediaItemPropertyArtist)
      let query: MPMediaQuery = MPMediaQuery.artists()
      let musicPlayerController: MPMusicPlayerController = MPMusicPlayerController.systemMusicPlayer

      query.addFilterPredicate(artistPredicate)
      musicPlayerController.setQueue(with: query)

person RubberDucky4444    schedule 19.03.2018    source источник
comment
@solenoid Я хочу начать воспроизведение музыки в случайном порядке, а затем при нажатии кнопки установить в очереди песни этого исполнителя, а затем, если нажать снова, снять блокировку этого исполнителя   -  person RubberDucky4444    schedule 19.03.2018
comment
Ahh - вы хотите, чтобы текущая песня истекла, чем кнопка IF = нажата, начните воспроизведение песен исполнителя. Если вы отправляете новую очередь с текущей песней, останавливает ли она воспроизведение текущей песни? Я не думаю, что вам нужно делать что-то еще, кроме как снова изменить очередь, когда кнопка выходит из включенного состояния.   -  person solenoid    schedule 19.03.2018
comment
@solenoid нет, срок действия текущей песни не истекает, она просто фиксирует исполнителя, а затем следующая песня должна быть от того же исполнителя. Когда пользователь хочет остановить этого исполнителя, у меня есть аналогичный метод, который снимает блокировку. Это работает, если приложение находится на переднем плане   -  person RubberDucky4444    schedule 19.03.2018
comment
В вашем artistPredicate ваше значение - действительно ли оно возвращает то, что вы хотите? Я нигде не вижу его определенного, я использую: MPMusicPlayerController.systemMusicPlayer().nowPlayingItem.albumArtist   -  person solenoid    schedule 19.03.2018
comment
@ соленоид да, это так. Я могу попробовать это, когда вернусь домой, но, как я уже сказал, это работает. Только не тогда, когда приложение находится в фоновом режиме   -  person RubberDucky4444    schedule 19.03.2018
comment
Судя по звукам, что-то не может работать фоном; попробуйте просто установить очередь, нажать кнопку исполнителя и установить новую очередь, затем перейти в фоновый режим и протестировать ее (без каких-либо других действий). В моем приложении я в основном делаю это без проблем с фоновой очередью.   -  person solenoid    schedule 19.03.2018
comment
@solenoid Я пробовал это, поэтому я здесь. Я установил очередь, и не должно иметь значения, является ли приложение фоновым или передним планом, но когда приложение входит в фоновый режим, оно портит очередь   -  person RubberDucky4444    schedule 19.03.2018
comment
Давайте продолжим обсуждение в чате.   -  person solenoid    schedule 19.03.2018
comment
@solenoid решил это как бы   -  person RubberDucky4444    schedule 20.03.2018


Ответы (2)


Я считаю, что установка очереди не «возьмет», пока вы не скажете prepareToPlay (буквально или неявно, сказав play). В текущем издании моей книги говорится:

Мой опыт показывает, что игрок может вести себя неожиданным образом, если вы не попросите его play или хотя бы prepareToPlay сразу после установки очереди. По-видимому, очередь не вступит в силу, пока вы этого не сделаете.

Вы также сказали (в комментарии):

в идеале я хотел бы, чтобы песня заканчивалась первой, а затем шла в очередь, которая у меня есть

Хорошо, но тогда, возможно, вам нужна функция (новая в iOS 11) append, которая позволяет изменять очередь, не заменяя ее полностью.

person matt    schedule 20.03.2018
comment
в идеале я хотел бы, чтобы песня заканчивалась первой, а затем переходила в очередь, которую я установил. использование prepareToPlay прерывает песню, как и мой ответ. - person RubberDucky4444; 20.03.2018
comment
это становится сложным, потому что, когда пользователь устает от этого исполнителя, он / она может снова нажать кнопку и снять блокировку. Затем мне нужно будет удалить этого исполнителя, но только песни, которые уже воспроизводились (если не воспроизводился полный список песен этого исполнителя). Нет никакого метода удаления, который я вижу, чтобы отменить добавление, и если я добавляю, то я дважды добавляю этого исполнителя в очередь? - person RubberDucky4444; 20.03.2018

Я нашел решение, которое не на 100% соответствует тому, что я хотел, но на данный момент я его принимаю.

На IBAction, где я привязываюсь к исполнителю, я изначально просто

self.mediaPlayer.prepareToPlay { error in
  let artistPredicate: MPMediaPropertyPredicate  = MPMediaPropertyPredicate(value: nowPlaying.artist, forProperty: MPMediaItemPropertyArtist)
  let query: MPMediaQuery = MPMediaQuery.artists()
  let musicPlayerController: MPMusicPlayerController = MPMusicPlayerController.systemMusicPlayer

  query.addFilterPredicate(artistPredicate)
  musicPlayerController.setQueue(with: query)
}

То, что я сделал, чтобы решить мою проблему, добавлено

musicPlayerController.play()

после того, как я установил очередь.

Недостатком этого подхода является то, что если я нахожусь в середине песни и нажимаю кнопку, чтобы заблокировать исполнителя, приложение немедленно завершает воспроизводимую песню и переходит к песням этого исполнителя, и я бы предпочел, чтобы песня закончила играть, а ТОГДА перейти к песни, которые я хочу.

Если кто-нибудь может придумать способ использовать play() И позволить песне закончиться естественным образом, отметьте этот ответ как правильный...

ПРИМЕЧАНИЕ Я никогда не хотел использовать логику, которая проверяет, сколько времени осталось в песне, и я никогда не хотел использовать фоновый код. Так что я избавился от этого, и он все еще работает с новой идеей.

person RubberDucky4444    schedule 20.03.2018
comment
Вместо этого попробуйте сказать prepareToPlay() сразу после установки очереди. Я считаю, что новые очереди не берут, пока вы не сделаете хотя бы это. И таким образом, вы не застрянете с пьесой, которая вам не нужна. - person matt; 20.03.2018
comment
@matt, который я обнаружил после бета-версии 3 или 4, prepareToPlay() заставляет музыку воспроизводиться немедленно. Я только что попробовал еще раз, и это имеет тот же эффект, что и использование play(), где оно прерывает текущую песню. - person RubberDucky4444; 20.03.2018
comment
Хорошо, но в iOS 11 вы можете append в очередь вместо ее установки, и это может быть то, что вам здесь нужно. - person matt; 20.03.2018