6

I am playing a song using AVAudioPlayerNode and I am trying to control its time using a UISlider but I can't figure it out how to seek the time using AVAUdioEngine.

Anthon
  • 51,019
  • 25
  • 150
  • 211
luciansvnc
  • 101
  • 1
  • 7
  • 1
    I have been waiting for the answer to this question for months and have been putting it off. I know ti involves getting lastRenderTime of your player node and subtracting or adding time from that. Getting it to play back from that time is the hard part though. – Paul Lehn May 06 '15 at 20:14

2 Answers2

14

After MUCH trial and error I think I have finally figured this out.

First you need to calculate the sample rate of your file. To do this get the last render time of your AudioNode:

var nodetime: AVAudioTime  = self.playerNode.lastRenderTime
var playerTime: AVAudioTime = self.playerNode.playerTimeForNodeTime(nodetime)
var sampleRate = playerTime.sampleRate

Then, multiply your sample rate by the new time in seconds. This will give you the exact frame of the song at which you want to start the player:

var newsampletime = AVAudioFramePosition(sampleRate * Double(Slider.value))

Next, you are going to want to calculate the amount of frames there are left in the audio file:

var length = Float(songDuration!) - Slider.value
var framestoplay = AVAudioFrameCount(Float(playerTime.sampleRate) * length)

Finally, stop your node, schedule the new segment of audio, and start your node again!

playerNode.stop()

if framestoplay > 1000 {
   playerNode.scheduleSegment(audioFile, startingFrame: newsampletime, frameCount: framestoplay, atTime: nil,completionHandler: nil)
}

playerNode.play()

If you need further explanation I wrote a short tutorial here: http://swiftexplained.com/?p=9

Paul Lehn
  • 2,695
  • 20
  • 24
1

For future readers, probably better to get the sample rate as :

playerNode.outputFormat(forBus: 0).sampleRate

Also take care when converting to AVAudioFramePosition, as it is an integer, while sample rate is a double. Without rounding the result, you may end up with undesirable results.

P.S. The above answer assumes that the file you are playing has the same sample rate as the output format of the player, which may or may not be true.

hhanesand
  • 968
  • 11
  • 27
  • I tried the above answer and it is not working for me. I'm printing the currentTime before and after the function and it shows that things are wrong. Can I get your help? here is my [question] (https://stackoverflow.com/questions/56355698/swift-trying-to-control-time-in-avaudioplayernode-using-uislider) – mahdi May 30 '19 at 06:44
  • OMG! I have been messing around with this code for hours and couldn't get it to work. I put in so many print statements to make sure the numbers were correct and was pulling my hair out! I finally got it to work, because regardless of whether you have a playerNode playing or not, you MUST call the ```playerNode.stop()``` method BEFORE you call ```playerNode.scheduleSegment()``` method and then it plays. I had omitted the call to ```playerNode.stop()``` because I wanted to start an audio track at a specified time. Whew! What a fiasco on my part! Thanks for the code example! – SouthernYankee65 Sep 12 '20 at 23:20