0

I'm trying to figure how to call an action when an AVAudioPlayer hits specific second by using NSTimer.

Code:

var audioFile = try! AVAudioPlayer(contentsOfURL: NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("FileName", ofType: "mp3")!))

override func viewDidLoad() {
    super.viewDidLoad()
    NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: Selector("checkPlaybackTime:"), userInfo: nil, repeats: true)
}

func checkPlaybackTime(timer:NSTimer){
    let seconds : NSTimeInterval = audioFile.currentTime
    if (seconds == 20.0){
        btnPausePlay.setTitle("Play", forState: UIControlState.Normal)
    }
}

The action that I wanted to call is that the btnPausePlay button text sets to "Play" which it never does as you see as I've called it in the if statement.

Edit: It now works. I changed from if (seconds == 20.0 to if (seconds >= 20.0)

Justin
  • 127
  • 1
  • 2
  • 15
  • What does it mean "it doesn't work"? Method is not called or what? – Tomasz Szulc Jul 19 '15 at 12:52
  • I've checked the code. `checkPlaybackTime:` method is called every second. – Tomasz Szulc Jul 19 '15 at 12:57
  • make sure your base class is NSObject, else the NSTimer selector might crash. – Daniel Krom Jul 19 '15 at 13:00
  • @DanielKrom snippet shows that `viewDidLoad` method is overriden. I think we can easily assume that `UIViewController` is parent class. – Tomasz Szulc Jul 19 '15 at 13:05
  • "doesn't work or do its job" is not an acceptable description of your problem on Stack Overflow (or anywhere, really). As such, I've voted to close your question. Please update your question with more details regarding exactly what is happening and how it differs from your expected outcome. Is it failing to compile? Is it crashing? Is `checkPlaybackTime(timer:)` called at all? Is your `if seconds == 20.0` comparison failing (if it's even hitting, it's almost certainly failing like 99.999% of the time)? If your code launching missiles? – nhgrif Jul 19 '15 at 13:10
  • 1
    possible duplicate: http://stackoverflow.com/q/4915462/2792531 – nhgrif Jul 19 '15 at 13:15
  • @TomaszSzulc yea, so it should be called from other class maybe – Daniel Krom Jul 19 '15 at 13:32
  • I've edited my question. Basically I wanted the btnPausePlay button to set the text to "Play" (which it doesn't) when the audio is done. 20 seconds hence being the length of the audio file. It doesn't crash or anything, it just doesn't call the action. – Justin Jul 19 '15 at 14:05
  • So did you read and understand my answer and the link that nhgrif provided? – Eiko Jul 19 '15 at 14:11
  • Yep. I took your suggestion by changing it to ">= 20" and it's working perfectly. Plus, I understand now what you pointed out. Thank you. – Justin Jul 19 '15 at 14:17

2 Answers2

0

Although this answer does not make explicit use of NSTimer, it does leverage the method addPeriodicTimeObserverForInterval of the AVPlayer class to achieve the same purpose:

After initialising your player, do this:

let observerInterval = CMTimeMakeWithSeconds(0.5, 100) //this sets the interval at every half second
timeObserverToken = audioPlayer.addPeriodicTimeObserverForInterval(observerInterval, queue: nil, usingBlock: self.getPlaybackTimer)

Function getPlaybackTimer looks like this:

func getPlaybackTimer(time: CMTime){
    let currentTime = audioPlayer.currentTime()
    /* *** */
}
Luis Delgado
  • 3,644
  • 4
  • 34
  • 54
  • While this is neat and could be part of a correct answer, I suspect that the actual problem in the question asked has nothing to do with the choice to use `NSTimer`, and this answer does nothing to solve the actual problem. – nhgrif Jul 19 '15 at 13:14
  • I'm using AVAudioPlayer because it has methods like "stop(), numberOfLoops, playing() etc which I'm currently using. Is there an alternative for the addPerodic method in AVAudioPlayer? – Justin Jul 19 '15 at 14:09
  • @Justin, no, sadly `AVAudioPlayer` is not a subclass of `AVPlayer`, so that method is not available. If you strictly need the methods/properties in AVAudioPlayer, you'll have to stick with that class. AVPlayer does have a method that should be interesting to you: `addBoundaryTimeObserverForTimes `. It would allow you to trigger an action after a determined period of time (like your 20 seconds), which is more efficient than running a timer every second until you reach your threshold. – Luis Delgado Jul 19 '15 at 14:55
0

As nhgrif points out, your logic is flawed. You are comparing floating point values in a way that you should not. You need to take little variations into account, almost always when comparing floating point numbers.

In this case, however, this wouldn't suffice alone, as your comparison might take place every so little outside your checking interval. You are better of by using inequality here, i.e. >= 20.0. You might want to disable the timer then.

Eiko
  • 25,601
  • 15
  • 56
  • 71