playlist_control.c 7.18 KB
Newer Older
1
/*
Max Kellermann's avatar
Max Kellermann committed
2
 * Copyright (C) 2003-2011 The Music Player Daemon Project
3 4 5 6 7 8 9 10 11 12 13
 * http://www.musicpd.org
 *
 * 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.
18 19 20 21 22 23 24
 */

/*
 * Functions for controlling playback on the playlist level.
 *
 */

25
#include "config.h"
26 27
#include "playlist_internal.h"
#include "player_control.h"
28
#include "song.h"
29
#include "idle.h"
30

31 32 33 34 35
#include <glib.h>

#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "playlist"

36 37
void
playlist_stop(struct playlist *playlist, struct player_control *pc)
38 39 40 41 42 43
{
	if (!playlist->playing)
		return;

	assert(playlist->current >= 0);

44
	g_debug("stop");
45
	pc_stop(pc);
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
	playlist->queued = -1;
	playlist->playing = false;

	if (playlist->queue.random) {
		/* shuffle the playlist, so the next playback will
		   result in a new random order */

		unsigned current_position =
			queue_order_to_position(&playlist->queue,
						playlist->current);

		queue_shuffle_order(&playlist->queue);

		/* make sure that "current" stays valid, and the next
		   "play" command plays the same song again */
		playlist->current =
			queue_position_to_order(&playlist->queue,
						current_position);
	}
}

67 68 69
enum playlist_result
playlist_play(struct playlist *playlist, struct player_control *pc,
	      int song)
70 71 72
{
	unsigned i = song;

73
	pc_clear_error(pc);
74 75 76 77 78 79 80 81 82 83

	if (song == -1) {
		/* play any song ("current" song, or the first song */

		if (queue_is_empty(&playlist->queue))
			return PLAYLIST_RESULT_SUCCESS;

		if (playlist->playing) {
			/* already playing: unpause playback, just in
			   case it was paused, and return */
84
			pc_set_pause(pc, false);
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
			return PLAYLIST_RESULT_SUCCESS;
		}

		/* select a song: "current" song, or the first one */
		i = playlist->current >= 0
			? playlist->current
			: 0;
	} else if (!queue_valid_position(&playlist->queue, song))
		return PLAYLIST_RESULT_BAD_RANGE;

	if (playlist->queue.random) {
		if (song >= 0)
			/* "i" is currently the song position (which
			   would be equal to the order number in
			   no-random mode); convert it to a order
			   number, because random mode is enabled */
			i = queue_position_to_order(&playlist->queue, song);

		if (!playlist->playing)
			playlist->current = 0;

		/* swap the new song with the previous "current" one,
		   so playback continues as planned */
		queue_swap_order(&playlist->queue,
				 i, playlist->current);
		i = playlist->current;
	}

	playlist->stop_on_error = false;
	playlist->error_count = 0;

116
	playlist_play_order(playlist, pc, i);
117 118 119 120
	return PLAYLIST_RESULT_SUCCESS;
}

enum playlist_result
121 122
playlist_play_id(struct playlist *playlist, struct player_control *pc,
		 int id)
123 124 125 126
{
	int song;

	if (id == -1) {
127
		return playlist_play(playlist, pc, id);
128 129 130 131 132 133
	}

	song = queue_id_to_position(&playlist->queue, id);
	if (song < 0)
		return PLAYLIST_RESULT_NO_SUCH_SONG;

134
	return playlist_play(playlist, pc, song);
135 136 137
}

void
138
playlist_next(struct playlist *playlist, struct player_control *pc)
139 140
{
	int next_order;
141
	int current;
142 143 144 145 146 147 148

	if (!playlist->playing)
		return;

	assert(!queue_is_empty(&playlist->queue));
	assert(queue_valid_order(&playlist->queue, playlist->current));

149
	current = playlist->current;
150 151 152 153 154 155 156
	playlist->stop_on_error = false;

	/* determine the next song from the queue's order list */

	next_order = queue_next_order(&playlist->queue, playlist->current);
	if (next_order < 0) {
		/* no song after this one: stop playback */
157
		playlist_stop(playlist, pc);
158 159 160

		/* reset "current song" */
		playlist->current = -1;
161
	}
162 163 164 165 166 167 168 169 170 171 172 173
	else
	{
		if (next_order == 0 && playlist->queue.random) {
			/* The queue told us that the next song is the first
			   song.  This means we are in repeat mode.  Shuffle
			   the queue order, so this time, the user hears the
			   songs in a different than before */
			assert(playlist->queue.repeat);

			queue_shuffle_order(&playlist->queue);

			/* note that playlist->current and playlist->queued are
174
			   now invalid, but playlist_play_order() will
175 176
			   discard them anyway */
		}
177

178
		playlist_play_order(playlist, pc, next_order);
179 180
	}

181 182
	/* Consume mode removes each played songs. */
	if(playlist->queue.consume)
183 184 185
		playlist_delete(playlist, pc,
				queue_order_to_position(&playlist->queue,
							current));
186 187
}

188 189
void
playlist_previous(struct playlist *playlist, struct player_control *pc)
190 191 192 193
{
	if (!playlist->playing)
		return;

194
	assert(queue_length(&playlist->queue) > 0);
195

196 197
	if (playlist->current > 0) {
		/* play the preceding song */
198
		playlist_play_order(playlist, pc,
199 200 201
				    playlist->current - 1);
	} else if (playlist->queue.repeat) {
		/* play the last song in "repeat" mode */
202
		playlist_play_order(playlist, pc,
203
				    queue_length(&playlist->queue) - 1);
204
	} else {
205 206
		/* re-start playing the current song if it's
		   the first one */
207
		playlist_play_order(playlist, pc, playlist->current);
208 209 210 211
	}
}

enum playlist_result
212 213
playlist_seek_song(struct playlist *playlist, struct player_control *pc,
		   unsigned song, float seek_time)
214 215 216
{
	const struct song *queued;
	unsigned i;
217
	bool success;
218 219 220 221 222 223 224 225 226 227 228

	if (!queue_valid_position(&playlist->queue, song))
		return PLAYLIST_RESULT_BAD_RANGE;

	queued = playlist_get_queued_song(playlist);

	if (playlist->queue.random)
		i = queue_position_to_order(&playlist->queue, song);
	else
		i = song;

229
	pc_clear_error(pc);
230 231 232 233
	playlist->stop_on_error = true;
	playlist->error_count = 0;

	if (!playlist->playing || (unsigned)playlist->current != i) {
234 235 236 237 238
		/* seeking is not within the current song - prepare
		   song change */

		playlist->playing = true;
		playlist->current = i;
239 240 241 242

		queued = NULL;
	}

243 244 245
	struct song *the_song =
		song_dup_detached(queue_get_order(&playlist->queue, i));
	success = pc_seek(pc, the_song, seek_time);
246
	if (!success) {
247
		playlist_update_queued_song(playlist, pc, queued);
248

249
		return PLAYLIST_RESULT_NOT_PLAYING;
250 251
	}

252
	playlist->queued = -1;
253
	playlist_update_queued_song(playlist, pc, NULL);
254 255 256 257 258

	return PLAYLIST_RESULT_SUCCESS;
}

enum playlist_result
259 260
playlist_seek_song_id(struct playlist *playlist, struct player_control *pc,
		      unsigned id, float seek_time)
261 262 263 264 265
{
	int song = queue_id_to_position(&playlist->queue, id);
	if (song < 0)
		return PLAYLIST_RESULT_NO_SUCH_SONG;

266
	return playlist_seek_song(playlist, pc, song, seek_time);
267
}
268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291

enum playlist_result
playlist_seek_current(struct playlist *playlist, struct player_control *pc,
		      float seek_time, bool relative)
{
	if (!playlist->playing)
		return PLAYLIST_RESULT_NOT_PLAYING;

	if (relative) {
		struct player_status status;
		pc_get_status(pc, &status);

		if (status.state != PLAYER_STATE_PLAY &&
		    status.state != PLAYER_STATE_PAUSE)
			return PLAYLIST_RESULT_NOT_PLAYING;

		seek_time += (int)status.elapsed_time;
	}

	if (seek_time < 0)
		seek_time = 0;

	return playlist_seek_song(playlist, pc, playlist->current, seek_time);
}