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., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
18 * Or go to http://www.gnu.org/copyleft/lgpl.html
32 #include "backends/base.h"
34 #include <alsa/asoundlib.h>
37 static const ALCchar alsaDevice
[] = "ALSA Default";
41 #define ALSA_FUNCS(MAGIC) \
42 MAGIC(snd_strerror); \
43 MAGIC(snd_pcm_open); \
44 MAGIC(snd_pcm_close); \
45 MAGIC(snd_pcm_nonblock); \
46 MAGIC(snd_pcm_frames_to_bytes); \
47 MAGIC(snd_pcm_bytes_to_frames); \
48 MAGIC(snd_pcm_hw_params_malloc); \
49 MAGIC(snd_pcm_hw_params_free); \
50 MAGIC(snd_pcm_hw_params_any); \
51 MAGIC(snd_pcm_hw_params_current); \
52 MAGIC(snd_pcm_hw_params_set_access); \
53 MAGIC(snd_pcm_hw_params_set_format); \
54 MAGIC(snd_pcm_hw_params_set_channels); \
55 MAGIC(snd_pcm_hw_params_set_periods_near); \
56 MAGIC(snd_pcm_hw_params_set_rate_near); \
57 MAGIC(snd_pcm_hw_params_set_rate); \
58 MAGIC(snd_pcm_hw_params_set_rate_resample); \
59 MAGIC(snd_pcm_hw_params_set_buffer_time_near); \
60 MAGIC(snd_pcm_hw_params_set_period_time_near); \
61 MAGIC(snd_pcm_hw_params_set_buffer_size_near); \
62 MAGIC(snd_pcm_hw_params_set_period_size_near); \
63 MAGIC(snd_pcm_hw_params_set_buffer_size_min); \
64 MAGIC(snd_pcm_hw_params_get_buffer_time_min); \
65 MAGIC(snd_pcm_hw_params_get_buffer_time_max); \
66 MAGIC(snd_pcm_hw_params_get_period_time_min); \
67 MAGIC(snd_pcm_hw_params_get_period_time_max); \
68 MAGIC(snd_pcm_hw_params_get_buffer_size); \
69 MAGIC(snd_pcm_hw_params_get_period_size); \
70 MAGIC(snd_pcm_hw_params_get_access); \
71 MAGIC(snd_pcm_hw_params_get_periods); \
72 MAGIC(snd_pcm_hw_params_test_format); \
73 MAGIC(snd_pcm_hw_params_test_channels); \
74 MAGIC(snd_pcm_hw_params); \
75 MAGIC(snd_pcm_sw_params_malloc); \
76 MAGIC(snd_pcm_sw_params_current); \
77 MAGIC(snd_pcm_sw_params_set_avail_min); \
78 MAGIC(snd_pcm_sw_params_set_stop_threshold); \
79 MAGIC(snd_pcm_sw_params); \
80 MAGIC(snd_pcm_sw_params_free); \
81 MAGIC(snd_pcm_prepare); \
82 MAGIC(snd_pcm_start); \
83 MAGIC(snd_pcm_resume); \
84 MAGIC(snd_pcm_reset); \
85 MAGIC(snd_pcm_wait); \
86 MAGIC(snd_pcm_delay); \
87 MAGIC(snd_pcm_state); \
88 MAGIC(snd_pcm_avail_update); \
89 MAGIC(snd_pcm_areas_silence); \
90 MAGIC(snd_pcm_mmap_begin); \
91 MAGIC(snd_pcm_mmap_commit); \
92 MAGIC(snd_pcm_readi); \
93 MAGIC(snd_pcm_writei); \
94 MAGIC(snd_pcm_drain); \
95 MAGIC(snd_pcm_drop); \
96 MAGIC(snd_pcm_recover); \
97 MAGIC(snd_pcm_info_malloc); \
98 MAGIC(snd_pcm_info_free); \
99 MAGIC(snd_pcm_info_set_device); \
100 MAGIC(snd_pcm_info_set_subdevice); \
101 MAGIC(snd_pcm_info_set_stream); \
102 MAGIC(snd_pcm_info_get_name); \
103 MAGIC(snd_ctl_pcm_next_device); \
104 MAGIC(snd_ctl_pcm_info); \
105 MAGIC(snd_ctl_open); \
106 MAGIC(snd_ctl_close); \
107 MAGIC(snd_ctl_card_info_malloc); \
108 MAGIC(snd_ctl_card_info_free); \
109 MAGIC(snd_ctl_card_info); \
110 MAGIC(snd_ctl_card_info_get_name); \
111 MAGIC(snd_ctl_card_info_get_id); \
112 MAGIC(snd_card_next); \
113 MAGIC(snd_config_update_free_global)
115 static void *alsa_handle
;
116 #define MAKE_FUNC(f) static __typeof(f) * p##f
117 ALSA_FUNCS(MAKE_FUNC
);
120 #define snd_strerror psnd_strerror
121 #define snd_pcm_open psnd_pcm_open
122 #define snd_pcm_close psnd_pcm_close
123 #define snd_pcm_nonblock psnd_pcm_nonblock
124 #define snd_pcm_frames_to_bytes psnd_pcm_frames_to_bytes
125 #define snd_pcm_bytes_to_frames psnd_pcm_bytes_to_frames
126 #define snd_pcm_hw_params_malloc psnd_pcm_hw_params_malloc
127 #define snd_pcm_hw_params_free psnd_pcm_hw_params_free
128 #define snd_pcm_hw_params_any psnd_pcm_hw_params_any
129 #define snd_pcm_hw_params_current psnd_pcm_hw_params_current
130 #define snd_pcm_hw_params_set_access psnd_pcm_hw_params_set_access
131 #define snd_pcm_hw_params_set_format psnd_pcm_hw_params_set_format
132 #define snd_pcm_hw_params_set_channels psnd_pcm_hw_params_set_channels
133 #define snd_pcm_hw_params_set_periods_near psnd_pcm_hw_params_set_periods_near
134 #define snd_pcm_hw_params_set_rate_near psnd_pcm_hw_params_set_rate_near
135 #define snd_pcm_hw_params_set_rate psnd_pcm_hw_params_set_rate
136 #define snd_pcm_hw_params_set_rate_resample psnd_pcm_hw_params_set_rate_resample
137 #define snd_pcm_hw_params_set_buffer_time_near psnd_pcm_hw_params_set_buffer_time_near
138 #define snd_pcm_hw_params_set_period_time_near psnd_pcm_hw_params_set_period_time_near
139 #define snd_pcm_hw_params_set_buffer_size_near psnd_pcm_hw_params_set_buffer_size_near
140 #define snd_pcm_hw_params_set_period_size_near psnd_pcm_hw_params_set_period_size_near
141 #define snd_pcm_hw_params_set_buffer_size_min psnd_pcm_hw_params_set_buffer_size_min
142 #define snd_pcm_hw_params_get_buffer_time_min psnd_pcm_hw_params_get_buffer_time_min
143 #define snd_pcm_hw_params_get_buffer_time_max psnd_pcm_hw_params_get_buffer_time_max
144 #define snd_pcm_hw_params_get_period_time_min psnd_pcm_hw_params_get_period_time_min
145 #define snd_pcm_hw_params_get_period_time_max psnd_pcm_hw_params_get_period_time_max
146 #define snd_pcm_hw_params_get_buffer_size psnd_pcm_hw_params_get_buffer_size
147 #define snd_pcm_hw_params_get_period_size psnd_pcm_hw_params_get_period_size
148 #define snd_pcm_hw_params_get_access psnd_pcm_hw_params_get_access
149 #define snd_pcm_hw_params_get_periods psnd_pcm_hw_params_get_periods
150 #define snd_pcm_hw_params_test_format psnd_pcm_hw_params_test_format
151 #define snd_pcm_hw_params_test_channels psnd_pcm_hw_params_test_channels
152 #define snd_pcm_hw_params psnd_pcm_hw_params
153 #define snd_pcm_sw_params_malloc psnd_pcm_sw_params_malloc
154 #define snd_pcm_sw_params_current psnd_pcm_sw_params_current
155 #define snd_pcm_sw_params_set_avail_min psnd_pcm_sw_params_set_avail_min
156 #define snd_pcm_sw_params_set_stop_threshold psnd_pcm_sw_params_set_stop_threshold
157 #define snd_pcm_sw_params psnd_pcm_sw_params
158 #define snd_pcm_sw_params_free psnd_pcm_sw_params_free
159 #define snd_pcm_prepare psnd_pcm_prepare
160 #define snd_pcm_start psnd_pcm_start
161 #define snd_pcm_resume psnd_pcm_resume
162 #define snd_pcm_reset psnd_pcm_reset
163 #define snd_pcm_wait psnd_pcm_wait
164 #define snd_pcm_delay psnd_pcm_delay
165 #define snd_pcm_state psnd_pcm_state
166 #define snd_pcm_avail_update psnd_pcm_avail_update
167 #define snd_pcm_areas_silence psnd_pcm_areas_silence
168 #define snd_pcm_mmap_begin psnd_pcm_mmap_begin
169 #define snd_pcm_mmap_commit psnd_pcm_mmap_commit
170 #define snd_pcm_readi psnd_pcm_readi
171 #define snd_pcm_writei psnd_pcm_writei
172 #define snd_pcm_drain psnd_pcm_drain
173 #define snd_pcm_drop psnd_pcm_drop
174 #define snd_pcm_recover psnd_pcm_recover
175 #define snd_pcm_info_malloc psnd_pcm_info_malloc
176 #define snd_pcm_info_free psnd_pcm_info_free
177 #define snd_pcm_info_set_device psnd_pcm_info_set_device
178 #define snd_pcm_info_set_subdevice psnd_pcm_info_set_subdevice
179 #define snd_pcm_info_set_stream psnd_pcm_info_set_stream
180 #define snd_pcm_info_get_name psnd_pcm_info_get_name
181 #define snd_ctl_pcm_next_device psnd_ctl_pcm_next_device
182 #define snd_ctl_pcm_info psnd_ctl_pcm_info
183 #define snd_ctl_open psnd_ctl_open
184 #define snd_ctl_close psnd_ctl_close
185 #define snd_ctl_card_info_malloc psnd_ctl_card_info_malloc
186 #define snd_ctl_card_info_free psnd_ctl_card_info_free
187 #define snd_ctl_card_info psnd_ctl_card_info
188 #define snd_ctl_card_info_get_name psnd_ctl_card_info_get_name
189 #define snd_ctl_card_info_get_id psnd_ctl_card_info_get_id
190 #define snd_card_next psnd_card_next
191 #define snd_config_update_free_global psnd_config_update_free_global
195 static ALCboolean
alsa_load(void)
197 ALCboolean error
= ALC_FALSE
;
202 alsa_handle
= LoadLib("libasound.so.2");
207 #define LOAD_FUNC(f) do { \
208 p##f = GetSymbol(alsa_handle, #f); \
213 ALSA_FUNCS(LOAD_FUNC
);
218 CloseLib(alsa_handle
);
231 al_string device_name
;
235 static vector_DevMap PlaybackDevices
;
236 static vector_DevMap CaptureDevices
;
238 static void clear_devlist(vector_DevMap
*devlist
)
242 iter
= VECTOR_ITER_BEGIN(*devlist
);
243 end
= VECTOR_ITER_END(*devlist
);
244 for(;iter
!= end
;iter
++)
246 AL_STRING_DEINIT(iter
->name
);
247 AL_STRING_DEINIT(iter
->device_name
);
249 VECTOR_RESIZE(*devlist
, 0);
253 static const char *prefix_name(snd_pcm_stream_t stream
)
255 assert(stream
== SND_PCM_STREAM_PLAYBACK
|| stream
== SND_PCM_STREAM_CAPTURE
);
256 return (stream
==SND_PCM_STREAM_PLAYBACK
) ? "device-prefix" : "capture-prefix";
259 static void probe_devices(snd_pcm_stream_t stream
, vector_DevMap
*DeviceList
)
261 const char *main_prefix
= "plughw:";
263 snd_ctl_card_info_t
*info
;
264 snd_pcm_info_t
*pcminfo
;
268 clear_devlist(DeviceList
);
270 snd_ctl_card_info_malloc(&info
);
271 snd_pcm_info_malloc(&pcminfo
);
273 AL_STRING_INIT(entry
.name
);
274 AL_STRING_INIT(entry
.device_name
);
275 al_string_copy_cstr(&entry
.name
, alsaDevice
);
276 al_string_copy_cstr(&entry
.device_name
, GetConfigValue("alsa", (stream
==SND_PCM_STREAM_PLAYBACK
) ?
277 "device" : "capture", "default"));
278 VECTOR_PUSH_BACK(*DeviceList
, entry
);
281 if((err
=snd_card_next(&card
)) < 0)
282 ERR("Failed to find a card: %s\n", snd_strerror(err
));
283 ConfigValueStr("alsa", prefix_name(stream
), &main_prefix
);
286 const char *card_prefix
= main_prefix
;
287 const char *cardname
, *cardid
;
290 snprintf(name
, sizeof(name
), "hw:%d", card
);
291 if((err
= snd_ctl_open(&handle
, name
, 0)) < 0)
293 ERR("control open (hw:%d): %s\n", card
, snd_strerror(err
));
296 if((err
= snd_ctl_card_info(handle
, info
)) < 0)
298 ERR("control hardware info (hw:%d): %s\n", card
, snd_strerror(err
));
299 snd_ctl_close(handle
);
303 cardname
= snd_ctl_card_info_get_name(info
);
304 cardid
= snd_ctl_card_info_get_id(info
);
306 snprintf(name
, sizeof(name
), "%s-%s", prefix_name(stream
), cardid
);
307 ConfigValueStr("alsa", name
, &card_prefix
);
312 const char *device_prefix
= card_prefix
;
316 if(snd_ctl_pcm_next_device(handle
, &dev
) < 0)
317 ERR("snd_ctl_pcm_next_device failed\n");
321 snd_pcm_info_set_device(pcminfo
, dev
);
322 snd_pcm_info_set_subdevice(pcminfo
, 0);
323 snd_pcm_info_set_stream(pcminfo
, stream
);
324 if((err
= snd_ctl_pcm_info(handle
, pcminfo
)) < 0) {
326 ERR("control digital audio info (hw:%d): %s\n", card
, snd_strerror(err
));
330 devname
= snd_pcm_info_get_name(pcminfo
);
332 snprintf(name
, sizeof(name
), "%s-%s-%d", prefix_name(stream
), cardid
, dev
);
333 ConfigValueStr("alsa", name
, &device_prefix
);
335 snprintf(name
, sizeof(name
), "%s, %s (CARD=%s,DEV=%d)",
336 cardname
, devname
, cardid
, dev
);
337 snprintf(device
, sizeof(device
), "%sCARD=%s,DEV=%d",
338 device_prefix
, cardid
, dev
);
340 TRACE("Got device \"%s\", \"%s\"\n", name
, device
);
341 AL_STRING_INIT(entry
.name
);
342 AL_STRING_INIT(entry
.device_name
);
343 al_string_copy_cstr(&entry
.name
, name
);
344 al_string_copy_cstr(&entry
.device_name
, device
);
345 VECTOR_PUSH_BACK(*DeviceList
, entry
);
347 snd_ctl_close(handle
);
349 if(snd_card_next(&card
) < 0) {
350 ERR("snd_card_next failed\n");
355 snd_pcm_info_free(pcminfo
);
356 snd_ctl_card_info_free(info
);
360 static int verify_state(snd_pcm_t
*handle
)
362 snd_pcm_state_t state
= snd_pcm_state(handle
);
367 case SND_PCM_STATE_OPEN
:
368 case SND_PCM_STATE_SETUP
:
369 case SND_PCM_STATE_PREPARED
:
370 case SND_PCM_STATE_RUNNING
:
371 case SND_PCM_STATE_DRAINING
:
372 case SND_PCM_STATE_PAUSED
:
376 case SND_PCM_STATE_XRUN
:
377 if((err
=snd_pcm_recover(handle
, -EPIPE
, 1)) < 0)
380 case SND_PCM_STATE_SUSPENDED
:
381 if((err
=snd_pcm_recover(handle
, -ESTRPIPE
, 1)) < 0)
384 case SND_PCM_STATE_DISCONNECTED
:
392 typedef struct ALCplaybackAlsa
{
393 DERIVE_FROM_TYPE(ALCbackend
);
395 snd_pcm_t
*pcmHandle
;
400 volatile int killNow
;
404 static int ALCplaybackAlsa_mixerProc(void *ptr
);
405 static int ALCplaybackAlsa_mixerNoMMapProc(void *ptr
);
407 static void ALCplaybackAlsa_Construct(ALCplaybackAlsa
*self
, ALCdevice
*device
);
408 static DECLARE_FORWARD(ALCplaybackAlsa
, ALCbackend
, void, Destruct
)
409 static ALCenum
ALCplaybackAlsa_open(ALCplaybackAlsa
*self
, const ALCchar
*name
);
410 static void ALCplaybackAlsa_close(ALCplaybackAlsa
*self
);
411 static ALCboolean
ALCplaybackAlsa_reset(ALCplaybackAlsa
*self
);
412 static ALCboolean
ALCplaybackAlsa_start(ALCplaybackAlsa
*self
);
413 static void ALCplaybackAlsa_stop(ALCplaybackAlsa
*self
);
414 static DECLARE_FORWARD2(ALCplaybackAlsa
, ALCbackend
, ALCenum
, captureSamples
, void*, ALCuint
)
415 static DECLARE_FORWARD(ALCplaybackAlsa
, ALCbackend
, ALCuint
, availableSamples
)
416 static ALint64
ALCplaybackAlsa_getLatency(ALCplaybackAlsa
*self
);
417 static DECLARE_FORWARD(ALCplaybackAlsa
, ALCbackend
, void, lock
)
418 static DECLARE_FORWARD(ALCplaybackAlsa
, ALCbackend
, void, unlock
)
419 DECLARE_DEFAULT_ALLOCATORS(ALCplaybackAlsa
)
421 DEFINE_ALCBACKEND_VTABLE(ALCplaybackAlsa
);
424 static void ALCplaybackAlsa_Construct(ALCplaybackAlsa
*self
, ALCdevice
*device
)
426 ALCbackend_Construct(STATIC_CAST(ALCbackend
, self
), device
);
427 SET_VTABLE2(ALCplaybackAlsa
, ALCbackend
, self
);
431 static int ALCplaybackAlsa_mixerProc(void *ptr
)
433 ALCplaybackAlsa
*self
= (ALCplaybackAlsa
*)ptr
;
434 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
435 const snd_pcm_channel_area_t
*areas
= NULL
;
436 snd_pcm_uframes_t update_size
, num_updates
;
437 snd_pcm_sframes_t avail
, commitres
;
438 snd_pcm_uframes_t offset
, frames
;
443 althrd_setname(althrd_current(), MIXER_THREAD_NAME
);
445 update_size
= device
->UpdateSize
;
446 num_updates
= device
->NumUpdates
;
447 while(!self
->killNow
)
449 int state
= verify_state(self
->pcmHandle
);
452 ERR("Invalid state detected: %s\n", snd_strerror(state
));
453 ALCplaybackAlsa_lock(self
);
454 aluHandleDisconnect(device
);
455 ALCplaybackAlsa_unlock(self
);
459 avail
= snd_pcm_avail_update(self
->pcmHandle
);
462 ERR("available update failed: %s\n", snd_strerror(avail
));
466 if((snd_pcm_uframes_t
)avail
> update_size
*(num_updates
+1))
468 WARN("available samples exceeds the buffer size\n");
469 snd_pcm_reset(self
->pcmHandle
);
473 // make sure there's frames to process
474 if((snd_pcm_uframes_t
)avail
< update_size
)
476 if(state
!= SND_PCM_STATE_RUNNING
)
478 err
= snd_pcm_start(self
->pcmHandle
);
481 ERR("start failed: %s\n", snd_strerror(err
));
485 if(snd_pcm_wait(self
->pcmHandle
, 1000) == 0)
486 ERR("Wait timeout... buffer size too low?\n");
489 avail
-= avail
%update_size
;
491 // it is possible that contiguous areas are smaller, thus we use a loop
492 ALCplaybackAlsa_lock(self
);
497 err
= snd_pcm_mmap_begin(self
->pcmHandle
, &areas
, &offset
, &frames
);
500 ERR("mmap begin error: %s\n", snd_strerror(err
));
504 WritePtr
= (char*)areas
->addr
+ (offset
* areas
->step
/ 8);
505 aluMixData(device
, WritePtr
, frames
);
507 commitres
= snd_pcm_mmap_commit(self
->pcmHandle
, offset
, frames
);
508 if(commitres
< 0 || (commitres
-frames
) != 0)
510 ERR("mmap commit error: %s\n",
511 snd_strerror(commitres
>= 0 ? -EPIPE
: commitres
));
517 ALCplaybackAlsa_unlock(self
);
523 static int ALCplaybackAlsa_mixerNoMMapProc(void *ptr
)
525 ALCplaybackAlsa
*self
= (ALCplaybackAlsa
*)ptr
;
526 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
527 snd_pcm_uframes_t update_size
, num_updates
;
528 snd_pcm_sframes_t avail
;
533 althrd_setname(althrd_current(), MIXER_THREAD_NAME
);
535 update_size
= device
->UpdateSize
;
536 num_updates
= device
->NumUpdates
;
537 while(!self
->killNow
)
539 int state
= verify_state(self
->pcmHandle
);
542 ERR("Invalid state detected: %s\n", snd_strerror(state
));
543 ALCplaybackAlsa_lock(self
);
544 aluHandleDisconnect(device
);
545 ALCplaybackAlsa_unlock(self
);
549 avail
= snd_pcm_avail_update(self
->pcmHandle
);
552 ERR("available update failed: %s\n", snd_strerror(avail
));
556 if((snd_pcm_uframes_t
)avail
> update_size
*num_updates
)
558 WARN("available samples exceeds the buffer size\n");
559 snd_pcm_reset(self
->pcmHandle
);
563 if((snd_pcm_uframes_t
)avail
< update_size
)
565 if(state
!= SND_PCM_STATE_RUNNING
)
567 err
= snd_pcm_start(self
->pcmHandle
);
570 ERR("start failed: %s\n", snd_strerror(err
));
574 if(snd_pcm_wait(self
->pcmHandle
, 1000) == 0)
575 ERR("Wait timeout... buffer size too low?\n");
579 ALCplaybackAlsa_lock(self
);
580 WritePtr
= self
->buffer
;
581 avail
= snd_pcm_bytes_to_frames(self
->pcmHandle
, self
->size
);
582 aluMixData(device
, WritePtr
, avail
);
586 int ret
= snd_pcm_writei(self
->pcmHandle
, WritePtr
, avail
);
594 ret
= snd_pcm_recover(self
->pcmHandle
, ret
, 1);
601 WritePtr
+= snd_pcm_frames_to_bytes(self
->pcmHandle
, ret
);
608 ret
= snd_pcm_prepare(self
->pcmHandle
);
613 ALCplaybackAlsa_unlock(self
);
620 static ALCenum
ALCplaybackAlsa_open(ALCplaybackAlsa
*self
, const ALCchar
*name
)
622 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
623 const char *driver
= NULL
;
628 const DevMap
*iter
, *end
;
630 if(VECTOR_SIZE(PlaybackDevices
) == 0)
631 probe_devices(SND_PCM_STREAM_PLAYBACK
, &PlaybackDevices
);
633 iter
= VECTOR_ITER_BEGIN(PlaybackDevices
);
634 end
= VECTOR_ITER_END(PlaybackDevices
);
635 for(;iter
!= end
;iter
++)
637 if(al_string_cmp_cstr(iter
->name
, name
) == 0)
639 driver
= al_string_get_cstr(iter
->device_name
);
644 return ALC_INVALID_VALUE
;
649 driver
= GetConfigValue("alsa", "device", "default");
652 TRACE("Opening device \"%s\"\n", driver
);
653 err
= snd_pcm_open(&self
->pcmHandle
, driver
, SND_PCM_STREAM_PLAYBACK
, SND_PCM_NONBLOCK
);
656 ERR("Could not open playback device '%s': %s\n", driver
, snd_strerror(err
));
657 return ALC_OUT_OF_MEMORY
;
660 /* Free alsa's global config tree. Otherwise valgrind reports a ton of leaks. */
661 snd_config_update_free_global();
663 al_string_copy_cstr(&device
->DeviceName
, name
);
668 static void ALCplaybackAlsa_close(ALCplaybackAlsa
*self
)
670 snd_pcm_close(self
->pcmHandle
);
673 static ALCboolean
ALCplaybackAlsa_reset(ALCplaybackAlsa
*self
)
675 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
676 snd_pcm_uframes_t periodSizeInFrames
;
677 unsigned int periodLen
, bufferLen
;
678 snd_pcm_sw_params_t
*sp
= NULL
;
679 snd_pcm_hw_params_t
*hp
= NULL
;
680 snd_pcm_format_t format
= -1;
681 snd_pcm_access_t access
;
682 unsigned int periods
;
688 switch(device
->FmtType
)
691 format
= SND_PCM_FORMAT_S8
;
694 format
= SND_PCM_FORMAT_U8
;
697 format
= SND_PCM_FORMAT_S16
;
700 format
= SND_PCM_FORMAT_U16
;
703 format
= SND_PCM_FORMAT_S32
;
706 format
= SND_PCM_FORMAT_U32
;
709 format
= SND_PCM_FORMAT_FLOAT
;
713 allowmmap
= GetConfigValueBool("alsa", "mmap", 1);
714 periods
= device
->NumUpdates
;
715 periodLen
= (ALuint64
)device
->UpdateSize
* 1000000 / device
->Frequency
;
716 bufferLen
= periodLen
* periods
;
717 rate
= device
->Frequency
;
719 snd_pcm_hw_params_malloc(&hp
);
720 #define CHECK(x) if((funcerr=#x),(err=(x)) < 0) goto error
721 CHECK(snd_pcm_hw_params_any(self
->pcmHandle
, hp
));
722 /* set interleaved access */
723 if(!allowmmap
|| snd_pcm_hw_params_set_access(self
->pcmHandle
, hp
, SND_PCM_ACCESS_MMAP_INTERLEAVED
) < 0)
726 CHECK(snd_pcm_hw_params_set_access(self
->pcmHandle
, hp
, SND_PCM_ACCESS_RW_INTERLEAVED
));
728 /* test and set format (implicitly sets sample bits) */
729 if(snd_pcm_hw_params_test_format(self
->pcmHandle
, hp
, format
) < 0)
731 static const struct {
732 snd_pcm_format_t format
;
733 enum DevFmtType fmttype
;
735 { SND_PCM_FORMAT_FLOAT
, DevFmtFloat
},
736 { SND_PCM_FORMAT_S32
, DevFmtInt
},
737 { SND_PCM_FORMAT_U32
, DevFmtUInt
},
738 { SND_PCM_FORMAT_S16
, DevFmtShort
},
739 { SND_PCM_FORMAT_U16
, DevFmtUShort
},
740 { SND_PCM_FORMAT_S8
, DevFmtByte
},
741 { SND_PCM_FORMAT_U8
, DevFmtUByte
},
745 for(k
= 0;k
< COUNTOF(formatlist
);k
++)
747 format
= formatlist
[k
].format
;
748 if(snd_pcm_hw_params_test_format(self
->pcmHandle
, hp
, format
) >= 0)
750 device
->FmtType
= formatlist
[k
].fmttype
;
755 CHECK(snd_pcm_hw_params_set_format(self
->pcmHandle
, hp
, format
));
756 /* test and set channels (implicitly sets frame bits) */
757 if(snd_pcm_hw_params_test_channels(self
->pcmHandle
, hp
, ChannelsFromDevFmt(device
->FmtChans
)) < 0)
759 static const enum DevFmtChannels channellist
[] = {
768 for(k
= 0;k
< COUNTOF(channellist
);k
++)
770 if(snd_pcm_hw_params_test_channels(self
->pcmHandle
, hp
, ChannelsFromDevFmt(channellist
[k
])) >= 0)
772 device
->FmtChans
= channellist
[k
];
777 CHECK(snd_pcm_hw_params_set_channels(self
->pcmHandle
, hp
, ChannelsFromDevFmt(device
->FmtChans
)));
778 /* set rate (implicitly constrains period/buffer parameters) */
779 if(snd_pcm_hw_params_set_rate_resample(self
->pcmHandle
, hp
, 0) < 0)
780 ERR("Failed to disable ALSA resampler\n");
781 CHECK(snd_pcm_hw_params_set_rate_near(self
->pcmHandle
, hp
, &rate
, NULL
));
782 /* set buffer time (implicitly constrains period/buffer parameters) */
783 if((err
=snd_pcm_hw_params_set_buffer_time_near(self
->pcmHandle
, hp
, &bufferLen
, NULL
)) < 0)
784 ERR("snd_pcm_hw_params_set_buffer_time_near failed: %s\n", snd_strerror(err
));
785 /* set period time (implicitly sets buffer size/bytes/time and period size/bytes) */
786 if((err
=snd_pcm_hw_params_set_period_time_near(self
->pcmHandle
, hp
, &periodLen
, NULL
)) < 0)
787 ERR("snd_pcm_hw_params_set_period_time_near failed: %s\n", snd_strerror(err
));
788 /* install and prepare hardware configuration */
789 CHECK(snd_pcm_hw_params(self
->pcmHandle
, hp
));
790 /* retrieve configuration info */
791 CHECK(snd_pcm_hw_params_get_access(hp
, &access
));
792 CHECK(snd_pcm_hw_params_get_period_size(hp
, &periodSizeInFrames
, NULL
));
793 CHECK(snd_pcm_hw_params_get_periods(hp
, &periods
, NULL
));
795 snd_pcm_hw_params_free(hp
);
797 snd_pcm_sw_params_malloc(&sp
);
799 CHECK(snd_pcm_sw_params_current(self
->pcmHandle
, sp
));
800 CHECK(snd_pcm_sw_params_set_avail_min(self
->pcmHandle
, sp
, periodSizeInFrames
));
801 CHECK(snd_pcm_sw_params_set_stop_threshold(self
->pcmHandle
, sp
, periodSizeInFrames
*periods
));
802 CHECK(snd_pcm_sw_params(self
->pcmHandle
, sp
));
804 snd_pcm_sw_params_free(sp
);
807 device
->NumUpdates
= periods
;
808 device
->UpdateSize
= periodSizeInFrames
;
809 device
->Frequency
= rate
;
811 SetDefaultChannelOrder(device
);
816 ERR("%s failed: %s\n", funcerr
, snd_strerror(err
));
817 if(hp
) snd_pcm_hw_params_free(hp
);
818 if(sp
) snd_pcm_sw_params_free(sp
);
822 static ALCboolean
ALCplaybackAlsa_start(ALCplaybackAlsa
*self
)
824 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
825 int (*thread_func
)(void*) = NULL
;
826 snd_pcm_hw_params_t
*hp
= NULL
;
827 snd_pcm_access_t access
;
831 snd_pcm_hw_params_malloc(&hp
);
832 #define CHECK(x) if((funcerr=#x),(err=(x)) < 0) goto error
833 CHECK(snd_pcm_hw_params_current(self
->pcmHandle
, hp
));
834 /* retrieve configuration info */
835 CHECK(snd_pcm_hw_params_get_access(hp
, &access
));
837 snd_pcm_hw_params_free(hp
);
840 self
->size
= snd_pcm_frames_to_bytes(self
->pcmHandle
, device
->UpdateSize
);
841 if(access
== SND_PCM_ACCESS_RW_INTERLEAVED
)
843 self
->buffer
= malloc(self
->size
);
846 ERR("buffer malloc failed\n");
849 thread_func
= ALCplaybackAlsa_mixerNoMMapProc
;
853 err
= snd_pcm_prepare(self
->pcmHandle
);
856 ERR("snd_pcm_prepare(data->pcmHandle) failed: %s\n", snd_strerror(err
));
859 thread_func
= ALCplaybackAlsa_mixerProc
;
862 if(althrd_create(&self
->thread
, thread_func
, self
) != althrd_success
)
864 ERR("Could not create playback thread\n");
873 ERR("%s failed: %s\n", funcerr
, snd_strerror(err
));
874 if(hp
) snd_pcm_hw_params_free(hp
);
878 static void ALCplaybackAlsa_stop(ALCplaybackAlsa
*self
)
886 althrd_join(self
->thread
, &res
);
892 static ALint64
ALCplaybackAlsa_getLatency(ALCplaybackAlsa
*self
)
894 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
895 snd_pcm_sframes_t delay
= 0;
898 if((err
=snd_pcm_delay(self
->pcmHandle
, &delay
)) < 0)
900 ERR("Failed to get pcm delay: %s\n", snd_strerror(err
));
903 return maxi64((ALint64
)delay
*1000000000/device
->Frequency
, 0);
907 typedef struct ALCcaptureAlsa
{
908 DERIVE_FROM_TYPE(ALCbackend
);
910 snd_pcm_t
*pcmHandle
;
918 snd_pcm_sframes_t last_avail
;
921 static void ALCcaptureAlsa_Construct(ALCcaptureAlsa
*self
, ALCdevice
*device
);
922 static DECLARE_FORWARD(ALCcaptureAlsa
, ALCbackend
, void, Destruct
)
923 static ALCenum
ALCcaptureAlsa_open(ALCcaptureAlsa
*self
, const ALCchar
*name
);
924 static void ALCcaptureAlsa_close(ALCcaptureAlsa
*self
);
925 static DECLARE_FORWARD(ALCcaptureAlsa
, ALCbackend
, ALCboolean
, reset
)
926 static ALCboolean
ALCcaptureAlsa_start(ALCcaptureAlsa
*self
);
927 static void ALCcaptureAlsa_stop(ALCcaptureAlsa
*self
);
928 static ALCenum
ALCcaptureAlsa_captureSamples(ALCcaptureAlsa
*self
, ALCvoid
*buffer
, ALCuint samples
);
929 static ALCuint
ALCcaptureAlsa_availableSamples(ALCcaptureAlsa
*self
);
930 static ALint64
ALCcaptureAlsa_getLatency(ALCcaptureAlsa
*self
);
931 static DECLARE_FORWARD(ALCcaptureAlsa
, ALCbackend
, void, lock
)
932 static DECLARE_FORWARD(ALCcaptureAlsa
, ALCbackend
, void, unlock
)
933 DECLARE_DEFAULT_ALLOCATORS(ALCcaptureAlsa
)
935 DEFINE_ALCBACKEND_VTABLE(ALCcaptureAlsa
);
938 static void ALCcaptureAlsa_Construct(ALCcaptureAlsa
*self
, ALCdevice
*device
)
940 ALCbackend_Construct(STATIC_CAST(ALCbackend
, self
), device
);
941 SET_VTABLE2(ALCcaptureAlsa
, ALCbackend
, self
);
945 static ALCenum
ALCcaptureAlsa_open(ALCcaptureAlsa
*self
, const ALCchar
*name
)
947 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
948 const char *driver
= NULL
;
949 snd_pcm_hw_params_t
*hp
;
950 snd_pcm_uframes_t bufferSizeInFrames
;
951 snd_pcm_uframes_t periodSizeInFrames
;
952 ALboolean needring
= AL_FALSE
;
953 snd_pcm_format_t format
= -1;
959 const DevMap
*iter
, *end
;
961 if(VECTOR_SIZE(CaptureDevices
) == 0)
962 probe_devices(SND_PCM_STREAM_CAPTURE
, &CaptureDevices
);
964 iter
= VECTOR_ITER_BEGIN(CaptureDevices
);
965 end
= VECTOR_ITER_END(CaptureDevices
);
966 for(;iter
!= end
;iter
++)
968 if(al_string_cmp_cstr(iter
->name
, name
) == 0)
970 driver
= al_string_get_cstr(iter
->device_name
);
975 return ALC_INVALID_VALUE
;
980 driver
= GetConfigValue("alsa", "capture", "default");
983 TRACE("Opening device \"%s\"\n", driver
);
984 err
= snd_pcm_open(&self
->pcmHandle
, driver
, SND_PCM_STREAM_CAPTURE
, SND_PCM_NONBLOCK
);
987 ERR("Could not open capture device '%s': %s\n", driver
, snd_strerror(err
));
988 return ALC_INVALID_VALUE
;
991 /* Free alsa's global config tree. Otherwise valgrind reports a ton of leaks. */
992 snd_config_update_free_global();
994 switch(device
->FmtType
)
997 format
= SND_PCM_FORMAT_S8
;
1000 format
= SND_PCM_FORMAT_U8
;
1003 format
= SND_PCM_FORMAT_S16
;
1006 format
= SND_PCM_FORMAT_U16
;
1009 format
= SND_PCM_FORMAT_S32
;
1012 format
= SND_PCM_FORMAT_U32
;
1015 format
= SND_PCM_FORMAT_FLOAT
;
1020 bufferSizeInFrames
= maxu(device
->UpdateSize
*device
->NumUpdates
,
1021 100*device
->Frequency
/1000);
1022 periodSizeInFrames
= minu(bufferSizeInFrames
, 25*device
->Frequency
/1000);
1024 snd_pcm_hw_params_malloc(&hp
);
1025 #define CHECK(x) if((funcerr=#x),(err=(x)) < 0) goto error
1026 CHECK(snd_pcm_hw_params_any(self
->pcmHandle
, hp
));
1027 /* set interleaved access */
1028 CHECK(snd_pcm_hw_params_set_access(self
->pcmHandle
, hp
, SND_PCM_ACCESS_RW_INTERLEAVED
));
1029 /* set format (implicitly sets sample bits) */
1030 CHECK(snd_pcm_hw_params_set_format(self
->pcmHandle
, hp
, format
));
1031 /* set channels (implicitly sets frame bits) */
1032 CHECK(snd_pcm_hw_params_set_channels(self
->pcmHandle
, hp
, ChannelsFromDevFmt(device
->FmtChans
)));
1033 /* set rate (implicitly constrains period/buffer parameters) */
1034 CHECK(snd_pcm_hw_params_set_rate(self
->pcmHandle
, hp
, device
->Frequency
, 0));
1035 /* set buffer size in frame units (implicitly sets period size/bytes/time and buffer time/bytes) */
1036 if(snd_pcm_hw_params_set_buffer_size_min(self
->pcmHandle
, hp
, &bufferSizeInFrames
) < 0)
1038 TRACE("Buffer too large, using intermediate ring buffer\n");
1040 CHECK(snd_pcm_hw_params_set_buffer_size_near(self
->pcmHandle
, hp
, &bufferSizeInFrames
));
1042 /* set buffer size in frame units (implicitly sets period size/bytes/time and buffer time/bytes) */
1043 CHECK(snd_pcm_hw_params_set_period_size_near(self
->pcmHandle
, hp
, &periodSizeInFrames
, NULL
));
1044 /* install and prepare hardware configuration */
1045 CHECK(snd_pcm_hw_params(self
->pcmHandle
, hp
));
1046 /* retrieve configuration info */
1047 CHECK(snd_pcm_hw_params_get_period_size(hp
, &periodSizeInFrames
, NULL
));
1049 snd_pcm_hw_params_free(hp
);
1054 self
->ring
= CreateRingBuffer(FrameSizeFromDevFmt(device
->FmtChans
, device
->FmtType
),
1055 device
->UpdateSize
*device
->NumUpdates
);
1058 ERR("ring buffer create failed\n");
1062 self
->size
= snd_pcm_frames_to_bytes(self
->pcmHandle
, periodSizeInFrames
);
1063 self
->buffer
= malloc(self
->size
);
1066 ERR("buffer malloc failed\n");
1071 al_string_copy_cstr(&device
->DeviceName
, name
);
1073 return ALC_NO_ERROR
;
1076 ERR("%s failed: %s\n", funcerr
, snd_strerror(err
));
1077 if(hp
) snd_pcm_hw_params_free(hp
);
1081 self
->buffer
= NULL
;
1082 DestroyRingBuffer(self
->ring
);
1084 snd_pcm_close(self
->pcmHandle
);
1086 return ALC_INVALID_VALUE
;
1089 static void ALCcaptureAlsa_close(ALCcaptureAlsa
*self
)
1091 snd_pcm_close(self
->pcmHandle
);
1092 DestroyRingBuffer(self
->ring
);
1095 self
->buffer
= NULL
;
1098 static ALCboolean
ALCcaptureAlsa_start(ALCcaptureAlsa
*self
)
1100 int err
= snd_pcm_start(self
->pcmHandle
);
1103 ERR("start failed: %s\n", snd_strerror(err
));
1104 aluHandleDisconnect(STATIC_CAST(ALCbackend
, self
)->mDevice
);
1108 self
->doCapture
= AL_TRUE
;
1112 static void ALCcaptureAlsa_stop(ALCcaptureAlsa
*self
)
1117 /* OpenAL requires access to unread audio after stopping, but ALSA's
1118 * snd_pcm_drain is unreliable and snd_pcm_drop drops it. Capture what's
1119 * available now so it'll be available later after the drop. */
1120 avail
= ALCcaptureAlsa_availableSamples(self
);
1121 if(!self
->ring
&& avail
> 0)
1123 /* The ring buffer implicitly captures when checking availability.
1124 * Direct access needs to explicitly capture it into temp storage. */
1128 size
= snd_pcm_frames_to_bytes(self
->pcmHandle
, avail
);
1132 ALCcaptureAlsa_captureSamples(self
, ptr
, avail
);
1138 err
= snd_pcm_drop(self
->pcmHandle
);
1140 ERR("drop failed: %s\n", snd_strerror(err
));
1141 self
->doCapture
= AL_FALSE
;
1144 static ALCenum
ALCcaptureAlsa_captureSamples(ALCcaptureAlsa
*self
, ALCvoid
*buffer
, ALCuint samples
)
1146 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
1150 ReadRingBuffer(self
->ring
, buffer
, samples
);
1151 return ALC_NO_ERROR
;
1154 self
->last_avail
-= samples
;
1155 while(device
->Connected
&& samples
> 0)
1157 snd_pcm_sframes_t amt
= 0;
1161 /* First get any data stored from the last stop */
1162 amt
= snd_pcm_bytes_to_frames(self
->pcmHandle
, self
->size
);
1163 if((snd_pcm_uframes_t
)amt
> samples
) amt
= samples
;
1165 amt
= snd_pcm_frames_to_bytes(self
->pcmHandle
, amt
);
1166 memcpy(buffer
, self
->buffer
, amt
);
1168 if(self
->size
> amt
)
1170 memmove(self
->buffer
, self
->buffer
+amt
, self
->size
- amt
);
1176 self
->buffer
= NULL
;
1179 amt
= snd_pcm_bytes_to_frames(self
->pcmHandle
, amt
);
1181 else if(self
->doCapture
)
1182 amt
= snd_pcm_readi(self
->pcmHandle
, buffer
, samples
);
1185 ERR("read error: %s\n", snd_strerror(amt
));
1189 if((amt
=snd_pcm_recover(self
->pcmHandle
, amt
, 1)) >= 0)
1191 amt
= snd_pcm_start(self
->pcmHandle
);
1193 amt
= snd_pcm_avail_update(self
->pcmHandle
);
1197 ERR("restore error: %s\n", snd_strerror(amt
));
1198 aluHandleDisconnect(device
);
1201 /* If the amount available is less than what's asked, we lost it
1202 * during recovery. So just give silence instead. */
1203 if((snd_pcm_uframes_t
)amt
< samples
)
1208 buffer
= (ALbyte
*)buffer
+ amt
;
1212 memset(buffer
, ((device
->FmtType
== DevFmtUByte
) ? 0x80 : 0),
1213 snd_pcm_frames_to_bytes(self
->pcmHandle
, samples
));
1215 return ALC_NO_ERROR
;
1218 static ALCuint
ALCcaptureAlsa_availableSamples(ALCcaptureAlsa
*self
)
1220 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
1221 snd_pcm_sframes_t avail
= 0;
1223 if(device
->Connected
&& self
->doCapture
)
1224 avail
= snd_pcm_avail_update(self
->pcmHandle
);
1227 ERR("avail update failed: %s\n", snd_strerror(avail
));
1229 if((avail
=snd_pcm_recover(self
->pcmHandle
, avail
, 1)) >= 0)
1232 avail
= snd_pcm_start(self
->pcmHandle
);
1234 avail
= snd_pcm_avail_update(self
->pcmHandle
);
1238 ERR("restore error: %s\n", snd_strerror(avail
));
1239 aluHandleDisconnect(device
);
1245 if(avail
< 0) avail
= 0;
1246 avail
+= snd_pcm_bytes_to_frames(self
->pcmHandle
, self
->size
);
1247 if(avail
> self
->last_avail
) self
->last_avail
= avail
;
1248 return self
->last_avail
;
1253 snd_pcm_sframes_t amt
;
1255 amt
= snd_pcm_bytes_to_frames(self
->pcmHandle
, self
->size
);
1256 if(avail
< amt
) amt
= avail
;
1258 amt
= snd_pcm_readi(self
->pcmHandle
, self
->buffer
, amt
);
1261 ERR("read error: %s\n", snd_strerror(amt
));
1265 if((amt
=snd_pcm_recover(self
->pcmHandle
, amt
, 1)) >= 0)
1268 amt
= snd_pcm_start(self
->pcmHandle
);
1270 amt
= snd_pcm_avail_update(self
->pcmHandle
);
1274 ERR("restore error: %s\n", snd_strerror(amt
));
1275 aluHandleDisconnect(device
);
1282 WriteRingBuffer(self
->ring
, self
->buffer
, amt
);
1286 return RingBufferSize(self
->ring
);
1289 static ALint64
ALCcaptureAlsa_getLatency(ALCcaptureAlsa
*self
)
1291 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
1292 snd_pcm_sframes_t delay
= 0;
1295 if((err
=snd_pcm_delay(self
->pcmHandle
, &delay
)) < 0)
1297 ERR("Failed to get pcm delay: %s\n", snd_strerror(err
));
1300 return maxi64((ALint64
)delay
*1000000000/device
->Frequency
, 0);
1304 static inline void AppendAllDevicesList2(const DevMap
*entry
)
1305 { AppendAllDevicesList(al_string_get_cstr(entry
->name
)); }
1306 static inline void AppendCaptureDeviceList2(const DevMap
*entry
)
1307 { AppendCaptureDeviceList(al_string_get_cstr(entry
->name
)); }
1309 typedef struct ALCalsaBackendFactory
{
1310 DERIVE_FROM_TYPE(ALCbackendFactory
);
1311 } ALCalsaBackendFactory
;
1312 #define ALCALSABACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCalsaBackendFactory, ALCbackendFactory) } }
1314 static ALCboolean
ALCalsaBackendFactory_init(ALCalsaBackendFactory
* UNUSED(self
))
1316 VECTOR_INIT(PlaybackDevices
);
1317 VECTOR_INIT(CaptureDevices
);
1324 static void ALCalsaBackendFactory_deinit(ALCalsaBackendFactory
* UNUSED(self
))
1326 clear_devlist(&PlaybackDevices
);
1327 VECTOR_DEINIT(PlaybackDevices
);
1329 clear_devlist(&CaptureDevices
);
1330 VECTOR_DEINIT(CaptureDevices
);
1334 CloseLib(alsa_handle
);
1339 static ALCboolean
ALCalsaBackendFactory_querySupport(ALCalsaBackendFactory
* UNUSED(self
), ALCbackend_Type type
)
1341 if(type
== ALCbackend_Playback
|| type
== ALCbackend_Capture
)
1346 static void ALCalsaBackendFactory_probe(ALCalsaBackendFactory
* UNUSED(self
), enum DevProbe type
)
1350 case ALL_DEVICE_PROBE
:
1351 probe_devices(SND_PCM_STREAM_PLAYBACK
, &PlaybackDevices
);
1352 VECTOR_FOR_EACH(const DevMap
, PlaybackDevices
, AppendAllDevicesList2
);
1355 case CAPTURE_DEVICE_PROBE
:
1356 probe_devices(SND_PCM_STREAM_CAPTURE
, &CaptureDevices
);
1357 VECTOR_FOR_EACH(const DevMap
, CaptureDevices
, AppendCaptureDeviceList2
);
1362 static ALCbackend
* ALCalsaBackendFactory_createBackend(ALCalsaBackendFactory
* UNUSED(self
), ALCdevice
*device
, ALCbackend_Type type
)
1364 if(type
== ALCbackend_Playback
)
1366 ALCplaybackAlsa
*backend
;
1368 backend
= ALCplaybackAlsa_New(sizeof(*backend
));
1369 if(!backend
) return NULL
;
1370 memset(backend
, 0, sizeof(*backend
));
1372 ALCplaybackAlsa_Construct(backend
, device
);
1374 return STATIC_CAST(ALCbackend
, backend
);
1376 if(type
== ALCbackend_Capture
)
1378 ALCcaptureAlsa
*backend
;
1380 backend
= ALCcaptureAlsa_New(sizeof(*backend
));
1381 if(!backend
) return NULL
;
1382 memset(backend
, 0, sizeof(*backend
));
1384 ALCcaptureAlsa_Construct(backend
, device
);
1386 return STATIC_CAST(ALCbackend
, backend
);
1392 DEFINE_ALCBACKENDFACTORY_VTABLE(ALCalsaBackendFactory
);
1395 ALCbackendFactory
*ALCalsaBackendFactory_getFactory(void)
1397 static ALCalsaBackendFactory factory
= ALCALSABACKENDFACTORY_INITIALIZER
;
1398 return STATIC_CAST(ALCbackendFactory
, &factory
);