Control.hxx 9.54 KB
Newer Older
1
/*
2
 * Copyright 2003-2016 The Music Player Daemon Project
3
 * http://www.musicpd.org
Warren Dukes's avatar
Warren Dukes committed
4 5 6 7 8 9 10 11 12 13
 *
 * 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.
Warren Dukes's avatar
Warren Dukes committed
18 19
 */

20 21
#ifndef MPD_PLAYER_CONTROL_HXX
#define MPD_PLAYER_CONTROL_HXX
Warren Dukes's avatar
Warren Dukes committed
22

23
#include "AudioFormat.hxx"
24 25
#include "thread/Mutex.hxx"
#include "thread/Cond.hxx"
26
#include "thread/Thread.hxx"
27
#include "CrossFade.hxx"
28
#include "Chrono.hxx"
29

30 31
#include <exception>

32
#include <stdint.h>
Warren Dukes's avatar
Warren Dukes committed
33

34
class Error;
35
class PlayerListener;
36
class MultipleOutputs;
37
class DetachedSong;
38

39 40 41 42
enum class PlayerState : uint8_t {
	STOP,
	PAUSE,
	PLAY
43
};
Warren Dukes's avatar
Warren Dukes committed
44

45 46 47 48 49 50 51
enum class PlayerCommand : uint8_t {
	NONE,
	EXIT,
	STOP,
	PAUSE,
	SEEK,
	CLOSE_AUDIO,
52

53
	/**
54
	 * At least one AudioOutput.enabled flag has been modified;
55 56
	 * commit those changes to the output threads.
	 */
57
	UPDATE_AUDIO,
58

59
	/** PlayerControl.next_song has been updated */
60
	QUEUE,
61 62

	/**
63
	 * cancel pre-decoding PlayerControl.next_song; if the player
64 65 66
	 * has already started playing this song, it will completely
	 * stop
	 */
67
	CANCEL,
68 69

	/**
70
	 * Refresh status information in the #PlayerControl struct,
71 72
	 * e.g. elapsed_time.
	 */
73
	REFRESH,
74 75
};

76 77
enum class PlayerError : uint8_t {
	NONE,
78 79 80 81

	/**
	 * The decoder has failed to decode the song.
	 */
82
	DECODER,
83 84 85 86

	/**
	 * The audio output has failed.
	 */
87
	OUTPUT,
88
};
Warren Dukes's avatar
Warren Dukes committed
89

90
struct player_status {
91
	PlayerState state;
92
	uint16_t bit_rate;
93
	AudioFormat audio_format;
94
	SignedSongTime total_time;
95
	SongTime elapsed_time;
96 97
};

98
struct PlayerControl {
99 100
	PlayerListener &listener;

101 102
	MultipleOutputs &outputs;

103
	const unsigned buffer_chunks;
104

105
	const unsigned buffered_before_play;
106

107 108 109 110
	/**
	 * The handle of the player thread.
	 */
	Thread thread;
111

112
	/**
113
	 * This lock protects #command, #state, #error, #tagged_song.
114
	 */
115
	mutable Mutex mutex;
116 117 118 119

	/**
	 * Trigger this object after you have modified #command.
	 */
120
	Cond cond;
121

122 123 124 125 126 127 128
	/**
	 * This object gets signalled when the player thread has
	 * finished the #command.  It wakes up the client that waits
	 * (i.e. the main thread).
	 */
	Cond client_cond;

129 130
	PlayerCommand command;
	PlayerState state;
131

132
	PlayerError error_type;
133

134 135
	/**
	 * The error that occurred in the player thread.  This
136
	 * attribute is only valid if #error_type is not
137 138
	 * #PlayerError::NONE.  The object must be freed when this
	 * object transitions back to #PlayerError::NONE.
139
	 */
140
	std::exception_ptr error;
141

142
	/**
143 144 145
	 * A copy of the current #DetachedSong after its tags have
	 * been updated by the decoder (for example, a radio stream
	 * that has sent a new tag after switching to the next song).
146 147
	 * This shall be used by PlayerListener::OnPlayerTagModified()
	 * to update the current #DetachedSong in the queue.
148 149 150 151
	 *
	 * Protected by #mutex.  Set by the PlayerThread and consumed
	 * by the main thread.
	 */
152
	DetachedSong *tagged_song;
153

154
	uint16_t bit_rate;
155
	AudioFormat audio_format;
156
	SignedSongTime total_time;
157
	SongTime elapsed_time;
158 159 160 161 162 163 164

