diff options
Diffstat (limited to 'src/player/Control.hxx')
-rw-r--r-- | src/player/Control.hxx | 216 |
1 files changed, 118 insertions, 98 deletions
diff --git a/src/player/Control.hxx b/src/player/Control.hxx index 39e24e4f8..ebd063098 100644 --- a/src/player/Control.hxx +++ b/src/player/Control.hxx @@ -31,11 +31,12 @@ #include "ReplayGainMode.hxx" #include <exception> +#include <memory> #include <stdint.h> class PlayerListener; -class MultipleOutputs; +class PlayerOutputs; class DetachedSong; enum class PlayerState : uint8_t { @@ -49,7 +50,16 @@ enum class PlayerCommand : uint8_t { EXIT, STOP, PAUSE, + + /** + * Seek to a certain position in the specified song. This + * command can also be used to change the current song or + * start playback. It "finishes" immediately, but + * PlayerControl::seeking will be set until seeking really + * completes (or fails). + */ SEEK, + CLOSE_AUDIO, /** @@ -100,7 +110,7 @@ struct player_status { struct PlayerControl final : AudioOutputClient { PlayerListener &listener; - MultipleOutputs &outputs; + PlayerOutputs &outputs; const unsigned buffer_chunks; @@ -133,11 +143,6 @@ struct PlayerControl final : AudioOutputClient { */ Cond client_cond; - PlayerCommand command = PlayerCommand::NONE; - PlayerState state = PlayerState::STOP; - - PlayerError error_type = PlayerError::NONE; - /** * The error that occurred in the player thread. This * attribute is only valid if #error_type is not @@ -147,6 +152,14 @@ struct PlayerControl final : AudioOutputClient { std::exception_ptr error; /** + * The next queued song. + * + * This is a duplicate, and must be freed when this attribute + * is cleared. + */ + std::unique_ptr<DetachedSong> next_song; + + /** * 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). @@ -156,29 +169,19 @@ struct PlayerControl final : AudioOutputClient { * Protected by #mutex. Set by the PlayerThread and consumed * by the main thread. */ - DetachedSong *tagged_song = nullptr; + std::unique_ptr<DetachedSong> tagged_song; - uint16_t bit_rate; - AudioFormat audio_format; - SignedSongTime total_time; - SongTime elapsed_time; - - /** - * The next queued song. - * - * This is a duplicate, and must be freed when this attribute - * is cleared. - */ - DetachedSong *next_song = nullptr; - - SongTime seek_time; + PlayerCommand command = PlayerCommand::NONE; + PlayerState state = PlayerState::STOP; - CrossFadeSettings cross_fade; + PlayerError error_type = PlayerError::NONE; - const ReplayGainConfig replay_gain_config; ReplayGainMode replay_gain_mode = ReplayGainMode::OFF; - double total_play_time = 0; + /** + * Is the player currently busy with the SEEK command? + */ + bool seeking = false; /** * If this flag is set, then the player will be auto-paused at @@ -189,25 +192,62 @@ struct PlayerControl final : AudioOutputClient { */ bool border_pause = false; + /** + * If this flag is set, then the player thread is currently + * occupied and will not be able to respond quickly to + * commands (e.g. waiting for the decoder thread to finish + * seeking). This is used to skip #PlayerCommand::REFRESH to + * avoid blocking the main thread. + */ + bool occupied = false; + + struct ScopeOccupied { + PlayerControl &pc; + + explicit ScopeOccupied(PlayerControl &_pc) noexcept:pc(_pc) { + assert(!pc.occupied); + pc.occupied = true; + } + + ~ScopeOccupied() noexcept { + assert(pc.occupied); + pc.occupied = false; + } + }; + + AudioFormat audio_format; + uint16_t bit_rate; + + SignedSongTime total_time; + SongTime elapsed_time; + + SongTime seek_time; + + CrossFadeSettings cross_fade; + + const ReplayGainConfig replay_gain_config; + + double total_play_time = 0; + PlayerControl(PlayerListener &_listener, - MultipleOutputs &_outputs, + PlayerOutputs &_outputs, unsigned buffer_chunks, unsigned buffered_before_play, AudioFormat _configured_audio_format, - const ReplayGainConfig &_replay_gain_config); - ~PlayerControl(); + const ReplayGainConfig &_replay_gain_config) noexcept; + ~PlayerControl() noexcept; /** * Locks the object. */ - void Lock() const { + void Lock() const noexcept { mutex.lock(); } /** * Unlocks the object. */ - void Unlock() const { + void Unlock() const noexcept { mutex.unlock(); } @@ -215,7 +255,7 @@ struct PlayerControl final : AudioOutputClient { * Signals the object. The object should be locked prior to * calling this function. */ - void Signal() { + void Signal() noexcept { cond.signal(); } @@ -223,7 +263,7 @@ struct PlayerControl final : AudioOutputClient { * Signals the object. The object is temporarily locked by * this function. */ - void LockSignal() { + void LockSignal() noexcept { const std::lock_guard<Mutex> protect(mutex); Signal(); } @@ -233,7 +273,7 @@ struct PlayerControl final : AudioOutputClient { * valid in the player thread. The object must be locked * prior to calling this function. */ - void Wait() { + void Wait() noexcept { assert(thread.IsInside()); cond.wait(mutex); @@ -244,7 +284,7 @@ struct PlayerControl final : AudioOutputClient { * * Caller must lock the object. */ - void ClientSignal() { + void ClientSignal() noexcept { assert(thread.IsInside()); client_cond.signal(); @@ -256,7 +296,7 @@ struct PlayerControl final : AudioOutputClient { * * Caller must lock the object. */ - void ClientWait() { + void ClientWait() noexcept { assert(!thread.IsInside()); client_cond.wait(mutex); @@ -269,14 +309,14 @@ struct PlayerControl final : AudioOutputClient { * To be called from the player thread. Caller must lock the * object. */ - void CommandFinished() { + void CommandFinished() noexcept { assert(command != PlayerCommand::NONE); command = PlayerCommand::NONE; ClientSignal(); } - void LockCommandFinished() { + void LockCommandFinished() noexcept { const std::lock_guard<Mutex> protect(mutex); CommandFinished(); } @@ -291,9 +331,9 @@ struct PlayerControl final : AudioOutputClient { * @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 WaitOutputConsumed(unsigned threshold) noexcept; - bool LockWaitOutputConsumed(unsigned threshold) { + bool LockWaitOutputConsumed(unsigned threshold) noexcept { const std::lock_guard<Mutex> protect(mutex); return WaitOutputConsumed(threshold); } @@ -305,7 +345,7 @@ private: * To be called from the main thread. Caller must lock the * object. */ - void WaitCommandLocked() { + void WaitCommandLocked() noexcept { while (command != PlayerCommand::NONE) ClientWait(); } @@ -339,53 +379,47 @@ private: public: /** - * Throws std::runtime_error or #Error on error. + * Throws on error. * - * @param song the song to be queued; the given instance will - * be owned and freed by the player + * @param song the song to be queued */ - void Play(DetachedSong *song); + void Play(std::unique_ptr<DetachedSong> song); /** * see PlayerCommand::CANCEL */ - void LockCancel(); + void LockCancel() noexcept; - void LockSetPause(bool pause_flag); + void LockSetPause(bool pause_flag) noexcept; private: - void PauseLocked(); + void PauseLocked() noexcept; - void ClearError() { + void ClearError() noexcept { error_type = PlayerError::NONE; error = std::exception_ptr(); } public: - void LockPause(); + void LockPause() noexcept; /** * Set the player's #border_pause flag. */ - void LockSetBorderPause(bool border_pause); + void LockSetBorderPause(bool border_pause) noexcept; - bool ApplyBorderPause() { + bool ApplyBorderPause() noexcept { if (border_pause) state = PlayerState::PAUSE; return border_pause; } - bool LockApplyBorderPause() { - const std::lock_guard<Mutex> lock(mutex); - return ApplyBorderPause(); - } - - void Kill(); + void Kill() noexcept; gcc_pure player_status LockGetStatus() noexcept; - PlayerState GetState() const { + PlayerState GetState() const noexcept { return state; } @@ -396,12 +430,12 @@ public: * * @param type the error type; must not be #PlayerError::NONE */ - void SetError(PlayerError type, std::exception_ptr &&_error); + void SetError(PlayerError type, std::exception_ptr &&_error) noexcept; /** * Set the error and set state to PlayerState::PAUSE. */ - void SetOutputError(std::exception_ptr &&_error) { + void SetOutputError(std::exception_ptr &&_error) noexcept { SetError(PlayerError::OUTPUT, std::move(_error)); /* pause: the user may resume playback as soon as an @@ -409,7 +443,7 @@ public: state = PlayerState::PAUSE; } - void LockSetOutputError(std::exception_ptr &&_error) { + void LockSetOutputError(std::exception_ptr &&_error) noexcept { const std::lock_guard<Mutex> lock(mutex); SetOutputError(std::move(_error)); } @@ -433,9 +467,9 @@ public: CheckRethrowError(); } - void LockClearError(); + void LockClearError() noexcept; - PlayerError GetErrorType() const { + PlayerError GetErrorType() const noexcept { return error_type; } @@ -443,89 +477,75 @@ public: * Set the #tagged_song attribute to a newly allocated copy of * the given #DetachedSong. Locks and unlocks the object. */ - void LockSetTaggedSong(const DetachedSong &song); + void LockSetTaggedSong(const DetachedSong &song) noexcept; - void ClearTaggedSong(); + void ClearTaggedSong() noexcept; /** * Read and clear the #tagged_song attribute. * * Caller must lock the object. */ - DetachedSong *ReadTaggedSong() { - DetachedSong *result = tagged_song; - tagged_song = nullptr; - return result; - } + std::unique_ptr<DetachedSong> ReadTaggedSong() noexcept; /** * Like ReadTaggedSong(), but locks and unlocks the object. */ - DetachedSong *LockReadTaggedSong() { - const std::lock_guard<Mutex> protect(mutex); - return ReadTaggedSong(); - } + std::unique_ptr<DetachedSong> LockReadTaggedSong() noexcept; - void LockStop(); + void LockStop() noexcept; - void LockUpdateAudio(); + void LockUpdateAudio() noexcept; private: - void EnqueueSongLocked(DetachedSong *song) { - assert(song != nullptr); - assert(next_song == nullptr); - - next_song = song; - seek_time = SongTime::zero(); - SynchronousCommand(PlayerCommand::QUEUE); - } + void EnqueueSongLocked(std::unique_ptr<DetachedSong> song) noexcept; /** - * Throws std::runtime_error or #Error on error. + * Throws on error. */ - void SeekLocked(DetachedSong *song, SongTime t); + void SeekLocked(std::unique_ptr<DetachedSong> song, SongTime t); public: /** * @param song the song to be queued; the given instance will be owned * and freed by the player */ - void LockEnqueueSong(DetachedSong *song); + void LockEnqueueSong(std::unique_ptr<DetachedSong> song) noexcept; /** * Makes the player thread seek the specified song to a position. * - * Throws std::runtime_error or #Error on error. + * Throws on error. * * @param song the song to be queued; the given instance will be owned * and freed by the player */ - void LockSeek(DetachedSong *song, SongTime t); + void LockSeek(std::unique_ptr<DetachedSong> song, SongTime t); - void SetCrossFade(float cross_fade_seconds); + void SetCrossFade(float cross_fade_seconds) noexcept; - float GetCrossFade() const { + float GetCrossFade() const noexcept { return cross_fade.duration; } - void SetMixRampDb(float mixramp_db); + void SetMixRampDb(float mixramp_db) noexcept; - float GetMixRampDb() const { + float GetMixRampDb() const noexcept { return cross_fade.mixramp_db; } - void SetMixRampDelay(float mixramp_delay_seconds); + void SetMixRampDelay(float mixramp_delay_seconds) noexcept; - float GetMixRampDelay() const { + float GetMixRampDelay() const noexcept { return cross_fade.mixramp_delay; } - void LockSetReplayGainMode(ReplayGainMode _mode) { + void LockSetReplayGainMode(ReplayGainMode _mode) noexcept { const std::lock_guard<Mutex> protect(mutex); replay_gain_mode = _mode; } - double GetTotalPlayTime() const { + double GetTotalPlayTime() const noexcept { return total_play_time; } @@ -539,7 +559,7 @@ public: } private: - void RunThread(); + void RunThread() noexcept; }; #endif |