Release 1.5.304
[openal-soft/openal-hmr.git] / Alc / alsa.c
blobea42b7fcdad796a180963e882823a30ab679dd25
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>
26 #ifdef HAVE_DLFCN_H
27 #include <dlfcn.h>
28 #endif
29 #include "alMain.h"
30 #include "AL/al.h"
31 #include "AL/alc.h"
33 #include <alsa/asoundlib.h>
36 typedef struct {
37 snd_pcm_t *pcmHandle;
38 snd_pcm_format_t format;
40 ALvoid *buffer;
41 ALsizei size;
43 volatile int killNow;
44 ALvoid *thread;
45 } alsa_data;
47 typedef struct {
48 ALCchar *name;
49 int card, dev;
50 } DevMap;
52 static void *alsa_handle;
53 #define MAKE_FUNC(f) static typeof(f) * p##f
54 MAKE_FUNC(snd_strerror);
55 MAKE_FUNC(snd_pcm_open);
56 MAKE_FUNC(snd_pcm_close);
57 MAKE_FUNC(snd_pcm_nonblock);
58 MAKE_FUNC(snd_pcm_frames_to_bytes);
59 MAKE_FUNC(snd_pcm_hw_params_malloc);
60 MAKE_FUNC(snd_pcm_hw_params_free);
61 MAKE_FUNC(snd_pcm_hw_params_any);
62 MAKE_FUNC(snd_pcm_hw_params_set_access);
63 MAKE_FUNC(snd_pcm_hw_params_set_format);
64 MAKE_FUNC(snd_pcm_hw_params_set_channels);
65 MAKE_FUNC(snd_pcm_hw_params_set_periods_near);
66 MAKE_FUNC(snd_pcm_hw_params_set_rate_near);
67 MAKE_FUNC(snd_pcm_hw_params_set_rate);
68 MAKE_FUNC(snd_pcm_hw_params_set_buffer_size_near);
69 MAKE_FUNC(snd_pcm_hw_params_set_buffer_size_min);
70 MAKE_FUNC(snd_pcm_hw_params_get_period_size);
71 MAKE_FUNC(snd_pcm_hw_params_get_access);
72 MAKE_FUNC(snd_pcm_hw_params);
73 MAKE_FUNC(snd_pcm_prepare);
74 MAKE_FUNC(snd_pcm_start);
75 MAKE_FUNC(snd_pcm_resume);
76 MAKE_FUNC(snd_pcm_wait);
77 MAKE_FUNC(snd_pcm_state);
78 MAKE_FUNC(snd_pcm_avail_update);
79 MAKE_FUNC(snd_pcm_areas_silence);
80 MAKE_FUNC(snd_pcm_mmap_begin);
81 MAKE_FUNC(snd_pcm_mmap_commit);
82 MAKE_FUNC(snd_pcm_writei);
83 MAKE_FUNC(snd_pcm_drain);
84 MAKE_FUNC(snd_pcm_info_malloc);
85 MAKE_FUNC(snd_pcm_info_free);
86 MAKE_FUNC(snd_pcm_info_set_device);
87 MAKE_FUNC(snd_pcm_info_set_subdevice);
88 MAKE_FUNC(snd_pcm_info_set_stream);
89 MAKE_FUNC(snd_pcm_info_get_name);
90 MAKE_FUNC(snd_ctl_pcm_next_device);
91 MAKE_FUNC(snd_ctl_pcm_info);
92 MAKE_FUNC(snd_ctl_open);
93 MAKE_FUNC(snd_ctl_close);
94 MAKE_FUNC(snd_ctl_card_info_malloc);
95 MAKE_FUNC(snd_ctl_card_info_free);
96 MAKE_FUNC(snd_ctl_card_info);
97 MAKE_FUNC(snd_ctl_card_info_get_name);
98 MAKE_FUNC(snd_card_next);
99 #undef MAKE_FUNC
101 #define MAX_DEVICES 16
102 #define MAX_ALL_DEVICES 32
104 static DevMap allDevNameMap[MAX_ALL_DEVICES];
105 static ALCchar *alsaDeviceList[MAX_DEVICES];
106 static DevMap allCaptureDevNameMap[MAX_ALL_DEVICES];
108 static int xrun_recovery(snd_pcm_t *handle, int err)
110 if (err == -EPIPE)
111 { /* under-run */
112 err = psnd_pcm_prepare(handle);
113 if (err < 0)
114 AL_PRINT("prepare failed: %s\n", psnd_strerror(err));
116 else if (err == -ESTRPIPE)
118 while ((err = psnd_pcm_resume(handle)) == -EAGAIN)
119 Sleep(1); /* wait until the suspend flag is released */
120 if (err < 0)
122 err = psnd_pcm_prepare(handle);
123 if (err < 0)
124 AL_PRINT("prepare failed: %s\n", psnd_strerror(err));
127 return err;
131 static ALuint ALSAProc(ALvoid *ptr)
133 ALCdevice *pDevice = (ALCdevice*)ptr;
134 alsa_data *data = (alsa_data*)pDevice->ExtraData;
135 const snd_pcm_channel_area_t *areas = NULL;
136 snd_pcm_sframes_t avail, commitres;
137 snd_pcm_uframes_t offset, frames;
138 char *WritePtr;
139 int WriteCnt;
140 int err;
142 while(!data->killNow)
144 snd_pcm_state_t state = psnd_pcm_state(data->pcmHandle);
145 if(state == SND_PCM_STATE_XRUN)
147 err = xrun_recovery(data->pcmHandle, -EPIPE);
148 if (err < 0)
150 AL_PRINT("XRUN recovery failed: %s\n", psnd_strerror(err));
151 break;
154 else if (state == SND_PCM_STATE_SUSPENDED)
156 err = xrun_recovery(data->pcmHandle, -ESTRPIPE);
157 if (err < 0)
159 AL_PRINT("SUSPEND recovery failed: %s\n", psnd_strerror(err));
160 break;
164 avail = psnd_pcm_avail_update(data->pcmHandle);
165 if(avail < 0)
167 err = xrun_recovery(data->pcmHandle, avail);
168 if (err < 0)
170 AL_PRINT("available update failed: %s\n", psnd_strerror(err));
171 break;
175 // make sure there's frames to process
176 if(avail == 0)
178 if(state != SND_PCM_STATE_RUNNING)
180 err = psnd_pcm_start(data->pcmHandle);
181 if(err < 0)
182 err = xrun_recovery(data->pcmHandle, err);
183 if(err < 0)
185 AL_PRINT("start failed: %s\n", psnd_strerror(err));
186 break;
189 else if(psnd_pcm_wait(data->pcmHandle, 1000) == 0)
190 AL_PRINT("Wait timeout... buffer size too low?\n");
191 continue;
194 // it is possible that contiguous areas are smaller, thus we use a loop
195 while (avail > 0)
197 frames = avail;
199 err = psnd_pcm_mmap_begin(data->pcmHandle, &areas, &offset, &frames);
200 if (err < 0)
202 err = xrun_recovery(data->pcmHandle, err);
203 if (err < 0)
204 AL_PRINT("mmap begin error: %s\n", psnd_strerror(err));
205 break;
208 SuspendContext(NULL);
209 WritePtr = (char*)areas->addr + (offset * areas->step / 8);
210 WriteCnt = psnd_pcm_frames_to_bytes(data->pcmHandle, frames);
211 aluMixData(pDevice->Context, WritePtr, WriteCnt, pDevice->Format);
212 ProcessContext(NULL);
214 commitres = psnd_pcm_mmap_commit(data->pcmHandle, offset, frames);
215 if (commitres < 0 || (commitres-frames) != 0)
217 AL_PRINT("mmap commit error: %s\n",
218 psnd_strerror(commitres >= 0 ? -EPIPE : commitres));
219 break;
222 avail -= frames;
226 return 0;
229 static ALuint ALSANoMMapProc(ALvoid *ptr)
231 ALCdevice *pDevice = (ALCdevice*)ptr;
232 alsa_data *data = (alsa_data*)pDevice->ExtraData;
233 snd_pcm_sframes_t avail;
234 char *WritePtr;
236 while(!data->killNow)
238 SuspendContext(NULL);
239 aluMixData(pDevice->Context, data->buffer, data->size, pDevice->Format);
240 ProcessContext(NULL);
242 WritePtr = data->buffer;
243 avail = (snd_pcm_uframes_t)data->size / psnd_pcm_frames_to_bytes(data->pcmHandle, 1);
244 while(avail > 0)
246 int ret = psnd_pcm_writei(data->pcmHandle, WritePtr, avail);
247 switch (ret)
249 case -EAGAIN:
250 continue;
251 case -ESTRPIPE:
252 while((ret=psnd_pcm_resume(data->pcmHandle)) == -EAGAIN)
253 Sleep(1);
254 break;
255 case -EPIPE:
256 break;
257 default:
258 if (ret >= 0)
260 WritePtr += psnd_pcm_frames_to_bytes(data->pcmHandle, ret);
261 avail -= ret;
263 break;
265 if (ret < 0)
267 ret = psnd_pcm_prepare(data->pcmHandle);
268 if(ret < 0)
269 break;
274 return 0;
277 static ALCboolean alsa_open_playback(ALCdevice *device, const ALCchar *deviceName)
279 snd_pcm_uframes_t bufferSizeInFrames;
280 snd_pcm_hw_params_t *p = NULL;
281 snd_pcm_access_t access;
282 unsigned int periods;
283 alsa_data *data;
284 char driver[64];
285 const char *str;
286 int allowmmap;
287 char *err;
288 int i;
290 if(!alsa_handle)
291 return ALC_FALSE;
293 strncpy(driver, GetConfigValue("alsa", "device", "default"), sizeof(driver)-1);
294 driver[sizeof(driver)-1] = 0;
295 if(deviceName)
297 size_t idx;
299 for(idx = 0;idx < MAX_ALL_DEVICES;idx++)
301 if(allDevNameMap[idx].name &&
302 strcmp(deviceName, allDevNameMap[idx].name) == 0)
304 device->szDeviceName = allDevNameMap[idx].name;
305 if(idx > 0)
306 sprintf(driver, "hw:%d,%d", allDevNameMap[idx].card, allDevNameMap[idx].dev);
307 goto open_alsa;
310 for(idx = 0;idx < MAX_DEVICES;idx++)
312 if(alsaDeviceList[idx] &&
313 strcmp(deviceName, alsaDeviceList[idx]) == 0)
315 device->szDeviceName = alsaDeviceList[idx];
316 if(idx > 0)
317 sprintf(driver, "hw:%zd,0", idx-1);
318 goto open_alsa;
321 return ALC_FALSE;
323 else
324 device->szDeviceName = alsaDeviceList[0];
326 open_alsa:
327 data = (alsa_data*)calloc(1, sizeof(alsa_data));
329 i = psnd_pcm_open(&data->pcmHandle, driver, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
330 if(i < 0)
332 Sleep(200);
333 i = psnd_pcm_open(&data->pcmHandle, driver, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
335 if(i >= 0)
337 i = psnd_pcm_nonblock(data->pcmHandle, 0);
338 if(i < 0)
339 psnd_pcm_close(data->pcmHandle);
341 if(i < 0)
343 free(data);
344 AL_PRINT("Could not open playback device '%s': %s\n", driver, psnd_strerror(i));
345 return ALC_FALSE;
348 switch(aluBytesFromFormat(device->Format))
350 case 1:
351 data->format = SND_PCM_FORMAT_U8;
352 break;
353 case 2:
354 data->format = SND_PCM_FORMAT_S16;
355 break;
356 default:
357 data->format = SND_PCM_FORMAT_UNKNOWN;
358 AL_PRINT("Unknown format?! %x\n", device->Format);
361 periods = GetConfigValueInt("alsa", "periods", 0);
362 bufferSizeInFrames = device->UpdateSize;
364 str = GetConfigValue("alsa", "mmap", "true");
365 allowmmap = (strcasecmp(str, "true") == 0 ||
366 strcasecmp(str, "yes") == 0 ||
367 strcasecmp(str, "on") == 0 ||
368 atoi(str) != 0);
370 psnd_pcm_hw_params_malloc(&p);
371 #define ok(func, str) (i=(func),((i<0)?(err=(str)),0:1))
372 /* start with the largest configuration space possible */
373 if(!(ok(psnd_pcm_hw_params_any(data->pcmHandle, p), "any") &&
374 /* set interleaved access */
375 ((allowmmap && ok(psnd_pcm_hw_params_set_access(data->pcmHandle, p, SND_PCM_ACCESS_MMAP_INTERLEAVED), "set access")) ||
376 ok(psnd_pcm_hw_params_set_access(data->pcmHandle, p, SND_PCM_ACCESS_RW_INTERLEAVED), "set access")) &&
377 /* set format (implicitly sets sample bits) */
378 ok(psnd_pcm_hw_params_set_format(data->pcmHandle, p, data->format), "set format") &&
379 /* set channels (implicitly sets frame bits) */
380 ok(psnd_pcm_hw_params_set_channels(data->pcmHandle, p, aluChannelsFromFormat(device->Format)), "set channels") &&
381 /* set periods (implicitly constrains period/buffer parameters) */
382 (!periods || ok(psnd_pcm_hw_params_set_periods_near(data->pcmHandle, p, &periods, NULL), "set periods near")) &&
383 /* set rate (implicitly constrains period/buffer parameters) */
384 ok(psnd_pcm_hw_params_set_rate_near(data->pcmHandle, p, &device->Frequency, NULL), "set rate near") &&
385 /* set buffer size in frame units (implicitly sets period size/bytes/time and buffer time/bytes) */
386 ok(psnd_pcm_hw_params_set_buffer_size_near(data->pcmHandle, p, &bufferSizeInFrames), "set buffer size near") &&
387 /* install and prepare hardware configuration */
388 ok(psnd_pcm_hw_params(data->pcmHandle, p), "set params")))
390 AL_PRINT("%s failed: %s\n", err, psnd_strerror(i));
391 psnd_pcm_hw_params_free(p);
392 psnd_pcm_close(data->pcmHandle);
393 free(data);
394 return ALC_FALSE;
396 #undef ok
398 if((i=psnd_pcm_hw_params_get_access(p, &access)) < 0)
400 AL_PRINT("get_access failed: %s\n", psnd_strerror(i));
401 psnd_pcm_hw_params_free(p);
402 psnd_pcm_close(data->pcmHandle);
403 free(data);
404 return ALC_FALSE;
407 if((i=psnd_pcm_hw_params_get_period_size(p, &bufferSizeInFrames, NULL)) < 0)
409 AL_PRINT("get_period_size failed: %s\n", psnd_strerror(i));
410 psnd_pcm_hw_params_free(p);
411 psnd_pcm_close(data->pcmHandle);
412 free(data);
413 return ALC_FALSE;
416 psnd_pcm_hw_params_free(p);
418 device->UpdateSize = bufferSizeInFrames;
420 data->size = psnd_pcm_frames_to_bytes(data->pcmHandle, device->UpdateSize);
421 if(access == SND_PCM_ACCESS_RW_INTERLEAVED)
423 data->buffer = malloc(data->size);
424 if(!data->buffer)
426 AL_PRINT("buffer malloc failed\n");
427 psnd_pcm_close(data->pcmHandle);
428 free(data);
429 return ALC_FALSE;
432 else
434 i = psnd_pcm_prepare(data->pcmHandle);
435 if(i < 0)
437 AL_PRINT("prepare error: %s\n", psnd_strerror(i));
438 psnd_pcm_close(data->pcmHandle);
439 free(data->buffer);
440 free(data);
441 return ALC_FALSE;
445 device->ExtraData = data;
446 if(access == SND_PCM_ACCESS_RW_INTERLEAVED)
447 data->thread = StartThread(ALSANoMMapProc, device);
448 else
449 data->thread = StartThread(ALSAProc, device);
450 if(data->thread == NULL)
452 psnd_pcm_close(data->pcmHandle);
453 device->ExtraData = NULL;
454 free(data->buffer);
455 free(data);
456 return ALC_FALSE;
459 return ALC_TRUE;
462 static void alsa_close_playback(ALCdevice *device)
464 alsa_data *data = (alsa_data*)device->ExtraData;
465 data->killNow = 1;
466 StopThread(data->thread);
467 psnd_pcm_close(data->pcmHandle);
469 free(data->buffer);
470 free(data);
471 device->ExtraData = NULL;
475 static ALCboolean alsa_open_capture(ALCdevice *pDevice, const ALCchar *deviceName, ALCuint frequency, ALCenum format, ALCsizei SampleSize)
477 snd_pcm_format_t alsaFormat;
478 snd_pcm_hw_params_t *p;
479 snd_pcm_uframes_t bufferSizeInFrames;
480 alsa_data *data;
481 char driver[64];
482 char *err;
483 int i;
485 if(!alsa_handle)
486 return ALC_FALSE;
488 strncpy(driver, GetConfigValue("alsa", "capture", "default"), sizeof(driver)-1);
489 driver[sizeof(driver)-1] = 0;
490 if(deviceName)
492 size_t idx;
494 for(idx = 0;idx < MAX_ALL_DEVICES;idx++)
496 if(allCaptureDevNameMap[idx].name &&
497 strcmp(deviceName, allCaptureDevNameMap[idx].name) == 0)
499 pDevice->szDeviceName = allCaptureDevNameMap[idx].name;
500 if(idx > 0)
501 sprintf(driver, "hw:%d,%d", allCaptureDevNameMap[idx].card, allCaptureDevNameMap[idx].dev);
502 goto open_alsa;
505 return ALC_FALSE;
507 else
508 pDevice->szDeviceName = allCaptureDevNameMap[0].name;
510 open_alsa:
511 data = (alsa_data*)calloc(1, sizeof(alsa_data));
513 i = psnd_pcm_open(&data->pcmHandle, driver, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK);
514 if(i < 0)
516 Sleep(200);
517 i = psnd_pcm_open(&data->pcmHandle, driver, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK);
519 if(i >= 0)
521 i = psnd_pcm_nonblock(data->pcmHandle, 0);
522 if(i < 0)
523 psnd_pcm_close(data->pcmHandle);
525 if(i < 0)
527 free(data);
528 AL_PRINT("Could not open capture device '%s': %s\n", driver, psnd_strerror(i));
529 return ALC_FALSE;
532 switch(aluBytesFromFormat(format))
534 case 1:
535 alsaFormat = SND_PCM_FORMAT_U8;
536 break;
537 case 2:
538 alsaFormat = SND_PCM_FORMAT_S16;
539 break;
540 default:
541 alsaFormat = SND_PCM_FORMAT_UNKNOWN;
542 AL_PRINT("Unknown format?! %x\n", format);
545 bufferSizeInFrames = SampleSize;
547 psnd_pcm_hw_params_malloc(&p);
548 #define ok(func, str) (i=(func),((i<0)?(err=(str)),0:1))
549 /* start with the largest configuration space possible */
550 if(!(ok(psnd_pcm_hw_params_any(data->pcmHandle, p), "any") &&
551 /* set interleaved access */
552 ok(psnd_pcm_hw_params_set_access(data->pcmHandle, p, SND_PCM_ACCESS_MMAP_INTERLEAVED), "set access") &&
553 /* set format (implicitly sets sample bits) */
554 ok(psnd_pcm_hw_params_set_format(data->pcmHandle, p, alsaFormat), "set format") &&
555 /* set channels (implicitly sets frame bits) */
556 ok(psnd_pcm_hw_params_set_channels(data->pcmHandle, p, aluChannelsFromFormat(pDevice->Format)), "set channels") &&
557 /* set rate (implicitly constrains period/buffer parameters) */
558 ok(psnd_pcm_hw_params_set_rate(data->pcmHandle, p, frequency, 0), "set rate") &&
559 /* set buffer size in frame units (implicitly sets period size/bytes/time and buffer time/bytes) */
560 ok(psnd_pcm_hw_params_set_buffer_size_min(data->pcmHandle, p, &bufferSizeInFrames), "set buffer size min") &&
561 /* install and prepare hardware configuration */
562 ok(psnd_pcm_hw_params(data->pcmHandle, p), "set params")))
564 AL_PRINT("%s failed: %s\n", err, psnd_strerror(i));
565 psnd_pcm_hw_params_free(p);
566 psnd_pcm_close(data->pcmHandle);
567 free(data);
568 return ALC_FALSE;
570 #undef ok
571 psnd_pcm_hw_params_free(p);
573 i = psnd_pcm_prepare(data->pcmHandle);
574 if(i < 0)
576 AL_PRINT("prepare error: %s\n", psnd_strerror(i));
577 psnd_pcm_close(data->pcmHandle);
578 free(data);
579 return ALC_FALSE;
582 pDevice->ExtraData = data;
583 return ALC_TRUE;
586 static void alsa_close_capture(ALCdevice *pDevice)
588 alsa_data *data = (alsa_data*)pDevice->ExtraData;
589 psnd_pcm_close(data->pcmHandle);
591 free(data);
592 pDevice->ExtraData = NULL;
595 static void alsa_start_capture(ALCdevice *pDevice)
597 alsa_data *data = (alsa_data*)pDevice->ExtraData;
598 psnd_pcm_prepare(data->pcmHandle);
599 psnd_pcm_start(data->pcmHandle);
602 static void alsa_stop_capture(ALCdevice *pDevice)
604 alsa_data *data = (alsa_data*)pDevice->ExtraData;
605 psnd_pcm_drain(data->pcmHandle);
608 static void alsa_capture_samples(ALCdevice *pDevice, ALCvoid *pBuffer, ALCuint lSamples)
610 alsa_data *data = (alsa_data*)pDevice->ExtraData;
611 const snd_pcm_channel_area_t *areas = NULL;
612 snd_pcm_sframes_t frames, commitres;
613 snd_pcm_uframes_t size, offset;
614 int err;
616 frames = psnd_pcm_avail_update(data->pcmHandle);
617 if(frames < 0)
619 err = xrun_recovery(data->pcmHandle, frames);
620 if (err < 0)
621 AL_PRINT("available update failed: %s\n", psnd_strerror(err));
622 else
623 frames = psnd_pcm_avail_update(data->pcmHandle);
625 if (frames < (snd_pcm_sframes_t)lSamples)
627 SetALCError(ALC_INVALID_VALUE);
628 return;
631 // it is possible that contiguous areas are smaller, thus we use a loop
632 while (lSamples > 0)
634 char *Pointer;
635 int Count;
637 size = lSamples;
638 err = psnd_pcm_mmap_begin(data->pcmHandle, &areas, &offset, &size);
639 if (err < 0)
641 err = xrun_recovery(data->pcmHandle, err);
642 if (err < 0)
644 AL_PRINT("mmap begin error: %s\n", psnd_strerror(err));
645 break;
647 continue;
650 Pointer = (char*)areas->addr + (offset * areas->step / 8);
651 Count = psnd_pcm_frames_to_bytes(data->pcmHandle, size);
653 memcpy(pBuffer, Pointer, Count);
654 pBuffer = (char*)pBuffer + Count;
656 commitres = psnd_pcm_mmap_commit(data->pcmHandle, offset, size);
657 if (commitres < 0 || (commitres-size) != 0)
659 AL_PRINT("mmap commit error: %s\n",
660 psnd_strerror(commitres >= 0 ? -EPIPE : commitres));
661 break;
664 lSamples -= size;
668 static ALCuint alsa_available_samples(ALCdevice *pDevice)
670 alsa_data *data = (alsa_data*)pDevice->ExtraData;
671 snd_pcm_sframes_t frames = psnd_pcm_avail_update(data->pcmHandle);
672 if(frames < 0)
674 int err = xrun_recovery(data->pcmHandle, frames);
675 if (err < 0)
676 AL_PRINT("available update failed: %s\n", psnd_strerror(err));
677 else
678 frames = psnd_pcm_avail_update(data->pcmHandle);
679 if(frames < 0) /* ew.. */
680 SetALCError(ALC_INVALID_DEVICE);
682 return max(frames, 0);
686 BackendFuncs alsa_funcs = {
687 alsa_open_playback,
688 alsa_close_playback,
689 alsa_open_capture,
690 alsa_close_capture,
691 alsa_start_capture,
692 alsa_stop_capture,
693 alsa_capture_samples,
694 alsa_available_samples
697 void alc_alsa_init(BackendFuncs *func_list)
699 snd_ctl_t *handle;
700 int card, err, dev, idx = 1;
701 snd_ctl_card_info_t *info;
702 snd_pcm_info_t *pcminfo;
703 snd_pcm_stream_t stream = SND_PCM_STREAM_PLAYBACK;
704 char name[128];
705 char *str;
707 *func_list = alsa_funcs;
709 #ifdef HAVE_DLFCN_H
710 alsa_handle = dlopen("libasound.so.2", RTLD_NOW);
711 if(!alsa_handle)
712 return;
713 dlerror();
715 #define LOAD_FUNC(f) do { \
716 p##f = (typeof(f)*)dlsym(alsa_handle, #f); \
717 if((str=dlerror()) != NULL) \
719 dlclose(alsa_handle); \
720 alsa_handle = NULL; \
721 AL_PRINT("Could not load %s from libasound.so.2: %s\n", #f, str); \
722 return; \
724 } while(0)
725 #else
726 str = NULL;
727 alsa_handle = 0xDEADBEEF;
728 #define LOAD_FUNC(f) p##f = f
729 #endif
731 LOAD_FUNC(snd_strerror);
732 LOAD_FUNC(snd_pcm_open);
733 LOAD_FUNC(snd_pcm_close);
734 LOAD_FUNC(snd_pcm_nonblock);
735 LOAD_FUNC(snd_pcm_frames_to_bytes);
736 LOAD_FUNC(snd_pcm_hw_params_malloc);
737 LOAD_FUNC(snd_pcm_hw_params_free);
738 LOAD_FUNC(snd_pcm_hw_params_any);
739 LOAD_FUNC(snd_pcm_hw_params_set_access);
740 LOAD_FUNC(snd_pcm_hw_params_set_format);
741 LOAD_FUNC(snd_pcm_hw_params_set_channels);
742 LOAD_FUNC(snd_pcm_hw_params_set_periods_near);
743 LOAD_FUNC(snd_pcm_hw_params_set_rate_near);
744 LOAD_FUNC(snd_pcm_hw_params_set_rate);
745 LOAD_FUNC(snd_pcm_hw_params_set_buffer_size_near);
746 LOAD_FUNC(snd_pcm_hw_params_set_buffer_size_min);
747 LOAD_FUNC(snd_pcm_hw_params_get_period_size);
748 LOAD_FUNC(snd_pcm_hw_params_get_access);
749 LOAD_FUNC(snd_pcm_hw_params);
750 LOAD_FUNC(snd_pcm_prepare);
751 LOAD_FUNC(snd_pcm_start);
752 LOAD_FUNC(snd_pcm_resume);
753 LOAD_FUNC(snd_pcm_wait);
754 LOAD_FUNC(snd_pcm_state);
755 LOAD_FUNC(snd_pcm_avail_update);
756 LOAD_FUNC(snd_pcm_areas_silence);
757 LOAD_FUNC(snd_pcm_mmap_begin);
758 LOAD_FUNC(snd_pcm_mmap_commit);
759 LOAD_FUNC(snd_pcm_writei);
760 LOAD_FUNC(snd_pcm_drain);
762 LOAD_FUNC(snd_pcm_info_malloc);
763 LOAD_FUNC(snd_pcm_info_free);
764 LOAD_FUNC(snd_pcm_info_set_device);
765 LOAD_FUNC(snd_pcm_info_set_subdevice);
766 LOAD_FUNC(snd_pcm_info_set_stream);
767 LOAD_FUNC(snd_pcm_info_get_name);
768 LOAD_FUNC(snd_ctl_pcm_next_device);
769 LOAD_FUNC(snd_ctl_pcm_info);
770 LOAD_FUNC(snd_ctl_open);
771 LOAD_FUNC(snd_ctl_close);
772 LOAD_FUNC(snd_ctl_card_info_malloc);
773 LOAD_FUNC(snd_ctl_card_info_free);
774 LOAD_FUNC(snd_ctl_card_info);
775 LOAD_FUNC(snd_ctl_card_info_get_name);
776 LOAD_FUNC(snd_card_next);
778 #undef LOAD_FUNC
780 psnd_ctl_card_info_malloc(&info);
781 psnd_pcm_info_malloc(&pcminfo);
783 card = -1;
784 if(psnd_card_next(&card) < 0 || card < 0)
785 AL_PRINT("no playback cards found...\n");
786 else
788 alsaDeviceList[0] = AppendDeviceList("ALSA Software on default");
789 allDevNameMap[0].name = AppendAllDeviceList("ALSA Software on default");
792 while (card >= 0) {
793 int firstDev = 1;
795 sprintf(name, "hw:%d", card);
796 if ((err = psnd_ctl_open(&handle, name, 0)) < 0) {
797 AL_PRINT("control open (%i): %s\n", card, psnd_strerror(err));
798 goto next_card;
800 if ((err = psnd_ctl_card_info(handle, info)) < 0) {
801 AL_PRINT("control hardware info (%i): %s\n", card, psnd_strerror(err));
802 psnd_ctl_close(handle);
803 goto next_card;
806 dev = -1;
807 while (idx < MAX_ALL_DEVICES) {
808 const char *cname, *dname;
810 if (psnd_ctl_pcm_next_device(handle, &dev)<0)
811 AL_PRINT("snd_ctl_pcm_next_device failed\n");
812 if (dev < 0)
813 break;
815 if(firstDev && card < MAX_DEVICES-1) {
816 firstDev = 0;
817 snprintf(name, sizeof(name), "ALSA Software on %s",
818 psnd_ctl_card_info_get_name(info));
819 alsaDeviceList[card+1] = AppendDeviceList(name);
822 psnd_pcm_info_set_device(pcminfo, dev);
823 psnd_pcm_info_set_subdevice(pcminfo, 0);
824 psnd_pcm_info_set_stream(pcminfo, stream);
825 if ((err = psnd_ctl_pcm_info(handle, pcminfo)) < 0) {
826 if (err != -ENOENT)
827 AL_PRINT("control digital audio info (%i): %s\n", card, psnd_strerror(err));
828 continue;
831 cname = psnd_ctl_card_info_get_name(info);
832 dname = psnd_pcm_info_get_name(pcminfo);
833 snprintf(name, sizeof(name), "ALSA Software on %s [%s]",
834 cname, dname);
835 allDevNameMap[idx].name = AppendAllDeviceList(name);
836 allDevNameMap[idx].card = card;
837 allDevNameMap[idx].dev = dev;
838 idx++;
840 psnd_ctl_close(handle);
841 next_card:
842 if(psnd_card_next(&card) < 0) {
843 AL_PRINT("snd_card_next failed\n");
844 break;
849 stream = SND_PCM_STREAM_CAPTURE;
851 card = -1;
852 if(psnd_card_next(&card) < 0 || card < 0) {
853 AL_PRINT("no capture cards found...\n");
854 psnd_pcm_info_free(pcminfo);
855 psnd_ctl_card_info_free(info);
856 return;
859 allCaptureDevNameMap[0].name = AppendCaptureDeviceList("ALSA Capture on default");
860 idx = 1;
862 while (card >= 0) {
863 sprintf(name, "hw:%d", card);
864 handle = NULL;
865 if ((err = psnd_ctl_open(&handle, name, 0)) < 0) {
866 AL_PRINT("control open (%i): %s\n", card, psnd_strerror(err));
868 if (err >= 0 && (err = psnd_ctl_card_info(handle, info)) < 0) {
869 AL_PRINT("control hardware info (%i): %s\n", card, psnd_strerror(err));
871 else if (err >= 0)
873 dev = -1;
874 while (idx < MAX_ALL_DEVICES) {
875 const char *cname, *dname;
877 if (psnd_ctl_pcm_next_device(handle, &dev)<0)
878 AL_PRINT("snd_ctl_pcm_next_device failed\n");
879 if (dev < 0)
880 break;
881 psnd_pcm_info_set_device(pcminfo, dev);
882 psnd_pcm_info_set_subdevice(pcminfo, 0);
883 psnd_pcm_info_set_stream(pcminfo, stream);
884 if ((err = psnd_ctl_pcm_info(handle, pcminfo)) < 0) {
885 if (err != -ENOENT)
886 AL_PRINT("control digital audio info (%i): %s\n", card, psnd_strerror(err));
887 continue;
890 cname = psnd_ctl_card_info_get_name(info);
891 dname = psnd_pcm_info_get_name(pcminfo);
892 snprintf(name, sizeof(name), "ALSA Capture on %s [%s]",
893 cname, dname);
894 allCaptureDevNameMap[idx].name = AppendCaptureDeviceList(name);
895 allCaptureDevNameMap[idx].card = card;
896 allCaptureDevNameMap[idx].dev = dev;
897 idx++;
900 if(handle) psnd_ctl_close(handle);
901 if(psnd_card_next(&card) < 0) {
902 AL_PRINT("snd_card_next failed\n");
903 break;
906 psnd_pcm_info_free(pcminfo);
907 psnd_ctl_card_info_free(info);