2 * Copyright (C) 2003-2010 The Music Player Daemon Project
3 * http://www.musicpd.org
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include "playlist_internal.h"
22 #include "playlist_save.h"
23 #include "player_control.h"
28 #include "stored_playlist.h"
36 #define G_LOG_DOMAIN "playlist"
39 playlist_increment_version_all(struct playlist
*playlist
)
41 queue_modify_all(&playlist
->queue
);
42 idle_add(IDLE_PLAYLIST
);
46 playlist_tag_changed(struct playlist
*playlist
)
48 if (!playlist
->playing
)
51 assert(playlist
->current
>= 0);
53 queue_modify(&playlist
->queue
, playlist
->current
);
54 idle_add(IDLE_PLAYLIST
);
58 playlist_init(struct playlist
*playlist
)
60 queue_init(&playlist
->queue
,
61 config_get_positive(CONF_MAX_PLAYLIST_LENGTH
,
62 DEFAULT_PLAYLIST_MAX_LENGTH
));
64 playlist
->queued
= -1;
65 playlist
->current
= -1;
69 playlist_finish(struct playlist
*playlist
)
71 queue_finish(&playlist
->queue
);
75 * Queue a song, addressed by its order number.
78 playlist_queue_song_order(struct playlist
*playlist
, unsigned order
)
83 assert(queue_valid_order(&playlist
->queue
, order
));
85 playlist
->queued
= order
;
87 song
= queue_get_order(&playlist
->queue
, order
);
88 uri
= song_get_uri(song
);
89 g_debug("queue song %i:\"%s\"", playlist
->queued
, uri
);
92 pc_enqueue_song(song
);
96 * Check if the player thread has already started playing the "queued"
100 playlist_sync_with_queue(struct playlist
*playlist
)
102 if (pc
.next_song
== NULL
&& playlist
->queued
!= -1) {
103 /* queued song has started: copy queued to current,
104 and notify the clients */
106 int current
= playlist
->current
;
107 playlist
->current
= playlist
->queued
;
108 playlist
->queued
= -1;
110 if(playlist
->queue
.consume
)
111 playlist_delete(playlist
, queue_order_to_position(&playlist
->queue
, current
));
113 idle_add(IDLE_PLAYER
);
118 playlist_get_queued_song(struct playlist
*playlist
)
120 if (!playlist
->playing
|| playlist
->queued
< 0)
123 return queue_get_order(&playlist
->queue
, playlist
->queued
);
127 playlist_update_queued_song(struct playlist
*playlist
, const struct song
*prev
)
130 const struct song
*next_song
;
132 if (!playlist
->playing
)
135 assert(!queue_is_empty(&playlist
->queue
));
136 assert((playlist
->queued
< 0) == (prev
== NULL
));
138 next_order
= playlist
->current
>= 0
139 ? queue_next_order(&playlist
->queue
, playlist
->current
)
142 if (next_order
== 0 && playlist
->queue
.random
) {
143 /* shuffle the song order again, so we get a different
144 order each time the playlist is played
146 unsigned current_position
=
147 queue_order_to_position(&playlist
->queue
,
150 queue_shuffle_order(&playlist
->queue
);
152 /* make sure that the playlist->current still points to
153 the current song, after the song order has been
156 queue_position_to_order(&playlist
->queue
,
161 next_song
= queue_get_order(&playlist
->queue
, next_order
);
165 if (prev
!= NULL
&& next_song
!= prev
) {
166 /* clear the currently queued song */
168 playlist
->queued
= -1;
171 if (next_order
>= 0) {
172 if (next_song
!= prev
)
173 playlist_queue_song_order(playlist
, next_order
);
175 playlist
->queued
= next_order
;
180 playlist_play_order(struct playlist
*playlist
, int orderNum
)
185 playlist
->playing
= true;
186 playlist
->queued
= -1;
188 song
= queue_get_order(&playlist
->queue
, orderNum
);
190 uri
= song_get_uri(song
);
191 g_debug("play %i:\"%s\"", orderNum
, uri
);
195 playlist
->current
= orderNum
;
199 playlist_resume_playback(struct playlist
*playlist
);
202 * This is the "PLAYLIST" event handler. It is invoked by the player
203 * thread whenever it requests a new queued song, or when it exits.
206 playlist_sync(struct playlist
*playlist
)
208 if (!playlist
->playing
)
209 /* this event has reached us out of sync: we aren't
210 playing anymore; ignore the event */
213 if (pc_get_state() == PLAYER_STATE_STOP
)
214 /* the player thread has stopped: check if playback
215 should be restarted with the next song. That can
216 happen if the playlist isn't filling the queue fast
218 playlist_resume_playback(playlist
);
220 /* check if the player thread has already started
221 playing the queued song */
222 playlist_sync_with_queue(playlist
);
224 /* make sure the queued song is always set (if
226 if (pc
.next_song
== NULL
)
227 playlist_update_queued_song(playlist
, NULL
);
232 * The player has stopped for some reason. Check the error, and
233 * decide whether to re-start playback
236 playlist_resume_playback(struct playlist
*playlist
)
238 enum player_error error
;
240 assert(playlist
->playing
);
241 assert(pc_get_state() == PLAYER_STATE_STOP
);
243 error
= pc_get_error();
244 if (error
== PLAYER_ERROR_NOERROR
)
245 playlist
->error_count
= 0;
247 ++playlist
->error_count
;
249 if ((playlist
->stop_on_error
&& error
!= PLAYER_ERROR_NOERROR
) ||
250 error
== PLAYER_ERROR_AUDIO
|| error
== PLAYER_ERROR_SYSTEM
||
251 playlist
->error_count
>= queue_length(&playlist
->queue
))
252 /* too many errors, or critical error: stop
254 playlist_stop(playlist
);
256 /* continue playback at the next song */
257 playlist_next(playlist
);
261 playlist_get_repeat(const struct playlist
*playlist
)
263 return playlist
->queue
.repeat
;
267 playlist_get_random(const struct playlist
*playlist
)
269 return playlist
->queue
.random
;
273 playlist_get_single(const struct playlist
*playlist
)
275 return playlist
->queue
.single
;
279 playlist_get_consume(const struct playlist
*playlist
)
281 return playlist
->queue
.consume
;
285 playlist_set_repeat(struct playlist
*playlist
, bool status
)
287 if (status
== playlist
->queue
.repeat
)
290 playlist
->queue
.repeat
= status
;
292 /* if the last song is currently being played, the "next song"
293 might change when repeat mode is toggled */
294 playlist_update_queued_song(playlist
,
295 playlist_get_queued_song(playlist
));
297 idle_add(IDLE_OPTIONS
);
301 playlist_order(struct playlist
*playlist
)
303 if (playlist
->current
>= 0)
304 /* update playlist.current, order==position now */
305 playlist
->current
= queue_order_to_position(&playlist
->queue
,
308 queue_restore_order(&playlist
->queue
);
312 playlist_set_single(struct playlist
*playlist
, bool status
)
314 if (status
== playlist
->queue
.single
)
317 playlist
->queue
.single
= status
;
319 /* if the last song is currently being played, the "next song"
320 might change when single mode is toggled */
321 playlist_update_queued_song(playlist
,
322 playlist_get_queued_song(playlist
));
324 idle_add(IDLE_OPTIONS
);
328 playlist_set_consume(struct playlist
*playlist
, bool status
)
330 if (status
== playlist
->queue
.consume
)
333 playlist
->queue
.consume
= status
;
334 idle_add(IDLE_OPTIONS
);
338 playlist_set_random(struct playlist
*playlist
, bool status
)
340 const struct song
*queued
;
342 if (status
== playlist
->queue
.random
)
345 queued
= playlist_get_queued_song(playlist
);
347 playlist
->queue
.random
= status
;
349 if (playlist
->queue
.random
) {
350 /* shuffle the queue order, but preserve
353 int current_position
=
354 playlist
->playing
&& playlist
->current
>= 0
355 ? (int)queue_order_to_position(&playlist
->queue
,
359 queue_shuffle_order(&playlist
->queue
);
361 if (current_position
>= 0) {
362 /* make sure the current song is the first in
363 the order list, so the whole rest of the
364 playlist is played after that */
365 unsigned current_order
=
366 queue_position_to_order(&playlist
->queue
,
368 queue_swap_order(&playlist
->queue
, 0, current_order
);
369 playlist
->current
= 0;
371 playlist
->current
= -1;
373 playlist_order(playlist
);
375 playlist_update_queued_song(playlist
, queued
);
377 idle_add(IDLE_OPTIONS
);
381 playlist_get_current_song(const struct playlist
*playlist
)
383 if (playlist
->current
>= 0)
384 return queue_order_to_position(&playlist
->queue
,
391 playlist_get_next_song(const struct playlist
*playlist
)
393 if (playlist
->current
>= 0)
395 if (playlist
->queue
.single
== 1)
397 if (playlist
->queue
.repeat
== 1)
398 return queue_order_to_position(&playlist
->queue
,
403 if (playlist
->current
+ 1 < (int)queue_length(&playlist
->queue
))
404 return queue_order_to_position(&playlist
->queue
,
405 playlist
->current
+ 1);
406 else if (playlist
->queue
.repeat
== 1)
407 return queue_order_to_position(&playlist
->queue
, 0);
414 playlist_get_version(const struct playlist
*playlist
)
416 return playlist
->queue
.version
;
420 playlist_get_length(const struct playlist
*playlist
)
422 return queue_length(&playlist
->queue
);
426 playlist_get_song_id(const struct playlist
*playlist
, unsigned song
)
428 return queue_position_to_id(&playlist
->queue
, song
);