playlist.c 9.89 KB
Newer Older
1 2 3
/*
 * Copyright (C) 2003-2009 The Music Player Daemon Project
 * 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 "playlist_internal.h"
21
#include "playlist_save.h"
22
#include "player_control.h"
Warren Dukes's avatar
Warren Dukes committed
23 24
#include "command.h"
#include "tag.h"
25
#include "song.h"
Warren Dukes's avatar
Warren Dukes committed
26
#include "conf.h"
27
#include "stored_playlist.h"
28
#include "idle.h"
Warren Dukes's avatar
Warren Dukes committed
29

30 31
#include <glib.h>

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

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

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

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

49
	assert(playlist->current >= 0);
50

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

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

62 63
	playlist->queued = -1;
	playlist->current = -1;
64 65

	playlist->prev_elapsed = g_timer_new();
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

	g_timer_destroy(playlist->prev_elapsed);
74 75
}

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

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

87
	playlist->queued = order;
88

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

	queueSong(song);
}
96

97 98 99 100
/**
 * Check if the player thread has already started playing the "queued"
 * song.
 */
101
static void syncPlaylistWithQueue(struct playlist *playlist)
Avuton Olrich's avatar
Avuton Olrich committed
102
{
103
	if (pc.next_song == NULL && playlist->queued != -1) {
104 105 106
		/* queued song has started: copy queued to current,
		   and notify the clients */

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

111 112 113
		if(playlist->queue.consume)
			deleteFromPlaylist(playlist, queue_order_to_position(&playlist->queue, current));

114
		idle_add(IDLE_PLAYER);
Warren Dukes's avatar
Warren Dukes committed
115 116 117
	}
}

118
const struct song *
119
playlist_get_queued_song(struct playlist *playlist)
Avuton Olrich's avatar
Avuton Olrich committed
120
{
121
	if (!playlist->playing || playlist->queued < 0)
122
		return NULL;
123

124
	return queue_get_order(&playlist->queue, playlist->queued);
125 126
}

127
void
128
playlist_update_queued_song(struct playlist *playlist, const struct song *prev)
129 130 131 132
{
	int next_order;
	const struct song *next_song;

133
	if (!playlist->playing)
134 135
		return;

136 137
	assert(!queue_is_empty(&playlist->queue));
	assert((playlist->queued < 0) == (prev == NULL));
138

139 140
	next_order = playlist->current >= 0
		? queue_next_order(&playlist->queue, playlist->current)
141 142
		: 0;

143
	if (next_order == 0 && playlist->queue.random) {
144 145 146 147
		/* shuffle the song order again, so we get a different
		   order each time the playlist is played
		   completely */
		unsigned current_position =
148 149
			queue_order_to_position(&playlist->queue,
						playlist->current);
150

151
		queue_shuffle_order(&playlist->queue);
152

153
		/* make sure that the playlist->current still points to
154 155
		   the current song, after the song order has been
		   shuffled */
156 157
		playlist->current =
			queue_position_to_order(&playlist->queue,
158 159 160 161
						current_position);
	}

	if (next_order >= 0)
162
		next_song = queue_get_order(&playlist->queue, next_order);
163 164 165 166 167 168
	else
		next_song = NULL;

	if (prev != NULL && next_song != prev) {
		/* clear the currently queued song */
		pc_cancel();
169
		playlist->queued = -1;
170
	}
Warren Dukes's avatar
Warren Dukes committed
171

172 173
	if (next_order >= 0) {
		if (next_song != prev)
174
			playlist_queue_song_order(playlist, next_order);
175
		else
176
			playlist->queued = next_order;
177
	}
178 179
}

180
void
181
playPlaylistOrderNumber(struct playlist *playlist, int orderNum)
Avuton Olrich's avatar
Avuton Olrich committed
182
{
183
	struct song *song;
184
	char *uri;
Eric Wong's avatar
Eric Wong committed
185

186 187
	playlist->playing = true;
	playlist->queued = -1;
Warren Dukes's avatar
Warren Dukes committed
188

189
	song = queue_get_order(&playlist->queue, orderNum);
190 191

	uri = song_get_uri(song);
192
	g_debug("play %i:\"%s\"", orderNum, uri);
193
	g_free(uri);
Warren Dukes's avatar
Warren Dukes committed
194

195
	playerPlay(song);
196
	playlist->current = orderNum;
Warren Dukes's avatar
Warren Dukes committed
197 198
}

