[GH-ISSUE #45] Gapless not working #37

Closed
opened 2026-02-28 14:24:51 +03:00 by kerem · 7 comments
Owner

Originally created by @volumio on GitHub (Jun 5, 2024).
Original GitHub issue: https://github.com/devgianlu/go-librespot/issues/45

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

Originally created by @volumio on GitHub (Jun 5, 2024). Original GitHub issue: https://github.com/devgianlu/go-librespot/issues/45 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
kerem 2026-02-28 14:24:51 +03:00
  • closed this issue
  • added the
    bug
    label
Author
Owner

@tylkie commented on GitHub (Jun 6, 2024):

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.

<!-- gh-comment-id:2152016976 --> @tylkie commented on GitHub (Jun 6, 2024): 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.
Author
Owner

@devgianlu commented on GitHub (Jun 6, 2024):

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.

<!-- gh-comment-id:2152047406 --> @devgianlu commented on GitHub (Jun 6, 2024): 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.
Author
Owner

@tylkie commented on GitHub (Jun 6, 2024):

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

<!-- gh-comment-id:2152702107 --> @tylkie commented on GitHub (Jun 6, 2024): I really hope you're right. I would guess that switching audio source will always impose the risk of micro-latencies.
Author
Owner

@tylkie commented on GitHub (Jun 7, 2024):

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.

<!-- gh-comment-id:2155433621 --> @tylkie commented on GitHub (Jun 7, 2024): 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](url) 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.
Author
Owner

@devgianlu commented on GitHub (Jul 10, 2024):

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.

<!-- gh-comment-id:2219905364 --> @devgianlu commented on GitHub (Jul 10, 2024): I have started implementing an alternative solution to `pcm_drain` in [gapless](https://github.com/devgianlu/go-librespot/tree/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.
Author
Owner

@devgianlu commented on GitHub (Jul 25, 2024):

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 github.com/devgianlu/go-librespot@756c644443 and github.com/devgianlu/go-librespot@ebcd739c5d. I am closing this as solved, there might be some additional bugs, but gapless is working now.

<!-- gh-comment-id:2249648101 --> @devgianlu commented on GitHub (Jul 25, 2024): 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.
Author
Owner

@tylkie commented on GitHub (Sep 16, 2024):

Since this update, I sometimes (about every 500th song) get the following seg fault... I cannot really tell wether this originates from the output_driver or if this is a conflict caused by my system audio setup. I cannot reproduce it on demand right now. I just wanted to mention it in case somebody else runs into the same problem.

For now, I just bypass this problem by telling systemd to restart the service when it crashes.

DEBU[332351] loading track (paused: false, position: 0ms) uri="spotify:track:06re5XslQ5PFkM3b0Yq4AC"
TRAC[332351] cleared closed output device
SIGSEGV: segmentation violation
PC=0x7f8efbb17647 m=3 sigcode=1 addr=0x7f8efbbc5000
signal arrived during cgo execution

goroutine 45943562 gp=0xc0004408c0 m=3 mp=0xc000075008 [syscall]:
runtime.cgocall(0x90f6d0, 0xc000297f08)
/usr/lib/go/src/runtime/cgocall.go:157 +0x4b fp=0xc000297ee0 sp=0xc000297ea8 pc=0x40cdeb
go-librespot/output._Cfunc_snd_pcm_drain(0x7f8ea97f8a50)
_cgo_gotypes.go:432 +0x47 fp=0xc000297f08 sp=0xc000297ee0 pc=0x82cc87
go-librespot/output.(*output).writeLoop.func1(0xb13aa0?)
/home/tylk/Downloads/go-librespot-0.0.17/output/driver_unix.go:275 +0x3f fp=0xc000297f48 sp=0xc000297f08 pc=0x830fff

<!-- gh-comment-id:2352121598 --> @tylkie commented on GitHub (Sep 16, 2024): Since this update, I sometimes (about every 500th song) get the following seg fault... I cannot really tell wether this originates from the output_driver or if this is a conflict caused by my system audio setup. I cannot reproduce it on demand right now. I just wanted to mention it in case somebody else runs into the same problem. For now, I just bypass this problem by telling systemd to restart the service when it crashes. `DEBU[332351] loading track (paused: false, position: 0ms) uri="spotify:track:06re5XslQ5PFkM3b0Yq4AC"` `TRAC[332351] cleared closed output device` `SIGSEGV: segmentation violation` `PC=0x7f8efbb17647 m=3 sigcode=1 addr=0x7f8efbbc5000` `signal arrived during cgo execution` `goroutine 45943562 gp=0xc0004408c0 m=3 mp=0xc000075008 [syscall]:` `runtime.cgocall(0x90f6d0, 0xc000297f08)` ` /usr/lib/go/src/runtime/cgocall.go:157 +0x4b fp=0xc000297ee0 sp=0xc000297ea8 pc=0x40cdeb` `go-librespot/output._Cfunc_snd_pcm_drain(0x7f8ea97f8a50)` ` _cgo_gotypes.go:432 +0x47 fp=0xc000297f08 sp=0xc000297ee0 pc=0x82cc87` `go-librespot/output.(*output).writeLoop.func1(0xb13aa0?)` ` /home/tylk/Downloads/go-librespot-0.0.17/output/driver_unix.go:275 +0x3f fp=0xc000297f48 sp=0xc000297f08 pc=0x830fff`
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/go-librespot#37
No description provided.