	/**
	 * The next queued song.
	 *
	 * This is a duplicate, and must be freed when this attribute
	 * is cleared.
	 */
165
	DetachedSong *next_song;
166

167
	SongTime seek_time;
168 169 170

	CrossFadeSettings cross_fade;

171
	double total_play_time;
172 173 174 175 176 177 178 179 180

	/**
	 * If this flag is set, then the player will be auto-paused at
	 * the end of the song, before the next song starts to play.
	 *
	 * This is a copy of the queue's "single" flag most of the
	 * time.
	 */
	bool border_pause;
Warren Dukes's avatar
Warren Dukes committed
181

182 183
	PlayerControl(PlayerListener &_listener,
		      MultipleOutputs &_outputs,
184
		      unsigned buffer_chunks,
185 186
		      unsigned buffered_before_play);
	~PlayerControl();
187

188 189 190 191 192 193
	/**
	 * Locks the object.
	 */
	void Lock() const {
		mutex.lock();
	}
194

195 196 197 198 199 200
	/**
	 * Unlocks the object.
	 */
	void Unlock() const {
		mutex.unlock();
	}
Warren Dukes's avatar
Warren Dukes committed
201

202 203 204 205 206 207 208
	/**
	 * Signals the object.  The object should be locked prior to
	 * calling this function.
	 */
	void Signal() {
		cond.signal();
	}
209

210 211 212 213 214
	/**
	 * Signals the object.  The object is temporarily locked by
	 * this function.
	 */
	void LockSignal() {
215
		const ScopeLock protect(mutex);
216 217
		Signal();
	}
218

219 220 221 222 223 224
	/**
	 * Waits for a signal on the object.  This function is only
	 * valid in the player thread.  The object must be locked
	 * prior to calling this function.
	 */
	void Wait() {
225
		assert(thread.IsInside());
226

227 228
		cond.wait(mutex);
	}
Warren Dukes's avatar
Warren Dukes committed
229

230 231 232 233 234 235
	/**
	 * Wake up the client waiting for command completion.
	 *
	 * Caller must lock the object.
	 */
	void ClientSignal() {
236
		assert(thread.IsInside());
237 238 239 240 241 242 243 244 245 246 247

		client_cond.signal();
	}

	/**
	 * The client calls this method to wait for command
	 * completion.
	 *
	 * Caller must lock the object.
	 */
	void ClientWait() {
248
		assert(!thread.IsInside());
249 250 251 252

		client_cond.wait(mutex);
	}

253 254 255 256 257 258 259 260
	/**
	 * A command has been finished.  This method clears the
	 * command and signals the client.
	 *
	 * To be called from the player thread.  Caller must lock the
	 * object.
	 */
	void CommandFinished() {
261
		assert(command != PlayerCommand::NONE);
262

263
		command = PlayerCommand::NONE;
264 265 266
		ClientSignal();
	}

267
	void LockCommandFinished() {
268
		const ScopeLock protect(mutex);
269 270 271
		CommandFinished();
	}

272 273 274 275 276 277 278 279
private:
	/**
	 * Wait for the command to be finished by the player thread.
	 *
	 * To be called from the main thread.  Caller must lock the
	 * object.
	 */
	void WaitCommandLocked() {
280
		while (command != PlayerCommand::NONE)
281 282 283 284 285 286 287 288 289 290
			ClientWait();
	}

	/**
	 * Send a command to the player thread and synchronously wait
	 * for it to finish.
	 *
	 * To be called from the main thread.  Caller must lock the
	 * object.
	 */
291 292
	void SynchronousCommand(PlayerCommand cmd) {
		assert(command == PlayerCommand::NONE);
293 294 295 296 297 298 299 300 301 302 303 304 305

		command = cmd;
		Signal();
		WaitCommandLocked();
	}