199 200
static void
playPlaylistIfPlayerStopped(struct playlist *playlist);
201

202 203 204 205
/**
 * This is the "PLAYLIST" event handler.  It is invoked by the player
 * thread whenever it requests a new queued song, or when it exits.
 */
206
void syncPlayerAndPlaylist(struct playlist *playlist)
Avuton Olrich's avatar
Avuton Olrich committed
207
{
208
	if (!playlist->playing)
209 210
		/* this event has reached us out of sync: we aren't
		   playing anymore; ignore the event */
Avuton Olrich's avatar
Avuton Olrich committed
211
		return;
Warren Dukes's avatar
Warren Dukes committed
212

Avuton Olrich's avatar
Avuton Olrich committed
213
	if (getPlayerState() == PLAYER_STATE_STOP)
214 215 216 217
		/* 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 */
218
		playPlaylistIfPlayerStopped(playlist);
219
	else {
220 221
		/* check if the player thread has already started
		   playing the queued song */
222
		syncPlaylistWithQueue(playlist);
223 224 225

		/* make sure the queued song is always set (if
		   possible) */
226
		if (pc.next_song == NULL)
227
			playlist_update_queued_song(playlist, NULL);
228
	}
Warren Dukes's avatar
Warren Dukes committed
229 230
}

231 232 233 234
/**
 * The player has stopped for some reason.  Check the error, and
 * decide whether to re-start playback
 */
235 236
static void
playPlaylistIfPlayerStopped(struct playlist *playlist)
Avuton Olrich's avatar
Avuton Olrich committed
237
{
238
	enum player_error error;
Warren Dukes's avatar
Warren Dukes committed
239

240
	assert(playlist->playing);
241
	assert(getPlayerState() == PLAYER_STATE_STOP);
Warren Dukes's avatar
Warren Dukes committed
242

243 244
	error = getPlayerError();
	if (error == PLAYER_ERROR_NOERROR)
245
		playlist->error_count = 0;
246
	else
247
		++playlist->error_count;
248

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 253
		/* too many errors, or critical error: stop
		   playback */
254
		stopPlaylist(playlist);
255
	else
256
		/* continue playback at the next song */
257
		nextSongInPlaylist(playlist);
Warren Dukes's avatar
Warren Dukes committed
258 259
}

260
bool
261
getPlaylistRepeatStatus(const struct playlist *playlist)
Avuton Olrich's avatar
Avuton Olrich committed
262
{
263
	return playlist->queue.repeat;
Warren Dukes's avatar
Warren Dukes committed
264 265
}

266
bool
267
getPlaylistRandomStatus(const struct playlist *playlist)
Avuton Olrich's avatar
Avuton Olrich committed
268
{
269
	return playlist->queue.random;
Warren Dukes's avatar
Warren Dukes committed
270 271
}

272
bool
273
getPlaylistSingleStatus(const struct playlist *playlist)
274
{
275
	return playlist->queue.single;
276 277
}

278 279 280 281 282 283
bool
getPlaylistConsumeStatus(const struct playlist *playlist)
{
	return playlist->queue.consume;
}

284
void setPlaylistRepeatStatus(struct playlist *playlist, bool status)
Avuton Olrich's avatar
Avuton Olrich committed
285
{
286
	if (status == playlist->queue.repeat)
287 288
		return;

289
	playlist->queue.repeat = status;
290

291 292
	/* if the last song is currently being played, the "next song"
	   might change when repeat mode is toggled */
293 294
	playlist_update_queued_song(playlist,
				    playlist_get_queued_song(playlist));
295

296
	idle_add(IDLE_OPTIONS);
Warren Dukes's avatar
Warren Dukes committed
297 298
}

299
static void orderPlaylist(struct playlist *playlist)
Avuton Olrich's avatar
Avuton Olrich committed
300
{
301
	if (playlist->current >= 0)
302
		/* update playlist.current, order==position now */
303 304
		playlist->current = queue_order_to_position(&playlist->queue,
							    playlist->current);
Warren Dukes's avatar
Warren Dukes committed
305

306
	queue_restore_order(&playlist->queue);
Warren Dukes's avatar
Warren Dukes committed
307 308
}

309
void setPlaylistSingleStatus(struct playlist *playlist, bool status)
310
{
311 312 313 314
	if (status == playlist->queue.single)
		return;

	playlist->queue.single = status;
315 316

	/* if the last song is currently being played, the "next song"
317
	   might change when single mode is toggled */
318 319 320 321 322 323
	playlist_update_queued_song(playlist,
				    playlist_get_queued_song(playlist));

	idle_add(IDLE_OPTIONS);
}

324 325 326 327 328 329 330 331 332
void setPlaylistConsumeStatus(struct playlist *playlist, bool status)
{
	if (status == playlist->queue.consume)
		return;

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

333
void setPlaylistRandomStatus(struct playlist *playlist, bool status)
Avuton Olrich's avatar
Avuton Olrich committed
334
{
335 336
	const struct song *queued;

337
	if (status == playlist->queue.random)
338
		return;
Warren Dukes's avatar
Warren Dukes committed
339

340
	queued = playlist_get_queued_song(playlist);
341

342
	playlist->queue.random = status;
Warren Dukes's avatar
Warren Dukes committed
343

344
	if (playlist->queue.random) {
345
		/* shuffle the queue order, but preserve
346
		   playlist->current */
347

348 349
		int current_position =
			playlist->playing && playlist->current >= 0
350 351
			? (int)queue_order_to_position(&playlist->queue,
						       playlist->current)
352 353
			: -1;

354
		queue_shuffle_order(&playlist->queue);
355 356

		if (current_position >= 0) {
357 358 359
			/* make sure the current song is the first in
			   the order list, so the whole rest of the
			   playlist is played after that */
360
			unsigned current_order =
361
				queue_position_to_order(&playlist->queue,
362
							current_position);
363 364
			queue_swap_order(&playlist->queue, 0, current_order);
			playlist->current = 0;
365 366
		} else
			playlist->current = -1;
367
	} else
368
		orderPlaylist(playlist);
369

370
	playlist_update_queued_song(playlist, queued);
371

372
	idle_add(IDLE_OPTIONS);
Warren Dukes's avatar
Warren Dukes committed
373 374
}

375
int getPlaylistCurrentSong(const struct playlist *playlist)
Avuton Olrich's avatar
Avuton Olrich committed
376
{
377 378 379
	if (playlist->current >= 0)
		return queue_order_to_position(&playlist->queue,
					       playlist->current);
380

Avuton Olrich's avatar
Avuton Olrich committed
381
	return -1;
382 383
}

384 385 386 387
int getPlaylistNextSong(const struct playlist *playlist)
{
	if (playlist->current >= 0)
	{
388 389 390 391 392 393 394 395
		if (playlist->queue.single == 1)
		{
			if (playlist->queue.repeat == 1)
				return queue_order_to_position(&playlist->queue,
			                              playlist->current);
			else
				return -1;
		}
396
		if (playlist->current + 1 < (int)queue_length(&playlist->queue))
397 398 399
			return queue_order_to_position(&playlist->queue,
					       playlist->current + 1);
		else if (playlist->queue.repeat == 1)
400
			return queue_order_to_position(&playlist->queue, 0);
401 402 403 404 405
	}

	return -1;
}

406
unsigned long
407
getPlaylistVersion(const struct playlist *playlist)
Avuton Olrich's avatar
Avuton Olrich committed
408
{
409
	return playlist->queue.version;
410 411
}

412
int
413
getPlaylistLength(const struct playlist *playlist)
Avuton Olrich's avatar
Avuton Olrich committed
414
{
415
	return queue_length(&playlist->queue);
416 417
}

418
unsigned
419
getPlaylistSongId(const struct playlist *playlist, unsigned song)
Avuton Olrich's avatar
Avuton Olrich committed
420
{
421
	return queue_position_to_id(&playlist->queue, song);
422
}