PlaylistControl.cxx 5.78 KB
Newer Older
1
/*
2
 * Copyright (C) 2003-2013 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
#include "Playlist.hxx"
27
#include "PlayerControl.hxx"
28
#include "song.h"
29

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

#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "playlist"

35
void
36
playlist::Stop(player_control &pc)
37
{
38
	if (!playing)
39 40
		return;

41
	assert(current >= 0);
42

43
	g_debug("stop");
44 45 46
	pc_stop(&pc);
	queued = -1;
	playing = false;
47

48
	if (queue.random) {
49 50 51
		/* shuffle the playlist, so the next playback will
		   result in a new random order */

52
		unsigned current_position = queue.OrderToPosition(current);
53

54
		queue.ShuffleOrder();
55 56 57

		/* make sure that "current" stays valid, and the next
		   "play" command plays the same song again */
58
		current = queue.PositionToOrder(current_position);
59 60 61
	}
}

62
enum playlist_result
63
playlist::PlayPosition(player_control &pc, int song)
64
{
65
	pc_clear_error(&pc);
66

67
	unsigned i = song;
68 69 70
	if (song == -1) {
		/* play any song ("current" song, or the first song */

71
		if (queue.IsEmpty())
72 73
			return PLAYLIST_RESULT_SUCCESS;

74
		if (playing) {
75 76
			/* already playing: unpause playback, just in
			   case it was paused, and return */
77
			pc_set_pause(&pc, false);
78 79 80 81
			return PLAYLIST_RESULT_SUCCESS;
		}

		/* select a song: "current" song, or the first one */
82 83
		i = current >= 0
			? current
84
			: 0;
85
	} else if (!queue.IsValidPosition(song))
86 87
		return PLAYLIST_RESULT_BAD_RANGE;

88
	if (queue.random) {
89 90 91 92 93
		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 */
94
			i = queue.PositionToOrder(song);
95

96 97
		if (!playing)
			current = 0;
98 99 100

		/* swap the new song with the previous "current" one,
		   so playback continues as planned */
101 102
		queue.SwapOrders(i, current);
		i = current;
103 104
	}

105 106
	stop_on_error = false;
	error_count = 0;
107

108
	PlayOrder(pc, i);
109 110 111 112
	return PLAYLIST_RESULT_SUCCESS;
}

enum playlist_result
113
playlist::PlayId(player_control &pc, int id)
114
{
115 116
	if (id == -1)
		return PlayPosition(pc, id);
117

118
	int song = queue.IdToPosition(id);
119 120 121
	if (song < 0)
		return PLAYLIST_RESULT_NO_SUCH_SONG;

122
	return PlayPosition(pc, song);
123 124 125
}

void
126
playlist::PlayNext(player_control &pc)
127
{
128
	if (!playing)
129 130
		return;

131 132
	assert(!queue.IsEmpty());
	assert(queue.IsValidOrder(current));
133

134 135
	const int old_current = current;
	stop_on_error = false;
136 137 138

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

139
	const int next_order = queue.GetNextOrder(current);
140 141
	if (next_order < 0) {
		/* no song after this one: stop playback */
142
		Stop(pc);
143 144

		/* reset "current song" */
145
		current = -1;
146
	}
147 148
	else
	{
149
		if (next_order == 0 && queue.random) {
150 151 152 153
			/* 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 */
154
			assert(queue.repeat);
155

156
			queue.ShuffleOrder();
157

158
			/* note that current and queued are
159
			   now invalid, but playlist_play_order() will
160 161
			   discard them anyway */
		}
162

163
		PlayOrder(pc, next_order);
164 165
	}

166
	/* Consume mode removes each played songs. */
167 168
	if (queue.consume)
		DeleteOrder(pc, old_current);
169 170
}

171
void
172
playlist::PlayPrevious(player_control &pc)
173
{
174
	if (!playing)
175 176
		return;

177
	assert(!queue.IsEmpty());
178

179 180
	int order;
	if (current > 0) {
181
		/* play the preceding song */
182 183
		order = current - 1;
	} else if (queue.repeat) {
184
		/* play the last song in "repeat" mode */
185
		order = queue.GetLength() - 1;
186
	} else {
187 188
		/* re-start playing the current song if it's
		   the first one */
189
		order = current;
190
	}
191 192

	PlayOrder(pc, order);
193 194 195
}

enum playlist_result
196
playlist::SeekSongPosition(player_control &pc, unsigned song, float seek_time)
197
{
198
	if (!queue.IsValidPosition(song))
199 200
		return PLAYLIST_RESULT_BAD_RANGE;

201
	const struct song *queued_song = GetQueuedSong();
202

203 204 205
	unsigned i = queue.random
		? queue.PositionToOrder(song)
		: song;
206

207 208 209
	pc_clear_error(&pc);
	stop_on_error = true;
	error_count = 0;
210

211
	if (!playing || (unsigned)current != i) {
212 213 214
		/* seeking is not within the current song - prepare
		   song change */

215 216
		playing = true;
		current = i;
217

218
		queued_song = nullptr;
219 220
	}

221 222 223
	struct song *the_song = song_dup_detached(queue.GetOrder(i));
	if (!pc_seek(&pc, the_song, seek_time)) {
		UpdateQueuedSong(pc, queued_song);
224

225
		return PLAYLIST_RESULT_NOT_PLAYING;
226 227
	}

228 229
	queued = -1;
	UpdateQueuedSong(pc, NULL);
230 231 232 233 234

	return PLAYLIST_RESULT_SUCCESS;
}

enum playlist_result
235
playlist::SeekSongId(player_control &pc, unsigned id, float seek_time)
236
{
237
	int song = queue.IdToPosition(id);
238 239 240
	if (song < 0)
		return PLAYLIST_RESULT_NO_SUCH_SONG;

241
	return SeekSongPosition(pc, song, seek_time);
242
}
243 244

enum playlist_result
245
playlist::SeekCurrent(player_control &pc, float seek_time, bool relative)
246
{
247
	if (!playing)
248 249 250 251
		return PLAYLIST_RESULT_NOT_PLAYING;

	if (relative) {
		struct player_status status;
252
		pc_get_status(&pc, &status);
253 254 255 256 257 258 259 260 261 262 263

		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;

264
	return SeekSongPosition(pc, current, seek_time);
265
}