	/**
	 * Send a command to the player thread and synchronously wait
	 * for it to finish.
	 *
	 * To be called from the main thread.  This method locks the
	 * object.
	 */
306
	void LockSynchronousCommand(PlayerCommand cmd) {
307
		const ScopeLock protect(mutex);
308 309 310 311
		SynchronousCommand(cmd);
	}

public:
312
	/**
313 314
	 * Throws std::runtime_error or #Error on error.
	 *
315 316 317
	 * @param song the song to be queued; the given instance will
	 * be owned and freed by the player
	 */
318
	void Play(DetachedSong *song);
319

320
	/**
321
	 * see PlayerCommand::CANCEL
322
	 */
323
	void LockCancel();
Warren Dukes's avatar
Warren Dukes committed
324

325
	void LockSetPause(bool pause_flag);
Warren Dukes's avatar
Warren Dukes committed
326

327 328 329
private:
	void PauseLocked();

330 331
	void ClearError() {
		error_type = PlayerError::NONE;
332
		error = std::exception_ptr();
333 334
	}

335
public:
336
	void LockPause();
Warren Dukes's avatar
Warren Dukes committed
337

338 339 340
	/**
	 * Set the player's #border_pause flag.
	 */
341
	void LockSetBorderPause(bool border_pause);
342

343
	void Kill();
Warren Dukes's avatar
Warren Dukes committed
344

345
	gcc_pure
346
	player_status LockGetStatus();
Warren Dukes's avatar
Warren Dukes committed
347

348
	PlayerState GetState() const {
349 350
		return state;
	}
Warren Dukes's avatar
Warren Dukes committed
351

352 353 354 355 356
	/**
	 * Set the error.  Discards any previous error condition.
	 *
	 * Caller must lock the object.
	 *
357
	 * @param type the error type; must not be #PlayerError::NONE
358
	 * @param error detailed error information; must be defined.
359
	 */
360
	void SetError(PlayerError type, Error &&error);
361
	void SetError(PlayerError type, std::exception_ptr &&_error);
362

363
	/**
364 365
	 * Checks whether an error has occurred, and if so, rethrows
	 * it.
366 367 368
	 *
	 * Caller must lock the object.
	 */
369
	void CheckRethrowError() const {
370
		if (error_type != PlayerError::NONE)
371
			std::rethrow_exception(error);
372
	}
373

374
	/**
375
	 * Like CheckRethrowError(), but locks and unlocks the object.
376
	 */
377
	void LockCheckRethrowError() const {
378
		const ScopeLock protect(mutex);
379
		CheckRethrowError();
380 381
	}

382
	void LockClearError();
Warren Dukes's avatar
Warren Dukes committed
383

384
	PlayerError GetErrorType() const {
385 386 387
		return error_type;
	}

388 389
	/**
	 * Set the #tagged_song attribute to a newly allocated copy of
390
	 * the given #DetachedSong.  Locks and unlocks the object.
391
	 */
392
	void LockSetTaggedSong(const DetachedSong &song);
393 394 395 396 397 398 399 400

	void ClearTaggedSong();

	/**
	 * Read and clear the #tagged_song attribute.
	 *
	 * Caller must lock the object.
	 */
401 402
	DetachedSong *ReadTaggedSong() {
		DetachedSong *result = tagged_song;
403 404 405 406 407 408 409
		tagged_song = nullptr;
		return result;
	}

	/**
	 * Like ReadTaggedSong(), but locks and unlocks the object.
	 */
410
	DetachedSong *LockReadTaggedSong() {
411 412
		const ScopeLock protect(mutex);
		return ReadTaggedSong();
413 414
	}

415
	void LockStop();
416

417
	void LockUpdateAudio();
418

419
private:
420
	void EnqueueSongLocked(DetachedSong *song) {
421 422 423 424
		assert(song != nullptr);
		assert(next_song == nullptr);

		next_song = song;
425
		seek_time = SongTime::zero();
426
		SynchronousCommand(PlayerCommand::QUEUE);
427 428
	}

429 430 431 432
	/**
	 * Throws std::runtime_error or #Error on error.
	 */
	void SeekLocked(DetachedSong *song, SongTime t);
433

434
public:
435 436 437 438
	/**
	 * @param song the song to be queued; the given instance will be owned
	 * and freed by the player
	 */
439
	void LockEnqueueSong(DetachedSong *song);
440 441 442 443

	/**
	 * Makes the player thread seek the specified song to a position.
	 *
444 445
	 * Throws std::runtime_error or #Error on error.
	 *
446 447 448
	 * @param song the song to be queued; the given instance will be owned
	 * and freed by the player
	 */
449
	void LockSeek(DetachedSong *song, SongTime t);
450 451 452 453

	void SetCrossFade(float cross_fade_seconds);

	float GetCrossFade() const {
454
		return cross_fade.duration;
455 456 457 458 459
	}

	void SetMixRampDb(float mixramp_db);

	float GetMixRampDb() const {
460
		return cross_fade.mixramp_db;
461 462 463 464 465
	}

	void SetMixRampDelay(float mixramp_delay_seconds);

	float GetMixRampDelay() const {
466
		return cross_fade.mixramp_delay;
467 468 469 470 471 472
	}

	double GetTotalPlayTime() const {
		return total_play_time;
	}
};
473

Warren Dukes's avatar
Warren Dukes committed
474
#endif