SoundPlayer+LibAudio: Seek does not work for variable bit rate audio formats (FLAC)
To replicate, load any FLAC file into SoundPlayer and try to seek.
How seeking works at the moment is SoundPlayer sets its playing progress bar maximum to the maximum number of samples in an audio stream, and when a user attempts to seek, passes the corresponding sample index into the audio stream's seek method.
This method assumes that that the sample index and the duration of audio played have a linear relationship. For constant bit rate formats (WAV), this is true, and the WavLoader class can easily convert a sample index to a stream offset and seek to the stream offset.
However for variable bit rate formats (FLAC), not only is the sample index wrong (because there is no linear mapping from time to sample index), but it cannot be used to calculate a stream offset.
I worked for a while trying to solve this, but ended up getting a little stuck :)
This is a known issue. Seek is just not implemented in FlacLoader. There was the PR #10273 for it which ran stale, if you want to pick it up, go ahead.
My preferred solution would be to keep a frame index table and prepopulate it with data from the SEEKTABLE block, but fill in the gaps when we actually read frames. Therefore, we can super-accurately seek to already read data, and (if the file has a SEEKTABLE) reasonably well seek to non-read data. A last thing to do would be that if we seek forward without having a SEEKTABLE, we can read the entire file up to that point (which is only possible at about 15x playback speed right now) and fill in the frame index table while we go along, stopping as soon as we have reached the desired sample index.
For reference, take a look at the FLAC spec, especially section 11.3 (SEEKTABLE metadata block)
Ah I see, thank you for the information!
Agreed about the SEEKTABLE. I was working on a more naive version (potentially reading the whole file) with intentions to add the SEEKTABLE once I got the more naive seek working. Because we'll still need to seek forward from the nearest SEEKTABLE seek point anyway, right?
My solution somewhat worked. except, I was never able to get the seek fully accurate. IE the progress bar would always be at least a little out of sync with the progress in the audio stream.
I think the reason it never worked accurately is because callers of the seek() method of the FlacLoader pass in a sample_index that's only correct for constant bit rate audio streams. For example, if you're halfway though a song, in a CBR song you're halfway through the samples, but in VBR that's not true. As a result, I was not accurately able to seek to specific sample because I didn't know which sample I was supposed to seek to.
I may ask some questions in the #audio channel of the discord :)
Seems like this was addressed by https://github.com/SerenityOS/serenity/pull/13262