Mercurial > audio-send
comparison Alc/backends/alsa.c @ 0:f9476ff7637e
initial forking of open-al to create multiple listeners
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Tue, 25 Oct 2011 13:02:31 -0700 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:f9476ff7637e |
---|---|
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 | |
27 #include "alMain.h" | |
28 | |
29 #include <alsa/asoundlib.h> | |
30 | |
31 | |
32 static const ALCchar alsaDevice[] = "ALSA Default"; | |
33 | |
34 | |
35 static void *alsa_handle; | |
36 #ifdef HAVE_DYNLOAD | |
37 #define MAKE_FUNC(f) static typeof(f) * p##f | |
38 MAKE_FUNC(snd_strerror); | |
39 MAKE_FUNC(snd_pcm_open); | |
40 MAKE_FUNC(snd_pcm_close); | |
41 MAKE_FUNC(snd_pcm_nonblock); | |
42 MAKE_FUNC(snd_pcm_frames_to_bytes); | |
43 MAKE_FUNC(snd_pcm_bytes_to_frames); | |
44 MAKE_FUNC(snd_pcm_hw_params_malloc); | |
45 MAKE_FUNC(snd_pcm_hw_params_free); | |
46 MAKE_FUNC(snd_pcm_hw_params_any); | |
47 MAKE_FUNC(snd_pcm_hw_params_set_access); | |
48 MAKE_FUNC(snd_pcm_hw_params_set_format); | |
49 MAKE_FUNC(snd_pcm_hw_params_set_channels); | |
50 MAKE_FUNC(snd_pcm_hw_params_set_periods_near); | |
51 MAKE_FUNC(snd_pcm_hw_params_set_rate_near); | |
52 MAKE_FUNC(snd_pcm_hw_params_set_rate); | |
53 MAKE_FUNC(snd_pcm_hw_params_set_rate_resample); | |
54 MAKE_FUNC(snd_pcm_hw_params_set_buffer_time_near); | |
55 MAKE_FUNC(snd_pcm_hw_params_set_period_time_near); | |
56 MAKE_FUNC(snd_pcm_hw_params_set_buffer_size_near); | |
57 MAKE_FUNC(snd_pcm_hw_params_set_period_size_near); | |
58 MAKE_FUNC(snd_pcm_hw_params_set_buffer_size_min); | |
59 MAKE_FUNC(snd_pcm_hw_params_get_buffer_size); | |
60 MAKE_FUNC(snd_pcm_hw_params_get_period_size); | |
61 MAKE_FUNC(snd_pcm_hw_params_get_access); | |
62 MAKE_FUNC(snd_pcm_hw_params_get_periods); | |
63 MAKE_FUNC(snd_pcm_hw_params); | |
64 MAKE_FUNC(snd_pcm_sw_params_malloc); | |
65 MAKE_FUNC(snd_pcm_sw_params_current); | |
66 MAKE_FUNC(snd_pcm_sw_params_set_avail_min); | |
67 MAKE_FUNC(snd_pcm_sw_params); | |
68 MAKE_FUNC(snd_pcm_sw_params_free); | |
69 MAKE_FUNC(snd_pcm_prepare); | |
70 MAKE_FUNC(snd_pcm_start); | |
71 MAKE_FUNC(snd_pcm_resume); | |
72 MAKE_FUNC(snd_pcm_wait); | |
73 MAKE_FUNC(snd_pcm_state); | |
74 MAKE_FUNC(snd_pcm_avail_update); | |
75 MAKE_FUNC(snd_pcm_areas_silence); | |
76 MAKE_FUNC(snd_pcm_mmap_begin); | |
77 MAKE_FUNC(snd_pcm_mmap_commit); | |
78 MAKE_FUNC(snd_pcm_readi); | |
79 MAKE_FUNC(snd_pcm_writei); | |
80 MAKE_FUNC(snd_pcm_drain); | |
81 MAKE_FUNC(snd_pcm_recover); | |
82 MAKE_FUNC(snd_pcm_info_malloc); | |
83 MAKE_FUNC(snd_pcm_info_free); | |
84 MAKE_FUNC(snd_pcm_info_set_device); | |
85 MAKE_FUNC(snd_pcm_info_set_subdevice); | |
86 MAKE_FUNC(snd_pcm_info_set_stream); | |
87 MAKE_FUNC(snd_pcm_info_get_name); | |
88 MAKE_FUNC(snd_ctl_pcm_next_device); | |
89 MAKE_FUNC(snd_ctl_pcm_info); | |
90 MAKE_FUNC(snd_ctl_open); | |
91 MAKE_FUNC(snd_ctl_close); | |
92 MAKE_FUNC(snd_ctl_card_info_malloc); | |
93 MAKE_FUNC(snd_ctl_card_info_free); | |
94 MAKE_FUNC(snd_ctl_card_info); | |
95 MAKE_FUNC(snd_ctl_card_info_get_name); | |
96 MAKE_FUNC(snd_ctl_card_info_get_id); | |
97 MAKE_FUNC(snd_card_next); | |
98 #undef MAKE_FUNC | |
99 | |
100 #define snd_strerror psnd_strerror | |
101 #define snd_pcm_open psnd_pcm_open | |
102 #define snd_pcm_close psnd_pcm_close | |
103 #define snd_pcm_nonblock psnd_pcm_nonblock | |
104 #define snd_pcm_frames_to_bytes psnd_pcm_frames_to_bytes | |
105 #define snd_pcm_bytes_to_frames psnd_pcm_bytes_to_frames | |
106 #define snd_pcm_hw_params_malloc psnd_pcm_hw_params_malloc | |
107 #define snd_pcm_hw_params_free psnd_pcm_hw_params_free | |
108 #define snd_pcm_hw_params_any psnd_pcm_hw_params_any | |
109 #define snd_pcm_hw_params_set_access psnd_pcm_hw_params_set_access | |
110 #define snd_pcm_hw_params_set_format psnd_pcm_hw_params_set_format | |
111 #define snd_pcm_hw_params_set_channels psnd_pcm_hw_params_set_channels | |
112 #define snd_pcm_hw_params_set_periods_near psnd_pcm_hw_params_set_periods_near | |
113 #define snd_pcm_hw_params_set_rate_near psnd_pcm_hw_params_set_rate_near | |
114 #define snd_pcm_hw_params_set_rate psnd_pcm_hw_params_set_rate | |
115 #define snd_pcm_hw_params_set_rate_resample psnd_pcm_hw_params_set_rate_resample | |
116 #define snd_pcm_hw_params_set_buffer_time_near psnd_pcm_hw_params_set_buffer_time_near | |
117 #define snd_pcm_hw_params_set_period_time_near psnd_pcm_hw_params_set_period_time_near | |
118 #define snd_pcm_hw_params_set_buffer_size_near psnd_pcm_hw_params_set_buffer_size_near | |
119 #define snd_pcm_hw_params_set_period_size_near psnd_pcm_hw_params_set_period_size_near | |
120 #define snd_pcm_hw_params_set_buffer_size_min psnd_pcm_hw_params_set_buffer_size_min | |
121 #define snd_pcm_hw_params_get_buffer_size psnd_pcm_hw_params_get_buffer_size | |
122 #define snd_pcm_hw_params_get_period_size psnd_pcm_hw_params_get_period_size | |
123 #define snd_pcm_hw_params_get_access psnd_pcm_hw_params_get_access | |
124 #define snd_pcm_hw_params_get_periods psnd_pcm_hw_params_get_periods | |
125 #define snd_pcm_hw_params psnd_pcm_hw_params | |
126 #define snd_pcm_sw_params_malloc psnd_pcm_sw_params_malloc | |
127 #define snd_pcm_sw_params_current psnd_pcm_sw_params_current | |
128 #define snd_pcm_sw_params_set_avail_min psnd_pcm_sw_params_set_avail_min | |
129 #define snd_pcm_sw_params psnd_pcm_sw_params | |
130 #define snd_pcm_sw_params_free psnd_pcm_sw_params_free | |
131 #define snd_pcm_prepare psnd_pcm_prepare | |
132 #define snd_pcm_start psnd_pcm_start | |
133 #define snd_pcm_resume psnd_pcm_resume | |
134 #define snd_pcm_wait psnd_pcm_wait | |
135 #define snd_pcm_state psnd_pcm_state | |
136 #define snd_pcm_avail_update psnd_pcm_avail_update | |
137 #define snd_pcm_areas_silence psnd_pcm_areas_silence | |
138 #define snd_pcm_mmap_begin psnd_pcm_mmap_begin | |
139 #define snd_pcm_mmap_commit psnd_pcm_mmap_commit | |
140 #define snd_pcm_readi psnd_pcm_readi | |
141 #define snd_pcm_writei psnd_pcm_writei | |
142 #define snd_pcm_drain psnd_pcm_drain | |
143 #define snd_pcm_recover psnd_pcm_recover | |
144 #define snd_pcm_info_malloc psnd_pcm_info_malloc | |
145 #define snd_pcm_info_free psnd_pcm_info_free | |
146 #define snd_pcm_info_set_device psnd_pcm_info_set_device | |
147 #define snd_pcm_info_set_subdevice psnd_pcm_info_set_subdevice | |
148 #define snd_pcm_info_set_stream psnd_pcm_info_set_stream | |
149 #define snd_pcm_info_get_name psnd_pcm_info_get_name | |
150 #define snd_ctl_pcm_next_device psnd_ctl_pcm_next_device | |
151 #define snd_ctl_pcm_info psnd_ctl_pcm_info | |
152 #define snd_ctl_open psnd_ctl_open | |
153 #define snd_ctl_close psnd_ctl_close | |
154 #define snd_ctl_card_info_malloc psnd_ctl_card_info_malloc | |
155 #define snd_ctl_card_info_free psnd_ctl_card_info_free | |
156 #define snd_ctl_card_info psnd_ctl_card_info | |
157 #define snd_ctl_card_info_get_name psnd_ctl_card_info_get_name | |
158 #define snd_ctl_card_info_get_id psnd_ctl_card_info_get_id | |
159 #define snd_card_next psnd_card_next | |
160 #endif | |
161 | |
162 | |
163 static ALCboolean alsa_load(void) | |
164 { | |
165 if(!alsa_handle) | |
166 { | |
167 #ifdef HAVE_DYNLOAD | |
168 alsa_handle = LoadLib("libasound.so.2"); | |
169 if(!alsa_handle) | |
170 return ALC_FALSE; | |
171 | |
172 #define LOAD_FUNC(f) do { \ | |
173 p##f = GetSymbol(alsa_handle, #f); \ | |
174 if(p##f == NULL) { \ | |
175 CloseLib(alsa_handle); \ | |
176 alsa_handle = NULL; \ | |
177 return ALC_FALSE; \ | |
178 } \ | |
179 } while(0) | |
180 LOAD_FUNC(snd_strerror); | |
181 LOAD_FUNC(snd_pcm_open); | |
182 LOAD_FUNC(snd_pcm_close); | |
183 LOAD_FUNC(snd_pcm_nonblock); | |
184 LOAD_FUNC(snd_pcm_frames_to_bytes); | |
185 LOAD_FUNC(snd_pcm_bytes_to_frames); | |
186 LOAD_FUNC(snd_pcm_hw_params_malloc); | |
187 LOAD_FUNC(snd_pcm_hw_params_free); | |
188 LOAD_FUNC(snd_pcm_hw_params_any); | |
189 LOAD_FUNC(snd_pcm_hw_params_set_access); | |
190 LOAD_FUNC(snd_pcm_hw_params_set_format); | |
191 LOAD_FUNC(snd_pcm_hw_params_set_channels); | |
192 LOAD_FUNC(snd_pcm_hw_params_set_periods_near); | |
193 LOAD_FUNC(snd_pcm_hw_params_set_rate_near); | |
194 LOAD_FUNC(snd_pcm_hw_params_set_rate); | |
195 LOAD_FUNC(snd_pcm_hw_params_set_rate_resample); | |
196 LOAD_FUNC(snd_pcm_hw_params_set_buffer_time_near); | |
197 LOAD_FUNC(snd_pcm_hw_params_set_period_time_near); | |
198 LOAD_FUNC(snd_pcm_hw_params_set_buffer_size_near); | |
199 LOAD_FUNC(snd_pcm_hw_params_set_buffer_size_min); | |
200 LOAD_FUNC(snd_pcm_hw_params_set_period_size_near); | |
201 LOAD_FUNC(snd_pcm_hw_params_get_buffer_size); | |
202 LOAD_FUNC(snd_pcm_hw_params_get_period_size); | |
203 LOAD_FUNC(snd_pcm_hw_params_get_access); | |
204 LOAD_FUNC(snd_pcm_hw_params_get_periods); | |
205 LOAD_FUNC(snd_pcm_hw_params); | |
206 LOAD_FUNC(snd_pcm_sw_params_malloc); | |
207 LOAD_FUNC(snd_pcm_sw_params_current); | |
208 LOAD_FUNC(snd_pcm_sw_params_set_avail_min); | |
209 LOAD_FUNC(snd_pcm_sw_params); | |
210 LOAD_FUNC(snd_pcm_sw_params_free); | |
211 LOAD_FUNC(snd_pcm_prepare); | |
212 LOAD_FUNC(snd_pcm_start); | |
213 LOAD_FUNC(snd_pcm_resume); | |
214 LOAD_FUNC(snd_pcm_wait); | |
215 LOAD_FUNC(snd_pcm_state); | |
216 LOAD_FUNC(snd_pcm_avail_update); | |
217 LOAD_FUNC(snd_pcm_areas_silence); | |
218 LOAD_FUNC(snd_pcm_mmap_begin); | |
219 LOAD_FUNC(snd_pcm_mmap_commit); | |
220 LOAD_FUNC(snd_pcm_readi); | |
221 LOAD_FUNC(snd_pcm_writei); | |
222 LOAD_FUNC(snd_pcm_drain); | |
223 LOAD_FUNC(snd_pcm_recover); | |
224 LOAD_FUNC(snd_pcm_info_malloc); | |
225 LOAD_FUNC(snd_pcm_info_free); | |
226 LOAD_FUNC(snd_pcm_info_set_device); | |
227 LOAD_FUNC(snd_pcm_info_set_subdevice); | |
228 LOAD_FUNC(snd_pcm_info_set_stream); | |
229 LOAD_FUNC(snd_pcm_info_get_name); | |
230 LOAD_FUNC(snd_ctl_pcm_next_device); | |
231 LOAD_FUNC(snd_ctl_pcm_info); | |
232 LOAD_FUNC(snd_ctl_open); | |
233 LOAD_FUNC(snd_ctl_close); | |
234 LOAD_FUNC(snd_ctl_card_info_malloc); | |
235 LOAD_FUNC(snd_ctl_card_info_free); | |
236 LOAD_FUNC(snd_ctl_card_info); | |
237 LOAD_FUNC(snd_ctl_card_info_get_name); | |
238 LOAD_FUNC(snd_ctl_card_info_get_id); | |
239 LOAD_FUNC(snd_card_next); | |
240 #undef LOAD_FUNC | |
241 #else | |
242 alsa_handle = (void*)0xDEADBEEF; | |
243 #endif | |
244 } | |
245 return ALC_TRUE; | |
246 } | |
247 | |
248 | |
249 typedef struct { | |
250 snd_pcm_t *pcmHandle; | |
251 | |
252 ALvoid *buffer; | |
253 ALsizei size; | |
254 | |
255 ALboolean doCapture; | |
256 RingBuffer *ring; | |
257 | |
258 volatile int killNow; | |
259 ALvoid *thread; | |
260 } alsa_data; | |
261 | |
262 typedef struct { | |
263 ALCchar *name; | |
264 char *card; | |
265 int dev; | |
266 } DevMap; | |
267 | |
268 static DevMap *allDevNameMap; | |
269 static ALuint numDevNames; | |
270 static DevMap *allCaptureDevNameMap; | |
271 static ALuint numCaptureDevNames; | |
272 | |
273 static const char *device_prefix; | |
274 static const char *capture_prefix; | |
275 | |
276 | |
277 static DevMap *probe_devices(snd_pcm_stream_t stream, ALuint *count) | |
278 { | |
279 snd_ctl_t *handle; | |
280 int card, err, dev, idx; | |
281 snd_ctl_card_info_t *info; | |
282 snd_pcm_info_t *pcminfo; | |
283 DevMap *DevList; | |
284 char name[1024]; | |
285 | |
286 snd_ctl_card_info_malloc(&info); | |
287 snd_pcm_info_malloc(&pcminfo); | |
288 | |
289 card = -1; | |
290 if((err=snd_card_next(&card)) < 0) | |
291 ERR("Failed to find a card: %s\n", snd_strerror(err)); | |
292 | |
293 DevList = malloc(sizeof(DevMap) * 1); | |
294 DevList[0].name = strdup("ALSA Default"); | |
295 DevList[0].card = NULL; | |
296 DevList[0].dev = 0; | |
297 idx = 1; | |
298 while(card >= 0) | |
299 { | |
300 sprintf(name, "hw:%d", card); | |
301 if((err = snd_ctl_open(&handle, name, 0)) < 0) | |
302 { | |
303 ERR("control open (%i): %s\n", card, snd_strerror(err)); | |
304 goto next_card; | |
305 } | |
306 if((err = snd_ctl_card_info(handle, info)) < 0) | |
307 { | |
308 ERR("control hardware info (%i): %s\n", card, snd_strerror(err)); | |
309 snd_ctl_close(handle); | |
310 goto next_card; | |
311 } | |
312 | |
313 dev = -1; | |
314 while(1) | |
315 { | |
316 const char *cname, *dname, *cid; | |
317 void *temp; | |
318 | |
319 if(snd_ctl_pcm_next_device(handle, &dev) < 0) | |
320 ERR("snd_ctl_pcm_next_device failed\n"); | |
321 if(dev < 0) | |
322 break; | |
323 | |
324 snd_pcm_info_set_device(pcminfo, dev); | |
325 snd_pcm_info_set_subdevice(pcminfo, 0); | |
326 snd_pcm_info_set_stream(pcminfo, stream); | |
327 if((err = snd_ctl_pcm_info(handle, pcminfo)) < 0) { | |
328 if(err != -ENOENT) | |
329 ERR("control digital audio info (%i): %s\n", card, snd_strerror(err)); | |
330 continue; | |
331 } | |
332 | |
333 temp = realloc(DevList, sizeof(DevMap) * (idx+1)); | |
334 if(temp) | |
335 { | |
336 DevList = temp; | |
337 cname = snd_ctl_card_info_get_name(info); | |
338 dname = snd_pcm_info_get_name(pcminfo); | |
339 cid = snd_ctl_card_info_get_id(info); | |
340 snprintf(name, sizeof(name), "%s, %s (CARD=%s,DEV=%d)", | |
341 cname, dname, cid, dev); | |
342 DevList[idx].name = strdup(name); | |
343 DevList[idx].card = strdup(cid); | |
344 DevList[idx].dev = dev; | |
345 idx++; | |
346 } | |
347 } | |
348 snd_ctl_close(handle); | |
349 next_card: | |
350 if(snd_card_next(&card) < 0) { | |
351 ERR("snd_card_next failed\n"); | |
352 break; | |
353 } | |
354 } | |
355 | |
356 snd_pcm_info_free(pcminfo); | |
357 snd_ctl_card_info_free(info); | |
358 | |
359 *count = idx; | |
360 return DevList; | |
361 } | |
362 | |
363 | |
364 static int xrun_recovery(snd_pcm_t *handle, int err) | |
365 { | |
366 err = snd_pcm_recover(handle, err, 1); | |
367 if(err < 0) | |
368 ERR("recover failed: %s\n", snd_strerror(err)); | |
369 return err; | |
370 } | |
371 | |
372 static int verify_state(snd_pcm_t *handle) | |
373 { | |
374 snd_pcm_state_t state = snd_pcm_state(handle); | |
375 if(state == SND_PCM_STATE_DISCONNECTED) | |
376 return -ENODEV; | |
377 if(state == SND_PCM_STATE_XRUN) | |
378 { | |
379 int err = xrun_recovery(handle, -EPIPE); | |
380 if(err < 0) return err; | |
381 } | |
382 else if(state == SND_PCM_STATE_SUSPENDED) | |
383 { | |
384 int err = xrun_recovery(handle, -ESTRPIPE); | |
385 if(err < 0) return err; | |
386 } | |
387 | |
388 return state; | |
389 } | |
390 | |
391 | |
392 static ALuint ALSAProc(ALvoid *ptr) | |
393 { | |
394 ALCdevice *pDevice = (ALCdevice*)ptr; | |
395 alsa_data *data = (alsa_data*)pDevice->ExtraData; | |
396 const snd_pcm_channel_area_t *areas = NULL; | |
397 snd_pcm_sframes_t avail, commitres; | |
398 snd_pcm_uframes_t offset, frames; | |
399 char *WritePtr; | |
400 int err; | |
401 | |
402 SetRTPriority(); | |
403 | |
404 while(!data->killNow) | |
405 { | |
406 int state = verify_state(data->pcmHandle); | |
407 if(state < 0) | |
408 { | |
409 ERR("Invalid state detected: %s\n", snd_strerror(state)); | |
410 aluHandleDisconnect(pDevice); | |
411 break; | |
412 } | |
413 | |
414 avail = snd_pcm_avail_update(data->pcmHandle); | |
415 if(avail < 0) | |
416 { | |
417 ERR("available update failed: %s\n", snd_strerror(avail)); | |
418 continue; | |
419 } | |
420 | |
421 // make sure there's frames to process | |
422 if((snd_pcm_uframes_t)avail < pDevice->UpdateSize) | |
423 { | |
424 if(state != SND_PCM_STATE_RUNNING) | |
425 { | |
426 err = snd_pcm_start(data->pcmHandle); | |
427 if(err < 0) | |
428 { | |
429 ERR("start failed: %s\n", snd_strerror(err)); | |
430 continue; | |
431 } | |
432 } | |
433 if(snd_pcm_wait(data->pcmHandle, 1000) == 0) | |
434 ERR("Wait timeout... buffer size too low?\n"); | |
435 continue; | |
436 } | |
437 avail -= avail%pDevice->UpdateSize; | |
438 | |
439 // it is possible that contiguous areas are smaller, thus we use a loop | |
440 while(avail > 0) | |
441 { | |
442 frames = avail; | |
443 | |
444 err = snd_pcm_mmap_begin(data->pcmHandle, &areas, &offset, &frames); | |
445 if(err < 0) | |
446 { | |
447 ERR("mmap begin error: %s\n", snd_strerror(err)); | |
448 break; | |
449 } | |
450 | |
451 WritePtr = (char*)areas->addr + (offset * areas->step / 8); | |
452 aluMixData(pDevice, WritePtr, frames); | |
453 | |
454 commitres = snd_pcm_mmap_commit(data->pcmHandle, offset, frames); | |
455 if(commitres < 0 || (commitres-frames) != 0) | |
456 { | |
457 ERR("mmap commit error: %s\n", | |
458 snd_strerror(commitres >= 0 ? -EPIPE : commitres)); | |
459 break; | |
460 } | |
461 | |
462 avail -= frames; | |
463 } | |
464 } | |
465 | |
466 return 0; | |
467 } | |
468 | |
469 static ALuint ALSANoMMapProc(ALvoid *ptr) | |
470 { | |
471 ALCdevice *pDevice = (ALCdevice*)ptr; | |
472 alsa_data *data = (alsa_data*)pDevice->ExtraData; | |
473 snd_pcm_sframes_t avail; | |
474 char *WritePtr; | |
475 | |
476 SetRTPriority(); | |
477 | |
478 while(!data->killNow) | |
479 { | |
480 int state = verify_state(data->pcmHandle); | |
481 if(state < 0) | |
482 { | |
483 ERR("Invalid state detected: %s\n", snd_strerror(state)); | |
484 aluHandleDisconnect(pDevice); | |
485 break; | |
486 } | |
487 | |
488 WritePtr = data->buffer; | |
489 avail = data->size / snd_pcm_frames_to_bytes(data->pcmHandle, 1); | |
490 aluMixData(pDevice, WritePtr, avail); | |
491 | |
492 while(avail > 0) | |
493 { | |
494 int ret = snd_pcm_writei(data->pcmHandle, WritePtr, avail); | |
495 switch (ret) | |
496 { | |
497 case -EAGAIN: | |
498 continue; | |
499 case -ESTRPIPE: | |
500 case -EPIPE: | |
501 case -EINTR: | |
502 ret = snd_pcm_recover(data->pcmHandle, ret, 1); | |
503 if(ret < 0) | |
504 avail = 0; | |
505 break; | |
506 default: | |
507 if (ret >= 0) | |
508 { | |
509 WritePtr += snd_pcm_frames_to_bytes(data->pcmHandle, ret); | |
510 avail -= ret; | |
511 } | |
512 break; | |
513 } | |
514 if (ret < 0) | |
515 { | |
516 ret = snd_pcm_prepare(data->pcmHandle); | |
517 if(ret < 0) | |
518 break; | |
519 } | |
520 } | |
521 } | |
522 | |
523 return 0; | |
524 } | |
525 | |
526 static ALCboolean alsa_open_playback(ALCdevice *device, const ALCchar *deviceName) | |
527 { | |
528 alsa_data *data; | |
529 char driver[128]; | |
530 int i; | |
531 | |
532 strncpy(driver, GetConfigValue("alsa", "device", "default"), sizeof(driver)-1); | |
533 driver[sizeof(driver)-1] = 0; | |
534 | |
535 if(!deviceName) | |
536 deviceName = alsaDevice; | |
537 else if(strcmp(deviceName, alsaDevice) != 0) | |
538 { | |
539 size_t idx; | |
540 | |
541 if(!allDevNameMap) | |
542 allDevNameMap = probe_devices(SND_PCM_STREAM_PLAYBACK, &numDevNames); | |
543 | |
544 for(idx = 0;idx < numDevNames;idx++) | |
545 { | |
546 if(allDevNameMap[idx].name && | |
547 strcmp(deviceName, allDevNameMap[idx].name) == 0) | |
548 { | |
549 if(idx > 0) | |
550 snprintf(driver, sizeof(driver), "%sCARD=%s,DEV=%d", device_prefix, | |
551 allDevNameMap[idx].card, allDevNameMap[idx].dev); | |
552 break; | |
553 } | |
554 } | |
555 if(idx == numDevNames) | |
556 return ALC_FALSE; | |
557 } | |
558 | |
559 data = (alsa_data*)calloc(1, sizeof(alsa_data)); | |
560 | |
561 i = snd_pcm_open(&data->pcmHandle, driver, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); | |
562 if(i >= 0) | |
563 { | |
564 i = snd_pcm_nonblock(data->pcmHandle, 0); | |
565 if(i < 0) | |
566 snd_pcm_close(data->pcmHandle); | |
567 } | |
568 if(i < 0) | |
569 { | |
570 free(data); | |
571 ERR("Could not open playback device '%s': %s\n", driver, snd_strerror(i)); | |
572 return ALC_FALSE; | |
573 } | |
574 | |
575 device->szDeviceName = strdup(deviceName); | |
576 device->ExtraData = data; | |
577 return ALC_TRUE; | |
578 } | |
579 | |
580 static void alsa_close_playback(ALCdevice *device) | |
581 { | |
582 alsa_data *data = (alsa_data*)device->ExtraData; | |
583 | |
584 snd_pcm_close(data->pcmHandle); | |
585 free(data); | |
586 device->ExtraData = NULL; | |
587 } | |
588 | |
589 static ALCboolean alsa_reset_playback(ALCdevice *device) | |
590 { | |
591 alsa_data *data = (alsa_data*)device->ExtraData; | |
592 snd_pcm_uframes_t periodSizeInFrames; | |
593 unsigned int periodLen, bufferLen; | |
594 snd_pcm_sw_params_t *sp = NULL; | |
595 snd_pcm_hw_params_t *p = NULL; | |
596 snd_pcm_access_t access; | |
597 snd_pcm_format_t format; | |
598 unsigned int periods; | |
599 unsigned int rate; | |
600 int allowmmap; | |
601 char *err; | |
602 int i; | |
603 | |
604 | |
605 format = -1; | |
606 switch(device->FmtType) | |
607 { | |
608 case DevFmtByte: | |
609 format = SND_PCM_FORMAT_S8; | |
610 break; | |
611 case DevFmtUByte: | |
612 format = SND_PCM_FORMAT_U8; | |
613 break; | |
614 case DevFmtShort: | |
615 format = SND_PCM_FORMAT_S16; | |
616 break; | |
617 case DevFmtUShort: | |
618 format = SND_PCM_FORMAT_U16; | |
619 break; | |
620 case DevFmtFloat: | |
621 format = SND_PCM_FORMAT_FLOAT; | |
622 break; | |
623 } | |
624 | |
625 allowmmap = GetConfigValueBool("alsa", "mmap", 1); | |
626 periods = device->NumUpdates; | |
627 periodLen = (ALuint64)device->UpdateSize * 1000000 / device->Frequency; | |
628 bufferLen = periodLen * periods; | |
629 rate = device->Frequency; | |
630 | |
631 err = NULL; | |
632 snd_pcm_hw_params_malloc(&p); | |
633 | |
634 if((i=snd_pcm_hw_params_any(data->pcmHandle, p)) < 0) | |
635 err = "any"; | |
636 /* set interleaved access */ | |
637 if(i >= 0 && (!allowmmap || (i=snd_pcm_hw_params_set_access(data->pcmHandle, p, SND_PCM_ACCESS_MMAP_INTERLEAVED)) < 0)) | |
638 { | |
639 if(periods > 2) | |
640 { | |
641 periods--; | |
642 bufferLen = periodLen * periods; | |
643 } | |
644 if((i=snd_pcm_hw_params_set_access(data->pcmHandle, p, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) | |
645 err = "set access"; | |
646 } | |
647 /* set format (implicitly sets sample bits) */ | |
648 if(i >= 0 && (i=snd_pcm_hw_params_set_format(data->pcmHandle, p, format)) < 0) | |
649 { | |
650 device->FmtType = DevFmtFloat; | |
651 if(format == SND_PCM_FORMAT_FLOAT || | |
652 (i=snd_pcm_hw_params_set_format(data->pcmHandle, p, SND_PCM_FORMAT_FLOAT)) < 0) | |
653 { | |
654 device->FmtType = DevFmtShort; | |
655 if(format == SND_PCM_FORMAT_S16 || | |
656 (i=snd_pcm_hw_params_set_format(data->pcmHandle, p, SND_PCM_FORMAT_S16)) < 0) | |
657 { | |
658 device->FmtType = DevFmtUByte; | |
659 if(format == SND_PCM_FORMAT_U8 || | |
660 (i=snd_pcm_hw_params_set_format(data->pcmHandle, p, SND_PCM_FORMAT_U8)) < 0) | |
661 err = "set format"; | |
662 } | |
663 } | |
664 } | |
665 /* set channels (implicitly sets frame bits) */ | |
666 if(i >= 0 && (i=snd_pcm_hw_params_set_channels(data->pcmHandle, p, ChannelsFromDevFmt(device->FmtChans))) < 0) | |
667 { | |
668 if((i=snd_pcm_hw_params_set_channels(data->pcmHandle, p, 2)) < 0) | |
669 { | |
670 if((i=snd_pcm_hw_params_set_channels(data->pcmHandle, p, 1)) < 0) | |
671 err = "set channels"; | |
672 else | |
673 { | |
674 if((device->Flags&DEVICE_CHANNELS_REQUEST)) | |
675 ERR("Failed to set %s, got Mono instead\n", DevFmtChannelsString(device->FmtChans)); | |
676 device->FmtChans = DevFmtMono; | |
677 } | |
678 } | |
679 else | |
680 { | |
681 if((device->Flags&DEVICE_CHANNELS_REQUEST)) | |
682 ERR("Failed to set %s, got Stereo instead\n", DevFmtChannelsString(device->FmtChans)); | |
683 device->FmtChans = DevFmtStereo; | |
684 } | |
685 device->Flags &= ~DEVICE_CHANNELS_REQUEST; | |
686 } | |
687 if(i >= 0 && (i=snd_pcm_hw_params_set_rate_resample(data->pcmHandle, p, 0)) < 0) | |
688 { | |
689 ERR("Failed to disable ALSA resampler\n"); | |
690 i = 0; | |
691 } | |
692 /* set rate (implicitly constrains period/buffer parameters) */ | |
693 if(i >= 0 && (i=snd_pcm_hw_params_set_rate_near(data->pcmHandle, p, &rate, NULL)) < 0) | |
694 err = "set rate near"; | |
695 /* set buffer time (implicitly constrains period/buffer parameters) */ | |
696 if(i >= 0 && (i=snd_pcm_hw_params_set_buffer_time_near(data->pcmHandle, p, &bufferLen, NULL)) < 0) | |
697 err = "set buffer time near"; | |
698 /* set period time in frame units (implicitly sets buffer size/bytes/time and period size/bytes) */ | |
699 if(i >= 0 && (i=snd_pcm_hw_params_set_period_time_near(data->pcmHandle, p, &periodLen, NULL)) < 0) | |
700 err = "set period time near"; | |
701 /* install and prepare hardware configuration */ | |
702 if(i >= 0 && (i=snd_pcm_hw_params(data->pcmHandle, p)) < 0) | |
703 err = "set params"; | |
704 if(i >= 0 && (i=snd_pcm_hw_params_get_access(p, &access)) < 0) | |
705 err = "get access"; | |
706 if(i >= 0 && (i=snd_pcm_hw_params_get_period_size(p, &periodSizeInFrames, NULL)) < 0) | |
707 err = "get period size"; | |
708 if(i >= 0 && (i=snd_pcm_hw_params_get_periods(p, &periods, NULL)) < 0) | |
709 err = "get periods"; | |
710 if(i < 0) | |
711 { | |
712 ERR("%s failed: %s\n", err, snd_strerror(i)); | |
713 snd_pcm_hw_params_free(p); | |
714 return ALC_FALSE; | |
715 } | |
716 | |
717 snd_pcm_hw_params_free(p); | |
718 | |
719 err = NULL; | |
720 snd_pcm_sw_params_malloc(&sp); | |
721 | |
722 if((i=snd_pcm_sw_params_current(data->pcmHandle, sp)) != 0) | |
723 err = "sw current"; | |
724 if(i == 0 && (i=snd_pcm_sw_params_set_avail_min(data->pcmHandle, sp, periodSizeInFrames)) != 0) | |
725 err = "sw set avail min"; | |
726 if(i == 0 && (i=snd_pcm_sw_params(data->pcmHandle, sp)) != 0) | |
727 err = "sw set params"; | |
728 if(i != 0) | |
729 { | |
730 ERR("%s failed: %s\n", err, snd_strerror(i)); | |
731 snd_pcm_sw_params_free(sp); | |
732 return ALC_FALSE; | |
733 } | |
734 | |
735 snd_pcm_sw_params_free(sp); | |
736 | |
737 if(device->Frequency != rate) | |
738 { | |
739 if((device->Flags&DEVICE_FREQUENCY_REQUEST)) | |
740 ERR("Failed to set %dhz, got %dhz instead\n", device->Frequency, rate); | |
741 device->Flags &= ~DEVICE_FREQUENCY_REQUEST; | |
742 device->Frequency = rate; | |
743 } | |
744 | |
745 SetDefaultChannelOrder(device); | |
746 | |
747 data->size = snd_pcm_frames_to_bytes(data->pcmHandle, periodSizeInFrames); | |
748 if(access == SND_PCM_ACCESS_RW_INTERLEAVED) | |
749 { | |
750 /* Increase periods by one, since the temp buffer counts as an extra | |
751 * period */ | |
752 periods++; | |
753 data->buffer = malloc(data->size); | |
754 if(!data->buffer) | |
755 { | |
756 ERR("buffer malloc failed\n"); | |
757 return ALC_FALSE; | |
758 } | |
759 device->UpdateSize = periodSizeInFrames; | |
760 device->NumUpdates = periods; | |
761 data->thread = StartThread(ALSANoMMapProc, device); | |
762 } | |
763 else | |
764 { | |
765 i = snd_pcm_prepare(data->pcmHandle); | |
766 if(i < 0) | |
767 { | |
768 ERR("prepare error: %s\n", snd_strerror(i)); | |
769 return ALC_FALSE; | |
770 } | |
771 device->UpdateSize = periodSizeInFrames; | |
772 device->NumUpdates = periods; | |
773 data->thread = StartThread(ALSAProc, device); | |
774 } | |
775 if(data->thread == NULL) | |
776 { | |
777 ERR("Could not create playback thread\n"); | |
778 free(data->buffer); | |
779 data->buffer = NULL; | |
780 return ALC_FALSE; | |
781 } | |
782 | |
783 return ALC_TRUE; | |
784 } | |
785 | |
786 static void alsa_stop_playback(ALCdevice *device) | |
787 { | |
788 alsa_data *data = (alsa_data*)device->ExtraData; | |
789 | |
790 if(data->thread) | |
791 { | |
792 data->killNow = 1; | |
793 StopThread(data->thread); | |
794 data->thread = NULL; | |
795 } | |
796 data->killNow = 0; | |
797 free(data->buffer); | |
798 data->buffer = NULL; | |
799 } | |
800 | |
801 | |
802 static ALCboolean alsa_open_capture(ALCdevice *pDevice, const ALCchar *deviceName) | |
803 { | |
804 snd_pcm_hw_params_t *p; | |
805 snd_pcm_uframes_t bufferSizeInFrames; | |
806 snd_pcm_format_t format; | |
807 ALuint frameSize; | |
808 alsa_data *data; | |
809 char driver[128]; | |
810 char *err; | |
811 int i; | |
812 | |
813 strncpy(driver, GetConfigValue("alsa", "capture", "default"), sizeof(driver)-1); | |
814 driver[sizeof(driver)-1] = 0; | |
815 | |
816 if(!allCaptureDevNameMap) | |
817 allCaptureDevNameMap = probe_devices(SND_PCM_STREAM_CAPTURE, &numCaptureDevNames); | |
818 | |
819 if(!deviceName) | |
820 deviceName = allCaptureDevNameMap[0].name; | |
821 else | |
822 { | |
823 size_t idx; | |
824 | |
825 for(idx = 0;idx < numCaptureDevNames;idx++) | |
826 { | |
827 if(allCaptureDevNameMap[idx].name && | |
828 strcmp(deviceName, allCaptureDevNameMap[idx].name) == 0) | |
829 { | |
830 if(idx > 0) | |
831 snprintf(driver, sizeof(driver), "%sCARD=%s,DEV=%d", capture_prefix, | |
832 allCaptureDevNameMap[idx].card, allCaptureDevNameMap[idx].dev); | |
833 break; | |
834 } | |
835 } | |
836 if(idx == numCaptureDevNames) | |
837 return ALC_FALSE; | |
838 } | |
839 | |
840 data = (alsa_data*)calloc(1, sizeof(alsa_data)); | |
841 | |
842 i = snd_pcm_open(&data->pcmHandle, driver, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK); | |
843 if(i < 0) | |
844 { | |
845 ERR("Could not open capture device '%s': %s\n", driver, snd_strerror(i)); | |
846 free(data); | |
847 return ALC_FALSE; | |
848 } | |
849 | |
850 format = -1; | |
851 switch(pDevice->FmtType) | |
852 { | |
853 case DevFmtByte: | |
854 format = SND_PCM_FORMAT_S8; | |
855 break; | |
856 case DevFmtUByte: | |
857 format = SND_PCM_FORMAT_U8; | |
858 break; | |
859 case DevFmtShort: | |
860 format = SND_PCM_FORMAT_S16; | |
861 break; | |
862 case DevFmtUShort: | |
863 format = SND_PCM_FORMAT_U16; | |
864 break; | |
865 case DevFmtFloat: | |
866 format = SND_PCM_FORMAT_FLOAT; | |
867 break; | |
868 } | |
869 | |
870 err = NULL; | |
871 bufferSizeInFrames = pDevice->UpdateSize * pDevice->NumUpdates; | |
872 snd_pcm_hw_params_malloc(&p); | |
873 | |
874 if((i=snd_pcm_hw_params_any(data->pcmHandle, p)) < 0) | |
875 err = "any"; | |
876 /* set interleaved access */ | |
877 if(i >= 0 && (i=snd_pcm_hw_params_set_access(data->pcmHandle, p, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) | |
878 err = "set access"; | |
879 /* set format (implicitly sets sample bits) */ | |
880 if(i >= 0 && (i=snd_pcm_hw_params_set_format(data->pcmHandle, p, format)) < 0) | |
881 err = "set format"; | |
882 /* set channels (implicitly sets frame bits) */ | |
883 if(i >= 0 && (i=snd_pcm_hw_params_set_channels(data->pcmHandle, p, ChannelsFromDevFmt(pDevice->FmtChans))) < 0) | |
884 err = "set channels"; | |
885 /* set rate (implicitly constrains period/buffer parameters) */ | |
886 if(i >= 0 && (i=snd_pcm_hw_params_set_rate(data->pcmHandle, p, pDevice->Frequency, 0)) < 0) | |
887 err = "set rate near"; | |
888 /* set buffer size in frame units (implicitly sets period size/bytes/time and buffer time/bytes) */ | |
889 if(i >= 0 && (i=snd_pcm_hw_params_set_buffer_size_near(data->pcmHandle, p, &bufferSizeInFrames)) < 0) | |
890 err = "set buffer size near"; | |
891 /* install and prepare hardware configuration */ | |
892 if(i >= 0 && (i=snd_pcm_hw_params(data->pcmHandle, p)) < 0) | |
893 err = "set params"; | |
894 if(i < 0) | |
895 { | |
896 ERR("%s failed: %s\n", err, snd_strerror(i)); | |
897 snd_pcm_hw_params_free(p); | |
898 goto error; | |
899 } | |
900 | |
901 if((i=snd_pcm_hw_params_get_period_size(p, &bufferSizeInFrames, NULL)) < 0) | |
902 { | |
903 ERR("get size failed: %s\n", snd_strerror(i)); | |
904 snd_pcm_hw_params_free(p); | |
905 goto error; | |
906 } | |
907 | |
908 snd_pcm_hw_params_free(p); | |
909 | |
910 frameSize = FrameSizeFromDevFmt(pDevice->FmtChans, pDevice->FmtType); | |
911 | |
912 data->ring = CreateRingBuffer(frameSize, pDevice->UpdateSize*pDevice->NumUpdates); | |
913 if(!data->ring) | |
914 { | |
915 ERR("ring buffer create failed\n"); | |
916 goto error; | |
917 } | |
918 | |
919 data->size = snd_pcm_frames_to_bytes(data->pcmHandle, bufferSizeInFrames); | |
920 data->buffer = malloc(data->size); | |
921 if(!data->buffer) | |
922 { | |
923 ERR("buffer malloc failed\n"); | |
924 goto error; | |
925 } | |
926 | |
927 pDevice->szDeviceName = strdup(deviceName); | |
928 | |
929 pDevice->ExtraData = data; | |
930 return ALC_TRUE; | |
931 | |
932 error: | |
933 free(data->buffer); | |
934 DestroyRingBuffer(data->ring); | |
935 snd_pcm_close(data->pcmHandle); | |
936 free(data); | |
937 | |
938 pDevice->ExtraData = NULL; | |
939 return ALC_FALSE; | |
940 } | |
941 | |
942 static void alsa_close_capture(ALCdevice *pDevice) | |
943 { | |
944 alsa_data *data = (alsa_data*)pDevice->ExtraData; | |
945 | |
946 snd_pcm_close(data->pcmHandle); | |
947 DestroyRingBuffer(data->ring); | |
948 | |
949 free(data->buffer); | |
950 free(data); | |
951 pDevice->ExtraData = NULL; | |
952 } | |
953 | |
954 static void alsa_start_capture(ALCdevice *Device) | |
955 { | |
956 alsa_data *data = (alsa_data*)Device->ExtraData; | |
957 int err; | |
958 | |
959 err = snd_pcm_start(data->pcmHandle); | |
960 if(err < 0) | |
961 { | |
962 ERR("start failed: %s\n", snd_strerror(err)); | |
963 aluHandleDisconnect(Device); | |
964 } | |
965 else | |
966 data->doCapture = AL_TRUE; | |
967 } | |
968 | |
969 static void alsa_stop_capture(ALCdevice *Device) | |
970 { | |
971 alsa_data *data = (alsa_data*)Device->ExtraData; | |
972 snd_pcm_drain(data->pcmHandle); | |
973 data->doCapture = AL_FALSE; | |
974 } | |
975 | |
976 static ALCuint alsa_available_samples(ALCdevice *Device) | |
977 { | |
978 alsa_data *data = (alsa_data*)Device->ExtraData; | |
979 snd_pcm_sframes_t avail; | |
980 | |
981 avail = (Device->Connected ? snd_pcm_avail_update(data->pcmHandle) : 0); | |
982 if(avail < 0) | |
983 { | |
984 ERR("avail update failed: %s\n", snd_strerror(avail)); | |
985 | |
986 if((avail=snd_pcm_recover(data->pcmHandle, avail, 1)) >= 0) | |
987 { | |
988 if(data->doCapture) | |
989 avail = snd_pcm_start(data->pcmHandle); | |
990 if(avail >= 0) | |
991 avail = snd_pcm_avail_update(data->pcmHandle); | |
992 } | |
993 if(avail < 0) | |
994 { | |
995 ERR("restore error: %s\n", snd_strerror(avail)); | |
996 aluHandleDisconnect(Device); | |
997 } | |
998 } | |
999 while(avail > 0) | |
1000 { | |
1001 snd_pcm_sframes_t amt; | |
1002 | |
1003 amt = snd_pcm_bytes_to_frames(data->pcmHandle, data->size); | |
1004 if(avail < amt) amt = avail; | |
1005 | |
1006 amt = snd_pcm_readi(data->pcmHandle, data->buffer, amt); | |
1007 if(amt < 0) | |
1008 { | |
1009 ERR("read error: %s\n", snd_strerror(amt)); | |
1010 | |
1011 if(amt == -EAGAIN) | |
1012 continue; | |
1013 if((amt=snd_pcm_recover(data->pcmHandle, amt, 1)) >= 0) | |
1014 { | |
1015 if(data->doCapture) | |
1016 amt = snd_pcm_start(data->pcmHandle); | |
1017 if(amt >= 0) | |
1018 amt = snd_pcm_avail_update(data->pcmHandle); | |
1019 } | |
1020 if(amt < 0) | |
1021 { | |
1022 ERR("restore error: %s\n", snd_strerror(amt)); | |
1023 aluHandleDisconnect(Device); | |
1024 break; | |
1025 } | |
1026 avail = amt; | |
1027 continue; | |
1028 } | |
1029 | |
1030 WriteRingBuffer(data->ring, data->buffer, amt); | |
1031 avail -= amt; | |
1032 } | |
1033 | |
1034 return RingBufferSize(data->ring); | |
1035 } | |
1036 | |
1037 static void alsa_capture_samples(ALCdevice *Device, ALCvoid *Buffer, ALCuint Samples) | |
1038 { | |
1039 alsa_data *data = (alsa_data*)Device->ExtraData; | |
1040 | |
1041 if(Samples <= alsa_available_samples(Device)) | |
1042 ReadRingBuffer(data->ring, Buffer, Samples); | |
1043 else | |
1044 alcSetError(Device, ALC_INVALID_VALUE); | |
1045 } | |
1046 | |
1047 | |
1048 static const BackendFuncs alsa_funcs = { | |
1049 alsa_open_playback, | |
1050 alsa_close_playback, | |
1051 alsa_reset_playback, | |
1052 alsa_stop_playback, | |
1053 alsa_open_capture, | |
1054 alsa_close_capture, | |
1055 alsa_start_capture, | |
1056 alsa_stop_capture, | |
1057 alsa_capture_samples, | |
1058 alsa_available_samples | |
1059 }; | |
1060 | |
1061 ALCboolean alc_alsa_init(BackendFuncs *func_list) | |
1062 { | |
1063 if(!alsa_load()) | |
1064 return ALC_FALSE; | |
1065 device_prefix = GetConfigValue("alsa", "device-prefix", "plughw:"); | |
1066 capture_prefix = GetConfigValue("alsa", "capture-prefix", "plughw:"); | |
1067 *func_list = alsa_funcs; | |
1068 return ALC_TRUE; | |
1069 } | |
1070 | |
1071 void alc_alsa_deinit(void) | |
1072 { | |
1073 ALuint i; | |
1074 | |
1075 for(i = 0;i < numDevNames;++i) | |
1076 { | |
1077 free(allDevNameMap[i].name); | |
1078 free(allDevNameMap[i].card); | |
1079 } | |
1080 free(allDevNameMap); | |
1081 allDevNameMap = NULL; | |
1082 numDevNames = 0; | |
1083 | |
1084 for(i = 0;i < numCaptureDevNames;++i) | |
1085 { | |
1086 free(allCaptureDevNameMap[i].name); | |
1087 free(allCaptureDevNameMap[i].card); | |
1088 } | |
1089 free(allCaptureDevNameMap); | |
1090 allCaptureDevNameMap = NULL; | |
1091 numCaptureDevNames = 0; | |
1092 | |
1093 #ifdef HAVE_DYNLOAD | |
1094 if(alsa_handle) | |
1095 CloseLib(alsa_handle); | |
1096 alsa_handle = NULL; | |
1097 #endif | |
1098 } | |
1099 | |
1100 void alc_alsa_probe(enum DevProbe type) | |
1101 { | |
1102 ALuint i; | |
1103 | |
1104 switch(type) | |
1105 { | |
1106 case DEVICE_PROBE: | |
1107 AppendDeviceList(alsaDevice); | |
1108 break; | |
1109 | |
1110 case ALL_DEVICE_PROBE: | |
1111 for(i = 0;i < numDevNames;++i) | |
1112 { | |
1113 free(allDevNameMap[i].name); | |
1114 free(allDevNameMap[i].card); | |
1115 } | |
1116 | |
1117 free(allDevNameMap); | |
1118 allDevNameMap = probe_devices(SND_PCM_STREAM_PLAYBACK, &numDevNames); | |
1119 | |
1120 for(i = 0;i < numDevNames;++i) | |
1121 AppendAllDeviceList(allDevNameMap[i].name); | |
1122 break; | |
1123 | |
1124 case CAPTURE_DEVICE_PROBE: | |
1125 for(i = 0;i < numCaptureDevNames;++i) | |
1126 { | |
1127 free(allCaptureDevNameMap[i].name); | |
1128 free(allCaptureDevNameMap[i].card); | |
1129 } | |
1130 | |
1131 free(allCaptureDevNameMap); | |
1132 allCaptureDevNameMap = probe_devices(SND_PCM_STREAM_CAPTURE, &numCaptureDevNames); | |
1133 | |
1134 for(i = 0;i < numCaptureDevNames;++i) | |
1135 AppendCaptureDeviceList(allCaptureDevNameMap[i].name); | |
1136 break; | |
1137 } | |
1138 } |