Merge lp:~victored/noise/gapless-playback into lp:~elementary-apps/noise/trunk

Proposed by Victor Martinez
Status: Work in progress
Proposed branch: lp:~victored/noise/gapless-playback
Merge into: lp:~elementary-apps/noise/trunk
Diff against target: 1754 lines (+539/-521)
16 files modified
core/GStreamer/Equalizer.vala (+2/-2)
core/GStreamer/Pipeline.vala (+129/-60)
core/GStreamer/Playback.vala (+8/-11)
core/LibraryWindowInterface.vala (+1/-1)
core/Media.vala (+3/-0)
core/Player.vala (+27/-3)
core/Settings.vala (+2/-2)
src/GStreamer/Streamer.vala (+59/-144)
src/LibraryWindow.vala (+119/-210)
src/LocalBackend/LocalLibrary.vala (+4/-4)
src/Objects/MediaKeyListener.vala (+1/-1)
src/PlaybackManager.vala (+117/-45)
src/Views/ListView/Lists/GenericList.vala (+13/-3)
src/Widgets/EqualizerPopover.vala (+2/-2)
src/Widgets/InfoPanel.vala (+19/-7)
src/Widgets/TopDisplay.vala (+33/-26)
To merge this branch: bzr merge lp:~victored/noise/gapless-playback
Reviewer Review Type Date Requested Status
elementary Apps team Pending
Review via email: mp+257485@code.launchpad.net

Description of the change

- Gapless playback implementation will be added soon.
- Required infrastructure changes are being performed at this moment.
- Proposed just to get a diff. Feel free to provide feedback but keep in mind code still needs cleanup.

== BUILD

Configure with 'cmake .. -DBUILD_PLUGINS=OFF' since plugins haven't been updated yet.

== REGRESSIONS

- Fix automatic scrolling to current media and properly refresh list view [GenericList]
- Automatically switch to next media after the previous one ends. This chaining and playback control was part of LibraryWindow. Move it to PlaybackManager and integrate gapless playback.

To post a comment you must log in.
lp:~victored/noise/gapless-playback updated
1801. By Victor Martinez

Cleanup & notify when media changes

1802. By Victor Martinez

reset timeout ID properly after removing source func

1803. By Victor Martinez

correct a few runtime warnings

1804. By Victor Martinez

Move some functions to Noise.Pipeline to avoid duplicating code in plugins

1805. By Victor Martinez

Code cleanup

Revision history for this message
Victor Martinez (victored) wrote :

Gapless playback is kinda broken for MP3/AAC on playbin for GStreamer 1.0. Might want to look into workarounds if we want this feature to ever land any time soon.

Unmerged revisions

1805. By Victor Martinez

Code cleanup

1804. By Victor Martinez

Move some functions to Noise.Pipeline to avoid duplicating code in plugins

1803. By Victor Martinez

correct a few runtime warnings

1802. By Victor Martinez

reset timeout ID properly after removing source func

1801. By Victor Martinez

Cleanup & notify when media changes

1800. By Victor Martinez

Playback: move playback logic and control away from LibraryWindow.vala.

Configure with 'cmake .. -DBUILD_PLUGINS=OFF' since plugins haven't been updated yet.

1799. By Victor Martinez

Playback: make sure no playback logic code happens on LibraryWindow.

Make wider use of PlaybackManager API. LibraryWindow will be another client of playback manager and will update its widgets accordingly as the playback class notifies status changes have occured.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'core/GStreamer/Equalizer.vala'
2--- core/GStreamer/Equalizer.vala 2015-02-26 16:44:26 +0000
3+++ core/GStreamer/Equalizer.vala 2015-04-27 05:07:36 +0000
4@@ -62,12 +62,12 @@
5 band.set("gain", gain);
6 }
7
8- private static Gee.TreeSet<EqualizerPreset> ? default_presets = null;
9+ private static Gee.ArrayList<EqualizerPreset> ? default_presets = null;
10 public static Gee.Collection<EqualizerPreset> get_default_presets () {
11 if (default_presets != null)
12 return default_presets;
13
14- default_presets = new Gee.TreeSet<EqualizerPreset> ();
15+ default_presets = new Gee.ArrayList<EqualizerPreset> ();
16
17 default_presets.add (new EqualizerPreset.with_gains (_("Flat"), {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}));
18 default_presets.add (new EqualizerPreset.with_gains (_("Classical"), {0, 0, 0, 0, 0, 0, -40, -40, -40, -50}));
19
20=== modified file 'core/GStreamer/Pipeline.vala'
21--- core/GStreamer/Pipeline.vala 2013-11-15 15:54:19 +0000
22+++ core/GStreamer/Pipeline.vala 2015-04-27 05:07:36 +0000
23@@ -28,78 +28,147 @@
24 * Authored by: Scott Ringwelski <sgringwe@mtu.edu>
25 */
26
27-public class Noise.Pipeline : GLib.Object {
28- public Gst.Pipeline pipe;
29- public Equalizer eq;
30-
31- public dynamic Gst.Bus bus;
32- public Gst.Pad pad;
33-
34- public dynamic Gst.Element audiosink;
35- public dynamic Gst.Element audiosinkqueue;
36- public dynamic Gst.Element eq_audioconvert;
37- public dynamic Gst.Element eq_audioconvert2;
38-
39- public dynamic Gst.Element playbin;
40- public dynamic Gst.Element audiotee;
41- public dynamic Gst.Element audiobin;
42- public dynamic Gst.Element preamp;
43-
44+public abstract class Noise.Pipeline : Object, PlaybackBackend {
45+ private Gst.Pipeline pipe;
46+ private Equalizer eq;
47+ private Gst.Pad pad;
48+
49+ private dynamic Gst.Element playbin;
50+ private dynamic Gst.Element audiosink;
51+ private dynamic Gst.Element audiosinkqueue;
52+ private dynamic Gst.Element eq_audioconvert;
53+ private dynamic Gst.Element eq_audioconvert2;
54+ private dynamic Gst.Element audiotee;
55+ private dynamic Gst.Element audiobin;
56+ private dynamic Gst.Element preamp;
57+
58 public Pipeline() {
59-
60- pipe = new Gst.Pipeline("pipeline");
61+ pipe = new Gst.Pipeline ("pipeline");
62 playbin = Gst.ElementFactory.make ("playbin", "play");
63-
64- audiosink = Gst.ElementFactory.make("autoaudiosink", "audio-sink");
65-
66- audiobin = new Gst.Bin("audiobin"); // this holds the real primary sink
67-
68- audiotee = Gst.ElementFactory.make("tee", null);
69- audiosinkqueue = Gst.ElementFactory.make("queue", null);
70-
71- eq = new Equalizer();
72- if(eq.element != null) {
73- eq_audioconvert = Gst.ElementFactory.make("audioconvert", null);
74- eq_audioconvert2 = Gst.ElementFactory.make("audioconvert", null);
75- preamp = Gst.ElementFactory.make("volume", "preamp");
76-
77- ((Gst.Bin)audiobin).add_many(eq.element, eq_audioconvert, eq_audioconvert2, preamp);
78+ audiosink = Gst.ElementFactory.make ("autoaudiosink", "audio-sink");
79+ audiobin = new Gst.Bin ("audiobin"); // this holds the real primary sink
80+ audiotee = Gst.ElementFactory.make ("tee", null);
81+ audiosinkqueue = Gst.ElementFactory.make ("queue", null);
82+ eq = new Equalizer ();
83+
84+ if (eq.element != null) {
85+ eq_audioconvert = Gst.ElementFactory.make ("audioconvert", null);
86+ eq_audioconvert2 = Gst.ElementFactory.make ("audioconvert", null);
87+ preamp = Gst.ElementFactory.make ("volume", "preamp");
88+
89+ ((Gst.Bin) audiobin).add_many (eq.element, eq_audioconvert, eq_audioconvert2, preamp);
90 }
91-
92- ((Gst.Bin)audiobin).add_many(audiotee, audiosinkqueue, audiosink);
93+
94+ ((Gst.Bin) audiobin).add_many (audiotee, audiosinkqueue, audiosink);
95
96 audiobin.add_pad (new Gst.GhostPad ("sink", audiotee.get_static_pad ("sink")));
97
98 if (eq.element != null)
99- audiosinkqueue.link_many(eq_audioconvert, preamp, eq.element, eq_audioconvert2, audiosink);
100+ audiosinkqueue.link_many (eq_audioconvert, preamp, eq.element, eq_audioconvert2, audiosink);
101 else
102- audiosinkqueue.link_many(audiosink); // link the queue with the real audio sink
103+ audiosinkqueue.link_many (audiosink); // link the queue with the real audio sink
104
105- playbin.set("audio-sink", audiobin);
106- bus = playbin.get_bus();
107+ playbin.set ("audio-sink", audiobin);
108
109 // Link the first tee pad to the primary audio sink queue
110 Gst.Pad sinkpad = audiosinkqueue.get_static_pad ("sink");
111 pad = audiotee.get_request_pad ("src_%u");
112- audiotee.set("alloc-pad", pad);
113- pad.link(sinkpad);
114- }
115-
116- public void enableEqualizer() {
117- if (eq.element != null) {
118- audiosinkqueue.unlink (audiosink); // link the queue with the real audio sink
119- audiosinkqueue.link_many(eq_audioconvert, preamp, eq.element, eq_audioconvert2, audiosink);
120- }
121- }
122-
123- public void disableEqualizer() {
124- if (eq.element != null) {
125- audiosinkqueue.unlink (eq_audioconvert);
126- audiosinkqueue.unlink (preamp);
127- audiosinkqueue.unlink (eq.element);
128- audiosinkqueue.unlink (eq_audioconvert2);
129- audiosinkqueue.unlink (audiosink);
130- audiosinkqueue.link_many(audiosink); // link the queue with the real audio sink
131- }
132+ audiotee.set ("alloc-pad", pad);
133+ pad.link (sinkpad);
134+
135+ playbin.get_bus ().add_watch (Priority.HIGH, bus_callback);
136+ playbin.about_to_finish.connect (on_about_to_finish);
137+ }
138+
139+ protected abstract bool bus_callback (Gst.Bus bus, Gst.Message message);
140+
141+ private void on_about_to_finish () {
142+ // propagate signal in a MT-safe way
143+ var message = new Gst.Message.application (playbin,
144+ new Gst.Structure.empty ("about-to-finish"));
145+ playbin.post_message (message);
146+ }
147+
148+ public void seek_simple (int64 pos) {
149+ playbin.seek_simple (Gst.Format.TIME, Gst.SeekFlags.FLUSH, pos);
150+ }
151+
152+ public string? get_current_uri () {
153+ return playbin.current_uri;
154+ }
155+
156+ public void set_uri (string uri) {
157+ message ("Playbin new URI: %s", uri);
158+ playbin.uri = uri;
159+ }
160+
161+ public void play () {
162+ set_state (Gst.State.PLAYING);
163+ }
164+
165+ public void pause () {
166+ set_state (Gst.State.PAUSED);
167+ }
168+
169+ private void set_state (Gst.State s) {
170+ playbin.set_state (s);
171+ }
172+
173+ public void set_position (int64 pos) {
174+ playbin.seek (1.0, Gst.Format.TIME, Gst.SeekFlags.FLUSH, Gst.SeekType.SET,
175+ pos, Gst.SeekType.NONE, get_duration ());
176+ }
177+
178+ public int64 get_position () {
179+ int64 rv = (int64) 0;
180+ Gst.Format f = Gst.Format.TIME;
181+
182+ playbin.query_position (f, out rv);
183+
184+ return rv;
185+ }
186+
187+ public int64 get_duration () {
188+ int64 rv = (int64)0;
189+ Gst.Format f = Gst.Format.TIME;
190+
191+ playbin.query_duration (f, out rv);
192+
193+ return rv;
194+ }
195+
196+ public void enable_equalizer () {
197+ if (eq.element == null)
198+ return;
199+
200+ // link the queue with the real audio sink
201+ audiosinkqueue.unlink (audiosink);
202+ audiosinkqueue.link_many (eq_audioconvert, preamp, eq.element, eq_audioconvert2, audiosink);
203+ }
204+
205+ public void set_equalizer_gain (int index, int val) {
206+ eq.setGain (index, val);
207+ }
208+
209+ public void disable_equalizer () {
210+ if (eq.element == null)
211+ return;
212+
213+ audiosinkqueue.unlink (eq_audioconvert);
214+ audiosinkqueue.unlink (preamp);
215+ audiosinkqueue.unlink (eq.element);
216+ audiosinkqueue.unlink (eq_audioconvert2);
217+ audiosinkqueue.unlink (audiosink);
218+
219+ // link the queue with the real audio sink
220+ audiosinkqueue.link_many (audiosink);
221+ }
222+
223+ public void set_volume (double val) {
224+ playbin.volume = val;
225+ }
226+
227+ public double get_volume () {
228+ return playbin.volume;
229 }
230 }
231
232=== modified file 'core/GStreamer/Playback.vala'
233--- core/GStreamer/Playback.vala 2013-10-11 11:58:03 +0000
234+++ core/GStreamer/Playback.vala 2015-04-27 05:07:36 +0000
235@@ -1,6 +1,6 @@
236 // -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*-
237 /*-
238- * Copyright (c) 2012-2013 Noise Developers (http://launchpad.net/noise)
239+ * Copyright (c) 2012-2015 Noise Developers (http://launchpad.net/noise)
240 *
241 * This library is free software; you can redistribute it and/or
242 * modify it under the terms of the GNU Library General Public
243@@ -28,27 +28,27 @@
244 * Authored by: Corentin Noël <tintou@mailoo.org>
245 */
246
247-public interface Noise.Playback : GLib.Object {
248-
249+public interface Noise.Playback : Noise.PlaybackBackend {
250 public abstract Gee.Collection<string> get_supported_uri ();
251+ public abstract void set_media (Media media);
252+ public abstract void set_next_media (Media media);
253+}
254
255+public interface Noise.PlaybackBackend : Object {
256 /*
257 * Signals
258 */
259-
260- public signal void end_of_stream ();
261- public signal void current_position_update (int64 position);
262+ public signal void about_to_finish (string uri_about_to_finish);
263+ public signal void end_of_stream (string uri_finished, string? next_uri);
264 public signal void media_not_found ();
265 public signal void error_occured ();
266
267 /*
268 * Basic playback functions
269 */
270-
271 public abstract void play ();
272 public abstract void pause ();
273 public abstract void set_state (Gst.State s);
274- public abstract void set_media (Media media);
275 public abstract void set_position (int64 pos);
276 public abstract int64 get_position ();
277 public abstract int64 get_duration ();
278@@ -58,10 +58,7 @@
279 /*
280 * Extra stuff
281 */
282-
283- public abstract bool update_position ();
284 public abstract void enable_equalizer ();
285 public abstract void disable_equalizer ();
286 public abstract void set_equalizer_gain (int index, int val);
287-
288 }
289
290=== modified file 'core/LibraryWindowInterface.vala'
291--- core/LibraryWindowInterface.vala 2015-02-24 18:16:35 +0000
292+++ core/LibraryWindowInterface.vala 2015-04-27 05:07:36 +0000
293@@ -29,7 +29,7 @@
294 */
295
296 public interface Noise.LibraryWindowInterface : Object {
297- public abstract async void update_sensitivities ();
298+ public abstract void update_sensitivities ();
299 public signal void add_preference_page (Noise.SettingsWindow.NoteBook_Page widget);
300 public signal void source_list_added (GLib.Object o, int view_number);
301
302
303=== modified file 'core/Media.vala'
304--- core/Media.vala 2015-02-24 00:04:03 +0000
305+++ core/Media.vala 2015-04-27 05:07:36 +0000
306@@ -29,6 +29,7 @@
307 * Victor Eduardo <victoreduardm@gmail.com>
308 */
309 public class Noise.Media : Object {
310+ public signal void updated ();
311
312 /// Used for unknown titles, artists, or album names.
313 protected static string UNKNOWN = _("Unknown");
314@@ -190,10 +191,12 @@
315
316 public Media (string uri) {
317 this.uri = uri;
318+ this.notify.connect (() => updated ());
319 }
320
321 public Media.from_file (File file) {
322 this.file = file;
323+ this.notify.connect (() => updated ());
324 }
325
326 public Media copy () {
327
328=== modified file 'core/Player.vala'
329--- core/Player.vala 2013-02-26 21:56:08 +0000
330+++ core/Player.vala 2015-04-27 05:07:36 +0000
331@@ -28,9 +28,33 @@
332 * Authored by: Victor Eduardo <victoreduardm@gmail.com>
333 */
334
335+public enum Noise.PlaybackStatus {
336+ STOPPED,
337+ PAUSED,
338+ PLAYING;
339+
340+ public bool is_playing () {
341+ return this == PLAYING;
342+ }
343+
344+ public bool is_paused () {
345+ return this == PAUSED;
346+ }
347+
348+ public bool is_stopped () {
349+ return this == STOPPED;
350+ }
351+}
352+
353 public interface Noise.Player : Object {
354-
355- public abstract void add_playback (Noise.Playback p);
356+ // XXX should be internal
357+ public signal void current_cleared ();
358+ public signal void queue_cleared ();
359+ public signal void media_queued (Gee.Collection<Media> queued);
360+
361+ public signal void status_change (Media? current_media, PlaybackStatus status);
362+
363+ //public abstract void add_playback (Noise.Playback p);
364
365 public enum Shuffle {
366 OFF,
367@@ -45,5 +69,5 @@
368 ALL
369 }
370
371- // TODO: ...
372+ public abstract void play_pause ();
373 }
374
375=== modified file 'core/Settings.vala'
376--- core/Settings.vala 2015-02-26 16:44:26 +0000
377+++ core/Settings.vala 2015-04-27 05:07:36 +0000
378@@ -114,7 +114,7 @@
379 }
380
381 public Gee.Collection<Noise.EqualizerPreset> getPresets () {
382- var presets_data = new Gee.TreeSet<string> ();
383+ var presets_data = new Gee.ArrayList<string> ();
384
385 if (custom_presets != null) {
386 for (int i = 0; i < custom_presets.length; i++) {
387@@ -122,7 +122,7 @@
388 }
389 }
390
391- var rv = new Gee.TreeSet<Noise.EqualizerPreset>();
392+ var rv = new Gee.ArrayList<Noise.EqualizerPreset>();
393
394 foreach (var preset_str in presets_data) {
395 rv.add (new Noise.EqualizerPreset.from_string (preset_str));
396
397=== modified file 'src/GStreamer/Streamer.vala'
398--- src/GStreamer/Streamer.vala 2015-03-01 16:43:54 +0000
399+++ src/GStreamer/Streamer.vala 2015-04-27 05:07:36 +0000
400@@ -20,30 +20,14 @@
401 * Boston, MA 02111-1307, USA.
402 */
403
404-public class Noise.Streamer : Noise.Playback, GLib.Object {
405- Noise.Pipeline pipe;
406+public class Noise.Streamer : Pipeline, Playback {
407+ private InstallGstreamerPluginsDialog dialog;
408+ private string? last_played_uri;
409+ private string? last_queued_uri;
410
411- InstallGstreamerPluginsDialog dialog;
412-
413- public Gst.Element cdda;
414- public bool set_resume_pos;
415-
416- /* Signals are now in the Playback interface !
417- public signal void end_of_stream ();
418- public signal void current_position_update (int64 position);
419- public signal void media_not_found ();
420- public signal void error_occured (); */
421-
422 public Streamer () {
423- pipe = new Noise.Pipeline();
424-
425- pipe.bus.add_watch (GLib.Priority.DEFAULT, bus_callback);
426- //pipe.playbin.about_to_finish.connect(about_to_finish);
427-
428-
429- Timeout.add (200, update_position);
430 }
431-
432+
433 public Gee.Collection<string> get_supported_uri () {
434 var uris = new Gee.LinkedList<string> ();
435 uris.add ("file://");
436@@ -52,136 +36,67 @@
437 return uris;
438 }
439
440- public bool update_position () {
441- if(set_resume_pos || (App.player.current_media != null && get_position() >= (int64)(App.player.current_media.resume_pos - 1) * 1000000000)) {
442- set_resume_pos = true;
443- current_position_update(get_position());
444- }
445- else if (App.player.current_media != null) {
446- pipe.playbin.seek_simple(Gst.Format.TIME, Gst.SeekFlags.FLUSH, (int64)App.player.current_media.resume_pos * 1000000000);
447- }
448-
449- return true;
450- }
451-
452- /* Basic playback functions */
453- public void play () {
454- set_state (Gst.State.PLAYING);
455- }
456-
457- public void pause () {
458- set_state (Gst.State.PAUSED);
459- }
460-
461- public void set_state (Gst.State s) {
462- pipe.playbin.set_state (s);
463- }
464-
465+ public void set_next_media (Media next) {
466+ assert (last_played_uri != null);
467+ debug ("set_next_media");
468+ last_queued_uri = next.uri;
469+ set_uri (last_queued_uri);
470+ }
471+
472 public void set_media (Media media) {
473 set_state (Gst.State.READY);
474- debug ("set uri to %s\n", media.uri);
475- //pipe.playbin.uri = uri.replace("#", "%23");
476- pipe.playbin.set_property ("uri", media.uri.replace("#", "%23"));
477-
478- set_state (Gst.State.PLAYING);
479-
480- debug ("setURI seeking to %d\n", App.player.current_media.resume_pos);
481- pipe.playbin.seek_simple (Gst.Format.TIME, Gst.SeekFlags.FLUSH, (int64)App.player.current_media.resume_pos * 1000000000);
482-
483+
484+ last_played_uri = media.uri;
485+ last_queued_uri = null;
486+
487+ debug ("set_media: %s", last_played_uri);
488+ set_uri (last_played_uri);
489+
490 play ();
491- }
492-
493- public void set_position (int64 pos) {
494- pipe.playbin.seek (1.0,
495- Gst.Format.TIME, Gst.SeekFlags.FLUSH,
496- Gst.SeekType.SET, pos,
497- Gst.SeekType.NONE, get_duration ());
498- }
499-
500- public int64 get_position () {
501- int64 rv = (int64)0;
502- Gst.Format f = Gst.Format.TIME;
503-
504- pipe.playbin.query_position (f, out rv);
505-
506- return rv;
507- }
508-
509- public int64 get_duration () {
510- int64 rv = (int64)0;
511- Gst.Format f = Gst.Format.TIME;
512-
513- pipe.playbin.query_duration (f, out rv);
514-
515- return rv;
516- }
517-
518- public void set_volume (double val) {
519- pipe.playbin.set_property ("volume", val);
520- }
521-
522- public double get_volume () {
523- var val = GLib.Value (typeof(double));
524- pipe.playbin.get_property ("volume", ref val);
525- return (double)val;
526- }
527-
528- /* Extra stuff */
529- public void enable_equalizer () {
530- pipe.enableEqualizer ();
531- }
532-
533- public void disable_equalizer() {
534- pipe.disableEqualizer ();
535- }
536-
537- public void set_equalizer_gain (int index, int val) {
538- pipe.eq.setGain (index, val);
539+
540+ debug ("set_media seeking to %d", App.player.current_media.resume_pos);
541+ seek_simple ((int64) App.player.current_media.resume_pos * 1000000000);
542 }
543
544 /* Callbacks */
545- private bool bus_callback (Gst.Bus bus, Gst.Message message) {
546+ protected override bool bus_callback (Gst.Bus bus, Gst.Message message) {
547 switch (message.type) {
548- case Gst.MessageType.ERROR:
549- GLib.Error err;
550- string debug;
551- message.parse_error (out err, out debug);
552- warning ("Error: %s\n", err.message);
553- error_occured();
554- break;
555- case Gst.MessageType.ELEMENT:
556- if(message.get_structure() != null && Gst.PbUtils.is_missing_plugin_message(message) && (dialog == null || !dialog.visible)) {
557- dialog = new InstallGstreamerPluginsDialog(message);
558- }
559- break;
560- case Gst.MessageType.EOS:
561- end_of_stream ();
562- break;
563- case Gst.MessageType.STATE_CHANGED:
564- Gst.State oldstate;
565- Gst.State newstate;
566- Gst.State pending;
567- message.parse_state_changed (out oldstate, out newstate,
568- out pending);
569-
570- if(newstate != Gst.State.PLAYING)
571- break;
572-
573-
574- break;
575- case Gst.MessageType.TAG:
576- Gst.TagList tag_list;
577-
578- message.parse_tag (out tag_list);
579- if (tag_list != null) {
580- if (tag_list.get_tag_size (Gst.Tags.TITLE) > 0) {
581- string title = "";
582- tag_list.get_string (Gst.Tags.TITLE, out title);
583- }
584- }
585- break;
586- default:
587- break;
588+ case Gst.MessageType.ERROR:
589+ GLib.Error err;
590+ string debug;
591+ message.parse_error (out err, out debug);
592+ warning ("Error: %s\n", err.message);
593+ error_occured ();
594+ break;
595+
596+ case Gst.MessageType.APPLICATION:
597+ unowned Gst.Structure structure = message.get_structure ();
598+ if (structure != null && structure.get_name () == "about-to-finish")
599+ about_to_finish (last_played_uri);
600+
601+ break;
602+
603+ case Gst.MessageType.ELEMENT:
604+ if (message.get_structure () == null || dialog != null || dialog.visible)
605+ break;
606+
607+ if (Gst.PbUtils.is_missing_plugin_message (message))
608+ dialog = new InstallGstreamerPluginsDialog (message);
609+
610+ break;
611+
612+ case Gst.MessageType.EOS:
613+ end_of_stream (last_played_uri, last_queued_uri);
614+
615+ last_played_uri = last_queued_uri;
616+ last_queued_uri = null;
617+
618+ break;
619+
620+ case Gst.MessageType.TAG:
621+ case Gst.MessageType.STATE_CHANGED:
622+ default:
623+ break;
624 }
625
626 return true;
627
628=== modified file 'src/LibraryWindow.vala'
629--- src/LibraryWindow.vala 2015-03-01 16:43:54 +0000
630+++ src/LibraryWindow.vala 2015-04-27 05:07:36 +0000
631@@ -1,6 +1,6 @@
632 // -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*-
633 /*-
634- * Copyright (c) 2012 Noise Developers (http://launchpad.net/noise)
635+ * Copyright (c) 2012-2015 Noise Developers (http://launchpad.net/noise)
636 *
637 * This library is free software; you can redistribute it and/or
638 * modify it under the terms of the GNU Lesser General Public
639@@ -22,7 +22,6 @@
640 */
641
642 public class Noise.LibraryWindow : LibraryWindowInterface, Gtk.Window {
643- public signal void playPauseChanged ();
644 public signal void close_subwindows ();
645
646 public Noise.LocalLibrary library_manager { get { return (Noise.LocalLibrary)libraries_manager.local_library; } }
647@@ -72,6 +71,7 @@
648 private Settings.Main main_settings;
649
650 private Cancellable notification_cancellable;
651+ private unowned Media current_media;
652
653 PreferencesWindow? preferences = null;
654
655@@ -101,24 +101,8 @@
656 device_manager.device_name_changed.connect (change_device_name);
657 device_manager.device_removed.connect (remove_device);
658
659- App.player.player.end_of_stream.connect (end_of_stream);
660- App.player.player.current_position_update.connect (current_position_update);
661- App.player.player.error_occured.connect (error_occured);
662- App.player.media_played.connect_after (media_played);
663- App.player.playback_stopped.connect (playback_stopped);
664- App.player.playback_started.connect (playback_started);
665- App.player.playback_paused.connect (playback_paused);
666- App.player.changing_player.connect (() => {
667- App.player.player.end_of_stream.disconnect (end_of_stream);
668- App.player.player.current_position_update.disconnect (current_position_update);
669- App.player.player.error_occured.disconnect (error_occured);
670- });
671- App.player.player_changed.connect (() => {
672- App.player.player.end_of_stream.connect (end_of_stream);
673- App.player.player.current_position_update.connect (current_position_update);
674- App.player.player.error_occured.connect (error_occured);
675- });
676-
677+ App.player.status_change.connect (on_playback_status_change);
678+
679 NotificationManager.get_default ().show_alert.connect (doAlert);
680
681 match_playlists = new Gee.HashMap<unowned Playlist, int> ();
682@@ -126,7 +110,6 @@
683 match_playlist_entry = new Gee.HashMap<unowned Playlist, SourceListEntry> ();
684 match_tvs = new Gee.HashMap<Playlist, TreeViewSetup> ();
685
686-
687 // init some booleans
688 if (this.library_manager.get_medias ().size > 0) {
689 App.player.clearCurrent();
690@@ -134,15 +117,67 @@
691 // make sure we don't re-count stats
692 if (main_settings.last_media_position > 5)
693 media_considered_previewed = true;
694- if(main_settings.last_media_position > 30)
695+ if (main_settings.last_media_position > 30)
696 media_considered_played = true;
697- if(App.player.current_media != null && (double)(main_settings.last_media_position/(double)App.player.current_media.length) > 0.90)
698- added_to_play_count = true;
699- }
700-
701- /*if(!File.new_for_path(settings.getMusicFolder()).query_exists() && settings.getMusicFolder() != "") {
702- doAlert("Music folder not mounted", "Your music folder is not mounted. Please mount your music folder before using Noise.");
703- }*/
704+ }
705+ }
706+
707+ private void on_playback_status_change (Media? current_media, PlaybackStatus status) {
708+ if (this.current_media != current_media) {
709+ this.current_media = current_media;
710+ //reset the media position
711+ topDisplay.set_media (App.player.current_media);
712+
713+ //reset some booleans
714+ tested_for_video = false;
715+ media_considered_previewed = false;
716+ media_considered_played = false;
717+ added_to_play_count = false;
718+ media_half_played_sended = false;
719+
720+ /* XXX
721+ Timeout.add (3000, () => {
722+ if (App.player.current_media != null && App.player.current_media == m) {
723+ new Thread<void*> (null, () => {
724+ update_media_info (App.player.current_media);
725+ return null;
726+ });
727+ }
728+
729+ return false;
730+ });
731+ */
732+ }
733+
734+ switch (status) {
735+ case PlaybackStatus.STOPPED:
736+ playButton.set_image (new Gtk.Image.from_icon_name ("media-playback-start-symbolic", Gtk.IconSize.LARGE_TOOLBAR));
737+ //reset some booleans
738+ tested_for_video = false;
739+ media_considered_previewed = false;
740+ media_considered_played = false;
741+ added_to_play_count = false;
742+
743+ debug ("playback stopped");
744+ break;
745+
746+ case PlaybackStatus.PLAYING:
747+ playButton.set_image (new Gtk.Image.from_icon_name ("media-playback-pause-symbolic", Gtk.IconSize.LARGE_TOOLBAR));
748+ playButton.set_tooltip_text (_("Pause"));
749+ debug ("playback started");
750+ break;
751+
752+ case PlaybackStatus.PAUSED:
753+ playButton.set_image (new Gtk.Image.from_icon_name ("media-playback-start-symbolic", Gtk.IconSize.LARGE_TOOLBAR));
754+ playButton.set_tooltip_text (_("Play"));
755+ debug ("playback paused");
756+ break;
757+
758+ default:
759+ assert_not_reached ();
760+ }
761+
762+ update_sensitivities ();
763 }
764
765 private void change_view (Widgets.ViewSelector.Mode mode) {
766@@ -162,7 +197,7 @@
767
768 if (!modifiers_active && search_field_has_focus) {
769 if (event.keyval == Gdk.Key.space && !searchField.has_focus && !source_list_view.editing) {
770- play_media (); // toggle play/pause
771+ play_pause (); // toggle play/pause
772 return true;
773 }
774
775@@ -485,7 +520,7 @@
776 source_list_view.playlist_import_clicked.connect ( () => {
777 try {
778 PlaylistsUtils.import_from_playlist_file_info(Noise.PlaylistsUtils.get_playlists_to_import (), library_manager);
779- update_sensitivities.begin ();
780+ update_sensitivities ();
781 } catch (GLib.Error e) {
782 warning (e.message);
783 }
784@@ -501,7 +536,7 @@
785
786 load_playlists ();
787 //sideTree.resetView ();
788- update_sensitivities_sync (); // we need to do this synchronously to avoid weird initial states
789+ update_sensitivities();
790
791 // Now set the selected view
792 viewSelector.selected = (Widgets.ViewSelector.Mode) Settings.SavedState.get_default ().view_mode;
793@@ -526,9 +561,9 @@
794 }
795
796 /* Connect events to functions */
797- previousButton.clicked.connect (() => {play_previous_media ();});
798- playButton.clicked.connect (() => {play_media ();});
799- nextButton.clicked.connect (() => {play_next_media ();});
800+ previousButton.clicked.connect (() => play_previous_media ());
801+ playButton.clicked.connect (() => play_pause ());
802+ nextButton.clicked.connect (() => play_next_media ());
803
804 searchField.activate.connect (searchFieldActivate);
805 searchField.search_changed.connect (() => {if (searchField.text_length != 1) libraries_manager.search_for_string (searchField.get_text ());});
806@@ -536,14 +571,6 @@
807 libraries_manager.search_for_string (main_settings.search_string);
808
809 debug ("DONE WITH USER INTERFACE");
810-
811- int last_playing_id = main_settings.last_media_playing;
812-
813- if (last_playing_id > 0) {
814- var last_playing_media = library_manager.media_from_id (last_playing_id);
815- if (last_playing_media != null && last_playing_media.file.query_exists ())
816- App.player.playMedia (last_playing_media, true);
817- }
818 }
819
820 /**
821@@ -602,7 +629,12 @@
822 #endif
823 }
824
825- public async void show_notification_from_media_async (Media media) {
826+ private void notify_current_media () {
827+ if (App.player.current_media != null)
828+ show_notification_from_media (App.player.current_media);
829+ }
830+
831+ public void show_notification_from_media (Media media) {
832 if (media == null)
833 return;
834
835@@ -629,12 +661,6 @@
836 }
837 }
838
839- private async void notify_current_media_async () {
840- if (App.player.current_media != null)
841- yield show_notification_from_media_async (App.player.current_media);
842- }
843-
844-
845 /**
846 * Sets the given view as the active item
847 */
848@@ -711,21 +737,12 @@
849 entry.badge = new_badge;
850 }
851
852- /**
853+ /**
854 * Updating the interface
855 */
856
857- private bool update_sensitivities_pending = false;
858-
859- public async void update_sensitivities () {
860- if (update_sensitivities_pending)
861- return;
862-
863- update_sensitivities_pending = true;
864- update_sensitivities_sync ();
865- update_sensitivities_pending = false;
866- }
867- private void update_sensitivities_sync () {
868+ // XXX should be private!
869+ public void update_sensitivities () {
870 debug ("UPDATE SENSITIVITIES");
871
872 bool folder_set = library_manager.main_directory_set;
873@@ -744,7 +761,7 @@
874 // hide playlists when media list is empty
875 source_list_view.change_playlist_category_visibility (have_media);
876
877- if(!media_active || have_media && !App.player.playing)
878+ if (!media_active || have_media && !App.player.playback_status.is_playing ())
879 playButton.set_image (new Gtk.Image.from_icon_name ("media-playback-start-symbolic", Gtk.IconSize.LARGE_TOOLBAR));
880
881 bool show_info_panel = Settings.SavedState.get_default ().more_visible && info_panel.can_show_up;
882@@ -964,63 +981,9 @@
883 spe.show ();
884 }
885
886- /** This should be used whenever a call to play a new media is made
887- *
888- * XXX: this doesn't belong here, but to the playback manager
889- * @param s The media that is now playing
890- */
891- public void media_played (Media m) {
892- //reset the media position
893- topDisplay.set_media (App.player.current_media);
894-
895- //reset some booleans
896- tested_for_video = false;
897- media_considered_previewed = false;
898- media_considered_played = false;
899- added_to_play_count = false;
900- media_half_played_sended = false;
901-
902- update_sensitivities.begin ();
903-
904- Timeout.add (3000, () => {
905- if (App.player.current_media != null && App.player.current_media == m) {
906- new Thread<void*> (null, () => {
907- update_media_info (App.player.current_media);
908- return null;
909- });
910- }
911-
912- return false;
913- });
914- }
915-
916-
917- public virtual void playback_stopped (int was_playing) {
918- playButton.set_image (new Gtk.Image.from_icon_name ("media-playback-start-symbolic", Gtk.IconSize.LARGE_TOOLBAR));
919- //reset some booleans
920- tested_for_video = false;
921- media_considered_previewed = false;
922- media_considered_played = false;
923- added_to_play_count = false;
924-
925- update_sensitivities.begin ();
926-
927- debug ("playback stopped");
928- }
929-
930- public virtual void playback_started () {
931- playButton.set_image (new Gtk.Image.from_icon_name ("media-playback-pause-symbolic", Gtk.IconSize.LARGE_TOOLBAR));
932- playButton.set_tooltip_text (_("Pause"));
933- debug ("playback started");
934- }
935-
936- public virtual void playback_paused () {
937- playButton.set_image (new Gtk.Image.from_icon_name ("media-playback-start-symbolic", Gtk.IconSize.LARGE_TOOLBAR));
938- playButton.set_tooltip_text (_("Play"));
939- debug ("playback paused");
940- }
941-
942- public virtual void play_media (bool inhibit_notifications = false) {
943+ public void play_pause (bool inhibit_notifications = false) {
944+#if REMEMBER_TO_MOVE_THIS_CODE_TO_PLAYBACK_MANAGER
945+
946 if(App.player.current_media == null) {
947 debug("No media is currently playing. Starting from the top\n");
948
949@@ -1028,19 +991,22 @@
950 App.player.start_playback ();
951
952 if (!inhibit_notifications)
953- notify_current_media_async.begin ();
954+ notify_current_media ();
955 } else {
956- if(App.player.playing) {
957+ if (App.player.playing) {
958 App.player.pause_playback ();
959 } else {
960 App.player.start_playback ();
961 }
962+
963 }
964+#endif
965
966- playPauseChanged();
967+ App.player.play_pause ();
968 }
969
970- public virtual void play_next_media (bool inhibit_notifications = false) {
971+ public void play_next_media (bool inhibit_notifications = false) {
972+#if REMEMBER_TO_MOVE_THIS_CODE_TO_PLAYBACK_MANAGER
973 // if not 90% done, skip it
974 if(!added_to_play_count) {
975 App.player.current_media.skip_count++;
976@@ -1060,31 +1026,44 @@
977 /* test to stop playback/reached end */
978 if(m == null) {
979 App.player.stop_playback ();
980- update_sensitivities.begin ();
981+ update_sensitivities ();
982 return;
983 }
984+#endif
985+ // no need to do UI work here. PlaybackManager will notify us through status_change.
986+ App.player.getNext (true);
987
988 if (!inhibit_notifications)
989- notify_current_media_async.begin ();
990+ notify_current_media ();
991 }
992
993- public virtual void play_previous_media (bool inhibit_notifications = false) {
994- if (App.player.player.get_position () < 5000000000) {
995+ public void play_previous_media (bool inhibit_notifications = false) {
996+#if REMEMBER_TO_MOVE_THIS_CODE_TO_PLAYBACK_MANAGER
997+
998+ if (App.player.get_position () < 5000000000) {
999 bool play = true;
1000 var prev = App.player.getPrevious(true);
1001
1002 /* test to stop playback/reached end */
1003 if(prev == null) {
1004 App.player.stop_playback ();
1005- update_sensitivities.begin ();
1006+ update_sensitivities ();
1007 return;
1008 } else if (play && !inhibit_notifications) {
1009- notify_current_media_async.begin ();
1010+ notify_current_media ();
1011 }
1012 } else
1013 topDisplay.change_value (Gtk.ScrollType.NONE, 0);
1014+#endif
1015+
1016+ // no need to do UI work here. PlaybackManager will notify us through status_change.
1017+ App.player.getPrevious (true);
1018+
1019+ if (!inhibit_notifications)
1020+ notify_current_media ();
1021 }
1022
1023+
1024 public virtual void fileImportMusicClick () {
1025 if(!library_manager.doing_file_operations()) {
1026
1027@@ -1109,7 +1088,7 @@
1028
1029 if (GLib.File.new_for_path (main_settings.music_folder).query_exists ()) {
1030 library_manager.add_folder_to_library (folders);
1031- update_sensitivities.begin ();
1032+ update_sensitivities ();
1033 }
1034 } else {
1035 debug("Can't add to library.. already doing file operations\n");
1036@@ -1145,79 +1124,6 @@
1037 }
1038 }
1039
1040- public virtual void end_of_stream() {
1041- play_next_media ();
1042- }
1043-
1044- public virtual void error_occured () {
1045- if(App.player.current_media != null) {
1046- play_media ();
1047- }
1048- }
1049-
1050- public virtual void current_position_update (int64 position) {
1051- if (App.player.current_media == null)
1052- return;
1053-
1054- double sec = ((double)position/1000000000);
1055- double media_length = ((double)App.player.current_media.length/1000);
1056-
1057- if(App.player.file_player.set_resume_pos)
1058- App.player.current_media.resume_pos = (int)sec;
1059-
1060- // at about 3 seconds, update last fm. we wait to avoid excessive querying last.fm for info
1061- if(sec > 3 && !media_considered_previewed) {
1062- media_considered_previewed = true;
1063- update_media_info (App.player.current_media);
1064- }
1065-
1066- //at 30 seconds in, we consider the media as played
1067- if(sec > 30 && !media_considered_played) {
1068- media_considered_played = true;
1069- App.player.current_media.last_played = (int)time_t();
1070-
1071- library_manager.update_media (App.player.current_media, false, false);
1072-
1073- // add to the already played list
1074- if(!App.player.history_playlist.medias.contains (App.player.current_media)) {
1075- var temp_media = new Gee.TreeSet<Media>();
1076- temp_media.add (App.player.current_media);
1077- App.player.history_playlist.add_medias (temp_media);
1078- }
1079-
1080-#if HAVE_ZEITGEIST
1081- var event = new Zeitgeist.Event.full (Zeitgeist.ZG_ACCESS_EVENT,
1082- Zeitgeist.ZG_SCHEDULED_ACTIVITY, "app://%s".printf (App.instance.get_desktop_file_name ()),
1083- new Zeitgeist.Subject.full(App.player.current_media.uri,
1084- Zeitgeist.NFO_AUDIO,
1085- Zeitgeist.NFO_FILE_DATA_OBJECT,
1086- "text/plain", "",
1087- App.player.current_media.title, ""));
1088- new Zeitgeist.Log ().insert_events_no_reply(event);
1089-#endif
1090- }
1091-
1092- if((sec/media_length > 0.50) && (media_half_played_sended == false)) {
1093- media_half_played (App.player.current_media);
1094- media_half_played_sended = true;
1095- }
1096-
1097- // at 80% done with media, add 1 to play count
1098- if(sec/media_length > 0.80 && !added_to_play_count) {
1099- added_to_play_count = true;
1100- App.player.current_media.play_count++;
1101- library_manager.update_media (App.player.current_media, false, false);
1102- }
1103- }
1104-
1105- public void media_not_found(int id) {
1106-// XXX FIXME TODO Don't depend on ids
1107-#if 0
1108- var not_found = new FileNotFoundDialog(library_manager, this, id);
1109- not_found.show();
1110-#endif
1111- }
1112-
1113 public void searchFieldActivate() {
1114 var vw = view_container.get_current_view ();
1115
1116@@ -1253,14 +1159,17 @@
1117 }
1118
1119 private void on_quit () {
1120+ int last_media_position = (int) ((double) App.player.get_position () / Numeric.NANO_INV);
1121+
1122 // Save media position and info
1123- main_settings.last_media_position = (int)((double)App.player.player.get_position
1124- ()/Numeric.NANO_INV);
1125- if(App.player.current_media != null) {
1126- App.player.current_media.resume_pos = (int)((double)App.player.player.get_position()/Numeric.NANO_INV);
1127+ main_settings.last_media_position = last_media_position;
1128+
1129+ if (App.player.current_media != null) {
1130+ App.player.current_media.resume_pos = last_media_position;
1131 library_manager.update_media (App.player.current_media, false, false);
1132 }
1133- App.player.player.pause();
1134+
1135+ App.player.pause_playback ();
1136
1137 // Now set the selected view
1138 var saved_state = Settings.SavedState.get_default ();
1139@@ -1293,7 +1202,7 @@
1140 * This doesn't apply to calls to App.instance.quit ()
1141 */
1142 public override bool delete_event (Gdk.EventAny event) {
1143- bool playing = App.player.current_media != null && App.player.playing;
1144+ bool playing = App.player.current_media != null && App.player.playback_status.is_playing ();
1145
1146 // if playing a song, don't allow closing
1147 if (!main_settings.close_while_playing && playing) {
1148
1149=== modified file 'src/LocalBackend/LocalLibrary.vala'
1150--- src/LocalBackend/LocalLibrary.vala 2015-03-01 16:43:54 +0000
1151+++ src/LocalBackend/LocalLibrary.vala 2015-04-27 05:07:36 +0000
1152@@ -162,7 +162,7 @@
1153
1154 App.player.reset_already_played ();
1155 // FIXME: these are library window's internals. Shouldn't be here
1156- App.main_window.update_sensitivities.begin ();
1157+ App.main_window.update_sensitivities ();
1158 App.player.stop_playback ();
1159
1160 set_music_folder_thread.begin (folder);
1161@@ -227,7 +227,7 @@
1162
1163 public void rescan_music_folder () {
1164 if (start_file_operations (_("Rescanning music for changes. This may take a while…"))) {
1165- App.main_window.update_sensitivities.begin ();
1166+ App.main_window.update_sensitivities ();
1167 rescan_music_folder_async.begin ();
1168 }
1169 }
1170@@ -298,7 +298,7 @@
1171 if (open_media_list.size > 0) {
1172 if (!App.player.playing) {
1173 App.player.playMedia (open_media_list.first (), false);
1174- App.main_window.play_media ();
1175+ //XXX not needed App.main_window.play_media ();
1176 } else {
1177 string primary_text = _("Added to your queue:");
1178
1179@@ -726,7 +726,7 @@
1180
1181 NotificationManager.get_default ().update_progress (message, 0.0);
1182 _doing_file_operations = true;
1183- App.main_window.update_sensitivities.begin ();
1184+ App.main_window.update_sensitivities ();
1185 file_operations_started ();
1186 return true;
1187 }
1188
1189=== modified file 'src/Objects/MediaKeyListener.vala'
1190--- src/Objects/MediaKeyListener.vala 2013-12-01 14:35:04 +0000
1191+++ src/Objects/MediaKeyListener.vala 2015-04-27 05:07:36 +0000
1192@@ -77,7 +77,7 @@
1193 App.main_window.play_previous_media ();
1194 }
1195 else if(key == "Play") {
1196- App.main_window.play_media ();
1197+ App.main_window.play_pause ();
1198 }
1199 else if(key == "Next") {
1200 App.main_window.play_next_media ();
1201
1202=== modified file 'src/PlaybackManager.vala'
1203--- src/PlaybackManager.vala 2015-03-23 04:51:51 +0000
1204+++ src/PlaybackManager.vala 2015-04-27 05:07:36 +0000
1205@@ -1,6 +1,6 @@
1206 // -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*-
1207 /*-
1208- * Copyright (c) 2012-2013 Noise Developers (http://launchpad.net/noise)
1209+ * Copyright (c) 2012-2015 Noise Developers (http://launchpad.net/noise)
1210 *
1211 * This library is free software; you can redistribute it and/or
1212 * modify it under the terms of the GNU Library General Public
1213@@ -27,27 +27,13 @@
1214 *
1215 * Authored by: Scott Ringwelski <sgringwe@mtu.edu>
1216 * Corentin Noël <tintou@mailoo.org>
1217+ * Victor Martinez <victor@elementaryos.org>
1218 */
1219
1220-public class Noise.PlaybackManager : Object, Noise.Player {
1221-
1222- // TODO: Have a correct implementation based on playlists.
1223- // Show the next 2 medias in the queue when shuffle mode is enabled and create a playlist to remember shuffled songs
1224- // Get the current filter (search, playlist) to be able to store and set it back when restart.
1225-
1226- public signal void current_cleared ();
1227-
1228- public signal void queue_cleared ();
1229- public signal void media_queued (Gee.Collection<Media> queued);
1230-
1231- public signal void media_played (Media played_media);
1232- public signal void playback_stopped (int was_playing);
1233- public signal void playback_started ();
1234- public signal void playback_paused ();
1235- public signal void changing_player ();
1236- public signal void player_changed ();
1237-
1238+public class Noise.PlaybackManager : Object, Player {
1239+ /*
1240 private Gee.TreeSet<unowned Noise.Playback> playbacks = new Gee.TreeSet<unowned Noise.Playback> ();
1241+ */
1242
1243 // id, media of current media.
1244 private Gee.HashMap<int, Media> _current = new Gee.HashMap<int, Media>();
1245@@ -55,16 +41,18 @@
1246 //list of id's yet to be played while on shuffle
1247 private Gee.HashMap<int, Media> _current_shuffled = new Gee.HashMap<int, Media>();
1248
1249- public StaticPlaylist queue_playlist = new StaticPlaylist ();
1250- public StaticPlaylist history_playlist = new StaticPlaylist ();
1251+ public StaticPlaylist queue_playlist { get; private set; default = new StaticPlaylist (); }
1252+ public StaticPlaylist history_playlist { get; private set; default = new StaticPlaylist (); }
1253
1254 // TODO: REWRITE IT USING THE LIBRARY
1255- public Library library { get { return libraries_manager.local_library; } }
1256+ // public Library library { get { return libraries_manager.local_library; } }
1257
1258 private int _current_index;
1259 private int _current_shuffled_index;
1260
1261- public bool playing { get; private set; default = false; }
1262+ [Deprecated (replacement = "playback_status.is_playing ()")]
1263+ public bool playing { get; private set; default = false; }
1264+
1265 private double saved_volume = 1;
1266 public double volume {
1267 get{
1268@@ -76,28 +64,48 @@
1269 }
1270 }
1271
1272- bool _playing_queued_song;
1273-
1274- public int next_gapless_id;
1275-
1276- public Noise.Streamer file_player;
1277- public Noise.Playback player;
1278- public Noise.Media current_media { private set; get; }
1279+ private bool _playing_queued_song;
1280+
1281+ public Media current_media { get; private set; }
1282+ public PlaybackStatus playback_status { get; private set; }
1283+ private Media? next;
1284+ private int next_gapless_id;
1285+ private Streamer file_player;
1286+ private Playback player;
1287
1288 public PlaybackManager () {
1289 file_player = new Streamer ();
1290- playbacks.add (file_player);
1291+ // XXX playbacks.add (file_player);
1292 player = file_player;
1293 history_playlist.name = _("History");
1294 history_playlist.read_only = true;
1295 queue_playlist.name = C_("Name of the playlist", "Queue");
1296 queue_playlist.read_only = true;
1297 queue_playlist.allow_duplicate = true;
1298+
1299+ player.about_to_finish.connect (media_about_to_finish);
1300+ player.end_of_stream.connect (media_finished_playing);
1301 }
1302
1303- public void add_playback (Noise.Playback playback) {
1304+/*
1305+ public void add_playback (Playback playback) {
1306 playbacks.add (playback);
1307 }
1308+*/
1309+ private void playback_stopped (int was_playing) {
1310+ playback_status = PlaybackStatus.STOPPED;
1311+ status_change (current_media, playback_status);
1312+ }
1313+
1314+ private void playback_started () {
1315+ playback_status = PlaybackStatus.PLAYING;
1316+ status_change (current_media, playback_status);
1317+ }
1318+
1319+ private void playback_paused () {
1320+ playback_status = PlaybackStatus.PAUSED;
1321+ status_change (current_media, playback_status);
1322+ }
1323
1324 /*
1325 * Queue Stuff
1326@@ -112,17 +120,18 @@
1327 media_queued (to_queue);
1328 }
1329
1330+ // TODO deprecate to get rid of circular library dependency
1331 public void queue_media_by_id (Gee.Collection<int> ids) {
1332- queue_media (library.medias_from_ids (ids));
1333+ queue_media (libraries_manager.local_library.medias_from_ids (ids));
1334 }
1335
1336-
1337 public void unqueue_media (Gee.Collection<Media> to_unqueue) {
1338 queue_playlist.remove_medias (to_unqueue);
1339 }
1340
1341+ // TODO deprecate to get rid of circular library dependency
1342 public void unqueue_media_by_id (Gee.Collection<int> ids) {
1343- unqueue_media (library.medias_from_ids (ids));
1344+ unqueue_media (libraries_manager.local_library.medias_from_ids (ids));
1345 }
1346
1347 public Media peek_queue () {
1348@@ -269,7 +278,61 @@
1349 }
1350 }
1351 }
1352-
1353+
1354+ public int64 get_position () {
1355+ return player.get_position ();
1356+ }
1357+
1358+ public void set_position (int64 pos) {
1359+ player.set_position (pos);
1360+ }
1361+
1362+ public void set_equalizer_gain (int index, int gain) {
1363+ player.set_equalizer_gain (index, gain);
1364+ }
1365+
1366+ public void play_pause () {
1367+ if (current_media == null) {
1368+ debug("No media is currently playing. Starting from the top\n");
1369+ getNext (true);
1370+ start_playback ();
1371+ } else if (playing) {
1372+ pause_playback ();
1373+ } else {
1374+ start_playback ();
1375+ }
1376+ }
1377+
1378+ private void media_about_to_finish (string about_to_finish_uri) {
1379+ debug ("About to finish: %s", about_to_finish_uri);
1380+
1381+ next = getNext (false);
1382+
1383+ if (next != null)
1384+ player.set_next_media (next);
1385+ else
1386+ debug ("No next media.");
1387+ }
1388+
1389+ private void media_finished_playing (string? finished_uri, string? new_uri) {
1390+ debug ("Finished playing: %s", finished_uri);
1391+
1392+ if (next != null && new_uri != next.uri)
1393+ critical ("Next song does not match URI!");
1394+
1395+ // notify media's changed
1396+ media_played (next);
1397+ }
1398+
1399+ // For gapless playback reasons, this function is only called when the streamer
1400+ // actually starts playing the media
1401+ private void media_played (Media? media) {
1402+ debug ("== MEDIA_PLAYED: %s", media.title);
1403+ current_media = media;
1404+ next = null;
1405+ status_change (current_media, playback_status);
1406+ }
1407+
1408 public Media? getNext(bool play) {
1409 Media? rv = null;
1410
1411@@ -280,7 +343,8 @@
1412 _playing_queued_song = true;
1413 } else if (main_settings.shuffle_mode != Noise.Settings.Shuffle.OFF) {
1414 if (_current_shuffled.is_empty ) {
1415- foreach (Media s in library.get_medias ())
1416+ // XXX dont depend on library! get a playlist instead
1417+ foreach (Media s in libraries_manager.local_library.get_medias ())
1418 addToCurrent (s); //first initialize the current selection the reshuffle it
1419 reshuffle ();
1420 }
1421@@ -371,7 +435,8 @@
1422
1423 rv = _current.get (_current_index);
1424 } else {
1425- foreach (Media s in library.get_medias ()) {
1426+ // XXX dont depend on library! get a playlist instead
1427+ foreach (Media s in libraries_manager.local_library.get_medias ()) {
1428 addToCurrent(s);
1429 }
1430
1431@@ -394,7 +459,8 @@
1432 var main_settings = Settings.Main.get_default ();
1433 if(main_settings.shuffle_mode != Noise.Settings.Shuffle.OFF) {
1434 if (_current_shuffled.is_empty)
1435- foreach (Media s in library.get_medias ())
1436+ // XXX dont depend on library! get a playlist instead
1437+ foreach (Media s in libraries_manager.local_library.get_medias ())
1438 addToCurrent (s); //first initialize the current selection the reshuffle it
1439 reshuffle ();
1440 _playing_queued_song = false;
1441@@ -460,7 +526,8 @@
1442
1443 rv = _current.get(_current_index);
1444 } else {
1445- foreach(Media s in library.get_medias ())
1446+ // XXX dont depend on library! get a playlist instead
1447+ foreach(Media s in libraries_manager.local_library.get_medias ())
1448 addToCurrent(s);
1449
1450 _current_index = _current.size - 1;
1451@@ -498,7 +565,8 @@
1452 return;
1453 }
1454 }
1455-
1456+
1457+ /* XXX no multiple streamers for now
1458 var found = false;
1459 lock (playbacks) {
1460 foreach (var playback in playbacks) {
1461@@ -519,12 +587,14 @@
1462 }
1463 }
1464 }
1465+
1466 if (found == false) {
1467 m.unique_status_image = Icons.PROCESS_ERROR.render(Gtk.IconSize.MENU);
1468 getNext(true);
1469 return;
1470 }
1471-
1472+ */
1473+
1474 // check that the file exists
1475 if (! m.file.query_exists ()) {
1476 m.unique_status_image = Icons.PROCESS_ERROR.render(Gtk.IconSize.MENU);
1477@@ -549,11 +619,11 @@
1478 else {
1479 next_gapless_id = 0;
1480 }
1481-
1482+
1483 //pause if paused
1484 if (!playing)
1485 player.pause ();
1486-
1487+
1488 //update settings
1489 if (m.rowid >= 0)
1490 Settings.Main.get_default ().last_media_playing = m.rowid;
1491@@ -574,7 +644,9 @@
1492 int delta_s = (int)player_duration_s - (int)(m.length / Numeric.MILI_INV);
1493 if (Math.fabs ((double)delta_s) > 3) {
1494 m.length = (uint)(player_duration_s * Numeric.MILI_INV);
1495- library.update_media (m, false, false);
1496+
1497+ // XXX dont depend on library! get a playlist instead
1498+ libraries_manager.local_library.update_media (m, false, false);
1499 }
1500 }
1501 }
1502
1503=== modified file 'src/Views/ListView/Lists/GenericList.vala'
1504--- src/Views/ListView/Lists/GenericList.vala 2015-03-01 16:43:54 +0000
1505+++ src/Views/ListView/Lists/GenericList.vala 2015-04-27 05:07:36 +0000
1506@@ -38,6 +38,8 @@
1507
1508 protected CellDataFunctionHelper cell_data_helper;
1509
1510+ private unowned Media current_media;
1511+
1512 public GenericList (ViewWrapper view_wrapper, TreeViewSetup tvs) {
1513 var types = new Gee.LinkedList<Type> ();
1514 foreach (var type in ListColumn.get_all ())
1515@@ -93,7 +95,15 @@
1516 parent_wrapper.library.media_updated.connect (media_updated);
1517
1518 App.player.current_cleared.connect (current_cleared);
1519- App.player.media_played.connect (media_played);
1520+ App.player.status_change.connect (on_playback_status_change);
1521+ }
1522+
1523+ private void on_playback_status_change (Media? current_media, PlaybackStatus status) {
1524+ if (this.current_media == current_media)
1525+ return;
1526+
1527+ this.current_media = current_media;
1528+ media_played (current_media);
1529 }
1530
1531 protected abstract void mediaRemoveClicked ();
1532@@ -276,8 +286,8 @@
1533 // Now play the song
1534 App.player.playMedia (m, false);
1535
1536- if (!App.player.playing) {
1537- App.main_window.play_media ();
1538+ if (!App.player.playback_status.is_playing ()) {
1539+ App.main_window.play_pause ();
1540 }
1541 }
1542
1543
1544=== modified file 'src/Widgets/EqualizerPopover.vala'
1545--- src/Widgets/EqualizerPopover.vala 2014-03-23 22:17:09 +0000
1546+++ src/Widgets/EqualizerPopover.vala 2015-04-27 05:07:36 +0000
1547@@ -191,7 +191,7 @@
1548 }
1549
1550 private void apply_equalizer_gain (int index, int val) {
1551- App.player.player.set_equalizer_gain (index, val);
1552+ App.player.set_equalizer_gain (index, val);
1553 }
1554
1555 private void apply_automatic_gains () {
1556@@ -420,4 +420,4 @@
1557 private void remove_preset_clicked () {
1558 preset_combo.removeCurrentPreset ();
1559 }
1560-}
1561\ No newline at end of file
1562+}
1563
1564=== modified file 'src/Widgets/InfoPanel.vala'
1565--- src/Widgets/InfoPanel.vala 2015-03-01 16:43:54 +0000
1566+++ src/Widgets/InfoPanel.vala 2015-04-27 05:07:36 +0000
1567@@ -23,7 +23,6 @@
1568 public class Noise.InfoPanel : Gtk.EventBox {
1569 public signal void to_update();
1570
1571- public unowned Media current_media { get { return App.player.current_media; } }
1572 public bool can_show_up { get { return App.player.current_media != null; } }
1573
1574 private Gtk.Label title;
1575@@ -36,15 +35,13 @@
1576 private Gtk.Grid container;
1577
1578 private const string TITLE_MARKUP = "<span size=\"large\"><b>%s</b></span>";
1579-
1580+ private unowned Media current_media;
1581
1582 public InfoPanel () {
1583-
1584 buildUI();
1585
1586- App.player.media_played.connect_after (on_media_updated);
1587- libraries_manager.local_library.media_updated.connect_after (on_media_updated);
1588- NotificationManager.get_default ().update_track.connect (on_media_updated);
1589+ App.player.status_change.connect_after (on_media_changed);
1590+ NotificationManager.get_default ().update_track.connect (update_current_media);
1591 CoverartCache.instance.changed.connect_after (update_cover_art);
1592 }
1593
1594@@ -116,7 +113,22 @@
1595 to_update ();
1596 }
1597
1598- private void on_media_updated () {
1599+ private void on_media_changed (Media? current_media, PlaybackStatus status) {
1600+ if (this.current_media == current_media)
1601+ return;
1602+
1603+ // unset previous media
1604+ if (this.current_media != null)
1605+ this.current_media.updated.disconnect (update_current_media);
1606+
1607+ // change media to new one
1608+ this.current_media = current_media;
1609+ current_media.updated.connect (update_current_media);
1610+
1611+ update_current_media ();
1612+ }
1613+
1614+ private void update_current_media () {
1615 update_visibilities ();
1616 update_metadata ();
1617 update_cover_art ();
1618
1619=== modified file 'src/Widgets/TopDisplay.vala'
1620--- src/Widgets/TopDisplay.vala 2015-03-12 02:43:20 +0000
1621+++ src/Widgets/TopDisplay.vala 2015-04-27 05:07:36 +0000
1622@@ -21,6 +21,8 @@
1623 */
1624
1625 public class Noise.TopDisplay : Gtk.Stack {
1626+ private const int PLAYBACK_POSITION_REFRESH_HZ = 5;
1627+
1628 MusicListView list_view;
1629
1630 Gtk.Grid empty_grid;
1631@@ -42,6 +44,8 @@
1632 private bool is_seeking = false;
1633 private uint change_timeout_id = 0;
1634 private uint progress_timeout_id = 0;
1635+ private uint playback_timeout_id = 0;
1636+ private unowned Media current_media;
1637
1638 public signal void scale_value_changed (Gtk.ScrollType scroll, double val);
1639
1640@@ -123,16 +127,6 @@
1641 scale.value_changed.connect (value_changed);
1642 scale.change_value.connect (change_value);
1643
1644- App.player.player.current_position_update.connect (player_position_update);
1645-
1646- App.player.changing_player.connect (() => {
1647- App.player.player.current_position_update.disconnect (player_position_update);
1648- });
1649-
1650- App.player.player_changed.connect (() => {
1651- App.player.player.current_position_update.connect (player_position_update);
1652- });
1653-
1654 var notification_manager = NotificationManager.get_default ();
1655 notification_manager.update_progress.connect ((message, progress) => {
1656 set_progress_value (progress);
1657@@ -144,7 +138,7 @@
1658 track_label.set_markup (message);
1659 });
1660
1661- libraries_manager.local_library.media_updated.connect (media_updated);
1662+ start_polling_position ();
1663 }
1664
1665 public override void get_preferred_width (out int minimum_width, out int natural_width) {
1666@@ -181,7 +175,6 @@
1667 return true;
1668 }
1669
1670- App.player.player.current_position_update.disconnect (player_position_update);
1671 is_seeking = true;
1672 change_value (Gtk.ScrollType.NONE, get_current_time ());
1673
1674@@ -230,8 +223,19 @@
1675 rightTime.set_text (TimeUtils.pretty_length_from_ms (media_duration_secs - elapsed_secs));
1676 }
1677
1678+ private void start_polling_position () {
1679+ playback_timeout_id = Timeout.add (1000 / PLAYBACK_POSITION_REFRESH_HZ, update_time_position);
1680+ }
1681+
1682+ private void stop_polling_position () {
1683+ if (playback_timeout_id > 0)
1684+ Source.remove (playback_timeout_id);
1685+
1686+ playback_timeout_id = 0;
1687+ }
1688+
1689 public virtual bool change_value (Gtk.ScrollType scroll, double val) {
1690- App.player.player.current_position_update.disconnect(player_position_update);
1691+ stop_polling_position ();
1692 scale.set_value(val);
1693 scale_value_changed(scroll, val);
1694
1695@@ -240,8 +244,8 @@
1696
1697 change_timeout_id = Timeout.add (300, () => {
1698 if (!is_seeking) {
1699- App.player.player.set_position((int64) TimeUtils.miliseconds_to_nanoseconds ((uint) val));
1700- App.player.player.current_position_update.connect(player_position_update);
1701+ App.player.set_position ((int64) TimeUtils.miliseconds_to_nanoseconds ((uint) val));
1702+ start_polling_position ();
1703 }
1704
1705 change_timeout_id = 0;
1706@@ -251,14 +255,22 @@
1707 return false;
1708 }
1709
1710- public virtual void player_position_update (int64 position) {
1711- if (App.player.current_media != null) {
1712- scale.set_value ((double) TimeUtils.nanoseconds_to_miliseconds (position));
1713- }
1714+ public virtual bool update_time_position () {
1715+ int64 position = App.player.get_position ();
1716+ scale.set_value ((double) TimeUtils.nanoseconds_to_miliseconds (position));
1717+ return true;
1718 }
1719
1720- public void set_media (Media current_media) {
1721+ public void set_media (Media? current_media) {
1722+ // unset previous media
1723+ if (this.current_media != null)
1724+ this.current_media.updated.disconnect (update_current_media);
1725+
1726+ this.current_media = current_media;
1727+ current_media.updated.connect (update_current_media);
1728+
1729 update_current_media ();
1730+
1731 // If the media changes while an action is goind, show it for 5 seconds then come back to the action.
1732 if (progressbar.fraction >= 0.0 && progressbar.fraction < 1.0) {
1733 progress_timeout_id = Timeout.add (300, () => {
1734@@ -269,12 +281,6 @@
1735 }
1736 }
1737
1738- private void media_updated (Gee.Collection<int> ids) {
1739- if (App.player.current_media != null && ids.contains (App.player.current_media.rowid)) {
1740- update_current_media ();
1741- }
1742- }
1743-
1744 private void update_current_media () {
1745 var notification_manager = NotificationManager.get_default ();
1746 if (App.player.current_media == null)
1747@@ -285,6 +291,7 @@
1748 if (m == null)
1749 return;
1750
1751+ // XXX does this belong here?
1752 notification_manager.update_track (m.get_title_markup ());
1753 set_max_time ((double) m.length);
1754 set_visible_child (time_eventbox);

Subscribers

People subscribed via source and target branches