PlaylistControl.cxx 5.96 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 PlayOrder() 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
Max Kellermann's avatar
Max Kellermann committed
193
playlist::SeekSongOrder(PlayerControl &pc, unsigned i, SongTime seek_time)
194
{
195
	assert(queue.IsValidOrder(i));
196

197
	const DetachedSong *queued_song = GetQueuedSong();
198

199
	pc.ClearError();
200 201
	stop_on_error = true;
	error_count = 0;
202

203
	if (!playing || (unsigned)current != i) {
204 205 206
		/* seeking is not within the current song - prepare
		   song change */

207 208
		playing = true;
		current = i;
209

210
		queued_song = nullptr;
211 212
	}

213
	if (!pc.Seek(new DetachedSong(queue.GetOrder(i)), seek_time)) {
214
		UpdateQueuedSong(pc, queued_song);
215

216
		return PlaylistResult::NOT_PLAYING;
217 218
	}

219
	queued = -1;
220
	UpdateQueuedSong(pc, nullptr);
221

222
	return PlaylistResult::SUCCESS;
223 224
}

225
PlaylistResult
Max Kellermann's avatar
Max Kellermann committed
226 227
playlist::SeekSongPosition(PlayerControl &pc, unsigned song,
			   SongTime seek_time)
228 229 230 231 232 233 234 235 236 237 238
{
	if (!queue.IsValidPosition(song))
		return PlaylistResult::BAD_RANGE;

	unsigned i = queue.random
		? queue.PositionToOrder(song)
		: song;

	return SeekSongOrder(pc, i, seek_time);
}

239
PlaylistResult
240
playlist::SeekSongId(PlayerControl &pc, unsigned id, SongTime seek_time)
241
{
242
	int song = queue.IdToPosition(id);
243
	if (song < 0)
244
		return PlaylistResult::NO_SUCH_SONG;
245

246
	return SeekSongPosition(pc, song, seek_time);
247
}
248

249
PlaylistResult
250 251
playlist::SeekCurrent(PlayerControl &pc,
		      SignedSongTime seek_time, bool relative)
252
{
253
	if (!playing)
254
		return PlaylistResult::NOT_PLAYING;
255 256

	if (relative) {
257
		const auto status = pc.GetStatus();
258

259 260
		if (status.state != PlayerState::PLAY &&
		    status.state != PlayerState::PAUSE)
261
			return PlaylistResult::NOT_PLAYING;
262

263
		seek_time += status.elapsed_time;
264 265
		if (seek_time.IsNegative())
			seek_time = SignedSongTime::zero();
266 267
	}

Max Kellermann's avatar
Max Kellermann committed
268 269
	if (seek_time.IsNegative())
		seek_time = SignedSongTime::zero();
270

Max Kellermann's avatar
Max Kellermann committed
271
	return SeekSongOrder(pc, current, SongTime(seek_time));
272
}