It’s easy to play

Generally divided into two processes, ready to play, and play

Ready to play, including ready to play resources, player initialization and player ready

Which is ready to play resources


        var currentAudioPath:URL!

        currentAudio = readSongNameFromPlist(currentAudioIndex)
        if let path = Bundle.main.path(forResource: currentAudio, ofType: "mp3"){
            currentAudioPath = URL(fileURLWithPath: path)
        }
        else{
            alertSongExsit()
        }
Copy the code

Player initialization and player ready

var audioPlayer:AVAudioPlayer! audioPlayer = try? AVAudioPlayer(contentsOf: currentAudioPath) audioPlayer.delegate = self audioLength = audioPlayer.duration playerProgressSlider.maximumValue = CFloat (audioPlayer. Duration) playerProgressSlider. MinimumValue = 0.0 playerProgressSlider. Value = 0.0 audioPlayer.prepareToPlay()Copy the code

play

Audioplayer.play (), one line of code

First, how do I make the progress bar?

A normal progress bar does two things,

As the playback progresses, the slider of the progress bar will keep moving forward, with a music playing match to the progress of the progress bar

The slider of the progress bar can be dragged to control where the current play can be played, for example, can be skipped

Play music, progress bar slider also move, progress is matched

Before each playback, set the progress bar.

MaximumValue is the amount of time it takes to finish a song

MinimumValue the minimumValue, that is, it is not playing, is 0

When value starts, it just doesn’t play, it’s 0

PlayerProgressSlider. MaximumValue = CFloat (audioPlayer. Duration) playerProgressSlider. MinimumValue = 0.0 PlayerProgressSlider. Value = 0.0Copy the code

To keep the progress bar slider moving forward, you need a timer

func startTimer() {ifTimer == nil {timer = timer.scheduledTimer (timeInterval: 1.0, target: self, selector:#selector(PlayerViewController.update(_:)), userInfo: nil,repeats: true)Timer.fire ()}} // Every second, to get the current playback time of the player, refresh the status of the progress bar playerProgressSlider @objc func update(_ timer: timer){if! audioPlayer.isPlaying{return
        }
        let time = calculateTimeFromNSTimeInterval(audioPlayer.currentTime)
        playerProgressSlider.value = CFloat(audioPlayer.currentTime)
    }
Copy the code

Drag the slider of the progress bar to adjust the position of the playback

Because the range of the scrollbar matches the length of the player

So set currentTime to the currentTime of the player

Pause first, then set currentTime of the player, after a short interval

The transition is smoother and the experience is slightly better

@IBAction func changeAudioLocationSlider(_ sender : UISlider) { audioPlayer.pause() audioPlayer.currentTime = TimeInterval(sender.value) DispatchQueue. Main. AsyncAfter (deadline: now () + 0.5) {self. The audioPlayer. The play ()}}Copy the code

Do fast forward and fast back, the same idea, change the current time of the playeraudioPlayer.currentTime

Second, out of order play and loop play, how to do?

Use out-of-order play and loop play two buttons, for the next song, and after the song

They do not affect the current song playback

So these two buttons click, they change the UI, they change the state, and when the current song is finished, it works

Or it’s not playing right now, and the next time it’s playing, the first song is done and it works

@IBAction func shuffleButtonTapped(_ sender: UIButton) {
        shuffleArray.removeAll()
        if sender.isSelected{
            sender.isSelected = false
            shuffleState = false
        } else {
            sender.isSelected = true
            shuffleState = true
        }
    }
    
    
    @IBAction func repeatButtonTapped(_ sender: UIButton) {
        if sender.isSelected == true {
            sender.isSelected = false
            repeatState = false
        } else {
            sender.isSelected = true
            repeatState = true}}Copy the code

Out-of-order and circulation, done in AVAudioPlayerDelegate play play a role in the callback method func audioPlayerDidFinishPlaying (_ player: AVAudioPlayer, successfully flag: Bool){

Single loop effect

So instead of clicking out of order, just clicking on loop,

       if shuffleState == false && repeatState == true{//repeat same song prepareAudio() playAudio()Copy the code

Out of order, no circulation effect

I didn’t hit loop, I just hit Out of order,

If it’s out of order, you have to pick a random number,

Do not cycle, it is necessary to remove repetition, here is to play the song list again, it is over

Var shuffleArray = [Int]()if shuffleState == true && repeatState == false{// Add shufflearray. append(currentAudioIndex) // Add an end condition, not less than the list of songsif shuffleArray.count >= audioList.count {
                  playButton.setImage( UIImage(named: "play"), for: UIControl.State())
                  returnVar newIndex = 0 var newIndex = 0 var newIndex = 0 var newIndex = 0false
              while newIndex == false {
                  randomIndex =  Int(arc4random_uniform(UInt32(audioList.count)))
                  if shuffleArray.contains(randomIndex) {
                      newIndex = false
                  }else{
                      newIndex = trueCurrentAudioIndex = randomIndex // prepareAudio() playAudio()Copy the code

Out-of-order loop effect

That’s loop and out of order

If it’s out of order, you have to pick a random number,

Out of order loop, here is to play the list out of order once, and then again

Var shuffleArray = [Int]()if shuffleState == true && repeatState == true{//shuffle song toil // add shufflearray. append(currentAudioIndex) // repeat conditions, so that all the songs have been played before, and so onifShufflearray.count >= audiolist.count {shufflearray.removeall ()} Var randomIndex = 0 var newIndex =false
                while newIndex == false {
                    randomIndex =  Int(arc4random_uniform(UInt32(audioList.count)))
                    if shuffleArray.contains(randomIndex) {
                        newIndex = false
                    }else{
                        newIndex = trueCurrentAudioIndex = randomIndex // prepareAudio() playAudio()}Copy the code

Third, lock screen play and switch to another application play

It’s actually playing in the background

Set the background mode to keep the session alive

        do {
            //keep alive audio at background
            try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.playback)
        } catch _ { }
        
        do {
            try AVAudioSession.sharedInstance().setActive(true)
        } catch _ { }
Copy the code

Fourth, remote control events for the player

At the bottom of the screen pops the player control events

Controls events for the player after the lock screen

The first step is to receive remote control events

        //LockScreen Media control registry
        if UIApplication.shared.responds(to: #selector(UIApplication.beginReceivingRemoteControlEvents)){
            UIApplication.shared.beginReceivingRemoteControlEvents()
            UIApplication.shared.beginBackgroundTask(expirationHandler: { () -> Void in})}Copy the code

Synchronize the play information to the lock screen player with the bottom popup window of the player

When it’s playing, synchronize it to the lock screen and the popup at the bottom,

// This shows media info on lock screen - used currently and perform controls
    func showMediaInfo() {let artistName = readArtistNameFromPlist(currentAudioIndex)
        let songName = readSongNameFromPlist(currentAudioIndex)
        MPNowPlayingInfoCenter.default().nowPlayingInfo = [MPMediaItemPropertyArtist : artistName,  MPMediaItemPropertyTitle : songName]
    }
Copy the code

Finally, rewritefunc remoteControlReceivedmethods

When locking the screen, you can pause and play the player, click on the previous song, and next song

And when I pull up the bottom popover

override func remoteControlReceived(with event: UIEvent?) {
        ifevent! .type == UIEvent.EventType.remoteControl{ switch event! .subtype{case UIEventSubtype.remoteControlPlay:
                play(self)
            case UIEventSubtype.remoteControlPause:
                play(self)
            case UIEventSubtype.remoteControlNextTrack:
                next(self)
            case UIEventSubtype.remoteControlPreviousTrack:
                previous(self)
            default:
                print("There is an issue with the control")}}}Copy the code

Fifth, how to play a variety of files, MP3, M4A?

Instantiate AVAudioPlayer via binary data

    var player: AVAudioPlayer!
     var tempPath: String?
        if let mpPath = Bundle.main.path(forResource: str, ofType: "mp3"){
            tempPath = mpPath
        }
        if let maPath = Bundle.main.path(forResource: str, ofType: "m4a"){
            tempPath = maPath
        }
       
        guard let path = tempPath, let playerTmp = try? AVAudioPlayer(data: Data(contentsOf: URL(fileURLWithPath: path))) else{
            return
        }
        self.player = playerTmp
Copy the code

Code of this article:Github.com/coyingcat/m…

This article is based on Bpolat /Music-Player