Queue.hxx 9.45 KB
Newer Older
1
/*
Max Kellermann's avatar
Max Kellermann committed
2
 * Copyright 2003-2017 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 "Compiler.h"
24
#include "IdTable.hxx"
25
#include "util/LazyRandomEngine.hxx"
26

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

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

32
class DetachedSong;
33

34 35 36 37 38 39 40 41 42 43
/**
 * 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)
 */
44
struct Queue {
45
	/**
46
	 * reserve max_length * HASH_MULT elements in the id
47 48
	 * number space
	 */
49
	static constexpr unsigned HASH_MULT = 4;
50 51 52 53 54

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

		/** 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;
	};

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

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

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

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

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

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

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

94
	/** play only current song. */
95
	bool single = false;
96

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

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

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

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

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

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

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

120 121
		return length;
	}
122

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

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

136 137
		return length >= max_length;
	}
138

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

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

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

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

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

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

167 168
		return order[_order];
	}
169

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

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

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

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

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

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

		return items[OrderToPosition(i)];
	}

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

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

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

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

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

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

228 229 230 231 232 233 234
	/**
	 * 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
235
	int GetNextOrder(unsigned order) const noexcept;
236

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

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

		items[position].version = version;
	}

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

261 262 263 264 265
	/**
	 * 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
266 267
	 * Song::IsInDatabase()), it is freed when removed from the
	 * queue.
268 269 270
	 *
	 * @param priority the priority of this new queue item
	 */
Max Kellermann's avatar
Max Kellermann committed
271
	unsigned Append(DetachedSong &&song, uint8_t priority) noexcept;
272

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

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

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

292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309
	/**
	 * 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;

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

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

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

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

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

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

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

	/**
	 * 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
355
	void ShuffleOrder() noexcept;
356

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

359
	/**
360 361 362 363
	 * 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().
364
	 */
365
	void ShuffleOrderLastWithPriority(unsigned start, unsigned end) noexcept;
366 367 368 369 370

	/**
	 * 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
371
	void ShuffleRange(unsigned start, unsigned end) noexcept;
372

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

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

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

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

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

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

401
#endif