Commit c40354bb authored by Max Kellermann's avatar Max Kellermann

player/Outputs: abstract interface wrapping class MultipleOutputs

parent c04aafb4
...@@ -143,6 +143,7 @@ libmpd_a_SOURCES = \ ...@@ -143,6 +143,7 @@ libmpd_a_SOURCES = \
src/player/Thread.cxx src/player/Thread.hxx \ src/player/Thread.cxx src/player/Thread.hxx \
src/player/Control.cxx src/player/Control.hxx \ src/player/Control.cxx src/player/Control.hxx \
src/player/Listener.hxx \ src/player/Listener.hxx \
src/player/Outputs.hxx \
src/PlaylistError.cxx src/PlaylistError.hxx \ src/PlaylistError.cxx src/PlaylistError.hxx \
src/PlaylistPrint.cxx src/PlaylistPrint.hxx \ src/PlaylistPrint.cxx src/PlaylistPrint.hxx \
src/PlaylistSave.cxx src/PlaylistSave.hxx \ src/PlaylistSave.cxx src/PlaylistSave.hxx \
......
...@@ -315,7 +315,7 @@ MultipleOutputs::ClearTailChunk(const MusicChunk *chunk, ...@@ -315,7 +315,7 @@ MultipleOutputs::ClearTailChunk(const MusicChunk *chunk,
} }
unsigned unsigned
MultipleOutputs::Check() noexcept MultipleOutputs::CheckPipe() noexcept
{ {
const MusicChunk *chunk; const MusicChunk *chunk;
bool is_tail; bool is_tail;
......
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#define OUTPUT_ALL_H #define OUTPUT_ALL_H
#include "Control.hxx" #include "Control.hxx"
#include "player/Outputs.hxx"
#include "AudioFormat.hxx" #include "AudioFormat.hxx"
#include "ReplayGainMode.hxx" #include "ReplayGainMode.hxx"
#include "Chrono.hxx" #include "Chrono.hxx"
...@@ -44,7 +45,7 @@ class AudioOutputClient; ...@@ -44,7 +45,7 @@ class AudioOutputClient;
struct MusicChunk; struct MusicChunk;
struct ReplayGainConfig; struct ReplayGainConfig;
class MultipleOutputs { class MultipleOutputs final : public PlayerOutputs {
MixerListener &mixer_listener; MixerListener &mixer_listener;
std::vector<AudioOutputControl *> outputs; std::vector<AudioOutputControl *> outputs;
...@@ -115,86 +116,9 @@ public: ...@@ -115,86 +116,9 @@ public:
gcc_pure gcc_pure
AudioOutputControl *FindByName(const char *name) noexcept; AudioOutputControl *FindByName(const char *name) noexcept;
/**
* Checks the "enabled" flag of all audio outputs, and if one has
* changed, commit the change.
*/
void EnableDisable();
/**
* Opens all audio outputs which are not disabled.
*
* Throws #std::runtime_error on error.
*
* @param audio_format the preferred audio format
* @param _buffer the #music_buffer where consumed #MusicChunk objects
* should be returned
*/
void Open(const AudioFormat audio_format, MusicBuffer &_buffer);
/**
* Closes all audio outputs.
*/
void Close() noexcept;
/**
* Closes all audio outputs. Outputs with the "always_on"
* flag are put into pause mode.
*/
void Release() noexcept;
void SetReplayGainMode(ReplayGainMode mode) noexcept; void SetReplayGainMode(ReplayGainMode mode) noexcept;
/** /**
* Enqueue a #MusicChunk object for playing, i.e. pushes it to a
* #MusicPipe.
*
* Throws #std::runtime_error on error (all closed then).
*
* @param chunk the #MusicChunk object to be played
*/
void Play(MusicChunk *chunk);
/**
* Checks if the output devices have drained their music pipe, and
* returns the consumed music chunks to the #music_buffer.
*
* @return the number of chunks to play left in the #MusicPipe
*/
unsigned Check() noexcept;
/**
* Puts all audio outputs into pause mode. Most implementations will
* simply close it then.
*/
void Pause() noexcept;
/**
* Drain all audio outputs.
*/
void Drain() noexcept;
/**
* Try to cancel data which may still be in the device's buffers.
*/
void Cancel() noexcept;
/**
* Indicate that a new song will begin now.
*/
void SongBorder() noexcept;
/**
* Returns the "elapsed_time" stamp of the most recently finished
* chunk. A negative value is returned when no chunk has been
* finished yet.
*/
gcc_pure
SignedSongTime GetElapsedTime() const noexcept {
return elapsed_time;
}
/**
* Returns the average volume of all available mixers (range * Returns the average volume of all available mixers (range
* 0..100). Returns -1 if no mixer can be queried. * 0..100). Returns -1 if no mixer can be queried.
*/ */
...@@ -259,6 +183,22 @@ private: ...@@ -259,6 +183,22 @@ private:
* reference. * reference.
*/ */
void ClearTailChunk(const MusicChunk *chunk, bool *locked) noexcept; void ClearTailChunk(const MusicChunk *chunk, bool *locked) noexcept;
/* virtual methods from class PlayerOutputs */
void EnableDisable() override;
void Open(const AudioFormat audio_format,
MusicBuffer &_buffer) override;
void Close() noexcept override;
void Release() noexcept override;
void Play(MusicChunk *chunk) override;
unsigned CheckPipe() noexcept override;
void Pause() noexcept override;
void Drain() noexcept override;
void Cancel() noexcept override;
void SongBorder() noexcept override;
SignedSongTime GetElapsedTime() const noexcept override {
return elapsed_time;
}
}; };
#endif #endif
...@@ -19,16 +19,16 @@ ...@@ -19,16 +19,16 @@
#include "config.h" #include "config.h"
#include "Control.hxx" #include "Control.hxx"
#include "Outputs.hxx"
#include "Idle.hxx" #include "Idle.hxx"
#include "DetachedSong.hxx" #include "DetachedSong.hxx"
#include "output/MultipleOutputs.hxx"
#include <algorithm> #include <algorithm>
#include <assert.h> #include <assert.h>
PlayerControl::PlayerControl(PlayerListener &_listener, PlayerControl::PlayerControl(PlayerListener &_listener,
MultipleOutputs &_outputs, PlayerOutputs &_outputs,
unsigned _buffer_chunks, unsigned _buffer_chunks,
unsigned _buffered_before_play, unsigned _buffered_before_play,
AudioFormat _configured_audio_format, AudioFormat _configured_audio_format,
...@@ -50,10 +50,10 @@ PlayerControl::~PlayerControl() noexcept ...@@ -50,10 +50,10 @@ PlayerControl::~PlayerControl() noexcept
bool bool
PlayerControl::WaitOutputConsumed(unsigned threshold) noexcept PlayerControl::WaitOutputConsumed(unsigned threshold) noexcept
{ {
bool result = outputs.Check() < threshold; bool result = outputs.CheckPipe() < threshold;
if (!result && command == PlayerCommand::NONE) { if (!result && command == PlayerCommand::NONE) {
Wait(); Wait();
result = outputs.Check() < threshold; result = outputs.CheckPipe() < threshold;
} }
return result; return result;
......
...@@ -36,7 +36,7 @@ ...@@ -36,7 +36,7 @@
#include <stdint.h> #include <stdint.h>
class PlayerListener; class PlayerListener;
class MultipleOutputs; class PlayerOutputs;
class DetachedSong; class DetachedSong;
enum class PlayerState : uint8_t { enum class PlayerState : uint8_t {
...@@ -108,7 +108,7 @@ struct player_status { ...@@ -108,7 +108,7 @@ struct player_status {
struct PlayerControl final : AudioOutputClient { struct PlayerControl final : AudioOutputClient {
PlayerListener &listener; PlayerListener &listener;
MultipleOutputs &outputs; PlayerOutputs &outputs;
const unsigned buffer_chunks; const unsigned buffer_chunks;
...@@ -223,7 +223,7 @@ struct PlayerControl final : AudioOutputClient { ...@@ -223,7 +223,7 @@ struct PlayerControl final : AudioOutputClient {
double total_play_time = 0; double total_play_time = 0;
PlayerControl(PlayerListener &_listener, PlayerControl(PlayerListener &_listener,
MultipleOutputs &_outputs, PlayerOutputs &_outputs,
unsigned buffer_chunks, unsigned buffer_chunks,
unsigned buffered_before_play, unsigned buffered_before_play,
AudioFormat _configured_audio_format, AudioFormat _configured_audio_format,
......
/*
* Copyright 2003-2017 The Music Player Daemon Project
* http://www.musicpd.org
*
* 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.
*
* 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.
*/
#ifndef MPD_PLAYER_OUTPUT_INTERFACE_HXX
#define MPD_PLAYER_OUTPUT_INTERFACE_HXX
#include "Chrono.hxx"
#include "Compiler.h"
struct AudioFormat;
struct MusicChunk;
class MusicBuffer;
/**
* An interface for the player thread to control all outputs. This
* interface is implemented only by #MultipleOutputs, and exists only
* to decouple the player code from the output code, to be able to
* unit-test the player code.
*/
class PlayerOutputs {
public:
/**
* Checks the "enabled" flag of all audio outputs, and if one has
* changed, commit the change.
*
* Throws on error.
*/
virtual void EnableDisable() = 0;
/**
* Opens all audio outputs which are not disabled.
*
* Throws on error.
*
* @param audio_format the preferred audio format
* @param buffer the #MusicBuffer where consumed #MusicChunk
* objects should be returned
*/
virtual void Open(const AudioFormat audio_format,
MusicBuffer &buffer) = 0;
/**
* Closes all audio outputs.
*/
virtual void Close() noexcept = 0;
/**
* Closes all audio outputs. Outputs with the "always_on"
* flag are put into pause mode.
*/
virtual void Release() noexcept = 0;
/**
* Enqueue a #MusicChunk object for playing, i.e. pushes it to a
* #MusicPipe.
*
* Throws on error (all closed then).
*
* @param chunk the #MusicChunk object to be played
*/
virtual void Play(MusicChunk *chunk) = 0;
/**
* Checks if the output devices have drained their music pipe, and
* returns the consumed music chunks to the #music_buffer.
*
* @return the number of chunks to play left in the #MusicPipe
*/
virtual unsigned CheckPipe() noexcept = 0;
/**
* Puts all audio outputs into pause mode. Most implementations will
* simply close it then.
*/
virtual void Pause() noexcept = 0;
/**
* Drain all audio outputs.
*/
virtual void Drain() noexcept = 0;
/**
* Try to cancel data which may still be in the device's buffers.
*/
virtual void Cancel() noexcept = 0;
/**
* Indicate that a new song will begin now.
*/
virtual void SongBorder() noexcept = 0;
/**
* Returns the "elapsed_time" stamp of the most recently finished
* chunk. A negative value is returned when no chunk has been
* finished yet.
*/
gcc_pure
virtual SignedSongTime GetElapsedTime() const noexcept = 0;
};
#endif
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include "config.h" #include "config.h"
#include "Thread.hxx" #include "Thread.hxx"
#include "Outputs.hxx"
#include "Listener.hxx" #include "Listener.hxx"
#include "decoder/DecoderThread.hxx" #include "decoder/DecoderThread.hxx"
#include "decoder/DecoderControl.hxx" #include "decoder/DecoderControl.hxx"
...@@ -29,7 +30,6 @@ ...@@ -29,7 +30,6 @@
#include "DetachedSong.hxx" #include "DetachedSong.hxx"
#include "CrossFade.hxx" #include "CrossFade.hxx"
#include "Control.hxx" #include "Control.hxx"
#include "output/MultipleOutputs.hxx"
#include "tag/Tag.hxx" #include "tag/Tag.hxx"
#include "Idle.hxx" #include "Idle.hxx"
#include "system/PeriodClock.hxx" #include "system/PeriodClock.hxx"
...@@ -334,7 +334,7 @@ private: ...@@ -334,7 +334,7 @@ private:
unsigned UnlockCheckOutputs() noexcept { unsigned UnlockCheckOutputs() noexcept {
const ScopeUnlock unlock(pc.mutex); const ScopeUnlock unlock(pc.mutex);
return pc.outputs.Check(); return pc.outputs.CheckPipe();
} }
/** /**
...@@ -750,7 +750,7 @@ Player::ProcessCommand() noexcept ...@@ -750,7 +750,7 @@ Player::ProcessCommand() noexcept
case PlayerCommand::REFRESH: case PlayerCommand::REFRESH:
if (output_open && !paused) { if (output_open && !paused) {
const ScopeUnlock unlock(pc.mutex); const ScopeUnlock unlock(pc.mutex);
pc.outputs.Check(); pc.outputs.CheckPipe();
} }
pc.elapsed_time = !pc.outputs.GetElapsedTime().IsNegative() pc.elapsed_time = !pc.outputs.GetElapsedTime().IsNegative()
...@@ -1004,7 +1004,7 @@ Player::Run() noexcept ...@@ -1004,7 +1004,7 @@ Player::Run() noexcept
{ {
const ScopeUnlock unlock(pc.mutex); const ScopeUnlock unlock(pc.mutex);
if (!paused && output_open && if (!paused && output_open &&
pc.outputs.Check() < 4 && pc.outputs.CheckPipe() < 4 &&
!SendSilence()) !SendSilence())
break; break;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment