More padding fixes
[openal-soft.git] / Alc / alsa.c
blob14450a953e10fc75be914f529648be5059ad4a05
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 RingBuffer *ring;
44 int doCapture;
46 volatile int killNow;
47 ALvoid *thread;
48 } alsa_data;
50 typedef struct {
51 ALCchar *name;
52 int card, dev;
53 } DevMap;
55 static void *alsa_handle;
56 #define MAKE_FUNC(f) static typeof(f) * p##f
57 MAKE_FUNC(snd_strerror);
58 MAKE_FUNC(snd_pcm_open);
59 MAKE_FUNC(snd_pcm_close);
60 MAKE_FUNC(snd_pcm_nonblock);
61 MAKE_FUNC(snd_pcm_frames_to_bytes);
62 MAKE_FUNC(snd_pcm_hw_params_malloc);
63 MAKE_FUNC(snd_pcm_hw_params_free);
64 MAKE_FUNC(snd_pcm_hw_params_any);
65 MAKE_FUNC(snd_pcm_hw_params_set_access);
66 MAKE_FUNC(snd_pcm_hw_params_set_format);
67 MAKE_FUNC(snd_pcm_hw_params_set_channels);
68 MAKE_FUNC(snd_pcm_hw_params_set_periods_near);
69 MAKE_FUNC(snd_pcm_hw_params_set_rate_near);
70 MAKE_FUNC(snd_pcm_hw_params_set_rate);
71 MAKE_FUNC(snd_pcm_hw_params_set_buffer_size_near);
72 MAKE_FUNC(snd_pcm_hw_params_set_buffer_size_min);
73 MAKE_FUNC(snd_pcm_hw_params_get_buffer_size);
74 MAKE_FUNC(snd_pcm_hw_params_get_period_size);
75 MAKE_FUNC(snd_pcm_hw_params_get_access);
76 MAKE_FUNC(snd_pcm_hw_params);
77 MAKE_FUNC(snd_pcm_prepare);
78 MAKE_FUNC(snd_pcm_start);
79 MAKE_FUNC(snd_pcm_resume);
80 MAKE_FUNC(snd_pcm_wait);
81 MAKE_FUNC(snd_pcm_state);
82 MAKE_FUNC(snd_pcm_avail_update);
83 MAKE_FUNC(snd_pcm_areas_silence);
84 MAKE_FUNC(snd_pcm_mmap_begin);
85 MAKE_FUNC(snd_pcm_mmap_commit);
86 MAKE_FUNC(snd_pcm_readi);
87 MAKE_FUNC(snd_pcm_writei);
88 MAKE_FUNC(snd_pcm_drain);
89 MAKE_FUNC(snd_pcm_info_malloc);
90 MAKE_FUNC(snd_pcm_info_free);
91 MAKE_FUNC(snd_pcm_info_set_device);
92 MAKE_FUNC(snd_pcm_info_set_subdevice);
93 MAKE_FUNC(snd_pcm_info_set_stream);
94 MAKE_FUNC(snd_pcm_info_get_name);
95 MAKE_FUNC(snd_ctl_pcm_next_device);
96 MAKE_FUNC(snd_ctl_pcm_info);
97 MAKE_FUNC(snd_ctl_open);
98 MAKE_FUNC(snd_ctl_close);
99 MAKE_FUNC(snd_ctl_card_info_malloc);
100 MAKE_FUNC(snd_ctl_card_info_free);
101 MAKE_FUNC(snd_ctl_card_info);
102 MAKE_FUNC(snd_ctl_card_info_get_name);
103 MAKE_FUNC(snd_card_next);
104 #undef MAKE_FUNC
106 #define MAX_DEVICES 16
107 #define MAX_ALL_DEVICES 32
109 static DevMap allDevNameMap[MAX_ALL_DEVICES];
110 static ALCchar *alsaDeviceList[MAX_DEVICES];
111 static DevMap allCaptureDevNameMap[MAX_ALL_DEVICES];
113 static int xrun_recovery(snd_pcm_t *handle, int err)
115 if (err == -EPIPE)
116 { /* under-run */
117 err = psnd_pcm_prepare(handle);
118 if (err < 0)
119 AL_PRINT("prepare failed: %s\n", psnd_strerror(err));
121 else if (err == -ESTRPIPE)
123 while ((err = psnd_pcm_resume(handle)) == -EAGAIN)
124 Sleep(1); /* wait until the suspend flag is released */
125 if (err < 0)
127 err = psnd_pcm_prepare(handle);
128 if (err < 0)
129 AL_PRINT("prepare failed: %s\n", psnd_strerror(err));
132 return err;
136 static ALuint ALSAProc(ALvoid *ptr)
138 ALCdevice *pDevice = (ALCdevice*)ptr;
139 alsa_data *data = (alsa_data*)pDevice->ExtraData;
140 const snd_pcm_channel_area_t *areas = NULL;
141 snd_pcm_sframes_t avail, commitres;
142 snd_pcm_uframes_t offset, frames;
143 char *WritePtr;
144 int WriteCnt;
145 int err;
147 while(!data->killNow)
149 snd_pcm_state_t state = psnd_pcm_state(data->pcmHandle);
150 if(state == SND_PCM_STATE_XRUN)
152 err = xrun_recovery(data->pcmHandle, -EPIPE);
153 if (err < 0)
155 AL_PRINT("XRUN recovery failed: %s\n", psnd_strerror(err));
156 break;
159 else if (state == SND_PCM_STATE_SUSPENDED)
161 err = xrun_recovery(data->pcmHandle, -ESTRPIPE);
162 if (err < 0)
164 AL_PRINT("SUSPEND recovery failed: %s\n", psnd_strerror(err));
165 break;
169 avail = psnd_pcm_avail_update(data->pcmHandle);
170 if(avail < 0)
172 err = xrun_recovery(data->pcmHandle, avail);
173 if (err < 0)
175 AL_PRINT("available update failed: %s\n", psnd_strerror(err));
176 break;
180 // make sure there's frames to process
181 if(avail == 0)
183 if(state != SND_PCM_STATE_RUNNING)
185 err = psnd_pcm_start(data->pcmHandle);
186 if(err < 0)
187 err = xrun_recovery(data->pcmHandle, err);
188 if(err < 0)
190 AL_PRINT("start failed: %s\n", psnd_strerror(err));
191 break;
194 else if(psnd_pcm_wait(data->pcmHandle, 1000) == 0)
195 AL_PRINT("Wait timeout... buffer size too low?\n");
196 continue;
199 // it is possible that contiguous areas are smaller, thus we use a loop
200 while (avail > 0)
202 frames = avail;
204 err = psnd_pcm_mmap_begin(data->pcmHandle, &areas, &offset, &frames);
205 if (err < 0)
207 err = xrun_recovery(data->pcmHandle, err);
208 if (err < 0)
209 AL_PRINT("mmap begin error: %s\n", psnd_strerror(err));
210 break;
213 SuspendContext(NULL);
214 WritePtr = (char*)areas->addr + (offset * areas->step / 8);
215 WriteCnt = psnd_pcm_frames_to_bytes(data->pcmHandle, frames);
216 aluMixData(pDevice->Context, WritePtr, WriteCnt, pDevice->Format);
217 ProcessContext(NULL);
219 commitres = psnd_pcm_mmap_commit(data->pcmHandle, offset, frames);
220 if (commitres < 0 || (commitres-frames) != 0)
222 AL_PRINT("mmap commit error: %s\n",
223 psnd_strerror(commitres >= 0 ? -EPIPE : commitres));
224 break;
227 avail -= frames;
231 return 0;
234 static ALuint ALSANoMMapProc(ALvoid *ptr)
236 ALCdevice *pDevice = (ALCdevice*)ptr;
237 alsa_data *data = (alsa_data*)pDevice->ExtraData;
238 snd_pcm_sframes_t avail;
239 char *WritePtr;
241 while(!data->killNow)
243 SuspendContext(NULL);
244 aluMixData(pDevice->Context, data->buffer, data->size, pDevice->Format);
245 ProcessContext(NULL);
247 WritePtr = data->buffer;
248 avail = (snd_pcm_uframes_t)data->size / psnd_pcm_frames_to_bytes(data->pcmHandle, 1);
249 while(avail > 0)
251 int ret = psnd_pcm_writei(data->pcmHandle, WritePtr, avail);
252 switch (ret)
254 case -EAGAIN:
255 continue;
256 case -ESTRPIPE:
257 while((ret=psnd_pcm_resume(data->pcmHandle)) == -EAGAIN)
258 Sleep(1);
259 break;
260 case -EPIPE:
261 break;
262 default:
263 if (ret >= 0)
265 WritePtr += psnd_pcm_frames_to_bytes(data->pcmHandle, ret);
266 avail -= ret;
268 break;
270 if (ret < 0)
272 ret = psnd_pcm_prepare(data->pcmHandle);
273 if(ret < 0)
274 break;
279 return 0;
282 static ALuint ALSANoMMapCaptureProc(ALvoid *ptr)
284 ALCdevice *pDevice = (ALCdevice*)ptr;
285 alsa_data *data = (alsa_data*)pDevice->ExtraData;
286 snd_pcm_sframes_t avail;
288 while(!data->killNow)
290 avail = (snd_pcm_uframes_t)data->size / psnd_pcm_frames_to_bytes(data->pcmHandle, 1);
291 avail = psnd_pcm_readi(data->pcmHandle, data->buffer, avail);
292 switch(avail)
294 case -EAGAIN:
295 continue;
296 case -ESTRPIPE:
297 while((avail=psnd_pcm_resume(data->pcmHandle)) == -EAGAIN)
298 Sleep(1);
299 break;
300 case -EPIPE:
301 break;
302 default:
303 if (avail >= 0 && data->doCapture)
304 WriteRingBuffer(data->ring, data->buffer, avail);
305 break;
307 if(avail < 0)
309 avail = psnd_pcm_prepare(data->pcmHandle);
310 if(avail < 0)
311 AL_PRINT("prepare error: %s\n", psnd_strerror(avail));
315 return 0;
318 static ALCboolean alsa_open_playback(ALCdevice *device, const ALCchar *deviceName)
320 snd_pcm_uframes_t bufferSizeInFrames;
321 snd_pcm_hw_params_t *p = NULL;
322 snd_pcm_access_t access;
323 unsigned int periods;
324 alsa_data *data;
325 char driver[64];
326 const char *str;
327 int allowmmap;
328 char *err;
329 int i;
331 if(!alsa_handle)
332 return ALC_FALSE;
334 strncpy(driver, GetConfigValue("alsa", "device", "default"), sizeof(driver)-1);
335 driver[sizeof(driver)-1] = 0;
336 if(deviceName)
338 size_t idx;
340 for(idx = 0;idx < MAX_ALL_DEVICES;idx++)
342 if(allDevNameMap[idx].name &&
343 strcmp(deviceName, allDevNameMap[idx].name) == 0)
345 device->szDeviceName = allDevNameMap[idx].name;
346 if(idx > 0)
347 sprintf(driver, "hw:%d,%d", allDevNameMap[idx].card, allDevNameMap[idx].dev);
348 goto open_alsa;
351 for(idx = 0;idx < MAX_DEVICES;idx++)
353 if(alsaDeviceList[idx] &&
354 strcmp(deviceName, alsaDeviceList[idx]) == 0)
356 device->szDeviceName = alsaDeviceList[idx];
357 if(idx > 0)
358 sprintf(driver, "hw:%zd,0", idx-1);
359 goto open_alsa;
362 return ALC_FALSE;
364 else
365 device->szDeviceName = alsaDeviceList[0];
367 open_alsa:
368 data = (alsa_data*)calloc(1, sizeof(alsa_data));
370 i = psnd_pcm_open(&data->pcmHandle, driver, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
371 if(i < 0)
373 Sleep(200);
374 i = psnd_pcm_open(&data->pcmHandle, driver, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
376 if(i >= 0)
378 i = psnd_pcm_nonblock(data->pcmHandle, 0);
379 if(i < 0)
380 psnd_pcm_close(data->pcmHandle);
382 if(i < 0)
384 free(data);
385 AL_PRINT("Could not open playback device '%s': %s\n", driver, psnd_strerror(i));
386 return ALC_FALSE;
389 switch(aluBytesFromFormat(device->Format))
391 case 1:
392 data->format = SND_PCM_FORMAT_U8;
393 break;
394 case 2:
395 data->format = SND_PCM_FORMAT_S16;
396 break;
397 default:
398 data->format = SND_PCM_FORMAT_UNKNOWN;
399 AL_PRINT("Unknown format?! %x\n", device->Format);
402 periods = GetConfigValueInt("alsa", "periods", 0);
403 bufferSizeInFrames = device->UpdateSize;
405 str = GetConfigValue("alsa", "mmap", "true");
406 allowmmap = (strcasecmp(str, "true") == 0 ||
407 strcasecmp(str, "yes") == 0 ||
408 strcasecmp(str, "on") == 0 ||
409 atoi(str) != 0);
411 psnd_pcm_hw_params_malloc(&p);
412 #define ok(func, str) (i=(func),((i<0)?(err=(str)),0:1))
413 /* start with the largest configuration space possible */
414 if(!(ok(psnd_pcm_hw_params_any(data->pcmHandle, p), "any") &&
415 /* set interleaved access */
416 ((allowmmap && ok(psnd_pcm_hw_params_set_access(data->pcmHandle, p, SND_PCM_ACCESS_MMAP_INTERLEAVED), "set access")) ||
417 ok(psnd_pcm_hw_params_set_access(data->pcmHandle, p, SND_PCM_ACCESS_RW_INTERLEAVED), "set access")) &&
418 /* set format (implicitly sets sample bits) */
419 ok(psnd_pcm_hw_params_set_format(data->pcmHandle, p, data->format), "set format") &&
420 /* set channels (implicitly sets frame bits) */
421 ok(psnd_pcm_hw_params_set_channels(data->pcmHandle, p, aluChannelsFromFormat(device->Format)), "set channels") &&
422 /* set periods (implicitly constrains period/buffer parameters) */
423 (!periods || ok(psnd_pcm_hw_params_set_periods_near(data->pcmHandle, p, &periods, NULL), "set periods near")) &&
424 /* set rate (implicitly constrains period/buffer parameters) */
425 ok(psnd_pcm_hw_params_set_rate_near(data->pcmHandle, p, &device->Frequency, NULL), "set rate near") &&
426 /* set buffer size in frame units (implicitly sets period size/bytes/time and buffer time/bytes) */
427 ok(psnd_pcm_hw_params_set_buffer_size_near(data->pcmHandle, p, &bufferSizeInFrames), "set buffer size near") &&
428 /* install and prepare hardware configuration */
429 ok(psnd_pcm_hw_params(data->pcmHandle, p), "set params")))
431 AL_PRINT("%s failed: %s\n", err, psnd_strerror(i));
432 psnd_pcm_hw_params_free(p);
433 psnd_pcm_close(data->pcmHandle);
434 free(data);
435 return ALC_FALSE;
437 #undef ok
439 if((i=psnd_pcm_hw_params_get_access(p, &access)) < 0)
441 AL_PRINT("get_access failed: %s\n", psnd_strerror(i));
442 psnd_pcm_hw_params_free(p);
443 psnd_pcm_close(data->pcmHandle);
444 free(data);
445 return ALC_FALSE;
448 if((i=psnd_pcm_hw_params_get_period_size(p, &bufferSizeInFrames, NULL)) < 0)
450 AL_PRINT("get_period_size failed: %s\n", psnd_strerror(i));
451 psnd_pcm_hw_params_free(p);
452 psnd_pcm_close(data->pcmHandle);
453 free(data);
454 return ALC_FALSE;
457 psnd_pcm_hw_params_free(p);
459 device->UpdateSize = bufferSizeInFrames;
461 data->size = psnd_pcm_frames_to_bytes(data->pcmHandle, device->UpdateSize);
462 if(access == SND_PCM_ACCESS_RW_INTERLEAVED)
464 data->buffer = malloc(data->size);
465 if(!data->buffer)
467 AL_PRINT("buffer malloc failed\n");
468 psnd_pcm_close(data->pcmHandle);
469 free(data);
470 return ALC_FALSE;
473 else
475 i = psnd_pcm_prepare(data->pcmHandle);
476 if(i < 0)
478 AL_PRINT("prepare error: %s\n", psnd_strerror(i));
479 psnd_pcm_close(data->pcmHandle);
480 free(data->buffer);
481 free(data);
482 return ALC_FALSE;
486 device->ExtraData = data;
487 if(access == SND_PCM_ACCESS_RW_INTERLEAVED)
488 data->thread = StartThread(ALSANoMMapProc, device);
489 else
490 data->thread = StartThread(ALSAProc, device);
491 if(data->thread == NULL)
493 AL_PRINT("Could not create playback thread\n");
494 psnd_pcm_close(data->pcmHandle);
495 device->ExtraData = NULL;
496 free(data->buffer);
497 free(data);
498 return ALC_FALSE;
501 return ALC_TRUE;
504 static void alsa_close_playback(ALCdevice *device)
506 alsa_data *data = (alsa_data*)device->ExtraData;
507 data->killNow = 1;
508 StopThread(data->thread);
509 psnd_pcm_close(data->pcmHandle);
511 free(data->buffer);
512 free(data);
513 device->ExtraData = NULL;
517 static ALCboolean alsa_open_capture(ALCdevice *pDevice, const ALCchar *deviceName, ALCuint frequency, ALCenum format, ALCsizei SampleSize)
519 snd_pcm_format_t alsaFormat;
520 snd_pcm_hw_params_t *p;
521 snd_pcm_uframes_t bufferSizeInFrames;
522 snd_pcm_access_t access;
523 const char *str;
524 alsa_data *data;
525 char driver[64];
526 int allowmmap;
527 char *err;
528 int i;
530 if(!alsa_handle)
531 return ALC_FALSE;
533 strncpy(driver, GetConfigValue("alsa", "capture", "default"), sizeof(driver)-1);
534 driver[sizeof(driver)-1] = 0;
535 if(deviceName)
537 size_t idx;
539 for(idx = 0;idx < MAX_ALL_DEVICES;idx++)
541 if(allCaptureDevNameMap[idx].name &&
542 strcmp(deviceName, allCaptureDevNameMap[idx].name) == 0)
544 pDevice->szDeviceName = allCaptureDevNameMap[idx].name;
545 if(idx > 0)
546 sprintf(driver, "plughw:%d,%d", allCaptureDevNameMap[idx].card, allCaptureDevNameMap[idx].dev);
547 goto open_alsa;
550 return ALC_FALSE;
552 else
553 pDevice->szDeviceName = allCaptureDevNameMap[0].name;
555 open_alsa:
556 data = (alsa_data*)calloc(1, sizeof(alsa_data));
558 i = psnd_pcm_open(&data->pcmHandle, driver, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK);
559 if(i < 0)
561 Sleep(200);
562 i = psnd_pcm_open(&data->pcmHandle, driver, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK);
564 if(i >= 0)
566 i = psnd_pcm_nonblock(data->pcmHandle, 0);
567 if(i < 0)
568 psnd_pcm_close(data->pcmHandle);
570 if(i < 0)
572 free(data);
573 AL_PRINT("Could not open capture device '%s': %s\n", driver, psnd_strerror(i));
574 return ALC_FALSE;
577 switch(aluBytesFromFormat(format))
579 case 1:
580 alsaFormat = SND_PCM_FORMAT_U8;
581 break;
582 case 2:
583 alsaFormat = SND_PCM_FORMAT_S16;
584 break;
585 default:
586 alsaFormat = SND_PCM_FORMAT_UNKNOWN;
587 AL_PRINT("Unknown format?! %x\n", format);
590 str = GetConfigValue("alsa", "mmap", "true");
591 allowmmap = (strcasecmp(str, "true") == 0 ||
592 strcasecmp(str, "yes") == 0 ||
593 strcasecmp(str, "on") == 0 ||
594 atoi(str) != 0);
596 bufferSizeInFrames = SampleSize;
597 psnd_pcm_hw_params_malloc(&p);
598 #define ok(func, str) (i=(func),((i<0)?(err=(str)),0:1))
599 /* start with the largest configuration space possible */
600 if(!(allowmmap &&
601 ok(psnd_pcm_hw_params_any(data->pcmHandle, p), "any") &&
602 /* set interleaved access */
603 ok(psnd_pcm_hw_params_set_access(data->pcmHandle, p, SND_PCM_ACCESS_MMAP_INTERLEAVED), "set access") &&
604 /* set format (implicitly sets sample bits) */
605 ok(psnd_pcm_hw_params_set_format(data->pcmHandle, p, alsaFormat), "set format") &&
606 /* set channels (implicitly sets frame bits) */
607 ok(psnd_pcm_hw_params_set_channels(data->pcmHandle, p, aluChannelsFromFormat(pDevice->Format)), "set channels") &&
608 /* set rate (implicitly constrains period/buffer parameters) */
609 ok(psnd_pcm_hw_params_set_rate(data->pcmHandle, p, frequency, 0), "set rate") &&
610 /* set buffer size in frame units (implicitly sets period size/bytes/time and buffer time/bytes) */
611 ok(psnd_pcm_hw_params_set_buffer_size_min(data->pcmHandle, p, &bufferSizeInFrames), "set buffer size min") &&
612 /* install and prepare hardware configuration */
613 ok(psnd_pcm_hw_params(data->pcmHandle, p), "set params")))
615 if(i < 0)
616 AL_PRINT("%s failed: %s\n", err, psnd_strerror(i));
617 bufferSizeInFrames = SampleSize;
618 if(!(ok(psnd_pcm_hw_params_any(data->pcmHandle, p), "any") &&
619 ok(psnd_pcm_hw_params_set_access(data->pcmHandle, p, SND_PCM_ACCESS_RW_INTERLEAVED), "set access") &&
620 ok(psnd_pcm_hw_params_set_format(data->pcmHandle, p, alsaFormat), "set format") &&
621 ok(psnd_pcm_hw_params_set_channels(data->pcmHandle, p, aluChannelsFromFormat(pDevice->Format)), "set channels") &&
622 ok(psnd_pcm_hw_params_set_rate(data->pcmHandle, p, frequency, 0), "set rate") &&
623 ok(psnd_pcm_hw_params_set_buffer_size_near(data->pcmHandle, p, &bufferSizeInFrames), "set buffer size near") &&
624 ok(psnd_pcm_hw_params(data->pcmHandle, p), "set params")))
626 AL_PRINT("%s failed: %s\n", err, psnd_strerror(i));
627 psnd_pcm_hw_params_free(p);
628 psnd_pcm_close(data->pcmHandle);
629 free(data);
630 return ALC_FALSE;
633 #undef ok
635 if((i=psnd_pcm_hw_params_get_access(p, &access)) < 0)
637 AL_PRINT("get_access failed: %s\n", psnd_strerror(i));
638 psnd_pcm_hw_params_free(p);
639 psnd_pcm_close(data->pcmHandle);
640 free(data);
641 return ALC_FALSE;
643 if((i=psnd_pcm_hw_params_get_period_size(p, &bufferSizeInFrames, NULL)) < 0)
645 AL_PRINT("get size failed: %s\n", psnd_strerror(i));
646 psnd_pcm_hw_params_free(p);
647 psnd_pcm_close(data->pcmHandle);
648 free(data);
649 return ALC_FALSE;
652 psnd_pcm_hw_params_free(p);
654 if(access == SND_PCM_ACCESS_RW_INTERLEAVED)
656 ALuint frameSize = aluChannelsFromFormat(pDevice->Format);
657 frameSize *= aluBytesFromFormat(pDevice->Format);
659 data->ring = CreateRingBuffer(frameSize, SampleSize);
660 if(!data->ring)
662 AL_PRINT("ring buffer create failed\n");
663 psnd_pcm_close(data->pcmHandle);
664 free(data);
665 return ALC_FALSE;
668 data->size = psnd_pcm_frames_to_bytes(data->pcmHandle, bufferSizeInFrames);
669 data->buffer = malloc(data->size);
670 if(!data->buffer)
672 AL_PRINT("buffer malloc failed\n");
673 psnd_pcm_close(data->pcmHandle);
674 DestroyRingBuffer(data->ring);
675 free(data);
676 return ALC_FALSE;
679 pDevice->ExtraData = data;
680 data->thread = StartThread(ALSANoMMapCaptureProc, pDevice);
681 if(data->thread == NULL)
683 AL_PRINT("Could not create capture thread\n");
684 pDevice->ExtraData = NULL;
685 psnd_pcm_close(data->pcmHandle);
686 DestroyRingBuffer(data->ring);
687 free(data->buffer);
688 free(data);
689 return ALC_FALSE;
692 else
694 i = psnd_pcm_prepare(data->pcmHandle);
695 if(i < 0)
697 AL_PRINT("prepare error: %s\n", psnd_strerror(i));
698 psnd_pcm_close(data->pcmHandle);
699 free(data);
700 return ALC_FALSE;
702 pDevice->ExtraData = data;
705 return ALC_TRUE;
708 static void alsa_close_capture(ALCdevice *pDevice)
710 alsa_data *data = (alsa_data*)pDevice->ExtraData;
712 if(data->thread)
714 data->killNow = 1;
715 StopThread(data->thread);
716 DestroyRingBuffer(data->ring);
718 psnd_pcm_close(data->pcmHandle);
720 free(data);
721 pDevice->ExtraData = NULL;
724 static void alsa_start_capture(ALCdevice *pDevice)
726 alsa_data *data = (alsa_data*)pDevice->ExtraData;
727 if(data->thread)
728 data->doCapture = 1;
729 else
731 psnd_pcm_prepare(data->pcmHandle);
732 psnd_pcm_start(data->pcmHandle);
736 static void alsa_stop_capture(ALCdevice *pDevice)
738 alsa_data *data = (alsa_data*)pDevice->ExtraData;
739 if(data->thread)
740 data->doCapture = 0;
741 else
742 psnd_pcm_drain(data->pcmHandle);
745 static void alsa_capture_samples(ALCdevice *pDevice, ALCvoid *pBuffer, ALCuint lSamples)
747 alsa_data *data = (alsa_data*)pDevice->ExtraData;
748 const snd_pcm_channel_area_t *areas = NULL;
749 snd_pcm_sframes_t frames, commitres;
750 snd_pcm_uframes_t size, offset;
751 int err;
753 if(data->thread)
755 if(lSamples <= (ALCuint)RingBufferSize(data->ring))
756 ReadRingBuffer(data->ring, pBuffer, lSamples);
757 else
758 SetALCError(ALC_INVALID_VALUE);
759 return;
762 frames = psnd_pcm_avail_update(data->pcmHandle);
763 if(frames < 0)
765 err = xrun_recovery(data->pcmHandle, frames);
766 if (err < 0)
767 AL_PRINT("available update failed: %s\n", psnd_strerror(err));
768 else
769 frames = psnd_pcm_avail_update(data->pcmHandle);
771 if (frames < (snd_pcm_sframes_t)lSamples)
773 SetALCError(ALC_INVALID_VALUE);
774 return;
777 // it is possible that contiguous areas are smaller, thus we use a loop
778 while (lSamples > 0)
780 char *Pointer;
781 int Count;
783 size = lSamples;
784 err = psnd_pcm_mmap_begin(data->pcmHandle, &areas, &offset, &size);
785 if (err < 0)
787 err = xrun_recovery(data->pcmHandle, err);
788 if (err < 0)
790 AL_PRINT("mmap begin error: %s\n", psnd_strerror(err));
791 break;
793 continue;
796 Pointer = (char*)areas->addr + (offset * areas->step / 8);
797 Count = psnd_pcm_frames_to_bytes(data->pcmHandle, size);
799 memcpy(pBuffer, Pointer, Count);
800 pBuffer = (char*)pBuffer + Count;
802 commitres = psnd_pcm_mmap_commit(data->pcmHandle, offset, size);
803 if (commitres < 0 || (commitres-size) != 0)
805 AL_PRINT("mmap commit error: %s\n",
806 psnd_strerror(commitres >= 0 ? -EPIPE : commitres));
807 break;
810 lSamples -= size;
814 static ALCuint alsa_available_samples(ALCdevice *pDevice)
816 alsa_data *data = (alsa_data*)pDevice->ExtraData;
817 snd_pcm_sframes_t frames;
819 if(data->thread)
820 return RingBufferSize(data->ring);
822 frames = psnd_pcm_avail_update(data->pcmHandle);
823 if(frames < 0)
825 int err = xrun_recovery(data->pcmHandle, frames);
826 if (err < 0)
827 AL_PRINT("available update failed: %s\n", psnd_strerror(err));
828 else
829 frames = psnd_pcm_avail_update(data->pcmHandle);
830 if(frames < 0) /* ew.. */
831 SetALCError(ALC_INVALID_DEVICE);
833 return max(frames, 0);
837 BackendFuncs alsa_funcs = {
838 alsa_open_playback,
839 alsa_close_playback,
840 alsa_open_capture,
841 alsa_close_capture,
842 alsa_start_capture,
843 alsa_stop_capture,
844 alsa_capture_samples,
845 alsa_available_samples
848 void alc_alsa_init(BackendFuncs *func_list)
850 snd_ctl_t *handle;
851 int card, err, dev, idx = 1;
852 snd_ctl_card_info_t *info;
853 snd_pcm_info_t *pcminfo;
854 snd_pcm_stream_t stream = SND_PCM_STREAM_PLAYBACK;
855 char name[128];
856 char *str;
858 *func_list = alsa_funcs;
860 #ifdef HAVE_DLFCN_H
861 alsa_handle = dlopen("libasound.so.2", RTLD_NOW);
862 if(!alsa_handle)
863 return;
864 dlerror();
866 #define LOAD_FUNC(f) do { \
867 p##f = (typeof(f)*)dlsym(alsa_handle, #f); \
868 if((str=dlerror()) != NULL) \
870 dlclose(alsa_handle); \
871 alsa_handle = NULL; \
872 AL_PRINT("Could not load %s from libasound.so.2: %s\n", #f, str); \
873 return; \
875 } while(0)
876 #else
877 str = NULL;
878 alsa_handle = 0xDEADBEEF;
879 #define LOAD_FUNC(f) p##f = f
880 #endif
882 LOAD_FUNC(snd_strerror);
883 LOAD_FUNC(snd_pcm_open);
884 LOAD_FUNC(snd_pcm_close);
885 LOAD_FUNC(snd_pcm_nonblock);
886 LOAD_FUNC(snd_pcm_frames_to_bytes);
887 LOAD_FUNC(snd_pcm_hw_params_malloc);
888 LOAD_FUNC(snd_pcm_hw_params_free);
889 LOAD_FUNC(snd_pcm_hw_params_any);
890 LOAD_FUNC(snd_pcm_hw_params_set_access);
891 LOAD_FUNC(snd_pcm_hw_params_set_format);
892 LOAD_FUNC(snd_pcm_hw_params_set_channels);
893 LOAD_FUNC(snd_pcm_hw_params_set_periods_near);
894 LOAD_FUNC(snd_pcm_hw_params_set_rate_near);
895 LOAD_FUNC(snd_pcm_hw_params_set_rate);
896 LOAD_FUNC(snd_pcm_hw_params_set_buffer_size_near);
897 LOAD_FUNC(snd_pcm_hw_params_set_buffer_size_min);
898 LOAD_FUNC(snd_pcm_hw_params_get_buffer_size);
899 LOAD_FUNC(snd_pcm_hw_params_get_period_size);
900 LOAD_FUNC(snd_pcm_hw_params_get_access);
901 LOAD_FUNC(snd_pcm_hw_params);
902 LOAD_FUNC(snd_pcm_prepare);
903 LOAD_FUNC(snd_pcm_start);
904 LOAD_FUNC(snd_pcm_resume);
905 LOAD_FUNC(snd_pcm_wait);
906 LOAD_FUNC(snd_pcm_state);
907 LOAD_FUNC(snd_pcm_avail_update);
908 LOAD_FUNC(snd_pcm_areas_silence);
909 LOAD_FUNC(snd_pcm_mmap_begin);
910 LOAD_FUNC(snd_pcm_mmap_commit);
911 LOAD_FUNC(snd_pcm_readi);
912 LOAD_FUNC(snd_pcm_writei);
913 LOAD_FUNC(snd_pcm_drain);
915 LOAD_FUNC(snd_pcm_info_malloc);
916 LOAD_FUNC(snd_pcm_info_free);
917 LOAD_FUNC(snd_pcm_info_set_device);
918 LOAD_FUNC(snd_pcm_info_set_subdevice);
919 LOAD_FUNC(snd_pcm_info_set_stream);
920 LOAD_FUNC(snd_pcm_info_get_name);
921 LOAD_FUNC(snd_ctl_pcm_next_device);
922 LOAD_FUNC(snd_ctl_pcm_info);
923 LOAD_FUNC(snd_ctl_open);
924 LOAD_FUNC(snd_ctl_close);
925 LOAD_FUNC(snd_ctl_card_info_malloc);
926 LOAD_FUNC(snd_ctl_card_info_free);
927 LOAD_FUNC(snd_ctl_card_info);
928 LOAD_FUNC(snd_ctl_card_info_get_name);
929 LOAD_FUNC(snd_card_next);
931 #undef LOAD_FUNC
933 psnd_ctl_card_info_malloc(&info);
934 psnd_pcm_info_malloc(&pcminfo);
936 card = -1;
937 if(psnd_card_next(&card) < 0 || card < 0)
938 AL_PRINT("no playback cards found...\n");
939 else
941 alsaDeviceList[0] = AppendDeviceList("ALSA Software on default");
942 allDevNameMap[0].name = AppendAllDeviceList("ALSA Software on default");
945 while (card >= 0) {
946 int firstDev = 1;
948 sprintf(name, "hw:%d", card);
949 if ((err = psnd_ctl_open(&handle, name, 0)) < 0) {
950 AL_PRINT("control open (%i): %s\n", card, psnd_strerror(err));
951 goto next_card;
953 if ((err = psnd_ctl_card_info(handle, info)) < 0) {
954 AL_PRINT("control hardware info (%i): %s\n", card, psnd_strerror(err));
955 psnd_ctl_close(handle);
956 goto next_card;
959 dev = -1;
960 while (idx < MAX_ALL_DEVICES) {
961 const char *cname, *dname;
963 if (psnd_ctl_pcm_next_device(handle, &dev)<0)
964 AL_PRINT("snd_ctl_pcm_next_device failed\n");
965 if (dev < 0)
966 break;
968 if(firstDev && card < MAX_DEVICES-1) {
969 firstDev = 0;
970 snprintf(name, sizeof(name), "ALSA Software on %s",
971 psnd_ctl_card_info_get_name(info));
972 alsaDeviceList[card+1] = AppendDeviceList(name);
975 psnd_pcm_info_set_device(pcminfo, dev);
976 psnd_pcm_info_set_subdevice(pcminfo, 0);
977 psnd_pcm_info_set_stream(pcminfo, stream);
978 if ((err = psnd_ctl_pcm_info(handle, pcminfo)) < 0) {
979 if (err != -ENOENT)
980 AL_PRINT("control digital audio info (%i): %s\n", card, psnd_strerror(err));
981 continue;
984 cname = psnd_ctl_card_info_get_name(info);
985 dname = psnd_pcm_info_get_name(pcminfo);
986 snprintf(name, sizeof(name), "ALSA Software on %s [%s]",
987 cname, dname);
988 allDevNameMap[idx].name = AppendAllDeviceList(name);
989 allDevNameMap[idx].card = card;
990 allDevNameMap[idx].dev = dev;
991 idx++;
993 psnd_ctl_close(handle);
994 next_card:
995 if(psnd_card_next(&card) < 0) {
996 AL_PRINT("snd_card_next failed\n");
997 break;
1002 stream = SND_PCM_STREAM_CAPTURE;
1004 card = -1;
1005 if(psnd_card_next(&card) < 0 || card < 0) {
1006 AL_PRINT("no capture cards found...\n");
1007 psnd_pcm_info_free(pcminfo);
1008 psnd_ctl_card_info_free(info);
1009 return;
1012 allCaptureDevNameMap[0].name = AppendCaptureDeviceList("ALSA Capture on default");
1013 idx = 1;
1015 while (card >= 0) {
1016 sprintf(name, "hw:%d", card);
1017 handle = NULL;
1018 if ((err = psnd_ctl_open(&handle, name, 0)) < 0) {
1019 AL_PRINT("control open (%i): %s\n", card, psnd_strerror(err));
1021 if (err >= 0 && (err = psnd_ctl_card_info(handle, info)) < 0) {
1022 AL_PRINT("control hardware info (%i): %s\n", card, psnd_strerror(err));
1024 else if (err >= 0)
1026 dev = -1;
1027 while (idx < MAX_ALL_DEVICES) {
1028 const char *cname, *dname;
1030 if (psnd_ctl_pcm_next_device(handle, &dev)<0)
1031 AL_PRINT("snd_ctl_pcm_next_device failed\n");
1032 if (dev < 0)
1033 break;
1034 psnd_pcm_info_set_device(pcminfo, dev);
1035 psnd_pcm_info_set_subdevice(pcminfo, 0);
1036 psnd_pcm_info_set_stream(pcminfo, stream);
1037 if ((err = psnd_ctl_pcm_info(handle, pcminfo)) < 0) {
1038 if (err != -ENOENT)
1039 AL_PRINT("control digital audio info (%i): %s\n", card, psnd_strerror(err));
1040 continue;
1043 cname = psnd_ctl_card_info_get_name(info);
1044 dname = psnd_pcm_info_get_name(pcminfo);
1045 snprintf(name, sizeof(name), "ALSA Capture on %s [%s]",
1046 cname, dname);
1047 allCaptureDevNameMap[idx].name = AppendCaptureDeviceList(name);
1048 allCaptureDevNameMap[idx].card = card;
1049 allCaptureDevNameMap[idx].dev = dev;
1050 idx++;
1053 if(handle) psnd_ctl_close(handle);
1054 if(psnd_card_next(&card) < 0) {
1055 AL_PRINT("snd_card_next failed\n");
1056 break;
1059 psnd_pcm_info_free(pcminfo);
1060 psnd_ctl_card_info_free(info);