]> git.tdb.fi Git - ext/openal.git/blob - alc/backends/alsa.cpp
Import OpenAL Soft 1.23.1 sources
[ext/openal.git] / alc / backends / alsa.cpp
1 /**
2  * OpenAL cross platform audio library
3  * Copyright (C) 1999-2007 by authors.
4  * This library is free software; you can redistribute it and/or
5  *  modify it under the terms of the GNU Library General Public
6  *  License as published by the Free Software Foundation; either
7  *  version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  *  Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  *  License along with this library; if not, write to the
16  *  Free Software Foundation, Inc.,
17  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  * Or go to http://www.gnu.org/copyleft/lgpl.html
19  */
20
21 #include "config.h"
22
23 #include "alsa.h"
24
25 #include <algorithm>
26 #include <atomic>
27 #include <cassert>
28 #include <cerrno>
29 #include <chrono>
30 #include <cstring>
31 #include <exception>
32 #include <functional>
33 #include <memory>
34 #include <string>
35 #include <thread>
36 #include <utility>
37
38 #include "albyte.h"
39 #include "alc/alconfig.h"
40 #include "almalloc.h"
41 #include "alnumeric.h"
42 #include "aloptional.h"
43 #include "core/device.h"
44 #include "core/helpers.h"
45 #include "core/logging.h"
46 #include "dynload.h"
47 #include "ringbuffer.h"
48 #include "threads.h"
49 #include "vector.h"
50
51 #include <alsa/asoundlib.h>
52
53
54 namespace {
55
56 constexpr char alsaDevice[] = "ALSA Default";
57
58
59 #ifdef HAVE_DYNLOAD
60 #define ALSA_FUNCS(MAGIC)                                                     \
61     MAGIC(snd_strerror);                                                      \
62     MAGIC(snd_pcm_open);                                                      \
63     MAGIC(snd_pcm_close);                                                     \
64     MAGIC(snd_pcm_nonblock);                                                  \
65     MAGIC(snd_pcm_frames_to_bytes);                                           \
66     MAGIC(snd_pcm_bytes_to_frames);                                           \
67     MAGIC(snd_pcm_hw_params_malloc);                                          \
68     MAGIC(snd_pcm_hw_params_free);                                            \
69     MAGIC(snd_pcm_hw_params_any);                                             \
70     MAGIC(snd_pcm_hw_params_current);                                         \
71     MAGIC(snd_pcm_hw_params_get_access);                                      \
72     MAGIC(snd_pcm_hw_params_get_buffer_size);                                 \
73     MAGIC(snd_pcm_hw_params_get_buffer_time_min);                             \
74     MAGIC(snd_pcm_hw_params_get_buffer_time_max);                             \
75     MAGIC(snd_pcm_hw_params_get_channels);                                    \
76     MAGIC(snd_pcm_hw_params_get_period_size);                                 \
77     MAGIC(snd_pcm_hw_params_get_period_time_max);                             \
78     MAGIC(snd_pcm_hw_params_get_period_time_min);                             \
79     MAGIC(snd_pcm_hw_params_get_periods);                                     \
80     MAGIC(snd_pcm_hw_params_set_access);                                      \
81     MAGIC(snd_pcm_hw_params_set_buffer_size_min);                             \
82     MAGIC(snd_pcm_hw_params_set_buffer_size_near);                            \
83     MAGIC(snd_pcm_hw_params_set_buffer_time_near);                            \
84     MAGIC(snd_pcm_hw_params_set_channels);                                    \
85     MAGIC(snd_pcm_hw_params_set_channels_near);                               \
86     MAGIC(snd_pcm_hw_params_set_format);                                      \
87     MAGIC(snd_pcm_hw_params_set_period_time_near);                            \
88     MAGIC(snd_pcm_hw_params_set_period_size_near);                            \
89     MAGIC(snd_pcm_hw_params_set_periods_near);                                \
90     MAGIC(snd_pcm_hw_params_set_rate_near);                                   \
91     MAGIC(snd_pcm_hw_params_set_rate);                                        \
92     MAGIC(snd_pcm_hw_params_set_rate_resample);                               \
93     MAGIC(snd_pcm_hw_params_test_format);                                     \
94     MAGIC(snd_pcm_hw_params_test_channels);                                   \
95     MAGIC(snd_pcm_hw_params);                                                 \
96     MAGIC(snd_pcm_sw_params);                                                 \
97     MAGIC(snd_pcm_sw_params_current);                                         \
98     MAGIC(snd_pcm_sw_params_free);                                            \
99     MAGIC(snd_pcm_sw_params_malloc);                                          \
100     MAGIC(snd_pcm_sw_params_set_avail_min);                                   \
101     MAGIC(snd_pcm_sw_params_set_stop_threshold);                              \
102     MAGIC(snd_pcm_prepare);                                                   \
103     MAGIC(snd_pcm_start);                                                     \
104     MAGIC(snd_pcm_resume);                                                    \
105     MAGIC(snd_pcm_reset);                                                     \
106     MAGIC(snd_pcm_wait);                                                      \
107     MAGIC(snd_pcm_delay);                                                     \
108     MAGIC(snd_pcm_state);                                                     \
109     MAGIC(snd_pcm_avail_update);                                              \
110     MAGIC(snd_pcm_mmap_begin);                                                \
111     MAGIC(snd_pcm_mmap_commit);                                               \
112     MAGIC(snd_pcm_readi);                                                     \
113     MAGIC(snd_pcm_writei);                                                    \
114     MAGIC(snd_pcm_drain);                                                     \
115     MAGIC(snd_pcm_drop);                                                      \
116     MAGIC(snd_pcm_recover);                                                   \
117     MAGIC(snd_pcm_info_malloc);                                               \
118     MAGIC(snd_pcm_info_free);                                                 \
119     MAGIC(snd_pcm_info_set_device);                                           \
120     MAGIC(snd_pcm_info_set_subdevice);                                        \
121     MAGIC(snd_pcm_info_set_stream);                                           \
122     MAGIC(snd_pcm_info_get_name);                                             \
123     MAGIC(snd_ctl_pcm_next_device);                                           \
124     MAGIC(snd_ctl_pcm_info);                                                  \
125     MAGIC(snd_ctl_open);                                                      \
126     MAGIC(snd_ctl_close);                                                     \
127     MAGIC(snd_ctl_card_info_malloc);                                          \
128     MAGIC(snd_ctl_card_info_free);                                            \
129     MAGIC(snd_ctl_card_info);                                                 \
130     MAGIC(snd_ctl_card_info_get_name);                                        \
131     MAGIC(snd_ctl_card_info_get_id);                                          \
132     MAGIC(snd_card_next);                                                     \
133     MAGIC(snd_config_update_free_global)
134
135 void *alsa_handle;
136 #define MAKE_FUNC(f) decltype(f) * p##f
137 ALSA_FUNCS(MAKE_FUNC);
138 #undef MAKE_FUNC
139
140 #ifndef IN_IDE_PARSER
141 #define snd_strerror psnd_strerror
142 #define snd_pcm_open psnd_pcm_open
143 #define snd_pcm_close psnd_pcm_close
144 #define snd_pcm_nonblock psnd_pcm_nonblock
145 #define snd_pcm_frames_to_bytes psnd_pcm_frames_to_bytes
146 #define snd_pcm_bytes_to_frames psnd_pcm_bytes_to_frames
147 #define snd_pcm_hw_params_malloc psnd_pcm_hw_params_malloc
148 #define snd_pcm_hw_params_free psnd_pcm_hw_params_free
149 #define snd_pcm_hw_params_any psnd_pcm_hw_params_any
150 #define snd_pcm_hw_params_current psnd_pcm_hw_params_current
151 #define snd_pcm_hw_params_set_access psnd_pcm_hw_params_set_access
152 #define snd_pcm_hw_params_set_format psnd_pcm_hw_params_set_format
153 #define snd_pcm_hw_params_set_channels psnd_pcm_hw_params_set_channels
154 #define snd_pcm_hw_params_set_channels_near psnd_pcm_hw_params_set_channels_near
155 #define snd_pcm_hw_params_set_periods_near psnd_pcm_hw_params_set_periods_near
156 #define snd_pcm_hw_params_set_rate_near psnd_pcm_hw_params_set_rate_near
157 #define snd_pcm_hw_params_set_rate psnd_pcm_hw_params_set_rate
158 #define snd_pcm_hw_params_set_rate_resample psnd_pcm_hw_params_set_rate_resample
159 #define snd_pcm_hw_params_set_buffer_time_near psnd_pcm_hw_params_set_buffer_time_near
160 #define snd_pcm_hw_params_set_period_time_near psnd_pcm_hw_params_set_period_time_near
161 #define snd_pcm_hw_params_set_buffer_size_near psnd_pcm_hw_params_set_buffer_size_near
162 #define snd_pcm_hw_params_set_period_size_near psnd_pcm_hw_params_set_period_size_near
163 #define snd_pcm_hw_params_set_buffer_size_min psnd_pcm_hw_params_set_buffer_size_min
164 #define snd_pcm_hw_params_get_buffer_time_min psnd_pcm_hw_params_get_buffer_time_min
165 #define snd_pcm_hw_params_get_buffer_time_max psnd_pcm_hw_params_get_buffer_time_max
166 #define snd_pcm_hw_params_get_period_time_min psnd_pcm_hw_params_get_period_time_min
167 #define snd_pcm_hw_params_get_period_time_max psnd_pcm_hw_params_get_period_time_max
168 #define snd_pcm_hw_params_get_buffer_size psnd_pcm_hw_params_get_buffer_size
169 #define snd_pcm_hw_params_get_period_size psnd_pcm_hw_params_get_period_size
170 #define snd_pcm_hw_params_get_access psnd_pcm_hw_params_get_access
171 #define snd_pcm_hw_params_get_periods psnd_pcm_hw_params_get_periods
172 #define snd_pcm_hw_params_get_channels psnd_pcm_hw_params_get_channels
173 #define snd_pcm_hw_params_test_format psnd_pcm_hw_params_test_format
174 #define snd_pcm_hw_params_test_channels psnd_pcm_hw_params_test_channels
175 #define snd_pcm_hw_params psnd_pcm_hw_params
176 #define snd_pcm_sw_params_malloc psnd_pcm_sw_params_malloc
177 #define snd_pcm_sw_params_current psnd_pcm_sw_params_current
178 #define snd_pcm_sw_params_set_avail_min psnd_pcm_sw_params_set_avail_min
179 #define snd_pcm_sw_params_set_stop_threshold psnd_pcm_sw_params_set_stop_threshold
180 #define snd_pcm_sw_params psnd_pcm_sw_params
181 #define snd_pcm_sw_params_free psnd_pcm_sw_params_free
182 #define snd_pcm_prepare psnd_pcm_prepare
183 #define snd_pcm_start psnd_pcm_start
184 #define snd_pcm_resume psnd_pcm_resume
185 #define snd_pcm_reset psnd_pcm_reset
186 #define snd_pcm_wait psnd_pcm_wait
187 #define snd_pcm_delay psnd_pcm_delay
188 #define snd_pcm_state psnd_pcm_state
189 #define snd_pcm_avail_update psnd_pcm_avail_update
190 #define snd_pcm_mmap_begin psnd_pcm_mmap_begin
191 #define snd_pcm_mmap_commit psnd_pcm_mmap_commit
192 #define snd_pcm_readi psnd_pcm_readi
193 #define snd_pcm_writei psnd_pcm_writei
194 #define snd_pcm_drain psnd_pcm_drain
195 #define snd_pcm_drop psnd_pcm_drop
196 #define snd_pcm_recover psnd_pcm_recover
197 #define snd_pcm_info_malloc psnd_pcm_info_malloc
198 #define snd_pcm_info_free psnd_pcm_info_free
199 #define snd_pcm_info_set_device psnd_pcm_info_set_device
200 #define snd_pcm_info_set_subdevice psnd_pcm_info_set_subdevice
201 #define snd_pcm_info_set_stream psnd_pcm_info_set_stream
202 #define snd_pcm_info_get_name psnd_pcm_info_get_name
203 #define snd_ctl_pcm_next_device psnd_ctl_pcm_next_device
204 #define snd_ctl_pcm_info psnd_ctl_pcm_info
205 #define snd_ctl_open psnd_ctl_open
206 #define snd_ctl_close psnd_ctl_close
207 #define snd_ctl_card_info_malloc psnd_ctl_card_info_malloc
208 #define snd_ctl_card_info_free psnd_ctl_card_info_free
209 #define snd_ctl_card_info psnd_ctl_card_info
210 #define snd_ctl_card_info_get_name psnd_ctl_card_info_get_name
211 #define snd_ctl_card_info_get_id psnd_ctl_card_info_get_id
212 #define snd_card_next psnd_card_next
213 #define snd_config_update_free_global psnd_config_update_free_global
214 #endif
215 #endif
216
217
218 struct HwParamsDeleter {
219     void operator()(snd_pcm_hw_params_t *ptr) { snd_pcm_hw_params_free(ptr); }
220 };
221 using HwParamsPtr = std::unique_ptr<snd_pcm_hw_params_t,HwParamsDeleter>;
222 HwParamsPtr CreateHwParams()
223 {
224     snd_pcm_hw_params_t *hp{};
225     snd_pcm_hw_params_malloc(&hp);
226     return HwParamsPtr{hp};
227 }
228
229 struct SwParamsDeleter {
230     void operator()(snd_pcm_sw_params_t *ptr) { snd_pcm_sw_params_free(ptr); }
231 };
232 using SwParamsPtr = std::unique_ptr<snd_pcm_sw_params_t,SwParamsDeleter>;
233 SwParamsPtr CreateSwParams()
234 {
235     snd_pcm_sw_params_t *sp{};
236     snd_pcm_sw_params_malloc(&sp);
237     return SwParamsPtr{sp};
238 }
239
240
241 struct DevMap {
242     std::string name;
243     std::string device_name;
244
245     template<typename T, typename U>
246     DevMap(T&& name_, U&& devname)
247         : name{std::forward<T>(name_)}, device_name{std::forward<U>(devname)}
248     { }
249 };
250
251 al::vector<DevMap> PlaybackDevices;
252 al::vector<DevMap> CaptureDevices;
253
254
255 const char *prefix_name(snd_pcm_stream_t stream)
256 {
257     assert(stream == SND_PCM_STREAM_PLAYBACK || stream == SND_PCM_STREAM_CAPTURE);
258     return (stream==SND_PCM_STREAM_PLAYBACK) ? "device-prefix" : "capture-prefix";
259 }
260
261 al::vector<DevMap> probe_devices(snd_pcm_stream_t stream)
262 {
263     al::vector<DevMap> devlist;
264
265     snd_ctl_card_info_t *info;
266     snd_ctl_card_info_malloc(&info);
267     snd_pcm_info_t *pcminfo;
268     snd_pcm_info_malloc(&pcminfo);
269
270     auto defname = ConfigValueStr(nullptr, "alsa",
271         (stream == SND_PCM_STREAM_PLAYBACK) ? "device" : "capture");
272     devlist.emplace_back(alsaDevice, defname ? defname->c_str() : "default");
273
274     if(auto customdevs = ConfigValueStr(nullptr, "alsa",
275         (stream == SND_PCM_STREAM_PLAYBACK) ? "custom-devices" : "custom-captures"))
276     {
277         size_t nextpos{customdevs->find_first_not_of(';')};
278         size_t curpos;
279         while((curpos=nextpos) < customdevs->length())
280         {
281             nextpos = customdevs->find_first_of(';', curpos+1);
282
283             size_t seppos{customdevs->find_first_of('=', curpos)};
284             if(seppos == curpos || seppos >= nextpos)
285             {
286                 std::string spec{customdevs->substr(curpos, nextpos-curpos)};
287                 ERR("Invalid ALSA device specification \"%s\"\n", spec.c_str());
288             }
289             else
290             {
291                 devlist.emplace_back(customdevs->substr(curpos, seppos-curpos),
292                     customdevs->substr(seppos+1, nextpos-seppos-1));
293                 const auto &entry = devlist.back();
294                 TRACE("Got device \"%s\", \"%s\"\n", entry.name.c_str(), entry.device_name.c_str());
295             }
296
297             if(nextpos < customdevs->length())
298                 nextpos = customdevs->find_first_not_of(';', nextpos+1);
299         }
300     }
301
302     const std::string main_prefix{
303         ConfigValueStr(nullptr, "alsa", prefix_name(stream)).value_or("plughw:")};
304
305     int card{-1};
306     int err{snd_card_next(&card)};
307     for(;err >= 0 && card >= 0;err = snd_card_next(&card))
308     {
309         std::string name{"hw:" + std::to_string(card)};
310
311         snd_ctl_t *handle;
312         if((err=snd_ctl_open(&handle, name.c_str(), 0)) < 0)
313         {
314             ERR("control open (hw:%d): %s\n", card, snd_strerror(err));
315             continue;
316         }
317         if((err=snd_ctl_card_info(handle, info)) < 0)
318         {
319             ERR("control hardware info (hw:%d): %s\n", card, snd_strerror(err));
320             snd_ctl_close(handle);
321             continue;
322         }
323
324         const char *cardname{snd_ctl_card_info_get_name(info)};
325         const char *cardid{snd_ctl_card_info_get_id(info)};
326         name = prefix_name(stream);
327         name += '-';
328         name += cardid;
329         const std::string card_prefix{
330             ConfigValueStr(nullptr, "alsa", name.c_str()).value_or(main_prefix)};
331
332         int dev{-1};
333         while(true)
334         {
335             if(snd_ctl_pcm_next_device(handle, &dev) < 0)
336                 ERR("snd_ctl_pcm_next_device failed\n");
337             if(dev < 0) break;
338
339             snd_pcm_info_set_device(pcminfo, static_cast<uint>(dev));
340             snd_pcm_info_set_subdevice(pcminfo, 0);
341             snd_pcm_info_set_stream(pcminfo, stream);
342             if((err=snd_ctl_pcm_info(handle, pcminfo)) < 0)
343             {
344                 if(err != -ENOENT)
345                     ERR("control digital audio info (hw:%d): %s\n", card, snd_strerror(err));
346                 continue;
347             }
348
349             /* "prefix-cardid-dev" */
350             name = prefix_name(stream);
351             name += '-';
352             name += cardid;
353             name += '-';
354             name += std::to_string(dev);
355             const std::string device_prefix{
356                 ConfigValueStr(nullptr, "alsa", name.c_str()).value_or(card_prefix)};
357
358             /* "CardName, PcmName (CARD=cardid,DEV=dev)" */
359             name = cardname;
360             name += ", ";
361             name += snd_pcm_info_get_name(pcminfo);
362             name += " (CARD=";
363             name += cardid;
364             name += ",DEV=";
365             name += std::to_string(dev);
366             name += ')';
367
368             /* "devprefixCARD=cardid,DEV=dev" */
369             std::string device{device_prefix};
370             device += "CARD=";
371             device += cardid;
372             device += ",DEV=";
373             device += std::to_string(dev);
374             
375             devlist.emplace_back(std::move(name), std::move(device));
376             const auto &entry = devlist.back();
377             TRACE("Got device \"%s\", \"%s\"\n", entry.name.c_str(), entry.device_name.c_str());
378         }
379         snd_ctl_close(handle);
380     }
381     if(err < 0)
382         ERR("snd_card_next failed: %s\n", snd_strerror(err));
383
384     snd_pcm_info_free(pcminfo);
385     snd_ctl_card_info_free(info);
386
387     return devlist;
388 }
389
390
391 int verify_state(snd_pcm_t *handle)
392 {
393     snd_pcm_state_t state{snd_pcm_state(handle)};
394
395     int err;
396     switch(state)
397     {
398         case SND_PCM_STATE_OPEN:
399         case SND_PCM_STATE_SETUP:
400         case SND_PCM_STATE_PREPARED:
401         case SND_PCM_STATE_RUNNING:
402         case SND_PCM_STATE_DRAINING:
403         case SND_PCM_STATE_PAUSED:
404             /* All Okay */
405             break;
406
407         case SND_PCM_STATE_XRUN:
408             if((err=snd_pcm_recover(handle, -EPIPE, 1)) < 0)
409                 return err;
410             break;
411         case SND_PCM_STATE_SUSPENDED:
412             if((err=snd_pcm_recover(handle, -ESTRPIPE, 1)) < 0)
413                 return err;
414             break;
415         case SND_PCM_STATE_DISCONNECTED:
416             return -ENODEV;
417     }
418
419     return state;
420 }
421
422
423 struct AlsaPlayback final : public BackendBase {
424     AlsaPlayback(DeviceBase *device) noexcept : BackendBase{device} { }
425     ~AlsaPlayback() override;
426
427     int mixerProc();
428     int mixerNoMMapProc();
429
430     void open(const char *name) override;
431     bool reset() override;
432     void start() override;
433     void stop() override;
434
435     ClockLatency getClockLatency() override;
436
437     snd_pcm_t *mPcmHandle{nullptr};
438
439     std::mutex mMutex;
440
441     uint mFrameStep{};
442     al::vector<al::byte> mBuffer;
443
444     std::atomic<bool> mKillNow{true};
445     std::thread mThread;
446
447     DEF_NEWDEL(AlsaPlayback)
448 };
449
450 AlsaPlayback::~AlsaPlayback()
451 {
452     if(mPcmHandle)
453         snd_pcm_close(mPcmHandle);
454     mPcmHandle = nullptr;
455 }
456
457
458 int AlsaPlayback::mixerProc()
459 {
460     SetRTPriority();
461     althrd_setname(MIXER_THREAD_NAME);
462
463     const snd_pcm_uframes_t update_size{mDevice->UpdateSize};
464     const snd_pcm_uframes_t buffer_size{mDevice->BufferSize};
465     while(!mKillNow.load(std::memory_order_acquire))
466     {
467         int state{verify_state(mPcmHandle)};
468         if(state < 0)
469         {
470             ERR("Invalid state detected: %s\n", snd_strerror(state));
471             mDevice->handleDisconnect("Bad state: %s", snd_strerror(state));
472             break;
473         }
474
475         snd_pcm_sframes_t avails{snd_pcm_avail_update(mPcmHandle)};
476         if(avails < 0)
477         {
478             ERR("available update failed: %s\n", snd_strerror(static_cast<int>(avails)));
479             continue;
480         }
481         snd_pcm_uframes_t avail{static_cast<snd_pcm_uframes_t>(avails)};
482
483         if(avail > buffer_size)
484         {
485             WARN("available samples exceeds the buffer size\n");
486             snd_pcm_reset(mPcmHandle);
487             continue;
488         }
489
490         // make sure there's frames to process
491         if(avail < update_size)
492         {
493             if(state != SND_PCM_STATE_RUNNING)
494             {
495                 int err{snd_pcm_start(mPcmHandle)};
496                 if(err < 0)
497                 {
498                     ERR("start failed: %s\n", snd_strerror(err));
499                     continue;
500                 }
501             }
502             if(snd_pcm_wait(mPcmHandle, 1000) == 0)
503                 ERR("Wait timeout... buffer size too low?\n");
504             continue;
505         }
506         avail -= avail%update_size;
507
508         // it is possible that contiguous areas are smaller, thus we use a loop
509         std::lock_guard<std::mutex> _{mMutex};
510         while(avail > 0)
511         {
512             snd_pcm_uframes_t frames{avail};
513
514             const snd_pcm_channel_area_t *areas{};
515             snd_pcm_uframes_t offset{};
516             int err{snd_pcm_mmap_begin(mPcmHandle, &areas, &offset, &frames)};
517             if(err < 0)
518             {
519                 ERR("mmap begin error: %s\n", snd_strerror(err));
520                 break;
521             }
522
523             char *WritePtr{static_cast<char*>(areas->addr) + (offset * areas->step / 8)};
524             mDevice->renderSamples(WritePtr, static_cast<uint>(frames), mFrameStep);
525
526             snd_pcm_sframes_t commitres{snd_pcm_mmap_commit(mPcmHandle, offset, frames)};
527             if(commitres < 0 || static_cast<snd_pcm_uframes_t>(commitres) != frames)
528             {
529                 ERR("mmap commit error: %s\n",
530                     snd_strerror(commitres >= 0 ? -EPIPE : static_cast<int>(commitres)));
531                 break;
532             }
533
534             avail -= frames;
535         }
536     }
537
538     return 0;
539 }
540
541 int AlsaPlayback::mixerNoMMapProc()
542 {
543     SetRTPriority();
544     althrd_setname(MIXER_THREAD_NAME);
545
546     const snd_pcm_uframes_t update_size{mDevice->UpdateSize};
547     const snd_pcm_uframes_t buffer_size{mDevice->BufferSize};
548     while(!mKillNow.load(std::memory_order_acquire))
549     {
550         int state{verify_state(mPcmHandle)};
551         if(state < 0)
552         {
553             ERR("Invalid state detected: %s\n", snd_strerror(state));
554             mDevice->handleDisconnect("Bad state: %s", snd_strerror(state));
555             break;
556         }
557
558         snd_pcm_sframes_t avail{snd_pcm_avail_update(mPcmHandle)};
559         if(avail < 0)
560         {
561             ERR("available update failed: %s\n", snd_strerror(static_cast<int>(avail)));
562             continue;
563         }
564
565         if(static_cast<snd_pcm_uframes_t>(avail) > buffer_size)
566         {
567             WARN("available samples exceeds the buffer size\n");
568             snd_pcm_reset(mPcmHandle);
569             continue;
570         }
571
572         if(static_cast<snd_pcm_uframes_t>(avail) < update_size)
573         {
574             if(state != SND_PCM_STATE_RUNNING)
575             {
576                 int err{snd_pcm_start(mPcmHandle)};
577                 if(err < 0)
578                 {
579                     ERR("start failed: %s\n", snd_strerror(err));
580                     continue;
581                 }
582             }
583             if(snd_pcm_wait(mPcmHandle, 1000) == 0)
584                 ERR("Wait timeout... buffer size too low?\n");
585             continue;
586         }
587
588         al::byte *WritePtr{mBuffer.data()};
589         avail = snd_pcm_bytes_to_frames(mPcmHandle, static_cast<ssize_t>(mBuffer.size()));
590         std::lock_guard<std::mutex> _{mMutex};
591         mDevice->renderSamples(WritePtr, static_cast<uint>(avail), mFrameStep);
592         while(avail > 0)
593         {
594             snd_pcm_sframes_t ret{snd_pcm_writei(mPcmHandle, WritePtr,
595                 static_cast<snd_pcm_uframes_t>(avail))};
596             switch(ret)
597             {
598             case -EAGAIN:
599                 continue;
600 #if ESTRPIPE != EPIPE
601             case -ESTRPIPE:
602 #endif
603             case -EPIPE:
604             case -EINTR:
605                 ret = snd_pcm_recover(mPcmHandle, static_cast<int>(ret), 1);
606                 if(ret < 0)
607                     avail = 0;
608                 break;
609             default:
610                 if(ret >= 0)
611                 {
612                     WritePtr += snd_pcm_frames_to_bytes(mPcmHandle, ret);
613                     avail -= ret;
614                 }
615                 break;
616             }
617             if(ret < 0)
618             {
619                 ret = snd_pcm_prepare(mPcmHandle);
620                 if(ret < 0) break;
621             }
622         }
623     }
624
625     return 0;
626 }
627
628
629 void AlsaPlayback::open(const char *name)
630 {
631     std::string driver{"default"};
632     if(name)
633     {
634         if(PlaybackDevices.empty())
635             PlaybackDevices = probe_devices(SND_PCM_STREAM_PLAYBACK);
636
637         auto iter = std::find_if(PlaybackDevices.cbegin(), PlaybackDevices.cend(),
638             [name](const DevMap &entry) -> bool { return entry.name == name; });
639         if(iter == PlaybackDevices.cend())
640             throw al::backend_exception{al::backend_error::NoDevice,
641                 "Device name \"%s\" not found", name};
642         driver = iter->device_name;
643     }
644     else
645     {
646         name = alsaDevice;
647         if(auto driveropt = ConfigValueStr(nullptr, "alsa", "device"))
648             driver = std::move(driveropt).value();
649     }
650     TRACE("Opening device \"%s\"\n", driver.c_str());
651
652     snd_pcm_t *pcmHandle{};
653     int err{snd_pcm_open(&pcmHandle, driver.c_str(), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)};
654     if(err < 0)
655         throw al::backend_exception{al::backend_error::NoDevice,
656             "Could not open ALSA device \"%s\"", driver.c_str()};
657     if(mPcmHandle)
658         snd_pcm_close(mPcmHandle);
659     mPcmHandle = pcmHandle;
660
661     /* Free alsa's global config tree. Otherwise valgrind reports a ton of leaks. */
662     snd_config_update_free_global();
663
664     mDevice->DeviceName = name;
665 }
666
667 bool AlsaPlayback::reset()
668 {
669     snd_pcm_format_t format{SND_PCM_FORMAT_UNKNOWN};
670     switch(mDevice->FmtType)
671     {
672     case DevFmtByte:
673         format = SND_PCM_FORMAT_S8;
674         break;
675     case DevFmtUByte:
676         format = SND_PCM_FORMAT_U8;
677         break;
678     case DevFmtShort:
679         format = SND_PCM_FORMAT_S16;
680         break;
681     case DevFmtUShort:
682         format = SND_PCM_FORMAT_U16;
683         break;
684     case DevFmtInt:
685         format = SND_PCM_FORMAT_S32;
686         break;
687     case DevFmtUInt:
688         format = SND_PCM_FORMAT_U32;
689         break;
690     case DevFmtFloat:
691         format = SND_PCM_FORMAT_FLOAT;
692         break;
693     }
694
695     bool allowmmap{!!GetConfigValueBool(mDevice->DeviceName.c_str(), "alsa", "mmap", true)};
696     uint periodLen{static_cast<uint>(mDevice->UpdateSize * 1000000_u64 / mDevice->Frequency)};
697     uint bufferLen{static_cast<uint>(mDevice->BufferSize * 1000000_u64 / mDevice->Frequency)};
698     uint rate{mDevice->Frequency};
699
700     int err{};
701     HwParamsPtr hp{CreateHwParams()};
702 #define CHECK(x) do {                                                         \
703     if((err=(x)) < 0)                                                         \
704         throw al::backend_exception{al::backend_error::DeviceError, #x " failed: %s", \
705             snd_strerror(err)};                                               \
706 } while(0)
707     CHECK(snd_pcm_hw_params_any(mPcmHandle, hp.get()));
708     /* set interleaved access */
709     if(!allowmmap
710         || snd_pcm_hw_params_set_access(mPcmHandle, hp.get(), SND_PCM_ACCESS_MMAP_INTERLEAVED) < 0)
711     {
712         /* No mmap */
713         CHECK(snd_pcm_hw_params_set_access(mPcmHandle, hp.get(), SND_PCM_ACCESS_RW_INTERLEAVED));
714     }
715     /* test and set format (implicitly sets sample bits) */
716     if(snd_pcm_hw_params_test_format(mPcmHandle, hp.get(), format) < 0)
717     {
718         static const struct {
719             snd_pcm_format_t format;
720             DevFmtType fmttype;
721         } formatlist[] = {
722             { SND_PCM_FORMAT_FLOAT, DevFmtFloat  },
723             { SND_PCM_FORMAT_S32,   DevFmtInt    },
724             { SND_PCM_FORMAT_U32,   DevFmtUInt   },
725             { SND_PCM_FORMAT_S16,   DevFmtShort  },
726             { SND_PCM_FORMAT_U16,   DevFmtUShort },
727             { SND_PCM_FORMAT_S8,    DevFmtByte   },
728             { SND_PCM_FORMAT_U8,    DevFmtUByte  },
729         };
730
731         for(const auto &fmt : formatlist)
732         {
733             format = fmt.format;
734             if(snd_pcm_hw_params_test_format(mPcmHandle, hp.get(), format) >= 0)
735             {
736                 mDevice->FmtType = fmt.fmttype;
737                 break;
738             }
739         }
740     }
741     CHECK(snd_pcm_hw_params_set_format(mPcmHandle, hp.get(), format));
742     /* set channels (implicitly sets frame bits) */
743     if(snd_pcm_hw_params_set_channels(mPcmHandle, hp.get(), mDevice->channelsFromFmt()) < 0)
744     {
745         uint numchans{2u};
746         CHECK(snd_pcm_hw_params_set_channels_near(mPcmHandle, hp.get(), &numchans));
747         if(numchans < 1)
748             throw al::backend_exception{al::backend_error::DeviceError, "Got 0 device channels"};
749         if(numchans == 1) mDevice->FmtChans = DevFmtMono;
750         else mDevice->FmtChans = DevFmtStereo;
751     }
752     /* set rate (implicitly constrains period/buffer parameters) */
753     if(!GetConfigValueBool(mDevice->DeviceName.c_str(), "alsa", "allow-resampler", false)
754         || !mDevice->Flags.test(FrequencyRequest))
755     {
756         if(snd_pcm_hw_params_set_rate_resample(mPcmHandle, hp.get(), 0) < 0)
757             WARN("Failed to disable ALSA resampler\n");
758     }
759     else if(snd_pcm_hw_params_set_rate_resample(mPcmHandle, hp.get(), 1) < 0)
760         WARN("Failed to enable ALSA resampler\n");
761     CHECK(snd_pcm_hw_params_set_rate_near(mPcmHandle, hp.get(), &rate, nullptr));
762     /* set period time (implicitly constrains period/buffer parameters) */
763     if((err=snd_pcm_hw_params_set_period_time_near(mPcmHandle, hp.get(), &periodLen, nullptr)) < 0)
764         ERR("snd_pcm_hw_params_set_period_time_near failed: %s\n", snd_strerror(err));
765     /* set buffer time (implicitly sets buffer size/bytes/time and period size/bytes) */
766     if((err=snd_pcm_hw_params_set_buffer_time_near(mPcmHandle, hp.get(), &bufferLen, nullptr)) < 0)
767         ERR("snd_pcm_hw_params_set_buffer_time_near failed: %s\n", snd_strerror(err));
768     /* install and prepare hardware configuration */
769     CHECK(snd_pcm_hw_params(mPcmHandle, hp.get()));
770
771     /* retrieve configuration info */
772     snd_pcm_uframes_t periodSizeInFrames{};
773     snd_pcm_uframes_t bufferSizeInFrames{};
774     snd_pcm_access_t access{};
775
776     CHECK(snd_pcm_hw_params_get_access(hp.get(), &access));
777     CHECK(snd_pcm_hw_params_get_period_size(hp.get(), &periodSizeInFrames, nullptr));
778     CHECK(snd_pcm_hw_params_get_buffer_size(hp.get(), &bufferSizeInFrames));
779     CHECK(snd_pcm_hw_params_get_channels(hp.get(), &mFrameStep));
780     hp = nullptr;
781
782     SwParamsPtr sp{CreateSwParams()};
783     CHECK(snd_pcm_sw_params_current(mPcmHandle, sp.get()));
784     CHECK(snd_pcm_sw_params_set_avail_min(mPcmHandle, sp.get(), periodSizeInFrames));
785     CHECK(snd_pcm_sw_params_set_stop_threshold(mPcmHandle, sp.get(), bufferSizeInFrames));
786     CHECK(snd_pcm_sw_params(mPcmHandle, sp.get()));
787 #undef CHECK
788     sp = nullptr;
789
790     mDevice->BufferSize = static_cast<uint>(bufferSizeInFrames);
791     mDevice->UpdateSize = static_cast<uint>(periodSizeInFrames);
792     mDevice->Frequency = rate;
793
794     setDefaultChannelOrder();
795
796     return true;
797 }
798
799 void AlsaPlayback::start()
800 {
801     int err{};
802     snd_pcm_access_t access{};
803     HwParamsPtr hp{CreateHwParams()};
804 #define CHECK(x) do {                                                         \
805     if((err=(x)) < 0)                                                         \
806         throw al::backend_exception{al::backend_error::DeviceError, #x " failed: %s", \
807             snd_strerror(err)};                                               \
808 } while(0)
809     CHECK(snd_pcm_hw_params_current(mPcmHandle, hp.get()));
810     /* retrieve configuration info */
811     CHECK(snd_pcm_hw_params_get_access(hp.get(), &access));
812     hp = nullptr;
813
814     int (AlsaPlayback::*thread_func)(){};
815     if(access == SND_PCM_ACCESS_RW_INTERLEAVED)
816     {
817         auto datalen = snd_pcm_frames_to_bytes(mPcmHandle, mDevice->UpdateSize);
818         mBuffer.resize(static_cast<size_t>(datalen));
819         thread_func = &AlsaPlayback::mixerNoMMapProc;
820     }
821     else
822     {
823         CHECK(snd_pcm_prepare(mPcmHandle));
824         thread_func = &AlsaPlayback::mixerProc;
825     }
826 #undef CHECK
827
828     try {
829         mKillNow.store(false, std::memory_order_release);
830         mThread = std::thread{std::mem_fn(thread_func), this};
831     }
832     catch(std::exception& e) {
833         throw al::backend_exception{al::backend_error::DeviceError,
834             "Failed to start mixing thread: %s", e.what()};
835     }
836 }
837
838 void AlsaPlayback::stop()
839 {
840     if(mKillNow.exchange(true, std::memory_order_acq_rel) || !mThread.joinable())
841         return;
842     mThread.join();
843
844     mBuffer.clear();
845     int err{snd_pcm_drop(mPcmHandle)};
846     if(err < 0)
847         ERR("snd_pcm_drop failed: %s\n", snd_strerror(err));
848 }
849
850 ClockLatency AlsaPlayback::getClockLatency()
851 {
852     ClockLatency ret;
853
854     std::lock_guard<std::mutex> _{mMutex};
855     ret.ClockTime = GetDeviceClockTime(mDevice);
856     snd_pcm_sframes_t delay{};
857     int err{snd_pcm_delay(mPcmHandle, &delay)};
858     if(err < 0)
859     {
860         ERR("Failed to get pcm delay: %s\n", snd_strerror(err));
861         delay = 0;
862     }
863     ret.Latency  = std::chrono::seconds{std::max<snd_pcm_sframes_t>(0, delay)};
864     ret.Latency /= mDevice->Frequency;
865
866     return ret;
867 }
868
869
870 struct AlsaCapture final : public BackendBase {
871     AlsaCapture(DeviceBase *device) noexcept : BackendBase{device} { }
872     ~AlsaCapture() override;
873
874     void open(const char *name) override;
875     void start() override;
876     void stop() override;
877     void captureSamples(al::byte *buffer, uint samples) override;
878     uint availableSamples() override;
879     ClockLatency getClockLatency() override;
880
881     snd_pcm_t *mPcmHandle{nullptr};
882
883     al::vector<al::byte> mBuffer;
884
885     bool mDoCapture{false};
886     RingBufferPtr mRing{nullptr};
887
888     snd_pcm_sframes_t mLastAvail{0};
889
890     DEF_NEWDEL(AlsaCapture)
891 };
892
893 AlsaCapture::~AlsaCapture()
894 {
895     if(mPcmHandle)
896         snd_pcm_close(mPcmHandle);
897     mPcmHandle = nullptr;
898 }
899
900
901 void AlsaCapture::open(const char *name)
902 {
903     std::string driver{"default"};
904     if(name)
905     {
906         if(CaptureDevices.empty())
907             CaptureDevices = probe_devices(SND_PCM_STREAM_CAPTURE);
908
909         auto iter = std::find_if(CaptureDevices.cbegin(), CaptureDevices.cend(),
910             [name](const DevMap &entry) -> bool { return entry.name == name; });
911         if(iter == CaptureDevices.cend())
912             throw al::backend_exception{al::backend_error::NoDevice,
913                 "Device name \"%s\" not found", name};
914         driver = iter->device_name;
915     }
916     else
917     {
918         name = alsaDevice;
919         if(auto driveropt = ConfigValueStr(nullptr, "alsa", "capture"))
920             driver = std::move(driveropt).value();
921     }
922
923     TRACE("Opening device \"%s\"\n", driver.c_str());
924     int err{snd_pcm_open(&mPcmHandle, driver.c_str(), SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK)};
925     if(err < 0)
926         throw al::backend_exception{al::backend_error::NoDevice,
927             "Could not open ALSA device \"%s\"", driver.c_str()};
928
929     /* Free alsa's global config tree. Otherwise valgrind reports a ton of leaks. */
930     snd_config_update_free_global();
931
932     snd_pcm_format_t format{SND_PCM_FORMAT_UNKNOWN};
933     switch(mDevice->FmtType)
934     {
935     case DevFmtByte:
936         format = SND_PCM_FORMAT_S8;
937         break;
938     case DevFmtUByte:
939         format = SND_PCM_FORMAT_U8;
940         break;
941     case DevFmtShort:
942         format = SND_PCM_FORMAT_S16;
943         break;
944     case DevFmtUShort:
945         format = SND_PCM_FORMAT_U16;
946         break;
947     case DevFmtInt:
948         format = SND_PCM_FORMAT_S32;
949         break;
950     case DevFmtUInt:
951         format = SND_PCM_FORMAT_U32;
952         break;
953     case DevFmtFloat:
954         format = SND_PCM_FORMAT_FLOAT;
955         break;
956     }
957
958     snd_pcm_uframes_t bufferSizeInFrames{maxu(mDevice->BufferSize, 100*mDevice->Frequency/1000)};
959     snd_pcm_uframes_t periodSizeInFrames{minu(mDevice->BufferSize, 25*mDevice->Frequency/1000)};
960
961     bool needring{false};
962     HwParamsPtr hp{CreateHwParams()};
963 #define CHECK(x) do {                                                         \
964     if((err=(x)) < 0)                                                         \
965         throw al::backend_exception{al::backend_error::DeviceError, #x " failed: %s", \
966             snd_strerror(err)};                                               \
967 } while(0)
968     CHECK(snd_pcm_hw_params_any(mPcmHandle, hp.get()));
969     /* set interleaved access */
970     CHECK(snd_pcm_hw_params_set_access(mPcmHandle, hp.get(), SND_PCM_ACCESS_RW_INTERLEAVED));
971     /* set format (implicitly sets sample bits) */
972     CHECK(snd_pcm_hw_params_set_format(mPcmHandle, hp.get(), format));
973     /* set channels (implicitly sets frame bits) */
974     CHECK(snd_pcm_hw_params_set_channels(mPcmHandle, hp.get(), mDevice->channelsFromFmt()));
975     /* set rate (implicitly constrains period/buffer parameters) */
976     CHECK(snd_pcm_hw_params_set_rate(mPcmHandle, hp.get(), mDevice->Frequency, 0));
977     /* set buffer size in frame units (implicitly sets period size/bytes/time and buffer time/bytes) */
978     if(snd_pcm_hw_params_set_buffer_size_min(mPcmHandle, hp.get(), &bufferSizeInFrames) < 0)
979     {
980         TRACE("Buffer too large, using intermediate ring buffer\n");
981         needring = true;
982         CHECK(snd_pcm_hw_params_set_buffer_size_near(mPcmHandle, hp.get(), &bufferSizeInFrames));
983     }
984     /* set buffer size in frame units (implicitly sets period size/bytes/time and buffer time/bytes) */
985     CHECK(snd_pcm_hw_params_set_period_size_near(mPcmHandle, hp.get(), &periodSizeInFrames, nullptr));
986     /* install and prepare hardware configuration */
987     CHECK(snd_pcm_hw_params(mPcmHandle, hp.get()));
988     /* retrieve configuration info */
989     CHECK(snd_pcm_hw_params_get_period_size(hp.get(), &periodSizeInFrames, nullptr));
990 #undef CHECK
991     hp = nullptr;
992
993     if(needring)
994         mRing = RingBuffer::Create(mDevice->BufferSize, mDevice->frameSizeFromFmt(), false);
995
996     mDevice->DeviceName = name;
997 }
998
999
1000 void AlsaCapture::start()
1001 {
1002     int err{snd_pcm_prepare(mPcmHandle)};
1003     if(err < 0)
1004         throw al::backend_exception{al::backend_error::DeviceError, "snd_pcm_prepare failed: %s",
1005             snd_strerror(err)};
1006
1007     err = snd_pcm_start(mPcmHandle);
1008     if(err < 0)
1009         throw al::backend_exception{al::backend_error::DeviceError, "snd_pcm_start failed: %s",
1010             snd_strerror(err)};
1011
1012     mDoCapture = true;
1013 }
1014
1015 void AlsaCapture::stop()
1016 {
1017     /* OpenAL requires access to unread audio after stopping, but ALSA's
1018      * snd_pcm_drain is unreliable and snd_pcm_drop drops it. Capture what's
1019      * available now so it'll be available later after the drop.
1020      */
1021     uint avail{availableSamples()};
1022     if(!mRing && avail > 0)
1023     {
1024         /* The ring buffer implicitly captures when checking availability.
1025          * Direct access needs to explicitly capture it into temp storage.
1026          */
1027         auto temp = al::vector<al::byte>(
1028             static_cast<size_t>(snd_pcm_frames_to_bytes(mPcmHandle, avail)));
1029         captureSamples(temp.data(), avail);
1030         mBuffer = std::move(temp);
1031     }
1032     int err{snd_pcm_drop(mPcmHandle)};
1033     if(err < 0)
1034         ERR("drop failed: %s\n", snd_strerror(err));
1035     mDoCapture = false;
1036 }
1037
1038 void AlsaCapture::captureSamples(al::byte *buffer, uint samples)
1039 {
1040     if(mRing)
1041     {
1042         mRing->read(buffer, samples);
1043         return;
1044     }
1045
1046     mLastAvail -= samples;
1047     while(mDevice->Connected.load(std::memory_order_acquire) && samples > 0)
1048     {
1049         snd_pcm_sframes_t amt{0};
1050
1051         if(!mBuffer.empty())
1052         {
1053             /* First get any data stored from the last stop */
1054             amt = snd_pcm_bytes_to_frames(mPcmHandle, static_cast<ssize_t>(mBuffer.size()));
1055             if(static_cast<snd_pcm_uframes_t>(amt) > samples) amt = samples;
1056
1057             amt = snd_pcm_frames_to_bytes(mPcmHandle, amt);
1058             std::copy_n(mBuffer.begin(), amt, buffer);
1059
1060             mBuffer.erase(mBuffer.begin(), mBuffer.begin()+amt);
1061             amt = snd_pcm_bytes_to_frames(mPcmHandle, amt);
1062         }
1063         else if(mDoCapture)
1064             amt = snd_pcm_readi(mPcmHandle, buffer, samples);
1065         if(amt < 0)
1066         {
1067             ERR("read error: %s\n", snd_strerror(static_cast<int>(amt)));
1068
1069             if(amt == -EAGAIN)
1070                 continue;
1071             if((amt=snd_pcm_recover(mPcmHandle, static_cast<int>(amt), 1)) >= 0)
1072             {
1073                 amt = snd_pcm_start(mPcmHandle);
1074                 if(amt >= 0)
1075                     amt = snd_pcm_avail_update(mPcmHandle);
1076             }
1077             if(amt < 0)
1078             {
1079                 const char *err{snd_strerror(static_cast<int>(amt))};
1080                 ERR("restore error: %s\n", err);
1081                 mDevice->handleDisconnect("Capture recovery failure: %s", err);
1082                 break;
1083             }
1084             /* If the amount available is less than what's asked, we lost it
1085              * during recovery. So just give silence instead. */
1086             if(static_cast<snd_pcm_uframes_t>(amt) < samples)
1087                 break;
1088             continue;
1089         }
1090
1091         buffer = buffer + amt;
1092         samples -= static_cast<uint>(amt);
1093     }
1094     if(samples > 0)
1095         std::fill_n(buffer, snd_pcm_frames_to_bytes(mPcmHandle, samples),
1096             al::byte((mDevice->FmtType == DevFmtUByte) ? 0x80 : 0));
1097 }
1098
1099 uint AlsaCapture::availableSamples()
1100 {
1101     snd_pcm_sframes_t avail{0};
1102     if(mDevice->Connected.load(std::memory_order_acquire) && mDoCapture)
1103         avail = snd_pcm_avail_update(mPcmHandle);
1104     if(avail < 0)
1105     {
1106         ERR("avail update failed: %s\n", snd_strerror(static_cast<int>(avail)));
1107
1108         if((avail=snd_pcm_recover(mPcmHandle, static_cast<int>(avail), 1)) >= 0)
1109         {
1110             if(mDoCapture)
1111                 avail = snd_pcm_start(mPcmHandle);
1112             if(avail >= 0)
1113                 avail = snd_pcm_avail_update(mPcmHandle);
1114         }
1115         if(avail < 0)
1116         {
1117             const char *err{snd_strerror(static_cast<int>(avail))};
1118             ERR("restore error: %s\n", err);
1119             mDevice->handleDisconnect("Capture recovery failure: %s", err);
1120         }
1121     }
1122
1123     if(!mRing)
1124     {
1125         if(avail < 0) avail = 0;
1126         avail += snd_pcm_bytes_to_frames(mPcmHandle, static_cast<ssize_t>(mBuffer.size()));
1127         if(avail > mLastAvail) mLastAvail = avail;
1128         return static_cast<uint>(mLastAvail);
1129     }
1130
1131     while(avail > 0)
1132     {
1133         auto vec = mRing->getWriteVector();
1134         if(vec.first.len == 0) break;
1135
1136         snd_pcm_sframes_t amt{std::min(static_cast<snd_pcm_sframes_t>(vec.first.len), avail)};
1137         amt = snd_pcm_readi(mPcmHandle, vec.first.buf, static_cast<snd_pcm_uframes_t>(amt));
1138         if(amt < 0)
1139         {
1140             ERR("read error: %s\n", snd_strerror(static_cast<int>(amt)));
1141
1142             if(amt == -EAGAIN)
1143                 continue;
1144             if((amt=snd_pcm_recover(mPcmHandle, static_cast<int>(amt), 1)) >= 0)
1145             {
1146                 if(mDoCapture)
1147                     amt = snd_pcm_start(mPcmHandle);
1148                 if(amt >= 0)
1149                     amt = snd_pcm_avail_update(mPcmHandle);
1150             }
1151             if(amt < 0)
1152             {
1153                 const char *err{snd_strerror(static_cast<int>(amt))};
1154                 ERR("restore error: %s\n", err);
1155                 mDevice->handleDisconnect("Capture recovery failure: %s", err);
1156                 break;
1157             }
1158             avail = amt;
1159             continue;
1160         }
1161
1162         mRing->writeAdvance(static_cast<snd_pcm_uframes_t>(amt));
1163         avail -= amt;
1164     }
1165
1166     return static_cast<uint>(mRing->readSpace());
1167 }
1168
1169 ClockLatency AlsaCapture::getClockLatency()
1170 {
1171     ClockLatency ret;
1172
1173     ret.ClockTime = GetDeviceClockTime(mDevice);
1174     snd_pcm_sframes_t delay{};
1175     int err{snd_pcm_delay(mPcmHandle, &delay)};
1176     if(err < 0)
1177     {
1178         ERR("Failed to get pcm delay: %s\n", snd_strerror(err));
1179         delay = 0;
1180     }
1181     ret.Latency  = std::chrono::seconds{std::max<snd_pcm_sframes_t>(0, delay)};
1182     ret.Latency /= mDevice->Frequency;
1183
1184     return ret;
1185 }
1186
1187 } // namespace
1188
1189
1190 bool AlsaBackendFactory::init()
1191 {
1192     bool error{false};
1193
1194 #ifdef HAVE_DYNLOAD
1195     if(!alsa_handle)
1196     {
1197         std::string missing_funcs;
1198
1199         alsa_handle = LoadLib("libasound.so.2");
1200         if(!alsa_handle)
1201         {
1202             WARN("Failed to load %s\n", "libasound.so.2");
1203             return false;
1204         }
1205
1206         error = false;
1207 #define LOAD_FUNC(f) do {                                                     \
1208     p##f = reinterpret_cast<decltype(p##f)>(GetSymbol(alsa_handle, #f));      \
1209     if(p##f == nullptr) {                                                     \
1210         error = true;                                                         \
1211         missing_funcs += "\n" #f;                                             \
1212     }                                                                         \
1213 } while(0)
1214         ALSA_FUNCS(LOAD_FUNC);
1215 #undef LOAD_FUNC
1216
1217         if(error)
1218         {
1219             WARN("Missing expected functions:%s\n", missing_funcs.c_str());
1220             CloseLib(alsa_handle);
1221             alsa_handle = nullptr;
1222         }
1223     }
1224 #endif
1225
1226     return !error;
1227 }
1228
1229 bool AlsaBackendFactory::querySupport(BackendType type)
1230 { return (type == BackendType::Playback || type == BackendType::Capture); }
1231
1232 std::string AlsaBackendFactory::probe(BackendType type)
1233 {
1234     std::string outnames;
1235
1236     auto add_device = [&outnames](const DevMap &entry) -> void
1237     {
1238         /* +1 to also append the null char (to ensure a null-separated list and
1239          * double-null terminated list).
1240          */
1241         outnames.append(entry.name.c_str(), entry.name.length()+1);
1242     };
1243     switch(type)
1244     {
1245     case BackendType::Playback:
1246         PlaybackDevices = probe_devices(SND_PCM_STREAM_PLAYBACK);
1247         std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device);
1248         break;
1249
1250     case BackendType::Capture:
1251         CaptureDevices = probe_devices(SND_PCM_STREAM_CAPTURE);
1252         std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device);
1253         break;
1254     }
1255
1256     return outnames;
1257 }
1258
1259 BackendPtr AlsaBackendFactory::createBackend(DeviceBase *device, BackendType type)
1260 {
1261     if(type == BackendType::Playback)
1262         return BackendPtr{new AlsaPlayback{device}};
1263     if(type == BackendType::Capture)
1264         return BackendPtr{new AlsaCapture{device}};
1265     return nullptr;
1266 }
1267
1268 BackendFactory &AlsaBackendFactory::getFactory()
1269 {
1270     static AlsaBackendFactory factory{};
1271     return factory;
1272 }