shuffle gapless playback

This commit is contained in:
Erica Z 2024-10-12 12:57:37 +00:00
parent c1e023276d
commit 216be6798e
4 changed files with 37 additions and 13 deletions

View file

@ -11,12 +11,14 @@ template $WaveletPlayQueue: Adw.NavigationPage {
ScrolledWindow {
ListView list_view {
single-click-activate: true;
show-separators: true;
activate => $on_song_activate ();
factory: BuilderListItemFactory {
template ListItem {
child: Label {
styles [ "bold" ]
halign: start;
label: bind template.item as <$WaveletSong>.title;
};

View file

@ -23,7 +23,9 @@ public class Wavelet.PlayQueue : Adw.NavigationPage {
[GtkChild] private unowned Gtk.ListView list_view;
private ListStore songs;
private uint next_song;
// this is the index of the song that will play on next on_stream_start
private uint next_stream_index;
public signal void play_now (Song song);
public signal void now_playing (Song song);
@ -31,7 +33,7 @@ public class Wavelet.PlayQueue : Adw.NavigationPage {
construct {
this.songs = new ListStore (typeof (Song));
this.next_song = 0;
this.next_stream_index = 0;
this.list_view.model = new Gtk.NoSelection (this.songs);
}
@ -41,22 +43,34 @@ public class Wavelet.PlayQueue : Adw.NavigationPage {
}
public void queue (Song song) {
uint new_index = this.songs.get_n_items ();
this.songs.append (song);
if (new_index == next_stream_index) {
this.play_next (song);
}
}
[GtkCallback] private void on_song_activate (uint position) {
this.next_song = position;
this.next_stream_index = position;
Song song = (Song) this.songs.get_item (position);
this.play_next (song);
this.play_now (song);
}
internal void on_stream_start (Playbin playbin) {
Song song = (Song) this.songs.get_item (this.next_song);
Song song = (Song) this.songs.get_item (this.next_stream_index);
this.now_playing (song);
// prepare for next song gapless
this.next_song += 1;
Song? next_song = (Song?) this.songs.get_item (this.next_song);
// prepare for next song ahead of time (gapless)
this.next_stream_index += 1;
Song? next_song = (Song?) this.songs.get_item (this.next_stream_index);
this.play_next (next_song);
}
internal void restart () {
this.next_stream_index = 0;
Song song = (Song) this.songs.get_item (0);
this.play_next (song);
this.play_now (song);
}
}

View file

@ -44,6 +44,7 @@ class Playbin : Object {
public int64 duration { get; private set; }
public signal void stream_started ();
public signal void stream_over ();
construct {
this.playbin = Gst.ElementFactory.make ("playbin3", null);
@ -81,8 +82,6 @@ class Playbin : Object {
}
if (Gst.MessageType.STREAM_START in message.type) {
print ("stream start\n");
int64 new_duration;
assert (this.playbin.query_duration (Gst.Format.TIME, out new_duration));
this.duration = new_duration;
@ -95,7 +94,16 @@ class Playbin : Object {
}
if (Gst.MessageType.EOS in message.type) {
print ("eos\n");
string next_uri;
this.next_uri_lock.lock ();
next_uri = this.next_uri;
this.next_uri_lock.unlock ();
if (next_uri == null) {
// no next track was arranged, we're done
this.stream_over ();
}
}
return true;
@ -181,8 +189,6 @@ class Playbin : Object {
// called when uri can be switched for gapless playback
// need async queue because this might be called from a gstreamer thread
private void on_about_to_finish (dynamic Gst.Element playbin) {
print("about to finish\n");
this.next_uri_lock.lock ();
string? next_uri = this.next_uri;
this.next_uri_lock.unlock ();

View file

@ -41,6 +41,7 @@ public class Wavelet.Window : Adw.ApplicationWindow {
private Cancellable cancel_loading_art;
public bool cover_art_loading { get; set; default = false; }
public Gdk.Paintable playing_cover_art { get; set; }
private Gdk.Paintable next_cover_art;
internal Playbin playbin { get; default = new Playbin (); }
@ -65,13 +66,14 @@ public class Wavelet.Window : Adw.ApplicationWindow {
error ("could not get random songs: %s", e.message);
}
this.shuffle_all_tracks.sensitive = true;
this.play_queue.restart ();
});
});
playbin.stream_started.connect (this.play_queue.on_stream_start);
this.play_queue.now_playing.connect ((song) => {
print ("now playing %s\n", song.title);
this.song = song;
});