make playbin authoritative on the current play queue position
This commit is contained in:
parent
de5c1179fa
commit
7f7a84d747
3 changed files with 61 additions and 62 deletions
|
@ -15,51 +15,35 @@ internal class Audrey.PlayQueue : GLib.Object, GLib.ListModel, Gtk.SelectionMode
|
|||
this.inner.items_changed.connect (this.on_inner_items_changed);
|
||||
}
|
||||
|
||||
// only called by playbin on track transition
|
||||
// only called by playbin
|
||||
// does not emit user_selected
|
||||
internal void playbin_advance (uint new_position)
|
||||
requires (this.current_position < this.get_n_items ())
|
||||
requires (this.current_position+1 == new_position)
|
||||
ensures (this.current_position == new_position)
|
||||
{
|
||||
this.current_position += 1;
|
||||
if (this.current_position < this.inner.get_n_items ()) {
|
||||
// previous track unselected, this newly selected
|
||||
this.selection_changed (this.current_position-1, 2);
|
||||
internal void playbin_select (uint new_position) {
|
||||
var previous_position = this.current_position;
|
||||
this.current_position = new_position;
|
||||
|
||||
if (previous_position < this.inner.get_n_items ()) {
|
||||
if (new_position < this.inner.get_n_items ()) {
|
||||
if (previous_position < new_position) {
|
||||
this.selection_changed (previous_position, new_position-previous_position+1);
|
||||
} else {
|
||||
this.selection_changed (new_position, previous_position-new_position+1);
|
||||
}
|
||||
} else {
|
||||
this.selection_changed (previous_position, 1);
|
||||
}
|
||||
} else {
|
||||
// end of play queue reached
|
||||
// only previous track unselected
|
||||
this.selection_changed (this.current_position-1, 1);
|
||||
if (new_position < this.inner.get_n_items ()) {
|
||||
this.selection_changed (new_position, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// called by anything else that wishes to switch tracks
|
||||
// emits user_selected
|
||||
public void user_select (uint position)
|
||||
requires (position <= this.get_n_items ())
|
||||
requires (position < this.get_n_items ())
|
||||
{
|
||||
var previous_position = this.current_position;
|
||||
this.current_position = position;
|
||||
this.user_selected (position);
|
||||
|
||||
if (previous_position == this.get_n_items ()) {
|
||||
// nothing selected before
|
||||
if (position == this.get_n_items ()) {
|
||||
// ...nothing selected after
|
||||
} else {
|
||||
// select new
|
||||
this.selection_changed (position, 1);
|
||||
}
|
||||
} else {
|
||||
if (position == this.get_n_items ()) {
|
||||
// unselect previous
|
||||
this.selection_changed (previous_position, 1);
|
||||
} else if (position < previous_position) {
|
||||
this.selection_changed (position, previous_position-position+1);
|
||||
} else {
|
||||
this.selection_changed (previous_position, position-previous_position+1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void on_inner_items_changed (GLib.ListModel inner, uint position, uint removed, uint added) {
|
||||
|
|
|
@ -25,18 +25,21 @@ class Playbin : GLib.Object {
|
|||
|
||||
public PlaybinState state { get; private set; default = PlaybinState.STOPPED; }
|
||||
|
||||
// true if a timer should update the postion property
|
||||
// true if a timer should update the position property
|
||||
private bool update_position = false;
|
||||
public int64 position { get; private set; default = 0; }
|
||||
|
||||
public Subsonic api { get; set; default = null; }
|
||||
|
||||
// sent when a new song starts playing
|
||||
// continues: whether the track is a gapless continuation
|
||||
public signal void now_playing (bool continues, uint index, Song song, int64 duration);
|
||||
|
||||
// FIXME this should be synced with the selection model, right??
|
||||
public uint playing_index { get; private set; }
|
||||
// the index of the track in the play queue that is currently playing
|
||||
// must equal play queue len iff state is STOPPED
|
||||
public uint current_position { get; private set; }
|
||||
|
||||
// whether we are expecting a gapless continuation next
|
||||
private bool next_gapless;
|
||||
|
||||
private void source_setup (Gst.Element playbin, dynamic Gst.Element source) {
|
||||
|
@ -45,7 +48,7 @@ class Playbin : GLib.Object {
|
|||
|
||||
// ASSUMPTION: about-to-finish will be signalled exactly once per track
|
||||
// even if seeking backwards after
|
||||
GLib.AsyncQueue<string> next_uri = new GLib.AsyncQueue<string> ();
|
||||
private GLib.AsyncQueue<string> next_uri = new GLib.AsyncQueue<string> ();
|
||||
|
||||
private ListModel _play_queue = null;
|
||||
private ulong _play_queue_items_changed;
|
||||
|
@ -79,27 +82,30 @@ class Playbin : GLib.Object {
|
|||
return;
|
||||
}
|
||||
|
||||
if (this.playing_index >= position) {
|
||||
if (this.playing_index < position+removed) {
|
||||
if (this.current_position >= position) {
|
||||
if (this.current_position < position+removed) {
|
||||
// current track was removed, start playing something else
|
||||
// TODO check if it was actually reordered
|
||||
|
||||
this.begin_playback (position);
|
||||
if (position == play_queue.get_n_items ()) {
|
||||
this.stop ();
|
||||
} else {
|
||||
this.select_track (position);
|
||||
}
|
||||
} else {
|
||||
// unaffected
|
||||
// fix up playing index though
|
||||
this.playing_index += added;
|
||||
this.playing_index -= removed;
|
||||
this.current_position = this.current_position + added - removed;
|
||||
}
|
||||
} else if (this.playing_index+1 == position) {
|
||||
} else if (this.current_position+1 == position) {
|
||||
// next track was changed
|
||||
// try to fix up gapless transition
|
||||
string? next_uri = this.next_uri.try_pop ();
|
||||
if (next_uri != null) {
|
||||
// we're in luck, about-to-finish hasn't been triggered yet
|
||||
// we can get away with replacing it
|
||||
if (this.playing_index+1 < play_queue.get_n_items ()) {
|
||||
Song song = (Song) play_queue.get_item (this.playing_index+1);
|
||||
if (this.current_position+1 < play_queue.get_n_items ()) {
|
||||
Song song = (Song) play_queue.get_item (this.current_position+1);
|
||||
this.next_uri.push (this.api.stream_uri (song.id));
|
||||
} else {
|
||||
this.next_uri.push ("");
|
||||
|
@ -165,17 +171,17 @@ class Playbin : GLib.Object {
|
|||
bool continues = this.next_gapless;
|
||||
if (this.next_gapless) {
|
||||
// advance position in play queue
|
||||
this.playing_index += 1;
|
||||
this.current_position += 1;
|
||||
} else {
|
||||
this.next_gapless = true;
|
||||
}
|
||||
|
||||
var now_playing = (Song) play_queue.get_item (this.playing_index);
|
||||
var now_playing = (Song) play_queue.get_item (this.current_position);
|
||||
if (this.api.stream_uri (now_playing.id) == (string) this.playbin.current_uri) {
|
||||
this.now_playing (continues, this.playing_index, now_playing, duration);
|
||||
this.now_playing (continues, this.current_position, now_playing, duration);
|
||||
|
||||
if (this.playing_index+1 < play_queue.get_n_items ()) {
|
||||
Song song = (Song) play_queue.get_item (this.playing_index+1);
|
||||
if (this.current_position+1 < play_queue.get_n_items ()) {
|
||||
Song song = (Song) play_queue.get_item (this.current_position+1);
|
||||
this.next_uri.push (this.api.stream_uri (song.id));
|
||||
} else {
|
||||
this.next_uri.push ("");
|
||||
|
@ -183,7 +189,7 @@ class Playbin : GLib.Object {
|
|||
} else {
|
||||
// edge case
|
||||
// just flush everything and pray next stream-start is fine
|
||||
this.begin_playback (this.playing_index);
|
||||
this.select_track (this.current_position);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -207,10 +213,13 @@ class Playbin : GLib.Object {
|
|||
}
|
||||
}
|
||||
|
||||
public void begin_playback (uint position) {
|
||||
// manually changes which track in the play queue to play
|
||||
public void select_track (uint position)
|
||||
requires (position < this.play_queue.get_n_items ())
|
||||
{
|
||||
this.state = PlaybinState.PLAYING;
|
||||
|
||||
this.playing_index = position;
|
||||
this.current_position = position;
|
||||
this.playbin.set_state (Gst.State.READY);
|
||||
this.playbin.uri = this.api.stream_uri (((Song) this.play_queue.get_item (position)).id);
|
||||
this.playbin.set_state (Gst.State.PLAYING);
|
||||
|
@ -234,4 +243,10 @@ class Playbin : GLib.Object {
|
|||
this.playbin.set_state (Gst.State.PLAYING);
|
||||
this.state = PlaybinState.PLAYING;
|
||||
}
|
||||
|
||||
public void stop () {
|
||||
this.playbin.set_state (Gst.State.READY);
|
||||
this.state = PlaybinState.STOPPED;
|
||||
this.current_position = this.play_queue.get_n_items ();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -65,13 +65,11 @@ class Ui.Window : Adw.ApplicationWindow {
|
|||
this.song = song;
|
||||
this.duration = duration;
|
||||
api.scrobble.begin (song.id);
|
||||
if (continues) {
|
||||
this.play_queue_model.playbin_advance (position);
|
||||
}
|
||||
this.play_queue_model.playbin_select (position);
|
||||
});
|
||||
|
||||
this.play_queue_model.user_selected.connect ((position) => {
|
||||
this.playbin.begin_playback (position);
|
||||
this.playbin.select_track (position);
|
||||
});
|
||||
|
||||
public_api = api;
|
||||
|
@ -191,12 +189,14 @@ class Ui.Window : Adw.ApplicationWindow {
|
|||
}
|
||||
|
||||
[GtkCallback] private void on_skip_forward_clicked () {
|
||||
this.play_queue_model.user_select (this.playbin.playing_index+1);
|
||||
if (this.playbin.current_position+1 < this.playbin.play_queue.get_n_items ()) {
|
||||
this.playbin.select_track (this.playbin.current_position+1);
|
||||
}
|
||||
}
|
||||
|
||||
[GtkCallback] private void on_skip_backward_clicked () {
|
||||
if (this.playbin.playing_index > 0) {
|
||||
this.play_queue_model.user_select (this.playbin.playing_index-1);
|
||||
if (this.playbin.current_position > 0) {
|
||||
this.play_queue_model.user_select (this.playbin.current_position-1);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue