HiveBrain v1.2.0
Get Started
← Back to all entries
patternswiftMinor

Music player in Swift

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
musicplayerswift

Problem

I made a simple mp3 player that plays, pauses and stops a given song, shows the time elapsed, and has a volume control. I read the documentation and can't figure out the difference between the pause and stop methods, although I have configured them to work as I want (i.e., pause lets you resume from where you left off while stop resets the track).

import UIKit
import AVFoundation

class ViewController: UIViewController {

    var player:AVAudioPlayer = AVAudioPlayer()
    var musicPlaying = false
    var timer:NSTimer!

    @IBOutlet weak var currentTime: UILabel!
    @IBOutlet weak var sliderValue: UISlider!

    override func viewDidLoad() {
        super.viewDidLoad()
        let audioPath = NSBundle.mainBundle().pathForResource("bach", ofType: "mp3")!
        do {
            try player = AVAudioPlayer(contentsOfURL: NSURL(string: audioPath)!)
        } catch let error as NSError {
            print(error)
        }
    }

    @IBAction func play(sender: AnyObject) {
        if musicPlaying {
            player.pause()
            musicPlaying = false
        } else {
            player.play()
            musicPlaying = true
            timer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: "updateTime", userInfo: nil, repeats: true)
        }
    }

    @IBAction func stop(sender: AnyObject) {
        player.stop()
        musicPlaying = false
        player.currentTime = 0
    }

    func updateTime() {
        let timePlayed = player.currentTime
        let minutes = Int(timePlayed / 60)
        let seconds = Int(timePlayed % 60)
        currentTime.text = NSString(format:"%02d:%02d", minutes, seconds) as String
    }

    @IBAction func sliderChanged(sender: AnyObject) {
        player.volume = sliderValue.value
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

}

Solution

Let's talk very generally about AVAudioPlayer.

There are a few relevant methods to discuss here:

  • play



  • pause



  • stop



  • prepareToPlay



Strictly speaking, prepareToPlay is optional, but if we call play without the player being prepared to play, there will potentially be some latency between calling play and audio actually beginning to play. If the player wasn't already prepared, the first thing that play will do is prepare it.

From the documentation on prepareToPlay:


Calling this method preloads buffers and acquires the audio hardware needed for playback, which minimizes the lag between calling the play method and the start of sound output.

And perhaps, the important part to read here is that calling stop will undo the set up. This is repeated in the documentation on stop:


Calling this method, or allowing a sound to finish playing, undoes the setup performed upon calling the play or prepareToPlay methods.

So, in your specific case, where we have a view controller that does nothing but play "bach.mp3", we should consider never actually calling stop except perhaps in viewWillDisappear. Calling stop will simply release resources that have to be re-obtained before the track can be played again. And if we continue reading the documentation for stop, we can see that the behavior of not resetting the currentTime to zero is well documented and to be expected:


The stop method does not reset the value of the currentTime property to 0. In other words, if you call stop during playback and then call play, playback resumes at the point where it left off.

So basically, if you're not actually done with that player, stop does the same as pause, but just requires a lot more work.

Realistically, if we want to present to the user behavior which stops the current audio from playing, and will resume the audio from the beginning of the track when play is pressed again (and we have no expectation of starting another track without some other action happening to trigger that), then we should be using pause and then resetting the currentTime to 0.

The only difference between our stop and pause buttons in this specific case should be that stop resets the current time to zero, but both buttons simply pause the player.

Context

StackExchange Code Review Q#121310, answer score: 6

Revisions (0)

No revisions yet.