diff options
Diffstat (limited to 'alc/backends/alsa.cpp')
-rw-r--r-- | alc/backends/alsa.cpp | 351 |
1 files changed, 179 insertions, 172 deletions
diff --git a/alc/backends/alsa.cpp b/alc/backends/alsa.cpp index 7dc3c3c4..d620a83c 100644 --- a/alc/backends/alsa.cpp +++ b/alc/backends/alsa.cpp @@ -20,7 +20,7 @@ #include "config.h" -#include "backends/alsa.h" +#include "alsa.h" #include <algorithm> #include <atomic> @@ -35,18 +35,15 @@ #include <thread> #include <utility> -#include "AL/al.h" - #include "albyte.h" -#include "alcmain.h" -#include "alconfig.h" -#include "alexcpt.h" +#include "alc/alconfig.h" #include "almalloc.h" #include "alnumeric.h" #include "aloptional.h" -#include "alu.h" +#include "core/device.h" +#include "core/helpers.h" +#include "core/logging.h" #include "dynload.h" -#include "logging.h" #include "ringbuffer.h" #include "threads.h" #include "vector.h" @@ -56,7 +53,7 @@ namespace { -constexpr ALCchar alsaDevice[] = "ALSA Default"; +constexpr char alsaDevice[] = "ALSA Default"; #ifdef HAVE_DYNLOAD @@ -71,35 +68,37 @@ constexpr ALCchar alsaDevice[] = "ALSA Default"; MAGIC(snd_pcm_hw_params_free); \ MAGIC(snd_pcm_hw_params_any); \ MAGIC(snd_pcm_hw_params_current); \ + MAGIC(snd_pcm_hw_params_get_access); \ + MAGIC(snd_pcm_hw_params_get_buffer_size); \ + MAGIC(snd_pcm_hw_params_get_buffer_time_min); \ + MAGIC(snd_pcm_hw_params_get_buffer_time_max); \ + MAGIC(snd_pcm_hw_params_get_channels); \ + MAGIC(snd_pcm_hw_params_get_period_size); \ + MAGIC(snd_pcm_hw_params_get_period_time_max); \ + MAGIC(snd_pcm_hw_params_get_period_time_min); \ + MAGIC(snd_pcm_hw_params_get_periods); \ MAGIC(snd_pcm_hw_params_set_access); \ - MAGIC(snd_pcm_hw_params_set_format); \ + MAGIC(snd_pcm_hw_params_set_buffer_size_min); \ + MAGIC(snd_pcm_hw_params_set_buffer_size_near); \ + MAGIC(snd_pcm_hw_params_set_buffer_time_near); \ MAGIC(snd_pcm_hw_params_set_channels); \ + MAGIC(snd_pcm_hw_params_set_channels_near); \ + MAGIC(snd_pcm_hw_params_set_format); \ + MAGIC(snd_pcm_hw_params_set_period_time_near); \ + MAGIC(snd_pcm_hw_params_set_period_size_near); \ MAGIC(snd_pcm_hw_params_set_periods_near); \ MAGIC(snd_pcm_hw_params_set_rate_near); \ MAGIC(snd_pcm_hw_params_set_rate); \ MAGIC(snd_pcm_hw_params_set_rate_resample); \ - MAGIC(snd_pcm_hw_params_set_buffer_time_near); \ - MAGIC(snd_pcm_hw_params_set_period_time_near); \ - MAGIC(snd_pcm_hw_params_set_buffer_size_near); \ - MAGIC(snd_pcm_hw_params_set_period_size_near); \ - MAGIC(snd_pcm_hw_params_set_buffer_size_min); \ - MAGIC(snd_pcm_hw_params_get_buffer_time_min); \ - MAGIC(snd_pcm_hw_params_get_buffer_time_max); \ - MAGIC(snd_pcm_hw_params_get_period_time_min); \ - MAGIC(snd_pcm_hw_params_get_period_time_max); \ - MAGIC(snd_pcm_hw_params_get_buffer_size); \ - MAGIC(snd_pcm_hw_params_get_period_size); \ - MAGIC(snd_pcm_hw_params_get_access); \ - MAGIC(snd_pcm_hw_params_get_periods); \ MAGIC(snd_pcm_hw_params_test_format); \ MAGIC(snd_pcm_hw_params_test_channels); \ MAGIC(snd_pcm_hw_params); \ - MAGIC(snd_pcm_sw_params_malloc); \ + MAGIC(snd_pcm_sw_params); \ MAGIC(snd_pcm_sw_params_current); \ + MAGIC(snd_pcm_sw_params_free); \ + MAGIC(snd_pcm_sw_params_malloc); \ MAGIC(snd_pcm_sw_params_set_avail_min); \ MAGIC(snd_pcm_sw_params_set_stop_threshold); \ - MAGIC(snd_pcm_sw_params); \ - MAGIC(snd_pcm_sw_params_free); \ MAGIC(snd_pcm_prepare); \ MAGIC(snd_pcm_start); \ MAGIC(snd_pcm_resume); \ @@ -108,7 +107,6 @@ constexpr ALCchar alsaDevice[] = "ALSA Default"; MAGIC(snd_pcm_delay); \ MAGIC(snd_pcm_state); \ MAGIC(snd_pcm_avail_update); \ - MAGIC(snd_pcm_areas_silence); \ MAGIC(snd_pcm_mmap_begin); \ MAGIC(snd_pcm_mmap_commit); \ MAGIC(snd_pcm_readi); \ @@ -134,7 +132,7 @@ constexpr ALCchar alsaDevice[] = "ALSA Default"; MAGIC(snd_card_next); \ MAGIC(snd_config_update_free_global) -static void *alsa_handle; +void *alsa_handle; #define MAKE_FUNC(f) decltype(f) * p##f ALSA_FUNCS(MAKE_FUNC); #undef MAKE_FUNC @@ -153,6 +151,7 @@ ALSA_FUNCS(MAKE_FUNC); #define snd_pcm_hw_params_set_access psnd_pcm_hw_params_set_access #define snd_pcm_hw_params_set_format psnd_pcm_hw_params_set_format #define snd_pcm_hw_params_set_channels psnd_pcm_hw_params_set_channels +#define snd_pcm_hw_params_set_channels_near psnd_pcm_hw_params_set_channels_near #define snd_pcm_hw_params_set_periods_near psnd_pcm_hw_params_set_periods_near #define snd_pcm_hw_params_set_rate_near psnd_pcm_hw_params_set_rate_near #define snd_pcm_hw_params_set_rate psnd_pcm_hw_params_set_rate @@ -170,6 +169,7 @@ ALSA_FUNCS(MAKE_FUNC); #define snd_pcm_hw_params_get_period_size psnd_pcm_hw_params_get_period_size #define snd_pcm_hw_params_get_access psnd_pcm_hw_params_get_access #define snd_pcm_hw_params_get_periods psnd_pcm_hw_params_get_periods +#define snd_pcm_hw_params_get_channels psnd_pcm_hw_params_get_channels #define snd_pcm_hw_params_test_format psnd_pcm_hw_params_test_format #define snd_pcm_hw_params_test_channels psnd_pcm_hw_params_test_channels #define snd_pcm_hw_params psnd_pcm_hw_params @@ -187,7 +187,6 @@ ALSA_FUNCS(MAKE_FUNC); #define snd_pcm_delay psnd_pcm_delay #define snd_pcm_state psnd_pcm_state #define snd_pcm_avail_update psnd_pcm_avail_update -#define snd_pcm_areas_silence psnd_pcm_areas_silence #define snd_pcm_mmap_begin psnd_pcm_mmap_begin #define snd_pcm_mmap_commit psnd_pcm_mmap_commit #define snd_pcm_readi psnd_pcm_readi @@ -242,6 +241,11 @@ SwParamsPtr CreateSwParams() struct DevMap { std::string name; std::string device_name; + + template<typename T, typename U> + DevMap(T&& name_, U&& devname) + : name{std::forward<T>(name_)}, device_name{std::forward<U>(devname)} + { } }; al::vector<DevMap> PlaybackDevices; @@ -263,29 +267,36 @@ al::vector<DevMap> probe_devices(snd_pcm_stream_t stream) snd_pcm_info_t *pcminfo; snd_pcm_info_malloc(&pcminfo); - devlist.emplace_back(DevMap{alsaDevice, - GetConfigValue(nullptr, "alsa", (stream==SND_PCM_STREAM_PLAYBACK) ? "device" : "capture", - "default")}); + auto defname = ConfigValueStr(nullptr, "alsa", + (stream == SND_PCM_STREAM_PLAYBACK) ? "device" : "capture"); + devlist.emplace_back(alsaDevice, defname ? defname->c_str() : "default"); - const char *customdevs{GetConfigValue(nullptr, "alsa", - (stream == SND_PCM_STREAM_PLAYBACK) ? "custom-devices" : "custom-captures", "")}; - while(const char *curdev{customdevs}) + if(auto customdevs = ConfigValueStr(nullptr, "alsa", + (stream == SND_PCM_STREAM_PLAYBACK) ? "custom-devices" : "custom-captures")) { - if(!curdev[0]) break; - customdevs = strchr(curdev, ';'); - const char *sep{strchr(curdev, '=')}; - if(!sep) + size_t nextpos{customdevs->find_first_not_of(';')}; + size_t curpos; + while((curpos=nextpos) < customdevs->length()) { - std::string spec{customdevs ? std::string(curdev, customdevs++) : std::string(curdev)}; - ERR("Invalid ALSA device specification \"%s\"\n", spec.c_str()); - continue; - } + nextpos = customdevs->find_first_of(';', curpos+1); - const char *oldsep{sep++}; - devlist.emplace_back(DevMap{std::string(curdev, oldsep), - customdevs ? std::string(sep, customdevs++) : std::string(sep)}); - const auto &entry = devlist.back(); - TRACE("Got device \"%s\", \"%s\"\n", entry.name.c_str(), entry.device_name.c_str()); + size_t seppos{customdevs->find_first_of('=', curpos)}; + if(seppos == curpos || seppos >= nextpos) + { + std::string spec{customdevs->substr(curpos, nextpos-curpos)}; + ERR("Invalid ALSA device specification \"%s\"\n", spec.c_str()); + } + else + { + devlist.emplace_back(customdevs->substr(curpos, seppos-curpos), + customdevs->substr(seppos+1, nextpos-seppos-1)); + const auto &entry = devlist.back(); + TRACE("Got device \"%s\", \"%s\"\n", entry.name.c_str(), entry.device_name.c_str()); + } + + if(nextpos < customdevs->length()) + nextpos = customdevs->find_first_not_of(';', nextpos+1); + } } const std::string main_prefix{ @@ -319,13 +330,13 @@ al::vector<DevMap> probe_devices(snd_pcm_stream_t stream) ConfigValueStr(nullptr, "alsa", name.c_str()).value_or(main_prefix)}; int dev{-1}; - while(1) + while(true) { if(snd_ctl_pcm_next_device(handle, &dev) < 0) ERR("snd_ctl_pcm_next_device failed\n"); if(dev < 0) break; - snd_pcm_info_set_device(pcminfo, static_cast<ALuint>(dev)); + snd_pcm_info_set_device(pcminfo, static_cast<uint>(dev)); snd_pcm_info_set_subdevice(pcminfo, 0); snd_pcm_info_set_stream(pcminfo, stream); if((err=snd_ctl_pcm_info(handle, pcminfo)) < 0) @@ -361,7 +372,7 @@ al::vector<DevMap> probe_devices(snd_pcm_stream_t stream) device += ",DEV="; device += std::to_string(dev); - devlist.emplace_back(DevMap{std::move(name), std::move(device)}); + devlist.emplace_back(std::move(name), std::move(device)); const auto &entry = devlist.back(); TRACE("Got device \"%s\", \"%s\"\n", entry.name.c_str(), entry.device_name.c_str()); } @@ -410,21 +421,24 @@ int verify_state(snd_pcm_t *handle) struct AlsaPlayback final : public BackendBase { - AlsaPlayback(ALCdevice *device) noexcept : BackendBase{device} { } + AlsaPlayback(DeviceBase *device) noexcept : BackendBase{device} { } ~AlsaPlayback() override; int mixerProc(); int mixerNoMMapProc(); - void open(const ALCchar *name) override; + void open(const char *name) override; bool reset() override; - bool start() override; + void start() override; void stop() override; ClockLatency getClockLatency() override; snd_pcm_t *mPcmHandle{nullptr}; + std::mutex mMutex; + + uint mFrameStep{}; al::vector<al::byte> mBuffer; std::atomic<bool> mKillNow{true}; @@ -454,7 +468,7 @@ int AlsaPlayback::mixerProc() if(state < 0) { ERR("Invalid state detected: %s\n", snd_strerror(state)); - aluHandleDisconnect(mDevice, "Bad state: %s", snd_strerror(state)); + mDevice->handleDisconnect("Bad state: %s", snd_strerror(state)); break; } @@ -492,7 +506,7 @@ int AlsaPlayback::mixerProc() avail -= avail%update_size; // it is possible that contiguous areas are smaller, thus we use a loop - std::lock_guard<AlsaPlayback> _{*this}; + std::lock_guard<std::mutex> _{mMutex}; while(avail > 0) { snd_pcm_uframes_t frames{avail}; @@ -507,10 +521,10 @@ int AlsaPlayback::mixerProc() } char *WritePtr{static_cast<char*>(areas->addr) + (offset * areas->step / 8)}; - aluMixData(mDevice, WritePtr, static_cast<ALuint>(frames)); + mDevice->renderSamples(WritePtr, static_cast<uint>(frames), mFrameStep); snd_pcm_sframes_t commitres{snd_pcm_mmap_commit(mPcmHandle, offset, frames)}; - if(commitres < 0 || (static_cast<snd_pcm_uframes_t>(commitres)-frames) != 0) + if(commitres < 0 || static_cast<snd_pcm_uframes_t>(commitres) != frames) { ERR("mmap commit error: %s\n", snd_strerror(commitres >= 0 ? -EPIPE : static_cast<int>(commitres))); @@ -537,7 +551,7 @@ int AlsaPlayback::mixerNoMMapProc() if(state < 0) { ERR("Invalid state detected: %s\n", snd_strerror(state)); - aluHandleDisconnect(mDevice, "Bad state: %s", snd_strerror(state)); + mDevice->handleDisconnect("Bad state: %s", snd_strerror(state)); break; } @@ -571,10 +585,10 @@ int AlsaPlayback::mixerNoMMapProc() continue; } - std::lock_guard<AlsaPlayback> _{*this}; al::byte *WritePtr{mBuffer.data()}; avail = snd_pcm_bytes_to_frames(mPcmHandle, static_cast<ssize_t>(mBuffer.size())); - aluMixData(mDevice, WritePtr, static_cast<ALuint>(avail)); + std::lock_guard<std::mutex> _{mMutex}; + mDevice->renderSamples(WritePtr, static_cast<uint>(avail), mFrameStep); while(avail > 0) { snd_pcm_sframes_t ret{snd_pcm_writei(mPcmHandle, WritePtr, @@ -612,33 +626,37 @@ int AlsaPlayback::mixerNoMMapProc() } -void AlsaPlayback::open(const ALCchar *name) +void AlsaPlayback::open(const char *name) { - const char *driver{}; + std::string driver{"default"}; if(name) { if(PlaybackDevices.empty()) PlaybackDevices = probe_devices(SND_PCM_STREAM_PLAYBACK); auto iter = std::find_if(PlaybackDevices.cbegin(), PlaybackDevices.cend(), - [name](const DevMap &entry) -> bool - { return entry.name == name; } - ); + [name](const DevMap &entry) -> bool { return entry.name == name; }); if(iter == PlaybackDevices.cend()) - throw al::backend_exception{ALC_INVALID_VALUE, "Device name \"%s\" not found", name}; - driver = iter->device_name.c_str(); + throw al::backend_exception{al::backend_error::NoDevice, + "Device name \"%s\" not found", name}; + driver = iter->device_name; } else { name = alsaDevice; - driver = GetConfigValue(nullptr, "alsa", "device", "default"); + if(auto driveropt = ConfigValueStr(nullptr, "alsa", "device")) + driver = std::move(driveropt).value(); } + TRACE("Opening device \"%s\"\n", driver.c_str()); - TRACE("Opening device \"%s\"\n", driver); - int err{snd_pcm_open(&mPcmHandle, driver, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)}; + snd_pcm_t *pcmHandle{}; + int err{snd_pcm_open(&pcmHandle, driver.c_str(), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)}; if(err < 0) - throw al::backend_exception{ALC_OUT_OF_MEMORY, "Could not open ALSA device \"%s\"", - driver}; + throw al::backend_exception{al::backend_error::NoDevice, + "Could not open ALSA device \"%s\"", driver.c_str()}; + if(mPcmHandle) + snd_pcm_close(mPcmHandle); + mPcmHandle = pcmHandle; /* Free alsa's global config tree. Otherwise valgrind reports a ton of leaks. */ snd_config_update_free_global(); @@ -674,16 +692,17 @@ bool AlsaPlayback::reset() break; } - bool allowmmap{!!GetConfigValueBool(mDevice->DeviceName.c_str(), "alsa", "mmap", 1)}; - ALuint periodLen{static_cast<ALuint>(mDevice->UpdateSize * 1000000_u64 / mDevice->Frequency)}; - ALuint bufferLen{static_cast<ALuint>(mDevice->BufferSize * 1000000_u64 / mDevice->Frequency)}; - ALuint rate{mDevice->Frequency}; + bool allowmmap{!!GetConfigValueBool(mDevice->DeviceName.c_str(), "alsa", "mmap", true)}; + uint periodLen{static_cast<uint>(mDevice->UpdateSize * 1000000_u64 / mDevice->Frequency)}; + uint bufferLen{static_cast<uint>(mDevice->BufferSize * 1000000_u64 / mDevice->Frequency)}; + uint rate{mDevice->Frequency}; int err{}; HwParamsPtr hp{CreateHwParams()}; #define CHECK(x) do { \ if((err=(x)) < 0) \ - throw al::backend_exception{ALC_INVALID_VALUE, #x " failed: %s", snd_strerror(err)}; \ + throw al::backend_exception{al::backend_error::DeviceError, #x " failed: %s", \ + snd_strerror(err)}; \ } while(0) CHECK(snd_pcm_hw_params_any(mPcmHandle, hp.get())); /* set interleaved access */ @@ -720,37 +739,25 @@ bool AlsaPlayback::reset() } } CHECK(snd_pcm_hw_params_set_format(mPcmHandle, hp.get(), format)); - /* test and set channels (implicitly sets frame bits) */ - if(snd_pcm_hw_params_test_channels(mPcmHandle, hp.get(), mDevice->channelsFromFmt()) < 0) + /* set channels (implicitly sets frame bits) */ + if(snd_pcm_hw_params_set_channels(mPcmHandle, hp.get(), mDevice->channelsFromFmt()) < 0) { - static const DevFmtChannels channellist[] = { - DevFmtStereo, - DevFmtQuad, - DevFmtX51, - DevFmtX71, - DevFmtMono, - }; - - for(const auto &chan : channellist) - { - if(snd_pcm_hw_params_test_channels(mPcmHandle, hp.get(), ChannelsFromDevFmt(chan, 0)) >= 0) - { - mDevice->FmtChans = chan; - mDevice->mAmbiOrder = 0; - break; - } - } + uint numchans{2u}; + CHECK(snd_pcm_hw_params_set_channels_near(mPcmHandle, hp.get(), &numchans)); + if(numchans < 1) + throw al::backend_exception{al::backend_error::DeviceError, "Got 0 device channels"}; + if(numchans == 1) mDevice->FmtChans = DevFmtMono; + else mDevice->FmtChans = DevFmtStereo; } - CHECK(snd_pcm_hw_params_set_channels(mPcmHandle, hp.get(), mDevice->channelsFromFmt())); /* set rate (implicitly constrains period/buffer parameters) */ - if(!GetConfigValueBool(mDevice->DeviceName.c_str(), "alsa", "allow-resampler", 0) || - !mDevice->Flags.get<FrequencyRequest>()) + if(!GetConfigValueBool(mDevice->DeviceName.c_str(), "alsa", "allow-resampler", false) + || !mDevice->Flags.test(FrequencyRequest)) { if(snd_pcm_hw_params_set_rate_resample(mPcmHandle, hp.get(), 0) < 0) - ERR("Failed to disable ALSA resampler\n"); + WARN("Failed to disable ALSA resampler\n"); } else if(snd_pcm_hw_params_set_rate_resample(mPcmHandle, hp.get(), 1) < 0) - ERR("Failed to enable ALSA resampler\n"); + WARN("Failed to enable ALSA resampler\n"); CHECK(snd_pcm_hw_params_set_rate_near(mPcmHandle, hp.get(), &rate, nullptr)); /* set period time (implicitly constrains period/buffer parameters) */ if((err=snd_pcm_hw_params_set_period_time_near(mPcmHandle, hp.get(), &periodLen, nullptr)) < 0) @@ -769,6 +776,7 @@ bool AlsaPlayback::reset() CHECK(snd_pcm_hw_params_get_access(hp.get(), &access)); CHECK(snd_pcm_hw_params_get_period_size(hp.get(), &periodSizeInFrames, nullptr)); CHECK(snd_pcm_hw_params_get_buffer_size(hp.get(), &bufferSizeInFrames)); + CHECK(snd_pcm_hw_params_get_channels(hp.get(), &mFrameStep)); hp = nullptr; SwParamsPtr sp{CreateSwParams()}; @@ -779,58 +787,52 @@ bool AlsaPlayback::reset() #undef CHECK sp = nullptr; - mDevice->BufferSize = static_cast<ALuint>(bufferSizeInFrames); - mDevice->UpdateSize = static_cast<ALuint>(periodSizeInFrames); + mDevice->BufferSize = static_cast<uint>(bufferSizeInFrames); + mDevice->UpdateSize = static_cast<uint>(periodSizeInFrames); mDevice->Frequency = rate; - SetDefaultChannelOrder(mDevice); + setDefaultChannelOrder(); return true; } -bool AlsaPlayback::start() +void AlsaPlayback::start() { int err{}; snd_pcm_access_t access{}; HwParamsPtr hp{CreateHwParams()}; #define CHECK(x) do { \ if((err=(x)) < 0) \ - throw al::backend_exception{ALC_INVALID_VALUE, #x " failed: %s", snd_strerror(err)}; \ + throw al::backend_exception{al::backend_error::DeviceError, #x " failed: %s", \ + snd_strerror(err)}; \ } while(0) CHECK(snd_pcm_hw_params_current(mPcmHandle, hp.get())); /* retrieve configuration info */ CHECK(snd_pcm_hw_params_get_access(hp.get(), &access)); -#undef CHECK hp = nullptr; int (AlsaPlayback::*thread_func)(){}; if(access == SND_PCM_ACCESS_RW_INTERLEAVED) { - mBuffer.resize( - static_cast<size_t>(snd_pcm_frames_to_bytes(mPcmHandle, mDevice->UpdateSize))); + auto datalen = snd_pcm_frames_to_bytes(mPcmHandle, mDevice->UpdateSize); + mBuffer.resize(static_cast<size_t>(datalen)); thread_func = &AlsaPlayback::mixerNoMMapProc; } else { - err = snd_pcm_prepare(mPcmHandle); - if(err < 0) - { - ERR("snd_pcm_prepare(data->mPcmHandle) failed: %s\n", snd_strerror(err)); - return false; - } + CHECK(snd_pcm_prepare(mPcmHandle)); thread_func = &AlsaPlayback::mixerProc; } +#undef CHECK try { mKillNow.store(false, std::memory_order_release); mThread = std::thread{std::mem_fn(thread_func), this}; - return true; } catch(std::exception& e) { - ERR("Could not create playback thread: %s\n", e.what()); + throw al::backend_exception{al::backend_error::DeviceError, + "Failed to start mixing thread: %s", e.what()}; } - mBuffer.clear(); - return false; } void AlsaPlayback::stop() @@ -840,13 +842,16 @@ void AlsaPlayback::stop() mThread.join(); mBuffer.clear(); + int err{snd_pcm_drop(mPcmHandle)}; + if(err < 0) + ERR("snd_pcm_drop failed: %s\n", snd_strerror(err)); } ClockLatency AlsaPlayback::getClockLatency() { ClockLatency ret; - std::lock_guard<AlsaPlayback> _{*this}; + std::lock_guard<std::mutex> _{mMutex}; ret.ClockTime = GetDeviceClockTime(mDevice); snd_pcm_sframes_t delay{}; int err{snd_pcm_delay(mPcmHandle, &delay)}; @@ -863,14 +868,14 @@ ClockLatency AlsaPlayback::getClockLatency() struct AlsaCapture final : public BackendBase { - AlsaCapture(ALCdevice *device) noexcept : BackendBase{device} { } + AlsaCapture(DeviceBase *device) noexcept : BackendBase{device} { } ~AlsaCapture() override; - void open(const ALCchar *name) override; - bool start() override; + void open(const char *name) override; + void start() override; void stop() override; - ALCenum captureSamples(al::byte *buffer, ALCuint samples) override; - ALCuint availableSamples() override; + void captureSamples(al::byte *buffer, uint samples) override; + uint availableSamples() override; ClockLatency getClockLatency() override; snd_pcm_t *mPcmHandle{nullptr}; @@ -893,33 +898,33 @@ AlsaCapture::~AlsaCapture() } -void AlsaCapture::open(const ALCchar *name) +void AlsaCapture::open(const char *name) { - const char *driver{}; + std::string driver{"default"}; if(name) { if(CaptureDevices.empty()) CaptureDevices = probe_devices(SND_PCM_STREAM_CAPTURE); auto iter = std::find_if(CaptureDevices.cbegin(), CaptureDevices.cend(), - [name](const DevMap &entry) -> bool - { return entry.name == name; } - ); + [name](const DevMap &entry) -> bool { return entry.name == name; }); if(iter == CaptureDevices.cend()) - throw al::backend_exception{ALC_INVALID_VALUE, "Device name \"%s\" not found", name}; - driver = iter->device_name.c_str(); + throw al::backend_exception{al::backend_error::NoDevice, + "Device name \"%s\" not found", name}; + driver = iter->device_name; } else { name = alsaDevice; - driver = GetConfigValue(nullptr, "alsa", "capture", "default"); + if(auto driveropt = ConfigValueStr(nullptr, "alsa", "capture")) + driver = std::move(driveropt).value(); } - TRACE("Opening device \"%s\"\n", driver); - int err{snd_pcm_open(&mPcmHandle, driver, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK)}; + TRACE("Opening device \"%s\"\n", driver.c_str()); + int err{snd_pcm_open(&mPcmHandle, driver.c_str(), SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK)}; if(err < 0) - throw al::backend_exception{ALC_OUT_OF_MEMORY, "Could not open ALSA device \"%s\"", - driver}; + throw al::backend_exception{al::backend_error::NoDevice, + "Could not open ALSA device \"%s\"", driver.c_str()}; /* Free alsa's global config tree. Otherwise valgrind reports a ton of leaks. */ snd_config_update_free_global(); @@ -957,7 +962,8 @@ void AlsaCapture::open(const ALCchar *name) HwParamsPtr hp{CreateHwParams()}; #define CHECK(x) do { \ if((err=(x)) < 0) \ - throw al::backend_exception{ALC_INVALID_VALUE, #x " failed: %s", snd_strerror(err)}; \ + throw al::backend_exception{al::backend_error::DeviceError, #x " failed: %s", \ + snd_strerror(err)}; \ } while(0) CHECK(snd_pcm_hw_params_any(mPcmHandle, hp.get())); /* set interleaved access */ @@ -985,26 +991,25 @@ void AlsaCapture::open(const ALCchar *name) hp = nullptr; if(needring) - mRing = CreateRingBuffer(mDevice->BufferSize, mDevice->frameSizeFromFmt(), false); + mRing = RingBuffer::Create(mDevice->BufferSize, mDevice->frameSizeFromFmt(), false); mDevice->DeviceName = name; } -bool AlsaCapture::start() +void AlsaCapture::start() { int err{snd_pcm_prepare(mPcmHandle)}; if(err < 0) - throw al::backend_exception{ALC_INVALID_VALUE, "snd_pcm_prepare failed: %s", + throw al::backend_exception{al::backend_error::DeviceError, "snd_pcm_prepare failed: %s", snd_strerror(err)}; err = snd_pcm_start(mPcmHandle); if(err < 0) - throw al::backend_exception{ALC_INVALID_VALUE, "snd_pcm_start failed: %s", + throw al::backend_exception{al::backend_error::DeviceError, "snd_pcm_start failed: %s", snd_strerror(err)}; mDoCapture = true; - return true; } void AlsaCapture::stop() @@ -1013,11 +1018,12 @@ void AlsaCapture::stop() * snd_pcm_drain is unreliable and snd_pcm_drop drops it. Capture what's * available now so it'll be available later after the drop. */ - ALCuint avail{availableSamples()}; + uint avail{availableSamples()}; if(!mRing && avail > 0) { /* The ring buffer implicitly captures when checking availability. - * Direct access needs to explicitly capture it into temp storage. */ + * Direct access needs to explicitly capture it into temp storage. + */ auto temp = al::vector<al::byte>( static_cast<size_t>(snd_pcm_frames_to_bytes(mPcmHandle, avail))); captureSamples(temp.data(), avail); @@ -1029,12 +1035,12 @@ void AlsaCapture::stop() mDoCapture = false; } -ALCenum AlsaCapture::captureSamples(al::byte *buffer, ALCuint samples) +void AlsaCapture::captureSamples(al::byte *buffer, uint samples) { if(mRing) { mRing->read(buffer, samples); - return ALC_NO_ERROR; + return; } mLastAvail -= samples; @@ -1072,7 +1078,7 @@ ALCenum AlsaCapture::captureSamples(al::byte *buffer, ALCuint samples) { const char *err{snd_strerror(static_cast<int>(amt))}; ERR("restore error: %s\n", err); - aluHandleDisconnect(mDevice, "Capture recovery failure: %s", err); + mDevice->handleDisconnect("Capture recovery failure: %s", err); break; } /* If the amount available is less than what's asked, we lost it @@ -1083,16 +1089,14 @@ ALCenum AlsaCapture::captureSamples(al::byte *buffer, ALCuint samples) } buffer = buffer + amt; - samples -= static_cast<ALCuint>(amt); + samples -= static_cast<uint>(amt); } if(samples > 0) std::fill_n(buffer, snd_pcm_frames_to_bytes(mPcmHandle, samples), al::byte((mDevice->FmtType == DevFmtUByte) ? 0x80 : 0)); - - return ALC_NO_ERROR; } -ALCuint AlsaCapture::availableSamples() +uint AlsaCapture::availableSamples() { snd_pcm_sframes_t avail{0}; if(mDevice->Connected.load(std::memory_order_acquire) && mDoCapture) @@ -1112,7 +1116,7 @@ ALCuint AlsaCapture::availableSamples() { const char *err{snd_strerror(static_cast<int>(avail))}; ERR("restore error: %s\n", err); - aluHandleDisconnect(mDevice, "Capture recovery failure: %s", err); + mDevice->handleDisconnect("Capture recovery failure: %s", err); } } @@ -1121,7 +1125,7 @@ ALCuint AlsaCapture::availableSamples() if(avail < 0) avail = 0; avail += snd_pcm_bytes_to_frames(mPcmHandle, static_cast<ssize_t>(mBuffer.size())); if(avail > mLastAvail) mLastAvail = avail; - return static_cast<ALCuint>(mLastAvail); + return static_cast<uint>(mLastAvail); } while(avail > 0) @@ -1148,7 +1152,7 @@ ALCuint AlsaCapture::availableSamples() { const char *err{snd_strerror(static_cast<int>(amt))}; ERR("restore error: %s\n", err); - aluHandleDisconnect(mDevice, "Capture recovery failure: %s", err); + mDevice->handleDisconnect("Capture recovery failure: %s", err); break; } avail = amt; @@ -1159,14 +1163,13 @@ ALCuint AlsaCapture::availableSamples() avail -= amt; } - return static_cast<ALCuint>(mRing->readSpace()); + return static_cast<uint>(mRing->readSpace()); } ClockLatency AlsaCapture::getClockLatency() { ClockLatency ret; - std::lock_guard<AlsaCapture> _{*this}; ret.ClockTime = GetDeviceClockTime(mDevice); snd_pcm_sframes_t delay{}; int err{snd_pcm_delay(mPcmHandle, &delay)}; @@ -1197,10 +1200,10 @@ bool AlsaBackendFactory::init() if(!alsa_handle) { WARN("Failed to load %s\n", "libasound.so.2"); - return ALC_FALSE; + return false; } - error = ALC_FALSE; + error = false; #define LOAD_FUNC(f) do { \ p##f = reinterpret_cast<decltype(p##f)>(GetSymbol(alsa_handle, #f)); \ if(p##f == nullptr) { \ @@ -1226,30 +1229,34 @@ bool AlsaBackendFactory::init() bool AlsaBackendFactory::querySupport(BackendType type) { return (type == BackendType::Playback || type == BackendType::Capture); } -void AlsaBackendFactory::probe(DevProbe type, std::string *outnames) +std::string AlsaBackendFactory::probe(BackendType type) { - auto add_device = [outnames](const DevMap &entry) -> void + std::string outnames; + + auto add_device = [&outnames](const DevMap &entry) -> void { /* +1 to also append the null char (to ensure a null-separated list and * double-null terminated list). */ - outnames->append(entry.name.c_str(), entry.name.length()+1); + outnames.append(entry.name.c_str(), entry.name.length()+1); }; switch(type) { - case DevProbe::Playback: - PlaybackDevices = probe_devices(SND_PCM_STREAM_PLAYBACK); - std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device); - break; + case BackendType::Playback: + PlaybackDevices = probe_devices(SND_PCM_STREAM_PLAYBACK); + std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device); + break; - case DevProbe::Capture: - CaptureDevices = probe_devices(SND_PCM_STREAM_CAPTURE); - std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device); - break; + case BackendType::Capture: + CaptureDevices = probe_devices(SND_PCM_STREAM_CAPTURE); + std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device); + break; } + + return outnames; } -BackendPtr AlsaBackendFactory::createBackend(ALCdevice *device, BackendType type) +BackendPtr AlsaBackendFactory::createBackend(DeviceBase *device, BackendType type) { if(type == BackendType::Playback) return BackendPtr{new AlsaPlayback{device}}; |