PlaylistControl.cxx 5.69 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 "PlaylistError.hxx"
28
#include "PlayerControl.hxx"
29
#include "Song.hxx"
30
#include "Log.hxx"
31

32
void
33
playlist::Stop(PlayerControl &pc)
34
{
35
	if (!playing)
36 37
		return;

38
	assert(current >= 0);
39

40
	FormatDebug(playlist_domain, "stop");
41
	pc.Stop();
42 43
	queued = -1;
	playing = false;
44

45
	if (queue.random) {
46 47 48
		/* shuffle the playlist, so the next playback will
		   result in a new random order */

49
		unsigned current_position = queue.OrderToPosition(current);
50

51
		queue.ShuffleOrder();
52 53 54

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

59
PlaylistResult
60
playlist::PlayPosition(PlayerControl &pc, int song)
61
{
62
	pc.ClearError();
63

64
	unsigned i = song;
65 66 67
	if (song == -1) {
		/* play any song ("current" song, or the first song */

68
		if (queue.IsEmpty())
69
			return PlaylistResult::SUCCESS;
70

71
		if (playing) {
72 73
			/* already playing: unpause playback, just in
			   case it was paused, and return */
74
			pc.SetPause(false);
75
			return PlaylistResult::SUCCESS;
76 77 78
		}

		/* select a song: "current" song, or the first one */
79 80
		i = current >= 0
			? current
81
			: 0;
82
	} else if (!queue.IsValidPosition(song))
83
		return PlaylistResult::BAD_RANGE;
84

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

93 94
		if (!playing)
			current = 0;
95 96 97

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

102 103
	stop_on_error = false;
	error_count = 0;
104

105
	PlayOrder(pc, i);
106
	return PlaylistResult::SUCCESS;
107 108
}

109
PlaylistResult
110
playlist::PlayId(PlayerControl &pc, int id)
111
{
112 113
	if (id == -1)
		return PlayPosition(pc, id);
114

115
	int song = queue.IdToPosition(id);
116
	if (song < 0)
117
		return PlaylistResult::NO_SUCH_SONG;
118

119
	return PlayPosition(pc, song);
120 121 122
}

void
123
playlist::PlayNext(PlayerControl &pc)
124
{
125
	if (!playing)
126 127
		return;

128 129
	assert(!queue.IsEmpty());
	assert(queue.IsValidOrder(current));
130

131 132
	const int old_current = current;
	stop_on_error = false;
133 134 135

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

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

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

153
			queue.ShuffleOrder();
154

155
			/* note that current and queued are
156
			   now invalid, but playlist_play_order() will
157 158
			   discard them anyway */
		}
159

160
		PlayOrder(pc, next_order);
161 162
	}

163
	/* Consume mode removes each played songs. */
164 165
	if (queue.consume)
		DeleteOrder(pc, old_current);
166 167
}

168
void
169
playlist::PlayPrevious(PlayerControl &pc)
170
{
171
	if (!playing)
172 173
		return;

174
	assert(!queue.IsEmpty());
175

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

	PlayOrder(pc, order);
190 191
}

192
PlaylistResult
193
playlist::SeekSongPosition(PlayerControl &pc, unsigned song, float seek_time)
194
{
195
	if (!queue.IsValidPosition(song))
196
		return PlaylistResult::BAD_RANGE;
197

198
	const Song *queued_song = GetQueuedSong();
199

200 201 202
	unsigned i = queue.random
		? queue.PositionToOrder(song)
		: song;
203

204
	pc.ClearError();
205 206
	stop_on_error = true;
	error_count = 0;
207

208
	if (!playing || (unsigned)current != i) {
209 210 211
		/* seeking is not within the current song - prepare
		   song change */

212 213
		playing = true;
		current = i;
214

215
		queued_song = nullptr;
216 217
	}

218
	Song *the_song = queue.GetOrder(i).DupDetached();
219
	if (!pc.Seek(the_song, seek_time)) {
220
		UpdateQueuedSong(pc, queued_song);
221

222
		return PlaylistResult::NOT_PLAYING;
223 224
	}

225
	queued = -1;
226
	UpdateQueuedSong(pc, nullptr);
227

228
	return PlaylistResult::SUCCESS;
229 230
}

231
PlaylistResult
232
playlist::SeekSongId(PlayerControl &pc, unsigned id, float seek_time)
233
{
234
	int song = queue.IdToPosition(id);
235
	if (song < 0)
236
		return PlaylistResult::NO_SUCH_SONG;
237

238
	return SeekSongPosition(pc, song, seek_time);
239
}
240

241
PlaylistResult
242
playlist::SeekCurrent(PlayerControl &pc, float seek_time, bool relative)
243
{
244
	if (!playing)
245
		return PlaylistResult::NOT_PLAYING;
246 247

	if (relative) {
248
		const auto status = pc.GetStatus();
249

250 251
		if (status.state != PlayerState::PLAY &&
		    status.state != PlayerState::PAUSE)
252
			return PlaylistResult::NOT_PLAYING;
253 254 255 256 257 258 259

		seek_time += (int)status.elapsed_time;
	}

	if (seek_time < 0)
		seek_time = 0;

260
	return SeekSongPosition(pc, current, seek_time);
261
}