I'm new to Swift development and I'm trying to play/pause on lock screen multiple audio files using:
remoteControlReceived
But it is only allowing me to add it once meaning I can only control one audio file but I'd like 2 other audio files to have play/pause enabled on lock screen.
Below is the code I'm using:
// Playback controls
override func remoteControlReceived(with event: UIEvent?) {
if let event = event {
if event.type == .remoteControl {
switch event.subtype {
case .remoteControlPlay: audioPlayer.play()
case . remoteControlPause: audioPlayer.pause()
default: print("Done")
}
}
}
}
override func remoteControlReceived0(with event: UIEvent?) {
if let event = event {
if event.type == .remoteControl {
switch event.subtype {
case .remoteControlPlay: audioPlayer1.play()
case . remoteControlPause: audioPlayer1.pause()
default: print("Done")
}
}
}
}
func remoteControlReceived(with event: UIEvent?) {
if let event = event {
if event.type == .remoteControl {
switch event.subtype {
case .remoteControlPlay: audioPlayer2.play()
case . remoteControlPause: audioPlayer2.pause()
default: print("Done")
}
}
}
}
but it is only allowing me to add it once meaning I can only control one audio file but I'd like 2 other audio files to have play/pause enabled on lock screen
You can’t. It references only the now playing item.
Related
I am using AudioKit to manage my sound engine in my app. I think AudioKit's settings are overriding something and I can't get lock screen audio controls. This is the code I have currently:
//Configure media control center
UIApplication.shared.beginReceivingRemoteControlEvents()
let commandCenter = MPRemoteCommandCenter.shared()
commandCenter.pauseCommand.addTarget { (event) -> MPRemoteCommandHandlerStatus in
//Update your button here for the pause command
return .success
}
commandCenter.playCommand.addTarget { (event) -> MPRemoteCommandHandlerStatus in
//Update your button here for the play command
return .success
}
I have this in a function which sets up all of my AudioKit settings but it doesn't work. Is there a special procedure for lock screen audio controls with AudioKit?
you have to write these code in appdelegate to implement :
do {
try AVAudioSession.sharedInstance().setActive(true)
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
}catch{
}
UIApplication.shared.beginReceivingRemoteControlEvents()
Keep in mind it's important to disable .mixWithOthers
You have to rewrite this methord:
//MARK:-音乐播放相应后台状态
override func remoteControlReceived(with event: UIEvent?) {
if (event?.type == .remoteControl) {
NotificationCenter.default.post(name: NSNotification.Name(rawValue: Configs.remotePlayCotrolNotificaation), object: nil, userInfo: ["event":event!])
}
}
I want to know how to get the state of my player (AVPlayer) (buffering, playing, stopped, error) and update the ui according to those states (including the player on the lock screen). How exactly should I do it?
I have a label that may contain:
"Buffering...", "Playing", "Stopped" or "Error".
Basically, I have the following:
MediaPlayer:
import Foundation
import AVFoundation
class MediaPlayer {
static let sharedInstance = MediaPlayer()
fileprivate var player = AVPlayer(url: URL(string: "my_hls_stream_url_here")!)
fileprivate var isPlaying = false
func play() {
player.play()
isPlaying = true
}
func pause() {
player.pause()
isPlaying = false
}
func toggle() {
if isPlaying == true {
pause()
} else {
play()
}
}
func currentlyPlaying() -> Bool {
return isPlaying
}
}
PlayerViewController:
class PlayerViewController: UIViewController {
#IBOutlet weak var label: UILabel!
#IBAction func playStopButtonAction(_ sender: UIButton) {
MediaPlayer.sharedInstance.toggle()
}
override func viewDidLoad() {
super.viewDidLoad()
label.text = "Disconnected"
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
try AVAudioSession.sharedInstance().setActive(true)
print("Audio session ok\n")
} catch {
print("Error: Audio session.\n")
}
// Show only play/pause button on the lock screen
if #available(iOS 9.1, *) {
let center = MPRemoteCommandCenter.shared()
[center.previousTrackCommand, center.nextTrackCommand, center.seekForwardCommand, center.seekBackwardCommand, center.skipForwardCommand, center.skipBackwardCommand, center.ratingCommand, center.changePlaybackRateCommand, center.likeCommand, center.dislikeCommand, center.bookmarkCommand, center.changePlaybackPositionCommand].forEach {
$0.isEnabled = false
}
center.togglePlayPauseCommand.addTarget { (commandEvent) -> MPRemoteCommandHandlerStatus in
MediaPlayer.sharedInstance.toggle()
return MPRemoteCommandHandlerStatus.success
}
center.playCommand.addTarget { (commandEvent) -> MPRemoteCommandHandlerStatus in
MediaPlayer.sharedInstance.play()
return MPRemoteCommandHandlerStatus.success
}
center.pauseCommand.addTarget { (commandEvent) -> MPRemoteCommandHandlerStatus in
MediaPlayer.sharedInstance.pause()
return MPRemoteCommandHandlerStatus.success
}
} else {
// Fallback on earlier versions
print("Error (MPRemoteCommandCenter)")
}
}
override func remoteControlReceived(with event: UIEvent?) {
guard let event = event else {
print("No event\n")
return
}
guard event.type == UIEventType.remoteControl else {
print("Another event received\n")
return
}
switch event.subtype {
case UIEventSubtype.remoteControlPlay:
print("'Play' event received\n")
case UIEventSubtype.remoteControlPause:
print("'Pause' event received\n")
case UIEventSubtype.remoteControlTogglePlayPause:
print("'Toggle' event received\n")
default:
print("\(event.subtype)\n")
}
}
}
I think you could use the timeControlStatus property of AVPlayer. According to the doc it can be paused, waitingToPlayAtSpecifiedRate which is basically what you call buffering or playing.
If you really need the error state, you could observe the error property or whether the status property is set to failed.
A simple KVO observer on these properties would do the trick.
A place to start could be through using the AVPlayer's "status" property. It is an enumeration that contains the following values (this is taken directly from the documentation):
'unknown': Indicates that the status of the player is not yet known because it has not tried to load new media resources for playback.
'readyToPlay': Indicates that the player is ready to play AVPlayerItem instances.
'failed': Indicates that the player can no longer play AVPlayerItem instances because of an error.
As to how you could tell if the content is actually playing, you could just use boolean checks as it seems you have partially implemented. For pausing and stopping, you could just keep the file loaded for pause, and delete the file for stop that way you could differentiate the two.
For buffering, if the enum is not unknown or readyToPlay then that theoretically should mean that there is a file being attached but is not quite ready to play (i.e. buffering).
I'm trying to get when you press the play / pause button of my earpods make an action. For this I am first trying to achieve change a text label ...
According to look at various websites, they performed this same but it does not work. Is there anything missing me add?
I have the following code in viewController.swift
override func viewDidLoad() {
super.viewDidLoad()
canBecomeFirstResponder()
UIApplication.sharedApplication().beginReceivingRemoteControlEvents()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBOutlet weak var Cosa: UILabel!
func play() {
Cosa.text = "Play"
}
override func canBecomeFirstResponder() -> Bool {
return true
}
override func remoteControlReceivedWithEvent(event: UIEvent?) {
guard let event = event else {
print("no event\n")
return
}
guard event.type == UIEventType.RemoteControl else {
print("received other event type\n")
return
}
switch event.subtype {
case UIEventSubtype.RemoteControlPlay:
print("received remote play\n")
play()
case UIEventSubtype.RemoteControlPause:
print("received remote pause\n")
case UIEventSubtype.RemoteControlTogglePlayPause:
print("received toggle\n")
default:
print("received \(event.subtype) which we did not process\n")
}
}
For any RemoteControlEvents you need to have the audio playing.
Preparing Your App for Remote Control Events
To receive remote control events, do the following:
Register handlers for each action you support. Use the shared
MPRemoteCommandCenter object to register handlers for different types
of events
Begin
playing audio. Your app must be the “Now Playing” app. An app does not
receive remote control events until it begins playing audio.
https://developer.apple.com/library/content/documentation/EventHandling/Conceptual/EventHandlingiPhoneOS/Remote-ControlEvents/Remote-ControlEvents.html
Also you can refer the below link for more clarification and Demo project
Receive remote control events without audio
In my app I have an Audio player that plays audio in background. Everything works as should be however, when I receive a call or I open another app the audio in background in my app goes silent but the remoteControlReceivedWithEvent function is not called.
My function is:
override func remoteControlReceivedWithEvent(event: UIEvent?) {
if event!.type == UIEventType.RemoteControl {
if event!.subtype == UIEventSubtype.RemoteControlPlay {
//print("received remote play")
NSNotificationCenter.defaultCenter().postNotificationName("AudioPlayerIsPlaying", object: nil)
} else if event!.subtype == UIEventSubtype.RemoteControlPause {
//print("received remote pause")
NSNotificationCenter.defaultCenter().postNotificationName("AudioPlayerIsNotPlaying", object: nil)
} else if event!.subtype == UIEventSubtype.RemoteControlNextTrack{
//print("received next")
nextBtn.sendActionsForControlEvents(UIControlEvents.TouchUpInside)
}else if event!.subtype == UIEventSubtype.RemoteControlPreviousTrack{
//print("received previus")
backBtn.sendActionsForControlEvents(UIControlEvents.TouchUpInside)
}else if event!.subtype == UIEventSubtype.RemoteControlTogglePlayPause{
//print("received toggle")
playBtn.sendActionsForControlEvents(UIControlEvents.TouchUpInside)
}
}
}
How can I tell my app that a call is received or that another app has been opened?
I'm implementing a low-latency drum set with TheAmazingAudioEngine framework. I have a scene with a single button and a viewController with the methods below. This code works very well if I touch the button slowly. But if I touch it many times in a short period of time --- 10 times per second, for instance ---, the sound is not played in some touches, without error messages. The audio sample is short (less than 2 seconds).
Why does it happen? What is wrong in my implementation?
I choose TheAmazingAudioEngine instead of AVAudioPlayer to get low-latency between the touch and the sound.
override func viewDidLoad() {
super.viewDidLoad()
// Enable multiple touch for the button
for v in view.subviews {
if v.isKindOfClass(UIButton) {
v.multipleTouchEnabled = true
}
}
// Init audio
audioController = AEAudioController(audioDescription: AEAudioController.nonInterleavedFloatStereoAudioDescription())
audioURL = NSBundle.mainBundle().URLForResource("shortSound", withExtension: "wav")!
do {
try audioController?.start()
} catch {
print("AudioController start Error")
}
}
override func viewDidDisappear(animated: Bool) {
super.viewDidDisappear(animated)
audioController?.stop()
}
#IBAction func playSound(sender: UIButton) {
do {
let player = try AEAudioFilePlayer(URL: audioURL)
player.removeUponFinish = true
audioController?.addChannels([player])
} catch {
print("Player start Error")
}
}