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
23 #include "backends/alsa.h"
38 #include "ringbuffer.h"
41 #include <alsa/asoundlib.h>
46 constexpr ALCchar alsaDevice
[] = "ALSA Default";
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
);
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
213 std::string missing_funcs
;
215 alsa_handle
= LoadLib("libasound.so.2");
218 WARN("Failed to load %s\n", "libasound.so.2");
223 #define LOAD_FUNC(f) do { \
224 p##f = reinterpret_cast<decltype(p##f)>(GetSymbol(alsa_handle, #f)); \
225 if(p##f == nullptr) { \
227 missing_funcs += "\n" #f; \
230 ALSA_FUNCS(LOAD_FUNC
);
235 WARN("Missing expected functions:%s\n", missing_funcs
.c_str());
236 CloseLib(alsa_handle
);
237 alsa_handle
= nullptr;
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",
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
, '=')};
290 std::string spec
{next
? std::string(customdevs
, next
++) : std::string(customdevs
)};
291 ERR("Invalid ALSA device specification \"%s\"\n", spec
.c_str());
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
);
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
)};
313 if((err
=snd_ctl_open(&handle
, name
.c_str(), 0)) < 0)
315 ERR("control open (hw:%d): %s\n", card
, snd_strerror(err
));
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
);
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
);
331 const char *card_prefix
{main_prefix
};
332 ConfigValueStr(nullptr, "alsa", name
.c_str(), &card_prefix
);
337 if(snd_ctl_pcm_next_device(handle
, &dev
) < 0)
338 ERR("snd_ctl_pcm_next_device failed\n");
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)
347 ERR("control digital audio info (hw:%d): %s\n", card
, snd_strerror(err
));
351 /* "prefix-cardid-dev" */
352 name
= prefix_name(stream
);
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)" */
363 name
+= snd_pcm_info_get_name(pcminfo
);
367 name
+= std::to_string(dev
);
370 /* "devprefixCARD=cardid,DEV=dev" */
371 std::string device
{device_prefix
};
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
);
384 ERR("snd_card_next failed: %s\n", snd_strerror(err
));
386 snd_pcm_info_free(pcminfo
);
387 snd_ctl_card_info_free(info
);
393 int verify_state(snd_pcm_t
*handle
)
395 snd_pcm_state_t state
{snd_pcm_state(handle
)};
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
:
409 case SND_PCM_STATE_XRUN
:
410 if((err
=snd_pcm_recover(handle
, -EPIPE
, 1)) < 0)
413 case SND_PCM_STATE_SUSPENDED
:
414 if((err
=snd_pcm_recover(handle
, -ESTRPIPE
, 1)) < 0)
417 case SND_PCM_STATE_DISCONNECTED
:
425 struct ALCplaybackAlsa final
: public ALCbackend
{
426 snd_pcm_t
*PcmHandle
{nullptr};
428 al::vector
<char> Buffer
;
430 std::atomic
<ALenum
> mKillNow
{AL_TRUE
};
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
)
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
};
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
)};
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
);
491 snd_pcm_sframes_t avail
{snd_pcm_avail_update(self
->PcmHandle
)};
494 ERR("available update failed: %s\n", snd_strerror(avail
));
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
);
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
)};
513 ERR("start failed: %s\n", snd_strerror(err
));
517 if(snd_pcm_wait(self
->PcmHandle
, 1000) == 0)
518 ERR("Wait timeout... buffer size too low?\n");
521 avail
-= avail
%update_size
;
523 // it is possible that contiguous areas are smaller, thus we use a loop
524 ALCplaybackAlsa_lock(self
);
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
)};
534 ERR("mmap begin error: %s\n", snd_strerror(err
));
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
));
551 ALCplaybackAlsa_unlock(self
);
557 int ALCplaybackAlsa_mixerNoMMapProc(ALCplaybackAlsa
*self
)
559 ALCdevice
*device
{STATIC_CAST(ALCbackend
, self
)->mDevice
};
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
)};
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
);
578 snd_pcm_sframes_t avail
{snd_pcm_avail_update(self
->PcmHandle
)};
581 ERR("available update failed: %s\n", snd_strerror(avail
));
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
);
592 if((snd_pcm_uframes_t
)avail
< update_size
)
594 if(state
!= SND_PCM_STATE_RUNNING
)
596 int err
{snd_pcm_start(self
->PcmHandle
)};
599 ERR("start failed: %s\n", snd_strerror(err
));
603 if(snd_pcm_wait(self
->PcmHandle
, 1000) == 0)
604 ERR("Wait timeout... buffer size too low?\n");
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
);
614 int ret
= snd_pcm_writei(self
->PcmHandle
, WritePtr
, avail
);
619 #if ESTRPIPE != EPIPE
624 ret
= snd_pcm_recover(self
->PcmHandle
, ret
, 1);
631 WritePtr
+= snd_pcm_frames_to_bytes(self
->PcmHandle
, ret
);
638 ret
= snd_pcm_prepare(self
->PcmHandle
);
643 ALCplaybackAlsa_unlock(self
);
650 ALCenum
ALCplaybackAlsa_open(ALCplaybackAlsa
*self
, const ALCchar
*name
)
652 const char *driver
{};
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();
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
)};
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
;
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
)
697 format
= SND_PCM_FORMAT_S8
;
700 format
= SND_PCM_FORMAT_U8
;
703 format
= SND_PCM_FORMAT_S16
;
706 format
= SND_PCM_FORMAT_U16
;
709 format
= SND_PCM_FORMAT_S32
;
712 format
= SND_PCM_FORMAT_U32
;
715 format
= SND_PCM_FORMAT_FLOAT
;
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
;
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)
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
;
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
)
759 if(snd_pcm_hw_params_test_format(self
->PcmHandle
, hp
, format
) >= 0)
761 device
->FmtType
= fmt
.fmttype
;
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
[] = {
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;
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
));
813 WARN("Inexact period count: %u (%d)\n", periods
, dir
);
814 snd_pcm_hw_params_free(hp
);
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
));
823 snd_pcm_sw_params_free(sp
);
826 device
->NumUpdates
= periods
;
827 device
->UpdateSize
= periodSizeInFrames
;
828 device
->Frequency
= rate
;
830 SetDefaultChannelOrder(device
);
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
);
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
;
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
));
856 snd_pcm_hw_params_free(hp
);
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
;
866 err
= snd_pcm_prepare(self
->PcmHandle
);
869 ERR("snd_pcm_prepare(data->PcmHandle) failed: %s\n", snd_strerror(err
));
872 thread_func
= ALCplaybackAlsa_mixerProc
;
876 self
->mKillNow
.store(AL_FALSE
, std::memory_order_release
);
877 self
->mThread
= std::thread(thread_func
, self
);
880 catch(std::exception
& e
) {
881 ERR("Could not create playback thread: %s\n", e
.what());
885 self
->Buffer
.clear();
889 ERR("%s failed: %s\n", funcerr
, snd_strerror(err
));
890 if(hp
) snd_pcm_hw_params_free(hp
);
894 void ALCplaybackAlsa_stop(ALCplaybackAlsa
*self
)
896 if(self
->mKillNow
.exchange(AL_TRUE
, std::memory_order_acq_rel
) || !self
->mThread
.joinable())
899 self
->mThread
.join();
901 self
->Buffer
.clear();
904 ClockLatency
ALCplaybackAlsa_getClockLatency(ALCplaybackAlsa
*self
)
906 ALCdevice
*device
{STATIC_CAST(ALCbackend
, self
)->mDevice
};
909 ALCplaybackAlsa_lock(self
);
910 ret
.ClockTime
= GetDeviceClockTime(device
);
911 snd_pcm_sframes_t delay
{};
912 int err
{snd_pcm_delay(self
->PcmHandle
, &delay
)};
915 ERR("Failed to get pcm delay: %s\n", snd_strerror(err
));
918 ret
.Latency
= std::chrono::seconds
{std::max
<snd_pcm_sframes_t
>(0, delay
)};
919 ret
.Latency
/= device
->Frequency
;
920 ALCplaybackAlsa_unlock(self
);
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
)
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
{};
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();
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
)};
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
)
1012 format
= SND_PCM_FORMAT_S8
;
1015 format
= SND_PCM_FORMAT_U8
;
1018 format
= SND_PCM_FORMAT_S16
;
1021 format
= SND_PCM_FORMAT_U16
;
1024 format
= SND_PCM_FORMAT_S32
;
1027 format
= SND_PCM_FORMAT_U32
;
1030 format
= SND_PCM_FORMAT_FLOAT
;
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");
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));
1066 snd_pcm_hw_params_free(hp
);
1071 self
->Ring
= ll_ringbuffer_create(
1072 device
->UpdateSize
*device
->NumUpdates
,
1073 FrameSizeFromDevFmt(device
->FmtChans
, device
->FmtType
, device
->mAmbiOrder
),
1078 ERR("ring buffer create failed\n");
1083 device
->DeviceName
= name
;
1085 return ALC_NO_ERROR
;
1088 ERR("%s failed: %s\n", funcerr
, snd_strerror(err
));
1089 if(hp
) snd_pcm_hw_params_free(hp
);
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
)};
1104 ERR("prepare failed: %s\n", snd_strerror(err
));
1107 err
= snd_pcm_start(self
->PcmHandle
);
1109 ERR("start failed: %s\n", snd_strerror(err
));
1113 aluHandleDisconnect(STATIC_CAST(ALCbackend
, self
)->mDevice
, "Capture state failure: %s",
1118 self
->DoCapture
= 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
)};
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
;
1148 ll_ringbuffer_read(self
->Ring
, static_cast<char*>(buffer
), samples
);
1149 return ALC_NO_ERROR
;
1152 self
->mLastAvail
-= samples
;
1153 while(device
->Connected
.load(std::memory_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
);
1173 ERR("read error: %s\n", snd_strerror(amt
));
1177 if((amt
=snd_pcm_recover(self
->PcmHandle
, amt
, 1)) >= 0)
1179 amt
= snd_pcm_start(self
->PcmHandle
);
1181 amt
= snd_pcm_avail_update(self
->PcmHandle
);
1185 ERR("restore error: %s\n", snd_strerror(amt
));
1186 aluHandleDisconnect(device
, "Capture recovery failure: %s", snd_strerror(amt
));
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
)
1196 buffer
= (ALbyte
*)buffer
+ amt
;
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(device
->Connected
.load(std::memory_order_acquire
) && self
->DoCapture
)
1212 avail
= snd_pcm_avail_update(self
->PcmHandle
);
1215 ERR("avail update failed: %s\n", snd_strerror(avail
));
1217 if((avail
=snd_pcm_recover(self
->PcmHandle
, avail
, 1)) >= 0)
1220 avail
= snd_pcm_start(self
->PcmHandle
);
1222 avail
= snd_pcm_avail_update(self
->PcmHandle
);
1226 ERR("restore error: %s\n", snd_strerror(avail
));
1227 aluHandleDisconnect(device
, "Capture recovery failure: %s", snd_strerror(avail
));
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
;
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
);
1248 ERR("read error: %s\n", snd_strerror(amt
));
1252 if((amt
=snd_pcm_recover(self
->PcmHandle
, amt
, 1)) >= 0)
1255 amt
= snd_pcm_start(self
->PcmHandle
);
1257 amt
= snd_pcm_avail_update(self
->PcmHandle
);
1261 ERR("restore error: %s\n", snd_strerror(amt
));
1262 aluHandleDisconnect(device
, "Capture recovery failure: %s", snd_strerror(amt
));
1269 ll_ringbuffer_write_advance(self
->Ring
, amt
);
1273 return ll_ringbuffer_read_space(self
->Ring
);
1276 ClockLatency
ALCcaptureAlsa_getClockLatency(ALCcaptureAlsa
*self
)
1278 ALCdevice
*device
{STATIC_CAST(ALCbackend
, self
)->mDevice
};
1281 ALCcaptureAlsa_lock(self
);
1282 ret
.ClockTime
= GetDeviceClockTime(device
);
1283 snd_pcm_sframes_t delay
{};
1284 int err
{snd_pcm_delay(self
->PcmHandle
, &delay
)};
1287 ERR("Failed to get pcm delay: %s\n", snd_strerror(err
));
1290 ret
.Latency
= std::chrono::seconds
{std::max
<snd_pcm_sframes_t
>(0, delay
)};
1291 ret
.Latency
/= device
->Frequency
;
1292 ALCcaptureAlsa_unlock(self
);
1300 bool AlsaBackendFactory::init()
1301 { return !!alsa_load(); }
1303 void AlsaBackendFactory::deinit()
1305 PlaybackDevices
.clear();
1306 CaptureDevices
.clear();
1310 CloseLib(alsa_handle
);
1311 alsa_handle
= nullptr;
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);
1329 case ALL_DEVICE_PROBE
:
1330 PlaybackDevices
= probe_devices(SND_PCM_STREAM_PLAYBACK
);
1331 std::for_each(PlaybackDevices
.cbegin(), PlaybackDevices
.cend(), add_device
);
1334 case CAPTURE_DEVICE_PROBE
:
1335 CaptureDevices
= probe_devices(SND_PCM_STREAM_CAPTURE
);
1336 std::for_each(CaptureDevices
.cbegin(), CaptureDevices
.cend(), add_device
);
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
);
1361 BackendFactory
&AlsaBackendFactory::getFactory()
1363 static AlsaBackendFactory factory
{};