Use snd_pcm_recover to recover from certain device errors
[openal-soft.git] / Alc / alsa.c
blobb9bb6259ce764d554ee3d4070173ebe44ec4b3eb
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;
39 ALvoid *buffer;
40 ALsizei size;
42 RingBuffer *ring;
43 int doCapture;
45 volatile int killNow;
46 ALvoid *thread;
47 } alsa_data;
49 typedef struct {
50 ALCchar *name;
51 int card, dev;
52 } DevMap;
54 static void *alsa_handle;
55 #define MAKE_FUNC(f) static typeof(f) * p##f
56 MAKE_FUNC(snd_strerror);
57 MAKE_FUNC(snd_pcm_open);
58 MAKE_FUNC(snd_pcm_close);
59 MAKE_FUNC(snd_pcm_nonblock);
60 MAKE_FUNC(snd_pcm_frames_to_bytes);
61 MAKE_FUNC(snd_pcm_hw_params_malloc);
62 MAKE_FUNC(snd_pcm_hw_params_free);
63 MAKE_FUNC(snd_pcm_hw_params_any);
64 MAKE_FUNC(snd_pcm_hw_params_set_access);
65 MAKE_FUNC(snd_pcm_hw_params_set_format);
66 MAKE_FUNC(snd_pcm_hw_params_set_channels);
67 MAKE_FUNC(snd_pcm_hw_params_set_periods_near);
68 MAKE_FUNC(snd_pcm_hw_params_set_rate_near);
69 MAKE_FUNC(snd_pcm_hw_params_set_rate);
70 MAKE_FUNC(snd_pcm_hw_params_set_buffer_size_near);
71 MAKE_FUNC(snd_pcm_hw_params_set_period_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_get_periods);
77 MAKE_FUNC(snd_pcm_hw_params);
78 MAKE_FUNC(snd_pcm_sw_params_malloc);
79 MAKE_FUNC(snd_pcm_sw_params_current);
80 MAKE_FUNC(snd_pcm_sw_params_set_avail_min);
81 MAKE_FUNC(snd_pcm_sw_params);
82 MAKE_FUNC(snd_pcm_sw_params_free);
83 MAKE_FUNC(snd_pcm_prepare);
84 MAKE_FUNC(snd_pcm_start);
85 MAKE_FUNC(snd_pcm_resume);
86 MAKE_FUNC(snd_pcm_wait);
87 MAKE_FUNC(snd_pcm_state);
88 MAKE_FUNC(snd_pcm_avail_update);
89 MAKE_FUNC(snd_pcm_areas_silence);
90 MAKE_FUNC(snd_pcm_mmap_begin);
91 MAKE_FUNC(snd_pcm_mmap_commit);
92 MAKE_FUNC(snd_pcm_readi);
93 MAKE_FUNC(snd_pcm_writei);
94 MAKE_FUNC(snd_pcm_drain);
95 MAKE_FUNC(snd_pcm_recover);
96 MAKE_FUNC(snd_pcm_info_malloc);
97 MAKE_FUNC(snd_pcm_info_free);
98 MAKE_FUNC(snd_pcm_info_set_device);
99 MAKE_FUNC(snd_pcm_info_set_subdevice);
100 MAKE_FUNC(snd_pcm_info_set_stream);
101 MAKE_FUNC(snd_pcm_info_get_name);
102 MAKE_FUNC(snd_ctl_pcm_next_device);
103 MAKE_FUNC(snd_ctl_pcm_info);
104 MAKE_FUNC(snd_ctl_open);
105 MAKE_FUNC(snd_ctl_close);
106 MAKE_FUNC(snd_ctl_card_info_malloc);
107 MAKE_FUNC(snd_ctl_card_info_free);
108 MAKE_FUNC(snd_ctl_card_info);
109 MAKE_FUNC(snd_ctl_card_info_get_name);
110 MAKE_FUNC(snd_card_next);
111 #undef MAKE_FUNC
114 static const ALCchar alsaDevice[] = "ALSA Software";
115 static DevMap *allDevNameMap;
116 static ALuint numDevNames;
117 static DevMap *allCaptureDevNameMap;
118 static ALuint numCaptureDevNames;
119 static volatile ALuint load_count;
122 void *alsa_load(void)
124 if(load_count == 0)
126 char *str;
128 #ifdef HAVE_DLFCN_H
129 alsa_handle = dlopen("libasound.so.2", RTLD_NOW);
130 if(!alsa_handle)
131 return NULL;
132 dlerror();
134 #define LOAD_FUNC(f) do { \
135 p##f = dlsym(alsa_handle, #f); \
136 if((str=dlerror()) != NULL) \
138 dlclose(alsa_handle); \
139 alsa_handle = NULL; \
140 AL_PRINT("Could not load %s from libasound.so.2: %s\n", #f, str); \
141 return NULL; \
143 } while(0)
144 #else
145 str = NULL;
146 alsa_handle = (void*)0xDEADBEEF;
147 #define LOAD_FUNC(f) p##f = f
148 #endif
150 LOAD_FUNC(snd_strerror);
151 LOAD_FUNC(snd_pcm_open);
152 LOAD_FUNC(snd_pcm_close);
153 LOAD_FUNC(snd_pcm_nonblock);
154 LOAD_FUNC(snd_pcm_frames_to_bytes);
155 LOAD_FUNC(snd_pcm_hw_params_malloc);
156 LOAD_FUNC(snd_pcm_hw_params_free);
157 LOAD_FUNC(snd_pcm_hw_params_any);
158 LOAD_FUNC(snd_pcm_hw_params_set_access);
159 LOAD_FUNC(snd_pcm_hw_params_set_format);
160 LOAD_FUNC(snd_pcm_hw_params_set_channels);
161 LOAD_FUNC(snd_pcm_hw_params_set_periods_near);
162 LOAD_FUNC(snd_pcm_hw_params_set_rate_near);
163 LOAD_FUNC(snd_pcm_hw_params_set_rate);
164 LOAD_FUNC(snd_pcm_hw_params_set_buffer_size_near);
165 LOAD_FUNC(snd_pcm_hw_params_set_buffer_size_min);
166 LOAD_FUNC(snd_pcm_hw_params_set_period_size_near);
167 LOAD_FUNC(snd_pcm_hw_params_get_buffer_size);
168 LOAD_FUNC(snd_pcm_hw_params_get_period_size);
169 LOAD_FUNC(snd_pcm_hw_params_get_access);
170 LOAD_FUNC(snd_pcm_hw_params_get_periods);
171 LOAD_FUNC(snd_pcm_hw_params);
172 LOAD_FUNC(snd_pcm_sw_params_malloc);
173 LOAD_FUNC(snd_pcm_sw_params_current);
174 LOAD_FUNC(snd_pcm_sw_params_set_avail_min);
175 LOAD_FUNC(snd_pcm_sw_params);
176 LOAD_FUNC(snd_pcm_sw_params_free);
177 LOAD_FUNC(snd_pcm_prepare);
178 LOAD_FUNC(snd_pcm_start);
179 LOAD_FUNC(snd_pcm_resume);
180 LOAD_FUNC(snd_pcm_wait);
181 LOAD_FUNC(snd_pcm_state);
182 LOAD_FUNC(snd_pcm_avail_update);
183 LOAD_FUNC(snd_pcm_areas_silence);
184 LOAD_FUNC(snd_pcm_mmap_begin);
185 LOAD_FUNC(snd_pcm_mmap_commit);
186 LOAD_FUNC(snd_pcm_readi);
187 LOAD_FUNC(snd_pcm_writei);
188 LOAD_FUNC(snd_pcm_drain);
189 LOAD_FUNC(snd_pcm_recover);
191 LOAD_FUNC(snd_pcm_info_malloc);
192 LOAD_FUNC(snd_pcm_info_free);
193 LOAD_FUNC(snd_pcm_info_set_device);
194 LOAD_FUNC(snd_pcm_info_set_subdevice);
195 LOAD_FUNC(snd_pcm_info_set_stream);
196 LOAD_FUNC(snd_pcm_info_get_name);
197 LOAD_FUNC(snd_ctl_pcm_next_device);
198 LOAD_FUNC(snd_ctl_pcm_info);
199 LOAD_FUNC(snd_ctl_open);
200 LOAD_FUNC(snd_ctl_close);
201 LOAD_FUNC(snd_ctl_card_info_malloc);
202 LOAD_FUNC(snd_ctl_card_info_free);
203 LOAD_FUNC(snd_ctl_card_info);
204 LOAD_FUNC(snd_ctl_card_info_get_name);
205 LOAD_FUNC(snd_card_next);
207 #undef LOAD_FUNC
209 ++load_count;
211 return alsa_handle;
214 void alsa_unload(void)
216 if(load_count == 0 || --load_count > 0)
217 return;
219 #ifdef HAVE_DLFCN_H
220 dlclose(alsa_handle);
221 #endif
222 alsa_handle = NULL;
226 static int xrun_recovery(snd_pcm_t *handle, int err)
228 if(err == -EINTR || err == -EPIPE || err == -ESTRPIPE)
230 err = psnd_pcm_recover(handle, err, 1);
231 if(err < 0)
232 AL_PRINT("recover failed: %s\n", psnd_strerror(err));
234 return err;
237 static int verify_state(snd_pcm_t *handle)
239 snd_pcm_state_t state = psnd_pcm_state(handle);
240 if(state == SND_PCM_STATE_DISCONNECTED)
241 return -ENODEV;
242 if(state == SND_PCM_STATE_XRUN)
244 int err = xrun_recovery(handle, -EPIPE);
245 if(err < 0) return err;
247 else if(state == SND_PCM_STATE_SUSPENDED)
249 int err = xrun_recovery(handle, -ESTRPIPE);
250 if(err < 0) return err;
253 return state;
257 static ALuint ALSAProc(ALvoid *ptr)
259 ALCdevice *pDevice = (ALCdevice*)ptr;
260 alsa_data *data = (alsa_data*)pDevice->ExtraData;
261 const snd_pcm_channel_area_t *areas = NULL;
262 snd_pcm_sframes_t avail, commitres;
263 snd_pcm_uframes_t offset, frames;
264 char *WritePtr;
265 int err;
267 EnableRTPrio(RTPrioLevel);
269 while(!data->killNow)
271 int state = verify_state(data->pcmHandle);
272 if(state < 0)
274 AL_PRINT("Invalid state detected: %s\n", psnd_strerror(state));
275 aluHandleDisconnect(pDevice);
276 break;
279 avail = psnd_pcm_avail_update(data->pcmHandle);
280 if(avail < 0)
282 AL_PRINT("available update failed: %s\n", psnd_strerror(avail));
283 continue;
286 // make sure there's frames to process
287 if((snd_pcm_uframes_t)avail < pDevice->UpdateSize)
289 if(state != SND_PCM_STATE_RUNNING)
291 err = psnd_pcm_start(data->pcmHandle);
292 if(err < 0)
294 AL_PRINT("start failed: %s\n", psnd_strerror(err));
295 continue;
298 if(psnd_pcm_wait(data->pcmHandle, 1000) == 0)
299 AL_PRINT("Wait timeout... buffer size too low?\n");
300 continue;
302 avail -= avail%pDevice->UpdateSize;
304 // it is possible that contiguous areas are smaller, thus we use a loop
305 while(avail > 0)
307 frames = avail;
309 err = psnd_pcm_mmap_begin(data->pcmHandle, &areas, &offset, &frames);
310 if(err < 0)
312 AL_PRINT("mmap begin error: %s\n", psnd_strerror(err));
313 break;
316 WritePtr = (char*)areas->addr + (offset * areas->step / 8);
317 aluMixData(pDevice, WritePtr, frames);
319 commitres = psnd_pcm_mmap_commit(data->pcmHandle, offset, frames);
320 if(commitres < 0 || (commitres-frames) != 0)
322 AL_PRINT("mmap commit error: %s\n",
323 psnd_strerror(commitres >= 0 ? -EPIPE : commitres));
324 break;
327 avail -= frames;
331 return 0;
334 static ALuint ALSANoMMapProc(ALvoid *ptr)
336 ALCdevice *pDevice = (ALCdevice*)ptr;
337 alsa_data *data = (alsa_data*)pDevice->ExtraData;
338 snd_pcm_sframes_t avail;
339 char *WritePtr;
341 EnableRTPrio(RTPrioLevel);
343 while(!data->killNow)
345 int state = verify_state(data->pcmHandle);
346 if(state < 0)
348 AL_PRINT("Invalid state detected: %s\n", psnd_strerror(state));
349 aluHandleDisconnect(pDevice);
350 break;
353 WritePtr = data->buffer;
354 avail = data->size / psnd_pcm_frames_to_bytes(data->pcmHandle, 1);
355 aluMixData(pDevice, WritePtr, avail);
357 while(avail > 0)
359 int ret = psnd_pcm_writei(data->pcmHandle, WritePtr, avail);
360 switch (ret)
362 case -EAGAIN:
363 continue;
364 case -ESTRPIPE:
365 while((ret=psnd_pcm_resume(data->pcmHandle)) == -EAGAIN)
366 Sleep(1);
367 break;
368 case -EPIPE:
369 break;
370 default:
371 if (ret >= 0)
373 WritePtr += psnd_pcm_frames_to_bytes(data->pcmHandle, ret);
374 avail -= ret;
376 break;
378 if (ret < 0)
380 ret = psnd_pcm_prepare(data->pcmHandle);
381 if(ret < 0)
382 break;
387 return 0;
390 static ALuint ALSANoMMapCaptureProc(ALvoid *ptr)
392 ALCdevice *pDevice = (ALCdevice*)ptr;
393 alsa_data *data = (alsa_data*)pDevice->ExtraData;
394 snd_pcm_sframes_t avail;
396 EnableRTPrio(RTPrioLevel);
398 while(!data->killNow)
400 int state = verify_state(data->pcmHandle);
401 if(state < 0)
403 AL_PRINT("Invalid state detected: %s\n", psnd_strerror(state));
404 aluHandleDisconnect(pDevice);
405 break;
408 avail = (snd_pcm_uframes_t)data->size / psnd_pcm_frames_to_bytes(data->pcmHandle, 1);
409 avail = psnd_pcm_readi(data->pcmHandle, data->buffer, avail);
410 switch(avail)
412 case -EAGAIN:
413 continue;
414 case -ESTRPIPE:
415 while((avail=psnd_pcm_resume(data->pcmHandle)) == -EAGAIN)
416 Sleep(1);
417 break;
418 case -EPIPE:
419 break;
420 default:
421 if (avail >= 0 && data->doCapture)
422 WriteRingBuffer(data->ring, data->buffer, avail);
423 break;
425 if(avail < 0)
427 avail = psnd_pcm_prepare(data->pcmHandle);
428 if(avail < 0)
429 AL_PRINT("prepare error: %s\n", psnd_strerror(avail));
433 return 0;
436 static ALCboolean alsa_open_playback(ALCdevice *device, const ALCchar *deviceName)
438 alsa_data *data;
439 char driver[64];
440 int i;
442 strncpy(driver, GetConfigValue("alsa", "device", "default"), sizeof(driver)-1);
443 driver[sizeof(driver)-1] = 0;
444 if(!deviceName)
445 deviceName = alsaDevice;
446 else if(strcmp(deviceName, alsaDevice) != 0)
448 size_t idx;
450 for(idx = 0;idx < numDevNames;idx++)
452 if(allDevNameMap[idx].name &&
453 strcmp(deviceName, allDevNameMap[idx].name) == 0)
455 if(idx > 0)
456 sprintf(driver, "hw:%d,%d", allDevNameMap[idx].card, allDevNameMap[idx].dev);
457 break;
460 if(idx == numDevNames)
461 return ALC_FALSE;
464 if(!alsa_load())
465 return ALC_FALSE;
467 data = (alsa_data*)calloc(1, sizeof(alsa_data));
469 i = psnd_pcm_open(&data->pcmHandle, driver, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
470 if(i < 0)
472 Sleep(200);
473 i = psnd_pcm_open(&data->pcmHandle, driver, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
475 if(i >= 0)
477 i = psnd_pcm_nonblock(data->pcmHandle, 0);
478 if(i < 0)
479 psnd_pcm_close(data->pcmHandle);
481 if(i < 0)
483 free(data);
484 AL_PRINT("Could not open playback device '%s': %s\n", driver, psnd_strerror(i));
485 alsa_unload();
486 return ALC_FALSE;
489 device->szDeviceName = strdup(deviceName);
490 device->ExtraData = data;
491 return ALC_TRUE;
494 static void alsa_close_playback(ALCdevice *device)
496 alsa_data *data = (alsa_data*)device->ExtraData;
498 psnd_pcm_close(data->pcmHandle);
499 free(data);
500 device->ExtraData = NULL;
502 alsa_unload();
505 static ALCboolean alsa_reset_playback(ALCdevice *device)
507 alsa_data *data = (alsa_data*)device->ExtraData;
508 snd_pcm_uframes_t periodSizeInFrames;
509 snd_pcm_sw_params_t *sp = NULL;
510 snd_pcm_hw_params_t *p = NULL;
511 snd_pcm_access_t access;
512 snd_pcm_format_t format;
513 unsigned int periods;
514 unsigned int rate;
515 int allowmmap;
516 char *err;
517 int i;
520 switch(aluBytesFromFormat(device->Format))
522 case 1:
523 format = SND_PCM_FORMAT_U8;
524 break;
525 case 2:
526 format = SND_PCM_FORMAT_S16;
527 break;
528 case 4:
529 format = SND_PCM_FORMAT_FLOAT;
530 break;
531 default:
532 AL_PRINT("Unknown format: 0x%x\n", device->Format);
533 return ALC_FALSE;
536 allowmmap = GetConfigValueBool("alsa", "mmap", 1);
537 periods = device->NumUpdates;
538 periodSizeInFrames = device->UpdateSize;
539 rate = device->Frequency;
541 err = NULL;
542 psnd_pcm_hw_params_malloc(&p);
544 if((i=psnd_pcm_hw_params_any(data->pcmHandle, p)) < 0)
545 err = "any";
546 /* set interleaved access */
547 if(i >= 0 && (!allowmmap || (i=psnd_pcm_hw_params_set_access(data->pcmHandle, p, SND_PCM_ACCESS_MMAP_INTERLEAVED)) < 0))
549 if(periods > 2) periods--;
550 if((i=psnd_pcm_hw_params_set_access(data->pcmHandle, p, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
551 err = "set access";
553 /* set format (implicitly sets sample bits) */
554 if(i >= 0 && (i=psnd_pcm_hw_params_set_format(data->pcmHandle, p, format)) < 0)
556 switch(aluChannelsFromFormat(device->Format))
558 case 1: device->Format = AL_FORMAT_MONO_FLOAT32; break;
559 case 2: device->Format = AL_FORMAT_STEREO_FLOAT32; break;
560 case 4: device->Format = AL_FORMAT_QUAD32; break;
561 case 6: device->Format = AL_FORMAT_51CHN32; break;
562 case 7: device->Format = AL_FORMAT_61CHN32; break;
563 case 8: device->Format = AL_FORMAT_71CHN32; break;
565 if((i=psnd_pcm_hw_params_set_format(data->pcmHandle, p, SND_PCM_FORMAT_FLOAT)) < 0)
567 switch(aluChannelsFromFormat(device->Format))
569 case 1: device->Format = AL_FORMAT_MONO16; break;
570 case 2: device->Format = AL_FORMAT_STEREO16; break;
571 case 4: device->Format = AL_FORMAT_QUAD16; break;
572 case 6: device->Format = AL_FORMAT_51CHN16; break;
573 case 7: device->Format = AL_FORMAT_61CHN16; break;
574 case 8: device->Format = AL_FORMAT_71CHN16; break;
576 if((i=psnd_pcm_hw_params_set_format(data->pcmHandle, p, SND_PCM_FORMAT_S16)) < 0)
578 switch(aluChannelsFromFormat(device->Format))
580 case 1: device->Format = AL_FORMAT_MONO8; break;
581 case 2: device->Format = AL_FORMAT_STEREO8; break;
582 case 4: device->Format = AL_FORMAT_QUAD8; break;
583 case 6: device->Format = AL_FORMAT_51CHN8; break;
584 case 7: device->Format = AL_FORMAT_61CHN8; break;
585 case 8: device->Format = AL_FORMAT_71CHN8; break;
587 if((i=psnd_pcm_hw_params_set_format(data->pcmHandle, p, SND_PCM_FORMAT_U8)) < 0)
588 err = "set format";
592 /* set channels (implicitly sets frame bits) */
593 if(i >= 0 && (i=psnd_pcm_hw_params_set_channels(data->pcmHandle, p, aluChannelsFromFormat(device->Format))) < 0)
595 switch(aluBytesFromFormat(device->Format))
597 case 1: device->Format = AL_FORMAT_STEREO8; break;
598 case 2: device->Format = AL_FORMAT_STEREO16; break;
599 case 4: device->Format = AL_FORMAT_STEREO_FLOAT32; break;
601 if((i=psnd_pcm_hw_params_set_channels(data->pcmHandle, p, 2)) < 0)
603 switch(aluBytesFromFormat(device->Format))
605 case 1: device->Format = AL_FORMAT_MONO8; break;
606 case 2: device->Format = AL_FORMAT_MONO16; break;
607 case 4: device->Format = AL_FORMAT_MONO_FLOAT32; break;
609 if((i=psnd_pcm_hw_params_set_channels(data->pcmHandle, p, 1)) < 0)
610 err = "set channels";
613 /* set periods (implicitly constrains period/buffer parameters) */
614 if(i >= 0 && (i=psnd_pcm_hw_params_set_periods_near(data->pcmHandle, p, &periods, NULL)) < 0)
615 err = "set periods near";
616 /* set rate (implicitly constrains period/buffer parameters) */
617 if(i >= 0 && (i=psnd_pcm_hw_params_set_rate_near(data->pcmHandle, p, &rate, NULL)) < 0)
618 err = "set rate near";
619 /* set period size in frame units (implicitly sets buffer size/bytes/time and period time/bytes) */
620 if(i >= 0 && (i=psnd_pcm_hw_params_set_period_size_near(data->pcmHandle, p, &periodSizeInFrames, NULL)) < 0)
621 err = "set period size near";
622 /* install and prepare hardware configuration */
623 if(i >= 0 && (i=psnd_pcm_hw_params(data->pcmHandle, p)) < 0)
624 err = "set params";
625 if(i >= 0 && (i=psnd_pcm_hw_params_get_access(p, &access)) < 0)
626 err = "get access";
627 if(i >= 0 && (i=psnd_pcm_hw_params_get_period_size(p, &periodSizeInFrames, NULL)) < 0)
628 err = "get period size";
629 if(i >= 0 && (i=psnd_pcm_hw_params_get_periods(p, &periods, NULL)) < 0)
630 err = "get periods";
631 if(i < 0)
633 AL_PRINT("%s failed: %s\n", err, psnd_strerror(i));
634 psnd_pcm_hw_params_free(p);
635 return ALC_FALSE;
638 psnd_pcm_hw_params_free(p);
640 err = NULL;
641 psnd_pcm_sw_params_malloc(&sp);
643 if((i=psnd_pcm_sw_params_current(data->pcmHandle, sp)) != 0)
644 err = "sw current";
645 if(i == 0 && (i=psnd_pcm_sw_params_set_avail_min(data->pcmHandle, sp, periodSizeInFrames)) != 0)
646 err = "sw set avail min";
647 if(i == 0 && (i=psnd_pcm_sw_params(data->pcmHandle, sp)) != 0)
648 err = "sw set params";
649 if(i != 0)
651 AL_PRINT("%s failed: %s\n", err, psnd_strerror(i));
652 psnd_pcm_sw_params_free(sp);
653 return ALC_FALSE;
656 psnd_pcm_sw_params_free(sp);
658 SetDefaultChannelOrder(device);
660 data->size = psnd_pcm_frames_to_bytes(data->pcmHandle, periodSizeInFrames);
661 if(access == SND_PCM_ACCESS_RW_INTERLEAVED)
663 /* Increase periods by one, since the temp buffer counts as an extra
664 * period */
665 periods++;
666 data->buffer = malloc(data->size);
667 if(!data->buffer)
669 AL_PRINT("buffer malloc failed\n");
670 return ALC_FALSE;
672 device->UpdateSize = periodSizeInFrames;
673 device->NumUpdates = periods;
674 device->Frequency = rate;
675 data->thread = StartThread(ALSANoMMapProc, device);
677 else
679 i = psnd_pcm_prepare(data->pcmHandle);
680 if(i < 0)
682 AL_PRINT("prepare error: %s\n", psnd_strerror(i));
683 return ALC_FALSE;
685 device->UpdateSize = periodSizeInFrames;
686 device->NumUpdates = periods;
687 device->Frequency = rate;
688 data->thread = StartThread(ALSAProc, device);
690 if(data->thread == NULL)
692 AL_PRINT("Could not create playback thread\n");
693 free(data->buffer);
694 data->buffer = NULL;
695 return ALC_FALSE;
698 return ALC_TRUE;
701 static void alsa_stop_playback(ALCdevice *device)
703 alsa_data *data = (alsa_data*)device->ExtraData;
705 if(data->thread)
707 data->killNow = 1;
708 StopThread(data->thread);
709 data->thread = NULL;
711 data->killNow = 0;
712 free(data->buffer);
713 data->buffer = NULL;
717 static ALCboolean alsa_open_capture(ALCdevice *pDevice, const ALCchar *deviceName)
719 const char *devName;
720 snd_pcm_hw_params_t *p;
721 snd_pcm_uframes_t bufferSizeInFrames;
722 snd_pcm_format_t format;
723 ALuint frameSize;
724 alsa_data *data;
725 char driver[64];
726 char *err;
727 int i;
729 strncpy(driver, GetConfigValue("alsa", "capture", "default"), sizeof(driver)-1);
730 driver[sizeof(driver)-1] = 0;
731 if(!deviceName)
732 deviceName = allCaptureDevNameMap[0].name;
733 else
735 size_t idx;
737 for(idx = 0;idx < numCaptureDevNames;idx++)
739 if(allCaptureDevNameMap[idx].name &&
740 strcmp(deviceName, allCaptureDevNameMap[idx].name) == 0)
742 devName = allCaptureDevNameMap[idx].name;
743 if(idx > 0)
744 sprintf(driver, "plughw:%d,%d", allCaptureDevNameMap[idx].card, allCaptureDevNameMap[idx].dev);
745 break;
748 if(idx == numCaptureDevNames)
749 return ALC_FALSE;
752 if(!alsa_load())
753 return ALC_FALSE;
755 data = (alsa_data*)calloc(1, sizeof(alsa_data));
757 i = psnd_pcm_open(&data->pcmHandle, driver, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK);
758 if(i < 0)
760 Sleep(200);
761 i = psnd_pcm_open(&data->pcmHandle, driver, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK);
763 if(i >= 0)
765 i = psnd_pcm_nonblock(data->pcmHandle, 0);
766 if(i < 0)
767 psnd_pcm_close(data->pcmHandle);
769 if(i < 0)
771 AL_PRINT("Could not open capture device '%s': %s\n", driver, psnd_strerror(i));
772 free(data);
773 alsa_unload();
774 return ALC_FALSE;
777 switch(aluBytesFromFormat(pDevice->Format))
779 case 1:
780 format = SND_PCM_FORMAT_U8;
781 break;
782 case 2:
783 format = SND_PCM_FORMAT_S16;
784 break;
785 case 4:
786 format = SND_PCM_FORMAT_FLOAT;
787 break;
788 default:
789 AL_PRINT("Unknown format: 0x%x\n", pDevice->Format);
790 goto error;
793 err = NULL;
794 bufferSizeInFrames = pDevice->UpdateSize * pDevice->NumUpdates;
795 psnd_pcm_hw_params_malloc(&p);
797 if((i=psnd_pcm_hw_params_any(data->pcmHandle, p)) < 0)
798 err = "any";
799 /* set interleaved access */
800 if(i >= 0 && (i=psnd_pcm_hw_params_set_access(data->pcmHandle, p, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
801 err = "set access";
802 /* set format (implicitly sets sample bits) */
803 if(i >= 0 && (i=psnd_pcm_hw_params_set_format(data->pcmHandle, p, format)) < 0)
804 err = "set format";
805 /* set channels (implicitly sets frame bits) */
806 if(i >= 0 && (i=psnd_pcm_hw_params_set_channels(data->pcmHandle, p, aluChannelsFromFormat(pDevice->Format))) < 0)
807 err = "set channels";
808 /* set rate (implicitly constrains period/buffer parameters) */
809 if(i >= 0 && (i=psnd_pcm_hw_params_set_rate(data->pcmHandle, p, pDevice->Frequency, 0)) < 0)
810 err = "set rate near";
811 /* set buffer size in frame units (implicitly sets period size/bytes/time and buffer time/bytes) */
812 if(i >= 0 && (i=psnd_pcm_hw_params_set_buffer_size_near(data->pcmHandle, p, &bufferSizeInFrames)) < 0)
813 err = "set buffer size near";
814 /* install and prepare hardware configuration */
815 if(i >= 0 && (i=psnd_pcm_hw_params(data->pcmHandle, p)) < 0)
816 err = "set params";
817 if(i < 0)
819 AL_PRINT("%s failed: %s\n", err, psnd_strerror(i));
820 psnd_pcm_hw_params_free(p);
821 goto error;
824 if((i=psnd_pcm_hw_params_get_period_size(p, &bufferSizeInFrames, NULL)) < 0)
826 AL_PRINT("get size failed: %s\n", psnd_strerror(i));
827 psnd_pcm_hw_params_free(p);
828 goto error;
831 psnd_pcm_hw_params_free(p);
833 frameSize = aluChannelsFromFormat(pDevice->Format);
834 frameSize *= aluBytesFromFormat(pDevice->Format);
836 data->ring = CreateRingBuffer(frameSize, pDevice->UpdateSize*pDevice->NumUpdates);
837 if(!data->ring)
839 AL_PRINT("ring buffer create failed\n");
840 goto error;
843 data->size = psnd_pcm_frames_to_bytes(data->pcmHandle, bufferSizeInFrames);
844 data->buffer = malloc(data->size);
845 if(!data->buffer)
847 AL_PRINT("buffer malloc failed\n");
848 goto error;
851 pDevice->ExtraData = data;
852 data->thread = StartThread(ALSANoMMapCaptureProc, pDevice);
853 if(data->thread == NULL)
855 AL_PRINT("Could not create capture thread\n");
856 goto error;
859 pDevice->szDeviceName = strdup(deviceName);
860 return ALC_TRUE;
862 error:
863 free(data->buffer);
864 DestroyRingBuffer(data->ring);
865 psnd_pcm_close(data->pcmHandle);
866 free(data);
867 alsa_unload();
869 pDevice->ExtraData = NULL;
870 return ALC_FALSE;
873 static void alsa_close_capture(ALCdevice *pDevice)
875 alsa_data *data = (alsa_data*)pDevice->ExtraData;
877 data->killNow = 1;
878 StopThread(data->thread);
880 psnd_pcm_close(data->pcmHandle);
881 DestroyRingBuffer(data->ring);
883 free(data->buffer);
884 free(data);
885 pDevice->ExtraData = NULL;
887 alsa_unload();
890 static void alsa_start_capture(ALCdevice *pDevice)
892 alsa_data *data = (alsa_data*)pDevice->ExtraData;
893 data->doCapture = 1;
896 static void alsa_stop_capture(ALCdevice *pDevice)
898 alsa_data *data = (alsa_data*)pDevice->ExtraData;
899 data->doCapture = 0;
902 static void alsa_capture_samples(ALCdevice *pDevice, ALCvoid *pBuffer, ALCuint lSamples)
904 alsa_data *data = (alsa_data*)pDevice->ExtraData;
906 if(lSamples <= (ALCuint)RingBufferSize(data->ring))
907 ReadRingBuffer(data->ring, pBuffer, lSamples);
908 else
909 alcSetError(pDevice, ALC_INVALID_VALUE);
912 static ALCuint alsa_available_samples(ALCdevice *pDevice)
914 alsa_data *data = (alsa_data*)pDevice->ExtraData;
915 return RingBufferSize(data->ring);
919 BackendFuncs alsa_funcs = {
920 alsa_open_playback,
921 alsa_close_playback,
922 alsa_reset_playback,
923 alsa_stop_playback,
924 alsa_open_capture,
925 alsa_close_capture,
926 alsa_start_capture,
927 alsa_stop_capture,
928 alsa_capture_samples,
929 alsa_available_samples
932 void alc_alsa_init(BackendFuncs *func_list)
934 *func_list = alsa_funcs;
937 void alc_alsa_deinit(void)
939 ALuint i;
941 for(i = 0;i < numDevNames;++i)
942 free(allDevNameMap[i].name);
943 free(allDevNameMap);
944 allDevNameMap = NULL;
945 numDevNames = 0;
947 for(i = 0;i < numCaptureDevNames;++i)
948 free(allCaptureDevNameMap[i].name);
949 free(allCaptureDevNameMap);
950 allCaptureDevNameMap = NULL;
951 numCaptureDevNames = 0;
954 void alc_alsa_probe(int type)
956 snd_ctl_t *handle;
957 int card, err, dev, idx;
958 snd_ctl_card_info_t *info;
959 snd_pcm_info_t *pcminfo;
960 snd_pcm_stream_t stream;
961 char name[128];
962 ALuint i;
964 if(!alsa_load())
965 return;
967 psnd_ctl_card_info_malloc(&info);
968 psnd_pcm_info_malloc(&pcminfo);
970 card = -1;
971 if((err=psnd_card_next(&card)) < 0)
972 AL_PRINT("Failed to find a card: %s\n", psnd_strerror(err));
974 if(type == DEVICE_PROBE)
975 AppendDeviceList(alsaDevice);
976 else if(type == ALL_DEVICE_PROBE)
978 stream = SND_PCM_STREAM_PLAYBACK;
980 for(i = 0;i < numDevNames;++i)
981 free(allDevNameMap[i].name);
983 allDevNameMap = realloc(allDevNameMap, sizeof(DevMap) * 1);
984 allDevNameMap[0].name = strdup("ALSA Software on default");
985 AppendAllDeviceList(allDevNameMap[0].name);
987 idx = 1;
988 while(card >= 0) {
989 sprintf(name, "hw:%d", card);
990 if ((err = psnd_ctl_open(&handle, name, 0)) < 0) {
991 AL_PRINT("control open (%i): %s\n", card, psnd_strerror(err));
992 goto next_card;
994 if ((err = psnd_ctl_card_info(handle, info)) < 0) {
995 AL_PRINT("control hardware info (%i): %s\n", card, psnd_strerror(err));
996 psnd_ctl_close(handle);
997 goto next_card;
1000 dev = -1;
1001 while(1) {
1002 const char *cname, *dname;
1003 void *temp;
1005 if (psnd_ctl_pcm_next_device(handle, &dev)<0)
1006 AL_PRINT("snd_ctl_pcm_next_device failed\n");
1007 if (dev < 0)
1008 break;
1010 psnd_pcm_info_set_device(pcminfo, dev);
1011 psnd_pcm_info_set_subdevice(pcminfo, 0);
1012 psnd_pcm_info_set_stream(pcminfo, stream);
1013 if ((err = psnd_ctl_pcm_info(handle, pcminfo)) < 0) {
1014 if (err != -ENOENT)
1015 AL_PRINT("control digital audio info (%i): %s\n", card, psnd_strerror(err));
1016 continue;
1019 temp = realloc(allDevNameMap, sizeof(DevMap) * (idx+1));
1020 if(temp)
1022 allDevNameMap = temp;
1023 cname = psnd_ctl_card_info_get_name(info);
1024 dname = psnd_pcm_info_get_name(pcminfo);
1025 snprintf(name, sizeof(name), "ALSA Software on %s [%s] (hw:%d,%d)",
1026 cname, dname, card, dev);
1027 AppendAllDeviceList(name);
1028 allDevNameMap[idx].name = strdup(name);
1029 allDevNameMap[idx].card = card;
1030 allDevNameMap[idx].dev = dev;
1031 idx++;
1034 psnd_ctl_close(handle);
1035 next_card:
1036 if(psnd_card_next(&card) < 0) {
1037 AL_PRINT("snd_card_next failed\n");
1038 break;
1041 numDevNames = idx;
1043 else if(type == CAPTURE_DEVICE_PROBE)
1045 stream = SND_PCM_STREAM_CAPTURE;
1047 for(i = 0;i < numCaptureDevNames;++i)
1048 free(allCaptureDevNameMap[i].name);
1050 allCaptureDevNameMap = realloc(allCaptureDevNameMap, sizeof(DevMap) * 1);
1051 allCaptureDevNameMap[0].name = strdup("ALSA Capture on default");
1052 AppendCaptureDeviceList(allCaptureDevNameMap[0].name);
1054 idx = 1;
1055 while (card >= 0) {
1056 sprintf(name, "hw:%d", card);
1057 handle = NULL;
1058 if ((err = psnd_ctl_open(&handle, name, 0)) < 0) {
1059 AL_PRINT("control open (%i): %s\n", card, psnd_strerror(err));
1061 if (err >= 0 && (err = psnd_ctl_card_info(handle, info)) < 0) {
1062 AL_PRINT("control hardware info (%i): %s\n", card, psnd_strerror(err));
1064 else if (err >= 0)
1066 dev = -1;
1067 while(1) {
1068 const char *cname, *dname;
1069 void *temp;
1071 if (psnd_ctl_pcm_next_device(handle, &dev)<0)
1072 AL_PRINT("snd_ctl_pcm_next_device failed\n");
1073 if (dev < 0)
1074 break;
1075 psnd_pcm_info_set_device(pcminfo, dev);
1076 psnd_pcm_info_set_subdevice(pcminfo, 0);
1077 psnd_pcm_info_set_stream(pcminfo, stream);
1078 if ((err = psnd_ctl_pcm_info(handle, pcminfo)) < 0) {
1079 if (err != -ENOENT)
1080 AL_PRINT("control digital audio info (%i): %s\n", card, psnd_strerror(err));
1081 continue;
1084 temp = realloc(allCaptureDevNameMap, sizeof(DevMap) * (idx+1));
1085 if(temp)
1087 allCaptureDevNameMap = temp;
1088 cname = psnd_ctl_card_info_get_name(info);
1089 dname = psnd_pcm_info_get_name(pcminfo);
1090 snprintf(name, sizeof(name), "ALSA Capture on %s [%s] (hw:%d,%d)",
1091 cname, dname, card, dev);
1092 AppendCaptureDeviceList(name);
1093 allCaptureDevNameMap[idx].name = strdup(name);
1094 allCaptureDevNameMap[idx].card = card;
1095 allCaptureDevNameMap[idx].dev = dev;
1096 idx++;
1100 if(handle) psnd_ctl_close(handle);
1101 if(psnd_card_next(&card) < 0) {
1102 AL_PRINT("snd_card_next failed\n");
1103 break;
1106 numCaptureDevNames = idx;
1109 psnd_pcm_info_free(pcminfo);
1110 psnd_ctl_card_info_free(info);
1112 alsa_unload();