MPMusicPlayerController.systemMusicPlayer setQueue 困难

Posted

技术标签:

【中文标题】MPMusicPlayerController.systemMusicPlayer setQueue 困难【英文标题】:MPMusicPlayerController.systemMusicPlayer setQueue difficulties 【发布时间】:2018-03-19 01:38:02 【问题描述】:

我的音乐应用程序有问题。这个想法是点击一个按钮并播放更多来自当前正在播放的艺术家的歌曲。当我在应用程序中并点击下一首歌曲时,它运行良好。但是,如果我让歌曲自然结束,应用程序会随机播放一首歌曲。因此,我添加了逻辑来说明歌曲剩余时间是否为 0,然后使用媒体播放器 func 播放下一首歌曲,因为我知道这是可行的。这解决了它,除非应用程序在后台。如果应用程序在后台,我试图让它保持活动状态,但我想这不起作用。

每次我认为我已经解决了问题时,问题又回来了。

我期望发生的事情是锁定艺术家,将应用程序关闭到后台,当歌曲结束时播放艺术家的另一首歌曲。

实际发生的情况是当我锁定艺术家并将应用程序关闭到后台时,歌曲将结束,有时它会播放正确的歌曲。有时它不会。 但是当它播放错误的歌曲并且我打开应用程序备份时,它会结束当前(错误)播放的歌曲并开始播放适当艺术家的歌曲

需要明确的是,我认为需要在歌曲剩余时间为 0 时运行的逻辑以跳到下一首歌曲但无论出于何种原因我都需要它是不明智的

我已将功能设置为后台获取和音频 Airplay 和画中画

我试图只按顺序发布相关代码..

我有财产

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()
    
  )

当我播放音乐时,我有一个 bool 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)

【问题讨论】:

@solenoid 我想开始随机播放音乐,然后点击按钮将队列设置为该艺术家的歌曲,然后如果再次点击,则删除该艺术家锁定 啊-如果您希望当前歌曲过期而不是 IF 按钮 = 按下,开始播放艺术家的歌曲。如果您发送一个正在播放当前歌曲的新队列,它会停止播放当前歌曲吗?我认为您不应该做任何其他事情,除了在按钮离开 on 状态时再次更改队列。 @solenoid 不,当前歌曲不会过期,它只是捕捉艺术家,然后下一首歌曲应该由同一位艺术家创作。当用户想要停止该艺术家时,我有一个类似的方法可以解除锁定。如果应用程序在前台,它可以工作 在您的artistPredicate 中,您的价值——实际上是返回您想要的吗?我没有看到它在任何地方定义,我使用: MPMusicPlayerController.systemMusicPlayer().nowPlayingItem.albumArtist @solenoid 是的。当我回到家时,我可以尝试,但就像我说的那样有效。只是在应用处于后台时不会 【参考方案1】:

我发现在你说prepareToPlay(字面意思或暗示play)之前,设置队列不会“占用”。我的书的当前版本说:

我的经验是,如果您在设置队列后不立即向play 或至少prepareToPlay 提出要求,玩家可能会做出意想不到的行为。显然,在您这样做之前,队列实际上并没有生效。

你也说过(在评论中):

理想情况下,我希望歌曲先完成,然后进入我的队列

好的,但也许您想要的是(iOS 11 中的新功能)append 功能,该功能允许您修改队列而不用完全替换它。

【讨论】:

理想情况下,我希望歌曲先完成,然后进入我设置的队列。就像我的回答一样,使用 prepareToPlay 会中断歌曲。 这很棘手,因为当用户厌倦了该艺术家时,他/她可以再次点击按钮并解除锁定。然后,我需要删除该艺术家,但只删除已播放的歌曲(如果未播放该艺术家的完整歌曲列表)。没有我可以看到撤消附加的删除方法,如果我正在附加,那么我将该艺术家添加到队列中两次?【参考方案2】:

我找到的解决方案不是 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() 的方法,并且让歌曲自然结束,则表示该答案是正确的......

注意我从不想使用检查歌曲剩余时间的逻辑,我也不想使用后台代码。所以我摆脱了它,它仍然适用于新想法。

【讨论】:

尝试在设置队列后立即说prepareToPlay()。我发现在您至少这样做之前,新队列不会“占用”。这样一来,您就不会被您不想要的游戏所困。 @matt 我在 beta 3 或 4 之后发现 prepareToPlay() 导致音乐立即开始播放。我刚刚又试了一次,它和使用 play() 的效果一样,它会中断当前歌曲 好的,但是在 iOS 11 中,您可以将 append 加入队列而不是设置它,这可能就是您想要的。

以上是关于MPMusicPlayerController.systemMusicPlayer setQueue 困难的主要内容,如果未能解决你的问题,请参考以下文章