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
30 #include "ringbuffer.h"
34 #include "backends/base.h"
36 #include <alsa/asoundlib.h>
39 static const ALCchar alsaDevice
[] = "ALSA Default";
43 #define ALSA_FUNCS(MAGIC) \
44 MAGIC(snd_strerror); \
45 MAGIC(snd_pcm_open); \
46 MAGIC(snd_pcm_close); \
47 MAGIC(snd_pcm_nonblock); \
48 MAGIC(snd_pcm_frames_to_bytes); \
49 MAGIC(snd_pcm_bytes_to_frames); \
50 MAGIC(snd_pcm_hw_params_malloc); \
51 MAGIC(snd_pcm_hw_params_free); \
52 MAGIC(snd_pcm_hw_params_any); \
53 MAGIC(snd_pcm_hw_params_current); \
54 MAGIC(snd_pcm_hw_params_set_access); \
55 MAGIC(snd_pcm_hw_params_set_format); \
56 MAGIC(snd_pcm_hw_params_set_channels); \
57 MAGIC(snd_pcm_hw_params_set_periods_near); \
58 MAGIC(snd_pcm_hw_params_set_rate_near); \
59 MAGIC(snd_pcm_hw_params_set_rate); \
60 MAGIC(snd_pcm_hw_params_set_rate_resample); \
61 MAGIC(snd_pcm_hw_params_set_buffer_time_near); \
62 MAGIC(snd_pcm_hw_params_set_period_time_near); \
63 MAGIC(snd_pcm_hw_params_set_buffer_size_near); \
64 MAGIC(snd_pcm_hw_params_set_period_size_near); \
65 MAGIC(snd_pcm_hw_params_set_buffer_size_min); \
66 MAGIC(snd_pcm_hw_params_get_buffer_time_min); \
67 MAGIC(snd_pcm_hw_params_get_buffer_time_max); \
68 MAGIC(snd_pcm_hw_params_get_period_time_min); \
69 MAGIC(snd_pcm_hw_params_get_period_time_max); \
70 MAGIC(snd_pcm_hw_params_get_buffer_size); \
71 MAGIC(snd_pcm_hw_params_get_period_size); \
72 MAGIC(snd_pcm_hw_params_get_access); \
73 MAGIC(snd_pcm_hw_params_get_periods); \
74 MAGIC(snd_pcm_hw_params_test_format); \
75 MAGIC(snd_pcm_hw_params_test_channels); \
76 MAGIC(snd_pcm_hw_params); \
77 MAGIC(snd_pcm_sw_params_malloc); \
78 MAGIC(snd_pcm_sw_params_current); \
79 MAGIC(snd_pcm_sw_params_set_avail_min); \
80 MAGIC(snd_pcm_sw_params_set_stop_threshold); \
81 MAGIC(snd_pcm_sw_params); \
82 MAGIC(snd_pcm_sw_params_free); \
83 MAGIC(snd_pcm_prepare); \
84 MAGIC(snd_pcm_start); \
85 MAGIC(snd_pcm_resume); \
86 MAGIC(snd_pcm_reset); \
87 MAGIC(snd_pcm_wait); \
88 MAGIC(snd_pcm_delay); \
89 MAGIC(snd_pcm_state); \
90 MAGIC(snd_pcm_avail_update); \
91 MAGIC(snd_pcm_areas_silence); \
92 MAGIC(snd_pcm_mmap_begin); \
93 MAGIC(snd_pcm_mmap_commit); \
94 MAGIC(snd_pcm_readi); \
95 MAGIC(snd_pcm_writei); \
96 MAGIC(snd_pcm_drain); \
97 MAGIC(snd_pcm_drop); \
98 MAGIC(snd_pcm_recover); \
99 MAGIC(snd_pcm_info_malloc); \
100 MAGIC(snd_pcm_info_free); \
101 MAGIC(snd_pcm_info_set_device); \
102 MAGIC(snd_pcm_info_set_subdevice); \
103 MAGIC(snd_pcm_info_set_stream); \
104 MAGIC(snd_pcm_info_get_name); \
105 MAGIC(snd_ctl_pcm_next_device); \
106 MAGIC(snd_ctl_pcm_info); \
107 MAGIC(snd_ctl_open); \
108 MAGIC(snd_ctl_close); \
109 MAGIC(snd_ctl_card_info_malloc); \
110 MAGIC(snd_ctl_card_info_free); \
111 MAGIC(snd_ctl_card_info); \
112 MAGIC(snd_ctl_card_info_get_name); \
113 MAGIC(snd_ctl_card_info_get_id); \
114 MAGIC(snd_card_next); \
115 MAGIC(snd_config_update_free_global)
117 static void *alsa_handle
;
118 #define MAKE_FUNC(f) static __typeof(f) * p##f
119 ALSA_FUNCS(MAKE_FUNC
);
122 #define snd_strerror psnd_strerror
123 #define snd_pcm_open psnd_pcm_open
124 #define snd_pcm_close psnd_pcm_close
125 #define snd_pcm_nonblock psnd_pcm_nonblock
126 #define snd_pcm_frames_to_bytes psnd_pcm_frames_to_bytes
127 #define snd_pcm_bytes_to_frames psnd_pcm_bytes_to_frames
128 #define snd_pcm_hw_params_malloc psnd_pcm_hw_params_malloc
129 #define snd_pcm_hw_params_free psnd_pcm_hw_params_free
130 #define snd_pcm_hw_params_any psnd_pcm_hw_params_any
131 #define snd_pcm_hw_params_current psnd_pcm_hw_params_current
132 #define snd_pcm_hw_params_set_access psnd_pcm_hw_params_set_access
133 #define snd_pcm_hw_params_set_format psnd_pcm_hw_params_set_format
134 #define snd_pcm_hw_params_set_channels psnd_pcm_hw_params_set_channels
135 #define snd_pcm_hw_params_set_periods_near psnd_pcm_hw_params_set_periods_near
136 #define snd_pcm_hw_params_set_rate_near psnd_pcm_hw_params_set_rate_near
137 #define snd_pcm_hw_params_set_rate psnd_pcm_hw_params_set_rate
138 #define snd_pcm_hw_params_set_rate_resample psnd_pcm_hw_params_set_rate_resample
139 #define snd_pcm_hw_params_set_buffer_time_near psnd_pcm_hw_params_set_buffer_time_near
140 #define snd_pcm_hw_params_set_period_time_near psnd_pcm_hw_params_set_period_time_near
141 #define snd_pcm_hw_params_set_buffer_size_near psnd_pcm_hw_params_set_buffer_size_near
142 #define snd_pcm_hw_params_set_period_size_near psnd_pcm_hw_params_set_period_size_near
143 #define snd_pcm_hw_params_set_buffer_size_min psnd_pcm_hw_params_set_buffer_size_min
144 #define snd_pcm_hw_params_get_buffer_time_min psnd_pcm_hw_params_get_buffer_time_min
145 #define snd_pcm_hw_params_get_buffer_time_max psnd_pcm_hw_params_get_buffer_time_max
146 #define snd_pcm_hw_params_get_period_time_min psnd_pcm_hw_params_get_period_time_min
147 #define snd_pcm_hw_params_get_period_time_max psnd_pcm_hw_params_get_period_time_max
148 #define snd_pcm_hw_params_get_buffer_size psnd_pcm_hw_params_get_buffer_size
149 #define snd_pcm_hw_params_get_period_size psnd_pcm_hw_params_get_period_size
150 #define snd_pcm_hw_params_get_access psnd_pcm_hw_params_get_access
151 #define snd_pcm_hw_params_get_periods psnd_pcm_hw_params_get_periods
152 #define snd_pcm_hw_params_test_format psnd_pcm_hw_params_test_format
153 #define snd_pcm_hw_params_test_channels psnd_pcm_hw_params_test_channels
154 #define snd_pcm_hw_params psnd_pcm_hw_params
155 #define snd_pcm_sw_params_malloc psnd_pcm_sw_params_malloc
156 #define snd_pcm_sw_params_current psnd_pcm_sw_params_current
157 #define snd_pcm_sw_params_set_avail_min psnd_pcm_sw_params_set_avail_min
158 #define snd_pcm_sw_params_set_stop_threshold psnd_pcm_sw_params_set_stop_threshold
159 #define snd_pcm_sw_params psnd_pcm_sw_params
160 #define snd_pcm_sw_params_free psnd_pcm_sw_params_free
161 #define snd_pcm_prepare psnd_pcm_prepare
162 #define snd_pcm_start psnd_pcm_start
163 #define snd_pcm_resume psnd_pcm_resume
164 #define snd_pcm_reset psnd_pcm_reset
165 #define snd_pcm_wait psnd_pcm_wait
166 #define snd_pcm_delay psnd_pcm_delay
167 #define snd_pcm_state psnd_pcm_state
168 #define snd_pcm_avail_update psnd_pcm_avail_update
169 #define snd_pcm_areas_silence psnd_pcm_areas_silence
170 #define snd_pcm_mmap_begin psnd_pcm_mmap_begin
171 #define snd_pcm_mmap_commit psnd_pcm_mmap_commit
172 #define snd_pcm_readi psnd_pcm_readi
173 #define snd_pcm_writei psnd_pcm_writei
174 #define snd_pcm_drain psnd_pcm_drain
175 #define snd_pcm_drop psnd_pcm_drop
176 #define snd_pcm_recover psnd_pcm_recover
177 #define snd_pcm_info_malloc psnd_pcm_info_malloc
178 #define snd_pcm_info_free psnd_pcm_info_free
179 #define snd_pcm_info_set_device psnd_pcm_info_set_device
180 #define snd_pcm_info_set_subdevice psnd_pcm_info_set_subdevice
181 #define snd_pcm_info_set_stream psnd_pcm_info_set_stream
182 #define snd_pcm_info_get_name psnd_pcm_info_get_name
183 #define snd_ctl_pcm_next_device psnd_ctl_pcm_next_device
184 #define snd_ctl_pcm_info psnd_ctl_pcm_info
185 #define snd_ctl_open psnd_ctl_open
186 #define snd_ctl_close psnd_ctl_close
187 #define snd_ctl_card_info_malloc psnd_ctl_card_info_malloc
188 #define snd_ctl_card_info_free psnd_ctl_card_info_free
189 #define snd_ctl_card_info psnd_ctl_card_info
190 #define snd_ctl_card_info_get_name psnd_ctl_card_info_get_name
191 #define snd_ctl_card_info_get_id psnd_ctl_card_info_get_id
192 #define snd_card_next psnd_card_next
193 #define snd_config_update_free_global psnd_config_update_free_global
197 static ALCboolean
alsa_load(void)
199 ALCboolean error
= ALC_FALSE
;
204 al_string missing_funcs
= AL_STRING_INIT_STATIC();
206 alsa_handle
= LoadLib("libasound.so.2");
209 WARN("Failed to load %s\n", "libasound.so.2");
214 #define LOAD_FUNC(f) do { \
215 p##f = GetSymbol(alsa_handle, #f); \
218 alstr_append_cstr(&missing_funcs, "\n" #f); \
221 ALSA_FUNCS(LOAD_FUNC
);
226 WARN("Missing expected functions:%s\n", alstr_get_cstr(missing_funcs
));
227 CloseLib(alsa_handle
);
230 alstr_reset(&missing_funcs
);
240 al_string device_name
;
242 TYPEDEF_VECTOR(DevMap
, vector_DevMap
)
244 static vector_DevMap PlaybackDevices
;
245 static vector_DevMap CaptureDevices
;
247 static void clear_devlist(vector_DevMap
*devlist
)
249 #define FREE_DEV(i) do { \
250 AL_STRING_DEINIT((i)->name); \
251 AL_STRING_DEINIT((i)->device_name); \
253 VECTOR_FOR_EACH(DevMap
, *devlist
, FREE_DEV
);
254 VECTOR_RESIZE(*devlist
, 0, 0);
259 static const char *prefix_name(snd_pcm_stream_t stream
)
261 assert(stream
== SND_PCM_STREAM_PLAYBACK
|| stream
== SND_PCM_STREAM_CAPTURE
);
262 return (stream
==SND_PCM_STREAM_PLAYBACK
) ? "device-prefix" : "capture-prefix";
265 static void probe_devices(snd_pcm_stream_t stream
, vector_DevMap
*DeviceList
)
267 const char *main_prefix
= "plughw:";
269 snd_ctl_card_info_t
*info
;
270 snd_pcm_info_t
*pcminfo
;
274 clear_devlist(DeviceList
);
276 snd_ctl_card_info_malloc(&info
);
277 snd_pcm_info_malloc(&pcminfo
);
279 AL_STRING_INIT(entry
.name
);
280 AL_STRING_INIT(entry
.device_name
);
281 alstr_copy_cstr(&entry
.name
, alsaDevice
);
282 alstr_copy_cstr(&entry
.device_name
, GetConfigValue(
283 NULL
, "alsa", (stream
==SND_PCM_STREAM_PLAYBACK
) ? "device" : "capture", "default"
285 VECTOR_PUSH_BACK(*DeviceList
, entry
);
287 if(stream
== SND_PCM_STREAM_PLAYBACK
)
289 const char *customdevs
, *sep
, *next
;
290 next
= GetConfigValue(NULL
, "alsa", "custom-devices", "");
291 while((customdevs
=next
) != NULL
&& customdevs
[0])
293 next
= strchr(customdevs
, ';');
294 sep
= strchr(customdevs
, '=');
297 al_string spec
= AL_STRING_INIT_STATIC();
299 alstr_copy_range(&spec
, customdevs
, next
++);
301 alstr_copy_cstr(&spec
, customdevs
);
302 ERR("Invalid ALSA device specification \"%s\"\n", alstr_get_cstr(spec
));
307 AL_STRING_INIT(entry
.name
);
308 AL_STRING_INIT(entry
.device_name
);
309 alstr_copy_range(&entry
.name
, customdevs
, sep
++);
311 alstr_copy_range(&entry
.device_name
, sep
, next
++);
313 alstr_copy_cstr(&entry
.device_name
, sep
);
314 TRACE("Got device \"%s\", \"%s\"\n", alstr_get_cstr(entry
.name
),
315 alstr_get_cstr(entry
.device_name
));
316 VECTOR_PUSH_BACK(*DeviceList
, entry
);
321 if((err
=snd_card_next(&card
)) < 0)
322 ERR("Failed to find a card: %s\n", snd_strerror(err
));
323 ConfigValueStr(NULL
, "alsa", prefix_name(stream
), &main_prefix
);
326 const char *card_prefix
= main_prefix
;
327 const char *cardname
, *cardid
;
330 snprintf(name
, sizeof(name
), "hw:%d", card
);
331 if((err
= snd_ctl_open(&handle
, name
, 0)) < 0)
333 ERR("control open (hw:%d): %s\n", card
, snd_strerror(err
));
336 if((err
= snd_ctl_card_info(handle
, info
)) < 0)
338 ERR("control hardware info (hw:%d): %s\n", card
, snd_strerror(err
));
339 snd_ctl_close(handle
);
343 cardname
= snd_ctl_card_info_get_name(info
);
344 cardid
= snd_ctl_card_info_get_id(info
);
346 snprintf(name
, sizeof(name
), "%s-%s", prefix_name(stream
), cardid
);
347 ConfigValueStr(NULL
, "alsa", name
, &card_prefix
);
352 const char *device_prefix
= card_prefix
;
356 if(snd_ctl_pcm_next_device(handle
, &dev
) < 0)
357 ERR("snd_ctl_pcm_next_device failed\n");
361 snd_pcm_info_set_device(pcminfo
, dev
);
362 snd_pcm_info_set_subdevice(pcminfo
, 0);
363 snd_pcm_info_set_stream(pcminfo
, stream
);
364 if((err
= snd_ctl_pcm_info(handle
, pcminfo
)) < 0)
367 ERR("control digital audio info (hw:%d): %s\n", card
, snd_strerror(err
));
371 devname
= snd_pcm_info_get_name(pcminfo
);
373 snprintf(name
, sizeof(name
), "%s-%s-%d", prefix_name(stream
), cardid
, dev
);
374 ConfigValueStr(NULL
, "alsa", name
, &device_prefix
);
376 snprintf(name
, sizeof(name
), "%s, %s (CARD=%s,DEV=%d)",
377 cardname
, devname
, cardid
, dev
);
378 snprintf(device
, sizeof(device
), "%sCARD=%s,DEV=%d",
379 device_prefix
, cardid
, dev
);
381 TRACE("Got device \"%s\", \"%s\"\n", name
, device
);
382 AL_STRING_INIT(entry
.name
);
383 AL_STRING_INIT(entry
.device_name
);
384 alstr_copy_cstr(&entry
.name
, name
);
385 alstr_copy_cstr(&entry
.device_name
, device
);
386 VECTOR_PUSH_BACK(*DeviceList
, entry
);
388 snd_ctl_close(handle
);
390 if(snd_card_next(&card
) < 0) {
391 ERR("snd_card_next failed\n");
396 snd_pcm_info_free(pcminfo
);
397 snd_ctl_card_info_free(info
);
401 static int verify_state(snd_pcm_t
*handle
)
403 snd_pcm_state_t state
= snd_pcm_state(handle
);
408 case SND_PCM_STATE_OPEN
:
409 case SND_PCM_STATE_SETUP
:
410 case SND_PCM_STATE_PREPARED
:
411 case SND_PCM_STATE_RUNNING
:
412 case SND_PCM_STATE_DRAINING
:
413 case SND_PCM_STATE_PAUSED
:
417 case SND_PCM_STATE_XRUN
:
418 if((err
=snd_pcm_recover(handle
, -EPIPE
, 1)) < 0)
421 case SND_PCM_STATE_SUSPENDED
:
422 if((err
=snd_pcm_recover(handle
, -ESTRPIPE
, 1)) < 0)
425 case SND_PCM_STATE_DISCONNECTED
:
433 typedef struct ALCplaybackAlsa
{
434 DERIVE_FROM_TYPE(ALCbackend
);
436 snd_pcm_t
*pcmHandle
;
441 ATOMIC(ALenum
) killNow
;
445 static int ALCplaybackAlsa_mixerProc(void *ptr
);
446 static int ALCplaybackAlsa_mixerNoMMapProc(void *ptr
);
448 static void ALCplaybackAlsa_Construct(ALCplaybackAlsa
*self
, ALCdevice
*device
);
449 static void ALCplaybackAlsa_Destruct(ALCplaybackAlsa
*self
);
450 static ALCenum
ALCplaybackAlsa_open(ALCplaybackAlsa
*self
, const ALCchar
*name
);
451 static ALCboolean
ALCplaybackAlsa_reset(ALCplaybackAlsa
*self
);
452 static ALCboolean
ALCplaybackAlsa_start(ALCplaybackAlsa
*self
);
453 static void ALCplaybackAlsa_stop(ALCplaybackAlsa
*self
);
454 static DECLARE_FORWARD2(ALCplaybackAlsa
, ALCbackend
, ALCenum
, captureSamples
, void*, ALCuint
)
455 static DECLARE_FORWARD(ALCplaybackAlsa
, ALCbackend
, ALCuint
, availableSamples
)
456 static ClockLatency
ALCplaybackAlsa_getClockLatency(ALCplaybackAlsa
*self
);
457 static DECLARE_FORWARD(ALCplaybackAlsa
, ALCbackend
, void, lock
)
458 static DECLARE_FORWARD(ALCplaybackAlsa
, ALCbackend
, void, unlock
)
459 DECLARE_DEFAULT_ALLOCATORS(ALCplaybackAlsa
)
461 DEFINE_ALCBACKEND_VTABLE(ALCplaybackAlsa
);
464 static void ALCplaybackAlsa_Construct(ALCplaybackAlsa
*self
, ALCdevice
*device
)
466 ALCbackend_Construct(STATIC_CAST(ALCbackend
, self
), device
);
467 SET_VTABLE2(ALCplaybackAlsa
, ALCbackend
, self
);
469 self
->pcmHandle
= NULL
;
472 ATOMIC_INIT(&self
->killNow
, AL_TRUE
);
475 void ALCplaybackAlsa_Destruct(ALCplaybackAlsa
*self
)
478 snd_pcm_close(self
->pcmHandle
);
479 self
->pcmHandle
= NULL
;
480 ALCbackend_Destruct(STATIC_CAST(ALCbackend
, self
));
484 static int ALCplaybackAlsa_mixerProc(void *ptr
)
486 ALCplaybackAlsa
*self
= (ALCplaybackAlsa
*)ptr
;
487 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
488 const snd_pcm_channel_area_t
*areas
= NULL
;
489 snd_pcm_uframes_t update_size
, num_updates
;
490 snd_pcm_sframes_t avail
, commitres
;
491 snd_pcm_uframes_t offset
, frames
;
496 althrd_setname(althrd_current(), MIXER_THREAD_NAME
);
498 update_size
= device
->UpdateSize
;
499 num_updates
= device
->NumUpdates
;
500 while(!ATOMIC_LOAD(&self
->killNow
, almemory_order_acquire
))
502 int state
= verify_state(self
->pcmHandle
);
505 ERR("Invalid state detected: %s\n", snd_strerror(state
));
506 ALCplaybackAlsa_lock(self
);
507 aluHandleDisconnect(device
, "Bad state: %s", snd_strerror(state
));
508 ALCplaybackAlsa_unlock(self
);
512 avail
= snd_pcm_avail_update(self
->pcmHandle
);
515 ERR("available update failed: %s\n", snd_strerror(avail
));
519 if((snd_pcm_uframes_t
)avail
> update_size
*(num_updates
+1))
521 WARN("available samples exceeds the buffer size\n");
522 snd_pcm_reset(self
->pcmHandle
);
526 // make sure there's frames to process
527 if((snd_pcm_uframes_t
)avail
< update_size
)
529 if(state
!= SND_PCM_STATE_RUNNING
)
531 err
= snd_pcm_start(self
->pcmHandle
);
534 ERR("start failed: %s\n", snd_strerror(err
));
538 if(snd_pcm_wait(self
->pcmHandle
, 1000) == 0)
539 ERR("Wait timeout... buffer size too low?\n");
542 avail
-= avail
%update_size
;
544 // it is possible that contiguous areas are smaller, thus we use a loop
545 ALCplaybackAlsa_lock(self
);
550 err
= snd_pcm_mmap_begin(self
->pcmHandle
, &areas
, &offset
, &frames
);
553 ERR("mmap begin error: %s\n", snd_strerror(err
));
557 WritePtr
= (char*)areas
->addr
+ (offset
* areas
->step
/ 8);
558 aluMixData(device
, WritePtr
, frames
);
560 commitres
= snd_pcm_mmap_commit(self
->pcmHandle
, offset
, frames
);
561 if(commitres
< 0 || (commitres
-frames
) != 0)
563 ERR("mmap commit error: %s\n",
564 snd_strerror(commitres
>= 0 ? -EPIPE
: commitres
));
570 ALCplaybackAlsa_unlock(self
);
576 static int ALCplaybackAlsa_mixerNoMMapProc(void *ptr
)
578 ALCplaybackAlsa
*self
= (ALCplaybackAlsa
*)ptr
;
579 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
580 snd_pcm_uframes_t update_size
, num_updates
;
581 snd_pcm_sframes_t avail
;
586 althrd_setname(althrd_current(), MIXER_THREAD_NAME
);
588 update_size
= device
->UpdateSize
;
589 num_updates
= device
->NumUpdates
;
590 while(!ATOMIC_LOAD(&self
->killNow
, almemory_order_acquire
))
592 int state
= verify_state(self
->pcmHandle
);
595 ERR("Invalid state detected: %s\n", snd_strerror(state
));
596 ALCplaybackAlsa_lock(self
);
597 aluHandleDisconnect(device
, "Bad state: %s", snd_strerror(state
));
598 ALCplaybackAlsa_unlock(self
);
602 avail
= snd_pcm_avail_update(self
->pcmHandle
);
605 ERR("available update failed: %s\n", snd_strerror(avail
));
609 if((snd_pcm_uframes_t
)avail
> update_size
*num_updates
)
611 WARN("available samples exceeds the buffer size\n");
612 snd_pcm_reset(self
->pcmHandle
);
616 if((snd_pcm_uframes_t
)avail
< update_size
)
618 if(state
!= SND_PCM_STATE_RUNNING
)
620 err
= snd_pcm_start(self
->pcmHandle
);
623 ERR("start failed: %s\n", snd_strerror(err
));
627 if(snd_pcm_wait(self
->pcmHandle
, 1000) == 0)
628 ERR("Wait timeout... buffer size too low?\n");
632 ALCplaybackAlsa_lock(self
);
633 WritePtr
= self
->buffer
;
634 avail
= snd_pcm_bytes_to_frames(self
->pcmHandle
, self
->size
);
635 aluMixData(device
, WritePtr
, avail
);
639 int ret
= snd_pcm_writei(self
->pcmHandle
, WritePtr
, avail
);
644 #if ESTRPIPE != EPIPE
649 ret
= snd_pcm_recover(self
->pcmHandle
, ret
, 1);
656 WritePtr
+= snd_pcm_frames_to_bytes(self
->pcmHandle
, ret
);
663 ret
= snd_pcm_prepare(self
->pcmHandle
);
668 ALCplaybackAlsa_unlock(self
);
675 static ALCenum
ALCplaybackAlsa_open(ALCplaybackAlsa
*self
, const ALCchar
*name
)
677 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
678 const char *driver
= NULL
;
685 if(VECTOR_SIZE(PlaybackDevices
) == 0)
686 probe_devices(SND_PCM_STREAM_PLAYBACK
, &PlaybackDevices
);
688 #define MATCH_NAME(i) (alstr_cmp_cstr((i)->name, name) == 0)
689 VECTOR_FIND_IF(iter
, const DevMap
, PlaybackDevices
, MATCH_NAME
);
691 if(iter
== VECTOR_END(PlaybackDevices
))
692 return ALC_INVALID_VALUE
;
693 driver
= alstr_get_cstr(iter
->device_name
);
698 driver
= GetConfigValue(NULL
, "alsa", "device", "default");
701 TRACE("Opening device \"%s\"\n", driver
);
702 err
= snd_pcm_open(&self
->pcmHandle
, driver
, SND_PCM_STREAM_PLAYBACK
, SND_PCM_NONBLOCK
);
705 ERR("Could not open playback device '%s': %s\n", driver
, snd_strerror(err
));
706 return ALC_OUT_OF_MEMORY
;
709 /* Free alsa's global config tree. Otherwise valgrind reports a ton of leaks. */
710 snd_config_update_free_global();
712 alstr_copy_cstr(&device
->DeviceName
, name
);
717 static ALCboolean
ALCplaybackAlsa_reset(ALCplaybackAlsa
*self
)
719 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
720 snd_pcm_uframes_t periodSizeInFrames
;
721 unsigned int periodLen
, bufferLen
;
722 snd_pcm_sw_params_t
*sp
= NULL
;
723 snd_pcm_hw_params_t
*hp
= NULL
;
724 snd_pcm_format_t format
= -1;
725 snd_pcm_access_t access
;
726 unsigned int periods
;
733 switch(device
->FmtType
)
736 format
= SND_PCM_FORMAT_S8
;
739 format
= SND_PCM_FORMAT_U8
;
742 format
= SND_PCM_FORMAT_S16
;
745 format
= SND_PCM_FORMAT_U16
;
748 format
= SND_PCM_FORMAT_S32
;
751 format
= SND_PCM_FORMAT_U32
;
754 format
= SND_PCM_FORMAT_FLOAT
;
758 allowmmap
= GetConfigValueBool(alstr_get_cstr(device
->DeviceName
), "alsa", "mmap", 1);
759 periods
= device
->NumUpdates
;
760 periodLen
= (ALuint64
)device
->UpdateSize
* 1000000 / device
->Frequency
;
761 bufferLen
= periodLen
* periods
;
762 rate
= device
->Frequency
;
764 snd_pcm_hw_params_malloc(&hp
);
765 #define CHECK(x) if((funcerr=#x),(err=(x)) < 0) goto error
766 CHECK(snd_pcm_hw_params_any(self
->pcmHandle
, hp
));
767 /* set interleaved access */
768 if(!allowmmap
|| snd_pcm_hw_params_set_access(self
->pcmHandle
, hp
, SND_PCM_ACCESS_MMAP_INTERLEAVED
) < 0)
771 CHECK(snd_pcm_hw_params_set_access(self
->pcmHandle
, hp
, SND_PCM_ACCESS_RW_INTERLEAVED
));
773 /* test and set format (implicitly sets sample bits) */
774 if(snd_pcm_hw_params_test_format(self
->pcmHandle
, hp
, format
) < 0)
776 static const struct {
777 snd_pcm_format_t format
;
778 enum DevFmtType fmttype
;
780 { SND_PCM_FORMAT_FLOAT
, DevFmtFloat
},
781 { SND_PCM_FORMAT_S32
, DevFmtInt
},
782 { SND_PCM_FORMAT_U32
, DevFmtUInt
},
783 { SND_PCM_FORMAT_S16
, DevFmtShort
},
784 { SND_PCM_FORMAT_U16
, DevFmtUShort
},
785 { SND_PCM_FORMAT_S8
, DevFmtByte
},
786 { SND_PCM_FORMAT_U8
, DevFmtUByte
},
790 for(k
= 0;k
< COUNTOF(formatlist
);k
++)
792 format
= formatlist
[k
].format
;
793 if(snd_pcm_hw_params_test_format(self
->pcmHandle
, hp
, format
) >= 0)
795 device
->FmtType
= formatlist
[k
].fmttype
;
800 CHECK(snd_pcm_hw_params_set_format(self
->pcmHandle
, hp
, format
));
801 /* test and set channels (implicitly sets frame bits) */
802 if(snd_pcm_hw_params_test_channels(self
->pcmHandle
, hp
, ChannelsFromDevFmt(device
->FmtChans
, device
->AmbiOrder
)) < 0)
804 static const enum DevFmtChannels channellist
[] = {
813 for(k
= 0;k
< COUNTOF(channellist
);k
++)
815 if(snd_pcm_hw_params_test_channels(self
->pcmHandle
, hp
, ChannelsFromDevFmt(channellist
[k
], 0)) >= 0)
817 device
->FmtChans
= channellist
[k
];
818 device
->AmbiOrder
= 0;
823 CHECK(snd_pcm_hw_params_set_channels(self
->pcmHandle
, hp
, ChannelsFromDevFmt(device
->FmtChans
, device
->AmbiOrder
)));
824 /* set rate (implicitly constrains period/buffer parameters) */
825 if(!GetConfigValueBool(alstr_get_cstr(device
->DeviceName
), "alsa", "allow-resampler", 0) ||
826 !(device
->Flags
&DEVICE_FREQUENCY_REQUEST
))
828 if(snd_pcm_hw_params_set_rate_resample(self
->pcmHandle
, hp
, 0) < 0)
829 ERR("Failed to disable ALSA resampler\n");
831 else if(snd_pcm_hw_params_set_rate_resample(self
->pcmHandle
, hp
, 1) < 0)
832 ERR("Failed to enable ALSA resampler\n");
833 CHECK(snd_pcm_hw_params_set_rate_near(self
->pcmHandle
, hp
, &rate
, NULL
));
834 /* set buffer time (implicitly constrains period/buffer parameters) */
835 if((err
=snd_pcm_hw_params_set_buffer_time_near(self
->pcmHandle
, hp
, &bufferLen
, NULL
)) < 0)
836 ERR("snd_pcm_hw_params_set_buffer_time_near failed: %s\n", snd_strerror(err
));
837 /* set period time (implicitly sets buffer size/bytes/time and period size/bytes) */
838 if((err
=snd_pcm_hw_params_set_period_time_near(self
->pcmHandle
, hp
, &periodLen
, NULL
)) < 0)
839 ERR("snd_pcm_hw_params_set_period_time_near failed: %s\n", snd_strerror(err
));
840 /* install and prepare hardware configuration */
841 CHECK(snd_pcm_hw_params(self
->pcmHandle
, hp
));
842 /* retrieve configuration info */
843 CHECK(snd_pcm_hw_params_get_access(hp
, &access
));
844 CHECK(snd_pcm_hw_params_get_period_size(hp
, &periodSizeInFrames
, NULL
));
845 CHECK(snd_pcm_hw_params_get_periods(hp
, &periods
, &dir
));
847 WARN("Inexact period count: %u (%d)\n", periods
, dir
);
849 snd_pcm_hw_params_free(hp
);
851 snd_pcm_sw_params_malloc(&sp
);
853 CHECK(snd_pcm_sw_params_current(self
->pcmHandle
, sp
));
854 CHECK(snd_pcm_sw_params_set_avail_min(self
->pcmHandle
, sp
, periodSizeInFrames
));
855 CHECK(snd_pcm_sw_params_set_stop_threshold(self
->pcmHandle
, sp
, periodSizeInFrames
*periods
));
856 CHECK(snd_pcm_sw_params(self
->pcmHandle
, sp
));
858 snd_pcm_sw_params_free(sp
);
861 device
->NumUpdates
= periods
;
862 device
->UpdateSize
= periodSizeInFrames
;
863 device
->Frequency
= rate
;
865 SetDefaultChannelOrder(device
);
870 ERR("%s failed: %s\n", funcerr
, snd_strerror(err
));
871 if(hp
) snd_pcm_hw_params_free(hp
);
872 if(sp
) snd_pcm_sw_params_free(sp
);
876 static ALCboolean
ALCplaybackAlsa_start(ALCplaybackAlsa
*self
)
878 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
879 int (*thread_func
)(void*) = NULL
;
880 snd_pcm_hw_params_t
*hp
= NULL
;
881 snd_pcm_access_t access
;
885 snd_pcm_hw_params_malloc(&hp
);
886 #define CHECK(x) if((funcerr=#x),(err=(x)) < 0) goto error
887 CHECK(snd_pcm_hw_params_current(self
->pcmHandle
, hp
));
888 /* retrieve configuration info */
889 CHECK(snd_pcm_hw_params_get_access(hp
, &access
));
891 snd_pcm_hw_params_free(hp
);
894 self
->size
= snd_pcm_frames_to_bytes(self
->pcmHandle
, device
->UpdateSize
);
895 if(access
== SND_PCM_ACCESS_RW_INTERLEAVED
)
897 self
->buffer
= al_malloc(16, self
->size
);
900 ERR("buffer malloc failed\n");
903 thread_func
= ALCplaybackAlsa_mixerNoMMapProc
;
907 err
= snd_pcm_prepare(self
->pcmHandle
);
910 ERR("snd_pcm_prepare(data->pcmHandle) failed: %s\n", snd_strerror(err
));
913 thread_func
= ALCplaybackAlsa_mixerProc
;
915 ATOMIC_STORE(&self
->killNow
, AL_FALSE
, almemory_order_release
);
916 if(althrd_create(&self
->thread
, thread_func
, self
) != althrd_success
)
918 ERR("Could not create playback thread\n");
919 al_free(self
->buffer
);
927 ERR("%s failed: %s\n", funcerr
, snd_strerror(err
));
928 if(hp
) snd_pcm_hw_params_free(hp
);
932 static void ALCplaybackAlsa_stop(ALCplaybackAlsa
*self
)
936 if(ATOMIC_EXCHANGE(&self
->killNow
, AL_TRUE
, almemory_order_acq_rel
))
938 althrd_join(self
->thread
, &res
);
940 al_free(self
->buffer
);
944 static ClockLatency
ALCplaybackAlsa_getClockLatency(ALCplaybackAlsa
*self
)
946 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
947 snd_pcm_sframes_t delay
= 0;
951 ALCplaybackAlsa_lock(self
);
952 ret
.ClockTime
= GetDeviceClockTime(device
);
953 if((err
=snd_pcm_delay(self
->pcmHandle
, &delay
)) < 0)
955 ERR("Failed to get pcm delay: %s\n", snd_strerror(err
));
958 if(delay
< 0) delay
= 0;
959 ret
.Latency
= delay
* DEVICE_CLOCK_RES
/ device
->Frequency
;
960 ALCplaybackAlsa_unlock(self
);
966 typedef struct ALCcaptureAlsa
{
967 DERIVE_FROM_TYPE(ALCbackend
);
969 snd_pcm_t
*pcmHandle
;
975 ll_ringbuffer_t
*ring
;
977 snd_pcm_sframes_t last_avail
;
980 static void ALCcaptureAlsa_Construct(ALCcaptureAlsa
*self
, ALCdevice
*device
);
981 static void ALCcaptureAlsa_Destruct(ALCcaptureAlsa
*self
);
982 static ALCenum
ALCcaptureAlsa_open(ALCcaptureAlsa
*self
, const ALCchar
*name
);
983 static DECLARE_FORWARD(ALCcaptureAlsa
, ALCbackend
, ALCboolean
, reset
)
984 static ALCboolean
ALCcaptureAlsa_start(ALCcaptureAlsa
*self
);
985 static void ALCcaptureAlsa_stop(ALCcaptureAlsa
*self
);
986 static ALCenum
ALCcaptureAlsa_captureSamples(ALCcaptureAlsa
*self
, ALCvoid
*buffer
, ALCuint samples
);
987 static ALCuint
ALCcaptureAlsa_availableSamples(ALCcaptureAlsa
*self
);
988 static ClockLatency
ALCcaptureAlsa_getClockLatency(ALCcaptureAlsa
*self
);
989 static DECLARE_FORWARD(ALCcaptureAlsa
, ALCbackend
, void, lock
)
990 static DECLARE_FORWARD(ALCcaptureAlsa
, ALCbackend
, void, unlock
)
991 DECLARE_DEFAULT_ALLOCATORS(ALCcaptureAlsa
)
993 DEFINE_ALCBACKEND_VTABLE(ALCcaptureAlsa
);
996 static void ALCcaptureAlsa_Construct(ALCcaptureAlsa
*self
, ALCdevice
*device
)
998 ALCbackend_Construct(STATIC_CAST(ALCbackend
, self
), device
);
999 SET_VTABLE2(ALCcaptureAlsa
, ALCbackend
, self
);
1001 self
->pcmHandle
= NULL
;
1002 self
->buffer
= NULL
;
1006 void ALCcaptureAlsa_Destruct(ALCcaptureAlsa
*self
)
1009 snd_pcm_close(self
->pcmHandle
);
1010 self
->pcmHandle
= NULL
;
1012 al_free(self
->buffer
);
1013 self
->buffer
= NULL
;
1015 ll_ringbuffer_free(self
->ring
);
1018 ALCbackend_Destruct(STATIC_CAST(ALCbackend
, self
));
1022 static ALCenum
ALCcaptureAlsa_open(ALCcaptureAlsa
*self
, const ALCchar
*name
)
1024 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
1025 const char *driver
= NULL
;
1026 snd_pcm_hw_params_t
*hp
;
1027 snd_pcm_uframes_t bufferSizeInFrames
;
1028 snd_pcm_uframes_t periodSizeInFrames
;
1029 ALboolean needring
= AL_FALSE
;
1030 snd_pcm_format_t format
= -1;
1031 const char *funcerr
;
1038 if(VECTOR_SIZE(CaptureDevices
) == 0)
1039 probe_devices(SND_PCM_STREAM_CAPTURE
, &CaptureDevices
);
1041 #define MATCH_NAME(i) (alstr_cmp_cstr((i)->name, name) == 0)
1042 VECTOR_FIND_IF(iter
, const DevMap
, CaptureDevices
, MATCH_NAME
);
1044 if(iter
== VECTOR_END(CaptureDevices
))
1045 return ALC_INVALID_VALUE
;
1046 driver
= alstr_get_cstr(iter
->device_name
);
1051 driver
= GetConfigValue(NULL
, "alsa", "capture", "default");
1054 TRACE("Opening device \"%s\"\n", driver
);
1055 err
= snd_pcm_open(&self
->pcmHandle
, driver
, SND_PCM_STREAM_CAPTURE
, SND_PCM_NONBLOCK
);
1058 ERR("Could not open capture device '%s': %s\n", driver
, snd_strerror(err
));
1059 return ALC_INVALID_VALUE
;
1062 /* Free alsa's global config tree. Otherwise valgrind reports a ton of leaks. */
1063 snd_config_update_free_global();
1065 switch(device
->FmtType
)
1068 format
= SND_PCM_FORMAT_S8
;
1071 format
= SND_PCM_FORMAT_U8
;
1074 format
= SND_PCM_FORMAT_S16
;
1077 format
= SND_PCM_FORMAT_U16
;
1080 format
= SND_PCM_FORMAT_S32
;
1083 format
= SND_PCM_FORMAT_U32
;
1086 format
= SND_PCM_FORMAT_FLOAT
;
1091 bufferSizeInFrames
= maxu(device
->UpdateSize
*device
->NumUpdates
,
1092 100*device
->Frequency
/1000);
1093 periodSizeInFrames
= minu(bufferSizeInFrames
, 25*device
->Frequency
/1000);
1095 snd_pcm_hw_params_malloc(&hp
);
1096 #define CHECK(x) if((funcerr=#x),(err=(x)) < 0) goto error
1097 CHECK(snd_pcm_hw_params_any(self
->pcmHandle
, hp
));
1098 /* set interleaved access */
1099 CHECK(snd_pcm_hw_params_set_access(self
->pcmHandle
, hp
, SND_PCM_ACCESS_RW_INTERLEAVED
));
1100 /* set format (implicitly sets sample bits) */
1101 CHECK(snd_pcm_hw_params_set_format(self
->pcmHandle
, hp
, format
));
1102 /* set channels (implicitly sets frame bits) */
1103 CHECK(snd_pcm_hw_params_set_channels(self
->pcmHandle
, hp
, ChannelsFromDevFmt(device
->FmtChans
, device
->AmbiOrder
)));
1104 /* set rate (implicitly constrains period/buffer parameters) */
1105 CHECK(snd_pcm_hw_params_set_rate(self
->pcmHandle
, hp
, device
->Frequency
, 0));
1106 /* set buffer size in frame units (implicitly sets period size/bytes/time and buffer time/bytes) */
1107 if(snd_pcm_hw_params_set_buffer_size_min(self
->pcmHandle
, hp
, &bufferSizeInFrames
) < 0)
1109 TRACE("Buffer too large, using intermediate ring buffer\n");
1111 CHECK(snd_pcm_hw_params_set_buffer_size_near(self
->pcmHandle
, hp
, &bufferSizeInFrames
));
1113 /* set buffer size in frame units (implicitly sets period size/bytes/time and buffer time/bytes) */
1114 CHECK(snd_pcm_hw_params_set_period_size_near(self
->pcmHandle
, hp
, &periodSizeInFrames
, NULL
));
1115 /* install and prepare hardware configuration */
1116 CHECK(snd_pcm_hw_params(self
->pcmHandle
, hp
));
1117 /* retrieve configuration info */
1118 CHECK(snd_pcm_hw_params_get_period_size(hp
, &periodSizeInFrames
, NULL
));
1120 snd_pcm_hw_params_free(hp
);
1125 self
->ring
= ll_ringbuffer_create(
1126 device
->UpdateSize
*device
->NumUpdates
,
1127 FrameSizeFromDevFmt(device
->FmtChans
, device
->FmtType
, device
->AmbiOrder
),
1132 ERR("ring buffer create failed\n");
1137 alstr_copy_cstr(&device
->DeviceName
, name
);
1139 return ALC_NO_ERROR
;
1142 ERR("%s failed: %s\n", funcerr
, snd_strerror(err
));
1143 if(hp
) snd_pcm_hw_params_free(hp
);
1146 ll_ringbuffer_free(self
->ring
);
1148 snd_pcm_close(self
->pcmHandle
);
1149 self
->pcmHandle
= NULL
;
1151 return ALC_INVALID_VALUE
;
1154 static ALCboolean
ALCcaptureAlsa_start(ALCcaptureAlsa
*self
)
1156 int err
= snd_pcm_prepare(self
->pcmHandle
);
1158 ERR("prepare failed: %s\n", snd_strerror(err
));
1161 err
= snd_pcm_start(self
->pcmHandle
);
1163 ERR("start failed: %s\n", snd_strerror(err
));
1167 aluHandleDisconnect(STATIC_CAST(ALCbackend
, self
)->mDevice
, "Capture state failure: %s",
1172 self
->doCapture
= AL_TRUE
;
1176 static void ALCcaptureAlsa_stop(ALCcaptureAlsa
*self
)
1181 /* OpenAL requires access to unread audio after stopping, but ALSA's
1182 * snd_pcm_drain is unreliable and snd_pcm_drop drops it. Capture what's
1183 * available now so it'll be available later after the drop. */
1184 avail
= ALCcaptureAlsa_availableSamples(self
);
1185 if(!self
->ring
&& avail
> 0)
1187 /* The ring buffer implicitly captures when checking availability.
1188 * Direct access needs to explicitly capture it into temp storage. */
1192 size
= snd_pcm_frames_to_bytes(self
->pcmHandle
, avail
);
1193 ptr
= al_malloc(16, size
);
1196 ALCcaptureAlsa_captureSamples(self
, ptr
, avail
);
1197 al_free(self
->buffer
);
1202 err
= snd_pcm_drop(self
->pcmHandle
);
1204 ERR("drop failed: %s\n", snd_strerror(err
));
1205 self
->doCapture
= AL_FALSE
;
1208 static ALCenum
ALCcaptureAlsa_captureSamples(ALCcaptureAlsa
*self
, ALCvoid
*buffer
, ALCuint samples
)
1210 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
1214 ll_ringbuffer_read(self
->ring
, buffer
, samples
);
1215 return ALC_NO_ERROR
;
1218 self
->last_avail
-= samples
;
1219 while(ATOMIC_LOAD(&device
->Connected
, almemory_order_acquire
) && samples
> 0)
1221 snd_pcm_sframes_t amt
= 0;
1225 /* First get any data stored from the last stop */
1226 amt
= snd_pcm_bytes_to_frames(self
->pcmHandle
, self
->size
);
1227 if((snd_pcm_uframes_t
)amt
> samples
) amt
= samples
;
1229 amt
= snd_pcm_frames_to_bytes(self
->pcmHandle
, amt
);
1230 memcpy(buffer
, self
->buffer
, amt
);
1232 if(self
->size
> amt
)
1234 memmove(self
->buffer
, self
->buffer
+amt
, self
->size
- amt
);
1239 al_free(self
->buffer
);
1240 self
->buffer
= NULL
;
1243 amt
= snd_pcm_bytes_to_frames(self
->pcmHandle
, amt
);
1245 else if(self
->doCapture
)
1246 amt
= snd_pcm_readi(self
->pcmHandle
, buffer
, samples
);
1249 ERR("read error: %s\n", snd_strerror(amt
));
1253 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
));
1265 /* If the amount available is less than what's asked, we lost it
1266 * during recovery. So just give silence instead. */
1267 if((snd_pcm_uframes_t
)amt
< samples
)
1272 buffer
= (ALbyte
*)buffer
+ amt
;
1276 memset(buffer
, ((device
->FmtType
== DevFmtUByte
) ? 0x80 : 0),
1277 snd_pcm_frames_to_bytes(self
->pcmHandle
, samples
));
1279 return ALC_NO_ERROR
;
1282 static ALCuint
ALCcaptureAlsa_availableSamples(ALCcaptureAlsa
*self
)
1284 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
1285 snd_pcm_sframes_t avail
= 0;
1287 if(ATOMIC_LOAD(&device
->Connected
, almemory_order_acquire
) && self
->doCapture
)
1288 avail
= snd_pcm_avail_update(self
->pcmHandle
);
1291 ERR("avail update failed: %s\n", snd_strerror(avail
));
1293 if((avail
=snd_pcm_recover(self
->pcmHandle
, avail
, 1)) >= 0)
1296 avail
= snd_pcm_start(self
->pcmHandle
);
1298 avail
= snd_pcm_avail_update(self
->pcmHandle
);
1302 ERR("restore error: %s\n", snd_strerror(avail
));
1303 aluHandleDisconnect(device
, "Capture recovery failure: %s", snd_strerror(avail
));
1309 if(avail
< 0) avail
= 0;
1310 avail
+= snd_pcm_bytes_to_frames(self
->pcmHandle
, self
->size
);
1311 if(avail
> self
->last_avail
) self
->last_avail
= avail
;
1312 return self
->last_avail
;
1317 ll_ringbuffer_data_t vec
[2];
1318 snd_pcm_sframes_t amt
;
1320 ll_ringbuffer_get_write_vector(self
->ring
, vec
);
1321 if(vec
[0].len
== 0) break;
1323 amt
= (vec
[0].len
< (snd_pcm_uframes_t
)avail
) ?
1324 vec
[0].len
: (snd_pcm_uframes_t
)avail
;
1325 amt
= snd_pcm_readi(self
->pcmHandle
, vec
[0].buf
, amt
);
1328 ERR("read error: %s\n", snd_strerror(amt
));
1332 if((amt
=snd_pcm_recover(self
->pcmHandle
, amt
, 1)) >= 0)
1335 amt
= snd_pcm_start(self
->pcmHandle
);
1337 amt
= snd_pcm_avail_update(self
->pcmHandle
);
1341 ERR("restore error: %s\n", snd_strerror(amt
));
1342 aluHandleDisconnect(device
, "Capture recovery failure: %s", snd_strerror(amt
));
1349 ll_ringbuffer_write_advance(self
->ring
, amt
);
1353 return ll_ringbuffer_read_space(self
->ring
);
1356 static ClockLatency
ALCcaptureAlsa_getClockLatency(ALCcaptureAlsa
*self
)
1358 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
1359 snd_pcm_sframes_t delay
= 0;
1363 ALCcaptureAlsa_lock(self
);
1364 ret
.ClockTime
= GetDeviceClockTime(device
);
1365 if((err
=snd_pcm_delay(self
->pcmHandle
, &delay
)) < 0)
1367 ERR("Failed to get pcm delay: %s\n", snd_strerror(err
));
1370 if(delay
< 0) delay
= 0;
1371 ret
.Latency
= delay
* DEVICE_CLOCK_RES
/ device
->Frequency
;
1372 ALCcaptureAlsa_unlock(self
);
1378 static inline void AppendAllDevicesList2(const DevMap
*entry
)
1379 { AppendAllDevicesList(alstr_get_cstr(entry
->name
)); }
1380 static inline void AppendCaptureDeviceList2(const DevMap
*entry
)
1381 { AppendCaptureDeviceList(alstr_get_cstr(entry
->name
)); }
1383 typedef struct ALCalsaBackendFactory
{
1384 DERIVE_FROM_TYPE(ALCbackendFactory
);
1385 } ALCalsaBackendFactory
;
1386 #define ALCALSABACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCalsaBackendFactory, ALCbackendFactory) } }
1388 static ALCboolean
ALCalsaBackendFactory_init(ALCalsaBackendFactory
* UNUSED(self
))
1390 VECTOR_INIT(PlaybackDevices
);
1391 VECTOR_INIT(CaptureDevices
);
1398 static void ALCalsaBackendFactory_deinit(ALCalsaBackendFactory
* UNUSED(self
))
1400 clear_devlist(&PlaybackDevices
);
1401 VECTOR_DEINIT(PlaybackDevices
);
1403 clear_devlist(&CaptureDevices
);
1404 VECTOR_DEINIT(CaptureDevices
);
1408 CloseLib(alsa_handle
);
1413 static ALCboolean
ALCalsaBackendFactory_querySupport(ALCalsaBackendFactory
* UNUSED(self
), ALCbackend_Type type
)
1415 if(type
== ALCbackend_Playback
|| type
== ALCbackend_Capture
)
1420 static void ALCalsaBackendFactory_probe(ALCalsaBackendFactory
* UNUSED(self
), enum DevProbe type
)
1424 case ALL_DEVICE_PROBE
:
1425 probe_devices(SND_PCM_STREAM_PLAYBACK
, &PlaybackDevices
);
1426 VECTOR_FOR_EACH(const DevMap
, PlaybackDevices
, AppendAllDevicesList2
);
1429 case CAPTURE_DEVICE_PROBE
:
1430 probe_devices(SND_PCM_STREAM_CAPTURE
, &CaptureDevices
);
1431 VECTOR_FOR_EACH(const DevMap
, CaptureDevices
, AppendCaptureDeviceList2
);
1436 static ALCbackend
* ALCalsaBackendFactory_createBackend(ALCalsaBackendFactory
* UNUSED(self
), ALCdevice
*device
, ALCbackend_Type type
)
1438 if(type
== ALCbackend_Playback
)
1440 ALCplaybackAlsa
*backend
;
1441 NEW_OBJ(backend
, ALCplaybackAlsa
)(device
);
1442 if(!backend
) return NULL
;
1443 return STATIC_CAST(ALCbackend
, backend
);
1445 if(type
== ALCbackend_Capture
)
1447 ALCcaptureAlsa
*backend
;
1448 NEW_OBJ(backend
, ALCcaptureAlsa
)(device
);
1449 if(!backend
) return NULL
;
1450 return STATIC_CAST(ALCbackend
, backend
);
1456 DEFINE_ALCBACKENDFACTORY_VTABLE(ALCalsaBackendFactory
);
1459 ALCbackendFactory
*ALCalsaBackendFactory_getFactory(void)
1461 static ALCalsaBackendFactory factory
= ALCALSABACKENDFACTORY_INITIALIZER
;
1462 return STATIC_CAST(ALCbackendFactory
, &factory
);