PlaylistControl.cxx 5.68 KB
Newer Older
1
/*
Max Kellermann's avatar
Max Kellermann committed
2
 * Copyright (C) 2003-2014 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 "DetachedSong.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 DetachedSong *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
	if (!pc.Seek(new DetachedSong(queue.GetOrder(i)), seek_time)) {
219
		UpdateQueuedSong(pc, queued_song);
220

221
		return PlaylistResult::NOT_PLAYING;
222 223
	}

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

227
	return PlaylistResult::SUCCESS;
228 229
}

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

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

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

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

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

		seek_time += (int)status.elapsed_time;
	}

	if (seek_time < 0)
		seek_time = 0;

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