user2493235
user2493235

Reputation:

Swift 3 iOS - Synchronised Pause with AVAudioPlayer

I am using AVAudioPlayer to play a set of synchronised MP3 files in a player style interface (Play/Pause/Stop). I am aware that I can synchronise playing them by using play(atTime:) but my question is, how do I synchronise pausing them? At the moment, to pause them, I am using:

for (_, audioTrackPlayer) in audioTrackPlayers.enumerated() {
    audioTrackPlayer.pause()
}

But of course, they all pause one after the other. There is no equivalent pause(atTime:) function. So how can precise synchronisation be achieved for the pausing?

Edit - Extra Info

Following matt's query about how noticeable it is, I got some data on that. It takes almost half a second to pause 5 tracks. To show that, I added a separate loop, after the pause loop, to report the currentTime on each track, and also added a couple of loops before doing the pausing - for reference and to show they were in sync to start with.

Loop benchmark, 0: 3.2118820861678
Loop benchmark, 0: 3.21580498866213
Loop benchmark, 0: 3.21888888888889
Loop benchmark, 0: 3.22126984126984
Loop benchmark, 0: 3.22328798185941

Before pause, 0: 3.22560090702948
Before pause, 1: 3.22750566893424
Before pause, 2: 3.22975056689342
Before pause, 3: 3.23185941043084
Before pause, 4: 3.23439909297052

After pause, 0: 3.35040816326531
After pause, 1: 3.46598639455782
After pause, 2: 3.56979591836735
After pause, 3: 3.67455782312925
After pause, 4: 3.77895691609977

So the tracks were in sync before the pause, with the time difference being only due to the 3 or 4 milliseconds it takes for each iteration of the loop. After the pause there was around one tenth of a second between each track.

The code for reference:

for _ in 0..<5 {
    print ("Loop benchmark, 0: \(audioTrackPlayers[0].currentTime)")
}
for (index, audioTrackPlayer) in audioTrackPlayers.enumerated() {
    print ("Before pause, \(index): \(audioTrackPlayer.currentTime)")
}
for audioTrackPlayer in audioTrackPlayers {
    audioTrackPlayer.pause()
}
for (index, audioTrackPlayer) in audioTrackPlayers.enumerated() {
    print ("After pause, \(index): \(audioTrackPlayer.currentTime)")
}

Upvotes: 0

Views: 690

Answers (1)

user2493235
user2493235

Reputation:

I have partially fixed this issue by setting the volume of all the audio tracks to 0 before stopping or pausing the tracks. This gives the user the "instant response" feel in terms of the audio, so mostly fixes this issue. It would still be nice to not need to take half a second to pause the tracks, so I am not accepting this answer, but it does solve the problem in a usable way.

Example code (a simplified version of what I'm using to demonstrate the point), including the necessary code to reset the currentTime values to match after being upset by the delayed pause. The truncatingRemainder is necessary because these are all looping audios.

// Set volumes of all audio tracks to 0, so delay on pause is not noticed
for audioTrackPlayer in audioTrackPlayers {
    audioTrackPlayer.volume = 0
}
// Pause audio track players
for audioTrackPlayer in audioTrackPlayers {
    audioTrackPlayer.pause()
}
// Update the currentTime values so they all match
for index in 1..<audioTrackPlayers.count {
    audioTrackPlayers[index].currentTime = audioTrackPlayers[0].currentTime.truncatingRemainder(dividingBy: audioTrackPlayers[index].duration)
}
// Reset the volumes now the tracks are paused
for audioTrackPlayer in audioTrackPlayers {
    audioTrackPlayer.volume = 1
}

Upvotes: 1

Related Questions