aboutsummaryrefslogtreecommitdiffstats
path: root/alc/backends/alsa.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'alc/backends/alsa.cpp')
-rw-r--r--alc/backends/alsa.cpp351
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}};