summaryrefslogtreecommitdiff
path: root/src/player/Control.hxx
diff options
context:
space:
mode:
Diffstat (limited to 'src/player/Control.hxx')
-rw-r--r--src/player/Control.hxx216
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