Queue.hxx 9.49 KB
Newer Older
1
/*
2
 * Copyright 2003-2018 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
#ifndef MPD_QUEUE_HXX
#define MPD_QUEUE_HXX
22

23
#include "util/Compiler.h"
24
#include "IdTable.hxx"
25
#include "SingleMode.hxx"
26
#include "util/LazyRandomEngine.hxx"
27

Max Kellermann's avatar
Max Kellermann committed
28 29
#include <algorithm>

30 31 32
#include <assert.h>
#include <stdint.h>

33
class DetachedSong;
34

35 36 37 38 39 40 41 42 43 44
/**
 * A queue of songs.  This is the backend of the playlist: it contains
 * an ordered list of songs.
 *
 * Songs can be addressed in three possible ways:
 *
 * - the position in the queue
 * - the unique id (which stays the same, regardless of moves)
 * - the order number (which only differs from "position" in random mode)
 */
45
struct Queue {
46
	/**
47
	 * reserve max_length * HASH_MULT elements in the id
48 49
	 * number space
	 */
50
	static constexpr unsigned HASH_MULT = 4;
51 52 53 54 55

	/**
	 * One element of the queue: basically a song plus some queue specific
	 * information attached.
	 */
56
	struct Item {
57
		DetachedSong *song;
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72

		/** the unique id of this item in the queue */
		unsigned id;

		/** when was this item last changed? */
		uint32_t version;

		/**
		 * The priority of this item, between 0 and 255.  High
		 * priority value means that this song gets played first in
		 * "random" mode.
		 */
		uint8_t priority;
	};

73
	/** configured maximum length of the queue */
74
	const unsigned max_length;
75 76

	/** number of songs in the queue */
77
	unsigned length = 0;
78 79

	/** the current version number */
80
	uint32_t version = 1;
81 82

	/** all songs in "position" order */
83
	Item *const items;
84 85

	/** map order numbers to positions */
86
	unsigned *const order;
87

Max Kellermann's avatar
Max Kellermann committed
88
	/** map song ids to positions */
89
	IdTable id_table;
90 91 92

	/** repeat playback when the end of the queue has been
	    reached? */
93
	bool repeat = false;
94

95
	/** play only current song. */
96
	SingleMode single = SingleMode::OFF;
97

98
	/** remove each played files. */
99
	bool consume = false;
100

101
	/** play back songs in random order? */
102
	bool random = false;
103 104

	/** random number generator for shuffle and random mode */
105
	LazyRandomEngine rand;
106

Max Kellermann's avatar
Max Kellermann committed
107
	explicit Queue(unsigned max_length) noexcept;
108 109 110 111 112

	/**
	 * Deinitializes a queue object.  It does not free the queue
	 * pointer itself.
	 */
Max Kellermann's avatar
Max Kellermann committed
113
	~Queue() noexcept;
114

115 116
	Queue(const Queue &) = delete;
	Queue &operator=(const Queue &) = delete;
117

Max Kellermann's avatar
Max Kellermann committed
118
	unsigned GetLength() const noexcept {
119
		assert(length <= max_length);
120

121 122
		return length;
	}
123

124 125 126
	/**
	 * Determine if the queue is empty, i.e. there are no songs.
	 */
Max Kellermann's avatar
Max Kellermann committed
127
	bool IsEmpty() const noexcept {
128 129
		return length == 0;
	}
130

131 132 133
	/**
	 * Determine if the maximum number of songs has been reached.
	 */
Max Kellermann's avatar
Max Kellermann committed
134
	bool IsFull() const noexcept {
135
		assert(length <= max_length);
136

137 138
		return length >= max_length;
	}
139

140 141 142
	/**
	 * Is that a valid position number?
	 */
Max Kellermann's avatar
Max Kellermann committed
143
	bool IsValidPosition(unsigned position) const noexcept {
144 145
		return position < length;
	}
146

147 148 149
	/**
	 * Is that a valid order number?
	 */
Max Kellermann's avatar
Max Kellermann committed
150
	bool IsValidOrder(unsigned _order) const noexcept {
151
		return _order < length;
152 153
	}

Max Kellermann's avatar
Max Kellermann committed
154
	int IdToPosition(unsigned id) const noexcept {
155
		return id_table.IdToPosition(id);
156
	}
157

Max Kellermann's avatar
Max Kellermann committed
158
	int PositionToId(unsigned position) const noexcept {
159
		assert(position < length);
160

161 162
		return items[position].id;
	}
163

164
	gcc_pure
165
	unsigned OrderToPosition(unsigned _order) const noexcept {
166
		assert(_order < length);
167

168 169
		return order[_order];
	}
170

171
	gcc_pure
172
	unsigned PositionToOrder(unsigned position) const noexcept {
173
		assert(position < length);
174

175 176
		for (unsigned i = 0;; ++i) {
			assert(i < length);
177

178 179 180 181
			if (order[i] == position)
				return i;
		}
	}
182

183
	gcc_pure
184
	uint8_t GetPriorityAtPosition(unsigned position) const noexcept {
185
		assert(position < length);
186

187 188
		return items[position].priority;
	}
189

Max Kellermann's avatar
Max Kellermann committed
190
	const Item &GetOrderItem(unsigned i) const noexcept {
191 192 193 194 195
		assert(IsValidOrder(i));

		return items[OrderToPosition(i)];
	}

Max Kellermann's avatar
Max Kellermann committed
196
	uint8_t GetOrderPriority(unsigned i) const noexcept {
197 198 199
		return GetOrderItem(i).priority;
	}

200 201 202
	/**
	 * Returns the song at the specified position.
	 */
Max Kellermann's avatar
Max Kellermann committed
203
	DetachedSong &Get(unsigned position) const noexcept {
204
		assert(position < length);
205

206
		return *items[position].song;
207
	}
208

209 210 211
	/**
	 * Returns the song at the specified order number.
	 */
Max Kellermann's avatar
Max Kellermann committed
212
	DetachedSong &GetOrder(unsigned _order) const noexcept {
213 214
		return Get(OrderToPosition(_order));
	}
215

216 217 218 219
	/**
	 * Is the song at the specified position newer than the specified
	 * version?
	 */
Max Kellermann's avatar
Max Kellermann committed
220 221
	bool IsNewerAtPosition(unsigned position,
			       uint32_t _version) const noexcept {
222
		assert(position < length);
223

224 225 226 227
		return _version > version ||
			items[position].version >= _version ||
			items[position].version == 0;
	}
228

229 230 231 232 233 234 235
	/**
	 * Returns the order number following the specified one.  This takes
	 * end of queue and "repeat" mode into account.
	 *
	 * @return the next order number, or -1 to stop playback
	 */
	gcc_pure
236
	int GetNextOrder(unsigned order) const noexcept;
237

238 239 240 241
	/**
	 * Increments the queue's version number.  This handles integer
	 * overflow well.
	 */
242
	void IncrementVersion() noexcept;
243

244 245 246 247 248
	/**
	 * Marks the specified song as "modified".  Call
	 * IncrementVersion() after all modifications have been made.
	 * number.
	 */
Max Kellermann's avatar
Max Kellermann committed
249
	void ModifyAtPosition(unsigned position) noexcept {
250 251 252 253 254
		assert(position < length);

		items[position].version = version;
	}

255
	/**
256 257
	 * Marks the specified song as "modified".  Call
	 * IncrementVersion() after all modifications have been made.
258 259
	 * number.
	 */
260
	void ModifyAtOrder(unsigned order) noexcept;
261

262 263 264 265 266
	/**
	 * Appends a song to the queue and returns its position.  Prior to
	 * that, the caller must check if the queue is already full.
	 *
	 * If a song is not in the database (determined by
267 268
	 * Song::IsInDatabase()), it is freed when removed from the
	 * queue.
269 270 271
	 *
	 * @param priority the priority of this new queue item
	 */
Max Kellermann's avatar
Max Kellermann committed
272
	unsigned Append(DetachedSong &&song, uint8_t priority) noexcept;
273

274 275 276
	/**
	 * Swaps two songs, addressed by their position.
	 */
277
	void SwapPositions(unsigned position1, unsigned position2) noexcept;
278

279 280 281
	/**
	 * Swaps two songs, addressed by their order number.
	 */
Max Kellermann's avatar
Max Kellermann committed
282
	void SwapOrders(unsigned order1, unsigned order2) noexcept {
Max Kellermann's avatar
Max Kellermann committed
283
		std::swap(order[order1], order[order2]);
284
	}
285

286 287
	/**
	 * Moves a song to a new position in the "order" list.
288 289
	 *
	 * @return to_order
290
	 */
291
	unsigned MoveOrder(unsigned from_order, unsigned to_order) noexcept;
292

293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310
	/**
	 * Moves a song to a new position in the "order" list before
	 * the given one.
	 *
	 * @return the new order number of the given "from" song
	 */
	unsigned MoveOrderBefore(unsigned from_order,
				 unsigned to_order) noexcept;

