Mercurial > fife-parpg
comparison ext/openal-soft/Alc/alsa.c @ 0:4a0efb7baf70
* Datasets becomes the new trunk and retires after that :-)
author | mvbarracuda@33b003aa-7bff-0310-803a-e67f0ece8222 |
---|---|
date | Sun, 29 Jun 2008 18:44:17 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4a0efb7baf70 |
---|---|
1 /** | |
2 * OpenAL cross platform audio library | |
3 * Copyright (C) 1999-2007 by authors. | |
4 * This library is free software; you can redistribute it and/or | |
5 * modify it under the terms of the GNU Library General Public | |
6 * License as published by the Free Software Foundation; either | |
7 * version 2 of the License, or (at your option) any later version. | |
8 * | |
9 * This library is distributed in the hope that it will be useful, | |
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
12 * Library General Public License for more details. | |
13 * | |
14 * You should have received a copy of the GNU Library General Public | |
15 * License along with this library; if not, write to the | |
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
17 * Boston, MA 02111-1307, USA. | |
18 * Or go to http://www.gnu.org/copyleft/lgpl.html | |
19 */ | |
20 | |
21 #include "config.h" | |
22 | |
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" | |
32 | |
33 #include <alsa/asoundlib.h> | |
34 | |
35 | |
36 typedef struct { | |
37 snd_pcm_t *pcmHandle; | |
38 snd_pcm_format_t format; | |
39 | |
40 ALvoid *buffer; | |
41 ALsizei size; | |
42 | |
43 int killNow; | |
44 ALvoid *thread; | |
45 } alsa_data; | |
46 | |
47 typedef struct { | |
48 ALCchar *name; | |
49 int card, dev; | |
50 } DevMap; | |
51 | |
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 | |
100 | |
101 #define MAX_DEVICES 16 | |
102 #define MAX_ALL_DEVICES 32 | |
103 | |
104 static DevMap allDevNameMap[MAX_ALL_DEVICES]; | |
105 static ALCchar *alsaDeviceList[MAX_DEVICES]; | |
106 static DevMap allCaptureDevNameMap[MAX_ALL_DEVICES]; | |
107 | |
108 static int xrun_recovery(snd_pcm_t *handle, int err) | |
109 { | |
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)); | |
115 } | |
116 else if (err == -ESTRPIPE) | |
117 { | |
118 while ((err = psnd_pcm_resume(handle)) == -EAGAIN) | |
119 Sleep(1); /* wait until the suspend flag is released */ | |
120 if (err < 0) | |
121 { | |
122 err = psnd_pcm_prepare(handle); | |
123 if (err < 0) | |
124 AL_PRINT("prepare failed: %s\n", psnd_strerror(err)); | |
125 } | |
126 } | |
127 return err; | |
128 } | |
129 | |
130 | |
131 static ALuint ALSAProc(ALvoid *ptr) | |
132 { | |
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; | |
141 | |
142 while(!data->killNow) | |
143 { | |
144 snd_pcm_state_t state = psnd_pcm_state(data->pcmHandle); | |
145 if(state == SND_PCM_STATE_XRUN) | |
146 { | |
147 err = xrun_recovery(data->pcmHandle, -EPIPE); | |
148 if (err < 0) | |
149 { | |
150 AL_PRINT("XRUN recovery failed: %s\n", psnd_strerror(err)); | |
151 break; | |
152 } | |
153 } | |
154 else if (state == SND_PCM_STATE_SUSPENDED) | |
155 { | |
156 err = xrun_recovery(data->pcmHandle, -ESTRPIPE); | |
157 if (err < 0) | |
158 { | |
159 AL_PRINT("SUSPEND recovery failed: %s\n", psnd_strerror(err)); | |
160 break; | |
161 } | |
162 } | |
163 | |
164 avail = psnd_pcm_avail_update(data->pcmHandle); | |
165 if(avail < 0) | |
166 { | |
167 err = xrun_recovery(data->pcmHandle, avail); | |
168 if (err < 0) | |
169 { | |
170 AL_PRINT("available update failed: %s\n", psnd_strerror(err)); | |
171 break; | |
172 } | |
173 } | |
174 | |
175 // make sure there's frames to process | |
176 if(avail == 0) | |
177 { | |
178 if(state != SND_PCM_STATE_RUNNING) | |
179 { | |
180 err = psnd_pcm_start(data->pcmHandle); | |
181 if(err < 0) | |
182 err = xrun_recovery(data->pcmHandle, err); | |
183 if(err < 0) | |
184 { | |
185 AL_PRINT("start failed: %s\n", psnd_strerror(err)); | |
186 break; | |
187 } | |
188 } | |
189 else if(psnd_pcm_wait(data->pcmHandle, 1000) == 0) | |
190 AL_PRINT("Wait timeout... buffer size too low?\n"); | |
191 continue; | |
192 } | |
193 | |
194 // it is possible that contiguous areas are smaller, thus we use a loop | |
195 while (avail > 0) | |
196 { | |
197 frames = avail; | |
198 | |
199 err = psnd_pcm_mmap_begin(data->pcmHandle, &areas, &offset, &frames); | |
200 if (err < 0) | |
201 { | |
202 err = xrun_recovery(data->pcmHandle, err); | |
203 if (err < 0) | |
204 AL_PRINT("mmap begin error: %s\n", psnd_strerror(err)); | |
205 break; | |
206 } | |
207 | |
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); | |
213 | |
214 commitres = psnd_pcm_mmap_commit(data->pcmHandle, offset, frames); | |
215 if (commitres < 0 || (commitres-frames) != 0) | |
216 { | |
217 AL_PRINT("mmap commit error: %s\n", | |
218 psnd_strerror(commitres >= 0 ? -EPIPE : commitres)); | |
219 break; | |
220 } | |
221 | |
222 avail -= frames; | |
223 } | |
224 } | |
225 | |
226 return 0; | |
227 } | |
228 | |
229 static ALuint ALSANoMMapProc(ALvoid *ptr) | |
230 { | |
231 ALCdevice *pDevice = (ALCdevice*)ptr; | |
232 alsa_data *data = (alsa_data*)pDevice->ExtraData; | |
233 snd_pcm_sframes_t avail; | |
234 char *WritePtr; | |
235 | |
236 while(!data->killNow) | |
237 { | |
238 SuspendContext(NULL); | |
239 aluMixData(pDevice->Context, data->buffer, data->size, pDevice->Format); | |
240 ProcessContext(NULL); | |
241 | |
242 WritePtr = data->buffer; | |
243 avail = (snd_pcm_uframes_t)data->size / psnd_pcm_frames_to_bytes(data->pcmHandle, 1); | |
244 while(avail > 0) | |
245 { | |
246 int ret = psnd_pcm_writei(data->pcmHandle, WritePtr, avail); | |
247 switch (ret) | |
248 { | |
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) | |
259 { | |
260 WritePtr += psnd_pcm_frames_to_bytes(data->pcmHandle, ret); | |
261 avail -= ret; | |
262 } | |
263 break; | |
264 } | |
265 if (ret < 0) | |
266 { | |
267 ret = psnd_pcm_prepare(data->pcmHandle); | |
268 if(ret < 0) | |
269 break; | |
270 } | |
271 } | |
272 } | |
273 | |
274 return 0; | |
275 } | |
276 | |
277 static ALCboolean alsa_open_playback(ALCdevice *device, const ALCchar *deviceName) | |
278 { | |
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; | |
289 | |
290 if(!alsa_handle) | |
291 return ALC_FALSE; | |
292 | |
293 strncpy(driver, GetConfigValue("alsa", "device", "default"), sizeof(driver)-1); | |
294 driver[sizeof(driver)-1] = 0; | |
295 if(deviceName) | |
296 { | |
297 size_t idx; | |
298 | |
299 for(idx = 0;idx < MAX_ALL_DEVICES;idx++) | |
300 { | |
301 if(allDevNameMap[idx].name && | |
302 strcmp(deviceName, allDevNameMap[idx].name) == 0) | |
303 { | |
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; | |
308 } | |
309 } | |
310 for(idx = 0;idx < MAX_DEVICES;idx++) | |
311 { | |
312 if(alsaDeviceList[idx] && | |
313 strcmp(deviceName, alsaDeviceList[idx]) == 0) | |
314 { | |
315 device->szDeviceName = alsaDeviceList[idx]; | |
316 if(idx > 0) | |
317 sprintf(driver, "hw:%zd,0", idx-1); | |
318 goto open_alsa; | |
319 } | |
320 } | |
321 return ALC_FALSE; | |
322 } | |
323 else | |
324 device->szDeviceName = alsaDeviceList[0]; | |
325 | |
326 open_alsa: | |
327 data = (alsa_data*)calloc(1, sizeof(alsa_data)); | |
328 | |
329 i = psnd_pcm_open(&data->pcmHandle, driver, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); | |
330 if(i < 0) | |
331 { | |
332 Sleep(200); | |
333 i = psnd_pcm_open(&data->pcmHandle, driver, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); | |
334 } | |
335 if(i >= 0) | |
336 { | |
337 i = psnd_pcm_nonblock(data->pcmHandle, 0); | |
338 if(i < 0) | |
339 psnd_pcm_close(data->pcmHandle); | |
340 } | |
341 if(i < 0) | |
342 { | |
343 free(data); | |
344 AL_PRINT("Could not open playback device '%s': %s\n", driver, psnd_strerror(i)); | |
345 return ALC_FALSE; | |
346 } | |
347 | |
348 switch(aluBytesFromFormat(device->Format)) | |
349 { | |
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); | |
359 } | |
360 | |
361 periods = GetConfigValueInt("alsa", "periods", 0); | |
362 bufferSizeInFrames = device->UpdateSize; | |
363 | |
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); | |
369 | |
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"))) | |
389 { | |
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; | |
395 } | |
396 #undef ok | |
397 | |
398 if((i=psnd_pcm_hw_params_get_access(p, &access)) < 0) | |
399 { | |
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; | |
405 } | |
406 | |
407 if((i=psnd_pcm_hw_params_get_period_size(p, &bufferSizeInFrames, NULL)) < 0) | |
408 { | |
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; | |
414 } | |
415 | |
416 psnd_pcm_hw_params_free(p); | |
417 | |
418 device->UpdateSize = bufferSizeInFrames; | |
419 | |
420 data->size = psnd_pcm_frames_to_bytes(data->pcmHandle, device->UpdateSize); | |
421 if(access == SND_PCM_ACCESS_RW_INTERLEAVED) | |
422 { | |
423 data->buffer = malloc(data->size); | |
424 if(!data->buffer) | |
425 { | |
426 AL_PRINT("buffer malloc failed\n"); | |
427 psnd_pcm_close(data->pcmHandle); | |
428 free(data); | |
429 return ALC_FALSE; | |
430 } | |
431 } | |
432 else | |
433 { | |
434 i = psnd_pcm_prepare(data->pcmHandle); | |
435 if(i < 0) | |
436 { | |
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; | |
442 } | |
443 } | |
444 | |
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) | |
451 { | |
452 psnd_pcm_close(data->pcmHandle); | |
453 device->ExtraData = NULL; | |
454 free(data->buffer); | |
455 free(data); | |
456 return ALC_FALSE; | |
457 } | |
458 | |
459 return ALC_TRUE; | |
460 } | |
461 | |
462 static void alsa_close_playback(ALCdevice *device) | |
463 { | |
464 alsa_data *data = (alsa_data*)device->ExtraData; | |
465 data->killNow = 1; | |
466 StopThread(data->thread); | |
467 psnd_pcm_close(data->pcmHandle); | |
468 | |
469 free(data->buffer); | |
470 free(data); | |
471 device->ExtraData = NULL; | |
472 } | |
473 | |
474 | |
475 static ALCboolean alsa_open_capture(ALCdevice *pDevice, const ALCchar *deviceName, ALCuint frequency, ALCenum format, ALCsizei SampleSize) | |
476 { | |
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; | |
484 | |
485 if(!alsa_handle) | |
486 return ALC_FALSE; | |
487 | |
488 strncpy(driver, GetConfigValue("alsa", "capture", "default"), sizeof(driver)-1); | |
489 driver[sizeof(driver)-1] = 0; | |
490 if(deviceName) | |
491 { | |
492 size_t idx; | |
493 | |
494 for(idx = 0;idx < MAX_ALL_DEVICES;idx++) | |
495 { | |
496 if(allCaptureDevNameMap[idx].name && | |
497 strcmp(deviceName, allCaptureDevNameMap[idx].name) == 0) | |
498 { | |
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; | |
503 } | |
504 } | |
505 return ALC_FALSE; | |
506 } | |
507 else | |
508 pDevice->szDeviceName = allCaptureDevNameMap[0].name; | |
509 | |
510 open_alsa: | |
511 data = (alsa_data*)calloc(1, sizeof(alsa_data)); | |
512 | |
513 i = psnd_pcm_open(&data->pcmHandle, driver, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK); | |
514 if(i < 0) | |
515 { | |
516 Sleep(200); | |
517 i = psnd_pcm_open(&data->pcmHandle, driver, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK); | |
518 } | |
519 if(i >= 0) | |
520 { | |
521 i = psnd_pcm_nonblock(data->pcmHandle, 0); | |
522 if(i < 0) | |
523 psnd_pcm_close(data->pcmHandle); | |
524 } | |
525 if(i < 0) | |
526 { | |
527 free(data); | |
528 AL_PRINT("Could not open capture device '%s': %s\n", driver, psnd_strerror(i)); | |
529 return ALC_FALSE; | |
530 } | |
531 | |
532 switch(aluBytesFromFormat(format)) | |
533 { | |
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); | |
543 } | |
544 | |
545 bufferSizeInFrames = SampleSize; | |
546 | |
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"))) | |
563 { | |
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; | |
569 } | |
570 #undef ok | |
571 psnd_pcm_hw_params_free(p); | |
572 | |
573 i = psnd_pcm_prepare(data->pcmHandle); | |
574 if(i < 0) | |
575 { | |
576 AL_PRINT("prepare error: %s\n", psnd_strerror(i)); | |
577 psnd_pcm_close(data->pcmHandle); | |
578 free(data); | |
579 return ALC_FALSE; | |
580 } | |
581 | |
582 pDevice->ExtraData = data; | |
583 return ALC_TRUE; | |
584 } | |
585 | |
586 static void alsa_close_capture(ALCdevice *pDevice) | |
587 { | |
588 alsa_data *data = (alsa_data*)pDevice->ExtraData; | |
589 psnd_pcm_close(data->pcmHandle); | |
590 | |
591 free(data); | |
592 pDevice->ExtraData = NULL; | |
593 } | |
594 | |
595 static void alsa_start_capture(ALCdevice *pDevice) | |
596 { | |
597 alsa_data *data = (alsa_data*)pDevice->ExtraData; | |
598 psnd_pcm_start(data->pcmHandle); | |
599 } | |
600 | |
601 static void alsa_stop_capture(ALCdevice *pDevice) | |
602 { | |
603 alsa_data *data = (alsa_data*)pDevice->ExtraData; | |
604 psnd_pcm_drain(data->pcmHandle); | |
605 } | |
606 | |
607 static void alsa_capture_samples(ALCdevice *pDevice, ALCvoid *pBuffer, ALCuint lSamples) | |
608 { | |
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; | |
614 | |
615 frames = psnd_pcm_avail_update(data->pcmHandle); | |
616 if(frames < 0) | |
617 { | |
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); | |
623 } | |
624 if (frames < (snd_pcm_sframes_t)lSamples) | |
625 { | |
626 SetALCError(ALC_INVALID_VALUE); | |
627 return; | |
628 } | |
629 | |
630 // it is possible that contiguous areas are smaller, thus we use a loop | |
631 while (lSamples > 0) | |
632 { | |
633 char *Pointer; | |
634 int Count; | |
635 | |
636 size = lSamples; | |
637 err = psnd_pcm_mmap_begin(data->pcmHandle, &areas, &offset, &size); | |
638 if (err < 0) | |
639 { | |
640 err = xrun_recovery(data->pcmHandle, err); | |
641 if (err < 0) | |
642 { | |
643 AL_PRINT("mmap begin error: %s\n", psnd_strerror(err)); | |
644 break; | |
645 } | |
646 continue; | |
647 } | |
648 | |
649 Pointer = (char*)areas->addr + (offset * areas->step / 8); | |
650 Count = psnd_pcm_frames_to_bytes(data->pcmHandle, size); | |
651 | |
652 memcpy(pBuffer, Pointer, Count); | |
653 pBuffer = (char*)pBuffer + Count; | |
654 | |
655 commitres = psnd_pcm_mmap_commit(data->pcmHandle, offset, size); | |
656 if (commitres < 0 || (commitres-size) != 0) | |
657 { | |
658 AL_PRINT("mmap commit error: %s\n", | |
659 psnd_strerror(commitres >= 0 ? -EPIPE : commitres)); | |
660 break; | |
661 } | |
662 | |
663 lSamples -= size; | |
664 } | |
665 } | |
666 | |
667 static ALCuint alsa_available_samples(ALCdevice *pDevice) | |
668 { | |
669 alsa_data *data = (alsa_data*)pDevice->ExtraData; | |
670 snd_pcm_sframes_t frames = psnd_pcm_avail_update(data->pcmHandle); | |
671 if(frames < 0) | |
672 { | |
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); | |
680 } | |
681 return max(frames, 0); | |
682 } | |
683 | |
684 | |
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 | |
694 }; | |
695 | |
696 void alc_alsa_init(BackendFuncs *func_list) | |
697 { | |
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; | |
705 | |
706 *func_list = alsa_funcs; | |
707 | |
708 #ifdef HAVE_DLFCN_H | |
709 alsa_handle = dlopen("libasound.so.2", RTLD_NOW); | |
710 if(!alsa_handle) | |
711 return; | |
712 dlerror(); | |
713 | |
714 #define LOAD_FUNC(f) do { \ | |
715 p##f = (typeof(f)*)dlsym(alsa_handle, #f); \ | |
716 if((str=dlerror()) != NULL) \ | |
717 { \ | |
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; \ | |
722 } \ | |
723 } while(0) | |
724 #else | |
725 str = NULL; | |
726 alsa_handle = 0xDEADBEEF; | |
727 #define LOAD_FUNC(f) p##f = f | |
728 #endif | |
729 | |
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); | |
760 | |
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); | |
776 | |
777 #undef LOAD_FUNC | |
778 | |
779 psnd_ctl_card_info_malloc(&info); | |
780 psnd_pcm_info_malloc(&pcminfo); | |
781 | |
782 card = -1; | |
783 if(psnd_card_next(&card) < 0 || card < 0) | |
784 AL_PRINT("no playback cards found...\n"); | |
785 else | |
786 { | |
787 alsaDeviceList[0] = AppendDeviceList("ALSA Software on default"); | |
788 allDevNameMap[0].name = AppendAllDeviceList("ALSA Software on default"); | |
789 } | |
790 | |
791 while (card >= 0) { | |
792 int firstDev = 1; | |
793 | |
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; | |
798 } | |
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; | |
803 } | |
804 | |
805 dev = -1; | |
806 while (idx < MAX_ALL_DEVICES) { | |
807 const char *cname, *dname; | |
808 | |
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; | |
813 | |
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); | |
819 } | |
820 | |
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; | |
828 } | |
829 | |
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++; | |
838 } | |
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; | |
844 } | |
845 } | |
846 | |
847 | |
848 stream = SND_PCM_STREAM_CAPTURE; | |
849 | |
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; | |
856 } | |
857 | |
858 allCaptureDevNameMap[0].name = AppendCaptureDeviceList("ALSA Capture on default"); | |
859 | |
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)); | |
865 } | |
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)); | |
868 } | |
869 else if (err >= 0) | |
870 { | |
871 dev = -1; | |
872 while (idx < MAX_ALL_DEVICES) { | |
873 const char *cname, *dname; | |
874 | |
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; | |
886 } | |
887 | |
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++; | |
896 } | |
897 } | |
898 if(handle) psnd_ctl_close(handle); | |
899 if(psnd_card_next(&card) < 0) { | |
900 AL_PRINT("snd_card_next failed\n"); | |
901 break; | |
902 } | |
903 } | |
904 psnd_pcm_info_free(pcminfo); | |
905 psnd_ctl_card_info_free(info); | |
906 } |