diff --git a/src/input/plugins/AlsaInputPlugin.cxx b/src/input/plugins/AlsaInputPlugin.cxx index 73a145208..b3e56c625 100644 --- a/src/input/plugins/AlsaInputPlugin.cxx +++ b/src/input/plugins/AlsaInputPlugin.cxx @@ -26,6 +26,7 @@ #include "AlsaInputPlugin.hxx" #include "lib/alsa/NonBlock.hxx" +#include "lib/alsa/Error.hxx" #include "lib/alsa/Format.hxx" #include "../InputPlugin.hxx" #include "../AsyncInputStream.hxx" @@ -332,28 +333,23 @@ AlsaInputStream::ConfigureCapture(AudioFormat audio_format) snd_pcm_hw_params_alloca(&hw_params); if ((err = snd_pcm_hw_params_any(capture_handle, hw_params)) < 0) - throw FormatRuntimeError("Cannot initialize hardware parameter structure (%s)", - snd_strerror(err)); + throw Alsa::MakeError(err, "snd_pcm_hw_params_any() failed"); if ((err = snd_pcm_hw_params_set_access(capture_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) - throw FormatRuntimeError("Cannot set access type (%s)", - snd_strerror(err)); + throw Alsa::MakeError(err, "snd_pcm_hw_params_set_access() failed"); if ((err = snd_pcm_hw_params_set_format(capture_handle, hw_params, ToAlsaPcmFormat(audio_format.format))) < 0) - throw FormatRuntimeError("Cannot set sample format (%s)", - snd_strerror(err)); + throw Alsa::MakeError(err, "Cannot set sample format"); if ((err = snd_pcm_hw_params_set_channels(capture_handle, hw_params, audio_format.channels)) < 0) - throw FormatRuntimeError("Cannot set channels (%s)", - snd_strerror(err)); + throw Alsa::MakeError(err, "Cannot set channels"); if ((err = snd_pcm_hw_params_set_rate(capture_handle, hw_params, audio_format.sample_rate, 0)) < 0) - throw FormatRuntimeError("Cannot set sample rate (%s)", - snd_strerror(err)); + throw Alsa::MakeError(err, "Cannot set sample rate"); snd_pcm_uframes_t buffer_size_min, buffer_size_max; snd_pcm_hw_params_get_buffer_size_min(hw_params, &buffer_size_min); @@ -388,26 +384,22 @@ AlsaInputStream::ConfigureCapture(AudioFormat audio_format) int direction = -1; if ((err = snd_pcm_hw_params_set_period_size_near(capture_handle, hw_params, &period_size, &direction)) < 0) - throw FormatRuntimeError("Cannot set period size (%s)", - snd_strerror(err)); + throw Alsa::MakeError(err, "Cannot set period size"); } if ((err = snd_pcm_hw_params(capture_handle, hw_params)) < 0) - throw FormatRuntimeError("Cannot set parameters (%s)", - snd_strerror(err)); + throw Alsa::MakeError(err, "snd_pcm_hw_params() failed"); snd_pcm_uframes_t alsa_buffer_size; err = snd_pcm_hw_params_get_buffer_size(hw_params, &alsa_buffer_size); if (err < 0) - throw FormatRuntimeError("snd_pcm_hw_params_get_buffer_size() failed: %s", - snd_strerror(-err)); + throw Alsa::MakeError(err, "snd_pcm_hw_params_get_buffer_size() failed"); snd_pcm_uframes_t alsa_period_size; err = snd_pcm_hw_params_get_period_size(hw_params, &alsa_period_size, nullptr); if (err < 0) - throw FormatRuntimeError("snd_pcm_hw_params_get_period_size() failed: %s", - snd_strerror(-err)); + throw Alsa::MakeError(err, "snd_pcm_hw_params_get_period_size() failed"); FmtDebug(alsa_input_domain, "buffer_size={} period_size={}", alsa_buffer_size, alsa_period_size); @@ -418,8 +410,7 @@ AlsaInputStream::ConfigureCapture(AudioFormat audio_format) snd_pcm_sw_params_current(capture_handle, sw_params); if ((err = snd_pcm_sw_params(capture_handle, sw_params)) < 0) - throw FormatRuntimeError("unable to install sw params (%s)", - snd_strerror(err)); + throw Alsa::MakeError(err, "snd_pcm_sw_params() failed"); } inline void @@ -430,8 +421,9 @@ AlsaInputStream::OpenDevice(const SourceSpec &spec) if ((err = snd_pcm_open(&capture_handle, spec.GetDeviceName(), SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK | global_config.mode)) < 0) - throw FormatRuntimeError("Failed to open device: %s (%s)", - spec.GetDeviceName(), snd_strerror(err)); + throw Alsa::MakeError(err, + fmt::format("Failed to open device {}", + spec.GetDeviceName()).c_str()); try { ConfigureCapture(spec.GetAudioFormat()); diff --git a/src/lib/alsa/Error.cxx b/src/lib/alsa/Error.cxx new file mode 100644 index 000000000..219abcecc --- /dev/null +++ b/src/lib/alsa/Error.cxx @@ -0,0 +1,44 @@ +/* + * Copyright 2021 Max Kellermann + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "Error.hxx" + +#include + +namespace Alsa { + +ErrorCategory error_category; + +std::string +ErrorCategory::message(int condition) const +{ + return snd_strerror(condition); +} + +} // namespace Avahi diff --git a/src/lib/alsa/Error.hxx b/src/lib/alsa/Error.hxx new file mode 100644 index 000000000..4a827c403 --- /dev/null +++ b/src/lib/alsa/Error.hxx @@ -0,0 +1,53 @@ +/* + * Copyright 2021 Max Kellermann + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include + +namespace Alsa { + +class ErrorCategory final : public std::error_category { +public: + const char *name() const noexcept override { + return "libasound"; + } + + std::string message(int condition) const override; +}; + +extern ErrorCategory error_category; + +inline std::system_error +MakeError(int error, const char *msg) noexcept +{ + return std::system_error(error, error_category, msg); +} + +} // namespace Avahi diff --git a/src/lib/alsa/HwSetup.cxx b/src/lib/alsa/HwSetup.cxx index d2821af78..167a36bd6 100644 --- a/src/lib/alsa/HwSetup.cxx +++ b/src/lib/alsa/HwSetup.cxx @@ -18,6 +18,7 @@ */ #include "HwSetup.hxx" +#include "Error.hxx" #include "Format.hxx" #include "util/ByteOrder.hxx" #include "util/Domain.hxx" @@ -185,29 +186,27 @@ SetupHw(snd_pcm_t *pcm, /* configure HW params */ err = snd_pcm_hw_params_any(pcm, hwparams); if (err < 0) - throw FormatRuntimeError("snd_pcm_hw_params_any() failed: %s", - snd_strerror(-err)); + throw Alsa::MakeError(err, "snd_pcm_hw_params_any() failed"); err = snd_pcm_hw_params_set_access(pcm, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED); if (err < 0) - throw FormatRuntimeError("snd_pcm_hw_params_set_access() failed: %s", - snd_strerror(-err)); + throw Alsa::MakeError(err, "snd_pcm_hw_params_set_access() failed"); err = SetupSampleFormat(pcm, hwparams, audio_format.format, params); if (err < 0) - throw FormatRuntimeError("Failed to configure format %s: %s", - sample_format_to_string(audio_format.format), - snd_strerror(-err)); + throw Alsa::MakeError(err, + fmt::format("Failed to configure format {}", + audio_format.format).c_str()); unsigned int channels = audio_format.channels; err = snd_pcm_hw_params_set_channels_near(pcm, hwparams, &channels); if (err < 0) - throw FormatRuntimeError("Failed to configure %i channels: %s", - (int)audio_format.channels, - snd_strerror(-err)); + throw Alsa::MakeError(err, + fmt::format("Failed to configure {} channels", + audio_format.channels).c_str()); audio_format.channels = (int8_t)channels; @@ -218,9 +217,9 @@ SetupHw(snd_pcm_t *pcm, err = snd_pcm_hw_params_set_rate_near(pcm, hwparams, &output_sample_rate, nullptr); if (err < 0) - throw FormatRuntimeError("Failed to configure sample rate %u Hz: %s", - requested_sample_rate, - snd_strerror(-err)); + throw Alsa::MakeError(err, + fmt::format("Failed to configure sample rate {} Hz", + requested_sample_rate).c_str()); if (output_sample_rate == 0) throw FormatRuntimeError("Failed to configure sample rate %u Hz", @@ -253,8 +252,7 @@ SetupHw(snd_pcm_t *pcm, err = snd_pcm_hw_params_set_buffer_time_near(pcm, hwparams, &buffer_time, nullptr); if (err < 0) - throw FormatRuntimeError("snd_pcm_hw_params_set_buffer_time_near() failed: %s", - snd_strerror(-err)); + throw Alsa::MakeError(err, "snd_pcm_hw_params_set_buffer_time_near() failed"); } else { err = snd_pcm_hw_params_get_buffer_time(hwparams, &buffer_time, nullptr); @@ -275,32 +273,27 @@ SetupHw(snd_pcm_t *pcm, err = snd_pcm_hw_params_set_period_time_near(pcm, hwparams, &period_time, nullptr); if (err < 0) - throw FormatRuntimeError("snd_pcm_hw_params_set_period_time_near() failed: %s", - snd_strerror(-err)); + throw Alsa::MakeError(err, "snd_pcm_hw_params_set_period_time_near() failed"); } err = snd_pcm_hw_params(pcm, hwparams); if (err < 0) - throw FormatRuntimeError("snd_pcm_hw_params() failed: %s", - snd_strerror(-err)); + throw Alsa::MakeError(err, "snd_pcm_hw_params() failed"); HwResult result; err = snd_pcm_hw_params_get_format(hwparams, &result.format); if (err < 0) - throw FormatRuntimeError("snd_pcm_hw_params_get_format() failed: %s", - snd_strerror(-err)); + throw Alsa::MakeError(err, "snd_pcm_hw_params_get_format() failed"); err = snd_pcm_hw_params_get_buffer_size(hwparams, &result.buffer_size); if (err < 0) - throw FormatRuntimeError("snd_pcm_hw_params_get_buffer_size() failed: %s", - snd_strerror(-err)); + throw Alsa::MakeError(err, "snd_pcm_hw_params_get_buffer_size() failed"); err = snd_pcm_hw_params_get_period_size(hwparams, &result.period_size, nullptr); if (err < 0) - throw FormatRuntimeError("snd_pcm_hw_params_get_period_size() failed: %s", - snd_strerror(-err)); + throw Alsa::MakeError(err, "snd_pcm_hw_params_get_period_size() failed"); return result; } diff --git a/src/lib/alsa/NonBlock.cxx b/src/lib/alsa/NonBlock.cxx index 8c697eeb3..087f68b4b 100644 --- a/src/lib/alsa/NonBlock.cxx +++ b/src/lib/alsa/NonBlock.cxx @@ -18,6 +18,7 @@ */ #include "NonBlock.hxx" +#include "Error.hxx" #include "event/MultiSocketMonitor.hxx" #include "util/RuntimeError.hxx" @@ -29,8 +30,7 @@ AlsaNonBlockPcm::PrepareSockets(MultiSocketMonitor &m, snd_pcm_t *pcm) if (count == 0) throw std::runtime_error("snd_pcm_poll_descriptors_count() failed"); else - throw FormatRuntimeError("snd_pcm_poll_descriptors_count() failed: %s", - snd_strerror(-count)); + throw Alsa::MakeError(count, "snd_pcm_poll_descriptors_count() failed"); } struct pollfd *pfds = pfd_buffer.Get(count); @@ -40,8 +40,7 @@ AlsaNonBlockPcm::PrepareSockets(MultiSocketMonitor &m, snd_pcm_t *pcm) if (count == 0) throw std::runtime_error("snd_pcm_poll_descriptors() failed"); else - throw FormatRuntimeError("snd_pcm_poll_descriptors() failed: %s", - snd_strerror(-count)); + throw Alsa::MakeError(count, "snd_pcm_poll_descriptors() failed"); } m.ReplaceSocketList(pfds, count); @@ -71,8 +70,7 @@ AlsaNonBlockPcm::DispatchSockets(MultiSocketMonitor &m, unsigned short dummy; int err = snd_pcm_poll_descriptors_revents(pcm, pfds, i - pfds, &dummy); if (err < 0) - throw FormatRuntimeError("snd_pcm_poll_descriptors_revents() failed: %s", - snd_strerror(-err)); + throw Alsa::MakeError(err, "snd_pcm_poll_descriptors_revents() failed"); } Event::Duration diff --git a/src/lib/alsa/meson.build b/src/lib/alsa/meson.build index 7391b2bbd..c1ab2df3c 100644 --- a/src/lib/alsa/meson.build +++ b/src/lib/alsa/meson.build @@ -14,6 +14,7 @@ conf.set('ENABLE_ALSA', true) alsa = static_library( 'alsa', 'Version.cxx', + 'Error.cxx', 'AllowedFormat.cxx', 'HwSetup.cxx', 'NonBlock.cxx', diff --git a/src/mixer/plugins/AlsaMixerPlugin.cxx b/src/mixer/plugins/AlsaMixerPlugin.cxx index 5649f765e..fa39ee120 100644 --- a/src/mixer/plugins/AlsaMixerPlugin.cxx +++ b/src/mixer/plugins/AlsaMixerPlugin.cxx @@ -18,6 +18,7 @@ */ #include "lib/alsa/NonBlock.hxx" +#include "lib/alsa/Error.hxx" #include "mixer/MixerInternal.hxx" #include "mixer/Listener.hxx" #include "output/OutputAPI.hxx" @@ -264,16 +265,15 @@ AlsaMixer::Setup() int err; if ((err = snd_mixer_attach(handle, device)) < 0) - throw FormatRuntimeError("failed to attach to %s: %s", - device, snd_strerror(err)); + throw Alsa::MakeError(err, + fmt::format("failed to attach to {}", + device).c_str()); if ((err = snd_mixer_selem_register(handle, nullptr, nullptr)) < 0) - throw FormatRuntimeError("snd_mixer_selem_register() failed: %s", - snd_strerror(err)); + throw Alsa::MakeError(err, "snd_mixer_selem_register() failed"); if ((err = snd_mixer_load(handle)) < 0) - throw FormatRuntimeError("snd_mixer_load() failed: %s\n", - snd_strerror(err)); + throw Alsa::MakeError(err, "snd_mixer_load() failed"); elem = alsa_mixer_lookup_elem(handle, control, index); if (elem == nullptr) @@ -294,8 +294,7 @@ AlsaMixer::Open() err = snd_mixer_open(&handle, 0); if (err < 0) - throw FormatRuntimeError("snd_mixer_open() failed: %s", - snd_strerror(err)); + throw Alsa::MakeError(err, "snd_mixer_open() failed"); try { Setup(); @@ -325,8 +324,7 @@ AlsaMixer::GetVolume() err = snd_mixer_handle_events(handle); if (err < 0) - throw FormatRuntimeError("snd_mixer_handle_events() failed: %s", - snd_strerror(err)); + throw Alsa::MakeError(err, "snd_mixer_handle_events() failed"); int volume = GetPercentVolume(); if (resulting_volume >= 0 && volume == resulting_volume) @@ -343,8 +341,7 @@ AlsaMixer::SetVolume(unsigned volume) int err = set_normalized_playback_volume(elem, 0.01*volume, 1); if (err < 0) - throw FormatRuntimeError("failed to set ALSA volume: %s", - snd_strerror(err)); + throw Alsa::MakeError(err, "failed to set ALSA volume"); desired_volume = volume; resulting_volume = GetPercentVolume(); diff --git a/src/output/plugins/AlsaOutputPlugin.cxx b/src/output/plugins/AlsaOutputPlugin.cxx index 8b93a464f..9fd857d3e 100644 --- a/src/output/plugins/AlsaOutputPlugin.cxx +++ b/src/output/plugins/AlsaOutputPlugin.cxx @@ -20,6 +20,7 @@ #include "config.h" #include "AlsaOutputPlugin.hxx" #include "lib/alsa/AllowedFormat.hxx" +#include "lib/alsa/Error.hxx" #include "lib/alsa/HwSetup.hxx" #include "lib/alsa/NonBlock.hxx" #include "lib/alsa/PeriodBuffer.hxx" @@ -519,24 +520,20 @@ AlsaSetupSw(snd_pcm_t *pcm, snd_pcm_uframes_t start_threshold, int err = snd_pcm_sw_params_current(pcm, swparams); if (err < 0) - throw FormatRuntimeError("snd_pcm_sw_params_current() failed: %s", - snd_strerror(-err)); + throw Alsa::MakeError(err, "snd_pcm_sw_params_current() failed"); err = snd_pcm_sw_params_set_start_threshold(pcm, swparams, start_threshold); if (err < 0) - throw FormatRuntimeError("snd_pcm_sw_params_set_start_threshold() failed: %s", - snd_strerror(-err)); + throw Alsa::MakeError(err, "snd_pcm_sw_params_set_start_threshold() failed"); err = snd_pcm_sw_params_set_avail_min(pcm, swparams, avail_min); if (err < 0) - throw FormatRuntimeError("snd_pcm_sw_params_set_avail_min() failed: %s", - snd_strerror(-err)); + throw Alsa::MakeError(err, "snd_pcm_sw_params_set_avail_min() failed"); err = snd_pcm_sw_params(pcm, swparams); if (err < 0) - throw FormatRuntimeError("snd_pcm_sw_params() failed: %s", - snd_strerror(-err)); + throw Alsa::MakeError(err, "snd_pcm_sw_params() failed"); } inline void @@ -704,8 +701,9 @@ AlsaOutput::Open(AudioFormat &audio_format) int err = snd_pcm_open(&pcm, GetDevice(), SND_PCM_STREAM_PLAYBACK, mode); if (err < 0) - throw FormatRuntimeError("Failed to open ALSA device \"%s\": %s", - GetDevice(), snd_strerror(err)); + throw Alsa::MakeError(err, + fmt::format("Failed to open ALSA device \"{}\"", + GetDevice()).c_str()); FmtDebug(alsa_output_domain, "opened {} type={}", snd_pcm_name(pcm), @@ -897,8 +895,8 @@ AlsaOutput::DrainInternal() if (frames_written == -EAGAIN) return false; - throw FormatRuntimeError("snd_pcm_writei() failed: %s", - snd_strerror(-frames_written)); + throw Alsa::MakeError(frames_written, + "snd_pcm_writei() failed"); } /* need to call CopyRingToPeriodBuffer() and @@ -947,8 +945,7 @@ AlsaOutput::DrainInternal() else if (result == -EAGAIN) return false; else - throw FormatRuntimeError("snd_pcm_drain() failed: %s", - snd_strerror(-result)); + throw Alsa::MakeError(result, "snd_pcm_drain() failed"); } void @@ -1147,8 +1144,7 @@ try { int err = snd_pcm_prepare(pcm); if (err < 0) - throw FormatRuntimeError("snd_pcm_prepare() failed: %s", - snd_strerror(-err)); + throw Alsa::MakeError(err, "snd_pcm_prepare() failed"); } { @@ -1236,8 +1232,8 @@ try { return; if (Recover(frames_written) < 0) - throw FormatRuntimeError("snd_pcm_writei() failed: %s", - snd_strerror(-frames_written)); + throw Alsa::MakeError(frames_written, + "snd_pcm_writei() failed"); /* recovered; try again in the next DispatchSockets() call */