	/**
	 * Moves a song to a new position in the "order" list after
	 * the given one.
	 *
	 * @return the new order number of the given "from" song
	 */
	unsigned MoveOrderAfter(unsigned from_order,
				unsigned to_order) noexcept;

311 312 313
	/**
	 * Moves a song to a new position.
	 */
314
	void MovePostion(unsigned from, unsigned to) noexcept;
315 316 317 318

	/**
	 * Moves a range of songs to a new position.
	 */
319
	void MoveRange(unsigned start, unsigned end, unsigned to) noexcept;
320 321 322 323

	/**
	 * Removes a song from the playlist.
	 */
324
	void DeletePosition(unsigned position) noexcept;
325 326 327 328

	/**
	 * Removes all songs from the playlist.
	 */
329
	void Clear() noexcept;
330 331 332 333

	/**
	 * Initializes the "order" array, and restores "normal" order.
	 */
Max Kellermann's avatar
Max Kellermann committed
334
	void RestoreOrder() noexcept {
335 336 337 338
		for (unsigned i = 0; i < length; ++i)
			order[i] = i;
	}

339 340 341 342
	/**
	 * Shuffle the order of items in the specified range, ignoring
	 * their priorities.
	 */
Max Kellermann's avatar
Max Kellermann committed
343
	void ShuffleOrderRange(unsigned start, unsigned end) noexcept;
344

345 346 347 348
	/**
	 * Shuffle the order of items in the specified range, taking their
	 * priorities into account.
	 */
Max Kellermann's avatar
Max Kellermann committed
349 350
	void ShuffleOrderRangeWithPriority(unsigned start,
					   unsigned end) noexcept;
351 352 353 354 355

	/**
	 * Shuffles the virtual order of songs, but does not move them
	 * physically.  This is used in random mode.
	 */
Max Kellermann's avatar
Max Kellermann committed
356
	void ShuffleOrder() noexcept;
357

Max Kellermann's avatar
Max Kellermann committed
358
	void ShuffleOrderFirst(unsigned start, unsigned end) noexcept;
359

360
	/**
361 362 363 364
	 * Shuffles the virtual order of the last song in the
	 * specified (order) range; only songs which match this song's
	 * priority are considered.  This is used in random mode after
	 * a song has been appended by Append().
365
	 */
366
	void ShuffleOrderLastWithPriority(unsigned start, unsigned end) noexcept;
367 368 369 370 371

	/**
	 * Shuffles a (position) range in the queue.  The songs are physically
	 * shuffled, not by using the "order" mapping.
	 */
Max Kellermann's avatar
Max Kellermann committed
372
	void ShuffleRange(unsigned start, unsigned end) noexcept;
373

374
	bool SetPriority(unsigned position, uint8_t priority, int after_order,
Max Kellermann's avatar
Max Kellermann committed
375
			 bool reorder=true) noexcept;
376 377

	bool SetPriorityRange(unsigned start_position, unsigned end_position,
Max Kellermann's avatar
Max Kellermann committed
378
			      uint8_t priority, int after_order) noexcept;
379 380

private:
Max Kellermann's avatar
Max Kellermann committed
381
	void MoveItemTo(unsigned from, unsigned to) noexcept {
382 383 384 385
		unsigned from_id = items[from].id;

		items[to] = items[from];
		items[to].version = version;
386
		id_table.Move(from_id, to);
387 388 389 390 391 392 393 394
	}

	/**
	 * Find the first item that has this specified priority or
	 * higher.
	 */
	gcc_pure
	unsigned FindPriorityOrder(unsigned start_order, uint8_t priority,
395
				   unsigned exclude_order) const noexcept;
396 397 398

	gcc_pure
	unsigned CountSamePriority(unsigned start_order,
399
				   uint8_t priority) const noexcept;
400
};
401

402
#endif