1 /* 2 * Copyright 2003-2021 The Music Player Daemon Project 3 * http://www.musicpd.org 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License along 16 * with this program; if not, write to the Free Software Foundation, Inc., 17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 */ 19 20 #ifndef MPD_PLAYER_CONTROL_HXX 21 #define MPD_PLAYER_CONTROL_HXX 22 23 #include "output/Client.hxx" 24 #include "pcm/AudioFormat.hxx" 25 #include "thread/Mutex.hxx" 26 #include "thread/Cond.hxx" 27 #include "thread/Thread.hxx" 28 #include "CrossFade.hxx" 29 #include "Chrono.hxx" 30 #include "ReplayGainConfig.hxx" 31 #include "ReplayGainMode.hxx" 32 #include "MusicChunkPtr.hxx" 33 34 #include <cstdint> 35 #include <exception> 36 #include <memory> 37 38 struct Tag; 39 class PlayerListener; 40 class PlayerOutputs; 41 class InputCacheManager; 42 class DetachedSong; 43 44 enum class PlayerState : uint8_t { 45 STOP, 46 PAUSE, 47 PLAY 48 }; 49 50 enum class PlayerCommand : uint8_t { 51 NONE, 52 EXIT, 53 STOP, 54 PAUSE, 55 56 /** 57 * Seek to a certain position in the specified song. This 58 * command can also be used to change the current song or 59 * start playback. It "finishes" immediately, but 60 * PlayerControl::seeking will be set until seeking really 61 * completes (or fails). 62 */ 63 SEEK, 64 65 CLOSE_AUDIO, 66 67 /** 68 * At least one AudioOutput.enabled flag has been modified; 69 * commit those changes to the output threads. 70 */ 71 UPDATE_AUDIO, 72 73 /** PlayerControl.next_song has been updated */ 74 QUEUE, 75 76 /** 77 * cancel pre-decoding PlayerControl.next_song; if the player 78 * has already started playing this song, it will completely 79 * stop 80 */ 81 CANCEL, 82 83 /** 84 * Refresh status information in the #PlayerControl struct, 85 * e.g. elapsed_time. 86 */ 87 REFRESH, 88 }; 89 90 enum class PlayerError : uint8_t { 91 NONE, 92 93 /** 94 * The decoder has failed to decode the song. 95 */ 96 DECODER, 97 98 /** 99 * The audio output has failed. 100 */ 101 OUTPUT, 102 }; 103 104 struct PlayerStatus { 105 PlayerState state; 106 uint16_t bit_rate; 107 AudioFormat audio_format; 108 SignedSongTime total_time; 109 SongTime elapsed_time; 110 }; 111 112 class PlayerControl final : public AudioOutputClient { 113 friend class Player; 114 115 PlayerListener &listener; 116 117 PlayerOutputs &outputs; 118 119 InputCacheManager *const input_cache; 120 121 const unsigned buffer_chunks; 122 123 /** 124 * The "audio_output_format" setting. 125 */ 126 const AudioFormat configured_audio_format; 127 128 /** 129 * The handle of the player thread. 130 */ 131 Thread thread; 132 133 /** 134 * This lock protects #command, #state, #error, #tagged_song. 135 */ 136 mutable Mutex mutex; 137 138 /** 139 * Trigger this object after you have modified #command. 140 */ 141 Cond cond; 142 143 /** 144 * This object gets signalled when the player thread has 145 * finished the #command. It wakes up the client that waits 146 * (i.e. the main thread). 147 */ 148 Cond client_cond; 149 150 /** 151 * The error that occurred in the player thread. This 152 * attribute is only valid if #error_type is not 153 * #PlayerError::NONE. The object must be freed when this 154 * object transitions back to #PlayerError::NONE. 155 */ 156 std::exception_ptr error; 157 158 /** 159 * The next queued song. 160 * 161 * This is a duplicate, and must be freed when this attribute 162 * is cleared. 163 */ 164 std::unique_ptr<DetachedSong> next_song; 165 166 /** 167 * A copy of the current #DetachedSong after its tags have 168 * been updated by the decoder (for example, a radio stream 169 * that has sent a new tag after switching to the next song). 170 * This shall be used by PlayerListener::OnPlayerTagModified() 171 * to update the current #DetachedSong in the queue. 172 * 173 * Protected by #mutex. Set by the PlayerThread and consumed 174 * by the main thread. 175 */ 176 std::unique_ptr<DetachedSong> tagged_song; 177 178 PlayerCommand command = PlayerCommand::NONE; 179 PlayerState state = PlayerState::STOP; 180 181 PlayerError error_type = PlayerError::NONE; 182 183 ReplayGainMode replay_gain_mode = ReplayGainMode::OFF; 184 185 /** 186 * Is the player currently busy with the SEEK command? 187 */ 188 bool seeking = false; 189 190 /** 191 * If this flag is set, then the player will be auto-paused at 192 * the end of the song, before the next song starts to play. 193 * 194 * This is a copy of the queue's "single" flag most of the 195 * time. 196 */ 197 bool border_pause = false; 198 199 /** 200 * If this flag is set, then the player thread is currently 201 * occupied and will not be able to respond quickly to 202 * commands (e.g. waiting for the decoder thread to finish 203 * seeking). This is used to skip #PlayerCommand::REFRESH to 204 * avoid blocking the main thread. 205 */ 206 bool occupied = false; 207 208 struct ScopeOccupied { 209 PlayerControl &pc; 210 ScopeOccupiedPlayerControl::ScopeOccupied211 explicit ScopeOccupied(PlayerControl &_pc) noexcept:pc(_pc) { 212 assert(!pc.occupied); 213 pc.occupied = true; 214 } 215 ~ScopeOccupiedPlayerControl::ScopeOccupied216 ~ScopeOccupied() noexcept { 217 assert(pc.occupied); 218 pc.occupied = false; 219 } 220 }; 221 222 AudioFormat audio_format; 223 uint16_t bit_rate; 224 225 SignedSongTime total_time; 226 SongTime elapsed_time; 227 228 SongTime seek_time; 229 230 CrossFadeSettings cross_fade; 231 232 const ReplayGainConfig replay_gain_config; 233 234 FloatDuration total_play_time = FloatDuration::zero(); 235 236 public: 237 PlayerControl(PlayerListener &_listener, 238 PlayerOutputs &_outputs, 239 InputCacheManager *_input_cache, 240 unsigned buffer_chunks, 241 AudioFormat _configured_audio_format, 242 const ReplayGainConfig &_replay_gain_config) noexcept; 243 ~PlayerControl() noexcept; 244 245 void Kill() noexcept; 246 247 /** 248 * Like CheckRethrowError(), but locks and unlocks the object. 249 */ LockCheckRethrowError() const250 void LockCheckRethrowError() const { 251 const std::scoped_lock<Mutex> protect(mutex); 252 CheckRethrowError(); 253 } 254 255 void LockClearError() noexcept; 256 GetErrorType() const257 PlayerError GetErrorType() const noexcept { 258 return error_type; 259 } 260 261 void LockUpdateAudio() noexcept; 262 263 /** 264 * Throws on error. 265 * 266 * @param song the song to be queued 267 */ 268 void Play(std::unique_ptr<DetachedSong> song); 269 270 /** 271 * @param song the song to be queued; the given instance will be owned 272 * and freed by the player 273 */ 274 void LockEnqueueSong(std::unique_ptr<DetachedSong> song) noexcept; 275 276 /** 277 * Makes the player thread seek the specified song to a position. 278 * 279 * Throws on error. 280 * 281 * @param song the song to be queued; the given instance will be owned 282 * and freed by the player 283 */ 284 void LockSeek(std::unique_ptr<DetachedSong> song, SongTime t); 285 286 void LockStop() noexcept; 287 288 /** 289 * see PlayerCommand::CANCEL 290 */ 291 void LockCancel() noexcept; 292 293 void LockSetPause(bool pause_flag) noexcept; 294 295 void LockPause() noexcept; 296 297 /** 298 * Set the player's #border_pause flag. 299 */ 300 void LockSetBorderPause(bool border_pause) noexcept; 301 void SetCrossFade(FloatDuration duration) noexcept; 302 GetCrossFade() const303 auto GetCrossFade() const noexcept { 304 return cross_fade.duration; 305 } 306 307 void SetMixRampDb(float mixramp_db) noexcept; 308 GetMixRampDb() const309 float GetMixRampDb() const noexcept { 310 return cross_fade.mixramp_db; 311 } 312 313 void SetMixRampDelay(FloatDuration mixramp_delay) noexcept; 314 GetMixRampDelay() const315 auto GetMixRampDelay() const noexcept { 316 return cross_fade.mixramp_delay; 317 } 318 LockSetReplayGainMode(ReplayGainMode _mode)319 void LockSetReplayGainMode(ReplayGainMode _mode) noexcept { 320 const std::scoped_lock<Mutex> protect(mutex); 321 replay_gain_mode = _mode; 322 } 323 324 /** 325 * Like ReadTaggedSong(), but locks and unlocks the object. 326 */ 327 std::unique_ptr<DetachedSong> LockReadTaggedSong() noexcept; 328 329 [[gnu::pure]] 330 PlayerStatus LockGetStatus() noexcept; 331 GetState() const332 PlayerState GetState() const noexcept { 333 return state; 334 } 335 336 struct SyncInfo { 337 PlayerState state; 338 bool has_next_song; 339 }; 340 341 [[gnu::pure]] LockGetSyncInfo() const342 SyncInfo LockGetSyncInfo() const noexcept { 343 const std::scoped_lock<Mutex> protect(mutex); 344 return {state, next_song != nullptr}; 345 } 346 GetTotalPlayTime() const347 auto GetTotalPlayTime() const noexcept { 348 return total_play_time; 349 } 350 351 private: 352 /** 353 * Signals the object. The object should be locked prior to 354 * calling this function. 355 */ Signal()356 void Signal() noexcept { 357 cond.notify_one(); 358 } 359 360 /** 361 * Signals the object. The object is temporarily locked by 362 * this function. 363 */ LockSignal()364 void LockSignal() noexcept { 365 const std::scoped_lock<Mutex> protect(mutex); 366 Signal(); 367 } 368 369 /** 370 * Waits for a signal on the object. This function is only 371 * valid in the player thread. The object must be locked 372 * prior to calling this function. 373 */ Wait(std::unique_lock<Mutex> & lock)374 void Wait(std::unique_lock<Mutex> &lock) noexcept { 375 assert(thread.IsInside()); 376 377 cond.wait(lock); 378 } 379 380 /** 381 * Wake up the client waiting for command completion. 382 * 383 * Caller must lock the object. 384 */ ClientSignal()385 void ClientSignal() noexcept { 386 assert(thread.IsInside()); 387 388 client_cond.notify_one(); 389 } 390 391 /** 392 * The client calls this method to wait for command 393 * completion. 394 * 395 * Caller must lock the object. 396 */ ClientWait(std::unique_lock<Mutex> & lock)397 void ClientWait(std::unique_lock<Mutex> &lock) noexcept { 398 assert(!thread.IsInside()); 399 400 client_cond.wait(lock); 401 } 402 403 /** 404 * A command has been finished. This method clears the 405 * command and signals the client. 406 * 407 * To be called from the player thread. Caller must lock the 408 * object. 409 */ CommandFinished()410 void CommandFinished() noexcept { 411 assert(command != PlayerCommand::NONE); 412 413 command = PlayerCommand::NONE; 414 ClientSignal(); 415 } 416 LockCommandFinished()417 void LockCommandFinished() noexcept { 418 const std::scoped_lock<Mutex> protect(mutex); 419 CommandFinished(); 420 } 421 422 /** 423 * Checks if the size of the #MusicPipe is below the #threshold. If 424 * not, it attempts to synchronize with all output threads, and waits 425 * until another #MusicChunk is finished. 426 * 427 * Caller must lock the mutex. 428 * 429 * @param threshold the maximum number of chunks in the pipe 430 * @return true if there are less than #threshold chunks in the pipe 431 */ 432 bool WaitOutputConsumed(std::unique_lock<Mutex> &lock, 433 unsigned threshold) noexcept; 434 LockWaitOutputConsumed(unsigned threshold)435 bool LockWaitOutputConsumed(unsigned threshold) noexcept { 436 std::unique_lock<Mutex> lock(mutex); 437 return WaitOutputConsumed(lock, threshold); 438 } 439 440 /** 441 * Wait for the command to be finished by the player thread. 442 * 443 * To be called from the main thread. Caller must lock the 444 * object. 445 */ WaitCommandLocked(std::unique_lock<Mutex> & lock)446 void WaitCommandLocked(std::unique_lock<Mutex> &lock) noexcept { 447 while (command != PlayerCommand::NONE) 448 ClientWait(lock); 449 } 450 451 /** 452 * Send a command to the player thread and synchronously wait 453 * for it to finish. 454 * 455 * To be called from the main thread. Caller must lock the 456 * object. 457 */ SynchronousCommand(std::unique_lock<Mutex> & lock,PlayerCommand cmd)458 void SynchronousCommand(std::unique_lock<Mutex> &lock, 459 PlayerCommand cmd) noexcept { 460 assert(command == PlayerCommand::NONE); 461 462 command = cmd; 463 Signal(); 464 WaitCommandLocked(lock); 465 } 466 467 /** 468 * Send a command to the player thread and synchronously wait 469 * for it to finish. 470 * 471 * To be called from the main thread. This method locks the 472 * object. 473 */ LockSynchronousCommand(PlayerCommand cmd)474 void LockSynchronousCommand(PlayerCommand cmd) noexcept { 475 std::unique_lock<Mutex> lock(mutex); 476 SynchronousCommand(lock, cmd); 477 } 478 479 void PauseLocked(std::unique_lock<Mutex> &lock) noexcept; 480 ClearError()481 void ClearError() noexcept { 482 error_type = PlayerError::NONE; 483 error = std::exception_ptr(); 484 } 485 ApplyBorderPause()486 bool ApplyBorderPause() noexcept { 487 if (border_pause) 488 state = PlayerState::PAUSE; 489 return border_pause; 490 } 491 492 /** 493 * Set the error. Discards any previous error condition. 494 * 495 * Caller must lock the object. 496 * 497 * @param type the error type; must not be #PlayerError::NONE 498 */ 499 void SetError(PlayerError type, std::exception_ptr &&_error) noexcept; 500 501 /** 502 * Set the error and set state to PlayerState::PAUSE. 503 */ SetOutputError(std::exception_ptr && _error)504 void SetOutputError(std::exception_ptr &&_error) noexcept { 505 SetError(PlayerError::OUTPUT, std::move(_error)); 506 507 /* pause: the user may resume playback as soon as an 508 audio output becomes available */ 509 state = PlayerState::PAUSE; 510 } 511 LockSetOutputError(std::exception_ptr && _error)512 void LockSetOutputError(std::exception_ptr &&_error) noexcept { 513 const std::scoped_lock<Mutex> lock(mutex); 514 SetOutputError(std::move(_error)); 515 } 516 517 /** 518 * Checks whether an error has occurred, and if so, rethrows 519 * it. 520 * 521 * Caller must lock the object. 522 */ CheckRethrowError() const523 void CheckRethrowError() const { 524 if (error_type != PlayerError::NONE) 525 std::rethrow_exception(error); 526 } 527 528 /** 529 * Set the #tagged_song attribute to a newly allocated copy of 530 * the given #DetachedSong. Locks and unlocks the object. 531 */ 532 void LockSetTaggedSong(const DetachedSong &song) noexcept; 533 534 void ClearTaggedSong() noexcept; 535 536 /** 537 * Read and clear the #tagged_song attribute. 538 * 539 * Caller must lock the object. 540 */ 541 std::unique_ptr<DetachedSong> ReadTaggedSong() noexcept; 542 543 void EnqueueSongLocked(std::unique_lock<Mutex> &lock, 544 std::unique_ptr<DetachedSong> song) noexcept; 545 546 /** 547 * Throws on error. 548 */ 549 void SeekLocked(std::unique_lock<Mutex> &lock, 550 std::unique_ptr<DetachedSong> song, SongTime t); 551 552 /** 553 * Caller must lock the object. 554 */ CancelPendingSeek()555 void CancelPendingSeek() noexcept { 556 if (!seeking) 557 return; 558 559 seeking = false; 560 ClientSignal(); 561 } 562 563 void LockUpdateSongTag(DetachedSong &song, 564 const Tag &new_tag) noexcept; 565 566 /** 567 * Plays a #MusicChunk object (after applying software 568 * volume). If it contains a (stream) tag, copy it to the 569 * current song, so MPD's playlist reflects the new stream 570 * tag. 571 * 572 * Player lock is not held. 573 * 574 * Throws on error. 575 */ 576 void PlayChunk(DetachedSong &song, MusicChunkPtr chunk, 577 const AudioFormat &format); 578 579 /* virtual methods from AudioOutputClient */ ChunksConsumed()580 void ChunksConsumed() override { 581 LockSignal(); 582 } 583 ApplyEnabled()584 void ApplyEnabled() override { 585 LockUpdateAudio(); 586 } 587 588 void RunThread() noexcept; 589 }; 590 591 #endif 592