Control.hxx 11.5 KB
Newer Older
1
/*
Max Kellermann's avatar
Max Kellermann committed
2
 * Copyright 2003-2017 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 "output/Client.hxx"
24
#include "AudioFormat.hxx"
25 26
#include "thread/Mutex.hxx"
#include "thread/Cond.hxx"
27
#include "thread/Thread.hxx"
28
#include "CrossFade.hxx"
29
#include "Chrono.hxx"
30 31
#include "ReplayGainConfig.hxx"
#include "ReplayGainMode.hxx"
32

33 34
#include <exception>

35
#include <stdint.h>
Warren Dukes's avatar
Warren Dukes committed
36

37
class PlayerListener;
38
class MultipleOutputs;
39
class DetachedSong;
40

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

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

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

61
	/** PlayerControl.next_song has been updated */
62
	QUEUE,
63 64

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

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

78 79
enum class PlayerError : uint8_t {
	NONE,
80 81 82 83

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

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

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

100
struct PlayerControl final : AudioOutputClient {
101 102
	PlayerListener &listener;

103 104
	MultipleOutputs &outputs;

105
	const unsigned buffer_chunks;
106

107
	const unsigned buffered_before_play;
108

109 110 111 112 113
	/**
	 * The "audio_output_format" setting.
	 */
	const AudioFormat configured_audio_format;

114 115 116 117
	/**
	 * The handle of the player thread.
	 */
	Thread thread;
118

119
	/**
120
	 * This lock protects #command, #state, #error, #tagged_song.
121
	 */
122
	mutable Mutex mutex;
123 124 125 126

	/**
	 * Trigger this object after you have modified #command.
	 */
127
	Cond cond;
128

129 130 131 132 133 134 135
	/**
	 * 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;

136 137
	PlayerCommand command = PlayerCommand::NONE;
	PlayerState state = PlayerState::STOP;
138

139
	PlayerError error_type = PlayerError::NONE;
140

141 142
	/**
	 * The error that occurred in the player thread.  This
143
	 * attribute is only valid if #error_type is not
144 145
	 * #PlayerError::NONE.  The object must be freed when this
	 * object transitions back to #PlayerError::NONE.
146
	 */
147
	std::exception_ptr error;
148

149
	/**
150 151 152
	 * 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).
153 154
	 * This shall be used by PlayerListener::OnPlayerTagModified()
	 * to update the current #DetachedSong in the queue.
155 156 157 158
	 *
	 * Protected by #mutex.  Set by the PlayerThread and consumed
	 * by the main thread.
	 */
159
	DetachedSong *tagged_song = nullptr;
160

161
	uint16_t bit_rate;
162
	AudioFormat audio_format;
163
	SignedSongTime total_time;
164
	SongTime elapsed_time;
165 166 167 168 169 170 171

	/**
	 * The next queued song.
	 *
	 * This is a duplicate, and must be freed when this attribute
	 * is cleared.
	 */
172
	DetachedSong *next_song = nullptr;
173

174
	SongTime seek_time;
175 176 177

	CrossFadeSettings cross_fade;

178
	const ReplayGainConfig replay_gain_config;
179 180
	ReplayGainMode replay_gain_mode = ReplayGainMode::OFF;

181
	double total_play_time = 0;
182 183 184 185 186 187 188 189

	/**
	 * 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.
	 */
190
	bool border_pause = false;
Warren Dukes's avatar
Warren Dukes committed
191

192 193
	PlayerControl(PlayerListener &_listener,
		      MultipleOutputs &_outputs,
194
		      unsigned buffer_chunks,
195
		      unsigned buffered_before_play,
196
		      AudioFormat _configured_audio_format,
197
		      const ReplayGainConfig &_replay_gain_config);
198
	~PlayerControl();
199

200 201 202 203 204 205
	/**
	 * Locks the object.
	 */
	void Lock() const {
		mutex.lock();
	}
206

207 208 209 210 211 212
	/**
	 * Unlocks the object.
	 */
	void Unlock() const {
		mutex.unlock();
	}
Warren Dukes's avatar
Warren Dukes committed
213

214 215 216 217 218 219 220
	/**
	 * Signals the object.  The object should be locked prior to
	 * calling this function.
	 */
	void Signal() {
		cond.signal();
	}
221

222 223 224 225 226
	/**
	 * Signals the object.  The object is temporarily locked by
	 * this function.
	 */
	void LockSignal() {
227
		const std::lock_guard<Mutex> protect(mutex);
228 229
		Signal();
	}
230

231 232 233 234 235 236
	/**
	 * 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() {
237
		assert(thread.IsInside());
238

239 240
		cond.wait(mutex);
	}
Warren Dukes's avatar
Warren Dukes committed
241

242 243 244 245 246 247
	/**
	 * Wake up the client waiting for command completion.
	 *
	 * Caller must lock the object.
	 */
	void ClientSignal() {
248
		assert(thread.IsInside());
249 250 251 252 253 254 255 256 257 258 259

		client_cond.signal();
	}

