shuffle gapless playback
This commit is contained in:
parent
c2ad6c425e
commit
85e5c33aec
4 changed files with 37 additions and 13 deletions
|
@ -11,12 +11,14 @@ template $WaveletPlayQueue: Adw.NavigationPage {
|
||||||
ScrolledWindow {
|
ScrolledWindow {
|
||||||
ListView list_view {
|
ListView list_view {
|
||||||
single-click-activate: true;
|
single-click-activate: true;
|
||||||
|
show-separators: true;
|
||||||
|
|
||||||
activate => $on_song_activate ();
|
activate => $on_song_activate ();
|
||||||
|
|
||||||
factory: BuilderListItemFactory {
|
factory: BuilderListItemFactory {
|
||||||
template ListItem {
|
template ListItem {
|
||||||
child: Label {
|
child: Label {
|
||||||
|
styles [ "bold" ]
|
||||||
halign: start;
|
halign: start;
|
||||||
label: bind template.item as <$WaveletSong>.title;
|
label: bind template.item as <$WaveletSong>.title;
|
||||||
};
|
};
|
||||||
|
|
|
@ -23,7 +23,9 @@ public class Wavelet.PlayQueue : Adw.NavigationPage {
|
||||||
[GtkChild] private unowned Gtk.ListView list_view;
|
[GtkChild] private unowned Gtk.ListView list_view;
|
||||||
|
|
||||||
private ListStore songs;
|
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 play_now (Song song);
|
||||||
public signal void now_playing (Song song);
|
public signal void now_playing (Song song);
|
||||||
|
@ -31,7 +33,7 @@ public class Wavelet.PlayQueue : Adw.NavigationPage {
|
||||||
|
|
||||||
construct {
|
construct {
|
||||||
this.songs = new ListStore (typeof (Song));
|
this.songs = new ListStore (typeof (Song));
|
||||||
this.next_song = 0;
|
this.next_stream_index = 0;
|
||||||
|
|
||||||
this.list_view.model = new Gtk.NoSelection (this.songs);
|
this.list_view.model = new Gtk.NoSelection (this.songs);
|
||||||
}
|
}
|
||||||
|
@ -41,22 +43,34 @@ public class Wavelet.PlayQueue : Adw.NavigationPage {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void queue (Song song) {
|
public void queue (Song song) {
|
||||||
|
uint new_index = this.songs.get_n_items ();
|
||||||
this.songs.append (song);
|
this.songs.append (song);
|
||||||
|
if (new_index == next_stream_index) {
|
||||||
|
this.play_next (song);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[GtkCallback] private void on_song_activate (uint position) {
|
[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);
|
Song song = (Song) this.songs.get_item (position);
|
||||||
|
this.play_next (song);
|
||||||
this.play_now (song);
|
this.play_now (song);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void on_stream_start (Playbin playbin) {
|
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);
|
this.now_playing (song);
|
||||||
|
|
||||||
// prepare for next song gapless
|
// prepare for next song ahead of time (gapless)
|
||||||
this.next_song += 1;
|
this.next_stream_index += 1;
|
||||||
Song? next_song = (Song?) this.songs.get_item (this.next_song);
|
Song? next_song = (Song?) this.songs.get_item (this.next_stream_index);
|
||||||
this.play_next (next_song);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,7 @@ class Playbin : Object {
|
||||||
public int64 duration { get; private set; }
|
public int64 duration { get; private set; }
|
||||||
|
|
||||||
public signal void stream_started ();
|
public signal void stream_started ();
|
||||||
|
public signal void stream_over ();
|
||||||
|
|
||||||
construct {
|
construct {
|
||||||
this.playbin = Gst.ElementFactory.make ("playbin3", null);
|
this.playbin = Gst.ElementFactory.make ("playbin3", null);
|
||||||
|
@ -81,8 +82,6 @@ class Playbin : Object {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Gst.MessageType.STREAM_START in message.type) {
|
if (Gst.MessageType.STREAM_START in message.type) {
|
||||||
print ("stream start\n");
|
|
||||||
|
|
||||||
int64 new_duration;
|
int64 new_duration;
|
||||||
assert (this.playbin.query_duration (Gst.Format.TIME, out new_duration));
|
assert (this.playbin.query_duration (Gst.Format.TIME, out new_duration));
|
||||||
this.duration = new_duration;
|
this.duration = new_duration;
|
||||||
|
@ -95,7 +94,16 @@ class Playbin : Object {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Gst.MessageType.EOS in message.type) {
|
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;
|
return true;
|
||||||
|
@ -181,8 +189,6 @@ class Playbin : Object {
|
||||||
// called when uri can be switched for gapless playback
|
// called when uri can be switched for gapless playback
|
||||||
// need async queue because this might be called from a gstreamer thread
|
// need async queue because this might be called from a gstreamer thread
|
||||||
private void on_about_to_finish (dynamic Gst.Element playbin) {
|
private void on_about_to_finish (dynamic Gst.Element playbin) {
|
||||||
print("about to finish\n");
|
|
||||||
|
|
||||||
this.next_uri_lock.lock ();
|
this.next_uri_lock.lock ();
|
||||||
string? next_uri = this.next_uri;
|
string? next_uri = this.next_uri;
|
||||||
this.next_uri_lock.unlock ();
|
this.next_uri_lock.unlock ();
|
||||||
|
|
|
@ -41,6 +41,7 @@ public class Wavelet.Window : Adw.ApplicationWindow {
|
||||||
private Cancellable cancel_loading_art;
|
private Cancellable cancel_loading_art;
|
||||||
public bool cover_art_loading { get; set; default = false; }
|
public bool cover_art_loading { get; set; default = false; }
|
||||||
public Gdk.Paintable playing_cover_art { get; set; }
|
public Gdk.Paintable playing_cover_art { get; set; }
|
||||||
|
private Gdk.Paintable next_cover_art;
|
||||||
|
|
||||||
internal Playbin playbin { get; default = new Playbin (); }
|
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);
|
error ("could not get random songs: %s", e.message);
|
||||||
}
|
}
|
||||||
this.shuffle_all_tracks.sensitive = true;
|
this.shuffle_all_tracks.sensitive = true;
|
||||||
|
|
||||||
|
this.play_queue.restart ();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
playbin.stream_started.connect (this.play_queue.on_stream_start);
|
playbin.stream_started.connect (this.play_queue.on_stream_start);
|
||||||
|
|
||||||
this.play_queue.now_playing.connect ((song) => {
|
this.play_queue.now_playing.connect ((song) => {
|
||||||
print ("now playing %s\n", song.title);
|
|
||||||
this.song = song;
|
this.song = song;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue