view Alc/backends/alsa.c @ 0:f9476ff7637e

initial forking of open-al to create multiple listeners
author Robert McIntyre <rlm@mit.edu>
date Tue, 25 Oct 2011 13:02:31 -0700
parents
children
line wrap: on
line source
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.
8 *
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.
13 *
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
19 */
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);
68 MAKE_FUNC(snd_pcm_sw_params_free);
69 MAKE_FUNC(snd_pcm_prepare);
70 MAKE_FUNC(snd_pcm_start);
71 MAKE_FUNC(snd_pcm_resume);
72 MAKE_FUNC(snd_pcm_wait);
73 MAKE_FUNC(snd_pcm_state);
74 MAKE_FUNC(snd_pcm_avail_update);
75 MAKE_FUNC(snd_pcm_areas_silence);
76 MAKE_FUNC(snd_pcm_mmap_begin);
77 MAKE_FUNC(snd_pcm_mmap_commit);
78 MAKE_FUNC(snd_pcm_readi);
79 MAKE_FUNC(snd_pcm_writei);
80 MAKE_FUNC(snd_pcm_drain);
81 MAKE_FUNC(snd_pcm_recover);
82 MAKE_FUNC(snd_pcm_info_malloc);
83 MAKE_FUNC(snd_pcm_info_free);
84 MAKE_FUNC(snd_pcm_info_set_device);
85 MAKE_FUNC(snd_pcm_info_set_subdevice);
86 MAKE_FUNC(snd_pcm_info_set_stream);
87 MAKE_FUNC(snd_pcm_info_get_name);
88 MAKE_FUNC(snd_ctl_pcm_next_device);
89 MAKE_FUNC(snd_ctl_pcm_info);
90 MAKE_FUNC(snd_ctl_open);
91 MAKE_FUNC(snd_ctl_close);
92 MAKE_FUNC(snd_ctl_card_info_malloc);
93 MAKE_FUNC(snd_ctl_card_info_free);
94 MAKE_FUNC(snd_ctl_card_info);
95 MAKE_FUNC(snd_ctl_card_info_get_name);
96 MAKE_FUNC(snd_ctl_card_info_get_id);
97 MAKE_FUNC(snd_card_next);
98 #undef MAKE_FUNC
100 #define snd_strerror psnd_strerror
101 #define snd_pcm_open psnd_pcm_open
102 #define snd_pcm_close psnd_pcm_close
103 #define snd_pcm_nonblock psnd_pcm_nonblock
104 #define snd_pcm_frames_to_bytes psnd_pcm_frames_to_bytes
105 #define snd_pcm_bytes_to_frames psnd_pcm_bytes_to_frames
106 #define snd_pcm_hw_params_malloc psnd_pcm_hw_params_malloc
107 #define snd_pcm_hw_params_free psnd_pcm_hw_params_free
108 #define snd_pcm_hw_params_any psnd_pcm_hw_params_any
109 #define snd_pcm_hw_params_set_access psnd_pcm_hw_params_set_access
110 #define snd_pcm_hw_params_set_format psnd_pcm_hw_params_set_format
111 #define snd_pcm_hw_params_set_channels psnd_pcm_hw_params_set_channels
112 #define snd_pcm_hw_params_set_periods_near psnd_pcm_hw_params_set_periods_near
113 #define snd_pcm_hw_params_set_rate_near psnd_pcm_hw_params_set_rate_near
114 #define snd_pcm_hw_params_set_rate psnd_pcm_hw_params_set_rate
115 #define snd_pcm_hw_params_set_rate_resample psnd_pcm_hw_params_set_rate_resample
116 #define snd_pcm_hw_params_set_buffer_time_near psnd_pcm_hw_params_set_buffer_time_near
117 #define snd_pcm_hw_params_set_period_time_near psnd_pcm_hw_params_set_period_time_near
118 #define snd_pcm_hw_params_set_buffer_size_near psnd_pcm_hw_params_set_buffer_size_near
119 #define snd_pcm_hw_params_set_period_size_near psnd_pcm_hw_params_set_period_size_near
120 #define snd_pcm_hw_params_set_buffer_size_min psnd_pcm_hw_params_set_buffer_size_min
121 #define snd_pcm_hw_params_get_buffer_size psnd_pcm_hw_params_get_buffer_size
122 #define snd_pcm_hw_params_get_period_size psnd_pcm_hw_params_get_period_size
123 #define snd_pcm_hw_params_get_access psnd_pcm_hw_params_get_access
124 #define snd_pcm_hw_params_get_periods psnd_pcm_hw_params_get_periods
125 #define snd_pcm_hw_params psnd_pcm_hw_params
126 #define snd_pcm_sw_params_malloc psnd_pcm_sw_params_malloc
127 #define snd_pcm_sw_params_current psnd_pcm_sw_params_current
128 #define snd_pcm_sw_params_set_avail_min psnd_pcm_sw_params_set_avail_min
129 #define snd_pcm_sw_params psnd_pcm_sw_params
130 #define snd_pcm_sw_params_free psnd_pcm_sw_params_free
131 #define snd_pcm_prepare psnd_pcm_prepare
132 #define snd_pcm_start psnd_pcm_start
133 #define snd_pcm_resume psnd_pcm_resume
134 #define snd_pcm_wait psnd_pcm_wait
135 #define snd_pcm_state psnd_pcm_state
136 #define snd_pcm_avail_update psnd_pcm_avail_update
137 #define snd_pcm_areas_silence psnd_pcm_areas_silence
138 #define snd_pcm_mmap_begin psnd_pcm_mmap_begin
139 #define snd_pcm_mmap_commit psnd_pcm_mmap_commit
140 #define snd_pcm_readi psnd_pcm_readi
141 #define snd_pcm_writei psnd_pcm_writei
142 #define snd_pcm_drain psnd_pcm_drain
143 #define snd_pcm_recover psnd_pcm_recover
144 #define snd_pcm_info_malloc psnd_pcm_info_malloc
145 #define snd_pcm_info_free psnd_pcm_info_free
146 #define snd_pcm_info_set_device psnd_pcm_info_set_device
147 #define snd_pcm_info_set_subdevice psnd_pcm_info_set_subdevice
148 #define snd_pcm_info_set_stream psnd_pcm_info_set_stream
149 #define snd_pcm_info_get_name psnd_pcm_info_get_name
150 #define snd_ctl_pcm_next_device psnd_ctl_pcm_next_device
151 #define snd_ctl_pcm_info psnd_ctl_pcm_info
152 #define snd_ctl_open psnd_ctl_open
153 #define snd_ctl_close psnd_ctl_close
154 #define snd_ctl_card_info_malloc psnd_ctl_card_info_malloc
155 #define snd_ctl_card_info_free psnd_ctl_card_info_free
156 #define snd_ctl_card_info psnd_ctl_card_info
157 #define snd_ctl_card_info_get_name psnd_ctl_card_info_get_name
158 #define snd_ctl_card_info_get_id psnd_ctl_card_info_get_id
159 #define snd_card_next psnd_card_next
160 #endif
163 static ALCboolean alsa_load(void)
164 {
165 if(!alsa_handle)
166 {
167 #ifdef HAVE_DYNLOAD
168 alsa_handle = LoadLib("libasound.so.2");
169 if(!alsa_handle)
170 return ALC_FALSE;
172 #define LOAD_FUNC(f) do { \
173 p##f = GetSymbol(alsa_handle, #f); \
174 if(p##f == NULL) { \
175 CloseLib(alsa_handle); \
176 alsa_handle = NULL; \
177 return ALC_FALSE; \
178 } \
179 } while(0)
180 LOAD_FUNC(snd_strerror);
181 LOAD_FUNC(snd_pcm_open);
182 LOAD_FUNC(snd_pcm_close);
183 LOAD_FUNC(snd_pcm_nonblock);
184 LOAD_FUNC(snd_pcm_frames_to_bytes);
185 LOAD_FUNC(snd_pcm_bytes_to_frames);
186 LOAD_FUNC(snd_pcm_hw_params_malloc);
187 LOAD_FUNC(snd_pcm_hw_params_free);
188 LOAD_FUNC(snd_pcm_hw_params_any);
189 LOAD_FUNC(snd_pcm_hw_params_set_access);
190 LOAD_FUNC(snd_pcm_hw_params_set_format);
191 LOAD_FUNC(snd_pcm_hw_params_set_channels);
192 LOAD_FUNC(snd_pcm_hw_params_set_periods_near);
193 LOAD_FUNC(snd_pcm_hw_params_set_rate_near);
194 LOAD_FUNC(snd_pcm_hw_params_set_rate);
195 LOAD_FUNC(snd_pcm_hw_params_set_rate_resample);
196 LOAD_FUNC(snd_pcm_hw_params_set_buffer_time_near);
197 LOAD_FUNC(snd_pcm_hw_params_set_period_time_near);
198 LOAD_FUNC(snd_pcm_hw_params_set_buffer_size_near);
199 LOAD_FUNC(snd_pcm_hw_params_set_buffer_size_min);
200 LOAD_FUNC(snd_pcm_hw_params_set_period_size_near);
201 LOAD_FUNC(snd_pcm_hw_params_get_buffer_size);
202 LOAD_FUNC(snd_pcm_hw_params_get_period_size);
203 LOAD_FUNC(snd_pcm_hw_params_get_access);
204 LOAD_FUNC(snd_pcm_hw_params_get_periods);
205 LOAD_FUNC(snd_pcm_hw_params);
206 LOAD_FUNC(snd_pcm_sw_params_malloc);
207 LOAD_FUNC(snd_pcm_sw_params_current);
208 LOAD_FUNC(snd_pcm_sw_params_set_avail_min);
209 LOAD_FUNC(snd_pcm_sw_params);
210 LOAD_FUNC(snd_pcm_sw_params_free);
211 LOAD_FUNC(snd_pcm_prepare);
212 LOAD_FUNC(snd_pcm_start);
213 LOAD_FUNC(snd_pcm_resume);
214 LOAD_FUNC(snd_pcm_wait);
215 LOAD_FUNC(snd_pcm_state);
216 LOAD_FUNC(snd_pcm_avail_update);
217 LOAD_FUNC(snd_pcm_areas_silence);
218 LOAD_FUNC(snd_pcm_mmap_begin);
219 LOAD_FUNC(snd_pcm_mmap_commit);
220 LOAD_FUNC(snd_pcm_readi);
221 LOAD_FUNC(snd_pcm_writei);
222 LOAD_FUNC(snd_pcm_drain);
223 LOAD_FUNC(snd_pcm_recover);
224 LOAD_FUNC(snd_pcm_info_malloc);
225 LOAD_FUNC(snd_pcm_info_free);
226 LOAD_FUNC(snd_pcm_info_set_device);
227 LOAD_FUNC(snd_pcm_info_set_subdevice);
228 LOAD_FUNC(snd_pcm_info_set_stream);
229 LOAD_FUNC(snd_pcm_info_get_name);
230 LOAD_FUNC(snd_ctl_pcm_next_device);
231 LOAD_FUNC(snd_ctl_pcm_info);
232 LOAD_FUNC(snd_ctl_open);
233 LOAD_FUNC(snd_ctl_close);
234 LOAD_FUNC(snd_ctl_card_info_malloc);
235 LOAD_FUNC(snd_ctl_card_info_free);
236 LOAD_FUNC(snd_ctl_card_info);
237 LOAD_FUNC(snd_ctl_card_info_get_name);
238 LOAD_FUNC(snd_ctl_card_info_get_id);
239 LOAD_FUNC(snd_card_next);
240 #undef LOAD_FUNC
241 #else
242 alsa_handle = (void*)0xDEADBEEF;
243 #endif
244 }
245 return ALC_TRUE;
246 }
249 typedef struct {
250 snd_pcm_t *pcmHandle;
252 ALvoid *buffer;
253 ALsizei size;
255 ALboolean doCapture;
256 RingBuffer *ring;
258 volatile int killNow;
259 ALvoid *thread;
260 } alsa_data;
262 typedef struct {
263 ALCchar *name;
264 char *card;
265 int dev;
266 } DevMap;
268 static DevMap *allDevNameMap;
269 static ALuint numDevNames;
270 static DevMap *allCaptureDevNameMap;
271 static ALuint numCaptureDevNames;
273 static const char *device_prefix;
274 static const char *capture_prefix;
277 static DevMap *probe_devices(snd_pcm_stream_t stream, ALuint *count)
278 {
279 snd_ctl_t *handle;
280 int card, err, dev, idx;
281 snd_ctl_card_info_t *info;
282 snd_pcm_info_t *pcminfo;
283 DevMap *DevList;
284 char name[1024];
286 snd_ctl_card_info_malloc(&info);
287 snd_pcm_info_malloc(&pcminfo);
289 card = -1;
290 if((err=snd_card_next(&card)) < 0)
291 ERR("Failed to find a card: %s\n", snd_strerror(err));
293 DevList = malloc(sizeof(DevMap) * 1);
294 DevList[0].name = strdup("ALSA Default");
295 DevList[0].card = NULL;
296 DevList[0].dev = 0;
297 idx = 1;
298 while(card >= 0)
299 {
300 sprintf(name, "hw:%d", card);
301 if((err = snd_ctl_open(&handle, name, 0)) < 0)
302 {
303 ERR("control open (%i): %s\n", card, snd_strerror(err));
304 goto next_card;
305 }
306 if((err = snd_ctl_card_info(handle, info)) < 0)
307 {
308 ERR("control hardware info (%i): %s\n", card, snd_strerror(err));
309 snd_ctl_close(handle);
310 goto next_card;
311 }
313 dev = -1;
314 while(1)
315 {
316 const char *cname, *dname, *cid;
317 void *temp;
319 if(snd_ctl_pcm_next_device(handle, &dev) < 0)
320 ERR("snd_ctl_pcm_next_device failed\n");
321 if(dev < 0)
322 break;
324 snd_pcm_info_set_device(pcminfo, dev);
325 snd_pcm_info_set_subdevice(pcminfo, 0);
326 snd_pcm_info_set_stream(pcminfo, stream);
327 if((err = snd_ctl_pcm_info(handle, pcminfo)) < 0) {
328 if(err != -ENOENT)
329 ERR("control digital audio info (%i): %s\n", card, snd_strerror(err));
330 continue;
331 }
333 temp = realloc(DevList, sizeof(DevMap) * (idx+1));
334 if(temp)
335 {
336 DevList = temp;
337 cname = snd_ctl_card_info_get_name(info);
338 dname = snd_pcm_info_get_name(pcminfo);
339 cid = snd_ctl_card_info_get_id(info);
340 snprintf(name, sizeof(name), "%s, %s (CARD=%s,DEV=%d)",
341 cname, dname, cid, dev);
342 DevList[idx].name = strdup(name);
343 DevList[idx].card = strdup(cid);
344 DevList[idx].dev = dev;
345 idx++;
346 }
347 }
348 snd_ctl_close(handle);
349 next_card:
350 if(snd_card_next(&card) < 0) {
351 ERR("snd_card_next failed\n");
352 break;
353 }
354 }
356 snd_pcm_info_free(pcminfo);
357 snd_ctl_card_info_free(info);
359 *count = idx;
360 return DevList;
361 }
364 static int xrun_recovery(snd_pcm_t *handle, int err)
365 {
366 err = snd_pcm_recover(handle, err, 1);
367 if(err < 0)
368 ERR("recover failed: %s\n", snd_strerror(err));
369 return err;
370 }
372 static int verify_state(snd_pcm_t *handle)
373 {
374 snd_pcm_state_t state = snd_pcm_state(handle);
375 if(state == SND_PCM_STATE_DISCONNECTED)
376 return -ENODEV;
377 if(state == SND_PCM_STATE_XRUN)
378 {
379 int err = xrun_recovery(handle, -EPIPE);
380 if(err < 0) return err;
381 }
382 else if(state == SND_PCM_STATE_SUSPENDED)
383 {
384 int err = xrun_recovery(handle, -ESTRPIPE);
385 if(err < 0) return err;
386 }
388 return state;
389 }
392 static ALuint ALSAProc(ALvoid *ptr)
393 {
394 ALCdevice *pDevice = (ALCdevice*)ptr;
395 alsa_data *data = (alsa_data*)pDevice->ExtraData;
396 const snd_pcm_channel_area_t *areas = NULL;
397 snd_pcm_sframes_t avail, commitres;
398 snd_pcm_uframes_t offset, frames;
399 char *WritePtr;
400 int err;
402 SetRTPriority();
404 while(!data->killNow)
405 {
406 int state = verify_state(data->pcmHandle);
407 if(state < 0)
408 {
409 ERR("Invalid state detected: %s\n", snd_strerror(state));
410 aluHandleDisconnect(pDevice);
411 break;
412 }
414 avail = snd_pcm_avail_update(data->pcmHandle);
415 if(avail < 0)
416 {
417 ERR("available update failed: %s\n", snd_strerror(avail));
418 continue;
419 }
421 // make sure there's frames to process
422 if((snd_pcm_uframes_t)avail < pDevice->UpdateSize)
423 {
424 if(state != SND_PCM_STATE_RUNNING)
425 {
426 err = snd_pcm_start(data->pcmHandle);
427 if(err < 0)
428 {
429 ERR("start failed: %s\n", snd_strerror(err));
430 continue;
431 }
432 }
433 if(snd_pcm_wait(data->pcmHandle, 1000) == 0)
434 ERR("Wait timeout... buffer size too low?\n");
435 continue;
436 }
437 avail -= avail%pDevice->UpdateSize;
439 // it is possible that contiguous areas are smaller, thus we use a loop
440 while(avail > 0)
441 {
442 frames = avail;
444 err = snd_pcm_mmap_begin(data->pcmHandle, &areas, &offset, &frames);
445 if(err < 0)
446 {
447 ERR("mmap begin error: %s\n", snd_strerror(err));
448 break;
449 }
451 WritePtr = (char*)areas->addr + (offset * areas->step / 8);
452 aluMixData(pDevice, WritePtr, frames);
454 commitres = snd_pcm_mmap_commit(data->pcmHandle, offset, frames);
455 if(commitres < 0 || (commitres-frames) != 0)
456 {
457 ERR("mmap commit error: %s\n",
458 snd_strerror(commitres >= 0 ? -EPIPE : commitres));
459 break;
460 }
462 avail -= frames;
463 }
464 }
466 return 0;
467 }
469 static ALuint ALSANoMMapProc(ALvoid *ptr)
470 {
471 ALCdevice *pDevice = (ALCdevice*)ptr;
472 alsa_data *data = (alsa_data*)pDevice->ExtraData;
473 snd_pcm_sframes_t avail;
474 char *WritePtr;
476 SetRTPriority();
478 while(!data->killNow)
479 {
480 int state = verify_state(data->pcmHandle);
481 if(state < 0)
482 {
483 ERR("Invalid state detected: %s\n", snd_strerror(state));
484 aluHandleDisconnect(pDevice);
485 break;
486 }
488 WritePtr = data->buffer;
489 avail = data->size / snd_pcm_frames_to_bytes(data->pcmHandle, 1);
490 aluMixData(pDevice, WritePtr, avail);
492 while(avail > 0)
493 {
494 int ret = snd_pcm_writei(data->pcmHandle, WritePtr, avail);
495 switch (ret)
496 {
497 case -EAGAIN:
498 continue;
499 case -ESTRPIPE:
500 case -EPIPE:
501 case -EINTR:
502 ret = snd_pcm_recover(data->pcmHandle, ret, 1);
503 if(ret < 0)
504 avail = 0;
505 break;
506 default:
507 if (ret >= 0)
508 {
509 WritePtr += snd_pcm_frames_to_bytes(data->pcmHandle, ret);
510 avail -= ret;
511 }
512 break;
513 }
514 if (ret < 0)
515 {
516 ret = snd_pcm_prepare(data->pcmHandle);
517 if(ret < 0)
518 break;
519 }
520 }
521 }
523 return 0;
524 }
526 static ALCboolean alsa_open_playback(ALCdevice *device, const ALCchar *deviceName)
527 {
528 alsa_data *data;
529 char driver[128];
530 int i;
532 strncpy(driver, GetConfigValue("alsa", "device", "default"), sizeof(driver)-1);
533 driver[sizeof(driver)-1] = 0;
535 if(!deviceName)
536 deviceName = alsaDevice;
537 else if(strcmp(deviceName, alsaDevice) != 0)
538 {
539 size_t idx;
541 if(!allDevNameMap)
542 allDevNameMap = probe_devices(SND_PCM_STREAM_PLAYBACK, &numDevNames);
544 for(idx = 0;idx < numDevNames;idx++)
545 {
546 if(allDevNameMap[idx].name &&
547 strcmp(deviceName, allDevNameMap[idx].name) == 0)
548 {
549 if(idx > 0)
550 snprintf(driver, sizeof(driver), "%sCARD=%s,DEV=%d", device_prefix,
551 allDevNameMap[idx].card, allDevNameMap[idx].dev);
552 break;
553 }
554 }
555 if(idx == numDevNames)
556 return ALC_FALSE;
557 }
559 data = (alsa_data*)calloc(1, sizeof(alsa_data));
561 i = snd_pcm_open(&data->pcmHandle, driver, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
562 if(i >= 0)
563 {
564 i = snd_pcm_nonblock(data->pcmHandle, 0);
565 if(i < 0)
566 snd_pcm_close(data->pcmHandle);
567 }
568 if(i < 0)
569 {
570 free(data);
571 ERR("Could not open playback device '%s': %s\n", driver, snd_strerror(i));
572 return ALC_FALSE;
573 }
575 device->szDeviceName = strdup(deviceName);
576 device->ExtraData = data;
577 return ALC_TRUE;
578 }
580 static void alsa_close_playback(ALCdevice *device)
581 {
582 alsa_data *data = (alsa_data*)device->ExtraData;
584 snd_pcm_close(data->pcmHandle);
585 free(data);
586 device->ExtraData = NULL;
587 }
589 static ALCboolean alsa_reset_playback(ALCdevice *device)
590 {
591 alsa_data *data = (alsa_data*)device->ExtraData;
592 snd_pcm_uframes_t periodSizeInFrames;
593 unsigned int periodLen, bufferLen;
594 snd_pcm_sw_params_t *sp = NULL;
595 snd_pcm_hw_params_t *p = NULL;
596 snd_pcm_access_t access;
597 snd_pcm_format_t format;
598 unsigned int periods;
599 unsigned int rate;
600 int allowmmap;
601 char *err;
602 int i;
605 format = -1;
606 switch(device->FmtType)
607 {
608 case DevFmtByte:
609 format = SND_PCM_FORMAT_S8;
610 break;
611 case DevFmtUByte:
612 format = SND_PCM_FORMAT_U8;
613 break;
614 case DevFmtShort:
615 format = SND_PCM_FORMAT_S16;
616 break;
617 case DevFmtUShort:
618 format = SND_PCM_FORMAT_U16;
619 break;
620 case DevFmtFloat:
621 format = SND_PCM_FORMAT_FLOAT;
622 break;
623 }
625 allowmmap = GetConfigValueBool("alsa", "mmap", 1);
626 periods = device->NumUpdates;
627 periodLen = (ALuint64)device->UpdateSize * 1000000 / device->Frequency;
628 bufferLen = periodLen * periods;
629 rate = device->Frequency;
631 err = NULL;
632 snd_pcm_hw_params_malloc(&p);
634 if((i=snd_pcm_hw_params_any(data->pcmHandle, p)) < 0)
635 err = "any";
636 /* set interleaved access */
637 if(i >= 0 && (!allowmmap || (i=snd_pcm_hw_params_set_access(data->pcmHandle, p, SND_PCM_ACCESS_MMAP_INTERLEAVED)) < 0))
638 {
639 if(periods > 2)
640 {
641 periods--;
642 bufferLen = periodLen * periods;
643 }
644 if((i=snd_pcm_hw_params_set_access(data->pcmHandle, p, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
645 err = "set access";
646 }
647 /* set format (implicitly sets sample bits) */
648 if(i >= 0 && (i=snd_pcm_hw_params_set_format(data->pcmHandle, p, format)) < 0)
649 {
650 device->FmtType = DevFmtFloat;
651 if(format == SND_PCM_FORMAT_FLOAT ||
652 (i=snd_pcm_hw_params_set_format(data->pcmHandle, p, SND_PCM_FORMAT_FLOAT)) < 0)
653 {
654 device->FmtType = DevFmtShort;
655 if(format == SND_PCM_FORMAT_S16 ||
656 (i=snd_pcm_hw_params_set_format(data->pcmHandle, p, SND_PCM_FORMAT_S16)) < 0)
657 {
658 device->FmtType = DevFmtUByte;
659 if(format == SND_PCM_FORMAT_U8 ||
660 (i=snd_pcm_hw_params_set_format(data->pcmHandle, p, SND_PCM_FORMAT_U8)) < 0)
661 err = "set format";
662 }
663 }
664 }
665 /* set channels (implicitly sets frame bits) */
666 if(i >= 0 && (i=snd_pcm_hw_params_set_channels(data->pcmHandle, p, ChannelsFromDevFmt(device->FmtChans))) < 0)
667 {
668 if((i=snd_pcm_hw_params_set_channels(data->pcmHandle, p, 2)) < 0)
669 {
670 if((i=snd_pcm_hw_params_set_channels(data->pcmHandle, p, 1)) < 0)
671 err = "set channels";
672 else
673 {
674 if((device->Flags&DEVICE_CHANNELS_REQUEST))
675 ERR("Failed to set %s, got Mono instead\n", DevFmtChannelsString(device->FmtChans));
676 device->FmtChans = DevFmtMono;
677 }
678 }
679 else
680 {
681 if((device->Flags&DEVICE_CHANNELS_REQUEST))
682 ERR("Failed to set %s, got Stereo instead\n", DevFmtChannelsString(device->FmtChans));
683 device->FmtChans = DevFmtStereo;
684 }
685 device->Flags &= ~DEVICE_CHANNELS_REQUEST;
686 }
687 if(i >= 0 && (i=snd_pcm_hw_params_set_rate_resample(data->pcmHandle, p, 0)) < 0)
688 {
689 ERR("Failed to disable ALSA resampler\n");
690 i = 0;
691 }
692 /* set rate (implicitly constrains period/buffer parameters) */
693 if(i >= 0 && (i=snd_pcm_hw_params_set_rate_near(data->pcmHandle, p, &rate, NULL)) < 0)
694 err = "set rate near";
695 /* set buffer time (implicitly constrains period/buffer parameters) */
696 if(i >= 0 && (i=snd_pcm_hw_params_set_buffer_time_near(data->pcmHandle, p, &bufferLen, NULL)) < 0)
697 err = "set buffer time near";
698 /* set period time in frame units (implicitly sets buffer size/bytes/time and period size/bytes) */
699 if(i >= 0 && (i=snd_pcm_hw_params_set_period_time_near(data->pcmHandle, p, &periodLen, NULL)) < 0)
700 err = "set period time near";
701 /* install and prepare hardware configuration */
702 if(i >= 0 && (i=snd_pcm_hw_params(data->pcmHandle, p)) < 0)
703 err = "set params";
704 if(i >= 0 && (i=snd_pcm_hw_params_get_access(p, &access)) < 0)
705 err = "get access";
706 if(i >= 0 && (i=snd_pcm_hw_params_get_period_size(p, &periodSizeInFrames, NULL)) < 0)
707 err = "get period size";
708 if(i >= 0 && (i=snd_pcm_hw_params_get_periods(p, &periods, NULL)) < 0)
709 err = "get periods";
710 if(i < 0)
711 {
712 ERR("%s failed: %s\n", err, snd_strerror(i));
713 snd_pcm_hw_params_free(p);
714 return ALC_FALSE;
715 }
717 snd_pcm_hw_params_free(p);
719 err = NULL;
720 snd_pcm_sw_params_malloc(&sp);
722 if((i=snd_pcm_sw_params_current(data->pcmHandle, sp)) != 0)
723 err = "sw current";
724 if(i == 0 && (i=snd_pcm_sw_params_set_avail_min(data->pcmHandle, sp, periodSizeInFrames)) != 0)
725 err = "sw set avail min";
726 if(i == 0 && (i=snd_pcm_sw_params(data->pcmHandle, sp)) != 0)
727 err = "sw set params";
728 if(i != 0)
729 {
730 ERR("%s failed: %s\n", err, snd_strerror(i));
731 snd_pcm_sw_params_free(sp);
732 return ALC_FALSE;
733 }
735 snd_pcm_sw_params_free(sp);
737 if(device->Frequency != rate)
738 {
739 if((device->Flags&DEVICE_FREQUENCY_REQUEST))
740 ERR("Failed to set %dhz, got %dhz instead\n", device->Frequency, rate);
741 device->Flags &= ~DEVICE_FREQUENCY_REQUEST;
742 device->Frequency = rate;
743 }
745 SetDefaultChannelOrder(device);
747 data->size = snd_pcm_frames_to_bytes(data->pcmHandle, periodSizeInFrames);
748 if(access == SND_PCM_ACCESS_RW_INTERLEAVED)
749 {
750 /* Increase periods by one, since the temp buffer counts as an extra
751 * period */
752 periods++;
753 data->buffer = malloc(data->size);
754 if(!data->buffer)
755 {
756 ERR("buffer malloc failed\n");
757 return ALC_FALSE;
758 }
759 device->UpdateSize = periodSizeInFrames;
760 device->NumUpdates = periods;
761 data->thread = StartThread(ALSANoMMapProc, device);
762 }
763 else
764 {
765 i = snd_pcm_prepare(data->pcmHandle);
766 if(i < 0)
767 {
768 ERR("prepare error: %s\n", snd_strerror(i));
769 return ALC_FALSE;
770 }
771 device->UpdateSize = periodSizeInFrames;
772 device->NumUpdates = periods;
773 data->thread = StartThread(ALSAProc, device);
774 }
775 if(data->thread == NULL)
776 {
777 ERR("Could not create playback thread\n");
778 free(data->buffer);
779 data->buffer = NULL;
780 return ALC_FALSE;
781 }
783 return ALC_TRUE;
784 }
786 static void alsa_stop_playback(ALCdevice *device)
787 {
788 alsa_data *data = (alsa_data*)device->ExtraData;
790 if(data->thread)
791 {
792 data->killNow = 1;
793 StopThread(data->thread);
794 data->thread = NULL;
795 }
796 data->killNow = 0;
797 free(data->buffer);
798 data->buffer = NULL;
799 }
802 static ALCboolean alsa_open_capture(ALCdevice *pDevice, const ALCchar *deviceName)
803 {
804 snd_pcm_hw_params_t *p;
805 snd_pcm_uframes_t bufferSizeInFrames;
806 snd_pcm_format_t format;
807 ALuint frameSize;
808 alsa_data *data;
809 char driver[128];
810 char *err;
811 int i;
813 strncpy(driver, GetConfigValue("alsa", "capture", "default"), sizeof(driver)-1);
814 driver[sizeof(driver)-1] = 0;
816 if(!allCaptureDevNameMap)
817 allCaptureDevNameMap = probe_devices(SND_PCM_STREAM_CAPTURE, &numCaptureDevNames);
819 if(!deviceName)
820 deviceName = allCaptureDevNameMap[0].name;
821 else
822 {
823 size_t idx;
825 for(idx = 0;idx < numCaptureDevNames;idx++)
826 {
827 if(allCaptureDevNameMap[idx].name &&
828 strcmp(deviceName, allCaptureDevNameMap[idx].name) == 0)
829 {
830 if(idx > 0)
831 snprintf(driver, sizeof(driver), "%sCARD=%s,DEV=%d", capture_prefix,
832 allCaptureDevNameMap[idx].card, allCaptureDevNameMap[idx].dev);
833 break;
834 }
835 }
836 if(idx == numCaptureDevNames)
837 return ALC_FALSE;
838 }
840 data = (alsa_data*)calloc(1, sizeof(alsa_data));
842 i = snd_pcm_open(&data->pcmHandle, driver, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK);
843 if(i < 0)
844 {
845 ERR("Could not open capture device '%s': %s\n", driver, snd_strerror(i));
846 free(data);
847 return ALC_FALSE;
848 }
850 format = -1;
851 switch(pDevice->FmtType)
852 {
853 case DevFmtByte:
854 format = SND_PCM_FORMAT_S8;
855 break;
856 case DevFmtUByte:
857 format = SND_PCM_FORMAT_U8;
858 break;
859 case DevFmtShort:
860 format = SND_PCM_FORMAT_S16;
861 break;
862 case DevFmtUShort:
863 format = SND_PCM_FORMAT_U16;
864 break;
865 case DevFmtFloat:
866 format = SND_PCM_FORMAT_FLOAT;
867 break;
868 }
870 err = NULL;
871 bufferSizeInFrames = pDevice->UpdateSize * pDevice->NumUpdates;
872 snd_pcm_hw_params_malloc(&p);
874 if((i=snd_pcm_hw_params_any(data->pcmHandle, p)) < 0)
875 err = "any";
876 /* set interleaved access */
877 if(i >= 0 && (i=snd_pcm_hw_params_set_access(data->pcmHandle, p, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
878 err = "set access";
879 /* set format (implicitly sets sample bits) */
880 if(i >= 0 && (i=snd_pcm_hw_params_set_format(data->pcmHandle, p, format)) < 0)
881 err = "set format";
882 /* set channels (implicitly sets frame bits) */
883 if(i >= 0 && (i=snd_pcm_hw_params_set_channels(data->pcmHandle, p, ChannelsFromDevFmt(pDevice->FmtChans))) < 0)
884 err = "set channels";
885 /* set rate (implicitly constrains period/buffer parameters) */
886 if(i >= 0 && (i=snd_pcm_hw_params_set_rate(data->pcmHandle, p, pDevice->Frequency, 0)) < 0)
887 err = "set rate near";
888 /* set buffer size in frame units (implicitly sets period size/bytes/time and buffer time/bytes) */
889 if(i >= 0 && (i=snd_pcm_hw_params_set_buffer_size_near(data->pcmHandle, p, &bufferSizeInFrames)) < 0)
890 err = "set buffer size near";
891 /* install and prepare hardware configuration */
892 if(i >= 0 && (i=snd_pcm_hw_params(data->pcmHandle, p)) < 0)
893 err = "set params";
894 if(i < 0)
895 {
896 ERR("%s failed: %s\n", err, snd_strerror(i));
897 snd_pcm_hw_params_free(p);
898 goto error;
899 }
901 if((i=snd_pcm_hw_params_get_period_size(p, &bufferSizeInFrames, NULL)) < 0)
902 {
903 ERR("get size failed: %s\n", snd_strerror(i));
904 snd_pcm_hw_params_free(p);
905 goto error;
906 }
908 snd_pcm_hw_params_free(p);
910 frameSize = FrameSizeFromDevFmt(pDevice->FmtChans, pDevice->FmtType);
912 data->ring = CreateRingBuffer(frameSize, pDevice->UpdateSize*pDevice->NumUpdates);
913 if(!data->ring)
914 {
915 ERR("ring buffer create failed\n");
916 goto error;
917 }
919 data->size = snd_pcm_frames_to_bytes(data->pcmHandle, bufferSizeInFrames);
920 data->buffer = malloc(data->size);
921 if(!data->buffer)
922 {
923 ERR("buffer malloc failed\n");
924 goto error;
925 }
927 pDevice->szDeviceName = strdup(deviceName);
929 pDevice->ExtraData = data;
930 return ALC_TRUE;
932 error:
933 free(data->buffer);
934 DestroyRingBuffer(data->ring);
935 snd_pcm_close(data->pcmHandle);
936 free(data);
938 pDevice->ExtraData = NULL;
939 return ALC_FALSE;
940 }
942 static void alsa_close_capture(ALCdevice *pDevice)
943 {
944 alsa_data *data = (alsa_data*)pDevice->ExtraData;
946 snd_pcm_close(data->pcmHandle);
947 DestroyRingBuffer(data->ring);
949 free(data->buffer);
950 free(data);
951 pDevice->ExtraData = NULL;
952 }
954 static void alsa_start_capture(ALCdevice *Device)
955 {
956 alsa_data *data = (alsa_data*)Device->ExtraData;
957 int err;
959 err = snd_pcm_start(data->pcmHandle);
960 if(err < 0)
961 {
962 ERR("start failed: %s\n", snd_strerror(err));
963 aluHandleDisconnect(Device);
964 }
965 else
966 data->doCapture = AL_TRUE;
967 }
969 static void alsa_stop_capture(ALCdevice *Device)
970 {
971 alsa_data *data = (alsa_data*)Device->ExtraData;
972 snd_pcm_drain(data->pcmHandle);
973 data->doCapture = AL_FALSE;
974 }
976 static ALCuint alsa_available_samples(ALCdevice *Device)
977 {
978 alsa_data *data = (alsa_data*)Device->ExtraData;
979 snd_pcm_sframes_t avail;
981 avail = (Device->Connected ? snd_pcm_avail_update(data->pcmHandle) : 0);
982 if(avail < 0)
983 {
984 ERR("avail update failed: %s\n", snd_strerror(avail));
986 if((avail=snd_pcm_recover(data->pcmHandle, avail, 1)) >= 0)
987 {
988 if(data->doCapture)
989 avail = snd_pcm_start(data->pcmHandle);
990 if(avail >= 0)
991 avail = snd_pcm_avail_update(data->pcmHandle);
992 }
993 if(avail < 0)
994 {
995 ERR("restore error: %s\n", snd_strerror(avail));
996 aluHandleDisconnect(Device);
997 }
998 }
999 while(avail > 0)
1001 snd_pcm_sframes_t amt;
1003 amt = snd_pcm_bytes_to_frames(data->pcmHandle, data->size);
1004 if(avail < amt) amt = avail;
1006 amt = snd_pcm_readi(data->pcmHandle, data->buffer, amt);
1007 if(amt < 0)
1009 ERR("read error: %s\n", snd_strerror(amt));
1011 if(amt == -EAGAIN)
1012 continue;
1013 if((amt=snd_pcm_recover(data->pcmHandle, amt, 1)) >= 0)
1015 if(data->doCapture)
1016 amt = snd_pcm_start(data->pcmHandle);
1017 if(amt >= 0)
1018 amt = snd_pcm_avail_update(data->pcmHandle);
1020 if(amt < 0)
1022 ERR("restore error: %s\n", snd_strerror(amt));
1023 aluHandleDisconnect(Device);
1024 break;
1026 avail = amt;
1027 continue;
1030 WriteRingBuffer(data->ring, data->buffer, amt);
1031 avail -= amt;
1034 return RingBufferSize(data->ring);
1037 static void alsa_capture_samples(ALCdevice *Device, ALCvoid *Buffer, ALCuint Samples)
1039 alsa_data *data = (alsa_data*)Device->ExtraData;
1041 if(Samples <= alsa_available_samples(Device))
1042 ReadRingBuffer(data->ring, Buffer, Samples);
1043 else
1044 alcSetError(Device, ALC_INVALID_VALUE);
1048 static const BackendFuncs alsa_funcs = {
1049 alsa_open_playback,
1050 alsa_close_playback,
1051 alsa_reset_playback,
1052 alsa_stop_playback,
1053 alsa_open_capture,
1054 alsa_close_capture,
1055 alsa_start_capture,
1056 alsa_stop_capture,
1057 alsa_capture_samples,
1058 alsa_available_samples
1059 };
1061 ALCboolean alc_alsa_init(BackendFuncs *func_list)
1063 if(!alsa_load())
1064 return ALC_FALSE;
1065 device_prefix = GetConfigValue("alsa", "device-prefix", "plughw:");
1066 capture_prefix = GetConfigValue("alsa", "capture-prefix", "plughw:");
1067 *func_list = alsa_funcs;
1068 return ALC_TRUE;
1071 void alc_alsa_deinit(void)
1073 ALuint i;
1075 for(i = 0;i < numDevNames;++i)
1077 free(allDevNameMap[i].name);
1078 free(allDevNameMap[i].card);
1080 free(allDevNameMap);
1081 allDevNameMap = NULL;
1082 numDevNames = 0;
1084 for(i = 0;i < numCaptureDevNames;++i)
1086 free(allCaptureDevNameMap[i].name);
1087 free(allCaptureDevNameMap[i].card);
1089 free(allCaptureDevNameMap);
1090 allCaptureDevNameMap = NULL;
1091 numCaptureDevNames = 0;
1093 #ifdef HAVE_DYNLOAD
1094 if(alsa_handle)
1095 CloseLib(alsa_handle);
1096 alsa_handle = NULL;
1097 #endif
1100 void alc_alsa_probe(enum DevProbe type)
1102 ALuint i;
1104 switch(type)
1106 case DEVICE_PROBE:
1107 AppendDeviceList(alsaDevice);
1108 break;
1110 case ALL_DEVICE_PROBE:
1111 for(i = 0;i < numDevNames;++i)
1113 free(allDevNameMap[i].name);
1114 free(allDevNameMap[i].card);
1117 free(allDevNameMap);
1118 allDevNameMap = probe_devices(SND_PCM_STREAM_PLAYBACK, &numDevNames);
1120 for(i = 0;i < numDevNames;++i)
1121 AppendAllDeviceList(allDevNameMap[i].name);
1122 break;
1124 case CAPTURE_DEVICE_PROBE:
1125 for(i = 0;i < numCaptureDevNames;++i)
1127 free(allCaptureDevNameMap[i].name);
1128 free(allCaptureDevNameMap[i].card);
1131 free(allCaptureDevNameMap);
1132 allCaptureDevNameMap = probe_devices(SND_PCM_STREAM_CAPTURE, &numCaptureDevNames);
1134 for(i = 0;i < numCaptureDevNames;++i)
1135 AppendCaptureDeviceList(allCaptureDevNameMap[i].name);
1136 break;