Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AudioPlayer seek not working as expected while looping #2788

Open
renezuidhof opened this issue Oct 9, 2022 · 7 comments
Open

AudioPlayer seek not working as expected while looping #2788

renezuidhof opened this issue Oct 9, 2022 · 7 comments
Labels

Comments

@renezuidhof
Copy link

renezuidhof commented Oct 9, 2022

macOS Version(s) Used to Build

macOS 12 Monterey

Xcode Version(s)

Xcode 14

Description

The seek function for the AudioPlayer does not work as expected when it's looping. While I would expect it to wraparound to the start it starts looping from the seek time to the end of the file. Source: https://github.com/AudioKit/AudioKit/blob/main/Sources/AudioKit/Nodes/Playback/AudioPlayer/AudioPlayer%2BPlayback.swift#L98

Or is this actually what this function is made to do?

I noticed there was a request for this before, which was fixed. But this fix seems to have been removed after it was added at some point.

Crash Logs, Screenshots or Other Attachments (if applicable)

No response

Edit: Using buffered AudioPlayer

@renezuidhof renezuidhof added the bug label Oct 9, 2022
@NickCulbertson
Copy link
Member

What happens if you call yourAudioPlayer.isEditTimeEnabled = false or yourAudioPlayer.editStartTime = 0 right after you seek?

@renezuidhof
Copy link
Author

This does not seem to help. I tried the following things:

audioPlayer.seek(time: secondsSinceStartTime)
audioPlayer.play()
audioPlayer.editStartTime = 0 // Also tried with audioPlayer.isEditTimeEnabled = false
audioPlayer.play()
audioPlayer.seek(time: secondsSinceStartTime)
audioPlayer.editStartTime = 0 // Also tried with audioPlayer.isEditTimeEnabled = false

It does not make a difference

@NickCulbertson
Copy link
Member

Hmmm. Someone else will have to look at this. I'm can't get seek to work at all.

@renezuidhof
Copy link
Author

The seek function might work for non-buffered audio. But because I need seamless looping I need a buffered audioplayer. Now when using the seek function the buffer is updated with a range from seek time to end of the file. And therefor only looping between these times.

I can't seem to find any option to set a currentTime or offset for the buffered AudioPlayer. Is there another player that allows seeking with seamless looping?

I noticed the AVAudioPlayer is looping seamlessly and has a seek function. But since I can't use it as a node to add the my audio flow this also does not work for me.

Any suggestions?

@renezuidhof
Copy link
Author

renezuidhof commented Oct 13, 2022

Correction: I don't think my original explanation below is correct. It works though. But it does not work because audioPlayer.play accepts a time in the past (When a time in the past is provided it just starts right away). It works because the buffer is updated in the updateBuffer function.

Apparently it is possible to call the play function with a negative at time. I didn't expect this to work so I never tried..

There seems to be an issue with getCurrentTime() though when doing this. It's not accurate at all. I think this is because timeBeforePlay is set and is not expecting negative values (not sure if this is true, but that's what I think for now). timeBeforePlay is used when calling getCurrentTime()

For now I can work around this issue by storing a startTime myself and use it to calculate my own currentTime.

// Here startTime is a time that was in the past. So this is not necessarily a seek function, but this is what I need it for 
func syncToStartTime(_ startTime: AVAudioTime) {
  let nanoSecondsSinceStartTime = Double(AVAudioTime.now().hostTime - startTime.hostTime)
        
  let samplesSinceStart = Int64(nanoSecondsSinceStartTime / numberOfNanosecondsInSecond * mixerNode.outputFormat.sampleRate)

  // Store startTime for my own `currentTime` calculations
  audioPlayerStartHostTime = startTime
  audioPlayerStartSampleTime = AVAudioTime(sampleTime: -samplesSinceStart, atRate: mixerNode.outputFormat.sampleRate)

  audioPlayer.stop()
  audioPlayer.play(at: audioPlayerStartSampleTime)
}

What I think could be improved is that the play call will update the buffers while it is not necessary. Because the start and end time did not change.

Schedule called when calling play(at:)
https://github.com/AudioKit/AudioKit/blob/main/Sources/AudioKit/Nodes/Playback/AudioPlayer/AudioPlayer%2BScheduling.swift#L17

updateBuffer called when scheduling:
https://github.com/AudioKit/AudioKit/blob/main/Sources/AudioKit/Nodes/Playback/AudioPlayer/AudioPlayer%2BBuffering.swift#L8

@ladeng
Copy link

ladeng commented Dec 23, 2022

you can try this
`
let mediaUrl = URL.init(fileURLWithPath: path)

let file = try! AVAudioFile(forReading: mediaUrl)

try! player.load(file: file, buffered: true)
`
and player.seek(time: time)

it's work for me
good luck

@renezuidhof
Copy link
Author

renezuidhof commented Jan 8, 2023

@ladeng this works when just playing it once. When 'isLooping' is enabled this does not work anymore. It will loop from the 'seek' position until end.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants