devgianlu / go-librespot

Yet another open source Spotify client, written in Go.
GNU General Public License v3.0
52 stars 7 forks source link

Gapless not working #45

Closed volumio closed 4 days ago

volumio commented 1 month ago

Even when implementing gapless on Volumio, it's not working as there's a slight pause in playback. Try with the Pink Floyd album the dark side of the moon, the transition from track 1 to 2 shall be smooth.

Use https://github.com/volumio/volumio-plugins-sources/tree/spotify14

tylkie commented 1 month ago

According to https://en.wikipedia.org/wiki/Gapless_playback this will be quite some work to fix, especially without real-time scheduling. A timing granularity of 1ms won't suffice to eliminate conceivable gaps. This is not a bug, but a feature, I believe.

devgianlu commented 1 month ago

I feel like there's still some improvements that can be made because the output driver will wait for the alsa sink to drain and then start the playback of the next track. I think an optimization can be made by not draining and simply letting the frames go through immediately after, but I haven't investigated this yet. This is why I marked it as a bug.

tylkie commented 1 month ago

I really hope you're right. I would guess that switching audio source will always impose the risk of micro-latencies.

tylkie commented 1 month ago

I just tried your assumption. I removed the following lines from driver_unix.go

203: out.cond.L.Lock() 204: C.snd_pcm_drain(out.pcmHandle) 205: out.cond.L.Unlock()

To my available sense of hearing, the transition is gapless after that. Unfortunately the last frames of the first song get cut off... so no smooth transition either.

https://stackoverflow.com/questions/3936420/alsa-how-to-tell-when-a-sound-is-finished-playing

Following this post, draining probably is the source of latency between streams. I fiddled around a bit with pcm_delay in order to get a better timing without the overhead from pcm_drain, but without any luck so far. I'd really love to get deeper into this, but unfortunately I don't have the time for this at the moment... my queue is quite filled up, too... :-/

The linux community has moved from direct communication with the alsa kernel driver to more application layer friendly solutions for mixing streams from different sources... such as PulseAudio and PipeWire. This would have some more advantages... like not having the player block the alsa sink for other applications and users on the same system. Stream mixing (i.e. crossfade) and stream manipulation (i.e. an equalizer) would be much easier, too.

devgianlu commented 2 weeks ago

I have started implementing an alternative solution to pcm_drain in gapless. Like I mentioned in the commit, it works for sequential playback, but it breaks very easily. It's a start, lets see where it goes.

devgianlu commented 4 days ago

Upon further investigation, I have realized that most players solve the problem by setting a small buffer time on the ALSA driver and just pipe everything continuosly and ignore the offset between the player state and output state.

This is what I've done in https://github.com/devgianlu/go-librespot/commit/756c644443dd5998065c8a217ab7dd4ca1d437bb and https://github.com/devgianlu/go-librespot/commit/ebcd739c5dbd2450ebfd95053e8b40292fb81399. I am closing this as solved, there might be some additional bugs, but gapless is working now.