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 !playlist
->queue
.single
) {
144 /* shuffle the song order again, so we get a different
145 order each time the playlist is played
147 unsigned current_position
=
148 queue_order_to_position(&playlist
->queue
,
151 queue_shuffle_order(&playlist
->queue
);
153 /* make sure that the playlist->current still points to
154 the current song, after the song order has been
157 queue_position_to_order(&playlist
->queue
,
162 next_song
= queue_get_order(&playlist
->queue
, next_order
);
166 if (prev
!= NULL
&& next_song
!= prev
) {
167 /* clear the currently queued song */
169 playlist
->queued
= -1;
172 if (next_order
>= 0) {
173 if (next_song
!= prev
)
174 playlist_queue_song_order(playlist
, next_order
);
176 playlist
->queued
= next_order
;
181 playlist_play_order(struct playlist
*playlist
, int orderNum
)
186 playlist
->playing
= true;
187 playlist
->queued
= -1;
189 song
= queue_get_order(&playlist
->queue
, orderNum
);
191 uri
= song_get_uri(song
);
192 g_debug("play %i:\"%s\"", orderNum
, uri
);
196 playlist
->current
= orderNum
;
200 playlist_resume_playback(struct playlist
*playlist
);
203 * This is the "PLAYLIST" event handler. It is invoked by the player
204 * thread whenever it requests a new queued song, or when it exits.
207 playlist_sync(struct playlist
*playlist
)
209 if (!playlist
->playing
)
210 /* this event has reached us out of sync: we aren't
211 playing anymore; ignore the event */
214 if (pc_get_state() == PLAYER_STATE_STOP
)
215 /* the player thread has stopped: check if playback
216 should be restarted with the next song. That can
217 happen if the playlist isn't filling the queue fast
219 playlist_resume_playback(playlist
);
221 /* check if the player thread has already started
222 playing the queued song */
223 playlist_sync_with_queue(playlist
);
225 /* make sure the queued song is always set (if
227 if (pc
.next_song
== NULL
)
228 playlist_update_queued_song(playlist
, NULL
);
233 * The player has stopped for some reason. Check the error, and
234 * decide whether to re-start playback
237 playlist_resume_playback(struct playlist
*playlist
)
239 enum player_error error
;
241 assert(playlist
->playing
);
242 assert(pc_get_state() == PLAYER_STATE_STOP
);
244 error
= pc_get_error();
245 if (error
== PLAYER_ERROR_NOERROR
)
246 playlist
->error_count
= 0;
248 ++playlist
->error_count
;
250 if ((playlist
->stop_on_error
&& error
!= PLAYER_ERROR_NOERROR
) ||
251 error
== PLAYER_ERROR_AUDIO
|| error
== PLAYER_ERROR_SYSTEM
||
252 playlist
->error_count
>= queue_length(&playlist
->queue
))
253 /* too many errors, or critical error: stop
255 playlist_stop(playlist
);
257 /* continue playback at the next song */
258 playlist_next(playlist
);
262 playlist_get_repeat(const struct playlist
*playlist
)
264 return playlist
->queue
.repeat
;
268 playlist_get_random(const struct playlist
*playlist
)
270 return playlist
->queue
.random
;
274 playlist_get_single(const struct playlist
*playlist
)
276 return playlist
->queue
.single
;
280 playlist_get_consume(const struct playlist
*playlist
)
282 return playlist
->queue
.consume
;
286 playlist_set_repeat(struct playlist
*playlist
, bool status
)
288 if (status
== playlist
->queue
.repeat
)
291 playlist
->queue
.repeat
= status
;
293 /* if the last song is currently being played, the "next song"
294 might change when repeat mode is toggled */
295 playlist_update_queued_song(playlist
,
296 playlist_get_queued_song(playlist
));
298 idle_add(IDLE_OPTIONS
);
302 playlist_order(struct playlist
*playlist
)
304 if (playlist
->current
>= 0)
305 /* update playlist.current, order==position now */
306 playlist
->current
= queue_order_to_position(&playlist
->queue
,
309 queue_restore_order(&playlist
->queue
);
313 playlist_set_single(struct playlist
*playlist
, bool status
)
315 if (status
== playlist
->queue
.single
)
318 playlist
->queue
.single
= status
;
320 /* if the last song is currently being played, the "next song"
321 might change when single mode is toggled */
322 playlist_update_queued_song(playlist
,
323 playlist_get_queued_song(playlist
));
325 idle_add(IDLE_OPTIONS
);
329 playlist_set_consume(struct playlist
*playlist
, bool status
)
331 if (status
== playlist
->queue
.consume
)
334 playlist
->queue
.consume
= status
;
335 idle_add(IDLE_OPTIONS
);
339 playlist_set_random(struct playlist
*playlist
, bool status
)
341 const struct song
*queued
;
343 if (status
== playlist
->queue
.random
)
346 queued
= playlist_get_queued_song(playlist
);
348 playlist
->queue
.random
= status
;
350 if (playlist
->queue
.random
) {
351 /* shuffle the queue order, but preserve
354 int current_position
=
355 playlist
->playing
&& playlist
->current
>= 0
356 ? (int)queue_order_to_position(&playlist
->queue
,
360 queue_shuffle_order(&playlist
->queue
);
362 if (current_position
>= 0) {
363 /* make sure the current song is the first in
364 the order list, so the whole rest of the
365 playlist is played after that */
366 unsigned current_order
=
367 queue_position_to_order(&playlist
->queue
,
369 queue_swap_order(&playlist
->queue
, 0, current_order
);
370 playlist
->current
= 0;
372 playlist
->current
= -1;
374 playlist_order(playlist
);
376 playlist_update_queued_song(playlist
, queued
);
378 idle_add(IDLE_OPTIONS
);
382 playlist_get_current_song(const struct playlist
*playlist
)
384 if (playlist
->current
>= 0)
385 return queue_order_to_position(&playlist
->queue
,
392 playlist_get_next_song(const struct playlist
*playlist
)
394 if (playlist
->current
>= 0)
396 if (playlist
->queue
.single
== 1)
398 if (playlist
->queue
.repeat
== 1)
399 return queue_order_to_position(&playlist
->queue
,
404 if (playlist
->current
+ 1 < (int)queue_length(&playlist
->queue
))
405 return queue_order_to_position(&playlist
->queue
,
406 playlist
->current
+ 1);
407 else if (playlist
->queue
.repeat
== 1)
408 return queue_order_to_position(&playlist
->queue
, 0);
415 playlist_get_version(const struct playlist
*playlist
)
417 return playlist
->queue
.version
;
421 playlist_get_length(const struct playlist
*playlist
)
423 return queue_length(&playlist
->queue
);
427 playlist_get_song_id(const struct playlist
*playlist
, unsigned song
)
429 return queue_position_to_id(&playlist
->queue
, song
);