sbooth / SFBAudioEngine

A powerhouse of audio functionality for macOS and iOS
https://sbooth.github.io/SFBAudioEngine/
MIT License
552 stars 87 forks source link

play audio files in sequence #188

Closed pzs7602 closed 3 years ago

pzs7602 commented 3 years ago

I have several audio files and want to play them in sequence:

var audioFiles = ["test.dsf","test.wv","test.ape"]
for index in (0 ..< audioFiles.count){
    do {
        self.url = Bundle.main.url(forResource: audioFiles[index], withExtension: "")
        if let decoder = try self.decoder(enableDoP: self.dop) {
            try player.enqueue(decoder, immediate: false)
        }
    }
    catch let error {
        print(error)
    }
}
try? player.play()

now the audio files can be played, but not in the sequence I added to the queue(many times test.wv is played first). Is the queue not the SERIAL one?

sbooth commented 3 years ago

This is interesting.

Internally both SFBAudioPlayer and SFBAudioPlayerNode use a std::queue for enqueued decoders, which are serial structures. Decoders are only added to SFBAudioPlayerNode's queue if -supportsFormat: returns YES, which occurs if the decoder being enqueued has the same channel count and sample rate (for gapless playback purposes). In SFBAudioPlayer, if a decoder is enqueued that doesn't have the same channel count and sample rate as the owned SFBAudioPlayerNode's queue, it is added to the SFBAudioPlayer's queue.

I think you've uncovered a situation like the following:

A and AA have the same channel count and sample rate. B and BB have the same channel count and sample rate as each other, but a different sample rate than A and AA.

  1. Play A
  2. Enqueue B, AA, and BB
  3. Playback order ends up being A, AA, B, BB

Does this match the behavior you're seeing?

I believe the fix is to check -queueIsEmpty in -enqueueDecoder:forImmediatePlayback:error: and if not, place the decoder in the SFBAudioPlayer's queue and not the SFBAudioPlayerNode's queue regardless of format. This should preserve ordering.

sbooth commented 3 years ago

Please give the linked PR a try and see if it fixes the problem.

pzs7602 commented 3 years ago

test by using playback-order branch:

var audioFiles = ["test.dsf","test.flac","test.wv","test.dff"]
......
func PlayItemList()
{
    var imediate = true
    for index in (0 ..< audioFiles.count){
        do {
            self.url = Bundle.main.url(forResource: audioFiles[index], withExtension: "")
            if let decoder = try self.decoder(enableDoP: self.dop) {
                imediate = index == 0 ? true : false
                try player.enqueue(decoder, immediate: imediate)
            }
        }
        catch let error {
            print(error)
        }
    }
    let session:AVAudioSession = AVAudioSession.sharedInstance() // get audio session
    do{
        try session.setCategory(AVAudioSession.Category.playback)  // for background play
        try session.setActive(true)
    }
    catch let error as NSError{
        print("err=\(error.description)")
    }

    try? player.play()
}

the audio files are played in order. thanks! This method is not suitable for playing audio files in queue if DSD audio in it to be played in DoP mode, because we can't change the session's sample rate before the DSD audio in queue is being played. This is somewhat complicated, because we may have to change session's sample rate many times. Is there a solution?

sbooth commented 3 years ago

I'm glad the playback order is fixed. Thanks for reporting the issue.

For the sample rate issue, does setting the sample rate in audioPlayer(_:decodingStarted:) work?