Merge lp:~victored/noise/gapless-playback into lp:~elementary-apps/noise/trunk
- gapless-playback
- Merge into trunk
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 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
elementary Apps team | Pending | ||
Review via email: mp+257485@code.launchpad.net |
Commit message
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_
== 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.
- 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
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
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); |
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.