Rename some struct members
[openal-soft.git] / Alc / backends / alsa.cpp
blob20ffc5592c55e87123da1cc1db20085fe0ba8caa
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.
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.
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
21 #include "config.h"
23 #include "backends/alsa.h"
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <memory.h>
29 #include <atomic>
30 #include <thread>
31 #include <vector>
32 #include <string>
33 #include <algorithm>
35 #include "alMain.h"
36 #include "alu.h"
37 #include "alconfig.h"
38 #include "ringbuffer.h"
39 #include "compat.h"
41 #include <alsa/asoundlib.h>
44 namespace {
46 constexpr ALCchar alsaDevice[] = "ALSA Default";
49 #ifdef HAVE_DYNLOAD
50 #define ALSA_FUNCS(MAGIC) \
51 MAGIC(snd_strerror); \
52 MAGIC(snd_pcm_open); \
53 MAGIC(snd_pcm_close); \
54 MAGIC(snd_pcm_nonblock); \
55 MAGIC(snd_pcm_frames_to_bytes); \
56 MAGIC(snd_pcm_bytes_to_frames); \
57 MAGIC(snd_pcm_hw_params_malloc); \
58 MAGIC(snd_pcm_hw_params_free); \
59 MAGIC(snd_pcm_hw_params_any); \
60 MAGIC(snd_pcm_hw_params_current); \
61 MAGIC(snd_pcm_hw_params_set_access); \
62 MAGIC(snd_pcm_hw_params_set_format); \
63 MAGIC(snd_pcm_hw_params_set_channels); \
64 MAGIC(snd_pcm_hw_params_set_periods_near); \
65 MAGIC(snd_pcm_hw_params_set_rate_near); \
66 MAGIC(snd_pcm_hw_params_set_rate); \
67 MAGIC(snd_pcm_hw_params_set_rate_resample); \
68 MAGIC(snd_pcm_hw_params_set_buffer_time_near); \
69 MAGIC(snd_pcm_hw_params_set_period_time_near); \
70 MAGIC(snd_pcm_hw_params_set_buffer_size_near); \
71 MAGIC(snd_pcm_hw_params_set_period_size_near); \
72 MAGIC(snd_pcm_hw_params_set_buffer_size_min); \
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_period_time_min); \
76 MAGIC(snd_pcm_hw_params_get_period_time_max); \
77 MAGIC(snd_pcm_hw_params_get_buffer_size); \
78 MAGIC(snd_pcm_hw_params_get_period_size); \
79 MAGIC(snd_pcm_hw_params_get_access); \
80 MAGIC(snd_pcm_hw_params_get_periods); \
81 MAGIC(snd_pcm_hw_params_test_format); \
82 MAGIC(snd_pcm_hw_params_test_channels); \
83 MAGIC(snd_pcm_hw_params); \
84 MAGIC(snd_pcm_sw_params_malloc); \
85 MAGIC(snd_pcm_sw_params_current); \
86 MAGIC(snd_pcm_sw_params_set_avail_min); \
87 MAGIC(snd_pcm_sw_params_set_stop_threshold); \
88 MAGIC(snd_pcm_sw_params); \
89 MAGIC(snd_pcm_sw_params_free); \
90 MAGIC(snd_pcm_prepare); \
91 MAGIC(snd_pcm_start); \
92 MAGIC(snd_pcm_resume); \
93 MAGIC(snd_pcm_reset); \
94 MAGIC(snd_pcm_wait); \
95 MAGIC(snd_pcm_delay); \
96 MAGIC(snd_pcm_state); \
97 MAGIC(snd_pcm_avail_update); \
98 MAGIC(snd_pcm_areas_silence); \
99 MAGIC(snd_pcm_mmap_begin); \
100 MAGIC(snd_pcm_mmap_commit); \
101 MAGIC(snd_pcm_readi); \
102 MAGIC(snd_pcm_writei); \
103 MAGIC(snd_pcm_drain); \
104 MAGIC(snd_pcm_drop); \
105 MAGIC(snd_pcm_recover); \
106 MAGIC(snd_pcm_info_malloc); \
107 MAGIC(snd_pcm_info_free); \
108 MAGIC(snd_pcm_info_set_device); \
109 MAGIC(snd_pcm_info_set_subdevice); \
110 MAGIC(snd_pcm_info_set_stream); \
111 MAGIC(snd_pcm_info_get_name); \
112 MAGIC(snd_ctl_pcm_next_device); \
113 MAGIC(snd_ctl_pcm_info); \
114 MAGIC(snd_ctl_open); \
115 MAGIC(snd_ctl_close); \
116 MAGIC(snd_ctl_card_info_malloc); \
117 MAGIC(snd_ctl_card_info_free); \
118 MAGIC(snd_ctl_card_info); \
119 MAGIC(snd_ctl_card_info_get_name); \
120 MAGIC(snd_ctl_card_info_get_id); \
121 MAGIC(snd_card_next); \
122 MAGIC(snd_config_update_free_global)
124 static void *alsa_handle;
125 #define MAKE_FUNC(f) decltype(f) * p##f
126 ALSA_FUNCS(MAKE_FUNC);
127 #undef MAKE_FUNC
129 #ifndef IN_IDE_PARSER
130 #define snd_strerror psnd_strerror
131 #define snd_pcm_open psnd_pcm_open
132 #define snd_pcm_close psnd_pcm_close
133 #define snd_pcm_nonblock psnd_pcm_nonblock
134 #define snd_pcm_frames_to_bytes psnd_pcm_frames_to_bytes
135 #define snd_pcm_bytes_to_frames psnd_pcm_bytes_to_frames
136 #define snd_pcm_hw_params_malloc psnd_pcm_hw_params_malloc
137 #define snd_pcm_hw_params_free psnd_pcm_hw_params_free
138 #define snd_pcm_hw_params_any psnd_pcm_hw_params_any
139 #define snd_pcm_hw_params_current psnd_pcm_hw_params_current
140 #define snd_pcm_hw_params_set_access psnd_pcm_hw_params_set_access
141 #define snd_pcm_hw_params_set_format psnd_pcm_hw_params_set_format
142 #define snd_pcm_hw_params_set_channels psnd_pcm_hw_params_set_channels
143 #define snd_pcm_hw_params_set_periods_near psnd_pcm_hw_params_set_periods_near
144 #define snd_pcm_hw_params_set_rate_near psnd_pcm_hw_params_set_rate_near
145 #define snd_pcm_hw_params_set_rate psnd_pcm_hw_params_set_rate
146 #define snd_pcm_hw_params_set_rate_resample psnd_pcm_hw_params_set_rate_resample
147 #define snd_pcm_hw_params_set_buffer_time_near psnd_pcm_hw_params_set_buffer_time_near
148 #define snd_pcm_hw_params_set_period_time_near psnd_pcm_hw_params_set_period_time_near
149 #define snd_pcm_hw_params_set_buffer_size_near psnd_pcm_hw_params_set_buffer_size_near
150 #define snd_pcm_hw_params_set_period_size_near psnd_pcm_hw_params_set_period_size_near
151 #define snd_pcm_hw_params_set_buffer_size_min psnd_pcm_hw_params_set_buffer_size_min
152 #define snd_pcm_hw_params_get_buffer_time_min psnd_pcm_hw_params_get_buffer_time_min
153 #define snd_pcm_hw_params_get_buffer_time_max psnd_pcm_hw_params_get_buffer_time_max
154 #define snd_pcm_hw_params_get_period_time_min psnd_pcm_hw_params_get_period_time_min
155 #define snd_pcm_hw_params_get_period_time_max psnd_pcm_hw_params_get_period_time_max
156 #define snd_pcm_hw_params_get_buffer_size psnd_pcm_hw_params_get_buffer_size
157 #define snd_pcm_hw_params_get_period_size psnd_pcm_hw_params_get_period_size
158 #define snd_pcm_hw_params_get_access psnd_pcm_hw_params_get_access
159 #define snd_pcm_hw_params_get_periods psnd_pcm_hw_params_get_periods
160 #define snd_pcm_hw_params_test_format psnd_pcm_hw_params_test_format
161 #define snd_pcm_hw_params_test_channels psnd_pcm_hw_params_test_channels
162 #define snd_pcm_hw_params psnd_pcm_hw_params
163 #define snd_pcm_sw_params_malloc psnd_pcm_sw_params_malloc
164 #define snd_pcm_sw_params_current psnd_pcm_sw_params_current
165 #define snd_pcm_sw_params_set_avail_min psnd_pcm_sw_params_set_avail_min
166 #define snd_pcm_sw_params_set_stop_threshold psnd_pcm_sw_params_set_stop_threshold
167 #define snd_pcm_sw_params psnd_pcm_sw_params
168 #define snd_pcm_sw_params_free psnd_pcm_sw_params_free
169 #define snd_pcm_prepare psnd_pcm_prepare
170 #define snd_pcm_start psnd_pcm_start
171 #define snd_pcm_resume psnd_pcm_resume
172 #define snd_pcm_reset psnd_pcm_reset
173 #define snd_pcm_wait psnd_pcm_wait
174 #define snd_pcm_delay psnd_pcm_delay
175 #define snd_pcm_state psnd_pcm_state
176 #define snd_pcm_avail_update psnd_pcm_avail_update
177 #define snd_pcm_areas_silence psnd_pcm_areas_silence
178 #define snd_pcm_mmap_begin psnd_pcm_mmap_begin
179 #define snd_pcm_mmap_commit psnd_pcm_mmap_commit
180 #define snd_pcm_readi psnd_pcm_readi
181 #define snd_pcm_writei psnd_pcm_writei
182 #define snd_pcm_drain psnd_pcm_drain
183 #define snd_pcm_drop psnd_pcm_drop
184 #define snd_pcm_recover psnd_pcm_recover
185 #define snd_pcm_info_malloc psnd_pcm_info_malloc
186 #define snd_pcm_info_free psnd_pcm_info_free
187 #define snd_pcm_info_set_device psnd_pcm_info_set_device
188 #define snd_pcm_info_set_subdevice psnd_pcm_info_set_subdevice
189 #define snd_pcm_info_set_stream psnd_pcm_info_set_stream
190 #define snd_pcm_info_get_name psnd_pcm_info_get_name
191 #define snd_ctl_pcm_next_device psnd_ctl_pcm_next_device
192 #define snd_ctl_pcm_info psnd_ctl_pcm_info
193 #define snd_ctl_open psnd_ctl_open
194 #define snd_ctl_close psnd_ctl_close
195 #define snd_ctl_card_info_malloc psnd_ctl_card_info_malloc
196 #define snd_ctl_card_info_free psnd_ctl_card_info_free
197 #define snd_ctl_card_info psnd_ctl_card_info
198 #define snd_ctl_card_info_get_name psnd_ctl_card_info_get_name
199 #define snd_ctl_card_info_get_id psnd_ctl_card_info_get_id
200 #define snd_card_next psnd_card_next
201 #define snd_config_update_free_global psnd_config_update_free_global
202 #endif
203 #endif
206 bool alsa_load(void)
208 bool error{false};
210 #ifdef HAVE_DYNLOAD
211 if(!alsa_handle)
213 std::string missing_funcs;
215 alsa_handle = LoadLib("libasound.so.2");
216 if(!alsa_handle)
218 WARN("Failed to load %s\n", "libasound.so.2");
219 return ALC_FALSE;
222 error = ALC_FALSE;
223 #define LOAD_FUNC(f) do { \
224 p##f = reinterpret_cast<decltype(p##f)>(GetSymbol(alsa_handle, #f)); \
225 if(p##f == nullptr) { \
226 error = true; \
227 missing_funcs += "\n" #f; \
229 } while(0)
230 ALSA_FUNCS(LOAD_FUNC);
231 #undef LOAD_FUNC
233 if(error)
235 WARN("Missing expected functions:%s\n", missing_funcs.c_str());
236 CloseLib(alsa_handle);
237 alsa_handle = nullptr;
240 #endif
242 return !error;
246 struct DevMap {
247 std::string name;
248 std::string device_name;
250 template<typename StrT0, typename StrT1>
251 DevMap(StrT0&& name_, StrT1&& devname_)
252 : name{std::forward<StrT0>(name_)}, device_name{std::forward<StrT1>(devname_)}
256 al::vector<DevMap> PlaybackDevices;
257 al::vector<DevMap> CaptureDevices;
260 const char *prefix_name(snd_pcm_stream_t stream)
262 assert(stream == SND_PCM_STREAM_PLAYBACK || stream == SND_PCM_STREAM_CAPTURE);
263 return (stream==SND_PCM_STREAM_PLAYBACK) ? "device-prefix" : "capture-prefix";
266 al::vector<DevMap> probe_devices(snd_pcm_stream_t stream)
268 al::vector<DevMap> devlist;
270 snd_ctl_card_info_t *info;
271 snd_ctl_card_info_malloc(&info);
272 snd_pcm_info_t *pcminfo;
273 snd_pcm_info_malloc(&pcminfo);
275 devlist.emplace_back(alsaDevice,
276 GetConfigValue(nullptr, "alsa", (stream==SND_PCM_STREAM_PLAYBACK) ? "device" : "capture",
277 "default")
280 if(stream == SND_PCM_STREAM_PLAYBACK)
282 const char *customdevs;
283 const char *next{GetConfigValue(nullptr, "alsa", "custom-devices", "")};
284 while((customdevs=next) != nullptr && customdevs[0])
286 next = strchr(customdevs, ';');
287 const char *sep{strchr(customdevs, '=')};
288 if(!sep)
290 std::string spec{next ? std::string(customdevs, next++) : std::string(customdevs)};
291 ERR("Invalid ALSA device specification \"%s\"\n", spec.c_str());
292 continue;
295 const char *oldsep{sep++};
296 devlist.emplace_back(std::string(customdevs, oldsep),
297 next ? std::string(sep, next++) : std::string(sep));
298 const auto &entry = devlist.back();
299 TRACE("Got device \"%s\", \"%s\"\n", entry.name.c_str(), entry.device_name.c_str());
303 const char *main_prefix{"plughw:"};
304 ConfigValueStr(nullptr, "alsa", prefix_name(stream), &main_prefix);
306 int card{-1};
307 int err{snd_card_next(&card)};
308 for(;err >= 0 && card >= 0;err = snd_card_next(&card))
310 std::string name{"hw:" + std::to_string(card)};
312 snd_ctl_t *handle;
313 if((err=snd_ctl_open(&handle, name.c_str(), 0)) < 0)
315 ERR("control open (hw:%d): %s\n", card, snd_strerror(err));
316 continue;
318 if((err=snd_ctl_card_info(handle, info)) < 0)
320 ERR("control hardware info (hw:%d): %s\n", card, snd_strerror(err));
321 snd_ctl_close(handle);
322 continue;
325 const char *cardname{snd_ctl_card_info_get_name(info)};
326 const char *cardid{snd_ctl_card_info_get_id(info)};
327 name = prefix_name(stream);
328 name += '-';
329 name += cardid;
331 const char *card_prefix{main_prefix};
332 ConfigValueStr(nullptr, "alsa", name.c_str(), &card_prefix);
334 int dev{-1};
335 while(1)
337 if(snd_ctl_pcm_next_device(handle, &dev) < 0)
338 ERR("snd_ctl_pcm_next_device failed\n");
339 if(dev < 0) break;
341 snd_pcm_info_set_device(pcminfo, dev);
342 snd_pcm_info_set_subdevice(pcminfo, 0);
343 snd_pcm_info_set_stream(pcminfo, stream);
344 if((err=snd_ctl_pcm_info(handle, pcminfo)) < 0)
346 if(err != -ENOENT)
347 ERR("control digital audio info (hw:%d): %s\n", card, snd_strerror(err));
348 continue;
351 /* "prefix-cardid-dev" */
352 name = prefix_name(stream);
353 name += '-';
354 name += cardid;
355 name += '-';
356 name += std::to_string(dev);
357 const char *device_prefix{card_prefix};
358 ConfigValueStr(nullptr, "alsa", name.c_str(), &device_prefix);
360 /* "CardName, PcmName (CARD=cardid,DEV=dev)" */
361 name = cardname;
362 name += ", ";
363 name += snd_pcm_info_get_name(pcminfo);
364 name += " (CARD=";
365 name += cardid;
366 name += ",DEV=";
367 name += std::to_string(dev);
368 name += ')';
370 /* "devprefixCARD=cardid,DEV=dev" */
371 std::string device{device_prefix};
372 device += "CARD=";
373 device += cardid;
374 device += ",DEV=";
375 device += std::to_string(dev);
377 devlist.emplace_back(std::move(name), std::move(device));
378 const auto &entry = devlist.back();
379 TRACE("Got device \"%s\", \"%s\"\n", entry.name.c_str(), entry.device_name.c_str());
381 snd_ctl_close(handle);
383 if(err < 0)
384 ERR("snd_card_next failed: %s\n", snd_strerror(err));
386 snd_pcm_info_free(pcminfo);
387 snd_ctl_card_info_free(info);
389 return devlist;
393 int verify_state(snd_pcm_t *handle)
395 snd_pcm_state_t state{snd_pcm_state(handle)};
397 int err;
398 switch(state)
400 case SND_PCM_STATE_OPEN:
401 case SND_PCM_STATE_SETUP:
402 case SND_PCM_STATE_PREPARED:
403 case SND_PCM_STATE_RUNNING:
404 case SND_PCM_STATE_DRAINING:
405 case SND_PCM_STATE_PAUSED:
406 /* All Okay */
407 break;
409 case SND_PCM_STATE_XRUN:
410 if((err=snd_pcm_recover(handle, -EPIPE, 1)) < 0)
411 return err;
412 break;
413 case SND_PCM_STATE_SUSPENDED:
414 if((err=snd_pcm_recover(handle, -ESTRPIPE, 1)) < 0)
415 return err;
416 break;
417 case SND_PCM_STATE_DISCONNECTED:
418 return -ENODEV;
421 return state;
425 struct ALCplaybackAlsa final : public ALCbackend {
426 snd_pcm_t *PcmHandle{nullptr};
428 al::vector<char> Buffer;
430 std::atomic<ALenum> mKillNow{AL_TRUE};
431 std::thread mThread;
434 int ALCplaybackAlsa_mixerProc(ALCplaybackAlsa *self);
435 int ALCplaybackAlsa_mixerNoMMapProc(ALCplaybackAlsa *self);
437 void ALCplaybackAlsa_Construct(ALCplaybackAlsa *self, ALCdevice *device);
438 void ALCplaybackAlsa_Destruct(ALCplaybackAlsa *self);
439 ALCenum ALCplaybackAlsa_open(ALCplaybackAlsa *self, const ALCchar *name);
440 ALCboolean ALCplaybackAlsa_reset(ALCplaybackAlsa *self);
441 ALCboolean ALCplaybackAlsa_start(ALCplaybackAlsa *self);
442 void ALCplaybackAlsa_stop(ALCplaybackAlsa *self);
443 DECLARE_FORWARD2(ALCplaybackAlsa, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
444 DECLARE_FORWARD(ALCplaybackAlsa, ALCbackend, ALCuint, availableSamples)
445 ClockLatency ALCplaybackAlsa_getClockLatency(ALCplaybackAlsa *self);
446 DECLARE_FORWARD(ALCplaybackAlsa, ALCbackend, void, lock)
447 DECLARE_FORWARD(ALCplaybackAlsa, ALCbackend, void, unlock)
448 DECLARE_DEFAULT_ALLOCATORS(ALCplaybackAlsa)
449 DEFINE_ALCBACKEND_VTABLE(ALCplaybackAlsa);
452 void ALCplaybackAlsa_Construct(ALCplaybackAlsa *self, ALCdevice *device)
454 new (self) ALCplaybackAlsa{};
455 ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
456 SET_VTABLE2(ALCplaybackAlsa, ALCbackend, self);
459 void ALCplaybackAlsa_Destruct(ALCplaybackAlsa *self)
461 if(self->PcmHandle)
462 snd_pcm_close(self->PcmHandle);
463 self->PcmHandle = nullptr;
465 ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
466 self->~ALCplaybackAlsa();
470 int ALCplaybackAlsa_mixerProc(ALCplaybackAlsa *self)
472 ALCdevice *device{STATIC_CAST(ALCbackend, self)->mDevice};
474 SetRTPriority();
475 althrd_setname(MIXER_THREAD_NAME);
477 snd_pcm_uframes_t update_size{device->UpdateSize};
478 snd_pcm_uframes_t num_updates{device->NumUpdates};
479 while(!self->mKillNow.load(std::memory_order_acquire))
481 int state{verify_state(self->PcmHandle)};
482 if(state < 0)
484 ERR("Invalid state detected: %s\n", snd_strerror(state));
485 ALCplaybackAlsa_lock(self);
486 aluHandleDisconnect(device, "Bad state: %s", snd_strerror(state));
487 ALCplaybackAlsa_unlock(self);
488 break;
491 snd_pcm_sframes_t avail{snd_pcm_avail_update(self->PcmHandle)};
492 if(avail < 0)
494 ERR("available update failed: %s\n", snd_strerror(avail));
495 continue;
498 if((snd_pcm_uframes_t)avail > update_size*(num_updates+1))
500 WARN("available samples exceeds the buffer size\n");
501 snd_pcm_reset(self->PcmHandle);
502 continue;
505 // make sure there's frames to process
506 if((snd_pcm_uframes_t)avail < update_size)
508 if(state != SND_PCM_STATE_RUNNING)
510 int err{snd_pcm_start(self->PcmHandle)};
511 if(err < 0)
513 ERR("start failed: %s\n", snd_strerror(err));
514 continue;
517 if(snd_pcm_wait(self->PcmHandle, 1000) == 0)
518 ERR("Wait timeout... buffer size too low?\n");
519 continue;
521 avail -= avail%update_size;
523 // it is possible that contiguous areas are smaller, thus we use a loop
524 ALCplaybackAlsa_lock(self);
525 while(avail > 0)
527 snd_pcm_uframes_t frames{static_cast<snd_pcm_uframes_t>(avail)};
529 const snd_pcm_channel_area_t *areas{};
530 snd_pcm_uframes_t offset{};
531 int err{snd_pcm_mmap_begin(self->PcmHandle, &areas, &offset, &frames)};
532 if(err < 0)
534 ERR("mmap begin error: %s\n", snd_strerror(err));
535 break;
538 char *WritePtr{(char*)areas->addr + (offset * areas->step / 8)};
539 aluMixData(device, WritePtr, frames);
541 snd_pcm_sframes_t commitres{snd_pcm_mmap_commit(self->PcmHandle, offset, frames)};
542 if(commitres < 0 || (commitres-frames) != 0)
544 ERR("mmap commit error: %s\n",
545 snd_strerror(commitres >= 0 ? -EPIPE : commitres));
546 break;
549 avail -= frames;
551 ALCplaybackAlsa_unlock(self);
554 return 0;
557 int ALCplaybackAlsa_mixerNoMMapProc(ALCplaybackAlsa *self)
559 ALCdevice *device{STATIC_CAST(ALCbackend, self)->mDevice};
561 SetRTPriority();
562 althrd_setname(MIXER_THREAD_NAME);
564 snd_pcm_uframes_t update_size{device->UpdateSize};
565 snd_pcm_uframes_t num_updates{device->NumUpdates};
566 while(!self->mKillNow.load(std::memory_order_acquire))
568 int state{verify_state(self->PcmHandle)};
569 if(state < 0)
571 ERR("Invalid state detected: %s\n", snd_strerror(state));
572 ALCplaybackAlsa_lock(self);
573 aluHandleDisconnect(device, "Bad state: %s", snd_strerror(state));
574 ALCplaybackAlsa_unlock(self);
575 break;
578 snd_pcm_sframes_t avail{snd_pcm_avail_update(self->PcmHandle)};
579 if(avail < 0)
581 ERR("available update failed: %s\n", snd_strerror(avail));
582 continue;
585 if((snd_pcm_uframes_t)avail > update_size*num_updates)
587 WARN("available samples exceeds the buffer size\n");
588 snd_pcm_reset(self->PcmHandle);
589 continue;
592 if((snd_pcm_uframes_t)avail < update_size)
594 if(state != SND_PCM_STATE_RUNNING)
596 int err{snd_pcm_start(self->PcmHandle)};
597 if(err < 0)
599 ERR("start failed: %s\n", snd_strerror(err));
600 continue;
603 if(snd_pcm_wait(self->PcmHandle, 1000) == 0)
604 ERR("Wait timeout... buffer size too low?\n");
605 continue;
608 ALCplaybackAlsa_lock(self);
609 char *WritePtr{self->Buffer.data()};
610 avail = snd_pcm_bytes_to_frames(self->PcmHandle, self->Buffer.size());
611 aluMixData(device, WritePtr, avail);
612 while(avail > 0)
614 int ret = snd_pcm_writei(self->PcmHandle, WritePtr, avail);
615 switch (ret)
617 case -EAGAIN:
618 continue;
619 #if ESTRPIPE != EPIPE
620 case -ESTRPIPE:
621 #endif
622 case -EPIPE:
623 case -EINTR:
624 ret = snd_pcm_recover(self->PcmHandle, ret, 1);
625 if(ret < 0)
626 avail = 0;
627 break;
628 default:
629 if (ret >= 0)
631 WritePtr += snd_pcm_frames_to_bytes(self->PcmHandle, ret);
632 avail -= ret;
634 break;
636 if (ret < 0)
638 ret = snd_pcm_prepare(self->PcmHandle);
639 if(ret < 0)
640 break;
643 ALCplaybackAlsa_unlock(self);
646 return 0;
650 ALCenum ALCplaybackAlsa_open(ALCplaybackAlsa *self, const ALCchar *name)
652 const char *driver{};
653 if(name)
655 if(PlaybackDevices.empty())
656 PlaybackDevices = probe_devices(SND_PCM_STREAM_PLAYBACK);
658 auto iter = std::find_if(PlaybackDevices.cbegin(), PlaybackDevices.cend(),
659 [name](const DevMap &entry) -> bool
660 { return entry.name == name; }
662 if(iter == PlaybackDevices.cend())
663 return ALC_INVALID_VALUE;
664 driver = iter->device_name.c_str();
666 else
668 name = alsaDevice;
669 driver = GetConfigValue(nullptr, "alsa", "device", "default");
672 TRACE("Opening device \"%s\"\n", driver);
673 int err{snd_pcm_open(&self->PcmHandle, driver, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)};
674 if(err < 0)
676 ERR("Could not open playback device '%s': %s\n", driver, snd_strerror(err));
677 return ALC_OUT_OF_MEMORY;
680 /* Free alsa's global config tree. Otherwise valgrind reports a ton of leaks. */
681 snd_config_update_free_global();
683 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
684 device->DeviceName = name;
686 return ALC_NO_ERROR;
689 ALCboolean ALCplaybackAlsa_reset(ALCplaybackAlsa *self)
691 ALCdevice *device{STATIC_CAST(ALCbackend, self)->mDevice};
693 snd_pcm_format_t format{SND_PCM_FORMAT_UNKNOWN};
694 switch(device->FmtType)
696 case DevFmtByte:
697 format = SND_PCM_FORMAT_S8;
698 break;
699 case DevFmtUByte:
700 format = SND_PCM_FORMAT_U8;
701 break;
702 case DevFmtShort:
703 format = SND_PCM_FORMAT_S16;
704 break;
705 case DevFmtUShort:
706 format = SND_PCM_FORMAT_U16;
707 break;
708 case DevFmtInt:
709 format = SND_PCM_FORMAT_S32;
710 break;
711 case DevFmtUInt:
712 format = SND_PCM_FORMAT_U32;
713 break;
714 case DevFmtFloat:
715 format = SND_PCM_FORMAT_FLOAT;
716 break;
719 bool allowmmap{!!GetConfigValueBool(device->DeviceName.c_str(), "alsa", "mmap", 1)};
720 ALuint periods{device->NumUpdates};
721 ALuint periodLen{static_cast<ALuint>(device->UpdateSize * U64(1000000) / device->Frequency)};
722 ALuint bufferLen{periodLen * periods};
723 ALuint rate{device->Frequency};
725 snd_pcm_uframes_t periodSizeInFrames;
726 snd_pcm_sw_params_t *sp{};
727 snd_pcm_hw_params_t *hp{};
728 snd_pcm_access_t access;
729 const char *funcerr;
730 int dir, err;
731 snd_pcm_hw_params_malloc(&hp);
732 #define CHECK(x) if((funcerr=#x),(err=(x)) < 0) goto error
733 CHECK(snd_pcm_hw_params_any(self->PcmHandle, hp));
734 /* set interleaved access */
735 if(!allowmmap || snd_pcm_hw_params_set_access(self->PcmHandle, hp, SND_PCM_ACCESS_MMAP_INTERLEAVED) < 0)
737 /* No mmap */
738 CHECK(snd_pcm_hw_params_set_access(self->PcmHandle, hp, SND_PCM_ACCESS_RW_INTERLEAVED));
740 /* test and set format (implicitly sets sample bits) */
741 if(snd_pcm_hw_params_test_format(self->PcmHandle, hp, format) < 0)
743 static const struct {
744 snd_pcm_format_t format;
745 enum DevFmtType fmttype;
746 } formatlist[] = {
747 { SND_PCM_FORMAT_FLOAT, DevFmtFloat },
748 { SND_PCM_FORMAT_S32, DevFmtInt },
749 { SND_PCM_FORMAT_U32, DevFmtUInt },
750 { SND_PCM_FORMAT_S16, DevFmtShort },
751 { SND_PCM_FORMAT_U16, DevFmtUShort },
752 { SND_PCM_FORMAT_S8, DevFmtByte },
753 { SND_PCM_FORMAT_U8, DevFmtUByte },
756 for(const auto &fmt : formatlist)
758 format = fmt.format;
759 if(snd_pcm_hw_params_test_format(self->PcmHandle, hp, format) >= 0)
761 device->FmtType = fmt.fmttype;
762 break;
766 CHECK(snd_pcm_hw_params_set_format(self->PcmHandle, hp, format));
767 /* test and set channels (implicitly sets frame bits) */
768 if(snd_pcm_hw_params_test_channels(self->PcmHandle, hp, ChannelsFromDevFmt(device->FmtChans, device->mAmbiOrder)) < 0)
770 static const enum DevFmtChannels channellist[] = {
771 DevFmtStereo,
772 DevFmtQuad,
773 DevFmtX51,
774 DevFmtX71,
775 DevFmtMono,
778 for(const auto &chan : channellist)
780 if(snd_pcm_hw_params_test_channels(self->PcmHandle, hp, ChannelsFromDevFmt(chan, 0)) >= 0)
782 device->FmtChans = chan;
783 device->mAmbiOrder = 0;
784 break;
788 CHECK(snd_pcm_hw_params_set_channels(self->PcmHandle, hp, ChannelsFromDevFmt(device->FmtChans, device->mAmbiOrder)));
789 /* set rate (implicitly constrains period/buffer parameters) */
790 if(!GetConfigValueBool(device->DeviceName.c_str(), "alsa", "allow-resampler", 0) ||
791 !(device->Flags&DEVICE_FREQUENCY_REQUEST))
793 if(snd_pcm_hw_params_set_rate_resample(self->PcmHandle, hp, 0) < 0)
794 ERR("Failed to disable ALSA resampler\n");
796 else if(snd_pcm_hw_params_set_rate_resample(self->PcmHandle, hp, 1) < 0)
797 ERR("Failed to enable ALSA resampler\n");
798 CHECK(snd_pcm_hw_params_set_rate_near(self->PcmHandle, hp, &rate, nullptr));
799 /* set buffer time (implicitly constrains period/buffer parameters) */
800 if((err=snd_pcm_hw_params_set_buffer_time_near(self->PcmHandle, hp, &bufferLen, nullptr)) < 0)
801 ERR("snd_pcm_hw_params_set_buffer_time_near failed: %s\n", snd_strerror(err));
802 /* set period time (implicitly sets buffer size/bytes/time and period size/bytes) */
803 if((err=snd_pcm_hw_params_set_period_time_near(self->PcmHandle, hp, &periodLen, nullptr)) < 0)
804 ERR("snd_pcm_hw_params_set_period_time_near failed: %s\n", snd_strerror(err));
805 /* install and prepare hardware configuration */
806 CHECK(snd_pcm_hw_params(self->PcmHandle, hp));
808 /* retrieve configuration info */
809 CHECK(snd_pcm_hw_params_get_access(hp, &access));
810 CHECK(snd_pcm_hw_params_get_period_size(hp, &periodSizeInFrames, nullptr));
811 CHECK(snd_pcm_hw_params_get_periods(hp, &periods, &dir));
812 if(dir != 0)
813 WARN("Inexact period count: %u (%d)\n", periods, dir);
814 snd_pcm_hw_params_free(hp);
815 hp = nullptr;
817 snd_pcm_sw_params_malloc(&sp);
818 CHECK(snd_pcm_sw_params_current(self->PcmHandle, sp));
819 CHECK(snd_pcm_sw_params_set_avail_min(self->PcmHandle, sp, periodSizeInFrames));
820 CHECK(snd_pcm_sw_params_set_stop_threshold(self->PcmHandle, sp, periodSizeInFrames*periods));
821 CHECK(snd_pcm_sw_params(self->PcmHandle, sp));
822 #undef CHECK
823 snd_pcm_sw_params_free(sp);
824 sp = nullptr;
826 device->NumUpdates = periods;
827 device->UpdateSize = periodSizeInFrames;
828 device->Frequency = rate;
830 SetDefaultChannelOrder(device);
832 return ALC_TRUE;
834 error:
835 ERR("%s failed: %s\n", funcerr, snd_strerror(err));
836 if(hp) snd_pcm_hw_params_free(hp);
837 if(sp) snd_pcm_sw_params_free(sp);
838 return ALC_FALSE;
841 ALCboolean ALCplaybackAlsa_start(ALCplaybackAlsa *self)
843 ALCdevice *device{STATIC_CAST(ALCbackend, self)->mDevice};
844 int (*thread_func)(ALCplaybackAlsa*){};
845 snd_pcm_hw_params_t *hp{};
846 snd_pcm_access_t access;
847 const char *funcerr;
848 int err;
850 snd_pcm_hw_params_malloc(&hp);
851 #define CHECK(x) if((funcerr=#x),(err=(x)) < 0) goto error
852 CHECK(snd_pcm_hw_params_current(self->PcmHandle, hp));
853 /* retrieve configuration info */
854 CHECK(snd_pcm_hw_params_get_access(hp, &access));
855 #undef CHECK
856 snd_pcm_hw_params_free(hp);
857 hp = nullptr;
859 if(access == SND_PCM_ACCESS_RW_INTERLEAVED)
861 self->Buffer.resize(snd_pcm_frames_to_bytes(self->PcmHandle, device->UpdateSize));
862 thread_func = ALCplaybackAlsa_mixerNoMMapProc;
864 else
866 err = snd_pcm_prepare(self->PcmHandle);
867 if(err < 0)
869 ERR("snd_pcm_prepare(data->PcmHandle) failed: %s\n", snd_strerror(err));
870 return ALC_FALSE;
872 thread_func = ALCplaybackAlsa_mixerProc;
875 try {
876 self->mKillNow.store(AL_FALSE, std::memory_order_release);
877 self->mThread = std::thread(thread_func, self);
878 return ALC_TRUE;
880 catch(std::exception& e) {
881 ERR("Could not create playback thread: %s\n", e.what());
883 catch(...) {
885 self->Buffer.clear();
886 return ALC_FALSE;
888 error:
889 ERR("%s failed: %s\n", funcerr, snd_strerror(err));
890 if(hp) snd_pcm_hw_params_free(hp);
891 return ALC_FALSE;
894 void ALCplaybackAlsa_stop(ALCplaybackAlsa *self)
896 if(self->mKillNow.exchange(AL_TRUE, std::memory_order_acq_rel) || !self->mThread.joinable())
897 return;
899 self->mThread.join();
901 self->Buffer.clear();
904 ClockLatency ALCplaybackAlsa_getClockLatency(ALCplaybackAlsa *self)
906 ALCdevice *device{STATIC_CAST(ALCbackend, self)->mDevice};
907 ClockLatency ret;
909 ALCplaybackAlsa_lock(self);
910 ret.ClockTime = GetDeviceClockTime(device);
911 snd_pcm_sframes_t delay{};
912 int err{snd_pcm_delay(self->PcmHandle, &delay)};
913 if(err < 0)
915 ERR("Failed to get pcm delay: %s\n", snd_strerror(err));
916 delay = 0;
918 ret.Latency = std::chrono::seconds{std::max<snd_pcm_sframes_t>(0, delay)};
919 ret.Latency /= device->Frequency;
920 ALCplaybackAlsa_unlock(self);
922 return ret;
926 struct ALCcaptureAlsa final : public ALCbackend {
927 snd_pcm_t *PcmHandle{nullptr};
929 al::vector<char> Buffer;
931 bool DoCapture{false};
932 ll_ringbuffer_t *Ring{nullptr};
934 snd_pcm_sframes_t mLastAvail{0};
937 void ALCcaptureAlsa_Construct(ALCcaptureAlsa *self, ALCdevice *device);
938 void ALCcaptureAlsa_Destruct(ALCcaptureAlsa *self);
939 ALCenum ALCcaptureAlsa_open(ALCcaptureAlsa *self, const ALCchar *name);
940 DECLARE_FORWARD(ALCcaptureAlsa, ALCbackend, ALCboolean, reset)
941 ALCboolean ALCcaptureAlsa_start(ALCcaptureAlsa *self);
942 void ALCcaptureAlsa_stop(ALCcaptureAlsa *self);
943 ALCenum ALCcaptureAlsa_captureSamples(ALCcaptureAlsa *self, ALCvoid *buffer, ALCuint samples);
944 ALCuint ALCcaptureAlsa_availableSamples(ALCcaptureAlsa *self);
945 ClockLatency ALCcaptureAlsa_getClockLatency(ALCcaptureAlsa *self);
946 DECLARE_FORWARD(ALCcaptureAlsa, ALCbackend, void, lock)
947 DECLARE_FORWARD(ALCcaptureAlsa, ALCbackend, void, unlock)
948 DECLARE_DEFAULT_ALLOCATORS(ALCcaptureAlsa)
950 DEFINE_ALCBACKEND_VTABLE(ALCcaptureAlsa);
953 void ALCcaptureAlsa_Construct(ALCcaptureAlsa *self, ALCdevice *device)
955 new (self) ALCcaptureAlsa{};
956 ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
957 SET_VTABLE2(ALCcaptureAlsa, ALCbackend, self);
960 void ALCcaptureAlsa_Destruct(ALCcaptureAlsa *self)
962 if(self->PcmHandle)
963 snd_pcm_close(self->PcmHandle);
964 self->PcmHandle = nullptr;
966 ll_ringbuffer_free(self->Ring);
967 self->Ring = nullptr;
969 ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
970 self->~ALCcaptureAlsa();
974 ALCenum ALCcaptureAlsa_open(ALCcaptureAlsa *self, const ALCchar *name)
976 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
977 const char *driver{};
978 if(name)
980 if(CaptureDevices.empty())
981 CaptureDevices = probe_devices(SND_PCM_STREAM_CAPTURE);
983 auto iter = std::find_if(CaptureDevices.cbegin(), CaptureDevices.cend(),
984 [name](const DevMap &entry) -> bool
985 { return entry.name == name; }
987 if(iter == CaptureDevices.cend())
988 return ALC_INVALID_VALUE;
989 driver = iter->device_name.c_str();
991 else
993 name = alsaDevice;
994 driver = GetConfigValue(nullptr, "alsa", "capture", "default");
997 TRACE("Opening device \"%s\"\n", driver);
998 int err{snd_pcm_open(&self->PcmHandle, driver, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK)};
999 if(err < 0)
1001 ERR("Could not open capture device '%s': %s\n", driver, snd_strerror(err));
1002 return ALC_INVALID_VALUE;
1005 /* Free alsa's global config tree. Otherwise valgrind reports a ton of leaks. */
1006 snd_config_update_free_global();
1008 snd_pcm_format_t format{SND_PCM_FORMAT_UNKNOWN};
1009 switch(device->FmtType)
1011 case DevFmtByte:
1012 format = SND_PCM_FORMAT_S8;
1013 break;
1014 case DevFmtUByte:
1015 format = SND_PCM_FORMAT_U8;
1016 break;
1017 case DevFmtShort:
1018 format = SND_PCM_FORMAT_S16;
1019 break;
1020 case DevFmtUShort:
1021 format = SND_PCM_FORMAT_U16;
1022 break;
1023 case DevFmtInt:
1024 format = SND_PCM_FORMAT_S32;
1025 break;
1026 case DevFmtUInt:
1027 format = SND_PCM_FORMAT_U32;
1028 break;
1029 case DevFmtFloat:
1030 format = SND_PCM_FORMAT_FLOAT;
1031 break;
1034 snd_pcm_uframes_t bufferSizeInFrames{maxu(device->UpdateSize*device->NumUpdates,
1035 100*device->Frequency/1000)};
1036 snd_pcm_uframes_t periodSizeInFrames{minu(bufferSizeInFrames, 25*device->Frequency/1000)};
1038 bool needring{false};
1039 const char *funcerr{};
1040 snd_pcm_hw_params_t *hp{};
1041 snd_pcm_hw_params_malloc(&hp);
1042 #define CHECK(x) if((funcerr=#x),(err=(x)) < 0) goto error
1043 CHECK(snd_pcm_hw_params_any(self->PcmHandle, hp));
1044 /* set interleaved access */
1045 CHECK(snd_pcm_hw_params_set_access(self->PcmHandle, hp, SND_PCM_ACCESS_RW_INTERLEAVED));
1046 /* set format (implicitly sets sample bits) */
1047 CHECK(snd_pcm_hw_params_set_format(self->PcmHandle, hp, format));
1048 /* set channels (implicitly sets frame bits) */
1049 CHECK(snd_pcm_hw_params_set_channels(self->PcmHandle, hp, ChannelsFromDevFmt(device->FmtChans, device->mAmbiOrder)));
1050 /* set rate (implicitly constrains period/buffer parameters) */
1051 CHECK(snd_pcm_hw_params_set_rate(self->PcmHandle, hp, device->Frequency, 0));
1052 /* set buffer size in frame units (implicitly sets period size/bytes/time and buffer time/bytes) */
1053 if(snd_pcm_hw_params_set_buffer_size_min(self->PcmHandle, hp, &bufferSizeInFrames) < 0)
1055 TRACE("Buffer too large, using intermediate ring buffer\n");
1056 needring = true;
1057 CHECK(snd_pcm_hw_params_set_buffer_size_near(self->PcmHandle, hp, &bufferSizeInFrames));
1059 /* set buffer size in frame units (implicitly sets period size/bytes/time and buffer time/bytes) */
1060 CHECK(snd_pcm_hw_params_set_period_size_near(self->PcmHandle, hp, &periodSizeInFrames, nullptr));
1061 /* install and prepare hardware configuration */
1062 CHECK(snd_pcm_hw_params(self->PcmHandle, hp));
1063 /* retrieve configuration info */
1064 CHECK(snd_pcm_hw_params_get_period_size(hp, &periodSizeInFrames, nullptr));
1065 #undef CHECK
1066 snd_pcm_hw_params_free(hp);
1067 hp = nullptr;
1069 if(needring)
1071 self->Ring = ll_ringbuffer_create(
1072 device->UpdateSize*device->NumUpdates,
1073 FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->mAmbiOrder),
1074 false
1076 if(!self->Ring)
1078 ERR("ring buffer create failed\n");
1079 goto error2;
1083 device->DeviceName = name;
1085 return ALC_NO_ERROR;
1087 error:
1088 ERR("%s failed: %s\n", funcerr, snd_strerror(err));
1089 if(hp) snd_pcm_hw_params_free(hp);
1091 error2:
1092 ll_ringbuffer_free(self->Ring);
1093 self->Ring = nullptr;
1094 snd_pcm_close(self->PcmHandle);
1095 self->PcmHandle = nullptr;
1097 return ALC_INVALID_VALUE;
1100 ALCboolean ALCcaptureAlsa_start(ALCcaptureAlsa *self)
1102 int err{snd_pcm_prepare(self->PcmHandle)};
1103 if(err < 0)
1104 ERR("prepare failed: %s\n", snd_strerror(err));
1105 else
1107 err = snd_pcm_start(self->PcmHandle);
1108 if(err < 0)
1109 ERR("start failed: %s\n", snd_strerror(err));
1111 if(err < 0)
1113 aluHandleDisconnect(STATIC_CAST(ALCbackend, self)->mDevice, "Capture state failure: %s",
1114 snd_strerror(err));
1115 return ALC_FALSE;
1118 self->DoCapture = true;
1119 return ALC_TRUE;
1122 void ALCcaptureAlsa_stop(ALCcaptureAlsa *self)
1124 /* OpenAL requires access to unread audio after stopping, but ALSA's
1125 * snd_pcm_drain is unreliable and snd_pcm_drop drops it. Capture what's
1126 * available now so it'll be available later after the drop. */
1127 ALCuint avail{ALCcaptureAlsa_availableSamples(self)};
1128 if(!self->Ring && avail > 0)
1130 /* The ring buffer implicitly captures when checking availability.
1131 * Direct access needs to explicitly capture it into temp storage. */
1132 al::vector<char> temp(snd_pcm_frames_to_bytes(self->PcmHandle, avail));
1133 ALCcaptureAlsa_captureSamples(self, temp.data(), avail);
1134 self->Buffer = std::move(temp);
1136 int err{snd_pcm_drop(self->PcmHandle)};
1137 if(err < 0)
1138 ERR("drop failed: %s\n", snd_strerror(err));
1139 self->DoCapture = false;
1142 ALCenum ALCcaptureAlsa_captureSamples(ALCcaptureAlsa *self, ALCvoid *buffer, ALCuint samples)
1144 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
1146 if(self->Ring)
1148 ll_ringbuffer_read(self->Ring, static_cast<char*>(buffer), samples);
1149 return ALC_NO_ERROR;
1152 self->mLastAvail -= samples;
1153 while(ATOMIC_LOAD(&device->Connected, almemory_order_acquire) && samples > 0)
1155 snd_pcm_sframes_t amt{0};
1157 if(!self->Buffer.empty())
1159 /* First get any data stored from the last stop */
1160 amt = snd_pcm_bytes_to_frames(self->PcmHandle, self->Buffer.size());
1161 if((snd_pcm_uframes_t)amt > samples) amt = samples;
1163 amt = snd_pcm_frames_to_bytes(self->PcmHandle, amt);
1164 memcpy(buffer, self->Buffer.data(), amt);
1166 self->Buffer.erase(self->Buffer.begin(), self->Buffer.begin()+amt);
1167 amt = snd_pcm_bytes_to_frames(self->PcmHandle, amt);
1169 else if(self->DoCapture)
1170 amt = snd_pcm_readi(self->PcmHandle, buffer, samples);
1171 if(amt < 0)
1173 ERR("read error: %s\n", snd_strerror(amt));
1175 if(amt == -EAGAIN)
1176 continue;
1177 if((amt=snd_pcm_recover(self->PcmHandle, amt, 1)) >= 0)
1179 amt = snd_pcm_start(self->PcmHandle);
1180 if(amt >= 0)
1181 amt = snd_pcm_avail_update(self->PcmHandle);
1183 if(amt < 0)
1185 ERR("restore error: %s\n", snd_strerror(amt));
1186 aluHandleDisconnect(device, "Capture recovery failure: %s", snd_strerror(amt));
1187 break;
1189 /* If the amount available is less than what's asked, we lost it
1190 * during recovery. So just give silence instead. */
1191 if((snd_pcm_uframes_t)amt < samples)
1192 break;
1193 continue;
1196 buffer = (ALbyte*)buffer + amt;
1197 samples -= amt;
1199 if(samples > 0)
1200 memset(buffer, ((device->FmtType == DevFmtUByte) ? 0x80 : 0),
1201 snd_pcm_frames_to_bytes(self->PcmHandle, samples));
1203 return ALC_NO_ERROR;
1206 ALCuint ALCcaptureAlsa_availableSamples(ALCcaptureAlsa *self)
1208 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
1210 snd_pcm_sframes_t avail{0};
1211 if(ATOMIC_LOAD(&device->Connected, almemory_order_acquire) && self->DoCapture)
1212 avail = snd_pcm_avail_update(self->PcmHandle);
1213 if(avail < 0)
1215 ERR("avail update failed: %s\n", snd_strerror(avail));
1217 if((avail=snd_pcm_recover(self->PcmHandle, avail, 1)) >= 0)
1219 if(self->DoCapture)
1220 avail = snd_pcm_start(self->PcmHandle);
1221 if(avail >= 0)
1222 avail = snd_pcm_avail_update(self->PcmHandle);
1224 if(avail < 0)
1226 ERR("restore error: %s\n", snd_strerror(avail));
1227 aluHandleDisconnect(device, "Capture recovery failure: %s", snd_strerror(avail));
1231 if(!self->Ring)
1233 if(avail < 0) avail = 0;
1234 avail += snd_pcm_bytes_to_frames(self->PcmHandle, self->Buffer.size());
1235 if(avail > self->mLastAvail) self->mLastAvail = avail;
1236 return self->mLastAvail;
1239 while(avail > 0)
1241 auto vec = ll_ringbuffer_get_write_vector(self->Ring);
1242 if(vec.first.len == 0) break;
1244 snd_pcm_sframes_t amt{std::min<snd_pcm_sframes_t>(vec.first.len, avail)};
1245 amt = snd_pcm_readi(self->PcmHandle, vec.first.buf, amt);
1246 if(amt < 0)
1248 ERR("read error: %s\n", snd_strerror(amt));
1250 if(amt == -EAGAIN)
1251 continue;
1252 if((amt=snd_pcm_recover(self->PcmHandle, amt, 1)) >= 0)
1254 if(self->DoCapture)
1255 amt = snd_pcm_start(self->PcmHandle);
1256 if(amt >= 0)
1257 amt = snd_pcm_avail_update(self->PcmHandle);
1259 if(amt < 0)
1261 ERR("restore error: %s\n", snd_strerror(amt));
1262 aluHandleDisconnect(device, "Capture recovery failure: %s", snd_strerror(amt));
1263 break;
1265 avail = amt;
1266 continue;
1269 ll_ringbuffer_write_advance(self->Ring, amt);
1270 avail -= amt;
1273 return ll_ringbuffer_read_space(self->Ring);
1276 ClockLatency ALCcaptureAlsa_getClockLatency(ALCcaptureAlsa *self)
1278 ALCdevice *device{STATIC_CAST(ALCbackend, self)->mDevice};
1279 ClockLatency ret;
1281 ALCcaptureAlsa_lock(self);
1282 ret.ClockTime = GetDeviceClockTime(device);
1283 snd_pcm_sframes_t delay{};
1284 int err{snd_pcm_delay(self->PcmHandle, &delay)};
1285 if(err < 0)
1287 ERR("Failed to get pcm delay: %s\n", snd_strerror(err));
1288 delay = 0;
1290 ret.Latency = std::chrono::seconds{std::max<snd_pcm_sframes_t>(0, delay)};
1291 ret.Latency /= device->Frequency;
1292 ALCcaptureAlsa_unlock(self);
1294 return ret;
1297 } // namespace
1300 bool AlsaBackendFactory::init()
1301 { return !!alsa_load(); }
1303 void AlsaBackendFactory::deinit()
1305 PlaybackDevices.clear();
1306 CaptureDevices.clear();
1308 #ifdef HAVE_DYNLOAD
1309 if(alsa_handle)
1310 CloseLib(alsa_handle);
1311 alsa_handle = nullptr;
1312 #endif
1315 bool AlsaBackendFactory::querySupport(ALCbackend_Type type)
1316 { return (type == ALCbackend_Playback || type == ALCbackend_Capture); }
1318 void AlsaBackendFactory::probe(enum DevProbe type, std::string *outnames)
1320 auto add_device = [outnames](const DevMap &entry) -> void
1322 /* +1 to also append the null char (to ensure a null-separated list and
1323 * double-null terminated list).
1325 outnames->append(entry.name.c_str(), entry.name.length()+1);
1327 switch(type)
1329 case ALL_DEVICE_PROBE:
1330 PlaybackDevices = probe_devices(SND_PCM_STREAM_PLAYBACK);
1331 std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device);
1332 break;
1334 case CAPTURE_DEVICE_PROBE:
1335 CaptureDevices = probe_devices(SND_PCM_STREAM_CAPTURE);
1336 std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device);
1337 break;
1341 ALCbackend *AlsaBackendFactory::createBackend(ALCdevice *device, ALCbackend_Type type)
1343 if(type == ALCbackend_Playback)
1345 ALCplaybackAlsa *backend;
1346 NEW_OBJ(backend, ALCplaybackAlsa)(device);
1347 if(!backend) return nullptr;
1348 return STATIC_CAST(ALCbackend, backend);
1350 if(type == ALCbackend_Capture)
1352 ALCcaptureAlsa *backend;
1353 NEW_OBJ(backend, ALCcaptureAlsa)(device);
1354 if(!backend) return nullptr;
1355 return STATIC_CAST(ALCbackend, backend);
1358 return nullptr;
1361 BackendFactory &AlsaBackendFactory::getFactory()
1363 static AlsaBackendFactory factory{};
1364 return factory;