Release 1.3.253
[openal-soft.git] / Alc / alsa.c
blobfa655386d9de647d0be2a697d04f5f5c218f6679
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 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_start(data->pcmHandle);
601 static void alsa_stop_capture(ALCdevice *pDevice)
603 alsa_data *data = (alsa_data*)pDevice->ExtraData;
604 psnd_pcm_drain(data->pcmHandle);
607 static void alsa_capture_samples(ALCdevice *pDevice, ALCvoid *pBuffer, ALCuint lSamples)
609 alsa_data *data = (alsa_data*)pDevice->ExtraData;
610 const snd_pcm_channel_area_t *areas = NULL;
611 snd_pcm_sframes_t frames, commitres;
612 snd_pcm_uframes_t size, offset;
613 int err;
615 frames = psnd_pcm_avail_update(data->pcmHandle);
616 if(frames < 0)
618 err = xrun_recovery(data->pcmHandle, frames);
619 if (err < 0)
620 AL_PRINT("available update failed: %s\n", psnd_strerror(err));
621 else
622 frames = psnd_pcm_avail_update(data->pcmHandle);
624 if (frames < (snd_pcm_sframes_t)lSamples)
626 SetALCError(ALC_INVALID_VALUE);
627 return;
630 // it is possible that contiguous areas are smaller, thus we use a loop
631 while (lSamples > 0)
633 char *Pointer;
634 int Count;
636 size = lSamples;
637 err = psnd_pcm_mmap_begin(data->pcmHandle, &areas, &offset, &size);
638 if (err < 0)
640 err = xrun_recovery(data->pcmHandle, err);
641 if (err < 0)
643 AL_PRINT("mmap begin error: %s\n", psnd_strerror(err));
644 break;
646 continue;
649 Pointer = (char*)areas->addr + (offset * areas->step / 8);
650 Count = psnd_pcm_frames_to_bytes(data->pcmHandle, size);
652 memcpy(pBuffer, Pointer, Count);
653 pBuffer = (char*)pBuffer + Count;
655 commitres = psnd_pcm_mmap_commit(data->pcmHandle, offset, size);
656 if (commitres < 0 || (commitres-size) != 0)
658 AL_PRINT("mmap commit error: %s\n",
659 psnd_strerror(commitres >= 0 ? -EPIPE : commitres));
660 break;
663 lSamples -= size;
667 static ALCuint alsa_available_samples(ALCdevice *pDevice)
669 alsa_data *data = (alsa_data*)pDevice->ExtraData;
670 snd_pcm_sframes_t frames = psnd_pcm_avail_update(data->pcmHandle);
671 if(frames < 0)
673 int err = xrun_recovery(data->pcmHandle, frames);
674 if (err < 0)
675 AL_PRINT("available update failed: %s\n", psnd_strerror(err));
676 else
677 frames = psnd_pcm_avail_update(data->pcmHandle);
678 if(frames < 0) /* ew.. */
679 SetALCError(ALC_INVALID_DEVICE);
681 return max(frames, 0);
685 BackendFuncs alsa_funcs = {
686 alsa_open_playback,
687 alsa_close_playback,
688 alsa_open_capture,
689 alsa_close_capture,
690 alsa_start_capture,
691 alsa_stop_capture,
692 alsa_capture_samples,
693 alsa_available_samples
696 void alc_alsa_init(BackendFuncs *func_list)
698 snd_ctl_t *handle;
699 int card, err, dev, idx = 1;
700 snd_ctl_card_info_t *info;
701 snd_pcm_info_t *pcminfo;
702 snd_pcm_stream_t stream = SND_PCM_STREAM_PLAYBACK;
703 char name[128];
704 char *str;
706 *func_list = alsa_funcs;
708 #ifdef HAVE_DLFCN_H
709 alsa_handle = dlopen("libasound.so.2", RTLD_NOW);
710 if(!alsa_handle)
711 return;
712 dlerror();
714 #define LOAD_FUNC(f) do { \
715 p##f = (typeof(f)*)dlsym(alsa_handle, #f); \
716 if((str=dlerror()) != NULL) \
718 dlclose(alsa_handle); \
719 alsa_handle = NULL; \
720 AL_PRINT("Could not load %s from libasound.so.2: %s\n", #f, str); \
721 return; \
723 } while(0)
724 #else
725 str = NULL;
726 alsa_handle = 0xDEADBEEF;
727 #define LOAD_FUNC(f) p##f = f
728 #endif
730 LOAD_FUNC(snd_strerror);
731 LOAD_FUNC(snd_pcm_open);
732 LOAD_FUNC(snd_pcm_close);
733 LOAD_FUNC(snd_pcm_nonblock);
734 LOAD_FUNC(snd_pcm_frames_to_bytes);
735 LOAD_FUNC(snd_pcm_hw_params_malloc);
736 LOAD_FUNC(snd_pcm_hw_params_free);
737 LOAD_FUNC(snd_pcm_hw_params_any);
738 LOAD_FUNC(snd_pcm_hw_params_set_access);
739 LOAD_FUNC(snd_pcm_hw_params_set_format);
740 LOAD_FUNC(snd_pcm_hw_params_set_channels);
741 LOAD_FUNC(snd_pcm_hw_params_set_periods_near);
742 LOAD_FUNC(snd_pcm_hw_params_set_rate_near);
743 LOAD_FUNC(snd_pcm_hw_params_set_rate);
744 LOAD_FUNC(snd_pcm_hw_params_set_buffer_size_near);
745 LOAD_FUNC(snd_pcm_hw_params_set_buffer_size_min);
746 LOAD_FUNC(snd_pcm_hw_params_get_period_size);
747 LOAD_FUNC(snd_pcm_hw_params_get_access);
748 LOAD_FUNC(snd_pcm_hw_params);
749 LOAD_FUNC(snd_pcm_prepare);
750 LOAD_FUNC(snd_pcm_start);
751 LOAD_FUNC(snd_pcm_resume);
752 LOAD_FUNC(snd_pcm_wait);
753 LOAD_FUNC(snd_pcm_state);
754 LOAD_FUNC(snd_pcm_avail_update);
755 LOAD_FUNC(snd_pcm_areas_silence);
756 LOAD_FUNC(snd_pcm_mmap_begin);
757 LOAD_FUNC(snd_pcm_mmap_commit);
758 LOAD_FUNC(snd_pcm_writei);
759 LOAD_FUNC(snd_pcm_drain);
761 LOAD_FUNC(snd_pcm_info_malloc);
762 LOAD_FUNC(snd_pcm_info_free);
763 LOAD_FUNC(snd_pcm_info_set_device);
764 LOAD_FUNC(snd_pcm_info_set_subdevice);
765 LOAD_FUNC(snd_pcm_info_set_stream);
766 LOAD_FUNC(snd_pcm_info_get_name);
767 LOAD_FUNC(snd_ctl_pcm_next_device);
768 LOAD_FUNC(snd_ctl_pcm_info);
769 LOAD_FUNC(snd_ctl_open);
770 LOAD_FUNC(snd_ctl_close);
771 LOAD_FUNC(snd_ctl_card_info_malloc);
772 LOAD_FUNC(snd_ctl_card_info_free);
773 LOAD_FUNC(snd_ctl_card_info);
774 LOAD_FUNC(snd_ctl_card_info_get_name);
775 LOAD_FUNC(snd_card_next);
777 #undef LOAD_FUNC
779 psnd_ctl_card_info_malloc(&info);
780 psnd_pcm_info_malloc(&pcminfo);
782 card = -1;
783 if(psnd_card_next(&card) < 0 || card < 0)
784 AL_PRINT("no playback cards found...\n");
785 else
787 alsaDeviceList[0] = AppendDeviceList("ALSA Software on default");
788 allDevNameMap[0].name = AppendAllDeviceList("ALSA Software on default");
791 while (card >= 0) {
792 int firstDev = 1;
794 sprintf(name, "hw:%d", card);
795 if ((err = psnd_ctl_open(&handle, name, 0)) < 0) {
796 AL_PRINT("control open (%i): %s\n", card, psnd_strerror(err));
797 goto next_card;
799 if ((err = psnd_ctl_card_info(handle, info)) < 0) {
800 AL_PRINT("control hardware info (%i): %s\n", card, psnd_strerror(err));
801 psnd_ctl_close(handle);
802 goto next_card;
805 dev = -1;
806 while (idx < MAX_ALL_DEVICES) {
807 const char *cname, *dname;
809 if (psnd_ctl_pcm_next_device(handle, &dev)<0)
810 AL_PRINT("snd_ctl_pcm_next_device failed\n");
811 if (dev < 0)
812 break;
814 if(firstDev && card < MAX_DEVICES-1) {
815 firstDev = 0;
816 snprintf(name, sizeof(name), "ALSA Software on %s",
817 psnd_ctl_card_info_get_name(info));
818 alsaDeviceList[card+1] = AppendDeviceList(name);
821 psnd_pcm_info_set_device(pcminfo, dev);
822 psnd_pcm_info_set_subdevice(pcminfo, 0);
823 psnd_pcm_info_set_stream(pcminfo, stream);
824 if ((err = psnd_ctl_pcm_info(handle, pcminfo)) < 0) {
825 if (err != -ENOENT)
826 AL_PRINT("control digital audio info (%i): %s\n", card, psnd_strerror(err));
827 continue;
830 cname = psnd_ctl_card_info_get_name(info);
831 dname = psnd_pcm_info_get_name(pcminfo);
832 snprintf(name, sizeof(name), "ALSA Software on %s [%s]",
833 cname, dname);
834 allDevNameMap[idx].name = AppendAllDeviceList(name);
835 allDevNameMap[idx].card = card;
836 allDevNameMap[idx].dev = dev;
837 idx++;
839 psnd_ctl_close(handle);
840 next_card:
841 if(psnd_card_next(&card) < 0) {
842 AL_PRINT("snd_card_next failed\n");
843 break;
848 stream = SND_PCM_STREAM_CAPTURE;
850 card = -1;
851 if(psnd_card_next(&card) < 0 || card < 0) {
852 AL_PRINT("no capture cards found...\n");
853 psnd_pcm_info_free(pcminfo);
854 psnd_ctl_card_info_free(info);
855 return;
858 allCaptureDevNameMap[0].name = AppendCaptureDeviceList("ALSA Capture on default");
860 while (card >= 0) {
861 sprintf(name, "hw:%d", card);
862 handle = NULL;
863 if ((err = psnd_ctl_open(&handle, name, 0)) < 0) {
864 AL_PRINT("control open (%i): %s\n", card, psnd_strerror(err));
866 if (err >= 0 && (err = psnd_ctl_card_info(handle, info)) < 0) {
867 AL_PRINT("control hardware info (%i): %s\n", card, psnd_strerror(err));
869 else if (err >= 0)
871 dev = -1;
872 while (idx < MAX_ALL_DEVICES) {
873 const char *cname, *dname;
875 if (psnd_ctl_pcm_next_device(handle, &dev)<0)
876 AL_PRINT("snd_ctl_pcm_next_device failed\n");
877 if (dev < 0)
878 break;
879 psnd_pcm_info_set_device(pcminfo, dev);
880 psnd_pcm_info_set_subdevice(pcminfo, 0);
881 psnd_pcm_info_set_stream(pcminfo, stream);
882 if ((err = psnd_ctl_pcm_info(handle, pcminfo)) < 0) {
883 if (err != -ENOENT)
884 AL_PRINT("control digital audio info (%i): %s\n", card, psnd_strerror(err));
885 continue;
888 cname = psnd_ctl_card_info_get_name(info);
889 dname = psnd_pcm_info_get_name(pcminfo);
890 snprintf(name, sizeof(name), "ALSA Capture on %s [%s]",
891 cname, dname);
892 allDevNameMap[idx].name = AppendCaptureDeviceList(name);
893 allDevNameMap[idx].card = card;
894 allDevNameMap[idx].dev = dev;
895 idx++;
898 if(handle) psnd_ctl_close(handle);
899 if(psnd_card_next(&card) < 0) {
900 AL_PRINT("snd_card_next failed\n");
901 break;
904 psnd_pcm_info_free(pcminfo);
905 psnd_ctl_card_info_free(info);