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