Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MacOS not responding to MPRemoteCommandCenter commands in the background

I am writing an application for my own purposes that aims to get play pause events no matter what is going on in the system. I have gotten this much working

let commandCenter = MPRemoteCommandCenter.shared()
commandCenter.togglePlayPauseCommand.isEnabled = true
commandCenter.togglePlayPauseCommand.addTarget { (MPRemoteCommandEvent) -> MPRemoteCommandHandlerStatus in
    print("Play Pause Command")
    return .success
}

commandCenter.nextTrackCommand.isEnabled = true
commandCenter.nextTrackCommand.addTarget { (MPRemoteCommandEvent) -> MPRemoteCommandHandlerStatus in
    print("NextTrackCommand")
    return .success
}
commandCenter.previousTrackCommand.isEnabled = true
commandCenter.previousTrackCommand.addTarget { (MPRemoteCommandEvent) -> MPRemoteCommandHandlerStatus in
    print("previousTrackCommand")
    return .success
}
commandCenter.playCommand.isEnabled = true
commandCenter.playCommand.addTarget { (MPRemoteCommandEvent) -> MPRemoteCommandHandlerStatus in
    print("playCommand")
    return .success
}

MPNowPlayingInfoCenter.default().playbackState = .playing

Most of those methods are there because apparently you will not get any notifications without having nextTrackCommand or previousTrackCommand or playCommand implemented.

Anyways my one issue is that as soon as you open another application that uses audio these event handlers stop getting called and I cant find a way to detect and fix this.

I would normally try doing AVAudioSession things to state this as a background application however that does not seem to work. Any ideas on how I can get playpause events no matter what state the system is in?

I would like to be able to always listen for these events OR get an indication of when someone else has taken control of the audio? Perhaps even be able to re-subscribe to these play pause events.

like image 873
J.Doe Avatar asked Apr 18 '20 21:04

J.Doe


1 Answers

There's an internal queue in the system which contains all the audio event subscribers. Other applications get on top of it when you start using them.

I would like to be able to always listen for these events

There's no API for that but there's a dirty workaround. If I understand your issue correctly, this snippet:

    MPNowPlayingInfoCenter.default().playbackState = .paused
    MPNowPlayingInfoCenter.default().playbackState = .playing

must do the trick for you if you run it in a loop somewhere in your application.

Note that this is not 100% reliable because:

  1. If an event is generated before two subsequent playbackState state changes right after you've switched to a different application, it would still be catched by the application in the active window;
  2. If another application is doing the same thing, there would be a constant race condition in the queue, with unpredictable outcome.

References:

  • Documentation for playbackState is here;
  • See also a similar question;
  • See also a bug report for mpv with a similar issue (a pre-MPRemoteCommandCenter one, but still very valuable)

OR get an indication of when someone else has taken control of the audio

As far as I know there's no public API for this in macOS.

like image 178
ximaera Avatar answered Nov 03 '22 03:11

ximaera