-
Hi! I've got a question that I think is more about patterns for handling audio than something library-specific: What is the intended pattern for using gapless playback? The problem that I'm running into is the silent time added by decoding the next file after playback of the current one ends, which is obviously wrong (I've set 'enable_gapless' on FormatOptions). Should I be reusing the same decoder across tracks, or preparing a new one on a separate thread? Is there a way to read how far 'ahead' the decoder is buffered so that I could start preparing a second decoder in the same audio thread? PS I love this crate, it's made a hobby project possible for me that would have been impossible otherwise. Thank you! |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment
-
Hi @Giesch, Regarding Symphonia, all you need to do is set It is then your responsibility to seamlessly join the output of your current track and your next track. To accomplish this, you must deal with a few things. First, you must always preload your next track. My recommendation here is to trigger the preloading of the next track at the time the current track begins playing. Preloading should be done on another thread ideally because IO can be unpredictable. Also, please note that you should never reuse a Second, for gapless playback you can't close and reopen the audio device as this will create audible gaps. This means that you need to choose a sample format and sampling rate for your audio output at the start. There's a few options here:
Third, since the sample format and sampling rate of your audio output is fixed you need to adapt the output of the decoder to match the audio output. Symphonia can do sample format conversion for you automatically via. ( You'll need to do the resampling however. I recommend the Putting all these things together, you could model your audio pipeline as below.
In this model you have a pair of Decoder + Resampler + Buffer components. These are your audio sources. Each of these would run on a separate thread. The ring buffer at the end allows them to buffer data from which the mixer will read from. The mixer will initially consume samples from the first input until it runs out. At that point, the mixer will then consume samples from the second input and the first thread will be free to load the next track. The mixer will flip between the two inputs like this until all tracks are played. Mixer may be a bad name here, but essentially it's just the component responsible for flipping between the two audio inputs. I hope this help, and glad you enjoy using Symphonia! |
Beta Was this translation helpful? Give feedback.
Hi @Giesch,
Regarding Symphonia, all you need to do is set
enable_gapless
totrue
. This options tells aFormatReader
to process the gapless metadata and provide trimming information in thePacket
s. TheDecoder
s will then use that trimming information to chop off the encoder delay and padding samples when required.It is then your responsibility to seamlessly join the output of your current track and your next track. To accomplish this, you must deal with a few things.
First, you must always preload your next track. My recommendation here is to trigger the preloading of the next track at the time the current track begins playing. Preloading should be done on another thread ideally because …