playlist.c 10.5 KB
Newer Older
1
/*
Max Kellermann's avatar
Max Kellermann committed
2
 * Copyright (C) 2003-2011 The Music Player Daemon Project
3
 * http://www.musicpd.org
Warren Dukes's avatar
Warren Dukes committed
4 5 6 7 8 9 10 11 12 13
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
14 15 16 17
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Warren Dukes's avatar
Warren Dukes committed
18 19
 */

20
#include "config.h"
21
#include "playlist_internal.h"
22
#include "playlist_save.h"
23
#include "player_control.h"
Warren Dukes's avatar
Warren Dukes committed
24 25
#include "command.h"
#include "tag.h"
26
#include "song.h"
Warren Dukes's avatar
Warren Dukes committed
27
#include "conf.h"
28
#include "stored_playlist.h"
29
#include "idle.h"
Warren Dukes's avatar
Warren Dukes committed
30

31 32
#include <glib.h>

Max Kellermann's avatar
Max Kellermann committed
33
#include <assert.h>
34

35 36 37
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "playlist"

38 39
void
playlist_increment_version_all(struct playlist *playlist)
Avuton Olrich's avatar
Avuton Olrich committed
40
{
41
	queue_modify_all(&playlist->queue);
42
	idle_add(IDLE_PLAYLIST);
Warren Dukes's avatar
Warren Dukes committed
43 44
}

45 46
void
playlist_tag_changed(struct playlist *playlist)
47
{
48
	if (!playlist->playing)
49 50
		return;

51
	assert(playlist->current >= 0);
52

53
	queue_modify(&playlist->queue, playlist->current);
54
	idle_add(IDLE_PLAYLIST);
55 56
}

57 58
void
playlist_init(struct playlist *playlist)
Avuton Olrich's avatar
Avuton Olrich committed
59
{
60
	queue_init(&playlist->queue,
61 62
		   config_get_positive(CONF_MAX_PLAYLIST_LENGTH,
				       DEFAULT_PLAYLIST_MAX_LENGTH));
63

64 65
	playlist->queued = -1;
	playlist->current = -1;
Warren Dukes's avatar
Warren Dukes committed
66 67
}

68 69
void
playlist_finish(struct playlist *playlist)
70
{
71
	queue_finish(&playlist->queue);
72 73
}

74 75 76
/**
 * Queue a song, addressed by its order number.
 */
77
static void
78 79
playlist_queue_song_order(struct playlist *playlist, struct player_control *pc,
			  unsigned order)
Avuton Olrich's avatar
Avuton Olrich committed
80
{
81 82
	struct song *song;
	char *uri;
83

84
	assert(queue_valid_order(&playlist->queue, order));
85

86
	playlist->queued = order;
87

88
	song = queue_get_order(&playlist->queue, order);
89
	uri = song_get_uri(song);
90
	g_debug("queue song %i:\"%s\"", playlist->queued, uri);
91 92
	g_free(uri);

93
	pc_enqueue_song(pc, song);
94
}
95

96
/**
97
 * Called if the player thread has started playing the "queued" song.
98
 */
99
static void
100
playlist_song_started(struct playlist *playlist, struct player_control *pc)
Avuton Olrich's avatar
Avuton Olrich committed
101
{
102
	assert(pc->next_song == NULL);
103
	assert(playlist->queued >= -1);
104

105 106
	/* queued song has started: copy queued to current,
	   and notify the clients */
107

108 109 110
	int current = playlist->current;
	playlist->current = playlist->queued;
	playlist->queued = -1;
111

112
	/* Pause if we are in single mode. */
113
	if(playlist->queue.single && !playlist->queue.repeat) {
114
		pc_set_pause(pc, true);
Warren Dukes's avatar
Warren Dukes committed
115
	}
116 117

	if(playlist->queue.consume)
118 119 120
		playlist_delete(playlist, pc,
				queue_order_to_position(&playlist->queue,
							current));
121 122

	idle_add(IDLE_PLAYER);
Warren Dukes's avatar
Warren Dukes committed
123 124
}

125
const struct song *
126
playlist_get_queued_song(struct playlist *playlist)
Avuton Olrich's avatar
Avuton Olrich committed
127
{
128
	if (!playlist->playing || playlist->queued < 0)
129
		return NULL;
130

131
	return queue_get_order(&playlist->queue, playlist->queued);
132 133
}

134
void
135 136 137
playlist_update_queued_song(struct playlist *playlist,
			    struct player_control *pc,
			    const struct song *prev)
138 139 140 141
{
	int next_order;
	const struct song *next_song;

142
	if (!playlist->playing)
143 144
		return;

145 146
	assert(!queue_is_empty(&playlist->queue));
	assert((playlist->queued < 0) == (prev == NULL));
147

148 149
	next_order = playlist->current >= 0
		? queue_next_order(&playlist->queue, playlist->current)
150 151
		: 0;

152 153
	if (next_order == 0 && playlist->queue.random &&
	    !playlist->queue.single) {
154 155 156 157
		/* shuffle the song order again, so we get a different
		   order each time the playlist is played
		   completely */
		unsigned current_position =
158 159
			queue_order_to_position(&playlist->queue,
						playlist->current);
160

161
		queue_shuffle_order(&playlist->queue);
162

163
		/* make sure that the playlist->current still points to
164 165
		   the current song, after the song order has been
		   shuffled */
166 167
		playlist->current =
			queue_position_to_order(&playlist->queue,
168 169 170 171
						current_position);
	}

	if (next_order >= 0)
172
		next_song = queue_get_order(&playlist->queue, next_order);
173 174 175 176 177
	else
		next_song = NULL;

	if (prev != NULL && next_song != prev) {
		/* clear the currently queued song */
178
		pc_cancel(pc);
179
		playlist->queued = -1;
180
	}
Warren Dukes's avatar
Warren Dukes committed
181

182 183
	if (next_order >= 0) {
		if (next_song != prev)
184
			playlist_queue_song_order(playlist, pc, next_order);
185
		else
186
			playlist->queued = next_order;
187
	}
188 189
}

190
void
191 192
playlist_play_order(struct playlist *playlist, struct player_control *pc,
		    int orderNum)
Avuton Olrich's avatar
Avuton Olrich committed
193
{
194
	struct song *song;
195
	char *uri;
Eric Wong's avatar
Eric Wong committed
196

197 198
	playlist->playing = true;
	playlist->queued = -1;
Warren Dukes's avatar
Warren Dukes committed
199

200
	song = queue_get_order(&playlist->queue, orderNum);
201 202

	uri = song_get_uri(song);
203
	g_debug("play %i:\"%s\"", orderNum, uri);
204
	g_free(uri);
Warren Dukes's avatar
Warren Dukes committed
205

206
	pc_play(pc, song);
207
	playlist->current = orderNum;
Warren Dukes's avatar
Warren Dukes committed
208 209
}

210
static void
211
playlist_resume_playback(struct playlist *playlist, struct player_control *pc);
212

213 214 215 216
/**
 * This is the "PLAYLIST" event handler.  It is invoked by the player
 * thread whenever it requests a new queued song, or when it exits.
 */
217
void
218
playlist_sync(struct playlist *playlist, struct player_control *pc)
Avuton Olrich's avatar
Avuton Olrich committed
219
{
220
	if (!playlist->playing)
221 222
		/* this event has reached us out of sync: we aren't
		   playing anymore; ignore the event */
Avuton Olrich's avatar
Avuton Olrich committed
223
		return;
Warren Dukes's avatar
Warren Dukes committed
224

225 226 227 228
	player_lock(pc);
	enum player_state pc_state = pc_get_state(pc);
	const struct song *pc_next_song = pc->next_song;
	player_unlock(pc);
229 230

	if (pc_state == PLAYER_STATE_STOP)
231 232 233 234
		/* the player thread has stopped: check if playback
		   should be restarted with the next song.  That can
		   happen if the playlist isn't filling the queue fast
		   enough */
235
		playlist_resume_playback(playlist, pc);
236
	else {
237 238
		/* check if the player thread has already started
		   playing the queued song */
239
		if (pc_next_song == NULL && playlist->queued != -1)
240
			playlist_song_started(playlist, pc);
241 242 243

		/* make sure the queued song is always set (if
		   possible) */
244 245
		if (pc->next_song == NULL && playlist->queued < 0)
			playlist_update_queued_song(playlist, pc, NULL);
246
	}
Warren Dukes's avatar
Warren Dukes committed
247 248
}

249 250 251 252
/**
 * The player has stopped for some reason.  Check the error, and
 * decide whether to re-start playback
 */
253
static void
254
playlist_resume_playback(struct playlist *playlist, struct player_control *pc)
Avuton Olrich's avatar
Avuton Olrich committed
255
{
256
	enum player_error error;
Warren Dukes's avatar
Warren Dukes committed
257

258
	assert(playlist->playing);
259
	assert(pc_get_state(pc) == PLAYER_STATE_STOP);
Warren Dukes's avatar
Warren Dukes committed
260

261
	error = pc_get_error(pc);
262
	if (error == PLAYER_ERROR_NOERROR)
263
		playlist->error_count = 0;
264
	else
265
		++playlist->error_count;
266

267
	if ((playlist->stop_on_error && error != PLAYER_ERROR_NOERROR) ||
268
	    error == PLAYER_ERROR_AUDIO || error == PLAYER_ERROR_SYSTEM ||
269
	    playlist->error_count >= queue_length(&playlist->queue))
270 271
		/* too many errors, or critical error: stop
		   playback */
272
		playlist_stop(playlist, pc);
273
	else
274
		/* continue playback at the next song */
275
		playlist_next(playlist, pc);
Warren Dukes's avatar
Warren Dukes committed
276 277
}

278
bool
279
playlist_get_repeat(const struct playlist *playlist)
Avuton Olrich's avatar
Avuton Olrich committed
280
{
281
	return playlist->queue.repeat;
Warren Dukes's avatar
Warren Dukes committed
282 283
}

284
bool
285
playlist_get_random(const struct playlist *playlist)
Avuton Olrich's avatar
Avuton Olrich committed
286
{
287
	return playlist->queue.random;
Warren Dukes's avatar
Warren Dukes committed
288 289
}

290
bool
291
playlist_get_single(const struct playlist *playlist)
292
{
293
	return playlist->queue.single;
294 295
}

296
bool
297
playlist_get_consume(const struct playlist *playlist)
298 299 300 301
{
	return playlist->queue.consume;
}

302
void
303 304
playlist_set_repeat(struct playlist *playlist, struct player_control *pc,
		    bool status)
Avuton Olrich's avatar
Avuton Olrich committed
305
{
306
	if (status == playlist->queue.repeat)
307 308
		return;

309
	playlist->queue.repeat = status;
310

311 312
	/* if the last song is currently being played, the "next song"
	   might change when repeat mode is toggled */
313
	playlist_update_queued_song(playlist, pc,
314
				    playlist_get_queued_song(playlist));
315

316
	idle_add(IDLE_OPTIONS);
Warren Dukes's avatar
Warren Dukes committed
317 318
}

319 320
static void
playlist_order(struct playlist *playlist)
Avuton Olrich's avatar
Avuton Olrich committed
321
{
322
	if (playlist->current >= 0)
323
		/* update playlist.current, order==position now */
324 325
		playlist->current = queue_order_to_position(&playlist->queue,
							    playlist->current);
Warren Dukes's avatar
Warren Dukes committed
326

327
	queue_restore_order(&playlist->queue);
Warren Dukes's avatar
Warren Dukes committed
328 329
}

