[GH-ISSUE #21] Preload next track when approaching the end of current one & gapless playback support #12

Closed
opened 2026-02-27 19:28:16 +03:00 by kerem · 60 comments
Owner

Originally created by @sashahilton00 on GitHub (Jan 29, 2018).
Original GitHub issue: https://github.com/librespot-org/librespot/issues/21

Issue by plietar
Wednesday Dec 30, 2015 at 12:01 GMT
Originally opened as https://github.com/plietar/librespot/issues/18


Currently we don't start loading the next track until the current one has completed. This causes a short delay between tracks.

Instead the next track should start loading before that.

Originally created by @sashahilton00 on GitHub (Jan 29, 2018). Original GitHub issue: https://github.com/librespot-org/librespot/issues/21 <a href="https://github.com/plietar"><img src="https://avatars0.githubusercontent.com/u/1489775?v=4" align="left" width="96" height="96" hspace="10"></img></a> **Issue by [plietar](https://github.com/plietar)** _Wednesday Dec 30, 2015 at 12:01 GMT_ _Originally opened as https://github.com/plietar/librespot/issues/18_ ---- Currently we don't start loading the next track until the current one has completed. This causes a short delay between tracks. Instead the next track should start loading before that.
kerem 2026-02-27 19:28:16 +03:00
Author
Owner

@sashahilton00 commented on GitHub (Jan 29, 2018):

Comment by fbrinker
Saturday Jan 02, 2016 at 10:11 GMT


Maybe with (optional) crossfading, if possible

<!-- gh-comment-id:361256981 --> @sashahilton00 commented on GitHub (Jan 29, 2018): <a href="https://github.com/fbrinker"><img src="https://avatars3.githubusercontent.com/u/5345116?v=4" align="left" width="48" height="48" hspace="10"></img></a> **Comment by [fbrinker](https://github.com/fbrinker)** _Saturday Jan 02, 2016 at 10:11 GMT_ ---- Maybe with (optional) crossfading, if possible
Author
Owner

@sashahilton00 commented on GitHub (Jan 29, 2018):

Comment by TonioRoffo
Friday Nov 11, 2016 at 19:00 GMT


Crossfading and gapless playback are two different things. In HW spotify connect gear, gapless is standard.

<!-- gh-comment-id:361257004 --> @sashahilton00 commented on GitHub (Jan 29, 2018): <a href="https://github.com/TonioRoffo"><img src="https://avatars0.githubusercontent.com/u/15906380?v=4" align="left" width="48" height="48" hspace="10"></img></a> **Comment by [TonioRoffo](https://github.com/TonioRoffo)** _Friday Nov 11, 2016 at 19:00 GMT_ ---- Crossfading and gapless playback are two different things. In HW spotify connect gear, gapless is standard.
Author
Owner

@sashahilton00 commented on GitHub (Jan 29, 2018):

Comment by RafaPolit
Friday Jan 20, 2017 at 21:54 GMT


Gapless is really a must for me, since most of what I listen to is either live or classical music, where much of the 'transitions' are mid-sound (specially in Opera, for instance).

Any time frame on this new feature? Thanks for all the hard work, best regards,
Rafa.

<!-- gh-comment-id:361257024 --> @sashahilton00 commented on GitHub (Jan 29, 2018): <a href="https://github.com/RafaPolit"><img src="https://avatars1.githubusercontent.com/u/4309019?v=4" align="left" width="48" height="48" hspace="10"></img></a> **Comment by [RafaPolit](https://github.com/RafaPolit)** _Friday Jan 20, 2017 at 21:54 GMT_ ---- Gapless is really a must for me, since most of what I listen to is either live or classical music, where much of the 'transitions' are mid-sound (specially in Opera, for instance). Any time frame on this new feature? Thanks for all the hard work, best regards, Rafa.
Author
Owner

@sashahilton00 commented on GitHub (Jan 29, 2018):

Comment by plietar
Friday Jan 20, 2017 at 22:14 GMT


Gapless isn't too hard, and I'm working on a refactor which will also enable that. I can't give it an ETA, but probably somewhere within the next month.

Cross-fade is a lot trickier, and I don't have any immediate plans for it

<!-- gh-comment-id:361257049 --> @sashahilton00 commented on GitHub (Jan 29, 2018): <a href="https://github.com/plietar"><img src="https://avatars0.githubusercontent.com/u/1489775?v=4" align="left" width="48" height="48" hspace="10"></img></a> **Comment by [plietar](https://github.com/plietar)** _Friday Jan 20, 2017 at 22:14 GMT_ ---- Gapless isn't too hard, and I'm working on a refactor which will also enable that. I can't give it an ETA, but probably somewhere within the next month. Cross-fade is a lot trickier, and I don't have any immediate plans for it
Author
Owner

@sashahilton00 commented on GitHub (Jan 29, 2018):

Comment by cortegedusage
Tuesday Feb 14, 2017 at 08:12 GMT


+1 for gapless for me.
(Just for the record, no pressure, keep up the good work.)

<!-- gh-comment-id:361257067 --> @sashahilton00 commented on GitHub (Jan 29, 2018): <a href="https://github.com/cortegedusage"><img src="https://avatars2.githubusercontent.com/u/22986061?v=4" align="left" width="48" height="48" hspace="10"></img></a> **Comment by [cortegedusage](https://github.com/cortegedusage)** _Tuesday Feb 14, 2017 at 08:12 GMT_ ---- +1 for gapless for me. (Just for the record, no pressure, keep up the good work.)
Author
Owner

@sashahilton00 commented on GitHub (Jan 29, 2018):

Comment by johnstok
Wednesday May 31, 2017 at 18:35 GMT


+1 – would be great if this was implemented.

<!-- gh-comment-id:361257088 --> @sashahilton00 commented on GitHub (Jan 29, 2018): <a href="https://github.com/johnstok"><img src="https://avatars1.githubusercontent.com/u/459385?v=4" align="left" width="48" height="48" hspace="10"></img></a> **Comment by [johnstok](https://github.com/johnstok)** _Wednesday May 31, 2017 at 18:35 GMT_ ---- +1 – would be great if this was implemented.
Author
Owner

@sashahilton00 commented on GitHub (Jan 29, 2018):

Comment by RafaPolit
Tuesday Jun 27, 2017 at 17:00 GMT


Any news on this? I'm eagerly awaiting for this and 6 months ago you said it wasn't hard. Was it more difficult than expected or this is simply not a priority for others? For me its critical. Please? Thanks.

<!-- gh-comment-id:361257103 --> @sashahilton00 commented on GitHub (Jan 29, 2018): <a href="https://github.com/RafaPolit"><img src="https://avatars1.githubusercontent.com/u/4309019?v=4" align="left" width="48" height="48" hspace="10"></img></a> **Comment by [RafaPolit](https://github.com/RafaPolit)** _Tuesday Jun 27, 2017 at 17:00 GMT_ ---- Any news on this? I'm eagerly awaiting for this and 6 months ago you said it wasn't hard. Was it more difficult than expected or this is simply not a priority for others? For me its critical. Please? Thanks.
Author
Owner

@sashahilton00 commented on GitHub (Jan 29, 2018):

Comment by sashahilton00
Tuesday Jun 27, 2017 at 18:18 GMT


Paul mentioned he was busy until July, most things have been put on hold till then. I'm sure it will be added in due course. Cross fade is harder due to the need to mix audio streams

<!-- gh-comment-id:361257115 --> @sashahilton00 commented on GitHub (Jan 29, 2018): <a href="https://github.com/sashahilton00"><img src="https://avatars3.githubusercontent.com/u/4185362?v=4" align="left" width="48" height="48" hspace="10"></img></a> **Comment by [sashahilton00](https://github.com/sashahilton00)** _Tuesday Jun 27, 2017 at 18:18 GMT_ ---- Paul mentioned he was busy until July, most things have been put on hold till then. I'm sure it will be added in due course. Cross fade is harder due to the need to mix audio streams
Author
Owner

@sashahilton00 commented on GitHub (Jan 29, 2018):

Comment by RafaPolit
Tuesday Jun 27, 2017 at 19:14 GMT


Yeah, I wasn't talking about crossfading, just gapless playback for concerts, and classical pieces that flow seamlessly into the next movement (most critical in Opera).

<!-- gh-comment-id:361257143 --> @sashahilton00 commented on GitHub (Jan 29, 2018): <a href="https://github.com/RafaPolit"><img src="https://avatars1.githubusercontent.com/u/4309019?v=4" align="left" width="48" height="48" hspace="10"></img></a> **Comment by [RafaPolit](https://github.com/RafaPolit)** _Tuesday Jun 27, 2017 at 19:14 GMT_ ---- Yeah, I wasn't talking about crossfading, just gapless playback for concerts, and classical pieces that flow seamlessly into the next movement (most critical in Opera).
Author
Owner

@sashahilton00 commented on GitHub (Jan 29, 2018):

Comment by pauLee
Friday Nov 17, 2017 at 14:37 GMT


Any progress on gapless playback? I still hear breaks between the tracks.

<!-- gh-comment-id:361257172 --> @sashahilton00 commented on GitHub (Jan 29, 2018): <a href="https://github.com/pauLee"><img src="https://avatars2.githubusercontent.com/u/592889?v=4" align="left" width="48" height="48" hspace="10"></img></a> **Comment by [pauLee](https://github.com/pauLee)** _Friday Nov 17, 2017 at 14:37 GMT_ ---- Any progress on gapless playback? I still hear breaks between the tracks.
Author
Owner

@sashahilton00 commented on GitHub (Mar 14, 2018):

With regards to this, one thing I have noticed, is that the loading of tracks is synchronous, hence if one is listening to a playlist and skips a few songs, it loads each of the skipped tracks, leading to a few seconds of silence. Ideally, when firing load_track, we'd dump any existing track load requests.

<!-- gh-comment-id:372878977 --> @sashahilton00 commented on GitHub (Mar 14, 2018): With regards to this, one thing I have noticed, is that the loading of tracks is synchronous, hence if one is listening to a playlist and skips a few songs, it loads each of the skipped tracks, leading to a few seconds of silence. Ideally, when firing load_track, we'd dump any existing track load requests.
Author
Owner

@RafaPolit commented on GitHub (Apr 2, 2018):

Please give some love to this issue. Most other issues can be accomplished by other means or extra steps. This is the one that is un-accomplishable, no matter how you see it.

It's the only thing still keeping me with other approaches.

Best regards,
Rafa.

<!-- gh-comment-id:377849713 --> @RafaPolit commented on GitHub (Apr 2, 2018): Please give some love to this issue. Most other issues can be accomplished by other means or extra steps. This is the one that is un-accomplishable, no matter how you see it. It's the only thing still keeping me with other approaches. Best regards, Rafa.
Author
Owner

@herrernst commented on GitHub (Apr 9, 2018):

Would also love to see somebody working on gapless playback (I don't care about crossfading). This is the last issue that make playback imperfect :/

<!-- gh-comment-id:379879515 --> @herrernst commented on GitHub (Apr 9, 2018): Would also love to see somebody working on gapless playback (I don't care about crossfading). This is the last issue that make playback imperfect :/
Author
Owner

@st0nec0ld commented on GitHub (May 8, 2018):

Anything new? The gap between two songs is realy annoying.

<!-- gh-comment-id:387566205 --> @st0nec0ld commented on GitHub (May 8, 2018): Anything new? The gap between two songs is realy annoying.
Author
Owner

@WhiteHatTux commented on GitHub (May 12, 2018):

I started looking into it. As the main delay for me is with the network, the first step I see is to implement a PlayerCommand:PreLoad which will just make sure, that the file is available, when it will be requested for real. This part I have actually implemented.

Something, that I have not yet figured out is, how the PreLoad Command could be triggered. Something like: "end of track is 15 seconds away. Start Preload now".

<!-- gh-comment-id:388523847 --> @WhiteHatTux commented on GitHub (May 12, 2018): I started looking into it. As the main delay for me is with the network, the first step I see is to implement a PlayerCommand:PreLoad which will just make sure, that the file is available, when it will be requested for real. This part I have actually implemented. Something, that I have not yet figured out is, how the PreLoad Command could be triggered. Something like: "end of track is 15 seconds away. Start Preload now".
Author
Owner

@WhiteHatTux commented on GitHub (May 12, 2018):

@st0nec0ld Do you get the delay between every song, or only between songs, that are not yet cached?

<!-- gh-comment-id:388529760 --> @WhiteHatTux commented on GitHub (May 12, 2018): @st0nec0ld Do you get the delay between every song, or only between songs, that are not yet cached?
Author
Owner

@berrywhite96 commented on GitHub (May 15, 2018):

@WhiteHatTux I think it make sense to trigger the loading of the next song on every new track. So if a new track starts, load the next track. Also the RAM on the pi is big enough, see raspberry pi 3 b+, to handle this easily.
I personally skip often through tracks. In detail: hear the song for 3 seconds and then skip. These 3 seconds are enough (in my network) to load a track, so in this scenario there wouldnt be a gap.

This gap is really annoying, loading tracks needs maybe 1-2 seconds (in my network with 320kbps), but my av receiver dont get a signal for a second and needs then some time to recognize the codec of the audio stream, so the gap is longer than the track needs to load.

<!-- gh-comment-id:389142981 --> @berrywhite96 commented on GitHub (May 15, 2018): @WhiteHatTux I think it make sense to trigger the loading of the next song on every new track. So if a new track starts, load the next track. Also the RAM on the pi is big enough, see raspberry pi 3 b+, to handle this easily. I personally skip often through tracks. In detail: hear the song for 3 seconds and then skip. These 3 seconds are enough (in my network) to load a track, so in this scenario there wouldnt be a gap. This gap is really annoying, loading tracks needs maybe 1-2 seconds (in my network with 320kbps), but my av receiver dont get a signal for a second and needs then some time to recognize the codec of the audio stream, so the gap is longer than the track needs to load.
Author
Owner

@github-ronk commented on GitHub (Jun 20, 2018):

+1 for this...

Thanks :)

<!-- gh-comment-id:398713794 --> @github-ronk commented on GitHub (Jun 20, 2018): +1 for this... Thanks :)
Author
Owner

@giggywithit commented on GitHub (Jun 27, 2018):

Vorbis is designed from the ground up to be used for streaming no?
MPD can do gapless playback and apparently buffers.
I know that it is also possible to playback a vorbis file as it is being created (downloaded)

Forget hacks of how to anticipate what the user is going to do.

<!-- gh-comment-id:400725440 --> @giggywithit commented on GitHub (Jun 27, 2018): Vorbis is designed from the ground up to be used for streaming no? MPD can do gapless playback and apparently buffers. I know that it is also possible to playback a vorbis file as it is being created (downloaded) Forget hacks of how to anticipate what the user is going to do.
Author
Owner

@kingosticks commented on GitHub (Jun 27, 2018):

I think it's safe to implement gapless the same way everyone else in the world does it. i.e. buffer the output slightly. If you skip through tracks you'll have a small gap but that's fine - gapless here makes no sense at all.

Issues with external audio devices caused by 'stopping' the audio output are a separate issue.

<!-- gh-comment-id:400733768 --> @kingosticks commented on GitHub (Jun 27, 2018): I think it's safe to implement gapless the same way everyone else in the world does it. i.e. buffer the output slightly. If you skip through tracks you'll have a small gap but that's fine - gapless here makes no sense at all. Issues with external audio devices caused by 'stopping' the audio output are a separate issue.
Author
Owner

@giggywithit commented on GitHub (Jun 27, 2018):

If I use the 'play *' command in a directory filled with files from a live album, the files are played back gaplessly. If I use the 'play *' command with one file in the directory and add one after I have executed the command, it will stop playback after playing the initial file, not seeing the next. It would seem apparent that one could implement an addition to the 'play' command to optionally seek another file during playback and adding it to its process. 'play' also has the capacity to playback files over http etc.. If this were the case, 'play' could be used to simply play files that are being loaded into a cache directory.

play is an audio player as part of the SOX project

<!-- gh-comment-id:400741467 --> @giggywithit commented on GitHub (Jun 27, 2018): If I use the 'play *' command in a directory filled with files from a live album, the files are played back gaplessly. If I use the 'play *' command with one file in the directory and add one after I have executed the command, it will stop playback after playing the initial file, not seeing the next. It would seem apparent that one could implement an addition to the 'play' command to optionally seek another file during playback and adding it to its process. 'play' also has the capacity to playback files over http etc.. If this were the case, 'play' could be used to simply play files that are being loaded into a cache directory. play is an audio player as part of the SOX project
Author
Owner

@giggywithit commented on GitHub (Jul 8, 2018):

Hello Nick Steel

It seems that you have been around these places for some time and would be the person to ask the question...

What I am looking for, since there are no reliable spotify players for the raspberry pi and seems that it will be quite some time until one that works effectively, is the best method to stream audio to the raspberry pi from another computer on the network. I have an ubuntu laptop that I connect directly to the raspberry pi via the cat5 wired interface and share the internet connection that I get on the laptop with it. I like the idea of streaming pcm audio rather than compressing it. I like using raspian lite headless.. Minimal but full effectiveness preferred.

Can you offer a better solution?
Absolutely appreciated

<!-- gh-comment-id:403258646 --> @giggywithit commented on GitHub (Jul 8, 2018): Hello Nick Steel It seems that you have been around these places for some time and would be the person to ask the question... What I am looking for, since there are no reliable spotify players for the raspberry pi and seems that it will be quite some time until one that works effectively, is the best method to stream audio to the raspberry pi from another computer on the network. I have an ubuntu laptop that I connect directly to the raspberry pi via the cat5 wired interface and share the internet connection that I get on the laptop with it. I like the idea of streaming pcm audio rather than compressing it. I like using raspian lite headless.. Minimal but full effectiveness preferred. Can you offer a better solution? Absolutely appreciated
Author
Owner

@kingosticks commented on GitHub (Jul 8, 2018):

I think you'd be better off asking your question on the official raspberry pi forums. Hijacking this issue here with your unrelated question isn't helpful. Please don't do that.

<!-- gh-comment-id:403273228 --> @kingosticks commented on GitHub (Jul 8, 2018): I think you'd be better off asking your question on the official raspberry pi forums. Hijacking this issue here with your unrelated question isn't helpful. Please don't do that.
Author
Owner

@RafaPolit commented on GitHub (Aug 15, 2018):

Another month gone by, is this going anywhere? Or are the people in need of gapless just doomed to look elsewhere?

<!-- gh-comment-id:413246466 --> @RafaPolit commented on GitHub (Aug 15, 2018): Another month gone by, is this going anywhere? Or are the people in need of gapless just doomed to look elsewhere?
Author
Owner

@jebos commented on GitHub (Oct 21, 2018):

was there any work on this? any updates?

<!-- gh-comment-id:431642950 --> @jebos commented on GitHub (Oct 21, 2018): was there any work on this? any updates?
Author
Owner

@awiouy commented on GitHub (Oct 21, 2018):

@jebos No

<!-- gh-comment-id:431652527 --> @awiouy commented on GitHub (Oct 21, 2018): @jebos No
Author
Owner

@CooperWhitlow commented on GitHub (Oct 30, 2018):

+1 would be super awesome for crossfade

<!-- gh-comment-id:434155065 --> @CooperWhitlow commented on GitHub (Oct 30, 2018): +1 would be super awesome for crossfade
Author
Owner

@laurensnl commented on GitHub (Nov 4, 2018):

+1 for gapless playback. Would be great!

<!-- gh-comment-id:435670168 --> @laurensnl commented on GitHub (Nov 4, 2018): +1 for gapless playback. Would be great!
Author
Owner

@sashahilton00 commented on GitHub (Nov 5, 2018):

no work on this issue yet. I think most maintainers are busy with work or studies atm. PR's always welcome, otherwise I suspect this will remain in progress for a while longer. also, please use reactions to indicate support. any more +1 comments will be deleted.

<!-- gh-comment-id:435723638 --> @sashahilton00 commented on GitHub (Nov 5, 2018): no work on this issue yet. I think most maintainers are busy with work or studies atm. PR's always welcome, otherwise I suspect this will remain in progress for a while longer. also, please use reactions to indicate support. any more +1 comments will be deleted.
Author
Owner

@jebos commented on GitHub (Nov 6, 2018):

Though, in theory - what needs to be done (for gapless)?

  1. keep the sink open, right now it looks like the sink is closed and recreated for each track.
  2. have buffering active and start loading of next track into buffer as soon as current track is fully buffered.
  3. play from buffer

Any suggestion on how to do this? Keeping the sink open (at least for some time) seems achiveable, I'm not sure about the buffering part, though (without large changes, at least)

<!-- gh-comment-id:436207480 --> @jebos commented on GitHub (Nov 6, 2018): Though, in theory - what needs to be done (for gapless)? 1) keep the sink open, right now it looks like the sink is closed and recreated for each track. 2) have buffering active and start loading of next track into buffer as soon as current track is fully buffered. 3) play from buffer Any suggestion on how to do this? Keeping the sink open (at least for some time) seems achiveable, I'm not sure about the buffering part, though (without large changes, at least)
Author
Owner

@ashthespy commented on GitHub (Nov 6, 2018):

FWIW: I managed to implement a proof of concept of the preloading - but didn't find time to implement a buffer yet from the sink side.
What it does it implement a new channel that communicates "almost end of track" to spric which triggers the prefetching of the next track provided by handle_next from spirc. I can clean it up and push it if someone is interested in it.

Though, in theory - what needs to be done (for gapless)?

1. keep the sink open, right now it looks like the sink is closed and recreated for each track.

2. have buffering active and start loading of next track into buffer as soon as current track is fully buffered.

3. play from buffer

Any suggestion on how to do this? Keeping the sink open (at least for some time) seems achiveable, I'm not sure about the buffering part, though (without large changes, at least)

Keeping the sink open as it is now will lead to underruns in alsa as there is no data to be played - we would need to implement a small buffer inside player that is responsible to feed the sink.

<!-- gh-comment-id:436210623 --> @ashthespy commented on GitHub (Nov 6, 2018): FWIW: I managed to implement a proof of concept of the preloading - but didn't find time to implement a buffer yet from the sink side. What it does it implement a new channel that communicates "almost end of track" to `spric` which triggers the prefetching of the next track provided by `handle_next` from `spirc`. I can clean it up and push it if someone is interested in it. > Though, in theory - what needs to be done (for gapless)? > > 1. keep the sink open, right now it looks like the sink is closed and recreated for each track. > > 2. have buffering active and start loading of next track into buffer as soon as current track is fully buffered. > > 3. play from buffer > > > Any suggestion on how to do this? Keeping the sink open (at least for some time) seems achiveable, I'm not sure about the buffering part, though (without large changes, at least) Keeping the sink open as it is now will lead to underruns in `alsa` as there is no data to be played - we would need to implement a small buffer inside `player` that is responsible to feed the sink.
Author
Owner

@sashahilton00 commented on GitHub (Nov 8, 2018):

@ashthespy by all means push it to a branch. i was just getting started on working out how to signal from player back to spirc, so if you have a PoC, that would save me a headache :)

<!-- gh-comment-id:436846721 --> @sashahilton00 commented on GitHub (Nov 8, 2018): @ashthespy by all means push it to a branch. i was just getting started on working out how to signal from player back to spirc, so if you have a PoC, that would save me a headache :)
Author
Owner

@ashthespy commented on GitHub (Nov 8, 2018):

@ashthespy by all means push it to a branch. i was just getting started on working out how to signal from player back to spirc, so if you have a PoC, that would save me a headache :)

Here you go https://github.com/librespot-org/librespot/compare/master...ashthespy:gapless
It's not an optimised implementation in anyway but should work with the lewton, and prefetch a track when there are 2 seconds left in the current track.

<!-- gh-comment-id:436991182 --> @ashthespy commented on GitHub (Nov 8, 2018): > > > @ashthespy by all means push it to a branch. i was just getting started on working out how to signal from player back to spirc, so if you have a PoC, that would save me a headache :) Here you go https://github.com/librespot-org/librespot/compare/master...ashthespy:gapless It's not an optimised implementation in anyway but should work with the `lewton`, and prefetch a track when there are 2 seconds left in the current track.
Author
Owner

@jebos commented on GitHub (Nov 9, 2018):

Hi, I think there should be a check that the current track duration is longer than 2 seconds (paranoid me) and maybe the amount of time should depend on the selected quality (assuming that higher qualtiy results in slower prefetch time)

What do you think about doing a procentual amount like 5% of duration and bounding that on 0 seconds (min) and 5 seconds. (max)

<!-- gh-comment-id:437289366 --> @jebos commented on GitHub (Nov 9, 2018): Hi, I think there should be a check that the current track duration is longer than 2 seconds (paranoid me) and maybe the amount of time should depend on the selected quality (assuming that higher qualtiy results in slower prefetch time) What do you think about doing a procentual amount like 5% of duration and bounding that on 0 seconds (min) and 5 seconds. (max)
Author
Owner

@plietar commented on GitHub (Nov 9, 2018):

IMO the best thing to do is start loading the next track as soon as the current one is fully cached. This may be a little complicated to implement for now, so a 2 second window sounds fine.

<!-- gh-comment-id:437297447 --> @plietar commented on GitHub (Nov 9, 2018): IMO the best thing to do is start loading the next track as soon as the current one is fully cached. This may be a little complicated to implement for now, so a 2 second window sounds fine.
Author
Owner

@ashthespy commented on GitHub (Nov 9, 2018):

Hi, I think there should be a check that the current track duration is longer than 2 seconds (paranoid me) and maybe the amount of time should depend on the selected quality (assuming that higher qualtiy results in slower prefetch time)

What do you think about doing a procentual amount like 5% of duration and bounding that on 0 seconds (min) and 5 seconds. (max)

I had initially set it to trigger when there was 20% remaining, but then switched to a hard-coded 2 seconds to test - but it might be a good idea to make this bitrate/file size dependent.

IMO the best thing to do is start loading the next track as soon as the current one is fully cached. This may be a little complicated to implement for now, so a 2 second window sounds fine.

That would need another channel with the fetcher right?
Also - any ideas about the buffer implementation? I I put something together, but now trying to implement tracking the playback state independent of the buffer - need to look into if there is some way to queue the Vorbis decoder?

<!-- gh-comment-id:437319321 --> @ashthespy commented on GitHub (Nov 9, 2018): > > > Hi, I think there should be a check that the current track duration is longer than 2 seconds (paranoid me) and maybe the amount of time should depend on the selected quality (assuming that higher qualtiy results in slower prefetch time) > > What do you think about doing a procentual amount like 5% of duration and bounding that on 0 seconds (min) and 5 seconds. (max) I had initially set it to trigger when there was 20% remaining, but then switched to a hard-coded 2 seconds to test - but it might be a good idea to make this bitrate/file size dependent. > > > IMO the best thing to do is start loading the next track as soon as the current one is fully cached. This may be a little complicated to implement for now, so a 2 second window sounds fine. That would need another channel with the fetcher right? Also - any ideas about the buffer implementation? I I put something together, but now trying to implement tracking the playback state independent of the buffer - need to look into if there is some way to queue the Vorbis decoder?
Author
Owner

@sashahilton00 commented on GitHub (Nov 9, 2018):

If we want to keep behaviour consistent with the Spotify clients, I believe that they have hardcoded 30 seconds before the end of the track as the preload trigger for all bitrates/files.

With regards to keeping to sink open, how about implementing a feature flag such as --disable-sink-keepalive, and then by default keep the sync open between songs unless that flag was present? If we preload things 30 seconds before the end of the track à la Spotify, there shouldn't be any delay in filling the buffer (causing an underrun), or if there is it should be <1s, at which point do we care? If it is an issue, one could just disable it for the current behaviour.

IMO the best thing to do is start loading the next track as soon as the current one is fully cached. This may be a little complicated to implement for now, so a 2 second window sounds fine.

This sounds potentially tricky from a logistics point of view. Let's say I play track 1. Track 2 is cached after a few seconds, then what? Does it cache Track 3, then 4, etc. or does it wait until Track 2 starts, then start caching Track 3? In the former case, if Spotify loads a large playlist or album, the cache is going to fill up quickly, which would potentially have to be managed through some sort of cache expiry mechanism mentioned in a previous issue (will add issue number later if I can find it), and in the latter case, why bother with the extra complexity of another channel and a buffer to preload a track at the beginning of a song? We could do that with minor modifications to @ashthespy's branch. Also, as an afterthought, for those on limited data plans (e.g. mobile broadband), librespot could use up a lot of bandwidth pointlessly loading tracks that end up not being played in the former case.

Also @ashthespy could you create a PR so that we can move discussion of implementation details over to that, review, commit, etc. Thanks.

<!-- gh-comment-id:437395385 --> @sashahilton00 commented on GitHub (Nov 9, 2018): If we want to keep behaviour consistent with the Spotify clients, I believe that they have hardcoded 30 seconds before the end of the track as the preload trigger for all bitrates/files. With regards to keeping to sink open, how about implementing a feature flag such as `--disable-sink-keepalive`, and then by default keep the sync open between songs unless that flag was present? If we preload things 30 seconds before the end of the track à la Spotify, there shouldn't be any delay in filling the buffer (causing an underrun), or if there is it should be <1s, at which point do we care? If it is an issue, one could just disable it for the current behaviour. > IMO the best thing to do is start loading the next track as soon as the current one is fully cached. This may be a little complicated to implement for now, so a 2 second window sounds fine. This sounds potentially tricky from a logistics point of view. Let's say I play track 1. Track 2 is cached after a few seconds, then what? Does it cache Track 3, then 4, etc. or does it wait until Track 2 starts, then start caching Track 3? In the former case, if Spotify loads a large playlist or album, the cache is going to fill up quickly, which would potentially have to be managed through some sort of cache expiry mechanism mentioned in a previous issue (will add issue number later if I can find it), and in the latter case, why bother with the extra complexity of another channel and a buffer to preload a track at the beginning of a song? We could do that with minor modifications to @ashthespy's branch. Also, as an afterthought, for those on limited data plans (e.g. mobile broadband), librespot could use up a lot of bandwidth pointlessly loading tracks that end up not being played in the former case. Also @ashthespy could you create a PR so that we can move discussion of implementation details over to that, review, commit, etc. Thanks.
Author
Owner

@kingosticks commented on GitHub (Nov 9, 2018):

This sounds potentially tricky from a logistics point of view. Let's say I play track 1. Track 2 is cached after a few seconds, then what? Does it cache Track 3, then 4, etc

No. That's a different strategy and it's unnecessary.

Signalling at x seconds before end of track to start fetching the next song is fine. x can be really small, you only need to fetch enough to start playing, you do not need the entire song.

<!-- gh-comment-id:437403874 --> @kingosticks commented on GitHub (Nov 9, 2018): > This sounds potentially tricky from a logistics point of view. Let's say I play track 1. Track 2 is cached after a few seconds, then what? Does it cache Track 3, then 4, etc No. That's a different strategy and it's unnecessary. Signalling at x seconds before end of track to start fetching the next song is fine. x can be really small, you only need to fetch enough to start playing, you do not need the entire song.
Author
Owner

@ashthespy commented on GitHub (Nov 11, 2018):

If we want to keep behaviour consistent with the Spotify clients, I believe that they have hardcoded 30 seconds before the end of the track as the preload trigger for all bitrates/files.

With regards to keeping to sink open, how about implementing a feature flag such as --disable-sink-keepalive, and then by default keep the sync open between songs unless that flag was present? If we preload things 30 seconds before the end of the track à la Spotify, there shouldn't be any delay in filling the buffer (causing an underrun), or if there is it should be <1s, at which point do we care? If it is an issue, one could just disable it for the current behaviour.

I don't know about that - I tried something along these lines already (an option to keep the sink open b/w songs at https://github.com/librespot-org/librespot/compare/master...ashthespy:greedysink) but on my fruity pi systems, the time taken to fetch the next song, decrypt, and decode the audio (even from cache) leads to noticeable under runs from alsa.

Also @ashthespy could you create a PR so that we can move discussion of implementation details over to that, review, commit, etc. Thanks.

#263 Sure, I might be pressed for time the next few weeks - so it might be slow progress!

<!-- gh-comment-id:437705956 --> @ashthespy commented on GitHub (Nov 11, 2018): > > > If we want to keep behaviour consistent with the Spotify clients, I believe that they have hardcoded 30 seconds before the end of the track as the preload trigger for all bitrates/files. > > With regards to keeping to sink open, how about implementing a feature flag such as `--disable-sink-keepalive`, and then by default keep the sync open between songs unless that flag was present? If we preload things 30 seconds before the end of the track à la Spotify, there shouldn't be any delay in filling the buffer (causing an underrun), or if there is it should be <1s, at which point do we care? If it is an issue, one could just disable it for the current behaviour. I don't know about that - I tried something along these lines already (an option to keep the sink open b/w songs at https://github.com/librespot-org/librespot/compare/master...ashthespy:greedysink) but on my fruity pi systems, the time taken to fetch the next song, decrypt, and decode the audio (even from cache) leads to noticeable under runs from alsa. > Also @ashthespy could you create a PR so that we can move discussion of implementation details over to that, review, commit, etc. Thanks. #263 Sure, I might be pressed for time the next few weeks - so it might be slow progress!
Author
Owner

@loeffelpan commented on GitHub (Dec 30, 2018):

Any progress here?
Is there a branch who could be built by someone for debian? Just for testing.

<!-- gh-comment-id:450550209 --> @loeffelpan commented on GitHub (Dec 30, 2018): Any progress here? Is there a branch who could be built by someone for debian? Just for testing.
Author
Owner

@devgianlu commented on GitHub (Dec 30, 2018):

@ashthespy is working on it here in Rust. I've implemented it in librespot-java.

<!-- gh-comment-id:450554581 --> @devgianlu commented on GitHub (Dec 30, 2018): @ashthespy is working on it [here](https://github.com/ashthespy/librespot/tree/gapless) in Rust. I've implemented it in [librespot-java](https://github.com/librespot-org/librespot-java).
Author
Owner

@loeffelpan commented on GitHub (Jan 6, 2019):

@ashthespy Is your gapless branch ready to use?
I would give it a try to use is in my raspotify installation.

<!-- gh-comment-id:451734720 --> @loeffelpan commented on GitHub (Jan 6, 2019): @ashthespy Is your gapless branch ready to use? I would give it a try to use is in my raspotify installation.
Author
Owner

@ashthespy commented on GitHub (Jan 8, 2019):

@loeffelpan No, haven't had much time off late, prefetch is implemented, gapless needs some restructuring of the player (unless I am missing something).

My tentative approach is to have two audio buffers - on for the currently playing track, and another buffer where a few packets of the next track are decoded and kept ready for playback. That way the current playback state can be tracked independent of the packet decoder. If anyone has suggestions for alternatives - do share!

<!-- gh-comment-id:452309911 --> @ashthespy commented on GitHub (Jan 8, 2019): @loeffelpan No, haven't had much time off late, prefetch is implemented, gapless needs some restructuring of the player (unless I am missing something). My tentative approach is to have two audio buffers - on for the currently playing track, and another buffer where a few packets of the next track are decoded and kept ready for playback. That way the current playback state can be tracked independent of the packet decoder. If anyone has suggestions for alternatives - do share!
Author
Owner

@loeffelpan commented on GitHub (Jan 8, 2019):

I don't have any suggestions. Just want to be up to date.

Sounds good. Do you have any plan to finish that project?
No pressure, but i'm very exited to try the result with my audiobooks hearing gapless! <3

<!-- gh-comment-id:452392587 --> @loeffelpan commented on GitHub (Jan 8, 2019): I don't have any suggestions. Just want to be up to date. Sounds good. Do you have any plan to finish that project? No pressure, but i'm very exited to try the result with my audiobooks hearing gapless! <3
Author
Owner

@nkappler commented on GitHub (Mar 5, 2019):

Hi, any news on this already?
Would love it as well :)

Let me know if you need help with this, I might have a look at it aswell...

<!-- gh-comment-id:469778224 --> @nkappler commented on GitHub (Mar 5, 2019): Hi, any news on this already? Would love it as well :) Let me know if you need help with this, I might have a look at it aswell...
Author
Owner

@loeffelpan commented on GitHub (Mar 5, 2019):

I switched to librespot-java.
Works flawlessly now after some issues.

<!-- gh-comment-id:469814365 --> @loeffelpan commented on GitHub (Mar 5, 2019): I switched to librespot-java. Works flawlessly now after some issues.
Author
Owner

@Identity9165 commented on GitHub (May 7, 2019):

librespot-java

I spent an hour with maven and it's not compiling. binaries don't help either. :(

<!-- gh-comment-id:489999338 --> @Identity9165 commented on GitHub (May 7, 2019): > librespot-java I spent an hour with maven and it's not compiling. binaries don't help either. :(
Author
Owner

@devgianlu commented on GitHub (May 7, 2019):

@msc1 You need to install Java, then download a precompiled binary and run java -jar ./librespot-core-jar-with-dependencies.jar. Check the read me, you can contact me on Gitter if you still have problems.

<!-- gh-comment-id:490017971 --> @devgianlu commented on GitHub (May 7, 2019): @msc1 You need to install Java, then download a precompiled binary and run `java -jar ./librespot-core-jar-with-dependencies.jar`. Check the read me, you can contact me on Gitter if you still have problems.
Author
Owner

@Identity9165 commented on GitHub (May 7, 2019):

yes I have them installed already, I couldn't found a solution for the following error:

env: DietPi v6.22.3 (Debian 9) RPi 3 Model B (armv7l)

> root@spotifypi:~# java -version
> openjdk version "1.8.0_212"
> OpenJDK Runtime Environment (build 1.8.0_212-8u212-b01-1~deb9u1-b01)
> OpenJDK Client VM (build 25.212-b01, mixed mode)
> root@spotifypi:~# javac -version
> javac 1.8.0_212
> root@spotifypi:~# mvn -v
> Apache Maven 3.3.9
> Maven home: /usr/share/maven
> Java version: 1.8.0_212, vendor: Oracle Corporation
> Java home: /usr/lib/jvm/java-8-openjdk-armhf/jre
> Default locale: en_GB, platform encoding: UTF-8
> OS name: "linux", version: "4.14.98-v7+", arch: "arm", family: "unix"

building with maven fails around api client

> /root/librespot-java-0.5.2/api-client/src/main/java/xyz.gianlu.librespot.api.client/Main.java:[3,26] cannot access javafx.application.Application
>   bad class file: /root/.m2/repository/org/openjfx/javafx-graphics/12-ea+8/javafx-graphics-12-ea+8-linux.jar(javafx/application/Application.class)
>     class file has wrong version 55.0, should be 52.0
>     Please remove or make sure it appears in the correct subdirectory of the classpath.
> 

any help would be appreciated, thanks.

<!-- gh-comment-id:490023085 --> @Identity9165 commented on GitHub (May 7, 2019): yes I have them installed already, I couldn't found a solution for the following error: env: DietPi v6.22.3 (Debian 9) RPi 3 Model B (armv7l) ``` > root@spotifypi:~# java -version > openjdk version "1.8.0_212" > OpenJDK Runtime Environment (build 1.8.0_212-8u212-b01-1~deb9u1-b01) > OpenJDK Client VM (build 25.212-b01, mixed mode) > root@spotifypi:~# javac -version > javac 1.8.0_212 > root@spotifypi:~# mvn -v > Apache Maven 3.3.9 > Maven home: /usr/share/maven > Java version: 1.8.0_212, vendor: Oracle Corporation > Java home: /usr/lib/jvm/java-8-openjdk-armhf/jre > Default locale: en_GB, platform encoding: UTF-8 > OS name: "linux", version: "4.14.98-v7+", arch: "arm", family: "unix" ``` building with maven fails around api client ``` > /root/librespot-java-0.5.2/api-client/src/main/java/xyz.gianlu.librespot.api.client/Main.java:[3,26] cannot access javafx.application.Application > bad class file: /root/.m2/repository/org/openjfx/javafx-graphics/12-ea+8/javafx-graphics-12-ea+8-linux.jar(javafx/application/Application.class) > class file has wrong version 55.0, should be 52.0 > Please remove or make sure it appears in the correct subdirectory of the classpath. > ``` any help would be appreciated, thanks.
Author
Owner

@devgianlu commented on GitHub (May 7, 2019):

Check https://github.com/librespot-org/librespot-java/issues/86#issuecomment-489096807

<!-- gh-comment-id:490024458 --> @devgianlu commented on GitHub (May 7, 2019): Check https://github.com/librespot-org/librespot-java/issues/86#issuecomment-489096807
Author
Owner

@Identity9165 commented on GitHub (May 7, 2019):

@devgianlu thanks for the tip, installing openjfx solved the issue. any tips for crossfading?

<!-- gh-comment-id:490061906 --> @Identity9165 commented on GitHub (May 7, 2019): @devgianlu thanks for the tip, installing `openjfx` solved the issue. any tips for crossfading?
Author
Owner

@devgianlu commented on GitHub (May 7, 2019):

Crossfade isn't implemented yet. Open an issue on librespot-java, I'll start to think about the implementation when I have time.

<!-- gh-comment-id:490063471 --> @devgianlu commented on GitHub (May 7, 2019): Crossfade isn't implemented yet. Open an issue on `librespot-java`, I'll start to think about the implementation when I have time.
Author
Owner

@AInteriorB commented on GitHub (Nov 13, 2019):

Anything new on this topic? I use raspotify. If I skip to the next song, everything is smooth. But if I'm listening till the end of a song I hear a "click" in the pause to the next one.

<!-- gh-comment-id:553557633 --> @AInteriorB commented on GitHub (Nov 13, 2019): Anything new on this topic? I use raspotify. If I skip to the next song, everything is smooth. But if I'm listening till the end of a song I hear a "click" in the pause to the next one.
Author
Owner

@bendarlog commented on GitHub (Dec 5, 2019):

Hello, Any news about gapless feature ? It would be really nice, as actually I hear clicks (Amplifier's DAC disconnect/reconnect) at the change of each tracks as the flow is cut and restarted.

I think it would be a great feature for this project.

Many thanks for all your great work,

<!-- gh-comment-id:562040631 --> @bendarlog commented on GitHub (Dec 5, 2019): Hello, Any news about gapless feature ? It would be really nice, as actually I hear clicks (Amplifier's DAC disconnect/reconnect) at the change of each tracks as the flow is cut and restarted. I think it would be a great feature for this project. Many thanks for all your great work,
Author
Owner

@nkappler commented on GitHub (Dec 5, 2019):

the click can be prevented by leaving the audio device attached.
This guide might help
https://learn.adafruit.com/adafruit-i2s-stereo-decoder-uda1334a/raspberry-pi-usage

We've added an extra helper systemd script that will play quiet audio when the I2S peripheral isn't in use. This removes popping when playback starts or stops. It uses a tiny amount of CPU time (on a Pi Zero, 5%, on a Pi 2 or 3 its negligible). You don't need this on RetroPie because it never releases the I2S device, but it's great for Raspbian.

<!-- gh-comment-id:562096697 --> @nkappler commented on GitHub (Dec 5, 2019): the click can be prevented by leaving the audio device attached. This guide might help https://learn.adafruit.com/adafruit-i2s-stereo-decoder-uda1334a/raspberry-pi-usage `We've added an extra helper systemd script that will play quiet audio when the I2S peripheral isn't in use. This removes popping when playback starts or stops. It uses a tiny amount of CPU time (on a Pi Zero, 5%, on a Pi 2 or 3 its negligible). You don't need this on RetroPie because it never releases the I2S device, but it's great for Raspbian.`
Author
Owner

@AInteriorB commented on GitHub (Dec 5, 2019):

the click can be prevented by leaving the audio device attached.
This guide might help
https://learn.adafruit.com/adafruit-i2s-stereo-decoder-uda1334a/raspberry-pi-usage

Thanks for your hint, but this breaks raspotify on Rasbian. I have no more sound on hdmi. Or is this workarount for the headphone jack?

Update: If you're just interested in removing the clicking noise between songs, it is sufficient to play a silent audio in the background so that the sound receiver doesn't detach.
aplay -D default -t raw -r 44100 -c 2 -f S16_LE /dev/zero

<!-- gh-comment-id:562282915 --> @AInteriorB commented on GitHub (Dec 5, 2019): > the click can be prevented by leaving the audio device attached. > This guide might help > https://learn.adafruit.com/adafruit-i2s-stereo-decoder-uda1334a/raspberry-pi-usage Thanks for your hint, but this breaks raspotify on Rasbian. I have no more sound on hdmi. Or is this workarount for the headphone jack? **Update:** If you're just interested in removing the clicking noise between songs, it is sufficient to play a silent audio in the background so that the sound receiver doesn't detach. `aplay -D default -t raw -r 44100 -c 2 -f S16_LE /dev/zero`
Author
Owner

@bendarlog commented on GitHub (Dec 12, 2019):

Thanks for the help but unfortunately this doesn’t start if raspotify is playing
aplay: main:788: audio open error: Device or resource busy

<!-- gh-comment-id:565151725 --> @bendarlog commented on GitHub (Dec 12, 2019): Thanks for the help but unfortunately this doesn’t start if raspotify is playing `aplay: main:788: audio open error: Device or resource busy`
Author
Owner

@AInteriorB commented on GitHub (Dec 12, 2019):

Thanks for the help but unfortunately this doesn’t start if raspotify is playing
aplay: main:788: audio open error: Device or resource busy

Start it as a service before you start playing your music:
https://github.com/dtcooper/raspotify/issues/231

<!-- gh-comment-id:565160896 --> @AInteriorB commented on GitHub (Dec 12, 2019): > Thanks for the help but unfortunately this doesn’t start if raspotify is playing > `aplay: main:788: audio open error: Device or resource busy` Start it as a service before you start playing your music: https://github.com/dtcooper/raspotify/issues/231
Author
Owner

@paolo1983 commented on GitHub (Feb 22, 2020):

Is there a news? I tried LMS + squezelite: with the plugin called "Spotty" there isn't the gap issue. But I read that "spotty" use librespot... So, can you get a look?

<!-- gh-comment-id:589935619 --> @paolo1983 commented on GitHub (Feb 22, 2020): Is there a news? I tried LMS + squezelite: with the plugin called "Spotty" there isn't the gap issue. But I read that "spotty" use librespot... So, can you get a look?
Author
Owner

@kaymes commented on GitHub (Feb 27, 2020):

Gapless playback has just been merged into dev (PR #430).
I think this issue can be closed now.

<!-- gh-comment-id:591781242 --> @kaymes commented on GitHub (Feb 27, 2020): Gapless playback has just been merged into dev (PR #430). I think this issue can be closed now.
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/librespot#12
No description provided.