Clean up some messy rounding code
[openal-soft.git] / Alc / backends / alsa.c
blob79f2795afbe4999f543587f110aafd3475bcfcc9
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 alstr_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", alstr_get_cstr(missing_funcs));
225 CloseLib(alsa_handle);
226 alsa_handle = NULL;
228 alstr_reset(&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 alstr_copy_cstr(&entry.name, alsaDevice);
280 alstr_copy_cstr(&entry.device_name, GetConfigValue(
281 NULL, "alsa", (stream==SND_PCM_STREAM_PLAYBACK) ? "device" : "capture", "default"
283 VECTOR_PUSH_BACK(*DeviceList, entry);
285 if(stream == SND_PCM_STREAM_PLAYBACK)
287 const char *customdevs, *sep, *next;
288 next = GetConfigValue(NULL, "alsa", "custom-devices", "");
289 while((customdevs=next) != NULL && customdevs[0])
291 next = strchr(customdevs, ';');
292 sep = strchr(customdevs, '=');
293 if(!sep)
295 al_string spec = AL_STRING_INIT_STATIC();
296 if(next)
297 alstr_copy_range(&spec, customdevs, next++);
298 else
299 alstr_copy_cstr(&spec, customdevs);
300 ERR("Invalid ALSA device specification \"%s\"\n", alstr_get_cstr(spec));
301 alstr_reset(&spec);
302 continue;
305 AL_STRING_INIT(entry.name);
306 AL_STRING_INIT(entry.device_name);
307 alstr_copy_range(&entry.name, customdevs, sep++);
308 if(next)
309 alstr_copy_range(&entry.device_name, sep, next++);
310 else
311 alstr_copy_cstr(&entry.device_name, sep);
312 TRACE("Got device \"%s\", \"%s\"\n", alstr_get_cstr(entry.name),
313 alstr_get_cstr(entry.device_name));
314 VECTOR_PUSH_BACK(*DeviceList, entry);
318 card = -1;
319 if((err=snd_card_next(&card)) < 0)
320 ERR("Failed to find a card: %s\n", snd_strerror(err));
321 ConfigValueStr(NULL, "alsa", prefix_name(stream), &main_prefix);
322 while(card >= 0)
324 const char *card_prefix = main_prefix;
325 const char *cardname, *cardid;
326 char name[256];
328 snprintf(name, sizeof(name), "hw:%d", card);
329 if((err = snd_ctl_open(&handle, name, 0)) < 0)
331 ERR("control open (hw:%d): %s\n", card, snd_strerror(err));
332 goto next_card;
334 if((err = snd_ctl_card_info(handle, info)) < 0)
336 ERR("control hardware info (hw:%d): %s\n", card, snd_strerror(err));
337 snd_ctl_close(handle);
338 goto next_card;
341 cardname = snd_ctl_card_info_get_name(info);
342 cardid = snd_ctl_card_info_get_id(info);
344 snprintf(name, sizeof(name), "%s-%s", prefix_name(stream), cardid);
345 ConfigValueStr(NULL, "alsa", name, &card_prefix);
347 dev = -1;
348 while(1)
350 const char *device_prefix = card_prefix;
351 const char *devname;
352 char device[128];
354 if(snd_ctl_pcm_next_device(handle, &dev) < 0)
355 ERR("snd_ctl_pcm_next_device failed\n");
356 if(dev < 0)
357 break;
359 snd_pcm_info_set_device(pcminfo, dev);
360 snd_pcm_info_set_subdevice(pcminfo, 0);
361 snd_pcm_info_set_stream(pcminfo, stream);
362 if((err = snd_ctl_pcm_info(handle, pcminfo)) < 0)
364 if(err != -ENOENT)
365 ERR("control digital audio info (hw:%d): %s\n", card, snd_strerror(err));
366 continue;
369 devname = snd_pcm_info_get_name(pcminfo);
371 snprintf(name, sizeof(name), "%s-%s-%d", prefix_name(stream), cardid, dev);
372 ConfigValueStr(NULL, "alsa", name, &device_prefix);
374 snprintf(name, sizeof(name), "%s, %s (CARD=%s,DEV=%d)",
375 cardname, devname, cardid, dev);
376 snprintf(device, sizeof(device), "%sCARD=%s,DEV=%d",
377 device_prefix, cardid, dev);
379 TRACE("Got device \"%s\", \"%s\"\n", name, device);
380 AL_STRING_INIT(entry.name);
381 AL_STRING_INIT(entry.device_name);
382 alstr_copy_cstr(&entry.name, name);
383 alstr_copy_cstr(&entry.device_name, device);
384 VECTOR_PUSH_BACK(*DeviceList, entry);
386 snd_ctl_close(handle);
387 next_card:
388 if(snd_card_next(&card) < 0) {
389 ERR("snd_card_next failed\n");
390 break;
394 snd_pcm_info_free(pcminfo);
395 snd_ctl_card_info_free(info);
399 static int verify_state(snd_pcm_t *handle)
401 snd_pcm_state_t state = snd_pcm_state(handle);
402 int err;
404 switch(state)
406 case SND_PCM_STATE_OPEN:
407 case SND_PCM_STATE_SETUP:
408 case SND_PCM_STATE_PREPARED:
409 case SND_PCM_STATE_RUNNING:
410 case SND_PCM_STATE_DRAINING:
411 case SND_PCM_STATE_PAUSED:
412 /* All Okay */
413 break;
415 case SND_PCM_STATE_XRUN:
416 if((err=snd_pcm_recover(handle, -EPIPE, 1)) < 0)
417 return err;
418 break;
419 case SND_PCM_STATE_SUSPENDED:
420 if((err=snd_pcm_recover(handle, -ESTRPIPE, 1)) < 0)
421 return err;
422 break;
423 case SND_PCM_STATE_DISCONNECTED:
424 return -ENODEV;
427 return state;
431 typedef struct ALCplaybackAlsa {
432 DERIVE_FROM_TYPE(ALCbackend);
434 snd_pcm_t *pcmHandle;
436 ALvoid *buffer;
437 ALsizei size;
439 volatile int killNow;
440 althrd_t thread;
441 } ALCplaybackAlsa;
443 static int ALCplaybackAlsa_mixerProc(void *ptr);
444 static int ALCplaybackAlsa_mixerNoMMapProc(void *ptr);
446 static void ALCplaybackAlsa_Construct(ALCplaybackAlsa *self, ALCdevice *device);
447 static DECLARE_FORWARD(ALCplaybackAlsa, ALCbackend, void, Destruct)
448 static ALCenum ALCplaybackAlsa_open(ALCplaybackAlsa *self, const ALCchar *name);
449 static void ALCplaybackAlsa_close(ALCplaybackAlsa *self);
450 static ALCboolean ALCplaybackAlsa_reset(ALCplaybackAlsa *self);
451 static ALCboolean ALCplaybackAlsa_start(ALCplaybackAlsa *self);
452 static void ALCplaybackAlsa_stop(ALCplaybackAlsa *self);
453 static DECLARE_FORWARD2(ALCplaybackAlsa, ALCbackend, ALCenum, captureSamples, void*, ALCuint)
454 static DECLARE_FORWARD(ALCplaybackAlsa, ALCbackend, ALCuint, availableSamples)
455 static ClockLatency ALCplaybackAlsa_getClockLatency(ALCplaybackAlsa *self);
456 static DECLARE_FORWARD(ALCplaybackAlsa, ALCbackend, void, lock)
457 static DECLARE_FORWARD(ALCplaybackAlsa, ALCbackend, void, unlock)
458 DECLARE_DEFAULT_ALLOCATORS(ALCplaybackAlsa)
460 DEFINE_ALCBACKEND_VTABLE(ALCplaybackAlsa);
463 static void ALCplaybackAlsa_Construct(ALCplaybackAlsa *self, ALCdevice *device)
465 ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
466 SET_VTABLE2(ALCplaybackAlsa, ALCbackend, self);
470 static int ALCplaybackAlsa_mixerProc(void *ptr)
472 ALCplaybackAlsa *self = (ALCplaybackAlsa*)ptr;
473 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
474 const snd_pcm_channel_area_t *areas = NULL;
475 snd_pcm_uframes_t update_size, num_updates;
476 snd_pcm_sframes_t avail, commitres;
477 snd_pcm_uframes_t offset, frames;
478 char *WritePtr;
479 int err;
481 SetRTPriority();
482 althrd_setname(althrd_current(), MIXER_THREAD_NAME);
484 update_size = device->UpdateSize;
485 num_updates = device->NumUpdates;
486 while(!self->killNow)
488 int state = verify_state(self->pcmHandle);
489 if(state < 0)
491 ERR("Invalid state detected: %s\n", snd_strerror(state));
492 ALCplaybackAlsa_lock(self);
493 aluHandleDisconnect(device);
494 ALCplaybackAlsa_unlock(self);
495 break;
498 avail = snd_pcm_avail_update(self->pcmHandle);
499 if(avail < 0)
501 ERR("available update failed: %s\n", snd_strerror(avail));
502 continue;
505 if((snd_pcm_uframes_t)avail > update_size*(num_updates+1))
507 WARN("available samples exceeds the buffer size\n");
508 snd_pcm_reset(self->pcmHandle);
509 continue;
512 // make sure there's frames to process
513 if((snd_pcm_uframes_t)avail < update_size)
515 if(state != SND_PCM_STATE_RUNNING)
517 err = snd_pcm_start(self->pcmHandle);
518 if(err < 0)
520 ERR("start failed: %s\n", snd_strerror(err));
521 continue;
524 if(snd_pcm_wait(self->pcmHandle, 1000) == 0)
525 ERR("Wait timeout... buffer size too low?\n");
526 continue;
528 avail -= avail%update_size;
530 // it is possible that contiguous areas are smaller, thus we use a loop
531 ALCplaybackAlsa_lock(self);
532 while(avail > 0)
534 frames = avail;
536 err = snd_pcm_mmap_begin(self->pcmHandle, &areas, &offset, &frames);
537 if(err < 0)
539 ERR("mmap begin error: %s\n", snd_strerror(err));
540 break;
543 WritePtr = (char*)areas->addr + (offset * areas->step / 8);
544 aluMixData(device, WritePtr, frames);
546 commitres = snd_pcm_mmap_commit(self->pcmHandle, offset, frames);
547 if(commitres < 0 || (commitres-frames) != 0)
549 ERR("mmap commit error: %s\n",
550 snd_strerror(commitres >= 0 ? -EPIPE : commitres));
551 break;
554 avail -= frames;
556 ALCplaybackAlsa_unlock(self);
559 return 0;
562 static int ALCplaybackAlsa_mixerNoMMapProc(void *ptr)
564 ALCplaybackAlsa *self = (ALCplaybackAlsa*)ptr;
565 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
566 snd_pcm_uframes_t update_size, num_updates;
567 snd_pcm_sframes_t avail;
568 char *WritePtr;
569 int err;
571 SetRTPriority();
572 althrd_setname(althrd_current(), MIXER_THREAD_NAME);
574 update_size = device->UpdateSize;
575 num_updates = device->NumUpdates;
576 while(!self->killNow)
578 int state = verify_state(self->pcmHandle);
579 if(state < 0)
581 ERR("Invalid state detected: %s\n", snd_strerror(state));
582 ALCplaybackAlsa_lock(self);
583 aluHandleDisconnect(device);
584 ALCplaybackAlsa_unlock(self);
585 break;
588 avail = snd_pcm_avail_update(self->pcmHandle);
589 if(avail < 0)
591 ERR("available update failed: %s\n", snd_strerror(avail));
592 continue;
595 if((snd_pcm_uframes_t)avail > update_size*num_updates)
597 WARN("available samples exceeds the buffer size\n");
598 snd_pcm_reset(self->pcmHandle);
599 continue;
602 if((snd_pcm_uframes_t)avail < update_size)
604 if(state != SND_PCM_STATE_RUNNING)
606 err = snd_pcm_start(self->pcmHandle);
607 if(err < 0)
609 ERR("start failed: %s\n", snd_strerror(err));
610 continue;
613 if(snd_pcm_wait(self->pcmHandle, 1000) == 0)
614 ERR("Wait timeout... buffer size too low?\n");
615 continue;
618 ALCplaybackAlsa_lock(self);
619 WritePtr = self->buffer;
620 avail = snd_pcm_bytes_to_frames(self->pcmHandle, self->size);
621 aluMixData(device, WritePtr, avail);
623 while(avail > 0)
625 int ret = snd_pcm_writei(self->pcmHandle, WritePtr, avail);
626 switch (ret)
628 case -EAGAIN:
629 continue;
630 #if ESTRPIPE != EPIPE
631 case -ESTRPIPE:
632 #endif
633 case -EPIPE:
634 case -EINTR:
635 ret = snd_pcm_recover(self->pcmHandle, ret, 1);
636 if(ret < 0)
637 avail = 0;
638 break;
639 default:
640 if (ret >= 0)
642 WritePtr += snd_pcm_frames_to_bytes(self->pcmHandle, ret);
643 avail -= ret;
645 break;
647 if (ret < 0)
649 ret = snd_pcm_prepare(self->pcmHandle);
650 if(ret < 0)
651 break;
654 ALCplaybackAlsa_unlock(self);
657 return 0;
661 static ALCenum ALCplaybackAlsa_open(ALCplaybackAlsa *self, const ALCchar *name)
663 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
664 const char *driver = NULL;
665 int err;
667 if(name)
669 const DevMap *iter;
671 if(VECTOR_SIZE(PlaybackDevices) == 0)
672 probe_devices(SND_PCM_STREAM_PLAYBACK, &PlaybackDevices);
674 #define MATCH_NAME(i) (alstr_cmp_cstr((i)->name, name) == 0)
675 VECTOR_FIND_IF(iter, const DevMap, PlaybackDevices, MATCH_NAME);
676 #undef MATCH_NAME
677 if(iter == VECTOR_END(PlaybackDevices))
678 return ALC_INVALID_VALUE;
679 driver = alstr_get_cstr(iter->device_name);
681 else
683 name = alsaDevice;
684 driver = GetConfigValue(NULL, "alsa", "device", "default");
687 TRACE("Opening device \"%s\"\n", driver);
688 err = snd_pcm_open(&self->pcmHandle, driver, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
689 if(err < 0)
691 ERR("Could not open playback device '%s': %s\n", driver, snd_strerror(err));
692 return ALC_OUT_OF_MEMORY;
695 /* Free alsa's global config tree. Otherwise valgrind reports a ton of leaks. */
696 snd_config_update_free_global();
698 alstr_copy_cstr(&device->DeviceName, name);
700 return ALC_NO_ERROR;
703 static void ALCplaybackAlsa_close(ALCplaybackAlsa *self)
705 snd_pcm_close(self->pcmHandle);
708 static ALCboolean ALCplaybackAlsa_reset(ALCplaybackAlsa *self)
710 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
711 snd_pcm_uframes_t periodSizeInFrames;
712 unsigned int periodLen, bufferLen;
713 snd_pcm_sw_params_t *sp = NULL;
714 snd_pcm_hw_params_t *hp = NULL;
715 snd_pcm_format_t format = -1;
716 snd_pcm_access_t access;
717 unsigned int periods;
718 unsigned int rate;
719 const char *funcerr;
720 int allowmmap;
721 int dir;
722 int err;
724 switch(device->FmtType)
726 case DevFmtByte:
727 format = SND_PCM_FORMAT_S8;
728 break;
729 case DevFmtUByte:
730 format = SND_PCM_FORMAT_U8;
731 break;
732 case DevFmtShort:
733 format = SND_PCM_FORMAT_S16;
734 break;
735 case DevFmtUShort:
736 format = SND_PCM_FORMAT_U16;
737 break;
738 case DevFmtInt:
739 format = SND_PCM_FORMAT_S32;
740 break;
741 case DevFmtUInt:
742 format = SND_PCM_FORMAT_U32;
743 break;
744 case DevFmtFloat:
745 format = SND_PCM_FORMAT_FLOAT;
746 break;
749 allowmmap = GetConfigValueBool(alstr_get_cstr(device->DeviceName), "alsa", "mmap", 1);
750 periods = device->NumUpdates;
751 periodLen = (ALuint64)device->UpdateSize * 1000000 / device->Frequency;
752 bufferLen = periodLen * periods;
753 rate = device->Frequency;
755 snd_pcm_hw_params_malloc(&hp);
756 #define CHECK(x) if((funcerr=#x),(err=(x)) < 0) goto error
757 CHECK(snd_pcm_hw_params_any(self->pcmHandle, hp));
758 /* set interleaved access */
759 if(!allowmmap || snd_pcm_hw_params_set_access(self->pcmHandle, hp, SND_PCM_ACCESS_MMAP_INTERLEAVED) < 0)
761 /* No mmap */
762 CHECK(snd_pcm_hw_params_set_access(self->pcmHandle, hp, SND_PCM_ACCESS_RW_INTERLEAVED));
764 /* test and set format (implicitly sets sample bits) */
765 if(snd_pcm_hw_params_test_format(self->pcmHandle, hp, format) < 0)
767 static const struct {
768 snd_pcm_format_t format;
769 enum DevFmtType fmttype;
770 } formatlist[] = {
771 { SND_PCM_FORMAT_FLOAT, DevFmtFloat },
772 { SND_PCM_FORMAT_S32, DevFmtInt },
773 { SND_PCM_FORMAT_U32, DevFmtUInt },
774 { SND_PCM_FORMAT_S16, DevFmtShort },
775 { SND_PCM_FORMAT_U16, DevFmtUShort },
776 { SND_PCM_FORMAT_S8, DevFmtByte },
777 { SND_PCM_FORMAT_U8, DevFmtUByte },
779 size_t k;
781 for(k = 0;k < COUNTOF(formatlist);k++)
783 format = formatlist[k].format;
784 if(snd_pcm_hw_params_test_format(self->pcmHandle, hp, format) >= 0)
786 device->FmtType = formatlist[k].fmttype;
787 break;
791 CHECK(snd_pcm_hw_params_set_format(self->pcmHandle, hp, format));
792 /* test and set channels (implicitly sets frame bits) */
793 if(snd_pcm_hw_params_test_channels(self->pcmHandle, hp, ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder)) < 0)
795 static const enum DevFmtChannels channellist[] = {
796 DevFmtStereo,
797 DevFmtQuad,
798 DevFmtX51,
799 DevFmtX71,
800 DevFmtMono,
802 size_t k;
804 for(k = 0;k < COUNTOF(channellist);k++)
806 if(snd_pcm_hw_params_test_channels(self->pcmHandle, hp, ChannelsFromDevFmt(channellist[k], 0)) >= 0)
808 device->FmtChans = channellist[k];
809 device->AmbiOrder = 0;
810 break;
814 CHECK(snd_pcm_hw_params_set_channels(self->pcmHandle, hp, ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder)));
815 /* set rate (implicitly constrains period/buffer parameters) */
816 if(!GetConfigValueBool(alstr_get_cstr(device->DeviceName), "alsa", "allow-resampler", 0) ||
817 !(device->Flags&DEVICE_FREQUENCY_REQUEST))
819 if(snd_pcm_hw_params_set_rate_resample(self->pcmHandle, hp, 0) < 0)
820 ERR("Failed to disable ALSA resampler\n");
822 else if(snd_pcm_hw_params_set_rate_resample(self->pcmHandle, hp, 1) < 0)
823 ERR("Failed to enable ALSA resampler\n");
824 CHECK(snd_pcm_hw_params_set_rate_near(self->pcmHandle, hp, &rate, NULL));
825 /* set buffer time (implicitly constrains period/buffer parameters) */
826 if((err=snd_pcm_hw_params_set_buffer_time_near(self->pcmHandle, hp, &bufferLen, NULL)) < 0)
827 ERR("snd_pcm_hw_params_set_buffer_time_near failed: %s\n", snd_strerror(err));
828 /* set period time (implicitly sets buffer size/bytes/time and period size/bytes) */
829 if((err=snd_pcm_hw_params_set_period_time_near(self->pcmHandle, hp, &periodLen, NULL)) < 0)
830 ERR("snd_pcm_hw_params_set_period_time_near failed: %s\n", snd_strerror(err));
831 /* install and prepare hardware configuration */
832 CHECK(snd_pcm_hw_params(self->pcmHandle, hp));
833 /* retrieve configuration info */
834 CHECK(snd_pcm_hw_params_get_access(hp, &access));
835 CHECK(snd_pcm_hw_params_get_period_size(hp, &periodSizeInFrames, NULL));
836 CHECK(snd_pcm_hw_params_get_periods(hp, &periods, &dir));
837 if(dir != 0)
838 WARN("Inexact period count: %u (%d)\n", periods, dir);
840 snd_pcm_hw_params_free(hp);
841 hp = NULL;
842 snd_pcm_sw_params_malloc(&sp);
844 CHECK(snd_pcm_sw_params_current(self->pcmHandle, sp));
845 CHECK(snd_pcm_sw_params_set_avail_min(self->pcmHandle, sp, periodSizeInFrames));
846 CHECK(snd_pcm_sw_params_set_stop_threshold(self->pcmHandle, sp, periodSizeInFrames*periods));
847 CHECK(snd_pcm_sw_params(self->pcmHandle, sp));
848 #undef CHECK
849 snd_pcm_sw_params_free(sp);
850 sp = NULL;
852 device->NumUpdates = periods;
853 device->UpdateSize = periodSizeInFrames;
854 device->Frequency = rate;
856 SetDefaultChannelOrder(device);
858 return ALC_TRUE;
860 error:
861 ERR("%s failed: %s\n", funcerr, snd_strerror(err));
862 if(hp) snd_pcm_hw_params_free(hp);
863 if(sp) snd_pcm_sw_params_free(sp);
864 return ALC_FALSE;
867 static ALCboolean ALCplaybackAlsa_start(ALCplaybackAlsa *self)
869 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
870 int (*thread_func)(void*) = NULL;
871 snd_pcm_hw_params_t *hp = NULL;
872 snd_pcm_access_t access;
873 const char *funcerr;
874 int err;
876 snd_pcm_hw_params_malloc(&hp);
877 #define CHECK(x) if((funcerr=#x),(err=(x)) < 0) goto error
878 CHECK(snd_pcm_hw_params_current(self->pcmHandle, hp));
879 /* retrieve configuration info */
880 CHECK(snd_pcm_hw_params_get_access(hp, &access));
881 #undef CHECK
882 snd_pcm_hw_params_free(hp);
883 hp = NULL;
885 self->size = snd_pcm_frames_to_bytes(self->pcmHandle, device->UpdateSize);
886 if(access == SND_PCM_ACCESS_RW_INTERLEAVED)
888 self->buffer = al_malloc(16, self->size);
889 if(!self->buffer)
891 ERR("buffer malloc failed\n");
892 return ALC_FALSE;
894 thread_func = ALCplaybackAlsa_mixerNoMMapProc;
896 else
898 err = snd_pcm_prepare(self->pcmHandle);
899 if(err < 0)
901 ERR("snd_pcm_prepare(data->pcmHandle) failed: %s\n", snd_strerror(err));
902 return ALC_FALSE;
904 thread_func = ALCplaybackAlsa_mixerProc;
906 self->killNow = 0;
907 if(althrd_create(&self->thread, thread_func, self) != althrd_success)
909 ERR("Could not create playback thread\n");
910 al_free(self->buffer);
911 self->buffer = NULL;
912 return ALC_FALSE;
915 return ALC_TRUE;
917 error:
918 ERR("%s failed: %s\n", funcerr, snd_strerror(err));
919 if(hp) snd_pcm_hw_params_free(hp);
920 return ALC_FALSE;
923 static void ALCplaybackAlsa_stop(ALCplaybackAlsa *self)
925 int res;
927 if(self->killNow)
928 return;
930 self->killNow = 1;
931 althrd_join(self->thread, &res);
933 al_free(self->buffer);
934 self->buffer = NULL;
937 static ClockLatency ALCplaybackAlsa_getClockLatency(ALCplaybackAlsa *self)
939 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
940 snd_pcm_sframes_t delay = 0;
941 ClockLatency ret;
942 int err;
944 ALCplaybackAlsa_lock(self);
945 ret.ClockTime = GetDeviceClockTime(device);
946 if((err=snd_pcm_delay(self->pcmHandle, &delay)) < 0)
948 ERR("Failed to get pcm delay: %s\n", snd_strerror(err));
949 delay = 0;
951 if(delay < 0) delay = 0;
952 ret.Latency = delay * DEVICE_CLOCK_RES / device->Frequency;
953 ALCplaybackAlsa_unlock(self);
955 return ret;
959 typedef struct ALCcaptureAlsa {
960 DERIVE_FROM_TYPE(ALCbackend);
962 snd_pcm_t *pcmHandle;
964 ALvoid *buffer;
965 ALsizei size;
967 ALboolean doCapture;
968 ll_ringbuffer_t *ring;
970 snd_pcm_sframes_t last_avail;
971 } ALCcaptureAlsa;
973 static void ALCcaptureAlsa_Construct(ALCcaptureAlsa *self, ALCdevice *device);
974 static DECLARE_FORWARD(ALCcaptureAlsa, ALCbackend, void, Destruct)
975 static ALCenum ALCcaptureAlsa_open(ALCcaptureAlsa *self, const ALCchar *name);
976 static void ALCcaptureAlsa_close(ALCcaptureAlsa *self);
977 static DECLARE_FORWARD(ALCcaptureAlsa, ALCbackend, ALCboolean, reset)
978 static ALCboolean ALCcaptureAlsa_start(ALCcaptureAlsa *self);
979 static void ALCcaptureAlsa_stop(ALCcaptureAlsa *self);
980 static ALCenum ALCcaptureAlsa_captureSamples(ALCcaptureAlsa *self, ALCvoid *buffer, ALCuint samples);
981 static ALCuint ALCcaptureAlsa_availableSamples(ALCcaptureAlsa *self);
982 static ClockLatency ALCcaptureAlsa_getClockLatency(ALCcaptureAlsa *self);
983 static DECLARE_FORWARD(ALCcaptureAlsa, ALCbackend, void, lock)
984 static DECLARE_FORWARD(ALCcaptureAlsa, ALCbackend, void, unlock)
985 DECLARE_DEFAULT_ALLOCATORS(ALCcaptureAlsa)
987 DEFINE_ALCBACKEND_VTABLE(ALCcaptureAlsa);
990 static void ALCcaptureAlsa_Construct(ALCcaptureAlsa *self, ALCdevice *device)
992 ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
993 SET_VTABLE2(ALCcaptureAlsa, ALCbackend, self);
997 static ALCenum ALCcaptureAlsa_open(ALCcaptureAlsa *self, const ALCchar *name)
999 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
1000 const char *driver = NULL;
1001 snd_pcm_hw_params_t *hp;
1002 snd_pcm_uframes_t bufferSizeInFrames;
1003 snd_pcm_uframes_t periodSizeInFrames;
1004 ALboolean needring = AL_FALSE;
1005 snd_pcm_format_t format = -1;
1006 const char *funcerr;
1007 int err;
1009 if(name)
1011 const DevMap *iter;
1013 if(VECTOR_SIZE(CaptureDevices) == 0)
1014 probe_devices(SND_PCM_STREAM_CAPTURE, &CaptureDevices);
1016 #define MATCH_NAME(i) (alstr_cmp_cstr((i)->name, name) == 0)
1017 VECTOR_FIND_IF(iter, const DevMap, CaptureDevices, MATCH_NAME);
1018 #undef MATCH_NAME
1019 if(iter == VECTOR_END(CaptureDevices))
1020 return ALC_INVALID_VALUE;
1021 driver = alstr_get_cstr(iter->device_name);
1023 else
1025 name = alsaDevice;
1026 driver = GetConfigValue(NULL, "alsa", "capture", "default");
1029 TRACE("Opening device \"%s\"\n", driver);
1030 err = snd_pcm_open(&self->pcmHandle, driver, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK);
1031 if(err < 0)
1033 ERR("Could not open capture device '%s': %s\n", driver, snd_strerror(err));
1034 return ALC_INVALID_VALUE;
1037 /* Free alsa's global config tree. Otherwise valgrind reports a ton of leaks. */
1038 snd_config_update_free_global();
1040 switch(device->FmtType)
1042 case DevFmtByte:
1043 format = SND_PCM_FORMAT_S8;
1044 break;
1045 case DevFmtUByte:
1046 format = SND_PCM_FORMAT_U8;
1047 break;
1048 case DevFmtShort:
1049 format = SND_PCM_FORMAT_S16;
1050 break;
1051 case DevFmtUShort:
1052 format = SND_PCM_FORMAT_U16;
1053 break;
1054 case DevFmtInt:
1055 format = SND_PCM_FORMAT_S32;
1056 break;
1057 case DevFmtUInt:
1058 format = SND_PCM_FORMAT_U32;
1059 break;
1060 case DevFmtFloat:
1061 format = SND_PCM_FORMAT_FLOAT;
1062 break;
1065 funcerr = NULL;
1066 bufferSizeInFrames = maxu(device->UpdateSize*device->NumUpdates,
1067 100*device->Frequency/1000);
1068 periodSizeInFrames = minu(bufferSizeInFrames, 25*device->Frequency/1000);
1070 snd_pcm_hw_params_malloc(&hp);
1071 #define CHECK(x) if((funcerr=#x),(err=(x)) < 0) goto error
1072 CHECK(snd_pcm_hw_params_any(self->pcmHandle, hp));
1073 /* set interleaved access */
1074 CHECK(snd_pcm_hw_params_set_access(self->pcmHandle, hp, SND_PCM_ACCESS_RW_INTERLEAVED));
1075 /* set format (implicitly sets sample bits) */
1076 CHECK(snd_pcm_hw_params_set_format(self->pcmHandle, hp, format));
1077 /* set channels (implicitly sets frame bits) */
1078 CHECK(snd_pcm_hw_params_set_channels(self->pcmHandle, hp, ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder)));
1079 /* set rate (implicitly constrains period/buffer parameters) */
1080 CHECK(snd_pcm_hw_params_set_rate(self->pcmHandle, hp, device->Frequency, 0));
1081 /* set buffer size in frame units (implicitly sets period size/bytes/time and buffer time/bytes) */
1082 if(snd_pcm_hw_params_set_buffer_size_min(self->pcmHandle, hp, &bufferSizeInFrames) < 0)
1084 TRACE("Buffer too large, using intermediate ring buffer\n");
1085 needring = AL_TRUE;
1086 CHECK(snd_pcm_hw_params_set_buffer_size_near(self->pcmHandle, hp, &bufferSizeInFrames));
1088 /* set buffer size in frame units (implicitly sets period size/bytes/time and buffer time/bytes) */
1089 CHECK(snd_pcm_hw_params_set_period_size_near(self->pcmHandle, hp, &periodSizeInFrames, NULL));
1090 /* install and prepare hardware configuration */
1091 CHECK(snd_pcm_hw_params(self->pcmHandle, hp));
1092 /* retrieve configuration info */
1093 CHECK(snd_pcm_hw_params_get_period_size(hp, &periodSizeInFrames, NULL));
1094 #undef CHECK
1095 snd_pcm_hw_params_free(hp);
1096 hp = NULL;
1098 if(needring)
1100 self->ring = ll_ringbuffer_create(
1101 device->UpdateSize*device->NumUpdates + 1,
1102 FrameSizeFromDevFmt(device->FmtChans, device->FmtType, device->AmbiOrder)
1104 if(!self->ring)
1106 ERR("ring buffer create failed\n");
1107 goto error2;
1111 alstr_copy_cstr(&device->DeviceName, name);
1113 return ALC_NO_ERROR;
1115 error:
1116 ERR("%s failed: %s\n", funcerr, snd_strerror(err));
1117 if(hp) snd_pcm_hw_params_free(hp);
1119 error2:
1120 ll_ringbuffer_free(self->ring);
1121 self->ring = NULL;
1122 snd_pcm_close(self->pcmHandle);
1124 return ALC_INVALID_VALUE;
1127 static void ALCcaptureAlsa_close(ALCcaptureAlsa *self)
1129 snd_pcm_close(self->pcmHandle);
1130 ll_ringbuffer_free(self->ring);
1132 al_free(self->buffer);
1133 self->buffer = NULL;
1136 static ALCboolean ALCcaptureAlsa_start(ALCcaptureAlsa *self)
1138 int err = snd_pcm_start(self->pcmHandle);
1139 if(err < 0)
1141 ERR("start failed: %s\n", snd_strerror(err));
1142 aluHandleDisconnect(STATIC_CAST(ALCbackend, self)->mDevice);
1143 return ALC_FALSE;
1146 self->doCapture = AL_TRUE;
1147 return ALC_TRUE;
1150 static void ALCcaptureAlsa_stop(ALCcaptureAlsa *self)
1152 ALCuint avail;
1153 int err;
1155 /* OpenAL requires access to unread audio after stopping, but ALSA's
1156 * snd_pcm_drain is unreliable and snd_pcm_drop drops it. Capture what's
1157 * available now so it'll be available later after the drop. */
1158 avail = ALCcaptureAlsa_availableSamples(self);
1159 if(!self->ring && avail > 0)
1161 /* The ring buffer implicitly captures when checking availability.
1162 * Direct access needs to explicitly capture it into temp storage. */
1163 ALsizei size;
1164 void *ptr;
1166 size = snd_pcm_frames_to_bytes(self->pcmHandle, avail);
1167 ptr = al_malloc(16, size);
1168 if(ptr)
1170 ALCcaptureAlsa_captureSamples(self, ptr, avail);
1171 al_free(self->buffer);
1172 self->buffer = ptr;
1173 self->size = size;
1176 err = snd_pcm_drop(self->pcmHandle);
1177 if(err < 0)
1178 ERR("drop failed: %s\n", snd_strerror(err));
1179 self->doCapture = AL_FALSE;
1182 static ALCenum ALCcaptureAlsa_captureSamples(ALCcaptureAlsa *self, ALCvoid *buffer, ALCuint samples)
1184 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
1186 if(self->ring)
1188 ll_ringbuffer_read(self->ring, buffer, samples);
1189 return ALC_NO_ERROR;
1192 self->last_avail -= samples;
1193 while(device->Connected && samples > 0)
1195 snd_pcm_sframes_t amt = 0;
1197 if(self->size > 0)
1199 /* First get any data stored from the last stop */
1200 amt = snd_pcm_bytes_to_frames(self->pcmHandle, self->size);
1201 if((snd_pcm_uframes_t)amt > samples) amt = samples;
1203 amt = snd_pcm_frames_to_bytes(self->pcmHandle, amt);
1204 memcpy(buffer, self->buffer, amt);
1206 if(self->size > amt)
1208 memmove(self->buffer, self->buffer+amt, self->size - amt);
1209 self->size -= amt;
1211 else
1213 al_free(self->buffer);
1214 self->buffer = NULL;
1215 self->size = 0;
1217 amt = snd_pcm_bytes_to_frames(self->pcmHandle, amt);
1219 else if(self->doCapture)
1220 amt = snd_pcm_readi(self->pcmHandle, buffer, samples);
1221 if(amt < 0)
1223 ERR("read error: %s\n", snd_strerror(amt));
1225 if(amt == -EAGAIN)
1226 continue;
1227 if((amt=snd_pcm_recover(self->pcmHandle, amt, 1)) >= 0)
1229 amt = snd_pcm_start(self->pcmHandle);
1230 if(amt >= 0)
1231 amt = snd_pcm_avail_update(self->pcmHandle);
1233 if(amt < 0)
1235 ERR("restore error: %s\n", snd_strerror(amt));
1236 aluHandleDisconnect(device);
1237 break;
1239 /* If the amount available is less than what's asked, we lost it
1240 * during recovery. So just give silence instead. */
1241 if((snd_pcm_uframes_t)amt < samples)
1242 break;
1243 continue;
1246 buffer = (ALbyte*)buffer + amt;
1247 samples -= amt;
1249 if(samples > 0)
1250 memset(buffer, ((device->FmtType == DevFmtUByte) ? 0x80 : 0),
1251 snd_pcm_frames_to_bytes(self->pcmHandle, samples));
1253 return ALC_NO_ERROR;
1256 static ALCuint ALCcaptureAlsa_availableSamples(ALCcaptureAlsa *self)
1258 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
1259 snd_pcm_sframes_t avail = 0;
1261 if(device->Connected && self->doCapture)
1262 avail = snd_pcm_avail_update(self->pcmHandle);
1263 if(avail < 0)
1265 ERR("avail update failed: %s\n", snd_strerror(avail));
1267 if((avail=snd_pcm_recover(self->pcmHandle, avail, 1)) >= 0)
1269 if(self->doCapture)
1270 avail = snd_pcm_start(self->pcmHandle);
1271 if(avail >= 0)
1272 avail = snd_pcm_avail_update(self->pcmHandle);
1274 if(avail < 0)
1276 ERR("restore error: %s\n", snd_strerror(avail));
1277 aluHandleDisconnect(device);
1281 if(!self->ring)
1283 if(avail < 0) avail = 0;
1284 avail += snd_pcm_bytes_to_frames(self->pcmHandle, self->size);
1285 if(avail > self->last_avail) self->last_avail = avail;
1286 return self->last_avail;
1289 while(avail > 0)
1291 ll_ringbuffer_data_t vec[2];
1292 snd_pcm_sframes_t amt;
1294 ll_ringbuffer_get_write_vector(self->ring, vec);
1295 if(vec[0].len == 0) break;
1297 amt = (vec[0].len < (snd_pcm_uframes_t)avail) ?
1298 vec[0].len : (snd_pcm_uframes_t)avail;
1299 amt = snd_pcm_readi(self->pcmHandle, vec[0].buf, amt);
1300 if(amt < 0)
1302 ERR("read error: %s\n", snd_strerror(amt));
1304 if(amt == -EAGAIN)
1305 continue;
1306 if((amt=snd_pcm_recover(self->pcmHandle, amt, 1)) >= 0)
1308 if(self->doCapture)
1309 amt = snd_pcm_start(self->pcmHandle);
1310 if(amt >= 0)
1311 amt = snd_pcm_avail_update(self->pcmHandle);
1313 if(amt < 0)
1315 ERR("restore error: %s\n", snd_strerror(amt));
1316 aluHandleDisconnect(device);
1317 break;
1319 avail = amt;
1320 continue;
1323 ll_ringbuffer_write_advance(self->ring, amt);
1324 avail -= amt;
1327 return ll_ringbuffer_read_space(self->ring);
1330 static ClockLatency ALCcaptureAlsa_getClockLatency(ALCcaptureAlsa *self)
1332 ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
1333 snd_pcm_sframes_t delay = 0;
1334 ClockLatency ret;
1335 int err;
1337 ALCcaptureAlsa_lock(self);
1338 ret.ClockTime = GetDeviceClockTime(device);
1339 if((err=snd_pcm_delay(self->pcmHandle, &delay)) < 0)
1341 ERR("Failed to get pcm delay: %s\n", snd_strerror(err));
1342 delay = 0;
1344 if(delay < 0) delay = 0;
1345 ret.Latency = delay * DEVICE_CLOCK_RES / device->Frequency;
1346 ALCcaptureAlsa_unlock(self);
1348 return ret;
1352 static inline void AppendAllDevicesList2(const DevMap *entry)
1353 { AppendAllDevicesList(alstr_get_cstr(entry->name)); }
1354 static inline void AppendCaptureDeviceList2(const DevMap *entry)
1355 { AppendCaptureDeviceList(alstr_get_cstr(entry->name)); }
1357 typedef struct ALCalsaBackendFactory {
1358 DERIVE_FROM_TYPE(ALCbackendFactory);
1359 } ALCalsaBackendFactory;
1360 #define ALCALSABACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCalsaBackendFactory, ALCbackendFactory) } }
1362 static ALCboolean ALCalsaBackendFactory_init(ALCalsaBackendFactory* UNUSED(self))
1364 VECTOR_INIT(PlaybackDevices);
1365 VECTOR_INIT(CaptureDevices);
1367 if(!alsa_load())
1368 return ALC_FALSE;
1369 return ALC_TRUE;
1372 static void ALCalsaBackendFactory_deinit(ALCalsaBackendFactory* UNUSED(self))
1374 clear_devlist(&PlaybackDevices);
1375 VECTOR_DEINIT(PlaybackDevices);
1377 clear_devlist(&CaptureDevices);
1378 VECTOR_DEINIT(CaptureDevices);
1380 #ifdef HAVE_DYNLOAD
1381 if(alsa_handle)
1382 CloseLib(alsa_handle);
1383 alsa_handle = NULL;
1384 #endif
1387 static ALCboolean ALCalsaBackendFactory_querySupport(ALCalsaBackendFactory* UNUSED(self), ALCbackend_Type type)
1389 if(type == ALCbackend_Playback || type == ALCbackend_Capture)
1390 return ALC_TRUE;
1391 return ALC_FALSE;
1394 static void ALCalsaBackendFactory_probe(ALCalsaBackendFactory* UNUSED(self), enum DevProbe type)
1396 switch(type)
1398 case ALL_DEVICE_PROBE:
1399 probe_devices(SND_PCM_STREAM_PLAYBACK, &PlaybackDevices);
1400 VECTOR_FOR_EACH(const DevMap, PlaybackDevices, AppendAllDevicesList2);
1401 break;
1403 case CAPTURE_DEVICE_PROBE:
1404 probe_devices(SND_PCM_STREAM_CAPTURE, &CaptureDevices);
1405 VECTOR_FOR_EACH(const DevMap, CaptureDevices, AppendCaptureDeviceList2);
1406 break;
1410 static ALCbackend* ALCalsaBackendFactory_createBackend(ALCalsaBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
1412 if(type == ALCbackend_Playback)
1414 ALCplaybackAlsa *backend;
1415 NEW_OBJ(backend, ALCplaybackAlsa)(device);
1416 if(!backend) return NULL;
1417 return STATIC_CAST(ALCbackend, backend);
1419 if(type == ALCbackend_Capture)
1421 ALCcaptureAlsa *backend;
1422 NEW_OBJ(backend, ALCcaptureAlsa)(device);
1423 if(!backend) return NULL;
1424 return STATIC_CAST(ALCbackend, backend);
1427 return NULL;
1430 DEFINE_ALCBACKENDFACTORY_VTABLE(ALCalsaBackendFactory);
1433 ALCbackendFactory *ALCalsaBackendFactory_getFactory(void)
1435 static ALCalsaBackendFactory factory = ALCALSABACKENDFACTORY_INITIALIZER;
1436 return STATIC_CAST(ALCbackendFactory, &factory);