Print warnings about missing libraries and functions
[openal-soft.git] / Alc / backends / alsa.c
blob2fa806ae3e311f2c8030c6e371b6b2bdbca2871a
1 /**
2 * OpenAL cross platform audio library
3 * Copyright (C) 1999-2007 by authors.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 * Or go to http://www.gnu.org/copyleft/lgpl.html
21 #include "config.h"
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <memory.h>
27 #include "alMain.h"
28 #include "alu.h"
29 #include "threads.h"
30 #include "compat.h"
32 #include "backends/base.h"
34 #include <alsa/asoundlib.h>
37 static const ALCchar alsaDevice[] = "ALSA Default";
40 #ifdef HAVE_DYNLOAD
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);
118 #undef 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
192 #endif
195 static ALCboolean alsa_load(void)
197 ALCboolean error = ALC_FALSE;
199 #ifdef HAVE_DYNLOAD
200 if(!alsa_handle)
202 al_string missing_funcs = AL_STRING_INIT_STATIC();
204 alsa_handle = LoadLib("libasound.so.2");
205 if(!alsa_handle)
207 WARN("Failed to load %s\n", "libasound.so.2");
208 return ALC_FALSE;
211 error = ALC_FALSE;
212 #define LOAD_FUNC(f) do { \
213 p##f = GetSymbol(alsa_handle, #f); \
214 if(p##f == NULL) { \
215 error = ALC_TRUE; \
216 al_string_append_cstr(&missing_funcs, "\n" #f); \
218 } while(0)
219 ALSA_FUNCS(LOAD_FUNC);
220 #undef LOAD_FUNC
222 if(error)
224 WARN("Missing expected functions:%s\n", al_string_get_cstr(missing_funcs));
225 CloseLib(alsa_handle);
226 alsa_handle = NULL;
228 al_string_deinit(&missing_funcs);
230 #endif
232 return !error;
236 typedef struct {
237 al_string name;
238 al_string device_name;
239 } DevMap;
240 TYPEDEF_VECTOR(DevMap, vector_DevMap)
242 static vector_DevMap PlaybackDevices;
243 static vector_DevMap CaptureDevices;
245 static void clear_devlist(vector_DevMap *devlist)
247 #define FREE_DEV(i) do { \
248 AL_STRING_DEINIT((i)->name); \
249 AL_STRING_DEINIT((i)->device_name); \
250 } while(0)
251 VECTOR_FOR_EACH(DevMap, *devlist, FREE_DEV);
252 VECTOR_RESIZE(*devlist, 0, 0);
253 #undef FREE_DEV
257 static const char *prefix_name(snd_pcm_stream_t stream)
259 assert(stream == SND_PCM_STREAM_PLAYBACK || stream == SND_PCM_STREAM_CAPTURE);
260 return (stream==SND_PCM_STREAM_PLAYBACK) ? "device-prefix" : "capture-prefix";
263 static void probe_devices(snd_pcm_stream_t stream, vector_DevMap *DeviceList)
265 const char *main_prefix = "plughw:";
266 snd_ctl_t *handle;
267 snd_ctl_card_info_t *info;
268 snd_pcm_info_t *pcminfo;
269 int card, err, dev;
270 DevMap entry;
272 clear_devlist(DeviceList);
274 snd_ctl_card_info_malloc(&info);
275 snd_pcm_info_malloc(&pcminfo);
277 AL_STRING_INIT(entry.name);
278 AL_STRING_INIT(entry.device_name);
279 al_string_copy_cstr(&entry.name, alsaDevice);
280 al_string_copy_cstr(&entry.device_name, GetConfigValue(NULL, "alsa", (stream==SND_PCM_STREAM_PLAYBACK) ?
281 "device" : "capture", "default"));
282 VECTOR_PUSH_BACK(*DeviceList, entry);
284 card = -1;
285 if((err=snd_card_next(&card)) < 0)
286 ERR("Failed to find a card: %s\n", snd_strerror(err));
287 ConfigValueStr(NULL, "alsa", prefix_name(stream), &main_prefix);
288 while(card >= 0)
290 const char *card_prefix = main_prefix;
291 const char *cardname, *cardid;
292 char name[256];
294 snprintf(name, sizeof(name), "hw:%d", card);
295 if((err = snd_ctl_open(&handle, name, 0)) < 0)
297 ERR("control open (hw:%d): %s\n", card, snd_strerror(err));
298 goto next_card;
300 if((err = snd_ctl_card_info(handle, info)) < 0)
302 ERR("control hardware info (hw:%d): %s\n", card, snd_strerror(err));
303 snd_ctl_close(handle);
304 goto next_card;
307 cardname = snd_ctl_card_info_get_name(info);
308 cardid = snd_ctl_card_info_get_id(info);
310 snprintf(name, sizeof(name), "%s-%s", prefix_name(stream), cardid);
311 ConfigValueStr(NULL, "alsa", name, &card_prefix);
313 dev = -1;
314 while(1)
316 const char *device_prefix = card_prefix;
317 const char *devname;
318 char device[128];
320 if(snd_ctl_pcm_next_device(handle, &dev) < 0)
321 ERR("snd_ctl_pcm_next_device failed\n");
322 if(dev < 0)
323 break;
325 snd_pcm_info_set_device(pcminfo, dev);
326 snd_pcm_info_set_subdevice(pcminfo, 0);
327 snd_pcm_info_set_stream(pcminfo, stream);
328 if((err = snd_ctl_pcm_info(handle, pcminfo)) < 0) {
329 if(err != -ENOENT)
330 ERR("control digital audio info (hw:%d): %s\n", card, snd_strerror(err));
331 continue;
334 devname = snd_pcm_info_get_name(pcminfo);
336 snprintf(name, sizeof(name), "%s-%s-%d", prefix_name(stream), cardid, dev);
337 ConfigValueStr(NULL, "alsa", name, &device_prefix);
339 snprintf(name, sizeof(name), "%s, %s (CARD=%s,DEV=%d)",
340 cardname, devname, cardid, dev);
341 snprintf(device, sizeof(device), "%sCARD=%s,DEV=%d",
342 device_prefix, cardid, dev);
344 TRACE("Got device \"%s\", \"%s\"\n", name, device);
345 AL_STRING_INIT(entry.name);
346 AL_STRING_INIT(entry.device_name);
347 al_string_copy_cstr(&entry.name, name);
348 al_string_copy_cstr(&entry.device_name, device);
349 VECTOR_PUSH_BACK(*DeviceList, entry);
351 snd_ctl_close(handle);
352 next_card:
353 if(snd_card_next(&card) < 0) {
354 ERR("snd_card_next failed\n");
355 break;
359 snd_pcm_info_free(pcminfo);
360 snd_ctl_card_info_free(info);
364 static int verify_state(snd_pcm_t *handle)
366 snd_pcm_state_t state = snd_pcm_state(handle);
367 int err;
369 switch(state)
371 case SND_PCM_STATE_OPEN:
372 case SND_PCM_STATE_SETUP:
373 case SND_PCM_STATE_PREPARED:
374 case SND_PCM_STATE_RUNNING:
375 case SND_PCM_STATE_DRAINING:
376 case SND_PCM_STATE_PAUSED:
377 /* All Okay */
378 break;
380 case SND_PCM_STATE_XRUN:
381 if((err=snd_pcm_recover(handle, -EPIPE, 1)) < 0)
382 return err;
383 break;
384 case SND_PCM_STATE_SUSPENDED:
385 if((err=snd_pcm_recover(handle, -ESTRPIPE, 1)) < 0)
386 return err;
387 break;
388 case SND_PCM_STATE_DISCONNECTED:
389 return -ENODEV;
392 return state;
396 typedef struct ALCplaybackAlsa {
397 DERIVE_FROM_TYPE(ALCbackend);
399 snd_pcm_t *pcmHandle;
401 ALvoid *buffer;
402 ALsizei size;
404 volatile int killNow;
405 althrd_t thread;
406 } ALCplaybackAlsa;
408 static int ALCplaybackAlsa_mixerProc(void *ptr);
409 static int ALCplaybackAlsa_mixerNoMMapProc(void *ptr);
411 static void ALCplaybackAlsa_Construct(ALCplaybackAlsa *self, ALCdevice *device);
412 static DECLARE_FORWARD(ALCplaybackAlsa, ALCbackend, void, Destruct)
413 static ALCenum ALCplaybackAlsa_open(ALCplaybackAlsa *self, const ALCchar *name);
414 static void ALCplaybackAlsa_close(ALCplaybackAlsa *self);
415 static ALCboolean ALCplaybackAlsa_reset(ALCplaybackAlsa *self);
416 static ALCboolean ALCplaybackAlsa_start(ALCplaybackAlsa *self);
417 static void ALCplaybackAlsa_stop(ALCplaybackAlsa *self);
418 static DECLARE_FORWARD2(ALCplaybackAlsa, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
419 static DECLARE_FORWARD(ALCplaybackAlsa, ALCbackend, ALCuint, availableSamples)
420 static ClockLatency ALCplaybackAlsa_getClockLatency(ALCplaybackAlsa *self);
421 static DECLARE_FORWARD(ALCplaybackAlsa, ALCbackend, void, lock)
422 static DECLARE_FORWARD(ALCplaybackAlsa, ALCbackend, void, unlock)
423 DECLARE_DEFAULT_ALLOCATORS(ALCplaybackAlsa)
425 DEFINE_ALCBACKEND_VTABLE(ALCplaybackAlsa);
428 static void ALCplaybackAlsa_Construct(ALCplaybackAlsa *self, ALCdevice *device)
430 ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
431 SET_VTABLE2(ALCplaybackAlsa, ALCbackend, self);
435 static int ALCplaybackAlsa_mixerProc(void *ptr)
437 ALCplaybackAlsa *self = (ALCplaybackAlsa*)ptr;
438 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
439 const snd_pcm_channel_area_t *areas = NULL;
440 snd_pcm_uframes_t update_size, num_updates;
441 snd_pcm_sframes_t avail, commitres;
442 snd_pcm_uframes_t offset, frames;
443 char *WritePtr;
444 int err;
446 SetRTPriority();
447 althrd_setname(althrd_current(), MIXER_THREAD_NAME);
449 update_size = device->UpdateSize;
450 num_updates = device->NumUpdates;
451 while(!self->killNow)
453 int state = verify_state(self->pcmHandle);
454 if(state < 0)
456 ERR("Invalid state detected: %s\n", snd_strerror(state));
457 ALCplaybackAlsa_lock(self);
458 aluHandleDisconnect(device);
459 ALCplaybackAlsa_unlock(self);
460 break;
463 avail = snd_pcm_avail_update(self->pcmHandle);
464 if(avail < 0)
466 ERR("available update failed: %s\n", snd_strerror(avail));
467 continue;
470 if((snd_pcm_uframes_t)avail > update_size*(num_updates+1))
472 WARN("available samples exceeds the buffer size\n");
473 snd_pcm_reset(self->pcmHandle);
474 continue;
477 // make sure there's frames to process
478 if((snd_pcm_uframes_t)avail < update_size)
480 if(state != SND_PCM_STATE_RUNNING)
482 err = snd_pcm_start(self->pcmHandle);
483 if(err < 0)
485 ERR("start failed: %s\n", snd_strerror(err));
486 continue;
489 if(snd_pcm_wait(self->pcmHandle, 1000) == 0)
490 ERR("Wait timeout... buffer size too low?\n");
491 continue;
493 avail -= avail%update_size;
495 // it is possible that contiguous areas are smaller, thus we use a loop
496 ALCplaybackAlsa_lock(self);
497 while(avail > 0)
499 frames = avail;
501 err = snd_pcm_mmap_begin(self->pcmHandle, &areas, &offset, &frames);
502 if(err < 0)
504 ERR("mmap begin error: %s\n", snd_strerror(err));
505 break;
508 WritePtr = (char*)areas->addr + (offset * areas->step / 8);
509 aluMixData(device, WritePtr, frames);
511 commitres = snd_pcm_mmap_commit(self->pcmHandle, offset, frames);
512 if(commitres < 0 || (commitres-frames) != 0)
514 ERR("mmap commit error: %s\n",
515 snd_strerror(commitres >= 0 ? -EPIPE : commitres));
516 break;
519 avail -= frames;
521 ALCplaybackAlsa_unlock(self);
524 return 0;
527 static int ALCplaybackAlsa_mixerNoMMapProc(void *ptr)
529 ALCplaybackAlsa *self = (ALCplaybackAlsa*)ptr;
530 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
531 snd_pcm_uframes_t update_size, num_updates;
532 snd_pcm_sframes_t avail;
533 char *WritePtr;
534 int err;
536 SetRTPriority();
537 althrd_setname(althrd_current(), MIXER_THREAD_NAME);
539 update_size = device->UpdateSize;
540 num_updates = device->NumUpdates;
541 while(!self->killNow)
543 int state = verify_state(self->pcmHandle);
544 if(state < 0)
546 ERR("Invalid state detected: %s\n", snd_strerror(state));
547 ALCplaybackAlsa_lock(self);
548 aluHandleDisconnect(device);
549 ALCplaybackAlsa_unlock(self);
550 break;
553 avail = snd_pcm_avail_update(self->pcmHandle);
554 if(avail < 0)
556 ERR("available update failed: %s\n", snd_strerror(avail));
557 continue;
560 if((snd_pcm_uframes_t)avail > update_size*num_updates)
562 WARN("available samples exceeds the buffer size\n");
563 snd_pcm_reset(self->pcmHandle);
564 continue;
567 if((snd_pcm_uframes_t)avail < update_size)
569 if(state != SND_PCM_STATE_RUNNING)
571 err = snd_pcm_start(self->pcmHandle);
572 if(err < 0)
574 ERR("start failed: %s\n", snd_strerror(err));
575 continue;
578 if(snd_pcm_wait(self->pcmHandle, 1000) == 0)
579 ERR("Wait timeout... buffer size too low?\n");
580 continue;
583 ALCplaybackAlsa_lock(self);
584 WritePtr = self->buffer;
585 avail = snd_pcm_bytes_to_frames(self->pcmHandle, self->size);
586 aluMixData(device, WritePtr, avail);
588 while(avail > 0)
590 int ret = snd_pcm_writei(self->pcmHandle, WritePtr, avail);
591 switch (ret)
593 case -EAGAIN:
594 continue;
595 #if ESTRPIPE != EPIPE
596 case -ESTRPIPE:
597 #endif
598 case -EPIPE:
599 case -EINTR:
600 ret = snd_pcm_recover(self->pcmHandle, ret, 1);
601 if(ret < 0)
602 avail = 0;
603 break;
604 default:
605 if (ret >= 0)
607 WritePtr += snd_pcm_frames_to_bytes(self->pcmHandle, ret);
608 avail -= ret;
610 break;
612 if (ret < 0)
614 ret = snd_pcm_prepare(self->pcmHandle);
615 if(ret < 0)
616 break;
619 ALCplaybackAlsa_unlock(self);
622 return 0;
626 static ALCenum ALCplaybackAlsa_open(ALCplaybackAlsa *self, const ALCchar *name)
628 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
629 const char *driver = NULL;
630 int err;
632 if(name)
634 const DevMap *iter;
636 if(VECTOR_SIZE(PlaybackDevices) == 0)
637 probe_devices(SND_PCM_STREAM_PLAYBACK, &PlaybackDevices);
639 #define MATCH_NAME(i) (al_string_cmp_cstr((i)->name, name) == 0)
640 VECTOR_FIND_IF(iter, const DevMap, PlaybackDevices, MATCH_NAME);
641 #undef MATCH_NAME
642 if(iter == VECTOR_END(PlaybackDevices))
643 return ALC_INVALID_VALUE;
644 driver = al_string_get_cstr(iter->device_name);
646 else
648 name = alsaDevice;
649 driver = GetConfigValue(NULL, "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);
654 if(err < 0)
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);
665 return ALC_NO_ERROR;
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;
683 unsigned int rate;
684 const char *funcerr;
685 int allowmmap;
686 int dir;
687 int err;
689 switch(device->FmtType)
691 case DevFmtByte:
692 format = SND_PCM_FORMAT_S8;
693 break;
694 case DevFmtUByte:
695 format = SND_PCM_FORMAT_U8;
696 break;
697 case DevFmtShort:
698 format = SND_PCM_FORMAT_S16;
699 break;
700 case DevFmtUShort:
701 format = SND_PCM_FORMAT_U16;
702 break;
703 case DevFmtInt:
704 format = SND_PCM_FORMAT_S32;
705 break;
706 case DevFmtUInt:
707 format = SND_PCM_FORMAT_U32;
708 break;
709 case DevFmtFloat:
710 format = SND_PCM_FORMAT_FLOAT;
711 break;
714 allowmmap = GetConfigValueBool(al_string_get_cstr(device->DeviceName), "alsa", "mmap", 1);
715 periods = device->NumUpdates;
716 periodLen = (ALuint64)device->UpdateSize * 1000000 / device->Frequency;
717 bufferLen = periodLen * periods;
718 rate = device->Frequency;
720 snd_pcm_hw_params_malloc(&hp);
721 #define CHECK(x) if((funcerr=#x),(err=(x)) < 0) goto error
722 CHECK(snd_pcm_hw_params_any(self->pcmHandle, hp));
723 /* set interleaved access */
724 if(!allowmmap || snd_pcm_hw_params_set_access(self->pcmHandle, hp, SND_PCM_ACCESS_MMAP_INTERLEAVED) < 0)
726 /* No mmap */
727 CHECK(snd_pcm_hw_params_set_access(self->pcmHandle, hp, SND_PCM_ACCESS_RW_INTERLEAVED));
729 /* test and set format (implicitly sets sample bits) */
730 if(snd_pcm_hw_params_test_format(self->pcmHandle, hp, format) < 0)
732 static const struct {
733 snd_pcm_format_t format;
734 enum DevFmtType fmttype;
735 } formatlist[] = {
736 { SND_PCM_FORMAT_FLOAT, DevFmtFloat },
737 { SND_PCM_FORMAT_S32, DevFmtInt },
738 { SND_PCM_FORMAT_U32, DevFmtUInt },
739 { SND_PCM_FORMAT_S16, DevFmtShort },
740 { SND_PCM_FORMAT_U16, DevFmtUShort },
741 { SND_PCM_FORMAT_S8, DevFmtByte },
742 { SND_PCM_FORMAT_U8, DevFmtUByte },
744 size_t k;
746 for(k = 0;k < COUNTOF(formatlist);k++)
748 format = formatlist[k].format;
749 if(snd_pcm_hw_params_test_format(self->pcmHandle, hp, format) >= 0)
751 device->FmtType = formatlist[k].fmttype;
752 break;
756 CHECK(snd_pcm_hw_params_set_format(self->pcmHandle, hp, format));
757 /* test and set channels (implicitly sets frame bits) */
758 if(snd_pcm_hw_params_test_channels(self->pcmHandle, hp, ChannelsFromDevFmt(device->FmtChans)) < 0)
760 static const enum DevFmtChannels channellist[] = {
761 DevFmtStereo,
762 DevFmtQuad,
763 DevFmtX51,
764 DevFmtX71,
765 DevFmtMono,
767 size_t k;
769 for(k = 0;k < COUNTOF(channellist);k++)
771 if(snd_pcm_hw_params_test_channels(self->pcmHandle, hp, ChannelsFromDevFmt(channellist[k])) >= 0)
773 device->FmtChans = channellist[k];
774 break;
778 CHECK(snd_pcm_hw_params_set_channels(self->pcmHandle, hp, ChannelsFromDevFmt(device->FmtChans)));
779 /* set rate (implicitly constrains period/buffer parameters) */
780 if(!GetConfigValueBool(al_string_get_cstr(device->DeviceName), "alsa", "allow-resampler", 0) ||
781 !(device->Flags&DEVICE_FREQUENCY_REQUEST))
783 if(snd_pcm_hw_params_set_rate_resample(self->pcmHandle, hp, 0) < 0)
784 ERR("Failed to disable ALSA resampler\n");
786 else if(snd_pcm_hw_params_set_rate_resample(self->pcmHandle, hp, 1) < 0)
787 ERR("Failed to enable ALSA resampler\n");
788 CHECK(snd_pcm_hw_params_set_rate_near(self->pcmHandle, hp, &rate, NULL));
789 /* set buffer time (implicitly constrains period/buffer parameters) */
790 if((err=snd_pcm_hw_params_set_buffer_time_near(self->pcmHandle, hp, &bufferLen, NULL)) < 0)
791 ERR("snd_pcm_hw_params_set_buffer_time_near failed: %s\n", snd_strerror(err));
792 /* set period time (implicitly sets buffer size/bytes/time and period size/bytes) */
793 if((err=snd_pcm_hw_params_set_period_time_near(self->pcmHandle, hp, &periodLen, NULL)) < 0)
794 ERR("snd_pcm_hw_params_set_period_time_near failed: %s\n", snd_strerror(err));
795 /* install and prepare hardware configuration */
796 CHECK(snd_pcm_hw_params(self->pcmHandle, hp));
797 /* retrieve configuration info */
798 CHECK(snd_pcm_hw_params_get_access(hp, &access));
799 CHECK(snd_pcm_hw_params_get_period_size(hp, &periodSizeInFrames, NULL));
800 CHECK(snd_pcm_hw_params_get_periods(hp, &periods, &dir));
801 if(dir != 0)
802 WARN("Inexact period count: %u (%d)\n", periods, dir);
804 snd_pcm_hw_params_free(hp);
805 hp = NULL;
806 snd_pcm_sw_params_malloc(&sp);
808 CHECK(snd_pcm_sw_params_current(self->pcmHandle, sp));
809 CHECK(snd_pcm_sw_params_set_avail_min(self->pcmHandle, sp, periodSizeInFrames));
810 CHECK(snd_pcm_sw_params_set_stop_threshold(self->pcmHandle, sp, periodSizeInFrames*periods));
811 CHECK(snd_pcm_sw_params(self->pcmHandle, sp));
812 #undef CHECK
813 snd_pcm_sw_params_free(sp);
814 sp = NULL;
816 device->NumUpdates = periods;
817 device->UpdateSize = periodSizeInFrames;
818 device->Frequency = rate;
820 SetDefaultChannelOrder(device);
822 return ALC_TRUE;
824 error:
825 ERR("%s failed: %s\n", funcerr, snd_strerror(err));
826 if(hp) snd_pcm_hw_params_free(hp);
827 if(sp) snd_pcm_sw_params_free(sp);
828 return ALC_FALSE;
831 static ALCboolean ALCplaybackAlsa_start(ALCplaybackAlsa *self)
833 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
834 int (*thread_func)(void*) = NULL;
835 snd_pcm_hw_params_t *hp = NULL;
836 snd_pcm_access_t access;
837 const char *funcerr;
838 int err;
840 snd_pcm_hw_params_malloc(&hp);
841 #define CHECK(x) if((funcerr=#x),(err=(x)) < 0) goto error
842 CHECK(snd_pcm_hw_params_current(self->pcmHandle, hp));
843 /* retrieve configuration info */
844 CHECK(snd_pcm_hw_params_get_access(hp, &access));
845 #undef CHECK
846 snd_pcm_hw_params_free(hp);
847 hp = NULL;
849 self->size = snd_pcm_frames_to_bytes(self->pcmHandle, device->UpdateSize);
850 if(access == SND_PCM_ACCESS_RW_INTERLEAVED)
852 self->buffer = al_malloc(16, self->size);
853 if(!self->buffer)
855 ERR("buffer malloc failed\n");
856 return ALC_FALSE;
858 thread_func = ALCplaybackAlsa_mixerNoMMapProc;
860 else
862 err = snd_pcm_prepare(self->pcmHandle);
863 if(err < 0)
865 ERR("snd_pcm_prepare(data->pcmHandle) failed: %s\n", snd_strerror(err));
866 return ALC_FALSE;
868 thread_func = ALCplaybackAlsa_mixerProc;
870 self->killNow = 0;
871 if(althrd_create(&self->thread, thread_func, self) != althrd_success)
873 ERR("Could not create playback thread\n");
874 al_free(self->buffer);
875 self->buffer = NULL;
876 return ALC_FALSE;
879 return ALC_TRUE;
881 error:
882 ERR("%s failed: %s\n", funcerr, snd_strerror(err));
883 if(hp) snd_pcm_hw_params_free(hp);
884 return ALC_FALSE;
887 static void ALCplaybackAlsa_stop(ALCplaybackAlsa *self)
889 int res;
891 if(self->killNow)
892 return;
894 self->killNow = 1;
895 althrd_join(self->thread, &res);
897 al_free(self->buffer);
898 self->buffer = NULL;
901 static ClockLatency ALCplaybackAlsa_getClockLatency(ALCplaybackAlsa *self)
903 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
904 snd_pcm_sframes_t delay = 0;
905 ClockLatency ret;
906 int err;
908 ALCplaybackAlsa_lock(self);
909 ret.ClockTime = GetDeviceClockTime(device);
910 if((err=snd_pcm_delay(self->pcmHandle, &delay)) < 0)
912 ERR("Failed to get pcm delay: %s\n", snd_strerror(err));
913 delay = 0;
915 if(delay < 0) delay = 0;
916 ret.Latency = delay * DEVICE_CLOCK_RES / device->Frequency;
917 ALCplaybackAlsa_unlock(self);
919 return ret;
923 typedef struct ALCcaptureAlsa {
924 DERIVE_FROM_TYPE(ALCbackend);
926 snd_pcm_t *pcmHandle;
928 ALvoid *buffer;
929 ALsizei size;
931 ALboolean doCapture;
932 ll_ringbuffer_t *ring;
934 snd_pcm_sframes_t last_avail;
935 } ALCcaptureAlsa;
937 static void ALCcaptureAlsa_Construct(ALCcaptureAlsa *self, ALCdevice *device);
938 static DECLARE_FORWARD(ALCcaptureAlsa, ALCbackend, void, Destruct)
939 static ALCenum ALCcaptureAlsa_open(ALCcaptureAlsa *self, const ALCchar *name);
940 static void ALCcaptureAlsa_close(ALCcaptureAlsa *self);
941 static DECLARE_FORWARD(ALCcaptureAlsa, ALCbackend, ALCboolean, reset)
942 static ALCboolean ALCcaptureAlsa_start(ALCcaptureAlsa *self);
943 static void ALCcaptureAlsa_stop(ALCcaptureAlsa *self);
944 static ALCenum ALCcaptureAlsa_captureSamples(ALCcaptureAlsa *self, ALCvoid *buffer, ALCuint samples);
945 static ALCuint ALCcaptureAlsa_availableSamples(ALCcaptureAlsa *self);
946 static ClockLatency ALCcaptureAlsa_getClockLatency(ALCcaptureAlsa *self);
947 static DECLARE_FORWARD(ALCcaptureAlsa, ALCbackend, void, lock)
948 static DECLARE_FORWARD(ALCcaptureAlsa, ALCbackend, void, unlock)
949 DECLARE_DEFAULT_ALLOCATORS(ALCcaptureAlsa)
951 DEFINE_ALCBACKEND_VTABLE(ALCcaptureAlsa);
954 static void ALCcaptureAlsa_Construct(ALCcaptureAlsa *self, ALCdevice *device)
956 ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
957 SET_VTABLE2(ALCcaptureAlsa, ALCbackend, self);
961 static ALCenum ALCcaptureAlsa_open(ALCcaptureAlsa *self, const ALCchar *name)
963 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
964 const char *driver = NULL;
965 snd_pcm_hw_params_t *hp;
966 snd_pcm_uframes_t bufferSizeInFrames;
967 snd_pcm_uframes_t periodSizeInFrames;
968 ALboolean needring = AL_FALSE;
969 snd_pcm_format_t format = -1;
970 const char *funcerr;
971 int err;
973 if(name)
975 const DevMap *iter;
977 if(VECTOR_SIZE(CaptureDevices) == 0)
978 probe_devices(SND_PCM_STREAM_CAPTURE, &CaptureDevices);
980 #define MATCH_NAME(i) (al_string_cmp_cstr((i)->name, name) == 0)
981 VECTOR_FIND_IF(iter, const DevMap, CaptureDevices, MATCH_NAME);
982 #undef MATCH_NAME
983 if(iter == VECTOR_END(CaptureDevices))
984 return ALC_INVALID_VALUE;
985 driver = al_string_get_cstr(iter->device_name);
987 else
989 name = alsaDevice;
990 driver = GetConfigValue(NULL, "alsa", "capture", "default");
993 TRACE("Opening device \"%s\"\n", driver);
994 err = snd_pcm_open(&self->pcmHandle, driver, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK);
995 if(err < 0)
997 ERR("Could not open capture device '%s': %s\n", driver, snd_strerror(err));
998 return ALC_INVALID_VALUE;
1001 /* Free alsa's global config tree. Otherwise valgrind reports a ton of leaks. */
1002 snd_config_update_free_global();
1004 switch(device->FmtType)
1006 case DevFmtByte:
1007 format = SND_PCM_FORMAT_S8;
1008 break;
1009 case DevFmtUByte:
1010 format = SND_PCM_FORMAT_U8;
1011 break;
1012 case DevFmtShort:
1013 format = SND_PCM_FORMAT_S16;
1014 break;
1015 case DevFmtUShort:
1016 format = SND_PCM_FORMAT_U16;
1017 break;
1018 case DevFmtInt:
1019 format = SND_PCM_FORMAT_S32;
1020 break;
1021 case DevFmtUInt:
1022 format = SND_PCM_FORMAT_U32;
1023 break;
1024 case DevFmtFloat:
1025 format = SND_PCM_FORMAT_FLOAT;
1026 break;
1029 funcerr = NULL;
1030 bufferSizeInFrames = maxu(device->UpdateSize*device->NumUpdates,
1031 100*device->Frequency/1000);
1032 periodSizeInFrames = minu(bufferSizeInFrames, 25*device->Frequency/1000);
1034 snd_pcm_hw_params_malloc(&hp);
1035 #define CHECK(x) if((funcerr=#x),(err=(x)) < 0) goto error
1036 CHECK(snd_pcm_hw_params_any(self->pcmHandle, hp));
1037 /* set interleaved access */
1038 CHECK(snd_pcm_hw_params_set_access(self->pcmHandle, hp, SND_PCM_ACCESS_RW_INTERLEAVED));
1039 /* set format (implicitly sets sample bits) */
1040 CHECK(snd_pcm_hw_params_set_format(self->pcmHandle, hp, format));
1041 /* set channels (implicitly sets frame bits) */
1042 CHECK(snd_pcm_hw_params_set_channels(self->pcmHandle, hp, ChannelsFromDevFmt(device->FmtChans)));
1043 /* set rate (implicitly constrains period/buffer parameters) */
1044 CHECK(snd_pcm_hw_params_set_rate(self->pcmHandle, hp, device->Frequency, 0));
1045 /* set buffer size in frame units (implicitly sets period size/bytes/time and buffer time/bytes) */
1046 if(snd_pcm_hw_params_set_buffer_size_min(self->pcmHandle, hp, &bufferSizeInFrames) < 0)
1048 TRACE("Buffer too large, using intermediate ring buffer\n");
1049 needring = AL_TRUE;
1050 CHECK(snd_pcm_hw_params_set_buffer_size_near(self->pcmHandle, hp, &bufferSizeInFrames));
1052 /* set buffer size in frame units (implicitly sets period size/bytes/time and buffer time/bytes) */
1053 CHECK(snd_pcm_hw_params_set_period_size_near(self->pcmHandle, hp, &periodSizeInFrames, NULL));
1054 /* install and prepare hardware configuration */
1055 CHECK(snd_pcm_hw_params(self->pcmHandle, hp));
1056 /* retrieve configuration info */
1057 CHECK(snd_pcm_hw_params_get_period_size(hp, &periodSizeInFrames, NULL));
1058 #undef CHECK
1059 snd_pcm_hw_params_free(hp);
1060 hp = NULL;
1062 if(needring)
1064 self->ring = ll_ringbuffer_create(
1065 device->UpdateSize*device->NumUpdates + 1,
1066 FrameSizeFromDevFmt(device->FmtChans, device->FmtType)
1068 if(!self->ring)
1070 ERR("ring buffer create failed\n");
1071 goto error2;
1075 al_string_copy_cstr(&device->DeviceName, name);
1077 return ALC_NO_ERROR;
1079 error:
1080 ERR("%s failed: %s\n", funcerr, snd_strerror(err));
1081 if(hp) snd_pcm_hw_params_free(hp);
1083 error2:
1084 ll_ringbuffer_free(self->ring);
1085 self->ring = NULL;
1086 snd_pcm_close(self->pcmHandle);
1088 return ALC_INVALID_VALUE;
1091 static void ALCcaptureAlsa_close(ALCcaptureAlsa *self)
1093 snd_pcm_close(self->pcmHandle);
1094 ll_ringbuffer_free(self->ring);
1096 al_free(self->buffer);
1097 self->buffer = NULL;
1100 static ALCboolean ALCcaptureAlsa_start(ALCcaptureAlsa *self)
1102 int err = snd_pcm_start(self->pcmHandle);
1103 if(err < 0)
1105 ERR("start failed: %s\n", snd_strerror(err));
1106 aluHandleDisconnect(STATIC_CAST(ALCbackend, self)->mDevice);
1107 return ALC_FALSE;
1110 self->doCapture = AL_TRUE;
1111 return ALC_TRUE;
1114 static void ALCcaptureAlsa_stop(ALCcaptureAlsa *self)
1116 ALCuint avail;
1117 int err;
1119 /* OpenAL requires access to unread audio after stopping, but ALSA's
1120 * snd_pcm_drain is unreliable and snd_pcm_drop drops it. Capture what's
1121 * available now so it'll be available later after the drop. */
1122 avail = ALCcaptureAlsa_availableSamples(self);
1123 if(!self->ring && avail > 0)
1125 /* The ring buffer implicitly captures when checking availability.
1126 * Direct access needs to explicitly capture it into temp storage. */
1127 ALsizei size;
1128 void *ptr;
1130 size = snd_pcm_frames_to_bytes(self->pcmHandle, avail);
1131 ptr = al_malloc(16, size);
1132 if(ptr)
1134 ALCcaptureAlsa_captureSamples(self, ptr, avail);
1135 al_free(self->buffer);
1136 self->buffer = ptr;
1137 self->size = size;
1140 err = snd_pcm_drop(self->pcmHandle);
1141 if(err < 0)
1142 ERR("drop failed: %s\n", snd_strerror(err));
1143 self->doCapture = AL_FALSE;
1146 static ALCenum ALCcaptureAlsa_captureSamples(ALCcaptureAlsa *self, ALCvoid *buffer, ALCuint samples)
1148 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
1150 if(self->ring)
1152 ll_ringbuffer_read(self->ring, buffer, samples);
1153 return ALC_NO_ERROR;
1156 self->last_avail -= samples;
1157 while(device->Connected && samples > 0)
1159 snd_pcm_sframes_t amt = 0;
1161 if(self->size > 0)
1163 /* First get any data stored from the last stop */
1164 amt = snd_pcm_bytes_to_frames(self->pcmHandle, self->size);
1165 if((snd_pcm_uframes_t)amt > samples) amt = samples;
1167 amt = snd_pcm_frames_to_bytes(self->pcmHandle, amt);
1168 memcpy(buffer, self->buffer, amt);
1170 if(self->size > amt)
1172 memmove(self->buffer, self->buffer+amt, self->size - amt);
1173 self->size -= amt;
1175 else
1177 al_free(self->buffer);
1178 self->buffer = NULL;
1179 self->size = 0;
1181 amt = snd_pcm_bytes_to_frames(self->pcmHandle, amt);
1183 else if(self->doCapture)
1184 amt = snd_pcm_readi(self->pcmHandle, buffer, samples);
1185 if(amt < 0)
1187 ERR("read error: %s\n", snd_strerror(amt));
1189 if(amt == -EAGAIN)
1190 continue;
1191 if((amt=snd_pcm_recover(self->pcmHandle, amt, 1)) >= 0)
1193 amt = snd_pcm_start(self->pcmHandle);
1194 if(amt >= 0)
1195 amt = snd_pcm_avail_update(self->pcmHandle);
1197 if(amt < 0)
1199 ERR("restore error: %s\n", snd_strerror(amt));
1200 aluHandleDisconnect(device);
1201 break;
1203 /* If the amount available is less than what's asked, we lost it
1204 * during recovery. So just give silence instead. */
1205 if((snd_pcm_uframes_t)amt < samples)
1206 break;
1207 continue;
1210 buffer = (ALbyte*)buffer + amt;
1211 samples -= amt;
1213 if(samples > 0)
1214 memset(buffer, ((device->FmtType == DevFmtUByte) ? 0x80 : 0),
1215 snd_pcm_frames_to_bytes(self->pcmHandle, samples));
1217 return ALC_NO_ERROR;
1220 static ALCuint ALCcaptureAlsa_availableSamples(ALCcaptureAlsa *self)
1222 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
1223 snd_pcm_sframes_t avail = 0;
1225 if(device->Connected && self->doCapture)
1226 avail = snd_pcm_avail_update(self->pcmHandle);
1227 if(avail < 0)
1229 ERR("avail update failed: %s\n", snd_strerror(avail));
1231 if((avail=snd_pcm_recover(self->pcmHandle, avail, 1)) >= 0)
1233 if(self->doCapture)
1234 avail = snd_pcm_start(self->pcmHandle);
1235 if(avail >= 0)
1236 avail = snd_pcm_avail_update(self->pcmHandle);
1238 if(avail < 0)
1240 ERR("restore error: %s\n", snd_strerror(avail));
1241 aluHandleDisconnect(device);
1245 if(!self->ring)
1247 if(avail < 0) avail = 0;
1248 avail += snd_pcm_bytes_to_frames(self->pcmHandle, self->size);
1249 if(avail > self->last_avail) self->last_avail = avail;
1250 return self->last_avail;
1253 while(avail > 0)
1255 ll_ringbuffer_data_t vec[2];
1256 snd_pcm_sframes_t amt;
1258 ll_ringbuffer_get_write_vector(self->ring, vec);
1259 if(vec[0].len == 0) break;
1261 amt = (vec[0].len < (snd_pcm_uframes_t)avail) ?
1262 vec[0].len : (snd_pcm_uframes_t)avail;
1263 amt = snd_pcm_readi(self->pcmHandle, vec[0].buf, amt);
1264 if(amt < 0)
1266 ERR("read error: %s\n", snd_strerror(amt));
1268 if(amt == -EAGAIN)
1269 continue;
1270 if((amt=snd_pcm_recover(self->pcmHandle, amt, 1)) >= 0)
1272 if(self->doCapture)
1273 amt = snd_pcm_start(self->pcmHandle);
1274 if(amt >= 0)
1275 amt = snd_pcm_avail_update(self->pcmHandle);
1277 if(amt < 0)
1279 ERR("restore error: %s\n", snd_strerror(amt));
1280 aluHandleDisconnect(device);
1281 break;
1283 avail = amt;
1284 continue;
1287 ll_ringbuffer_write_advance(self->ring, amt);
1288 avail -= amt;
1291 return ll_ringbuffer_read_space(self->ring);
1294 static ClockLatency ALCcaptureAlsa_getClockLatency(ALCcaptureAlsa *self)
1296 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
1297 snd_pcm_sframes_t delay = 0;
1298 ClockLatency ret;
1299 int err;
1301 ALCcaptureAlsa_lock(self);
1302 ret.ClockTime = GetDeviceClockTime(device);
1303 if((err=snd_pcm_delay(self->pcmHandle, &delay)) < 0)
1305 ERR("Failed to get pcm delay: %s\n", snd_strerror(err));
1306 delay = 0;
1308 if(delay < 0) delay = 0;
1309 ret.Latency = delay * DEVICE_CLOCK_RES / device->Frequency;
1310 ALCcaptureAlsa_unlock(self);
1312 return ret;
1316 static inline void AppendAllDevicesList2(const DevMap *entry)
1317 { AppendAllDevicesList(al_string_get_cstr(entry->name)); }
1318 static inline void AppendCaptureDeviceList2(const DevMap *entry)
1319 { AppendCaptureDeviceList(al_string_get_cstr(entry->name)); }
1321 typedef struct ALCalsaBackendFactory {
1322 DERIVE_FROM_TYPE(ALCbackendFactory);
1323 } ALCalsaBackendFactory;
1324 #define ALCALSABACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCalsaBackendFactory, ALCbackendFactory) } }
1326 static ALCboolean ALCalsaBackendFactory_init(ALCalsaBackendFactory* UNUSED(self))
1328 VECTOR_INIT(PlaybackDevices);
1329 VECTOR_INIT(CaptureDevices);
1331 if(!alsa_load())
1332 return ALC_FALSE;
1333 return ALC_TRUE;
1336 static void ALCalsaBackendFactory_deinit(ALCalsaBackendFactory* UNUSED(self))
1338 clear_devlist(&PlaybackDevices);
1339 VECTOR_DEINIT(PlaybackDevices);
1341 clear_devlist(&CaptureDevices);
1342 VECTOR_DEINIT(CaptureDevices);
1344 #ifdef HAVE_DYNLOAD
1345 if(alsa_handle)
1346 CloseLib(alsa_handle);
1347 alsa_handle = NULL;
1348 #endif
1351 static ALCboolean ALCalsaBackendFactory_querySupport(ALCalsaBackendFactory* UNUSED(self), ALCbackend_Type type)
1353 if(type == ALCbackend_Playback || type == ALCbackend_Capture)
1354 return ALC_TRUE;
1355 return ALC_FALSE;
1358 static void ALCalsaBackendFactory_probe(ALCalsaBackendFactory* UNUSED(self), enum DevProbe type)
1360 switch(type)
1362 case ALL_DEVICE_PROBE:
1363 probe_devices(SND_PCM_STREAM_PLAYBACK, &PlaybackDevices);
1364 VECTOR_FOR_EACH(const DevMap, PlaybackDevices, AppendAllDevicesList2);
1365 break;
1367 case CAPTURE_DEVICE_PROBE:
1368 probe_devices(SND_PCM_STREAM_CAPTURE, &CaptureDevices);
1369 VECTOR_FOR_EACH(const DevMap, CaptureDevices, AppendCaptureDeviceList2);
1370 break;
1374 static ALCbackend* ALCalsaBackendFactory_createBackend(ALCalsaBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
1376 if(type == ALCbackend_Playback)
1378 ALCplaybackAlsa *backend;
1379 NEW_OBJ(backend, ALCplaybackAlsa)(device);
1380 if(!backend) return NULL;
1381 return STATIC_CAST(ALCbackend, backend);
1383 if(type == ALCbackend_Capture)
1385 ALCcaptureAlsa *backend;
1386 NEW_OBJ(backend, ALCcaptureAlsa)(device);
1387 if(!backend) return NULL;
1388 return STATIC_CAST(ALCbackend, backend);
1391 return NULL;
1394 DEFINE_ALCBACKENDFACTORY_VTABLE(ALCalsaBackendFactory);
1397 ALCbackendFactory *ALCalsaBackendFactory_getFactory(void)
1399 static ALCalsaBackendFactory factory = ALCALSABACKENDFACTORY_INITIALIZER;
1400 return STATIC_CAST(ALCbackendFactory, &factory);