PlayerControl.hxx 8.77 KB
Newer Older
1
/*
2
 * Copyright (C) 2003-2013 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_H
#define MPD_PLAYER_H
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 "util/Error.hxx"
28
#include "CrossFade.hxx"
29 30

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

32
struct Song;
33

34 35 36 37
enum class PlayerState : uint8_t {
	STOP,
	PAUSE,
	PLAY
38
};
Warren Dukes's avatar
Warren Dukes committed
39

40 41 42 43 44 45 46
enum class PlayerCommand : uint8_t {
	NONE,
	EXIT,
	STOP,
	PAUSE,
	SEEK,
	CLOSE_AUDIO,
47

48 49 50 51
	/**
	 * At least one audio_output.enabled flag has been modified;
	 * commit those changes to the output threads.
	 */
52
	UPDATE_AUDIO,
53

54
	/** PlayerControl.next_song has been updated */
55
	QUEUE,
56 57

	/**
58
	 * cancel pre-decoding PlayerControl.next_song; if the player
59 60 61
	 * has already started playing this song, it will completely
	 * stop
	 */
62
	CANCEL,
63 64

	/**
65
	 * Refresh status information in the #PlayerControl struct,
66 67
	 * e.g. elapsed_time.
	 */
68
	REFRESH,
69 70
};

71 72
enum class PlayerError : uint8_t {
	NONE,
73 74 75 76

	/**
	 * The decoder has failed to decode the song.
	 */
77
	DECODER,
78 79 80 81

	/**
	 * The audio output has failed.
	 */
82
	OUTPUT,
83
};
Warren Dukes's avatar
Warren Dukes committed
84

85
struct player_status {
86
	PlayerState state;
87
	uint16_t bit_rate;
88
	AudioFormat audio_format;
89 90 91 92
	float total_time;
	float elapsed_time;
};

93
struct PlayerControl {
94 95
	unsigned buffer_chunks;

96 97
	unsigned int buffered_before_play;

98 99 100 101
	/**
	 * The handle of the player thread.
	 */
	Thread thread;
102

103
	/**
104
	 * This lock protects #command, #state, #error, #tagged_song.
105
	 */
106
	mutable Mutex mutex;
107 108 109 110

	/**
	 * Trigger this object after you have modified #command.
	 */
111
	Cond cond;
112

113 114 115 116 117 118 119
	/**
	 * 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;

120 121
	PlayerCommand command;
	PlayerState state;
122

123
	PlayerError error_type;
124

125 126 127
	/**
	 * The error that occurred in the player thread.  This
	 * attribute is only valid if #error is not
128 129
	 * #PlayerError::NONE.  The object must be freed when this
	 * object transitions back to #PlayerError::NONE.
130
	 */
131
	Error error;
132

133 134 135 136 137 138 139 140 141 142 143 144
	/**
	 * A copy of the current #Song 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).  This
	 * shall be used by the GlobalEvents::TAG handler to update
	 * the current #Song in the queue.
	 *
	 * Protected by #mutex.  Set by the PlayerThread and consumed
	 * by the main thread.
	 */
	Song *tagged_song;

145
	uint16_t bit_rate;
146
	AudioFormat audio_format;
147 148
	float total_time;
	float elapsed_time;
149 150 151 152 153 154 155

	/**
	 * The next queued song.
	 *
	 * This is a duplicate, and must be freed when this attribute
	 * is cleared.
	 */
156
	Song *next_song;
157

158
	double seek_where;
159 160 161

	CrossFadeSettings cross_fade;

162
	double total_play_time;
163 164 165 166 167 168 169 170 171

	/**
	 * 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
172

173 174 175
	PlayerControl(unsigned buffer_chunks,
		      unsigned buffered_before_play);
	~PlayerControl();
176

177 178 179 180 181 182
	/**
	 * Locks the object.
	 */
	void Lock() const {
		mutex.lock();
	}
183

184 185 186 187 188 189
	/**
	 * Unlocks the object.
	 */
	void Unlock() const {
		mutex.unlock();
	}
Warren Dukes's avatar
Warren Dukes committed
190

191 192 193 194 195 196 197
	/**
	 * Signals the object.  The object should be locked prior to
	 * calling this function.
	 */
	void Signal() {
		cond.signal();
	}
198

199 200 201 202 203 204 205 206 207
	/**
	 * Signals the object.  The object is temporarily locked by
	 * this function.
	 */
	void LockSignal() {
		Lock();
		Signal();
		Unlock();
	}
208

209 210 211 212 213 214
	/**
	 * 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() {
215
		assert(thread.IsInside());
216

217 218
		cond.wait(mutex);
	}
Warren Dukes's avatar
Warren Dukes committed
219

220 221 222 223 224 225
	/**
	 * Wake up the client waiting for command completion.
	 *
	 * Caller must lock the object.
	 */
	void ClientSignal() {
226
		assert(thread.IsInside());
227 228 229 230 231 232 233 234 235 236 237

		client_cond.signal();
	}