	/**
	 * The client calls this method to wait for command
	 * completion.
	 *
	 * Caller must lock the object.
	 */
	void ClientWait() {
260
		assert(!thread.IsInside());
261 262 263 264

		client_cond.wait(mutex);
	}

265 266 267 268 269 270 271 272
	/**
	 * 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() {
273
		assert(command != PlayerCommand::NONE);
274

275
		command = PlayerCommand::NONE;
276 277 278
		ClientSignal();
	}

279
	void LockCommandFinished() {
280
		const std::lock_guard<Mutex> protect(mutex);
281 282 283
		CommandFinished();
	}

284 285 286 287 288 289 290 291 292 293 294 295 296
	/**
	 * Checks if the size of the #MusicPipe is below the #threshold.  If
	 * not, it attempts to synchronize with all output threads, and waits
	 * until another #MusicChunk is finished.
	 *
	 * Caller must lock the mutex.
	 *
	 * @param threshold the maximum number of chunks in the pipe
	 * @return true if there are less than #threshold chunks in the pipe
	 */
	bool WaitOutputConsumed(unsigned threshold);

	bool LockWaitOutputConsumed(unsigned threshold) {
297
		const std::lock_guard<Mutex> protect(mutex);
298 299 300
		return WaitOutputConsumed(threshold);
	}

301 302 303 304 305 306 307 308
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() {
309
		while (command != PlayerCommand::NONE)
310 311 312 313 314 315 316 317 318 319
			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.
	 */
320
	void SynchronousCommand(PlayerCommand cmd) noexcept {
321
		assert(command == PlayerCommand::NONE);
322 323 324 325 326 327 328 329 330 331 332 333 334

		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.
	 */
335
	void LockSynchronousCommand(PlayerCommand cmd) noexcept {
336
		const std::lock_guard<Mutex> protect(mutex);
337 338 339 340
		SynchronousCommand(cmd);
	}

public:
341
	/**
342 343
	 * Throws std::runtime_error or #Error on error.
	 *
344 345 346
	 * @param song the song to be queued; the given instance will
	 * be owned and freed by the player
	 */
347
	void Play(DetachedSong *song);
348

349
	/**
350
	 * see PlayerCommand::CANCEL
351
	 */
352
	void LockCancel();
Warren Dukes's avatar
Warren Dukes committed
353

354
	void LockSetPause(bool pause_flag);
Warren Dukes's avatar
Warren Dukes committed
355

356 357 358
private:
	void PauseLocked();

359 360
	void ClearError() {
		error_type = PlayerError::NONE;
361
		error = std::exception_ptr();
362 363
	}

364
public:
365
	void LockPause();
Warren Dukes's avatar
Warren Dukes committed
366

367 368 369
	/**
	 * Set the player's #border_pause flag.
	 */
370
	void LockSetBorderPause(bool border_pause);
371

372 373 374 375 376 377 378
	bool ApplyBorderPause() {
		if (border_pause)
			state = PlayerState::PAUSE;
		return border_pause;
	}

	bool LockApplyBorderPause() {
379
		const std::lock_guard<Mutex> lock(mutex);
380 381 382
		return ApplyBorderPause();
	}

383
	void Kill();
Warren Dukes's avatar
Warren Dukes committed
384

385
	gcc_pure
386
	player_status LockGetStatus() noexcept;
Warren Dukes's avatar
Warren Dukes committed
387

388
	PlayerState GetState() const {
389 390
		return state;
	}
Warren Dukes's avatar
Warren Dukes committed
391

392 393 394 395 396
	/**
	 * Set the error.  Discards any previous error condition.
	 *
	 * Caller must lock the object.
	 *
397
	 * @param type the error type; must not be #PlayerError::NONE
398
	 */
399
	void SetError(PlayerError type, std::exception_ptr &&_error);
400

401 402 403 404 405 406 407 408 409 410 411 412
	/**
	 * Set the error and set state to PlayerState::PAUSE.
	 */
	void SetOutputError(std::exception_ptr &&_error) {
		SetError(PlayerError::OUTPUT, std::move(_error));

		/* pause: the user may resume playback as soon as an
		   audio output becomes available */
		state = PlayerState::PAUSE;
	}

	void LockSetOutputError(std::exception_ptr &&_error) {
413
		const std::lock_guard<Mutex> lock(mutex);
414 415 416
		SetOutputError(std::move(_error));
	}

417
	/**
418 419
	 * Checks whether an error has occurred, and if so, rethrows
	 * it.
420 421 422
	 *
	 * Caller must lock the object.
	 */
423
	void CheckRethrowError() const {
424
		if (error_type != PlayerError::NONE)
425
			std::rethrow_exception(error);
426
	}
427

428
	/**
429
	 * Like CheckRethrowError(), but locks and unlocks the object.
430
	 */
431
	void LockCheckRethrowError() const {
432
		const std::lock_guard<Mutex> protect(mutex);
433
		CheckRethrowError();
434 435
	}

436
	void LockClearError();
Warren Dukes's avatar
Warren Dukes committed
437

438
	PlayerError GetErrorType() const {
439 440 441
		return error_type;
	}

442 443
	/**
	 * Set the #tagged_song attribute to a newly allocated copy of
444
	 * the given #DetachedSong.  Locks and unlocks the object.
445
	 */
446
	void LockSetTaggedSong(const DetachedSong &song);
447 448 449 450 451 452 453 454

	void ClearTaggedSong();

	/**
	 * Read and clear the #tagged_song attribute.
	 *
	 * Caller must lock the object.
	 */
455 456
	DetachedSong *ReadTaggedSong() {
		DetachedSong *result = tagged_song;
457 458 459 460 461 462 463
		tagged_song = nullptr;
		return result;
	}

	/**
	 * Like ReadTaggedSong(), but locks and unlocks the object.
	 */
464
	DetachedSong *LockReadTaggedSong() {
465
		const std::lock_guard<Mutex> protect(mutex);
466
		return ReadTaggedSong();
467 468
	}

469
	void LockStop();
470

471
	void LockUpdateAudio();
472

473
private:
474
	void EnqueueSongLocked(DetachedSong *song) {
475 476 477 478
		assert(song != nullptr);
		assert(next_song == nullptr);

		next_song = song;
479
		seek_time = SongTime::zero();
480
		SynchronousCommand(PlayerCommand::QUEUE);
481 482
	}

483 484 485 486
	/**
	 * Throws std::runtime_error or #Error on error.
	 */
	void SeekLocked(DetachedSong *song, SongTime t);
487

488
public:
489 490 491 492
	/**
	 * @param song the song to be queued; the given instance will be owned
	 * and freed by the player
	 */
493
	void LockEnqueueSong(DetachedSong *song);
494 495 496 497

	/**
	 * Makes the player thread seek the specified song to a position.
	 *
498 499
	 * Throws std::runtime_error or #Error on error.
	 *
500 501 502
	 * @param song the song to be queued; the given instance will be owned
	 * and freed by the player
	 */
503
	void LockSeek(DetachedSong *song, SongTime t);
504 505 506 507

	void SetCrossFade(float cross_fade_seconds);

	float GetCrossFade() const {
508
		return cross_fade.duration;
509 510 511 512 513
	}

	void SetMixRampDb(float mixramp_db);

	float GetMixRampDb() const {
514
		return cross_fade.mixramp_db;
515 516 517 518 519
	}

	void SetMixRampDelay(float mixramp_delay_seconds);

	float GetMixRampDelay() const {
520
		return cross_fade.mixramp_delay;
521 522
	}

523
	void LockSetReplayGainMode(ReplayGainMode _mode) {
524
		const std::lock_guard<Mutex> protect(mutex);
525 526 527
		replay_gain_mode = _mode;
	}

528 529 530
	double GetTotalPlayTime() const {
		return total_play_time;
	}
531 532 533 534 535 536 537 538 539

	/* virtual methods from AudioOutputClient */
	void ChunksConsumed() override {
		LockSignal();
	}

	void ApplyEnabled() override {
		LockUpdateAudio();
	}
540
};
541

Warren Dukes's avatar
Warren Dukes committed
542
#endif