output/Control: add struct AudioOutputControl
Will move attributes from struct AudioOutput that are specific to the OutputThread. The new struct AudioOutputControl is a holder for the AudioOutput pointer. This prepares for making the output list more dynamic, to allow moving outputs to between partitions.
This commit is contained in:
parent
59181ac5fb
commit
8bb9d0960b
@ -31,12 +31,12 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
static int
|
static int
|
||||||
output_mixer_get_volume(const AudioOutput &ao)
|
output_mixer_get_volume(const AudioOutputControl &ao)
|
||||||
{
|
{
|
||||||
if (!ao.enabled)
|
if (!ao.IsEnabled())
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
Mixer *mixer = ao.mixer;
|
auto *mixer = ao.GetMixer();
|
||||||
if (mixer == nullptr)
|
if (mixer == nullptr)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
@ -56,7 +56,7 @@ MultipleOutputs::GetVolume() const
|
|||||||
unsigned ok = 0;
|
unsigned ok = 0;
|
||||||
int total = 0;
|
int total = 0;
|
||||||
|
|
||||||
for (auto ao : outputs) {
|
for (auto *ao : outputs) {
|
||||||
int volume = output_mixer_get_volume(*ao);
|
int volume = output_mixer_get_volume(*ao);
|
||||||
if (volume >= 0) {
|
if (volume >= 0) {
|
||||||
total += volume;
|
total += volume;
|
||||||
@ -71,14 +71,14 @@ MultipleOutputs::GetVolume() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
output_mixer_set_volume(AudioOutput &ao, unsigned volume)
|
output_mixer_set_volume(AudioOutputControl &ao, unsigned volume)
|
||||||
{
|
{
|
||||||
assert(volume <= 100);
|
assert(volume <= 100);
|
||||||
|
|
||||||
if (!ao.enabled)
|
if (!ao.IsEnabled())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Mixer *mixer = ao.mixer;
|
auto *mixer = ao.GetMixer();
|
||||||
if (mixer == nullptr)
|
if (mixer == nullptr)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -99,7 +99,7 @@ MultipleOutputs::SetVolume(unsigned volume)
|
|||||||
assert(volume <= 100);
|
assert(volume <= 100);
|
||||||
|
|
||||||
bool success = false;
|
bool success = false;
|
||||||
for (auto ao : outputs)
|
for (auto *ao : outputs)
|
||||||
success = output_mixer_set_volume(*ao, volume)
|
success = output_mixer_set_volume(*ao, volume)
|
||||||
|| success;
|
|| success;
|
||||||
|
|
||||||
@ -107,12 +107,12 @@ MultipleOutputs::SetVolume(unsigned volume)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
output_mixer_get_software_volume(const AudioOutput &ao)
|
output_mixer_get_software_volume(const AudioOutputControl &ao)
|
||||||
{
|
{
|
||||||
if (!ao.enabled)
|
if (!ao.IsEnabled())
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
Mixer *mixer = ao.mixer;
|
auto *mixer = ao.GetMixer();
|
||||||
if (mixer == nullptr || !mixer->IsPlugin(software_mixer_plugin))
|
if (mixer == nullptr || !mixer->IsPlugin(software_mixer_plugin))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
@ -125,7 +125,7 @@ MultipleOutputs::GetSoftwareVolume() const
|
|||||||
unsigned ok = 0;
|
unsigned ok = 0;
|
||||||
int total = 0;
|
int total = 0;
|
||||||
|
|
||||||
for (auto ao : outputs) {
|
for (auto *ao : outputs) {
|
||||||
int volume = output_mixer_get_software_volume(*ao);
|
int volume = output_mixer_get_software_volume(*ao);
|
||||||
if (volume >= 0) {
|
if (volume >= 0) {
|
||||||
total += volume;
|
total += volume;
|
||||||
@ -144,8 +144,8 @@ MultipleOutputs::SetSoftwareVolume(unsigned volume)
|
|||||||
{
|
{
|
||||||
assert(volume <= PCM_VOLUME_1);
|
assert(volume <= PCM_VOLUME_1);
|
||||||
|
|
||||||
for (auto ao : outputs) {
|
for (auto *ao : outputs) {
|
||||||
const auto mixer = ao->mixer;
|
auto *mixer = ao->GetMixer();
|
||||||
|
|
||||||
if (mixer != nullptr &&
|
if (mixer != nullptr &&
|
||||||
(&mixer->plugin == &software_mixer_plugin ||
|
(&mixer->plugin == &software_mixer_plugin ||
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
#include "Control.hxx"
|
||||||
#include "Internal.hxx"
|
#include "Internal.hxx"
|
||||||
#include "OutputPlugin.hxx"
|
#include "OutputPlugin.hxx"
|
||||||
#include "Domain.hxx"
|
#include "Domain.hxx"
|
||||||
@ -36,6 +37,72 @@ static constexpr PeriodClock::Duration REOPEN_AFTER = std::chrono::seconds(10);
|
|||||||
|
|
||||||
struct notify audio_output_client_notify;
|
struct notify audio_output_client_notify;
|
||||||
|
|
||||||
|
AudioOutputControl::AudioOutputControl(AudioOutput *_output)
|
||||||
|
:output(_output), mutex(output->mutex)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
AudioOutputControl::GetName() const
|
||||||
|
{
|
||||||
|
return output->GetName();
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioOutputClient &
|
||||||
|
AudioOutputControl::GetClient()
|
||||||
|
{
|
||||||
|
return *output->client;
|
||||||
|
}
|
||||||
|
|
||||||
|
Mixer *
|
||||||
|
AudioOutputControl::GetMixer() const
|
||||||
|
{
|
||||||
|
return output->mixer;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
AudioOutputControl::IsEnabled() const
|
||||||
|
{
|
||||||
|
return output->IsEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
AudioOutputControl::LockSetEnabled(bool new_value)
|
||||||
|
{
|
||||||
|
const std::lock_guard<Mutex> protect(mutex);
|
||||||
|
|
||||||
|
if (new_value == output->enabled)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
output->enabled = new_value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
AudioOutputControl::LockToggleEnabled()
|
||||||
|
{
|
||||||
|
const std::lock_guard<Mutex> protect(mutex);
|
||||||
|
return output->enabled = !output->enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
AudioOutputControl::IsOpen() const
|
||||||
|
{
|
||||||
|
return output->IsOpen();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
AudioOutputControl::IsBusy() const
|
||||||
|
{
|
||||||
|
return output->IsBusy();
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::exception_ptr &
|
||||||
|
AudioOutputControl::GetLastError() const
|
||||||
|
{
|
||||||
|
return output->GetLastError();
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
AudioOutput::WaitForCommand()
|
AudioOutput::WaitForCommand()
|
||||||
{
|
{
|
||||||
@ -46,6 +113,12 @@ AudioOutput::WaitForCommand()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
AudioOutputControl::WaitForCommand()
|
||||||
|
{
|
||||||
|
output->WaitForCommand();
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
AudioOutput::CommandAsync(Command cmd)
|
AudioOutput::CommandAsync(Command cmd)
|
||||||
{
|
{
|
||||||
@ -104,6 +177,18 @@ AudioOutput::DisableAsync()
|
|||||||
CommandAsync(Command::DISABLE);
|
CommandAsync(Command::DISABLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
AudioOutputControl::EnableDisableAsync()
|
||||||
|
{
|
||||||
|
output->EnableDisableAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
AudioOutputControl::LockPauseAsync()
|
||||||
|
{
|
||||||
|
output->LockPauseAsync();
|
||||||
|
}
|
||||||
|
|
||||||
inline bool
|
inline bool
|
||||||
AudioOutput::Open(const AudioFormat audio_format, const MusicPipe &mp)
|
AudioOutput::Open(const AudioFormat audio_format, const MusicPipe &mp)
|
||||||
{
|
{
|
||||||
@ -175,6 +260,38 @@ AudioOutput::LockUpdate(const AudioFormat audio_format,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
AudioOutputControl::LockRelease()
|
||||||
|
{
|
||||||
|
output->LockRelease();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
AudioOutputControl::LockCloseWait()
|
||||||
|
{
|
||||||
|
output->LockCloseWait();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
AudioOutputControl::LockUpdate(const AudioFormat audio_format,
|
||||||
|
const MusicPipe &mp,
|
||||||
|
bool force)
|
||||||
|
{
|
||||||
|
return output->LockUpdate(audio_format, mp, force);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
AudioOutputControl::LockIsChunkConsumed(const MusicChunk &chunk) const
|
||||||
|
{
|
||||||
|
return output->LockIsChunkConsumed(chunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
AudioOutputControl::ClearTailChunk(const MusicChunk &chunk)
|
||||||
|
{
|
||||||
|
output->ClearTailChunk(chunk);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
AudioOutput::LockPlay()
|
AudioOutput::LockPlay()
|
||||||
{
|
{
|
||||||
@ -253,6 +370,12 @@ AudioOutput::LockCloseWait()
|
|||||||
CloseWait();
|
CloseWait();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
AudioOutputControl::SetReplayGainMode(ReplayGainMode _mode)
|
||||||
|
{
|
||||||
|
return output->SetReplayGainMode(_mode);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
AudioOutput::StopThread()
|
AudioOutput::StopThread()
|
||||||
{
|
{
|
||||||
@ -275,6 +398,12 @@ AudioOutput::BeginDestroy()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
AudioOutputControl::BeginDestroy()
|
||||||
|
{
|
||||||
|
output->BeginDestroy();
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
AudioOutput::FinishDestroy()
|
AudioOutput::FinishDestroy()
|
||||||
{
|
{
|
||||||
@ -283,3 +412,34 @@ AudioOutput::FinishDestroy()
|
|||||||
|
|
||||||
audio_output_free(this);
|
audio_output_free(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
AudioOutputControl::FinishDestroy()
|
||||||
|
{
|
||||||
|
output->FinishDestroy();
|
||||||
|
output = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
AudioOutputControl::LockPlay()
|
||||||
|
{
|
||||||
|
output->LockPlay();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
AudioOutputControl::LockDrainAsync()
|
||||||
|
{
|
||||||
|
output->LockDrainAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
AudioOutputControl::LockCancelAsync()
|
||||||
|
{
|
||||||
|
output->LockCancelAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
AudioOutputControl::LockAllowPlay()
|
||||||
|
{
|
||||||
|
output->LockAllowPlay();
|
||||||
|
}
|
||||||
|
@ -20,6 +20,112 @@
|
|||||||
#ifndef MPD_OUTPUT_CONTROL_HXX
|
#ifndef MPD_OUTPUT_CONTROL_HXX
|
||||||
#define MPD_OUTPUT_CONTROL_HXX
|
#define MPD_OUTPUT_CONTROL_HXX
|
||||||
|
|
||||||
|
#include "Compiler.h"
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
#include <exception>
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
#include <assert.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
enum class ReplayGainMode : uint8_t;
|
||||||
|
struct AudioFormat;
|
||||||
struct AudioOutput;
|
struct AudioOutput;
|
||||||
|
struct MusicChunk;
|
||||||
|
class MusicPipe;
|
||||||
|
class Mutex;
|
||||||
|
class Mixer;
|
||||||
|
class AudioOutputClient;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller for an #AudioOutput and its output thread.
|
||||||
|
*/
|
||||||
|
class AudioOutputControl {
|
||||||
|
AudioOutput *output;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Mutex &mutex;
|
||||||
|
|
||||||
|
explicit AudioOutputControl(AudioOutput *_output);
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
~AudioOutputControl() {
|
||||||
|
assert(output == nullptr);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
AudioOutputControl(const AudioOutputControl &) = delete;
|
||||||
|
AudioOutputControl &operator=(const AudioOutputControl &) = delete;
|
||||||
|
|
||||||
|
gcc_pure
|
||||||
|
const char *GetName() const;
|
||||||
|
|
||||||
|
AudioOutputClient &GetClient();
|
||||||
|
|
||||||
|
gcc_pure
|
||||||
|
Mixer *GetMixer() const;
|
||||||
|
|
||||||
|
gcc_pure
|
||||||
|
bool IsEnabled() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true if the value has been modified
|
||||||
|
*/
|
||||||
|
bool LockSetEnabled(bool new_value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the new "enabled" value
|
||||||
|
*/
|
||||||
|
bool LockToggleEnabled();
|
||||||
|
|
||||||
|
gcc_pure
|
||||||
|
bool IsOpen() const;
|
||||||
|
|
||||||
|
gcc_pure
|
||||||
|
bool IsBusy() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Caller must lock the mutex.
|
||||||
|
*/
|
||||||
|
gcc_const
|
||||||
|
const std::exception_ptr &GetLastError() const;
|
||||||
|
|
||||||
|
void WaitForCommand();
|
||||||
|
|
||||||
|
void BeginDestroy();
|
||||||
|
void FinishDestroy();
|
||||||
|
|
||||||
|
void EnableDisableAsync();
|
||||||
|
void LockPauseAsync();
|
||||||
|
|
||||||
|
void LockCloseWait();
|
||||||
|
void LockRelease();
|
||||||
|
|
||||||
|
void SetReplayGainMode(ReplayGainMode _mode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens or closes the device, depending on the "enabled"
|
||||||
|
* flag.
|
||||||
|
*
|
||||||
|
* @param force true to ignore the #fail_timer
|
||||||
|
* @return true if the device is open
|
||||||
|
*/
|
||||||
|
bool LockUpdate(const AudioFormat audio_format,
|
||||||
|
const MusicPipe &mp,
|
||||||
|
bool force);
|
||||||
|
|
||||||
|
gcc_pure
|
||||||
|
bool LockIsChunkConsumed(const MusicChunk &chunk) const;
|
||||||
|
|
||||||
|
void ClearTailChunk(const MusicChunk &chunk);
|
||||||
|
|
||||||
|
void LockPlay();
|
||||||
|
void LockDrainAsync();
|
||||||
|
void LockCancelAsync();
|
||||||
|
void LockAllowPlay();
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -43,9 +43,9 @@ MultipleOutputs::MultipleOutputs(MixerListener &_mixer_listener)
|
|||||||
MultipleOutputs::~MultipleOutputs()
|
MultipleOutputs::~MultipleOutputs()
|
||||||
{
|
{
|
||||||
/* parallel destruction */
|
/* parallel destruction */
|
||||||
for (auto i : outputs)
|
for (auto *i : outputs)
|
||||||
i->BeginDestroy();
|
i->BeginDestroy();
|
||||||
for (auto i : outputs)
|
for (auto *i : outputs)
|
||||||
i->FinishDestroy();
|
i->FinishDestroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,7 +80,7 @@ MultipleOutputs::Configure(EventLoop &event_loop,
|
|||||||
throw FormatRuntimeError("output devices with identical "
|
throw FormatRuntimeError("output devices with identical "
|
||||||
"names: %s", output->GetName());
|
"names: %s", output->GetName());
|
||||||
|
|
||||||
outputs.push_back(output);
|
outputs.push_back(new AudioOutputControl(output));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (outputs.empty()) {
|
if (outputs.empty()) {
|
||||||
@ -89,7 +89,7 @@ MultipleOutputs::Configure(EventLoop &event_loop,
|
|||||||
auto output = LoadOutput(event_loop, replay_gain_config,
|
auto output = LoadOutput(event_loop, replay_gain_config,
|
||||||
mixer_listener,
|
mixer_listener,
|
||||||
client, empty);
|
client, empty);
|
||||||
outputs.push_back(output);
|
outputs.push_back(new AudioOutputControl(output));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,13 +104,13 @@ MultipleOutputs::AddNullOutput(EventLoop &event_loop,
|
|||||||
auto output = LoadOutput(event_loop, replay_gain_config,
|
auto output = LoadOutput(event_loop, replay_gain_config,
|
||||||
mixer_listener,
|
mixer_listener,
|
||||||
client, block);
|
client, block);
|
||||||
outputs.push_back(output);
|
outputs.push_back(new AudioOutputControl(output));
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioOutput *
|
AudioOutputControl *
|
||||||
MultipleOutputs::FindByName(const char *name) const
|
MultipleOutputs::FindByName(const char *name)
|
||||||
{
|
{
|
||||||
for (auto i : outputs)
|
for (auto *i : outputs)
|
||||||
if (strcmp(i->GetName(), name) == 0)
|
if (strcmp(i->GetName(), name) == 0)
|
||||||
return i;
|
return i;
|
||||||
|
|
||||||
@ -122,12 +122,12 @@ MultipleOutputs::EnableDisable()
|
|||||||
{
|
{
|
||||||
/* parallel execution */
|
/* parallel execution */
|
||||||
|
|
||||||
for (auto ao : outputs) {
|
for (auto *ao : outputs) {
|
||||||
const std::lock_guard<Mutex> lock(ao->mutex);
|
const std::lock_guard<Mutex> lock(ao->mutex);
|
||||||
ao->EnableDisableAsync();
|
ao->EnableDisableAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto ao : outputs) {
|
for (auto *ao : outputs) {
|
||||||
const std::lock_guard<Mutex> lock(ao->mutex);
|
const std::lock_guard<Mutex> lock(ao->mutex);
|
||||||
ao->WaitForCommand();
|
ao->WaitForCommand();
|
||||||
}
|
}
|
||||||
@ -136,7 +136,7 @@ MultipleOutputs::EnableDisable()
|
|||||||
bool
|
bool
|
||||||
MultipleOutputs::AllFinished() const
|
MultipleOutputs::AllFinished() const
|
||||||
{
|
{
|
||||||
for (auto ao : outputs) {
|
for (auto *ao : outputs) {
|
||||||
const std::lock_guard<Mutex> protect(ao->mutex);
|
const std::lock_guard<Mutex> protect(ao->mutex);
|
||||||
if (ao->IsBusy())
|
if (ao->IsBusy())
|
||||||
return false;
|
return false;
|
||||||
@ -155,7 +155,7 @@ MultipleOutputs::WaitAll()
|
|||||||
void
|
void
|
||||||
MultipleOutputs::AllowPlay()
|
MultipleOutputs::AllowPlay()
|
||||||
{
|
{
|
||||||
for (auto ao : outputs)
|
for (auto *ao : outputs)
|
||||||
ao->LockAllowPlay();
|
ao->LockAllowPlay();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,7 +167,7 @@ MultipleOutputs::Update(bool force)
|
|||||||
if (!input_audio_format.IsDefined())
|
if (!input_audio_format.IsDefined())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
for (auto ao : outputs)
|
for (auto *ao : outputs)
|
||||||
ret = ao->LockUpdate(input_audio_format, *pipe, force)
|
ret = ao->LockUpdate(input_audio_format, *pipe, force)
|
||||||
|| ret;
|
|| ret;
|
||||||
|
|
||||||
@ -177,7 +177,7 @@ MultipleOutputs::Update(bool force)
|
|||||||
void
|
void
|
||||||
MultipleOutputs::SetReplayGainMode(ReplayGainMode mode)
|
MultipleOutputs::SetReplayGainMode(ReplayGainMode mode)
|
||||||
{
|
{
|
||||||
for (auto ao : outputs)
|
for (auto *ao : outputs)
|
||||||
ao->SetReplayGainMode(mode);
|
ao->SetReplayGainMode(mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -195,7 +195,7 @@ MultipleOutputs::Play(MusicChunk *chunk)
|
|||||||
|
|
||||||
pipe->Push(chunk);
|
pipe->Push(chunk);
|
||||||
|
|
||||||
for (auto ao : outputs)
|
for (auto *ao : outputs)
|
||||||
ao->LockPlay();
|
ao->LockPlay();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -228,7 +228,7 @@ MultipleOutputs::Open(const AudioFormat audio_format,
|
|||||||
|
|
||||||
std::exception_ptr first_error;
|
std::exception_ptr first_error;
|
||||||
|
|
||||||
for (auto ao : outputs) {
|
for (auto *ao : outputs) {
|
||||||
const std::lock_guard<Mutex> lock(ao->mutex);
|
const std::lock_guard<Mutex> lock(ao->mutex);
|
||||||
|
|
||||||
if (ao->IsEnabled())
|
if (ao->IsEnabled())
|
||||||
@ -259,7 +259,7 @@ MultipleOutputs::Open(const AudioFormat audio_format,
|
|||||||
bool
|
bool
|
||||||
MultipleOutputs::IsChunkConsumed(const MusicChunk *chunk) const
|
MultipleOutputs::IsChunkConsumed(const MusicChunk *chunk) const
|
||||||
{
|
{
|
||||||
for (auto ao : outputs)
|
for (auto *ao : outputs)
|
||||||
if (!ao->LockIsChunkConsumed(*chunk))
|
if (!ao->LockIsChunkConsumed(*chunk))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -274,7 +274,7 @@ MultipleOutputs::ClearTailChunk(const MusicChunk *chunk,
|
|||||||
assert(pipe->Contains(chunk));
|
assert(pipe->Contains(chunk));
|
||||||
|
|
||||||
for (unsigned i = 0, n = outputs.size(); i != n; ++i) {
|
for (unsigned i = 0, n = outputs.size(); i != n; ++i) {
|
||||||
AudioOutput *ao = outputs[i];
|
auto *ao = outputs[i];
|
||||||
|
|
||||||
/* this mutex will be unlocked by the caller when it's
|
/* this mutex will be unlocked by the caller when it's
|
||||||
ready */
|
ready */
|
||||||
@ -343,7 +343,7 @@ MultipleOutputs::Pause()
|
|||||||
{
|
{
|
||||||
Update(false);
|
Update(false);
|
||||||
|
|
||||||
for (auto ao : outputs)
|
for (auto *ao : outputs)
|
||||||
ao->LockPauseAsync();
|
ao->LockPauseAsync();
|
||||||
|
|
||||||
WaitAll();
|
WaitAll();
|
||||||
@ -352,7 +352,7 @@ MultipleOutputs::Pause()
|
|||||||
void
|
void
|
||||||
MultipleOutputs::Drain()
|
MultipleOutputs::Drain()
|
||||||
{
|
{
|
||||||
for (auto ao : outputs)
|
for (auto *ao : outputs)
|
||||||
ao->LockDrainAsync();
|
ao->LockDrainAsync();
|
||||||
|
|
||||||
WaitAll();
|
WaitAll();
|
||||||
@ -363,7 +363,7 @@ MultipleOutputs::Cancel()
|
|||||||
{
|
{
|
||||||
/* send the cancel() command to all audio outputs */
|
/* send the cancel() command to all audio outputs */
|
||||||
|
|
||||||
for (auto ao : outputs)
|
for (auto *ao : outputs)
|
||||||
ao->LockCancelAsync();
|
ao->LockCancelAsync();
|
||||||
|
|
||||||
WaitAll();
|
WaitAll();
|
||||||
@ -386,7 +386,7 @@ MultipleOutputs::Cancel()
|
|||||||
void
|
void
|
||||||
MultipleOutputs::Close()
|
MultipleOutputs::Close()
|
||||||
{
|
{
|
||||||
for (auto ao : outputs)
|
for (auto *ao : outputs)
|
||||||
ao->LockCloseWait();
|
ao->LockCloseWait();
|
||||||
|
|
||||||
if (pipe != nullptr) {
|
if (pipe != nullptr) {
|
||||||
@ -407,7 +407,7 @@ MultipleOutputs::Close()
|
|||||||
void
|
void
|
||||||
MultipleOutputs::Release()
|
MultipleOutputs::Release()
|
||||||
{
|
{
|
||||||
for (auto ao : outputs)
|
for (auto *ao : outputs)
|
||||||
ao->LockRelease();
|
ao->LockRelease();
|
||||||
|
|
||||||
if (pipe != nullptr) {
|
if (pipe != nullptr) {
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
#ifndef OUTPUT_ALL_H
|
#ifndef OUTPUT_ALL_H
|
||||||
#define OUTPUT_ALL_H
|
#define OUTPUT_ALL_H
|
||||||
|
|
||||||
|
#include "Control.hxx"
|
||||||
#include "AudioFormat.hxx"
|
#include "AudioFormat.hxx"
|
||||||
#include "ReplayGainMode.hxx"
|
#include "ReplayGainMode.hxx"
|
||||||
#include "Chrono.hxx"
|
#include "Chrono.hxx"
|
||||||
@ -47,7 +48,7 @@ struct ReplayGainConfig;
|
|||||||
class MultipleOutputs {
|
class MultipleOutputs {
|
||||||
MixerListener &mixer_listener;
|
MixerListener &mixer_listener;
|
||||||
|
|
||||||
std::vector<AudioOutput *> outputs;
|
std::vector<AudioOutputControl *> outputs;
|
||||||
|
|
||||||
AudioFormat input_audio_format = AudioFormat::Undefined();
|
AudioFormat input_audio_format = AudioFormat::Undefined();
|
||||||
|
|
||||||
@ -96,13 +97,13 @@ public:
|
|||||||
/**
|
/**
|
||||||
* Returns the "i"th audio output device.
|
* Returns the "i"th audio output device.
|
||||||
*/
|
*/
|
||||||
const AudioOutput &Get(unsigned i) const {
|
const AudioOutputControl &Get(unsigned i) const {
|
||||||
assert(i < Size());
|
assert(i < Size());
|
||||||
|
|
||||||
return *outputs[i];
|
return *outputs[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioOutput &Get(unsigned i) {
|
AudioOutputControl &Get(unsigned i) {
|
||||||
assert(i < Size());
|
assert(i < Size());
|
||||||
|
|
||||||
return *outputs[i];
|
return *outputs[i];
|
||||||
@ -113,7 +114,7 @@ public:
|
|||||||
* Returns nullptr if the name does not exist.
|
* Returns nullptr if the name does not exist.
|
||||||
*/
|
*/
|
||||||
gcc_pure
|
gcc_pure
|
||||||
AudioOutput *FindByName(const char *name) const;
|
AudioOutputControl *FindByName(const char *name);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks the "enabled" flag of all audio outputs, and if one has
|
* Checks the "enabled" flag of all audio outputs, and if one has
|
||||||
|
@ -42,19 +42,18 @@ audio_output_enable_index(MultipleOutputs &outputs, unsigned idx)
|
|||||||
if (idx >= outputs.Size())
|
if (idx >= outputs.Size())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
AudioOutput &ao = outputs.Get(idx);
|
auto &ao = outputs.Get(idx);
|
||||||
if (ao.enabled)
|
if (!ao.LockSetEnabled(true))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
ao.enabled = true;
|
|
||||||
idle_add(IDLE_OUTPUT);
|
idle_add(IDLE_OUTPUT);
|
||||||
|
|
||||||
if (ao.mixer != nullptr) {
|
if (ao.GetMixer() != nullptr) {
|
||||||
InvalidateHardwareVolume();
|
InvalidateHardwareVolume();
|
||||||
idle_add(IDLE_MIXER);
|
idle_add(IDLE_MIXER);
|
||||||
}
|
}
|
||||||
|
|
||||||
ao.client->ApplyEnabled();
|
ao.GetClient().ApplyEnabled();
|
||||||
|
|
||||||
++audio_output_state_version;
|
++audio_output_state_version;
|
||||||
|
|
||||||
@ -67,21 +66,20 @@ audio_output_disable_index(MultipleOutputs &outputs, unsigned idx)
|
|||||||
if (idx >= outputs.Size())
|
if (idx >= outputs.Size())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
AudioOutput &ao = outputs.Get(idx);
|
auto &ao = outputs.Get(idx);
|
||||||
if (!ao.enabled)
|
if (!ao.LockSetEnabled(false))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
ao.enabled = false;
|
|
||||||
idle_add(IDLE_OUTPUT);
|
idle_add(IDLE_OUTPUT);
|
||||||
|
|
||||||
Mixer *mixer = ao.mixer;
|
auto *mixer = ao.GetMixer();
|
||||||
if (mixer != nullptr) {
|
if (mixer != nullptr) {
|
||||||
mixer_close(mixer);
|
mixer_close(mixer);
|
||||||
InvalidateHardwareVolume();
|
InvalidateHardwareVolume();
|
||||||
idle_add(IDLE_MIXER);
|
idle_add(IDLE_MIXER);
|
||||||
}
|
}
|
||||||
|
|
||||||
ao.client->ApplyEnabled();
|
ao.GetClient().ApplyEnabled();
|
||||||
|
|
||||||
++audio_output_state_version;
|
++audio_output_state_version;
|
||||||
|
|
||||||
@ -94,12 +92,12 @@ audio_output_toggle_index(MultipleOutputs &outputs, unsigned idx)
|
|||||||
if (idx >= outputs.Size())
|
if (idx >= outputs.Size())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
AudioOutput &ao = outputs.Get(idx);
|
auto &ao = outputs.Get(idx);
|
||||||
const bool enabled = ao.enabled = !ao.enabled;
|
const bool enabled = ao.LockToggleEnabled();
|
||||||
idle_add(IDLE_OUTPUT);
|
idle_add(IDLE_OUTPUT);
|
||||||
|
|
||||||
if (!enabled) {
|
if (!enabled) {
|
||||||
Mixer *mixer = ao.mixer;
|
auto *mixer = ao.GetMixer();
|
||||||
if (mixer != nullptr) {
|
if (mixer != nullptr) {
|
||||||
mixer_close(mixer);
|
mixer_close(mixer);
|
||||||
InvalidateHardwareVolume();
|
InvalidateHardwareVolume();
|
||||||
@ -107,7 +105,7 @@ audio_output_toggle_index(MultipleOutputs &outputs, unsigned idx)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ao.client->ApplyEnabled();
|
ao.GetClient().ApplyEnabled();
|
||||||
|
|
||||||
++audio_output_state_version;
|
++audio_output_state_version;
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ void
|
|||||||
printAudioDevices(Response &r, const MultipleOutputs &outputs)
|
printAudioDevices(Response &r, const MultipleOutputs &outputs)
|
||||||
{
|
{
|
||||||
for (unsigned i = 0, n = outputs.Size(); i != n; ++i) {
|
for (unsigned i = 0, n = outputs.Size(); i != n; ++i) {
|
||||||
const AudioOutput &ao = outputs.Get(i);
|
const auto &ao = outputs.Get(i);
|
||||||
|
|
||||||
r.Format("outputid: %i\n"
|
r.Format("outputid: %i\n"
|
||||||
"outputname: %s\n"
|
"outputname: %s\n"
|
||||||
|
@ -42,7 +42,7 @@ audio_output_state_save(BufferedOutputStream &os,
|
|||||||
const MultipleOutputs &outputs)
|
const MultipleOutputs &outputs)
|
||||||
{
|
{
|
||||||
for (unsigned i = 0, n = outputs.Size(); i != n; ++i) {
|
for (unsigned i = 0, n = outputs.Size(); i != n; ++i) {
|
||||||
const AudioOutput &ao = outputs.Get(i);
|
const auto &ao = outputs.Get(i);
|
||||||
const std::lock_guard<Mutex> lock(ao.mutex);
|
const std::lock_guard<Mutex> lock(ao.mutex);
|
||||||
|
|
||||||
os.Format(AUDIO_DEVICE_STATE "%d:%s\n",
|
os.Format(AUDIO_DEVICE_STATE "%d:%s\n",
|
||||||
@ -70,14 +70,14 @@ audio_output_state_read(const char *line, MultipleOutputs &outputs)
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
name = endptr + 1;
|
name = endptr + 1;
|
||||||
AudioOutput *ao = outputs.FindByName(name);
|
auto *ao = outputs.FindByName(name);
|
||||||
if (ao == NULL) {
|
if (ao == NULL) {
|
||||||
FormatDebug(output_domain,
|
FormatDebug(output_domain,
|
||||||
"Ignoring device state for '%s'", name);
|
"Ignoring device state for '%s'", name);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ao->enabled = false;
|
ao->LockSetEnabled(false);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
#include "Control.hxx"
|
||||||
#include "Internal.hxx"
|
#include "Internal.hxx"
|
||||||
#include "Client.hxx"
|
#include "Client.hxx"
|
||||||
#include "OutputAPI.hxx"
|
#include "OutputAPI.hxx"
|
||||||
|
Loading…
Reference in New Issue
Block a user