Make ALSA XRUN when the number of available samples exceeds the buffer size
[openal-soft/android.git] / Alc / backends / alsa.c
blob85e1d802d6f36bbe6e72fcbe8042d4a5c99052ee
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., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, 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"
29 #include <alsa/asoundlib.h>
32 static const ALCchar alsaDevice[] = "ALSA Default";
35 static void *alsa_handle;
36 #ifdef HAVE_DYNLOAD
37 #define MAKE_FUNC(f) static typeof(f) * p##f
38 MAKE_FUNC(snd_strerror);
39 MAKE_FUNC(snd_pcm_open);
40 MAKE_FUNC(snd_pcm_close);
41 MAKE_FUNC(snd_pcm_nonblock);
42 MAKE_FUNC(snd_pcm_frames_to_bytes);
43 MAKE_FUNC(snd_pcm_bytes_to_frames);
44 MAKE_FUNC(snd_pcm_hw_params_malloc);
45 MAKE_FUNC(snd_pcm_hw_params_free);
46 MAKE_FUNC(snd_pcm_hw_params_any);
47 MAKE_FUNC(snd_pcm_hw_params_set_access);
48 MAKE_FUNC(snd_pcm_hw_params_set_format);
49 MAKE_FUNC(snd_pcm_hw_params_set_channels);
50 MAKE_FUNC(snd_pcm_hw_params_set_periods_near);
51 MAKE_FUNC(snd_pcm_hw_params_set_rate_near);
52 MAKE_FUNC(snd_pcm_hw_params_set_rate);
53 MAKE_FUNC(snd_pcm_hw_params_set_rate_resample);
54 MAKE_FUNC(snd_pcm_hw_params_set_buffer_time_near);
55 MAKE_FUNC(snd_pcm_hw_params_set_period_time_near);
56 MAKE_FUNC(snd_pcm_hw_params_set_buffer_size_near);
57 MAKE_FUNC(snd_pcm_hw_params_set_period_size_near);
58 MAKE_FUNC(snd_pcm_hw_params_set_buffer_size_min);
59 MAKE_FUNC(snd_pcm_hw_params_get_buffer_size);
60 MAKE_FUNC(snd_pcm_hw_params_get_period_size);
61 MAKE_FUNC(snd_pcm_hw_params_get_access);
62 MAKE_FUNC(snd_pcm_hw_params_get_periods);
63 MAKE_FUNC(snd_pcm_hw_params);
64 MAKE_FUNC(snd_pcm_sw_params_malloc);
65 MAKE_FUNC(snd_pcm_sw_params_current);
66 MAKE_FUNC(snd_pcm_sw_params_set_avail_min);
67 MAKE_FUNC(snd_pcm_sw_params_set_stop_threshold);
68 MAKE_FUNC(snd_pcm_sw_params);
69 MAKE_FUNC(snd_pcm_sw_params_free);
70 MAKE_FUNC(snd_pcm_prepare);
71 MAKE_FUNC(snd_pcm_start);
72 MAKE_FUNC(snd_pcm_resume);
73 MAKE_FUNC(snd_pcm_wait);
74 MAKE_FUNC(snd_pcm_state);
75 MAKE_FUNC(snd_pcm_avail_update);
76 MAKE_FUNC(snd_pcm_areas_silence);
77 MAKE_FUNC(snd_pcm_mmap_begin);
78 MAKE_FUNC(snd_pcm_mmap_commit);
79 MAKE_FUNC(snd_pcm_readi);
80 MAKE_FUNC(snd_pcm_writei);
81 MAKE_FUNC(snd_pcm_drain);
82 MAKE_FUNC(snd_pcm_recover);
83 MAKE_FUNC(snd_pcm_info_malloc);
84 MAKE_FUNC(snd_pcm_info_free);
85 MAKE_FUNC(snd_pcm_info_set_device);
86 MAKE_FUNC(snd_pcm_info_set_subdevice);
87 MAKE_FUNC(snd_pcm_info_set_stream);
88 MAKE_FUNC(snd_pcm_info_get_name);
89 MAKE_FUNC(snd_ctl_pcm_next_device);
90 MAKE_FUNC(snd_ctl_pcm_info);
91 MAKE_FUNC(snd_ctl_open);
92 MAKE_FUNC(snd_ctl_close);
93 MAKE_FUNC(snd_ctl_card_info_malloc);
94 MAKE_FUNC(snd_ctl_card_info_free);
95 MAKE_FUNC(snd_ctl_card_info);
96 MAKE_FUNC(snd_ctl_card_info_get_name);
97 MAKE_FUNC(snd_ctl_card_info_get_id);
98 MAKE_FUNC(snd_card_next);
99 #undef MAKE_FUNC
101 #define snd_strerror psnd_strerror
102 #define snd_pcm_open psnd_pcm_open
103 #define snd_pcm_close psnd_pcm_close
104 #define snd_pcm_nonblock psnd_pcm_nonblock
105 #define snd_pcm_frames_to_bytes psnd_pcm_frames_to_bytes
106 #define snd_pcm_bytes_to_frames psnd_pcm_bytes_to_frames
107 #define snd_pcm_hw_params_malloc psnd_pcm_hw_params_malloc
108 #define snd_pcm_hw_params_free psnd_pcm_hw_params_free
109 #define snd_pcm_hw_params_any psnd_pcm_hw_params_any
110 #define snd_pcm_hw_params_set_access psnd_pcm_hw_params_set_access
111 #define snd_pcm_hw_params_set_format psnd_pcm_hw_params_set_format
112 #define snd_pcm_hw_params_set_channels psnd_pcm_hw_params_set_channels
113 #define snd_pcm_hw_params_set_periods_near psnd_pcm_hw_params_set_periods_near
114 #define snd_pcm_hw_params_set_rate_near psnd_pcm_hw_params_set_rate_near
115 #define snd_pcm_hw_params_set_rate psnd_pcm_hw_params_set_rate
116 #define snd_pcm_hw_params_set_rate_resample psnd_pcm_hw_params_set_rate_resample
117 #define snd_pcm_hw_params_set_buffer_time_near psnd_pcm_hw_params_set_buffer_time_near
118 #define snd_pcm_hw_params_set_period_time_near psnd_pcm_hw_params_set_period_time_near
119 #define snd_pcm_hw_params_set_buffer_size_near psnd_pcm_hw_params_set_buffer_size_near
120 #define snd_pcm_hw_params_set_period_size_near psnd_pcm_hw_params_set_period_size_near
121 #define snd_pcm_hw_params_set_buffer_size_min psnd_pcm_hw_params_set_buffer_size_min
122 #define snd_pcm_hw_params_get_buffer_size psnd_pcm_hw_params_get_buffer_size
123 #define snd_pcm_hw_params_get_period_size psnd_pcm_hw_params_get_period_size
124 #define snd_pcm_hw_params_get_access psnd_pcm_hw_params_get_access
125 #define snd_pcm_hw_params_get_periods psnd_pcm_hw_params_get_periods
126 #define snd_pcm_hw_params psnd_pcm_hw_params
127 #define snd_pcm_sw_params_malloc psnd_pcm_sw_params_malloc
128 #define snd_pcm_sw_params_current psnd_pcm_sw_params_current
129 #define snd_pcm_sw_params_set_avail_min psnd_pcm_sw_params_set_avail_min
130 #define snd_pcm_sw_params_set_stop_threshold psnd_pcm_sw_params_set_stop_threshold
131 #define snd_pcm_sw_params psnd_pcm_sw_params
132 #define snd_pcm_sw_params_free psnd_pcm_sw_params_free
133 #define snd_pcm_prepare psnd_pcm_prepare
134 #define snd_pcm_start psnd_pcm_start
135 #define snd_pcm_resume psnd_pcm_resume
136 #define snd_pcm_wait psnd_pcm_wait
137 #define snd_pcm_state psnd_pcm_state
138 #define snd_pcm_avail_update psnd_pcm_avail_update
139 #define snd_pcm_areas_silence psnd_pcm_areas_silence
140 #define snd_pcm_mmap_begin psnd_pcm_mmap_begin
141 #define snd_pcm_mmap_commit psnd_pcm_mmap_commit
142 #define snd_pcm_readi psnd_pcm_readi
143 #define snd_pcm_writei psnd_pcm_writei
144 #define snd_pcm_drain psnd_pcm_drain
145 #define snd_pcm_recover psnd_pcm_recover
146 #define snd_pcm_info_malloc psnd_pcm_info_malloc
147 #define snd_pcm_info_free psnd_pcm_info_free
148 #define snd_pcm_info_set_device psnd_pcm_info_set_device
149 #define snd_pcm_info_set_subdevice psnd_pcm_info_set_subdevice
150 #define snd_pcm_info_set_stream psnd_pcm_info_set_stream
151 #define snd_pcm_info_get_name psnd_pcm_info_get_name
152 #define snd_ctl_pcm_next_device psnd_ctl_pcm_next_device
153 #define snd_ctl_pcm_info psnd_ctl_pcm_info
154 #define snd_ctl_open psnd_ctl_open
155 #define snd_ctl_close psnd_ctl_close
156 #define snd_ctl_card_info_malloc psnd_ctl_card_info_malloc
157 #define snd_ctl_card_info_free psnd_ctl_card_info_free
158 #define snd_ctl_card_info psnd_ctl_card_info
159 #define snd_ctl_card_info_get_name psnd_ctl_card_info_get_name
160 #define snd_ctl_card_info_get_id psnd_ctl_card_info_get_id
161 #define snd_card_next psnd_card_next
162 #endif
165 static ALCboolean alsa_load(void)
167 if(!alsa_handle)
169 #ifdef HAVE_DYNLOAD
170 alsa_handle = LoadLib("libasound.so.2");
171 if(!alsa_handle)
172 return ALC_FALSE;
174 #define LOAD_FUNC(f) do { \
175 p##f = GetSymbol(alsa_handle, #f); \
176 if(p##f == NULL) { \
177 CloseLib(alsa_handle); \
178 alsa_handle = NULL; \
179 return ALC_FALSE; \
181 } while(0)
182 LOAD_FUNC(snd_strerror);
183 LOAD_FUNC(snd_pcm_open);
184 LOAD_FUNC(snd_pcm_close);
185 LOAD_FUNC(snd_pcm_nonblock);
186 LOAD_FUNC(snd_pcm_frames_to_bytes);
187 LOAD_FUNC(snd_pcm_bytes_to_frames);
188 LOAD_FUNC(snd_pcm_hw_params_malloc);
189 LOAD_FUNC(snd_pcm_hw_params_free);
190 LOAD_FUNC(snd_pcm_hw_params_any);
191 LOAD_FUNC(snd_pcm_hw_params_set_access);
192 LOAD_FUNC(snd_pcm_hw_params_set_format);
193 LOAD_FUNC(snd_pcm_hw_params_set_channels);
194 LOAD_FUNC(snd_pcm_hw_params_set_periods_near);
195 LOAD_FUNC(snd_pcm_hw_params_set_rate_near);
196 LOAD_FUNC(snd_pcm_hw_params_set_rate);
197 LOAD_FUNC(snd_pcm_hw_params_set_rate_resample);
198 LOAD_FUNC(snd_pcm_hw_params_set_buffer_time_near);
199 LOAD_FUNC(snd_pcm_hw_params_set_period_time_near);
200 LOAD_FUNC(snd_pcm_hw_params_set_buffer_size_near);
201 LOAD_FUNC(snd_pcm_hw_params_set_buffer_size_min);
202 LOAD_FUNC(snd_pcm_hw_params_set_period_size_near);
203 LOAD_FUNC(snd_pcm_hw_params_get_buffer_size);
204 LOAD_FUNC(snd_pcm_hw_params_get_period_size);
205 LOAD_FUNC(snd_pcm_hw_params_get_access);
206 LOAD_FUNC(snd_pcm_hw_params_get_periods);
207 LOAD_FUNC(snd_pcm_hw_params);
208 LOAD_FUNC(snd_pcm_sw_params_malloc);
209 LOAD_FUNC(snd_pcm_sw_params_current);
210 LOAD_FUNC(snd_pcm_sw_params_set_avail_min);
211 LOAD_FUNC(snd_pcm_sw_params_set_stop_threshold);
212 LOAD_FUNC(snd_pcm_sw_params);
213 LOAD_FUNC(snd_pcm_sw_params_free);
214 LOAD_FUNC(snd_pcm_prepare);
215 LOAD_FUNC(snd_pcm_start);
216 LOAD_FUNC(snd_pcm_resume);
217 LOAD_FUNC(snd_pcm_wait);
218 LOAD_FUNC(snd_pcm_state);
219 LOAD_FUNC(snd_pcm_avail_update);
220 LOAD_FUNC(snd_pcm_areas_silence);
221 LOAD_FUNC(snd_pcm_mmap_begin);
222 LOAD_FUNC(snd_pcm_mmap_commit);
223 LOAD_FUNC(snd_pcm_readi);
224 LOAD_FUNC(snd_pcm_writei);
225 LOAD_FUNC(snd_pcm_drain);
226 LOAD_FUNC(snd_pcm_recover);
227 LOAD_FUNC(snd_pcm_info_malloc);
228 LOAD_FUNC(snd_pcm_info_free);
229 LOAD_FUNC(snd_pcm_info_set_device);
230 LOAD_FUNC(snd_pcm_info_set_subdevice);
231 LOAD_FUNC(snd_pcm_info_set_stream);
232 LOAD_FUNC(snd_pcm_info_get_name);
233 LOAD_FUNC(snd_ctl_pcm_next_device);
234 LOAD_FUNC(snd_ctl_pcm_info);
235 LOAD_FUNC(snd_ctl_open);
236 LOAD_FUNC(snd_ctl_close);
237 LOAD_FUNC(snd_ctl_card_info_malloc);
238 LOAD_FUNC(snd_ctl_card_info_free);
239 LOAD_FUNC(snd_ctl_card_info);
240 LOAD_FUNC(snd_ctl_card_info_get_name);
241 LOAD_FUNC(snd_ctl_card_info_get_id);
242 LOAD_FUNC(snd_card_next);
243 #undef LOAD_FUNC
244 #else
245 alsa_handle = (void*)0xDEADBEEF;
246 #endif
248 return ALC_TRUE;
252 typedef struct {
253 snd_pcm_t *pcmHandle;
255 ALvoid *buffer;
256 ALsizei size;
258 ALboolean doCapture;
259 RingBuffer *ring;
261 volatile int killNow;
262 ALvoid *thread;
263 } alsa_data;
265 typedef struct {
266 ALCchar *name;
267 char *card;
268 int dev;
269 } DevMap;
271 static DevMap *allDevNameMap;
272 static ALuint numDevNames;
273 static DevMap *allCaptureDevNameMap;
274 static ALuint numCaptureDevNames;
276 static const char *device_prefix;
277 static const char *capture_prefix;
280 static DevMap *probe_devices(snd_pcm_stream_t stream, ALuint *count)
282 snd_ctl_t *handle;
283 int card, err, dev, idx;
284 snd_ctl_card_info_t *info;
285 snd_pcm_info_t *pcminfo;
286 DevMap *DevList;
287 char name[1024];
289 snd_ctl_card_info_malloc(&info);
290 snd_pcm_info_malloc(&pcminfo);
292 card = -1;
293 if((err=snd_card_next(&card)) < 0)
294 ERR("Failed to find a card: %s\n", snd_strerror(err));
296 DevList = malloc(sizeof(DevMap) * 1);
297 DevList[0].name = strdup("ALSA Default");
298 DevList[0].card = NULL;
299 DevList[0].dev = 0;
300 idx = 1;
301 while(card >= 0)
303 sprintf(name, "hw:%d", card);
304 if((err = snd_ctl_open(&handle, name, 0)) < 0)
306 ERR("control open (%i): %s\n", card, snd_strerror(err));
307 goto next_card;
309 if((err = snd_ctl_card_info(handle, info)) < 0)
311 ERR("control hardware info (%i): %s\n", card, snd_strerror(err));
312 snd_ctl_close(handle);
313 goto next_card;
316 dev = -1;
317 while(1)
319 const char *cname, *dname, *cid;
320 void *temp;
322 if(snd_ctl_pcm_next_device(handle, &dev) < 0)
323 ERR("snd_ctl_pcm_next_device failed\n");
324 if(dev < 0)
325 break;
327 snd_pcm_info_set_device(pcminfo, dev);
328 snd_pcm_info_set_subdevice(pcminfo, 0);
329 snd_pcm_info_set_stream(pcminfo, stream);
330 if((err = snd_ctl_pcm_info(handle, pcminfo)) < 0) {
331 if(err != -ENOENT)
332 ERR("control digital audio info (%i): %s\n", card, snd_strerror(err));
333 continue;
336 temp = realloc(DevList, sizeof(DevMap) * (idx+1));
337 if(temp)
339 DevList = temp;
340 cname = snd_ctl_card_info_get_name(info);
341 dname = snd_pcm_info_get_name(pcminfo);
342 cid = snd_ctl_card_info_get_id(info);
343 snprintf(name, sizeof(name), "%s, %s (CARD=%s,DEV=%d)",
344 cname, dname, cid, dev);
345 DevList[idx].name = strdup(name);
346 DevList[idx].card = strdup(cid);
347 DevList[idx].dev = dev;
348 idx++;
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);
362 *count = idx;
363 return DevList;
367 static int xrun_recovery(snd_pcm_t *handle, int err)
369 err = snd_pcm_recover(handle, err, 1);
370 if(err < 0)
371 ERR("recover failed: %s\n", snd_strerror(err));
372 return err;
375 static int verify_state(snd_pcm_t *handle)
377 snd_pcm_state_t state = snd_pcm_state(handle);
378 if(state == SND_PCM_STATE_DISCONNECTED)
379 return -ENODEV;
380 if(state == SND_PCM_STATE_XRUN)
382 int err = xrun_recovery(handle, -EPIPE);
383 if(err < 0) return err;
385 else if(state == SND_PCM_STATE_SUSPENDED)
387 int err = xrun_recovery(handle, -ESTRPIPE);
388 if(err < 0) return err;
391 return state;
395 static ALuint ALSAProc(ALvoid *ptr)
397 ALCdevice *pDevice = (ALCdevice*)ptr;
398 alsa_data *data = (alsa_data*)pDevice->ExtraData;
399 const snd_pcm_channel_area_t *areas = NULL;
400 snd_pcm_sframes_t avail, commitres;
401 snd_pcm_uframes_t offset, frames;
402 char *WritePtr;
403 int err;
405 SetRTPriority();
407 while(!data->killNow)
409 int state = verify_state(data->pcmHandle);
410 if(state < 0)
412 ERR("Invalid state detected: %s\n", snd_strerror(state));
413 aluHandleDisconnect(pDevice);
414 break;
417 avail = snd_pcm_avail_update(data->pcmHandle);
418 if(avail < 0)
420 ERR("available update failed: %s\n", snd_strerror(avail));
421 continue;
424 // make sure there's frames to process
425 if((snd_pcm_uframes_t)avail < pDevice->UpdateSize)
427 if(state != SND_PCM_STATE_RUNNING)
429 err = snd_pcm_start(data->pcmHandle);
430 if(err < 0)
432 ERR("start failed: %s\n", snd_strerror(err));
433 continue;
436 if(snd_pcm_wait(data->pcmHandle, 1000) == 0)
437 ERR("Wait timeout... buffer size too low?\n");
438 continue;
440 avail -= avail%pDevice->UpdateSize;
442 // it is possible that contiguous areas are smaller, thus we use a loop
443 while(avail > 0)
445 frames = avail;
447 err = snd_pcm_mmap_begin(data->pcmHandle, &areas, &offset, &frames);
448 if(err < 0)
450 ERR("mmap begin error: %s\n", snd_strerror(err));
451 break;
454 WritePtr = (char*)areas->addr + (offset * areas->step / 8);
455 aluMixData(pDevice, WritePtr, frames);
457 commitres = snd_pcm_mmap_commit(data->pcmHandle, offset, frames);
458 if(commitres < 0 || (commitres-frames) != 0)
460 ERR("mmap commit error: %s\n",
461 snd_strerror(commitres >= 0 ? -EPIPE : commitres));
462 break;
465 avail -= frames;
469 return 0;
472 static ALuint ALSANoMMapProc(ALvoid *ptr)
474 ALCdevice *pDevice = (ALCdevice*)ptr;
475 alsa_data *data = (alsa_data*)pDevice->ExtraData;
476 snd_pcm_sframes_t avail;
477 char *WritePtr;
479 SetRTPriority();
481 while(!data->killNow)
483 int state = verify_state(data->pcmHandle);
484 if(state < 0)
486 ERR("Invalid state detected: %s\n", snd_strerror(state));
487 aluHandleDisconnect(pDevice);
488 break;
491 WritePtr = data->buffer;
492 avail = data->size / snd_pcm_frames_to_bytes(data->pcmHandle, 1);
493 aluMixData(pDevice, WritePtr, avail);
495 while(avail > 0)
497 int ret = snd_pcm_writei(data->pcmHandle, WritePtr, avail);
498 switch (ret)
500 case -EAGAIN:
501 continue;
502 case -ESTRPIPE:
503 case -EPIPE:
504 case -EINTR:
505 ret = snd_pcm_recover(data->pcmHandle, ret, 1);
506 if(ret < 0)
507 avail = 0;
508 break;
509 default:
510 if (ret >= 0)
512 WritePtr += snd_pcm_frames_to_bytes(data->pcmHandle, ret);
513 avail -= ret;
515 break;
517 if (ret < 0)
519 ret = snd_pcm_prepare(data->pcmHandle);
520 if(ret < 0)
521 break;
526 return 0;
529 static ALCenum alsa_open_playback(ALCdevice *device, const ALCchar *deviceName)
531 const char *driver = "default";
532 alsa_data *data;
533 char str[128];
534 int i;
536 ConfigValueStr("alsa", "device", &driver);
538 if(!deviceName)
539 deviceName = alsaDevice;
540 else if(strcmp(deviceName, alsaDevice) != 0)
542 size_t idx;
544 if(!allDevNameMap)
545 allDevNameMap = probe_devices(SND_PCM_STREAM_PLAYBACK, &numDevNames);
547 for(idx = 0;idx < numDevNames;idx++)
549 if(allDevNameMap[idx].name &&
550 strcmp(deviceName, allDevNameMap[idx].name) == 0)
552 if(idx > 0)
554 snprintf(str, sizeof(str), "%sCARD=%s,DEV=%d", device_prefix,
555 allDevNameMap[idx].card, allDevNameMap[idx].dev);
556 driver = str;
558 break;
561 if(idx == numDevNames)
562 return ALC_INVALID_VALUE;
565 data = (alsa_data*)calloc(1, sizeof(alsa_data));
567 i = snd_pcm_open(&data->pcmHandle, driver, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
568 if(i >= 0)
570 i = snd_pcm_nonblock(data->pcmHandle, 0);
571 if(i < 0)
572 snd_pcm_close(data->pcmHandle);
574 if(i < 0)
576 free(data);
577 ERR("Could not open playback device '%s': %s\n", driver, snd_strerror(i));
578 return ALC_OUT_OF_MEMORY;
581 device->szDeviceName = strdup(deviceName);
582 device->ExtraData = data;
583 return ALC_NO_ERROR;
586 static void alsa_close_playback(ALCdevice *device)
588 alsa_data *data = (alsa_data*)device->ExtraData;
590 snd_pcm_close(data->pcmHandle);
591 free(data);
592 device->ExtraData = NULL;
595 static ALCboolean alsa_reset_playback(ALCdevice *device)
597 alsa_data *data = (alsa_data*)device->ExtraData;
598 snd_pcm_uframes_t periodSizeInFrames;
599 unsigned int periodLen, bufferLen;
600 snd_pcm_sw_params_t *sp = NULL;
601 snd_pcm_hw_params_t *p = NULL;
602 snd_pcm_access_t access;
603 snd_pcm_format_t format;
604 unsigned int periods;
605 unsigned int rate;
606 int allowmmap;
607 char *err;
608 int i;
611 format = -1;
612 switch(device->FmtType)
614 case DevFmtByte:
615 format = SND_PCM_FORMAT_S8;
616 break;
617 case DevFmtUByte:
618 format = SND_PCM_FORMAT_U8;
619 break;
620 case DevFmtShort:
621 format = SND_PCM_FORMAT_S16;
622 break;
623 case DevFmtUShort:
624 format = SND_PCM_FORMAT_U16;
625 break;
626 case DevFmtFloat:
627 format = SND_PCM_FORMAT_FLOAT;
628 break;
631 allowmmap = GetConfigValueBool("alsa", "mmap", 1);
632 periods = device->NumUpdates;
633 periodLen = (ALuint64)device->UpdateSize * 1000000 / device->Frequency;
634 bufferLen = periodLen * periods;
635 rate = device->Frequency;
637 err = NULL;
638 snd_pcm_hw_params_malloc(&p);
640 if((i=snd_pcm_hw_params_any(data->pcmHandle, p)) < 0)
641 err = "any";
642 /* set interleaved access */
643 if(i >= 0 && (!allowmmap || (i=snd_pcm_hw_params_set_access(data->pcmHandle, p, SND_PCM_ACCESS_MMAP_INTERLEAVED)) < 0))
645 if(periods > 2)
647 periods--;
648 bufferLen = periodLen * periods;
650 if((i=snd_pcm_hw_params_set_access(data->pcmHandle, p, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
651 err = "set access";
653 /* set format (implicitly sets sample bits) */
654 if(i >= 0 && (i=snd_pcm_hw_params_set_format(data->pcmHandle, p, format)) < 0)
656 device->FmtType = DevFmtFloat;
657 if(format == SND_PCM_FORMAT_FLOAT ||
658 (i=snd_pcm_hw_params_set_format(data->pcmHandle, p, SND_PCM_FORMAT_FLOAT)) < 0)
660 device->FmtType = DevFmtShort;
661 if(format == SND_PCM_FORMAT_S16 ||
662 (i=snd_pcm_hw_params_set_format(data->pcmHandle, p, SND_PCM_FORMAT_S16)) < 0)
664 device->FmtType = DevFmtUByte;
665 if(format == SND_PCM_FORMAT_U8 ||
666 (i=snd_pcm_hw_params_set_format(data->pcmHandle, p, SND_PCM_FORMAT_U8)) < 0)
667 err = "set format";
671 /* set channels (implicitly sets frame bits) */
672 if(i >= 0 && (i=snd_pcm_hw_params_set_channels(data->pcmHandle, p, ChannelsFromDevFmt(device->FmtChans))) < 0)
674 if((i=snd_pcm_hw_params_set_channels(data->pcmHandle, p, 2)) < 0)
676 if((i=snd_pcm_hw_params_set_channels(data->pcmHandle, p, 1)) < 0)
677 err = "set channels";
678 else
680 if((device->Flags&DEVICE_CHANNELS_REQUEST))
681 ERR("Failed to set %s, got Mono instead\n", DevFmtChannelsString(device->FmtChans));
682 device->FmtChans = DevFmtMono;
685 else
687 if((device->Flags&DEVICE_CHANNELS_REQUEST))
688 ERR("Failed to set %s, got Stereo instead\n", DevFmtChannelsString(device->FmtChans));
689 device->FmtChans = DevFmtStereo;
691 device->Flags &= ~DEVICE_CHANNELS_REQUEST;
693 if(i >= 0 && (i=snd_pcm_hw_params_set_rate_resample(data->pcmHandle, p, 0)) < 0)
695 ERR("Failed to disable ALSA resampler\n");
696 i = 0;
698 /* set rate (implicitly constrains period/buffer parameters) */
699 if(i >= 0 && (i=snd_pcm_hw_params_set_rate_near(data->pcmHandle, p, &rate, NULL)) < 0)
700 err = "set rate near";
701 /* set buffer time (implicitly constrains period/buffer parameters) */
702 if(i >= 0 && (i=snd_pcm_hw_params_set_buffer_time_near(data->pcmHandle, p, &bufferLen, NULL)) < 0)
703 err = "set buffer time near";
704 /* set period time in frame units (implicitly sets buffer size/bytes/time and period size/bytes) */
705 if(i >= 0 && (i=snd_pcm_hw_params_set_period_time_near(data->pcmHandle, p, &periodLen, NULL)) < 0)
706 err = "set period time near";
707 /* install and prepare hardware configuration */
708 if(i >= 0 && (i=snd_pcm_hw_params(data->pcmHandle, p)) < 0)
709 err = "set params";
710 if(i >= 0 && (i=snd_pcm_hw_params_get_access(p, &access)) < 0)
711 err = "get access";
712 if(i >= 0 && (i=snd_pcm_hw_params_get_period_size(p, &periodSizeInFrames, NULL)) < 0)
713 err = "get period size";
714 if(i >= 0 && (i=snd_pcm_hw_params_get_periods(p, &periods, NULL)) < 0)
715 err = "get periods";
716 if(i < 0)
718 ERR("%s failed: %s\n", err, snd_strerror(i));
719 snd_pcm_hw_params_free(p);
720 return ALC_FALSE;
723 snd_pcm_hw_params_free(p);
725 err = NULL;
726 snd_pcm_sw_params_malloc(&sp);
728 if((i=snd_pcm_sw_params_current(data->pcmHandle, sp)) != 0)
729 err = "sw current";
730 if(i == 0 && (i=snd_pcm_sw_params_set_avail_min(data->pcmHandle, sp, periodSizeInFrames)) != 0)
731 err = "sw set avail min";
732 if(i == 0 && (i=snd_pcm_sw_params_set_stop_threshold(data->pcmHandle, sp, periodSizeInFrames*periods)) != 0)
733 err = "sw set stop threshold";
734 if(i == 0 && (i=snd_pcm_sw_params(data->pcmHandle, sp)) != 0)
735 err = "sw set params";
736 if(i != 0)
738 ERR("%s failed: %s\n", err, snd_strerror(i));
739 snd_pcm_sw_params_free(sp);
740 return ALC_FALSE;
743 snd_pcm_sw_params_free(sp);
745 if(device->Frequency != rate)
747 if((device->Flags&DEVICE_FREQUENCY_REQUEST))
748 ERR("Failed to set %dhz, got %dhz instead\n", device->Frequency, rate);
749 device->Flags &= ~DEVICE_FREQUENCY_REQUEST;
750 device->Frequency = rate;
753 SetDefaultChannelOrder(device);
755 data->size = snd_pcm_frames_to_bytes(data->pcmHandle, periodSizeInFrames);
756 if(access == SND_PCM_ACCESS_RW_INTERLEAVED)
758 /* Increase periods by one, since the temp buffer counts as an extra
759 * period */
760 periods++;
761 data->buffer = malloc(data->size);
762 if(!data->buffer)
764 ERR("buffer malloc failed\n");
765 return ALC_FALSE;
767 device->UpdateSize = periodSizeInFrames;
768 device->NumUpdates = periods;
769 data->thread = StartThread(ALSANoMMapProc, device);
771 else
773 i = snd_pcm_prepare(data->pcmHandle);
774 if(i < 0)
776 ERR("prepare error: %s\n", snd_strerror(i));
777 return ALC_FALSE;
779 device->UpdateSize = periodSizeInFrames;
780 device->NumUpdates = periods;
781 data->thread = StartThread(ALSAProc, device);
783 if(data->thread == NULL)
785 ERR("Could not create playback thread\n");
786 free(data->buffer);
787 data->buffer = NULL;
788 return ALC_FALSE;
791 return ALC_TRUE;
794 static void alsa_stop_playback(ALCdevice *device)
796 alsa_data *data = (alsa_data*)device->ExtraData;
798 if(data->thread)
800 data->killNow = 1;
801 StopThread(data->thread);
802 data->thread = NULL;
804 data->killNow = 0;
805 free(data->buffer);
806 data->buffer = NULL;
810 static ALCenum alsa_open_capture(ALCdevice *pDevice, const ALCchar *deviceName)
812 const char *driver = "default";
813 snd_pcm_hw_params_t *p;
814 snd_pcm_uframes_t bufferSizeInFrames;
815 snd_pcm_uframes_t periodSizeInFrames;
816 snd_pcm_format_t format;
817 ALuint frameSize;
818 alsa_data *data;
819 char str[128];
820 char *err;
821 int i;
823 ConfigValueStr("alsa", "capture", &driver);
825 if(!allCaptureDevNameMap)
826 allCaptureDevNameMap = probe_devices(SND_PCM_STREAM_CAPTURE, &numCaptureDevNames);
828 if(!deviceName)
829 deviceName = allCaptureDevNameMap[0].name;
830 else
832 size_t idx;
834 for(idx = 0;idx < numCaptureDevNames;idx++)
836 if(allCaptureDevNameMap[idx].name &&
837 strcmp(deviceName, allCaptureDevNameMap[idx].name) == 0)
839 if(idx > 0)
841 snprintf(str, sizeof(str), "%sCARD=%s,DEV=%d", capture_prefix,
842 allCaptureDevNameMap[idx].card, allCaptureDevNameMap[idx].dev);
843 driver = str;
845 break;
848 if(idx == numCaptureDevNames)
849 return ALC_INVALID_VALUE;
852 data = (alsa_data*)calloc(1, sizeof(alsa_data));
854 i = snd_pcm_open(&data->pcmHandle, driver, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK);
855 if(i < 0)
857 ERR("Could not open capture device '%s': %s\n", driver, snd_strerror(i));
858 free(data);
859 return ALC_INVALID_VALUE;
862 format = -1;
863 switch(pDevice->FmtType)
865 case DevFmtByte:
866 format = SND_PCM_FORMAT_S8;
867 break;
868 case DevFmtUByte:
869 format = SND_PCM_FORMAT_U8;
870 break;
871 case DevFmtShort:
872 format = SND_PCM_FORMAT_S16;
873 break;
874 case DevFmtUShort:
875 format = SND_PCM_FORMAT_U16;
876 break;
877 case DevFmtFloat:
878 format = SND_PCM_FORMAT_FLOAT;
879 break;
882 err = NULL;
883 bufferSizeInFrames = maxu(pDevice->UpdateSize*pDevice->NumUpdates,
884 100*pDevice->Frequency/1000);
885 periodSizeInFrames = minu(bufferSizeInFrames, 50*pDevice->Frequency/1000);
886 snd_pcm_hw_params_malloc(&p);
888 if((i=snd_pcm_hw_params_any(data->pcmHandle, p)) < 0)
889 err = "any";
890 /* set interleaved access */
891 if(i >= 0 && (i=snd_pcm_hw_params_set_access(data->pcmHandle, p, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
892 err = "set access";
893 /* set format (implicitly sets sample bits) */
894 if(i >= 0 && (i=snd_pcm_hw_params_set_format(data->pcmHandle, p, format)) < 0)
895 err = "set format";
896 /* set channels (implicitly sets frame bits) */
897 if(i >= 0 && (i=snd_pcm_hw_params_set_channels(data->pcmHandle, p, ChannelsFromDevFmt(pDevice->FmtChans))) < 0)
898 err = "set channels";
899 /* set rate (implicitly constrains period/buffer parameters) */
900 if(i >= 0 && (i=snd_pcm_hw_params_set_rate(data->pcmHandle, p, pDevice->Frequency, 0)) < 0)
901 err = "set rate near";
902 /* set buffer size in frame units (implicitly sets period size/bytes/time and buffer time/bytes) */
903 if(i >= 0 && (i=snd_pcm_hw_params_set_buffer_size_near(data->pcmHandle, p, &bufferSizeInFrames)) < 0)
904 err = "set buffer size near";
905 /* set buffer size in frame units (implicitly sets period size/bytes/time and buffer time/bytes) */
906 if(i >= 0 && (i=snd_pcm_hw_params_set_period_size_near(data->pcmHandle, p, &periodSizeInFrames, NULL)) < 0)
907 err = "set period size near";
908 /* install and prepare hardware configuration */
909 if(i >= 0 && (i=snd_pcm_hw_params(data->pcmHandle, p)) < 0)
910 err = "set params";
911 if(i < 0)
913 ERR("%s failed: %s\n", err, snd_strerror(i));
914 snd_pcm_hw_params_free(p);
915 goto error;
918 if((i=snd_pcm_hw_params_get_period_size(p, &bufferSizeInFrames, NULL)) < 0)
920 ERR("get size failed: %s\n", snd_strerror(i));
921 snd_pcm_hw_params_free(p);
922 goto error;
925 snd_pcm_hw_params_free(p);
927 frameSize = FrameSizeFromDevFmt(pDevice->FmtChans, pDevice->FmtType);
929 data->ring = CreateRingBuffer(frameSize, pDevice->UpdateSize*pDevice->NumUpdates);
930 if(!data->ring)
932 ERR("ring buffer create failed\n");
933 goto error;
936 data->size = snd_pcm_frames_to_bytes(data->pcmHandle, bufferSizeInFrames);
937 data->buffer = malloc(data->size);
938 if(!data->buffer)
940 ERR("buffer malloc failed\n");
941 goto error;
944 pDevice->szDeviceName = strdup(deviceName);
946 pDevice->ExtraData = data;
947 return ALC_NO_ERROR;
949 error:
950 free(data->buffer);
951 DestroyRingBuffer(data->ring);
952 snd_pcm_close(data->pcmHandle);
953 free(data);
955 pDevice->ExtraData = NULL;
956 return ALC_INVALID_VALUE;
959 static void alsa_close_capture(ALCdevice *pDevice)
961 alsa_data *data = (alsa_data*)pDevice->ExtraData;
963 snd_pcm_close(data->pcmHandle);
964 DestroyRingBuffer(data->ring);
966 free(data->buffer);
967 free(data);
968 pDevice->ExtraData = NULL;
971 static void alsa_start_capture(ALCdevice *Device)
973 alsa_data *data = (alsa_data*)Device->ExtraData;
974 int err;
976 err = snd_pcm_start(data->pcmHandle);
977 if(err < 0)
979 ERR("start failed: %s\n", snd_strerror(err));
980 aluHandleDisconnect(Device);
982 else
983 data->doCapture = AL_TRUE;
986 static void alsa_stop_capture(ALCdevice *Device)
988 alsa_data *data = (alsa_data*)Device->ExtraData;
989 snd_pcm_drain(data->pcmHandle);
990 data->doCapture = AL_FALSE;
993 static ALCenum alsa_capture_samples(ALCdevice *Device, ALCvoid *Buffer, ALCuint Samples)
995 alsa_data *data = (alsa_data*)Device->ExtraData;
996 ReadRingBuffer(data->ring, Buffer, Samples);
997 return ALC_NO_ERROR;
1000 static ALCuint alsa_available_samples(ALCdevice *Device)
1002 alsa_data *data = (alsa_data*)Device->ExtraData;
1003 snd_pcm_sframes_t avail;
1005 avail = (Device->Connected ? snd_pcm_avail_update(data->pcmHandle) : 0);
1006 if(avail < 0)
1008 ERR("avail update failed: %s\n", snd_strerror(avail));
1010 if((avail=snd_pcm_recover(data->pcmHandle, avail, 1)) >= 0)
1012 if(data->doCapture)
1013 avail = snd_pcm_start(data->pcmHandle);
1014 if(avail >= 0)
1015 avail = snd_pcm_avail_update(data->pcmHandle);
1017 if(avail < 0)
1019 ERR("restore error: %s\n", snd_strerror(avail));
1020 aluHandleDisconnect(Device);
1023 while(avail > 0)
1025 snd_pcm_sframes_t amt;
1027 amt = snd_pcm_bytes_to_frames(data->pcmHandle, data->size);
1028 if(avail < amt) amt = avail;
1030 amt = snd_pcm_readi(data->pcmHandle, data->buffer, amt);
1031 if(amt < 0)
1033 ERR("read error: %s\n", snd_strerror(amt));
1035 if(amt == -EAGAIN)
1036 continue;
1037 if((amt=snd_pcm_recover(data->pcmHandle, amt, 1)) >= 0)
1039 if(data->doCapture)
1040 amt = snd_pcm_start(data->pcmHandle);
1041 if(amt >= 0)
1042 amt = snd_pcm_avail_update(data->pcmHandle);
1044 if(amt < 0)
1046 ERR("restore error: %s\n", snd_strerror(amt));
1047 aluHandleDisconnect(Device);
1048 break;
1050 avail = amt;
1051 continue;
1054 WriteRingBuffer(data->ring, data->buffer, amt);
1055 avail -= amt;
1058 return RingBufferSize(data->ring);
1062 static const BackendFuncs alsa_funcs = {
1063 alsa_open_playback,
1064 alsa_close_playback,
1065 alsa_reset_playback,
1066 alsa_stop_playback,
1067 alsa_open_capture,
1068 alsa_close_capture,
1069 alsa_start_capture,
1070 alsa_stop_capture,
1071 alsa_capture_samples,
1072 alsa_available_samples
1075 ALCboolean alc_alsa_init(BackendFuncs *func_list)
1077 if(!alsa_load())
1078 return ALC_FALSE;
1079 device_prefix = GetConfigValue("alsa", "device-prefix", "plughw:");
1080 capture_prefix = GetConfigValue("alsa", "capture-prefix", "plughw:");
1081 *func_list = alsa_funcs;
1082 return ALC_TRUE;
1085 void alc_alsa_deinit(void)
1087 ALuint i;
1089 for(i = 0;i < numDevNames;++i)
1091 free(allDevNameMap[i].name);
1092 free(allDevNameMap[i].card);
1094 free(allDevNameMap);
1095 allDevNameMap = NULL;
1096 numDevNames = 0;
1098 for(i = 0;i < numCaptureDevNames;++i)
1100 free(allCaptureDevNameMap[i].name);
1101 free(allCaptureDevNameMap[i].card);
1103 free(allCaptureDevNameMap);
1104 allCaptureDevNameMap = NULL;
1105 numCaptureDevNames = 0;
1107 #ifdef HAVE_DYNLOAD
1108 if(alsa_handle)
1109 CloseLib(alsa_handle);
1110 alsa_handle = NULL;
1111 #endif
1114 void alc_alsa_probe(enum DevProbe type)
1116 ALuint i;
1118 switch(type)
1120 case DEVICE_PROBE:
1121 AppendDeviceList(alsaDevice);
1122 break;
1124 case ALL_DEVICE_PROBE:
1125 for(i = 0;i < numDevNames;++i)
1127 free(allDevNameMap[i].name);
1128 free(allDevNameMap[i].card);
1131 free(allDevNameMap);
1132 allDevNameMap = probe_devices(SND_PCM_STREAM_PLAYBACK, &numDevNames);
1134 for(i = 0;i < numDevNames;++i)
1135 AppendAllDeviceList(allDevNameMap[i].name);
1136 break;
1138 case CAPTURE_DEVICE_PROBE:
1139 for(i = 0;i < numCaptureDevNames;++i)
1141 free(allCaptureDevNameMap[i].name);
1142 free(allCaptureDevNameMap[i].card);
1145 free(allCaptureDevNameMap);
1146 allCaptureDevNameMap = probe_devices(SND_PCM_STREAM_CAPTURE, &numCaptureDevNames);
1148 for(i = 0;i < numCaptureDevNames;++i)
1149 AppendCaptureDeviceList(allCaptureDevNameMap[i].name);
1150 break;