330
void
331 332
playlist_set_single(struct playlist *playlist, struct player_control *pc,
		    bool status)
333
{
334 335 336 337
	if (status == playlist->queue.single)
		return;

	playlist->queue.single = status;
338 339

	/* if the last song is currently being played, the "next song"
340
	   might change when single mode is toggled */
341
	playlist_update_queued_song(playlist, pc,
342 343 344 345 346
				    playlist_get_queued_song(playlist));

	idle_add(IDLE_OPTIONS);
}

347 348
void
playlist_set_consume(struct playlist *playlist, bool status)
349 350 351 352 353 354 355 356
{
	if (status == playlist->queue.consume)
		return;

	playlist->queue.consume = status;
	idle_add(IDLE_OPTIONS);
}

357
void
358 359
playlist_set_random(struct playlist *playlist, struct player_control *pc,
		    bool status)
Avuton Olrich's avatar
Avuton Olrich committed
360
{
361 362
	const struct song *queued;

363
	if (status == playlist->queue.random)
364
		return;
Warren Dukes's avatar
Warren Dukes committed
365

366
	queued = playlist_get_queued_song(playlist);
367

368
	playlist->queue.random = status;
Warren Dukes's avatar
Warren Dukes committed
369

370
	if (playlist->queue.random) {
371
		/* shuffle the queue order, but preserve
372
		   playlist->current */
373

374 375
		int current_position =
			playlist->playing && playlist->current >= 0
376 377
			? (int)queue_order_to_position(&playlist->queue,
						       playlist->current)
378 379
			: -1;

380
		queue_shuffle_order(&playlist->queue);
381 382

		if (current_position >= 0) {
383 384 385
			/* make sure the current song is the first in
			   the order list, so the whole rest of the
			   playlist is played after that */
386
			unsigned current_order =
387
				queue_position_to_order(&playlist->queue,
388
							current_position);
389 390
			queue_swap_order(&playlist->queue, 0, current_order);
			playlist->current = 0;
391 392
		} else
			playlist->current = -1;
393
	} else
394
		playlist_order(playlist);
395

396
	playlist_update_queued_song(playlist, pc, queued);
397

398
	idle_add(IDLE_OPTIONS);
Warren Dukes's avatar
Warren Dukes committed
399 400
}

401 402
int
playlist_get_current_song(const struct playlist *playlist)
Avuton Olrich's avatar
Avuton Olrich committed
403
{
404 405 406
	if (playlist->current >= 0)
		return queue_order_to_position(&playlist->queue,
					       playlist->current);
407

Avuton Olrich's avatar
Avuton Olrich committed
408
	return -1;
409 410
}

411 412
int
playlist_get_next_song(const struct playlist *playlist)
413 414 415
{
	if (playlist->current >= 0)
	{
416 417 418 419
		if (playlist->queue.single == 1 && playlist->queue.repeat == 1)
			return queue_order_to_position(&playlist->queue,
			                               playlist->current);
		else if (playlist->current + 1 < (int)queue_length(&playlist->queue))
420 421 422
			return queue_order_to_position(&playlist->queue,
					       playlist->current + 1);
		else if (playlist->queue.repeat == 1)
423
			return queue_order_to_position(&playlist->queue, 0);
424 425 426 427 428
	}

	return -1;
}

429
unsigned long
430
playlist_get_version(const struct playlist *playlist)
Avuton Olrich's avatar
Avuton Olrich committed
431
{
432
	return playlist->queue.version;
433 434
}

435
int
436
playlist_get_length(const struct playlist *playlist)
Avuton Olrich's avatar
Avuton Olrich committed
437
{
438
	return queue_length(&playlist->queue);
439 440
}

441
unsigned
442
playlist_get_song_id(const struct playlist *playlist, unsigned song)
Avuton Olrich's avatar
Avuton Olrich committed
443
{
444
	return queue_position_to_id(&playlist->queue, song);
445
}