	/**
	 * The client calls this method to wait for command
	 * completion.
	 *
	 * Caller must lock the object.
	 */
	void ClientWait() {
238
		assert(!thread.IsInside());
239 240 241 242

		client_cond.wait(mutex);
	}

243 244 245 246 247 248 249 250
	/**
	 * 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() {
251
		assert(command != PlayerCommand::NONE);
252

253
		command = PlayerCommand::NONE;
254 255 256
		ClientSignal();
	}

257 258 259 260 261 262 263 264
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() {
265
		while (command != PlayerCommand::NONE)
266 267 268 269 270 271 272 273 274 275
			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.
	 */
276 277
	void SynchronousCommand(PlayerCommand cmd) {
		assert(command == PlayerCommand::NONE);
278 279 280 281 282 283 284 285 286 287 288 289 290

		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.
	 */
291
	void LockSynchronousCommand(PlayerCommand cmd) {
292 293 294 295 296 297
		Lock();
		SynchronousCommand(cmd);
		Unlock();
	}

public:
298 299 300 301
	/**
	 * @param song the song to be queued; the given instance will
	 * be owned and freed by the player
	 */
302
	void Play(Song *song);
303

304
	/**
305
	 * see PlayerCommand::CANCEL
306 307
	 */
	void Cancel();
Warren Dukes's avatar
Warren Dukes committed
308

309
	void SetPause(bool pause_flag);
Warren Dukes's avatar
Warren Dukes committed
310

311 312 313 314
private:
	void PauseLocked();

public:
315
	void Pause();
Warren Dukes's avatar
Warren Dukes committed
316

317 318 319 320
	/**
	 * Set the player's #border_pause flag.
	 */
	void SetBorderPause(bool border_pause);
321

322
	void Kill();
Warren Dukes's avatar
Warren Dukes committed
323

324 325
	gcc_pure
	player_status GetStatus();
Warren Dukes's avatar
Warren Dukes committed
326

327
	PlayerState GetState() const {
328 329
		return state;
	}
Warren Dukes's avatar
Warren Dukes committed
330

331 332 333 334 335
	/**
	 * Set the error.  Discards any previous error condition.
	 *
	 * Caller must lock the object.
	 *
336
	 * @param type the error type; must not be #PlayerError::NONE
337
	 * @param error detailed error information; must be defined.
338
	 */
339
	void SetError(PlayerError type, Error &&error);
340

341 342 343 344 345 346 347 348 349 350 351 352 353
	/**
	 * Checks whether an error has occurred, and if so, returns a
	 * copy of the #Error object.
	 *
	 * Caller must lock the object.
	 */
	gcc_pure
	Error GetError() const {
		Error result;
		if (error_type != PlayerError::NONE)
			result.Set(error);
		return result;
	}
354

355
	/**
356
	 * Like GetError(), but locks and unlocks the object.
357
	 */
358 359 360 361 362 363 364 365 366
	gcc_pure
	Error LockGetError() const {
		Lock();
		Error result = GetError();
		Unlock();
		return result;
	}

	void ClearError();
Warren Dukes's avatar
Warren Dukes committed
367

368
	PlayerError GetErrorType() const {
369 370 371
		return error_type;
	}

372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400
	/**
	 * Set the #tagged_song attribute to a newly allocated copy of
	 * the given #Song.  Locks and unlocks the object.
	 */
	void LockSetTaggedSong(const Song &song);

	void ClearTaggedSong();

	/**
	 * Read and clear the #tagged_song attribute.
	 *
	 * Caller must lock the object.
	 */
	Song *ReadTaggedSong() {
		Song *result = tagged_song;
		tagged_song = nullptr;
		return result;
	}

	/**
	 * Like ReadTaggedSong(), but locks and unlocks the object.
	 */
	Song *LockReadTaggedSong() {
		Lock();
		Song *result = ReadTaggedSong();
		Unlock();
		return result;
	}

401 402 403 404
	void Stop();

	void UpdateAudio();

405 406 407 408 409 410
private:
	void EnqueueSongLocked(Song *song) {
		assert(song != nullptr);
		assert(next_song == nullptr);

		next_song = song;
411
		SynchronousCommand(PlayerCommand::QUEUE);
412 413 414
	}

public:
415 416 417 418
	/**
	 * @param song the song to be queued; the given instance will be owned
	 * and freed by the player
	 */
419
	void EnqueueSong(Song *song);
420 421 422 423 424 425 426 427 428

	/**
	 * Makes the player thread seek the specified song to a position.
	 *
	 * @param song the song to be queued; the given instance will be owned
	 * and freed by the player
	 * @return true on success, false on failure (e.g. if MPD isn't
	 * playing currently)
	 */
429
	bool Seek(Song *song, float seek_time);
430 431 432 433

	void SetCrossFade(float cross_fade_seconds);

	float GetCrossFade() const {
434
		return cross_fade.duration;
435 436 437 438 439
	}

	void SetMixRampDb(float mixramp_db);

	float GetMixRampDb() const {
440
		return cross_fade.mixramp_db;
441 442 443 444 445
	}

	void SetMixRampDelay(float mixramp_delay_seconds);

	float GetMixRampDelay() const {
446
		return cross_fade.mixramp_delay;
447 448 449 450 451 452
	}

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

Warren Dukes's avatar
Warren Dukes committed
454
#endif