output/MultipleOutputs: SetVolume() throws on error
This reveals more about the nature of an error instead of just returning "problems setting volume".
This commit is contained in:
parent
9bdc75524b
commit
2d7181105d
2
NEWS
2
NEWS
|
@ -3,6 +3,8 @@ ver 0.23.8 (not yet released)
|
||||||
- curl: fix crash if web server does not understand WebDAV
|
- curl: fix crash if web server does not understand WebDAV
|
||||||
* output
|
* output
|
||||||
- pipewire: fix crash with PipeWire 0.3.53
|
- pipewire: fix crash with PipeWire 0.3.53
|
||||||
|
* mixer
|
||||||
|
- better error messages
|
||||||
* support libfmt 9
|
* support libfmt 9
|
||||||
|
|
||||||
ver 0.23.7 (2022/05/09)
|
ver 0.23.7 (2022/05/09)
|
||||||
|
|
|
@ -333,15 +333,11 @@ handle_getvol(Client &client, Request, Response &r)
|
||||||
}
|
}
|
||||||
|
|
||||||
CommandResult
|
CommandResult
|
||||||
handle_setvol(Client &client, Request args, Response &r)
|
handle_setvol(Client &client, Request args, Response &)
|
||||||
{
|
{
|
||||||
unsigned level = args.ParseUnsigned(0, 100);
|
unsigned level = args.ParseUnsigned(0, 100);
|
||||||
|
|
||||||
if (!volume_level_change(client.GetPartition().outputs, level)) {
|
volume_level_change(client.GetPartition().outputs, level);
|
||||||
r.Error(ACK_ERROR_SYSTEM, "problems setting volume");
|
|
||||||
return CommandResult::ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
return CommandResult::OK;
|
return CommandResult::OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -364,11 +360,8 @@ handle_volume(Client &client, Request args, Response &r)
|
||||||
else if (new_volume > 100)
|
else if (new_volume > 100)
|
||||||
new_volume = 100;
|
new_volume = 100;
|
||||||
|
|
||||||
if (new_volume != old_volume &&
|
if (new_volume != old_volume)
|
||||||
!volume_level_change(outputs, new_volume)) {
|
volume_level_change(outputs, new_volume);
|
||||||
r.Error(ACK_ERROR_SYSTEM, "problems setting volume");
|
|
||||||
return CommandResult::ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
return CommandResult::OK;
|
return CommandResult::OK;
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,42 +73,74 @@ MultipleOutputs::GetVolume() const noexcept
|
||||||
return total / ok;
|
return total / ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
enum class SetVolumeResult {
|
||||||
output_mixer_set_volume(AudioOutputControl &ao, unsigned volume) noexcept
|
NO_MIXER,
|
||||||
|
DISABLED,
|
||||||
|
ERROR,
|
||||||
|
OK,
|
||||||
|
};
|
||||||
|
|
||||||
|
static SetVolumeResult
|
||||||
|
output_mixer_set_volume(AudioOutputControl &ao, unsigned volume)
|
||||||
{
|
{
|
||||||
assert(volume <= 100);
|
assert(volume <= 100);
|
||||||
|
|
||||||
auto *mixer = ao.GetMixer();
|
auto *mixer = ao.GetMixer();
|
||||||
if (mixer == nullptr)
|
if (mixer == nullptr)
|
||||||
return false;
|
return SetVolumeResult::NO_MIXER;
|
||||||
|
|
||||||
/* software mixers are always updated, even if they are
|
/* software mixers are always updated, even if they are
|
||||||
disabled */
|
disabled */
|
||||||
if (!ao.IsReallyEnabled() && !mixer->IsPlugin(software_mixer_plugin))
|
if (!ao.IsReallyEnabled() && !mixer->IsPlugin(software_mixer_plugin))
|
||||||
return false;
|
return SetVolumeResult::DISABLED;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
mixer_set_volume(mixer, volume);
|
mixer_set_volume(mixer, volume);
|
||||||
return true;
|
return SetVolumeResult::OK;
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
FmtError(mixer_domain,
|
FmtError(mixer_domain,
|
||||||
"Failed to set mixer for '{}': {}",
|
"Failed to set mixer for '{}': {}",
|
||||||
ao.GetName(), std::current_exception());
|
ao.GetName(), std::current_exception());
|
||||||
return false;
|
std::throw_with_nested(std::runtime_error(fmt::format("Failed to set mixer for '{}'",
|
||||||
|
ao.GetName())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
void
|
||||||
MultipleOutputs::SetVolume(unsigned volume) noexcept
|
MultipleOutputs::SetVolume(unsigned volume)
|
||||||
{
|
{
|
||||||
assert(volume <= 100);
|
assert(volume <= 100);
|
||||||
|
|
||||||
bool success = false;
|
SetVolumeResult result = SetVolumeResult::NO_MIXER;
|
||||||
for (const auto &ao : outputs)
|
std::exception_ptr error;
|
||||||
success = output_mixer_set_volume(*ao, volume)
|
|
||||||
|| success;
|
|
||||||
|
|
||||||
return success;
|
for (const auto &ao : outputs) {
|
||||||
|
try {
|
||||||
|
auto r = output_mixer_set_volume(*ao, volume);
|
||||||
|
if (r > result)
|
||||||
|
result = r;
|
||||||
|
} catch (...) {
|
||||||
|
/* remember the first error */
|
||||||
|
if (!error) {
|
||||||
|
error = std::current_exception();
|
||||||
|
result = SetVolumeResult::ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (result) {
|
||||||
|
case SetVolumeResult::NO_MIXER:
|
||||||
|
throw std::runtime_error{"No mixer"};
|
||||||
|
|
||||||
|
case SetVolumeResult::DISABLED:
|
||||||
|
throw std::runtime_error{"All outputs are disabled"};
|
||||||
|
|
||||||
|
case SetVolumeResult::ERROR:
|
||||||
|
std::rethrow_exception(error);
|
||||||
|
|
||||||
|
case SetVolumeResult::OK:
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
|
|
@ -71,16 +71,16 @@ software_volume_change(MultipleOutputs &outputs, unsigned volume)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static void
|
||||||
hardware_volume_change(MultipleOutputs &outputs, unsigned volume)
|
hardware_volume_change(MultipleOutputs &outputs, unsigned volume)
|
||||||
{
|
{
|
||||||
/* reset the cache */
|
/* reset the cache */
|
||||||
last_hardware_volume = -1;
|
last_hardware_volume = -1;
|
||||||
|
|
||||||
return outputs.SetVolume(volume);
|
outputs.SetVolume(volume);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
void
|
||||||
volume_level_change(MultipleOutputs &outputs, unsigned volume)
|
volume_level_change(MultipleOutputs &outputs, unsigned volume)
|
||||||
{
|
{
|
||||||
assert(volume <= 100);
|
assert(volume <= 100);
|
||||||
|
@ -89,7 +89,7 @@ volume_level_change(MultipleOutputs &outputs, unsigned volume)
|
||||||
|
|
||||||
idle_add(IDLE_MIXER);
|
idle_add(IDLE_MIXER);
|
||||||
|
|
||||||
return hardware_volume_change(outputs, volume);
|
hardware_volume_change(outputs, volume);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
|
|
@ -30,7 +30,10 @@ InvalidateHardwareVolume() noexcept;
|
||||||
int
|
int
|
||||||
volume_level_get(const MultipleOutputs &outputs) noexcept;
|
volume_level_get(const MultipleOutputs &outputs) noexcept;
|
||||||
|
|
||||||
bool
|
/**
|
||||||
|
* Throws on error.
|
||||||
|
*/
|
||||||
|
void
|
||||||
volume_level_change(MultipleOutputs &outputs, unsigned volume);
|
volume_level_change(MultipleOutputs &outputs, unsigned volume);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
|
|
@ -141,10 +141,11 @@ public:
|
||||||
/**
|
/**
|
||||||
* Sets the volume on all available mixers.
|
* Sets the volume on all available mixers.
|
||||||
*
|
*
|
||||||
|
* Throws on error.
|
||||||
|
*
|
||||||
* @param volume the volume (range 0..100)
|
* @param volume the volume (range 0..100)
|
||||||
* @return true on success, false on failure
|
|
||||||
*/
|
*/
|
||||||
bool SetVolume(unsigned volume) noexcept;
|
void SetVolume(unsigned volume);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Similar to GetVolume(), but gets the volume only for
|
* Similar to GetVolume(), but gets the volume only for
|
||||||
|
|
Loading…
Reference in New Issue