diff options
Diffstat (limited to 'apps/plugins/pdbox/PDa/src/s_audio_alsa.c')
-rw-r--r-- | apps/plugins/pdbox/PDa/src/s_audio_alsa.c | 1890 |
1 files changed, 1890 insertions, 0 deletions
diff --git a/apps/plugins/pdbox/PDa/src/s_audio_alsa.c b/apps/plugins/pdbox/PDa/src/s_audio_alsa.c new file mode 100644 index 0000000000..87d7cb929b --- /dev/null +++ b/apps/plugins/pdbox/PDa/src/s_audio_alsa.c | |||
@@ -0,0 +1,1890 @@ | |||
1 | /* Copyright (c) 1997-2003 Guenter Geiger, Miller Puckette, Larry Troxler, | ||
2 | * Winfried Ritsch, Karl MacMillan, and others. | ||
3 | * For information on usage and redistribution, and for a DISCLAIMER OF ALL | ||
4 | * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ | ||
5 | |||
6 | /* this file inputs and outputs audio using the ALSA API available on linux. */ | ||
7 | |||
8 | #include <alsa/asoundlib.h> | ||
9 | |||
10 | #include "m_pd.h" | ||
11 | #include "s_stuff.h" | ||
12 | #include <errno.h> | ||
13 | #include <stdio.h> | ||
14 | #include <unistd.h> | ||
15 | #include <stdlib.h> | ||
16 | #include <string.h> | ||
17 | #include <sys/types.h> | ||
18 | #include <sys/time.h> | ||
19 | #include <sys/stat.h> | ||
20 | #include <sys/ioctl.h> | ||
21 | #include <fcntl.h> | ||
22 | #include <sched.h> | ||
23 | #include <sys/mman.h> | ||
24 | |||
25 | typedef int16_t t_alsa_sample16; | ||
26 | typedef int32_t t_alsa_sample32; | ||
27 | #define ALSA_SAMPLEWIDTH_16 sizeof(t_alsa_sample16) | ||
28 | #define ALSA_SAMPLEWIDTH_32 sizeof(t_alsa_sample32) | ||
29 | #define ALSA_XFERSIZE16 (signed int)(sizeof(t_alsa_sample16) * DEFDACBLKSIZE) | ||
30 | #define ALSA_XFERSIZE32 (signed int)(sizeof(t_alsa_sample32) * DEFDACBLKSIZE) | ||
31 | #define ALSA_MAXDEV 1 | ||
32 | #define ALSA_JITTER 1024 | ||
33 | #define ALSA_EXTRABUFFER 2048 | ||
34 | #define ALSA_DEFFRAGSIZE 64 | ||
35 | #define ALSA_DEFNFRAG 12 | ||
36 | |||
37 | #ifndef INT32_MAX | ||
38 | #define INT32_MAX 0x7fffffff | ||
39 | #endif | ||
40 | |||
41 | #if (SND_LIB_MAJOR < 1) | ||
42 | #define ALSAAPI9 | ||
43 | #endif | ||
44 | |||
45 | typedef struct _alsa_dev | ||
46 | { | ||
47 | snd_pcm_t *inhandle; | ||
48 | snd_pcm_t *outhandle; | ||
49 | int innoninterleave; /* true if we're set for noninterleaved read */ | ||
50 | int outnoninterleave; /* same for write */ | ||
51 | } t_alsa_dev; | ||
52 | |||
53 | t_alsa_dev alsa_device; | ||
54 | static void *alsa_snd_buf = 0; | ||
55 | static void **alsa_buf_ptrs; | ||
56 | static int alsa_samplewidth; | ||
57 | static snd_pcm_status_t* in_status; | ||
58 | static snd_pcm_status_t* out_status; | ||
59 | |||
60 | static int alsa_mode; | ||
61 | static int alsa_buf_samps; /* believed actual ALSA bufsize in sample frames */ | ||
62 | static int alsa_inchannels; | ||
63 | static int alsa_outchannels; | ||
64 | |||
65 | /* Defines */ | ||
66 | #define DEBUG(x) x | ||
67 | #define DEBUG2(x) {x;} | ||
68 | |||
69 | static void alsa_checkiosync( void); | ||
70 | static void alsa_numbertoname(int devno, char *devname, int nchar); | ||
71 | |||
72 | /* don't assume we can turn all 31 bits when doing float-to-fix; | ||
73 | otherwise some audio drivers (e.g. Midiman/ALSA) wrap around. */ | ||
74 | #define FMAX 0x7ffff000 | ||
75 | #define CLIP32(x) (((x)>FMAX)?FMAX:((x) < -FMAX)?-FMAX:(x)) | ||
76 | |||
77 | /* support for ALSA pcmv2 api by Karl MacMillan<karlmac@peabody.jhu.edu> */ | ||
78 | |||
79 | static void check_error(int err, const char *why) | ||
80 | { | ||
81 | if (err < 0) | ||
82 | fprintf(stderr, "%s: %s\n", why, snd_strerror(err)); | ||
83 | } | ||
84 | |||
85 | /* was: alsa_open_audio(int wantinchans, int wantoutchans, int srate) */ | ||
86 | |||
87 | int alsa_open_audio(int naudioindev, int *audioindev, int nchindev, | ||
88 | int *chindev, int naudiooutdev, int *audiooutdev, int nchoutdev, | ||
89 | int *choutdev, int rate) | ||
90 | { | ||
91 | int err, inchans = 0, outchans = 0, subunitdir; | ||
92 | char devname[512]; | ||
93 | snd_pcm_hw_params_t* hw_params; | ||
94 | snd_pcm_sw_params_t* sw_params; | ||
95 | snd_output_t* out; | ||
96 | int frag_size = (sys_blocksize ? sys_blocksize : ALSA_DEFFRAGSIZE); | ||
97 | int nfrags, i; | ||
98 | short* tmp_buf; | ||
99 | unsigned int tmp_uint; | ||
100 | snd_pcm_uframes_t tmp_snd_pcm_uframes; | ||
101 | int wantinchans, wantoutchans, devno; | ||
102 | |||
103 | if (naudioindev >= 2 || naudiooutdev >= 2) | ||
104 | post("alsa: only one input and output device allowed (extras ignored"); | ||
105 | if (naudioindev >= 1 && naudiooutdev >= 1 && | ||
106 | audioindev[0] != audiooutdev[0]) | ||
107 | post("alsa: changing output device to agree with input device"); | ||
108 | if (nchindev) | ||
109 | wantinchans = chindev[0]; | ||
110 | else wantinchans = (naudioindev ? 2 : 0); | ||
111 | if (nchoutdev) | ||
112 | wantoutchans = choutdev[0]; | ||
113 | else wantoutchans = (naudiooutdev ? 2 : 0); | ||
114 | devno = (naudioindev > 0 ? audioindev[0] : | ||
115 | (naudiooutdev > 0 ? audiooutdev[0] : 0)); | ||
116 | |||
117 | alsa_numbertoname(devno, devname, 512); | ||
118 | |||
119 | if (sys_verbose) | ||
120 | post("device name %s; channels in %d, out %d", devname, wantinchans, | ||
121 | wantoutchans); | ||
122 | |||
123 | nfrags = sys_schedadvance * (float)rate / (1e6 * frag_size); | ||
124 | /* save our belief as to ALSA's buffer size for later */ | ||
125 | alsa_buf_samps = nfrags * frag_size; | ||
126 | |||
127 | if (sys_verbose) | ||
128 | post("audio buffer set to %d", (int)(0.001 * sys_schedadvance)); | ||
129 | |||
130 | alsa_device.innoninterleave = alsa_device.outnoninterleave = 0; | ||
131 | if (wantinchans) | ||
132 | { | ||
133 | err = snd_pcm_open(&alsa_device.inhandle, devname, | ||
134 | SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK); | ||
135 | |||
136 | check_error(err, "snd_pcm_open (input)"); | ||
137 | if (err < 0) | ||
138 | inchans = 0; | ||
139 | else | ||
140 | { | ||
141 | inchans = wantinchans; | ||
142 | snd_pcm_nonblock(alsa_device.inhandle, 1); | ||
143 | } | ||
144 | } | ||
145 | if (wantoutchans) | ||
146 | { | ||
147 | err = snd_pcm_open(&alsa_device.outhandle, devname, | ||
148 | SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); | ||
149 | |||
150 | check_error(err, "snd_pcm_open (output)"); | ||
151 | if (err < 0) | ||
152 | outchans = 0; | ||
153 | else | ||
154 | { | ||
155 | outchans = wantoutchans; | ||
156 | snd_pcm_nonblock(alsa_device.outhandle, 1); | ||
157 | } | ||
158 | } | ||
159 | if (inchans) | ||
160 | { | ||
161 | if (sys_verbose) | ||
162 | post("opening sound input..."); | ||
163 | err = snd_pcm_hw_params_malloc(&hw_params); | ||
164 | check_error(err, "snd_pcm_hw_params_malloc (input)"); | ||
165 | |||
166 | // get the default params | ||
167 | err = snd_pcm_hw_params_any(alsa_device.inhandle, hw_params); | ||
168 | check_error(err, "snd_pcm_hw_params_any (input)"); | ||
169 | |||
170 | /* try to set interleaved access */ | ||
171 | err = snd_pcm_hw_params_set_access(alsa_device.inhandle, | ||
172 | hw_params, SND_PCM_ACCESS_RW_INTERLEAVED); | ||
173 | if (err < 0) | ||
174 | { | ||
175 | /* OK, so try non-interleaved */ | ||
176 | err = snd_pcm_hw_params_set_access(alsa_device.inhandle, | ||
177 | hw_params, SND_PCM_ACCESS_RW_NONINTERLEAVED); | ||
178 | if (err >= 0) | ||
179 | { | ||
180 | post("using non-interleaved audio input"); | ||
181 | alsa_device.innoninterleave = 1; | ||
182 | } | ||
183 | } | ||
184 | check_error(err, "snd_pcm_hw_params_set_access (input)"); | ||
185 | // Try to set 32 bit format first | ||
186 | err = snd_pcm_hw_params_set_format(alsa_device.inhandle, hw_params, | ||
187 | SND_PCM_FORMAT_S32); | ||
188 | if (err < 0) | ||
189 | { | ||
190 | /* fprintf(stderr, | ||
191 | "PD-ALSA: 32 bit format not available - using 16\n"); */ | ||
192 | err = snd_pcm_hw_params_set_format(alsa_device.inhandle, hw_params, | ||
193 | SND_PCM_FORMAT_S16); | ||
194 | check_error(err, "snd_pcm_hw_params_set_format (input)"); | ||
195 | alsa_samplewidth = 2; | ||
196 | } | ||
197 | else | ||
198 | { | ||
199 | alsa_samplewidth = 4; | ||
200 | } | ||
201 | post("Sample width set to %d bytes", alsa_samplewidth); | ||
202 | // set the subformat | ||
203 | err = snd_pcm_hw_params_set_subformat(alsa_device.inhandle, hw_params, | ||
204 | SND_PCM_SUBFORMAT_STD); | ||
205 | check_error(err, "snd_pcm_hw_params_set_subformat (input)"); | ||
206 | // set the number of channels | ||
207 | tmp_uint = inchans; | ||
208 | err = snd_pcm_hw_params_set_channels_min(alsa_device.inhandle, | ||
209 | hw_params, &tmp_uint); | ||
210 | check_error(err, "snd_pcm_hw_params_set_channels (input)"); | ||
211 | if (tmp_uint != (unsigned)inchans) | ||
212 | post("ALSA: set input channels to %d", tmp_uint); | ||
213 | inchans = tmp_uint; | ||
214 | // set the sampling rate | ||
215 | err = snd_pcm_hw_params_set_rate_min(alsa_device.inhandle, hw_params, | ||
216 | &rate, 0); | ||
217 | check_error(err, "snd_pcm_hw_params_set_rate_min (input)"); | ||
218 | #if 0 | ||
219 | err = snd_pcm_hw_params_get_rate(hw_params, &subunitdir); | ||
220 | post("input sample rate %d", err); | ||
221 | #endif | ||
222 | // set the period - ie frag size | ||
223 | // post("fragsize a %d", frag_size); | ||
224 | |||
225 | /* LATER try this to get a recommended period size... | ||
226 | right now, it trips an assertion failure in ALSA lib */ | ||
227 | #if 0 | ||
228 | post("input period was %d, min %d, max %d\n", | ||
229 | snd_pcm_hw_params_get_period_size(hw_params, 0), | ||
230 | snd_pcm_hw_params_get_period_size_min(hw_params, 0), | ||
231 | snd_pcm_hw_params_get_period_size_max(hw_params, 0)); | ||
232 | #endif | ||
233 | #ifdef ALSAAPI9 | ||
234 | err = snd_pcm_hw_params_set_period_size_near(alsa_device.inhandle, | ||
235 | hw_params, | ||
236 | (snd_pcm_uframes_t) | ||
237 | frag_size, 0); | ||
238 | #else | ||
239 | tmp_snd_pcm_uframes = frag_size; | ||
240 | err = snd_pcm_hw_params_set_period_size_near(alsa_device.inhandle, | ||
241 | hw_params, &tmp_snd_pcm_uframes, 0); | ||
242 | #endif | ||
243 | check_error(err, "snd_pcm_hw_params_set_period_size_near (input)"); | ||
244 | // post("fragsize b %d", frag_size); | ||
245 | // set the number of periods - ie numfrags | ||
246 | // post("nfrags a %d", nfrags); | ||
247 | #ifdef ALSAAPI9 | ||
248 | err = snd_pcm_hw_params_set_periods_near(alsa_device.inhandle, | ||
249 | hw_params, nfrags, 0); | ||
250 | #else | ||
251 | tmp_uint = nfrags; | ||
252 | err = snd_pcm_hw_params_set_periods_near(alsa_device.inhandle, | ||
253 | hw_params, &tmp_uint, 0); | ||
254 | #endif | ||
255 | check_error(err, "snd_pcm_hw_params_set_periods_near (input)"); | ||
256 | // set the buffer size | ||
257 | #ifdef ALSAAPI9 | ||
258 | err = snd_pcm_hw_params_set_buffer_size_near(alsa_device.inhandle, | ||
259 | hw_params, nfrags * frag_size); | ||
260 | #else | ||
261 | tmp_snd_pcm_uframes = nfrags * frag_size; | ||
262 | err = snd_pcm_hw_params_set_buffer_size_near(alsa_device.inhandle, | ||
263 | hw_params, &tmp_snd_pcm_uframes); | ||
264 | #endif | ||
265 | check_error(err, "snd_pcm_hw_params_set_buffer_size_near (input)"); | ||
266 | |||
267 | err = snd_pcm_hw_params(alsa_device.inhandle, hw_params); | ||
268 | check_error(err, "snd_pcm_hw_params (input)"); | ||
269 | |||
270 | snd_pcm_hw_params_free(hw_params); | ||
271 | |||
272 | err = snd_pcm_sw_params_malloc(&sw_params); | ||
273 | check_error(err, "snd_pcm_sw_params_malloc (input)"); | ||
274 | err = snd_pcm_sw_params_current(alsa_device.inhandle, sw_params); | ||
275 | check_error(err, "snd_pcm_sw_params_current (input)"); | ||
276 | err = snd_pcm_sw_params_set_start_threshold(alsa_device.inhandle, | ||
277 | sw_params, nfrags * frag_size); | ||
278 | check_error(err, "snd_pcm_sw_params_set_start_threshold (input)"); | ||
279 | err = snd_pcm_sw_params_set_stop_threshold(alsa_device.inhandle, | ||
280 | sw_params, 0x7fffffff); | ||
281 | check_error(err, "snd_pcm_sw_params_set_stop_threshold (input)"); | ||
282 | err = snd_pcm_sw_params_set_avail_min(alsa_device.inhandle, sw_params, | ||
283 | frag_size); | ||
284 | check_error(err, "snd_pcm_sw_params_set_avail_min (input)"); | ||
285 | err = snd_pcm_sw_params(alsa_device.inhandle, sw_params); | ||
286 | check_error(err, "snd_pcm_sw_params (input)"); | ||
287 | |||
288 | snd_pcm_sw_params_free(sw_params); | ||
289 | |||
290 | snd_output_stdio_attach(&out, stderr, 0); | ||
291 | #if 0 | ||
292 | if (sys_verbose) | ||
293 | { | ||
294 | snd_pcm_dump_hw_setup(alsa_device.inhandle, out); | ||
295 | snd_pcm_dump_sw_setup(alsa_device.inhandle, out); | ||
296 | } | ||
297 | #endif | ||
298 | } | ||
299 | |||
300 | if (outchans) | ||
301 | { | ||
302 | int foo; | ||
303 | if (sys_verbose) | ||
304 | post("opening sound output..."); | ||
305 | err = snd_pcm_hw_params_malloc(&hw_params); | ||
306 | check_error(err, "snd_pcm_sw_params (output)"); | ||
307 | |||
308 | // get the default params | ||
309 | err = snd_pcm_hw_params_any(alsa_device.outhandle, hw_params); | ||
310 | check_error(err, "snd_pcm_hw_params_any (output)"); | ||
311 | // set interleaved access - FIXME deal with other access types | ||
312 | err = snd_pcm_hw_params_set_access(alsa_device.outhandle, hw_params, | ||
313 | SND_PCM_ACCESS_RW_INTERLEAVED); | ||
314 | check_error(err, "snd_pcm_hw_params_set_access (output)"); | ||
315 | |||
316 | /* try to set interleaved access */ | ||
317 | err = snd_pcm_hw_params_set_access(alsa_device.outhandle, | ||
318 | hw_params, SND_PCM_ACCESS_RW_INTERLEAVED); | ||
319 | if (err < 0) | ||
320 | { | ||
321 | /* OK, so try non-interleaved */ | ||
322 | err = snd_pcm_hw_params_set_access(alsa_device.outhandle, | ||
323 | hw_params, SND_PCM_ACCESS_RW_NONINTERLEAVED); | ||
324 | if (err >= 0) | ||
325 | { | ||
326 | post("using non-interleaved audio"); | ||
327 | alsa_device.outnoninterleave = 1; | ||
328 | } | ||
329 | } | ||
330 | check_error(err, "snd_pcm_hw_params_set_access (output)"); | ||
331 | |||
332 | |||
333 | // Try to set 32 bit format first | ||
334 | err = snd_pcm_hw_params_set_format(alsa_device.outhandle, hw_params, | ||
335 | SND_PCM_FORMAT_S32); | ||
336 | if (err < 0) | ||
337 | { | ||
338 | err = snd_pcm_hw_params_set_format(alsa_device.outhandle, | ||
339 | hw_params,SND_PCM_FORMAT_S16); | ||
340 | check_error(err, "snd_pcm_hw_params_set_format (output)"); | ||
341 | /* fprintf(stderr, | ||
342 | "PD-ALSA: 32 bit format not available - using 16\n"); */ | ||
343 | alsa_samplewidth = 2; | ||
344 | } | ||
345 | else | ||
346 | { | ||
347 | alsa_samplewidth = 4; | ||
348 | } | ||
349 | // set the subformat | ||
350 | err = snd_pcm_hw_params_set_subformat(alsa_device.outhandle, hw_params, | ||
351 | SND_PCM_SUBFORMAT_STD); | ||
352 | check_error(err, "snd_pcm_hw_params_set_subformat (output)"); | ||
353 | // set the number of channels | ||
354 | tmp_uint = outchans; | ||
355 | err = snd_pcm_hw_params_set_channels_min(alsa_device.outhandle, | ||
356 | hw_params, &tmp_uint); | ||
357 | check_error(err, "snd_pcm_hw_params_set_channels (output)"); | ||
358 | if (tmp_uint != (unsigned)outchans) | ||
359 | post("alsa: set output channels to %d", tmp_uint); | ||
360 | outchans = tmp_uint; | ||
361 | // set the sampling rate | ||
362 | err = snd_pcm_hw_params_set_rate_min(alsa_device.outhandle, hw_params, | ||
363 | &rate, 0); | ||
364 | check_error(err, "snd_pcm_hw_params_set_rate_min (output)"); | ||
365 | #if 0 | ||
366 | err = snd_pcm_hw_params_get_rate(hw_params, &subunitdir); | ||
367 | post("output sample rate %d", err); | ||
368 | #endif | ||
369 | // set the period - ie frag size | ||
370 | #if 0 | ||
371 | post("output period was %d, min %d, max %d\n", | ||
372 | snd_pcm_hw_params_get_period_size(hw_params, 0), | ||
373 | snd_pcm_hw_params_get_period_size_min(hw_params, 0), | ||
374 | snd_pcm_hw_params_get_period_size_max(hw_params, 0)); | ||
375 | #endif | ||
376 | // post("fragsize c %d", frag_size); | ||
377 | #ifdef ALSAAPI9 | ||
378 | err = snd_pcm_hw_params_set_period_size_near(alsa_device.outhandle, | ||
379 | hw_params, | ||
380 | (snd_pcm_uframes_t) | ||
381 | frag_size, 0); | ||
382 | #else | ||
383 | tmp_snd_pcm_uframes = frag_size; | ||
384 | err = snd_pcm_hw_params_set_period_size_near(alsa_device.outhandle, | ||
385 | hw_params, &tmp_snd_pcm_uframes, 0); | ||
386 | #endif | ||
387 | // post("fragsize d %d", frag_size); | ||
388 | check_error(err, "snd_pcm_hw_params_set_period_size_near (output)"); | ||
389 | // set the number of periods - ie numfrags | ||
390 | #ifdef ALSAAPI9 | ||
391 | err = snd_pcm_hw_params_set_periods_near(alsa_device.outhandle, | ||
392 | hw_params, nfrags, 0); | ||
393 | #else | ||
394 | tmp_uint = nfrags; | ||
395 | err = snd_pcm_hw_params_set_periods_near(alsa_device.outhandle, | ||
396 | hw_params, &tmp_uint, 0); | ||
397 | #endif | ||
398 | check_error(err, "snd_pcm_hw_params_set_periods_near (output)"); | ||
399 | // set the buffer size | ||
400 | #ifdef ALSAAPI9 | ||
401 | err = snd_pcm_hw_params_set_buffer_size_near(alsa_device.outhandle, | ||
402 | hw_params, nfrags * frag_size); | ||
403 | #else | ||
404 | tmp_snd_pcm_uframes = nfrags * frag_size; | ||
405 | err = snd_pcm_hw_params_set_buffer_size_near(alsa_device.outhandle, | ||
406 | hw_params, &tmp_snd_pcm_uframes); | ||
407 | #endif | ||
408 | check_error(err, "snd_pcm_hw_params_set_buffer_size_near (output)"); | ||
409 | |||
410 | err = snd_pcm_hw_params(alsa_device.outhandle, hw_params); | ||
411 | check_error(err, "snd_pcm_hw_params (output)"); | ||
412 | |||
413 | snd_pcm_hw_params_free(hw_params); | ||
414 | |||
415 | err = snd_pcm_sw_params_malloc(&sw_params); | ||
416 | check_error(err, "snd_pcm_sw_params_malloc (output)"); | ||
417 | err = snd_pcm_sw_params_current(alsa_device.outhandle, sw_params); | ||
418 | check_error(err, "snd_pcm_sw_params_current (output)"); | ||
419 | err = snd_pcm_sw_params_set_start_threshold(alsa_device.outhandle, | ||
420 | sw_params, nfrags * frag_size); | ||
421 | check_error(err, "snd_pcm_sw_params_set_start_threshold (output)"); | ||
422 | err = snd_pcm_sw_params_set_stop_threshold(alsa_device.outhandle, | ||
423 | sw_params, 0x7fffffff); | ||
424 | check_error(err, "snd_pcm_sw_params_set_stop_threshold (output)"); | ||
425 | err = snd_pcm_sw_params_set_avail_min(alsa_device.outhandle, sw_params, | ||
426 | frag_size); | ||
427 | check_error(err, "snd_pcm_sw_params_set_avail_min (output)"); | ||
428 | err = snd_pcm_sw_params(alsa_device.outhandle, sw_params); | ||
429 | check_error(err, "snd_pcm_sw_params (output)"); | ||
430 | snd_pcm_sw_params_free(sw_params); | ||
431 | |||
432 | snd_output_stdio_attach(&out, stderr, 0); | ||
433 | #if 0 | ||
434 | if (sys_verbose) | ||
435 | { | ||
436 | snd_pcm_dump_hw_setup(alsa_device.outhandle, out); | ||
437 | snd_pcm_dump_sw_setup(alsa_device.outhandle, out); | ||
438 | } | ||
439 | #endif | ||
440 | } | ||
441 | |||
442 | if (inchans) | ||
443 | snd_pcm_prepare(alsa_device.inhandle); | ||
444 | if (outchans) | ||
445 | snd_pcm_prepare(alsa_device.outhandle); | ||
446 | |||
447 | // if duplex we can link the channels so they start together | ||
448 | if (inchans && outchans) | ||
449 | snd_pcm_link(alsa_device.inhandle, alsa_device.outhandle); | ||
450 | |||
451 | // set up the status variables | ||
452 | err = snd_pcm_status_malloc(&in_status); | ||
453 | check_error(err, "snd_pcm_status_malloc"); | ||
454 | err = snd_pcm_status_malloc(&out_status); | ||
455 | check_error(err, "snd_pcm_status_malloc"); | ||
456 | |||
457 | // set up the buffer | ||
458 | if (alsa_snd_buf) | ||
459 | free(alsa_snd_buf); | ||
460 | alsa_snd_buf = (void *)malloc( | ||
461 | sizeof(char) * alsa_samplewidth * DEFDACBLKSIZE * | ||
462 | (outchans > inchans ? outchans : inchans)); | ||
463 | memset(alsa_snd_buf, 0, sizeof(char) * alsa_samplewidth * DEFDACBLKSIZE * | ||
464 | (outchans > inchans ? outchans : inchans)); | ||
465 | /* make an array of pointers too in case we need them */ | ||
466 | if (alsa_buf_ptrs) | ||
467 | free(alsa_buf_ptrs); | ||
468 | alsa_buf_ptrs = (void **)malloc( | ||
469 | sizeof(void *) * (outchans > inchans ? outchans : inchans)); | ||
470 | for (i = 0; i < (outchans > inchans ? outchans : inchans); i++) | ||
471 | alsa_buf_ptrs[i] = (t_alsa_sample32 *)alsa_snd_buf + i * DEFDACBLKSIZE; | ||
472 | |||
473 | // fill the buffer with silence | ||
474 | if (outchans) | ||
475 | { | ||
476 | i = (frag_size * nfrags)/DEFDACBLKSIZE + 1; | ||
477 | while (i--) | ||
478 | { | ||
479 | if (alsa_device.outnoninterleave) | ||
480 | snd_pcm_writen(alsa_device.outhandle, alsa_buf_ptrs, | ||
481 | DEFDACBLKSIZE); | ||
482 | else snd_pcm_writei(alsa_device.outhandle, alsa_snd_buf, | ||
483 | DEFDACBLKSIZE); | ||
484 | } | ||
485 | /* confused about this: */ | ||
486 | /* if ((err = snd_pcm_start(alsa_device.outhandle) < 0)) | ||
487 | check_error(err, "output start failed\n"); */ | ||
488 | } | ||
489 | else if (inchans) | ||
490 | { | ||
491 | if (snd_pcm_start(alsa_device.inhandle) < 0) | ||
492 | check_error(err, "input start failed\n"); | ||
493 | } | ||
494 | alsa_outchannels = outchans; | ||
495 | alsa_inchannels = inchans; | ||
496 | |||
497 | return (!(inchans || outchans)); | ||
498 | } | ||
499 | |||
500 | void alsa_close_audio(void) | ||
501 | { | ||
502 | int err; | ||
503 | if (alsa_inchannels) | ||
504 | { | ||
505 | err = snd_pcm_close(alsa_device.inhandle); | ||
506 | check_error(err, "snd_pcm_close (input)"); | ||
507 | } | ||
508 | if (alsa_outchannels) | ||
509 | { | ||
510 | err = snd_pcm_close(alsa_device.outhandle); | ||
511 | check_error(err, "snd_pcm_close (output)"); | ||
512 | } | ||
513 | } | ||
514 | |||
515 | // #define DEBUG_ALSA_XFER | ||
516 | |||
517 | int alsa_send_dacs(void) | ||
518 | { | ||
519 | static int16_t *sp; | ||
520 | static int xferno = 0; | ||
521 | static int callno = 0; | ||
522 | static double timenow; | ||
523 | double timelast; | ||
524 | t_sample *fp, *fp1, *fp2; | ||
525 | int i, j, k, err, devno = 0; | ||
526 | int inputcount = 0, outputcount = 0, inputlate = 0, outputlate = 0; | ||
527 | int result; | ||
528 | int inchannels = (sys_inchannels > alsa_inchannels ? | ||
529 | alsa_inchannels : sys_inchannels); | ||
530 | int outchannels = (sys_outchannels > alsa_outchannels ? | ||
531 | alsa_outchannels : sys_outchannels); | ||
532 | unsigned int intransfersize = DEFDACBLKSIZE; | ||
533 | unsigned int outtransfersize = DEFDACBLKSIZE; | ||
534 | |||
535 | // get the status | ||
536 | if (!inchannels && !outchannels) | ||
537 | { | ||
538 | return SENDDACS_NO; | ||
539 | } | ||
540 | |||
541 | timelast = timenow; | ||
542 | timenow = sys_getrealtime(); | ||
543 | |||
544 | #ifdef DEBUG_ALSA_XFER | ||
545 | if (timenow - timelast > 0.050) | ||
546 | fprintf(stderr, "(%d)", | ||
547 | (int)(1000 * (timenow - timelast))), fflush(stderr); | ||
548 | #endif | ||
549 | |||
550 | callno++; | ||
551 | |||
552 | alsa_checkiosync(); /* check I/O are in sync and data not late */ | ||
553 | |||
554 | if (alsa_inchannels) | ||
555 | { | ||
556 | snd_pcm_status(alsa_device.inhandle, in_status); | ||
557 | if (snd_pcm_status_get_avail(in_status) < intransfersize) | ||
558 | return SENDDACS_NO; | ||
559 | } | ||
560 | if (alsa_outchannels) | ||
561 | { | ||
562 | snd_pcm_status(alsa_device.outhandle, out_status); | ||
563 | if (snd_pcm_status_get_avail(out_status) < outtransfersize) | ||
564 | return SENDDACS_NO; | ||
565 | } | ||
566 | |||
567 | /* do output */ | ||
568 | if (alsa_outchannels) | ||
569 | { | ||
570 | fp = sys_soundout; | ||
571 | if (alsa_samplewidth == 4) | ||
572 | { | ||
573 | if (alsa_device.outnoninterleave) | ||
574 | { | ||
575 | int n = outchannels * DEFDACBLKSIZE; | ||
576 | for (i = 0, fp1 = fp; i < n; i++) | ||
577 | { | ||
578 | float s1 = *fp1 * INT32_MAX; | ||
579 | ((t_alsa_sample32 *)alsa_snd_buf)[i] = CLIP32(s1); | ||
580 | } | ||
581 | n = alsa_outchannels * DEFDACBLKSIZE; | ||
582 | for (; i < n; i++) | ||
583 | ((t_alsa_sample32 *)alsa_snd_buf)[i] = 0; | ||
584 | } | ||
585 | else | ||
586 | { | ||
587 | for (i = 0, fp1 = fp; i < outchannels; i++, | ||
588 | fp1 += DEFDACBLKSIZE) | ||
589 | { | ||
590 | for (j = i, k = DEFDACBLKSIZE, fp2 = fp1; k--; | ||
591 | j += alsa_outchannels, fp2++) | ||
592 | { | ||
593 | float s1 = *fp2 * INT32_MAX; | ||
594 | ((t_alsa_sample32 *)alsa_snd_buf)[j] = CLIP32(s1); | ||
595 | } | ||
596 | } | ||
597 | } | ||
598 | } | ||
599 | else | ||
600 | { | ||
601 | for (i = 0, fp1 = fp; i < outchannels; i++, fp1 += DEFDACBLKSIZE) | ||
602 | { | ||
603 | for (j = i, k = DEFDACBLKSIZE, fp2 = fp1; k--; | ||
604 | j += alsa_outchannels, fp2++) | ||
605 | { | ||
606 | int s = *fp2 * 32767.; | ||
607 | if (s > 32767) | ||
608 | s = 32767; | ||
609 | else if (s < -32767) | ||
610 | s = -32767; | ||
611 | ((t_alsa_sample16 *)alsa_snd_buf)[j] = s; | ||
612 | } | ||
613 | } | ||
614 | } | ||
615 | |||
616 | if (alsa_device.outnoninterleave) | ||
617 | result = snd_pcm_writen(alsa_device.outhandle, alsa_buf_ptrs, | ||
618 | outtransfersize); | ||
619 | else result = snd_pcm_writei(alsa_device.outhandle, alsa_snd_buf, | ||
620 | outtransfersize); | ||
621 | |||
622 | if (result != (int)outtransfersize) | ||
623 | { | ||
624 | #ifdef DEBUG_ALSA_XFER | ||
625 | if (result >= 0 || errno == EAGAIN) | ||
626 | fprintf(stderr, "ALSA: write returned %d of %d\n", | ||
627 | result, outtransfersize); | ||
628 | else fprintf(stderr, "ALSA: write: %s\n", | ||
629 | snd_strerror(errno)); | ||
630 | fprintf(stderr, | ||
631 | "inputcount %d, outputcount %d, outbufsize %d\n", | ||
632 | inputcount, outputcount, | ||
633 | (ALSA_EXTRABUFFER + sys_advance_samples) | ||
634 | * alsa_samplewidth * outchannels); | ||
635 | #endif | ||
636 | sys_log_error(ERR_DACSLEPT); | ||
637 | return (SENDDACS_NO); | ||
638 | } | ||
639 | |||
640 | /* zero out the output buffer */ | ||
641 | memset(sys_soundout, 0, DEFDACBLKSIZE * sizeof(*sys_soundout) * | ||
642 | sys_outchannels); | ||
643 | if (sys_getrealtime() - timenow > 0.002) | ||
644 | { | ||
645 | #ifdef DEBUG_ALSA_XFER | ||
646 | fprintf(stderr, "output %d took %d msec\n", | ||
647 | callno, (int)(1000 * (timenow - timelast))), fflush(stderr); | ||
648 | #endif | ||
649 | timenow = sys_getrealtime(); | ||
650 | sys_log_error(ERR_DACSLEPT); | ||
651 | } | ||
652 | } | ||
653 | /* do input */ | ||
654 | if (alsa_inchannels) | ||
655 | { | ||
656 | if (alsa_device.innoninterleave) | ||
657 | result = snd_pcm_readn(alsa_device.inhandle, alsa_buf_ptrs, | ||
658 | intransfersize); | ||
659 | else result = snd_pcm_readi(alsa_device.inhandle, alsa_snd_buf, | ||
660 | intransfersize); | ||
661 | if (result < (int)intransfersize) | ||
662 | { | ||
663 | #ifdef DEBUG_ALSA_XFER | ||
664 | if (result < 0) | ||
665 | fprintf(stderr, | ||
666 | "snd_pcm_read %d %d: %s\n", | ||
667 | callno, xferno, snd_strerror(errno)); | ||
668 | else fprintf(stderr, | ||
669 | "snd_pcm_read %d %d returned only %d\n", | ||
670 | callno, xferno, result); | ||
671 | fprintf(stderr, | ||
672 | "inputcount %d, outputcount %d, inbufsize %d\n", | ||
673 | inputcount, outputcount, | ||
674 | (ALSA_EXTRABUFFER + sys_advance_samples) | ||
675 | * alsa_samplewidth * inchannels); | ||
676 | #endif | ||
677 | sys_log_error(ERR_ADCSLEPT); | ||
678 | return (SENDDACS_NO); | ||
679 | } | ||
680 | fp = sys_soundin; | ||
681 | if (alsa_samplewidth == 4) | ||
682 | { | ||
683 | if (alsa_device.innoninterleave) | ||
684 | { | ||
685 | int n = inchannels * DEFDACBLKSIZE; | ||
686 | for (i = 0, fp1 = fp; i < n; i++) | ||
687 | *fp1 = (float) ((t_alsa_sample32 *)alsa_snd_buf)[i] | ||
688 | * (1./ INT32_MAX); | ||
689 | } | ||
690 | else | ||
691 | { | ||
692 | for (i = 0, fp1 = fp; i < inchannels; | ||
693 | i++, fp1 += DEFDACBLKSIZE) | ||
694 | { | ||
695 | for (j = i, k = DEFDACBLKSIZE, fp2 = fp1; k--; | ||
696 | j += alsa_inchannels, fp2++) | ||
697 | *fp2 = (float) ((t_alsa_sample32 *)alsa_snd_buf)[j] | ||
698 | * (1./ INT32_MAX); | ||
699 | } | ||
700 | } | ||
701 | } | ||
702 | else | ||
703 | { | ||
704 | for (i = 0, fp1 = fp; i < inchannels; i++, fp1 += DEFDACBLKSIZE) | ||
705 | { | ||
706 | for (j = i, k = DEFDACBLKSIZE, fp2 = fp1; k--; | ||
707 | j += alsa_inchannels, fp2++) | ||
708 | *fp2 = (float) ((t_alsa_sample16 *)alsa_snd_buf)[j] | ||
709 | * 3.051850e-05; | ||
710 | } | ||
711 | } | ||
712 | } | ||
713 | xferno++; | ||
714 | if (sys_getrealtime() - timenow > 0.002) | ||
715 | { | ||
716 | #ifdef DEBUG_ALSA_XFER | ||
717 | fprintf(stderr, "routine took %d msec\n", | ||
718 | (int)(1000 * (sys_getrealtime() - timenow))); | ||
719 | #endif | ||
720 | sys_log_error(ERR_ADCSLEPT); | ||
721 | } | ||
722 | return SENDDACS_YES; | ||
723 | } | ||
724 | |||
725 | void alsa_printstate( void) | ||
726 | { | ||
727 | int i, result; | ||
728 | snd_pcm_sframes_t indelay, outdelay; | ||
729 | if (sys_audioapi != API_ALSA) | ||
730 | { | ||
731 | error("restart-audio: implemented for ALSA only."); | ||
732 | return; | ||
733 | } | ||
734 | if (sys_inchannels) | ||
735 | { | ||
736 | result = snd_pcm_delay(alsa_device.inhandle, &indelay); | ||
737 | if (result < 0) | ||
738 | post("snd_pcm_delay 1 failed"); | ||
739 | else post("in delay %d", indelay); | ||
740 | } | ||
741 | if (sys_outchannels) | ||
742 | { | ||
743 | result = snd_pcm_delay(alsa_device.outhandle, &outdelay); | ||
744 | if (result < 0) | ||
745 | post("snd_pcm_delay 2 failed"); | ||
746 | else post("out delay %d", outdelay); | ||
747 | } | ||
748 | post("sum %d (%d mod 64)\n", indelay + outdelay, (indelay+outdelay)%64); | ||
749 | |||
750 | post("buf samples %d", alsa_buf_samps); | ||
751 | } | ||
752 | |||
753 | |||
754 | void alsa_resync( void) | ||
755 | { | ||
756 | int i, result; | ||
757 | if (sys_audioapi != API_ALSA) | ||
758 | { | ||
759 | error("restart-audio: implemented for ALSA only."); | ||
760 | return; | ||
761 | } | ||
762 | memset(alsa_snd_buf, 0, | ||
763 | sizeof(char) * alsa_samplewidth * DEFDACBLKSIZE * sys_outchannels); | ||
764 | for (i = 0; i < 1000000; i++) | ||
765 | { | ||
766 | if (alsa_device.outnoninterleave) | ||
767 | result = snd_pcm_writen(alsa_device.outhandle, alsa_buf_ptrs, | ||
768 | DEFDACBLKSIZE); | ||
769 | else result = snd_pcm_writei(alsa_device.outhandle, alsa_snd_buf, | ||
770 | DEFDACBLKSIZE); | ||
771 | if (result != (int)DEFDACBLKSIZE) | ||
772 | break; | ||
773 | } | ||
774 | post("%d written", i); | ||
775 | } | ||
776 | |||
777 | void alsa_putzeros(int n) | ||
778 | { | ||
779 | int i, result; | ||
780 | memset(alsa_snd_buf, 0, | ||
781 | sizeof(char) * alsa_samplewidth * DEFDACBLKSIZE * alsa_outchannels); | ||
782 | for (i = 0; i < n; i++) | ||
783 | { | ||
784 | if (alsa_device.outnoninterleave) | ||
785 | result = snd_pcm_writen(alsa_device.outhandle, alsa_buf_ptrs, | ||
786 | DEFDACBLKSIZE); | ||
787 | else result = snd_pcm_writei(alsa_device.outhandle, alsa_snd_buf, | ||
788 | DEFDACBLKSIZE); | ||
789 | #if 0 | ||
790 | if (result != DEFDACBLKSIZE) | ||
791 | post("result %d", result); | ||
792 | #endif | ||
793 | } | ||
794 | /* post ("putzeros %d", n); */ | ||
795 | } | ||
796 | |||
797 | void alsa_getzeros(int n) | ||
798 | { | ||
799 | int i, result; | ||
800 | for (i = 0; i < n; i++) | ||
801 | { | ||
802 | result = snd_pcm_readi(alsa_device.inhandle, alsa_snd_buf, | ||
803 | DEFDACBLKSIZE); | ||
804 | #if 0 | ||
805 | if (result != DEFDACBLKSIZE) | ||
806 | post("result %d", result); | ||
807 | #endif | ||
808 | } | ||
809 | /* post ("getzeros %d", n); */ | ||
810 | } | ||
811 | |||
812 | /* call this only if both input and output are open */ | ||
813 | static void alsa_checkiosync( void) | ||
814 | { | ||
815 | int i, result, checkit = 1, giveup = 1000, alreadylogged = 0; | ||
816 | snd_pcm_sframes_t indelay, outdelay, defect; | ||
817 | |||
818 | if (!(alsa_outchannels && alsa_inchannels)) | ||
819 | return; | ||
820 | while (checkit) | ||
821 | { | ||
822 | checkit = 0; | ||
823 | if (giveup-- <= 0) | ||
824 | return; | ||
825 | result = snd_pcm_delay(alsa_device.outhandle, &outdelay); | ||
826 | if (result < 0) | ||
827 | { | ||
828 | post("output snd_pcm_delay failed: %s", snd_strerror(result)); | ||
829 | if (snd_pcm_status(alsa_device.outhandle, out_status) < 0) | ||
830 | post("output snd_pcm_status failed"); | ||
831 | else post("astate %d", | ||
832 | snd_pcm_status_get_state(out_status)); | ||
833 | return; | ||
834 | } | ||
835 | if (outdelay < 0) | ||
836 | sys_log_error(ERR_DATALATE), alreadylogged = 1; | ||
837 | |||
838 | if (sys_inchannels) | ||
839 | { | ||
840 | result = snd_pcm_delay(alsa_device.inhandle, &indelay); | ||
841 | if (result < 0) | ||
842 | { | ||
843 | post("input snd_pcm_delay failed"); | ||
844 | return; | ||
845 | } | ||
846 | defect = indelay + outdelay - alsa_buf_samps; | ||
847 | if (defect < -(3 * DEFDACBLKSIZE / 2) ) | ||
848 | { | ||
849 | checkit = 1; | ||
850 | alsa_putzeros(1); | ||
851 | if (!alreadylogged) | ||
852 | sys_log_error(ERR_RESYNC), alreadylogged = 1; | ||
853 | } | ||
854 | else if (defect > 0) | ||
855 | { | ||
856 | checkit = 1; | ||
857 | alsa_getzeros(1); | ||
858 | if (!alreadylogged) | ||
859 | sys_log_error(ERR_RESYNC), alreadylogged = 1; | ||
860 | } | ||
861 | /* if (alreadylogged) | ||
862 | post("in %d out %d defect %d", indelay, outdelay, defect); */ | ||
863 | } | ||
864 | } | ||
865 | } | ||
866 | |||
867 | static int alsa_nnames = 0; | ||
868 | static char **alsa_names = 0; | ||
869 | |||
870 | void alsa_adddev(char *name) | ||
871 | { | ||
872 | if (alsa_nnames) | ||
873 | alsa_names = (char **)t_resizebytes(alsa_names, | ||
874 | alsa_nnames * sizeof(char *), | ||
875 | (alsa_nnames+1) * sizeof(char *)); | ||
876 | else alsa_names = (char **)t_getbytes(sizeof(char *)); | ||
877 | alsa_names[alsa_nnames] = gensym(name)->s_name; | ||
878 | alsa_nnames++; | ||
879 | } | ||
880 | |||
881 | static void alsa_numbertoname(int devno, char *devname, int nchar) | ||
882 | { | ||
883 | int ndev = 0, cardno = -1; | ||
884 | while (!snd_card_next(&cardno) && cardno >= 0) | ||
885 | ndev++; | ||
886 | if (devno < 2*ndev) | ||
887 | { | ||
888 | if (devno & 1) | ||
889 | snprintf(devname, nchar, "plughw:%d", devno/2); | ||
890 | else snprintf(devname, nchar, "hw:%d", devno/2); | ||
891 | } | ||
892 | else if (devno <2*ndev + alsa_nnames) | ||
893 | snprintf(devname, nchar, "%s", alsa_names[devno - 2*ndev]); | ||
894 | else snprintf(devname, nchar, "???"); | ||
895 | } | ||
896 | |||
897 | /* For each hardware card found, we list two devices, the "hard" and | ||
898 | "plug" one. The card scan is derived from portaudio code. */ | ||
899 | void alsa_getdevs(char *indevlist, int *nindevs, | ||
900 | char *outdevlist, int *noutdevs, int *canmulti, | ||
901 | int maxndev, int devdescsize) | ||
902 | { | ||
903 | int ndev = 0, cardno = -1, i, j; | ||
904 | *canmulti = 0; /* only one device; must be the same for input&output */ | ||
905 | while (!snd_card_next(&cardno) && cardno >= 0) | ||
906 | { | ||
907 | snd_ctl_t *ctl; | ||
908 | snd_ctl_card_info_t *info; | ||
909 | char devname[80]; | ||
910 | const char *desc; | ||
911 | if (2 * ndev + 2 > maxndev) | ||
912 | break; | ||
913 | /* apparently, "cardno" is just a counter; but check that here */ | ||
914 | if (ndev != cardno) | ||
915 | fprintf(stderr, "oops: ALSA cards not reported in order?\n"); | ||
916 | sprintf(devname, "hw:%d", cardno ); | ||
917 | /* fprintf(stderr, "\ntry %s...\n", devname); */ | ||
918 | if (snd_ctl_open(&ctl, devname, 0) >= 0) | ||
919 | { | ||
920 | snd_ctl_card_info_malloc(&info); | ||
921 | snd_ctl_card_info(ctl, info); | ||
922 | desc = snd_ctl_card_info_get_name(info); | ||
923 | snd_ctl_card_info_free(info); | ||
924 | } | ||
925 | else | ||
926 | { | ||
927 | fprintf(stderr, "ALSA card scan error\n"); | ||
928 | desc = "???"; | ||
929 | } | ||
930 | /* fprintf(stderr, "name: %s\n", snd_ctl_card_info_get_name(info)); */ | ||
931 | sprintf(indevlist + 2*ndev * devdescsize, "%s (hardware)", desc); | ||
932 | sprintf(indevlist + (2*ndev + 1) * devdescsize, "%s (plug-in)", desc); | ||
933 | sprintf(outdevlist + 2*ndev * devdescsize, "%s (hardware)", desc); | ||
934 | sprintf(outdevlist + (2*ndev + 1) * devdescsize, "%s (plug-in)", desc); | ||
935 | ndev++; | ||
936 | } | ||
937 | for (i = 0, j = 2*ndev; i < alsa_nnames; i++, j++) | ||
938 | { | ||
939 | if (j >= maxndev) | ||
940 | break; | ||
941 | snprintf(indevlist + j * devdescsize, devdescsize, "%s", | ||
942 | alsa_names[i]); | ||
943 | } | ||
944 | *nindevs = *noutdevs = j; | ||
945 | } | ||
946 | /* Copyright (c) 1997-2003 Guenter Geiger, Miller Puckette, Larry Troxler, | ||
947 | * Winfried Ritsch, Karl MacMillan, and others. | ||
948 | * For information on usage and redistribution, and for a DISCLAIMER OF ALL | ||
949 | * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ | ||
950 | |||
951 | /* this file inputs and outputs audio using the ALSA API available on linux. */ | ||
952 | |||
953 | #include <alsa/asoundlib.h> | ||
954 | |||
955 | #include "m_pd.h" | ||
956 | #include "s_stuff.h" | ||
957 | #include <errno.h> | ||
958 | #include <stdio.h> | ||
959 | #include <unistd.h> | ||
960 | #include <stdlib.h> | ||
961 | #include <string.h> | ||
962 | #include <sys/types.h> | ||
963 | #include <sys/time.h> | ||
964 | #include <sys/stat.h> | ||
965 | #include <sys/ioctl.h> | ||
966 | #include <fcntl.h> | ||
967 | #include <sched.h> | ||
968 | #include <sys/mman.h> | ||
969 | |||
970 | typedef int16_t t_alsa_sample16; | ||
971 | typedef int32_t t_alsa_sample32; | ||
972 | #define ALSA_SAMPLEWIDTH_16 sizeof(t_alsa_sample16) | ||
973 | #define ALSA_SAMPLEWIDTH_32 sizeof(t_alsa_sample32) | ||
974 | #define ALSA_XFERSIZE16 (signed int)(sizeof(t_alsa_sample16) * DEFDACBLKSIZE) | ||
975 | #define ALSA_XFERSIZE32 (signed int)(sizeof(t_alsa_sample32) * DEFDACBLKSIZE) | ||
976 | #define ALSA_MAXDEV 1 | ||
977 | #define ALSA_JITTER 1024 | ||
978 | #define ALSA_EXTRABUFFER 2048 | ||
979 | #define ALSA_DEFFRAGSIZE 64 | ||
980 | #define ALSA_DEFNFRAG 12 | ||
981 | |||
982 | #ifndef INT32_MAX | ||
983 | #define INT32_MAX 0x7fffffff | ||
984 | #endif | ||
985 | |||
986 | #if (SND_LIB_MAJOR < 1) | ||
987 | #define ALSAAPI9 | ||
988 | #endif | ||
989 | |||
990 | typedef struct _alsa_dev | ||
991 | { | ||
992 | snd_pcm_t *inhandle; | ||
993 | snd_pcm_t *outhandle; | ||
994 | int innoninterleave; /* true if we're set for noninterleaved read */ | ||
995 | int outnoninterleave; /* same for write */ | ||
996 | } t_alsa_dev; | ||
997 | |||
998 | t_alsa_dev alsa_device; | ||
999 | static void *alsa_snd_buf = 0; | ||
1000 | static void **alsa_buf_ptrs; | ||
1001 | static int alsa_samplewidth; | ||
1002 | static snd_pcm_status_t* in_status; | ||
1003 | static snd_pcm_status_t* out_status; | ||
1004 | |||
1005 | static int alsa_mode; | ||
1006 | static int alsa_buf_samps; /* believed actual ALSA bufsize in sample frames */ | ||
1007 | static int alsa_inchannels; | ||
1008 | static int alsa_outchannels; | ||
1009 | |||
1010 | /* Defines */ | ||
1011 | #define DEBUG(x) x | ||
1012 | #define DEBUG2(x) {x;} | ||
1013 | |||
1014 | static void alsa_checkiosync( void); | ||
1015 | static void alsa_numbertoname(int devno, char *devname, int nchar); | ||
1016 | |||
1017 | /* don't assume we can turn all 31 bits when doing float-to-fix; | ||
1018 | otherwise some audio drivers (e.g. Midiman/ALSA) wrap around. */ | ||
1019 | #define FMAX 0x7ffff000 | ||
1020 | #define CLIP32(x) (((x)>FMAX)?FMAX:((x) < -FMAX)?-FMAX:(x)) | ||
1021 | |||
1022 | /* support for ALSA pcmv2 api by Karl MacMillan<karlmac@peabody.jhu.edu> */ | ||
1023 | |||
1024 | static void check_error(int err, const char *why) | ||
1025 | { | ||
1026 | if (err < 0) | ||
1027 | fprintf(stderr, "%s: %s\n", why, snd_strerror(err)); | ||
1028 | } | ||
1029 | |||
1030 | /* was: alsa_open_audio(int wantinchans, int wantoutchans, int srate) */ | ||
1031 | |||
1032 | int alsa_open_audio(int naudioindev, int *audioindev, int nchindev, | ||
1033 | int *chindev, int naudiooutdev, int *audiooutdev, int nchoutdev, | ||
1034 | int *choutdev, int rate) | ||
1035 | { | ||
1036 | int err, inchans = 0, outchans = 0, subunitdir; | ||
1037 | char devname[512]; | ||
1038 | snd_pcm_hw_params_t* hw_params; | ||
1039 | snd_pcm_sw_params_t* sw_params; | ||
1040 | snd_output_t* out; | ||
1041 | int frag_size = (sys_blocksize ? sys_blocksize : ALSA_DEFFRAGSIZE); | ||
1042 | int nfrags, i; | ||
1043 | short* tmp_buf; | ||
1044 | unsigned int tmp_uint; | ||
1045 | snd_pcm_uframes_t tmp_snd_pcm_uframes; | ||
1046 | int wantinchans, wantoutchans, devno; | ||
1047 | |||
1048 | if (naudioindev >= 2 || naudiooutdev >= 2) | ||
1049 | post("alsa: only one input and output device allowed (extras ignored"); | ||
1050 | if (naudioindev >= 1 && naudiooutdev >= 1 && | ||
1051 | audioindev[0] != audiooutdev[0]) | ||
1052 | post("alsa: changing output device to agree with input device"); | ||
1053 | if (nchindev) | ||
1054 | wantinchans = chindev[0]; | ||
1055 | else wantinchans = (naudioindev ? 2 : 0); | ||
1056 | if (nchoutdev) | ||
1057 | wantoutchans = choutdev[0]; | ||
1058 | else wantoutchans = (naudiooutdev ? 2 : 0); | ||
1059 | devno = (naudioindev > 0 ? audioindev[0] : | ||
1060 | (naudiooutdev > 0 ? audiooutdev[0] : 0)); | ||
1061 | |||
1062 | alsa_numbertoname(devno, devname, 512); | ||
1063 | |||
1064 | if (sys_verbose) | ||
1065 | post("device name %s; channels in %d, out %d", devname, wantinchans, | ||
1066 | wantoutchans); | ||
1067 | |||
1068 | nfrags = sys_schedadvance * (float)rate / (1e6 * frag_size); | ||
1069 | /* save our belief as to ALSA's buffer size for later */ | ||
1070 | alsa_buf_samps = nfrags * frag_size; | ||
1071 | |||
1072 | if (sys_verbose) | ||
1073 | post("audio buffer set to %d", (int)(0.001 * sys_schedadvance)); | ||
1074 | |||
1075 | alsa_device.innoninterleave = alsa_device.outnoninterleave = 0; | ||
1076 | if (wantinchans) | ||
1077 | { | ||
1078 | err = snd_pcm_open(&alsa_device.inhandle, devname, | ||
1079 | SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK); | ||
1080 | |||
1081 | check_error(err, "snd_pcm_open (input)"); | ||
1082 | if (err < 0) | ||
1083 | inchans = 0; | ||
1084 | else | ||
1085 | { | ||
1086 | inchans = wantinchans; | ||
1087 | snd_pcm_nonblock(alsa_device.inhandle, 1); | ||
1088 | } | ||
1089 | } | ||
1090 | if (wantoutchans) | ||
1091 | { | ||
1092 | err = snd_pcm_open(&alsa_device.outhandle, devname, | ||
1093 | SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); | ||
1094 | |||
1095 | check_error(err, "snd_pcm_open (output)"); | ||
1096 | if (err < 0) | ||
1097 | outchans = 0; | ||
1098 | else | ||
1099 | { | ||
1100 | outchans = wantoutchans; | ||
1101 | snd_pcm_nonblock(alsa_device.outhandle, 1); | ||
1102 | } | ||
1103 | } | ||
1104 | if (inchans) | ||
1105 | { | ||
1106 | if (sys_verbose) | ||
1107 | post("opening sound input..."); | ||
1108 | err = snd_pcm_hw_params_malloc(&hw_params); | ||
1109 | check_error(err, "snd_pcm_hw_params_malloc (input)"); | ||
1110 | |||
1111 | // get the default params | ||
1112 | err = snd_pcm_hw_params_any(alsa_device.inhandle, hw_params); | ||
1113 | check_error(err, "snd_pcm_hw_params_any (input)"); | ||
1114 | |||
1115 | /* try to set interleaved access */ | ||
1116 | err = snd_pcm_hw_params_set_access(alsa_device.inhandle, | ||
1117 | hw_params, SND_PCM_ACCESS_RW_INTERLEAVED); | ||
1118 | if (err < 0) | ||
1119 | { | ||
1120 | /* OK, so try non-interleaved */ | ||
1121 | err = snd_pcm_hw_params_set_access(alsa_device.inhandle, | ||
1122 | hw_params, SND_PCM_ACCESS_RW_NONINTERLEAVED); | ||
1123 | if (err >= 0) | ||
1124 | { | ||
1125 | post("using non-interleaved audio input"); | ||
1126 | alsa_device.innoninterleave = 1; | ||
1127 | } | ||
1128 | } | ||
1129 | check_error(err, "snd_pcm_hw_params_set_access (input)"); | ||
1130 | // Try to set 32 bit format first | ||
1131 | err = snd_pcm_hw_params_set_format(alsa_device.inhandle, hw_params, | ||
1132 | SND_PCM_FORMAT_S32); | ||
1133 | if (err < 0) | ||
1134 | { | ||
1135 | /* fprintf(stderr, | ||
1136 | "PD-ALSA: 32 bit format not available - using 16\n"); */ | ||
1137 | err = snd_pcm_hw_params_set_format(alsa_device.inhandle, hw_params, | ||
1138 | SND_PCM_FORMAT_S16); | ||
1139 | check_error(err, "snd_pcm_hw_params_set_format (input)"); | ||
1140 | alsa_samplewidth = 2; | ||
1141 | } | ||
1142 | else | ||
1143 | { | ||
1144 | alsa_samplewidth = 4; | ||
1145 | } | ||
1146 | post("Sample width set to %d bytes", alsa_samplewidth); | ||
1147 | // set the subformat | ||
1148 | err = snd_pcm_hw_params_set_subformat(alsa_device.inhandle, hw_params, | ||
1149 | SND_PCM_SUBFORMAT_STD); | ||
1150 | check_error(err, "snd_pcm_hw_params_set_subformat (input)"); | ||
1151 | // set the number of channels | ||
1152 | tmp_uint = inchans; | ||
1153 | err = snd_pcm_hw_params_set_channels_min(alsa_device.inhandle, | ||
1154 | hw_params, &tmp_uint); | ||
1155 | check_error(err, "snd_pcm_hw_params_set_channels (input)"); | ||
1156 | if (tmp_uint != (unsigned)inchans) | ||
1157 | post("ALSA: set input channels to %d", tmp_uint); | ||
1158 | inchans = tmp_uint; | ||
1159 | // set the sampling rate | ||
1160 | err = snd_pcm_hw_params_set_rate_min(alsa_device.inhandle, hw_params, | ||
1161 | &rate, 0); | ||
1162 | check_error(err, "snd_pcm_hw_params_set_rate_min (input)"); | ||
1163 | #if 0 | ||
1164 | err = snd_pcm_hw_params_get_rate(hw_params, &subunitdir); | ||
1165 | post("input sample rate %d", err); | ||
1166 | #endif | ||
1167 | // set the period - ie frag size | ||
1168 | // post("fragsize a %d", frag_size); | ||
1169 | |||
1170 | /* LATER try this to get a recommended period size... | ||
1171 | right now, it trips an assertion failure in ALSA lib */ | ||
1172 | #if 0 | ||
1173 | post("input period was %d, min %d, max %d\n", | ||
1174 | snd_pcm_hw_params_get_period_size(hw_params, 0), | ||
1175 | snd_pcm_hw_params_get_period_size_min(hw_params, 0), | ||
1176 | snd_pcm_hw_params_get_period_size_max(hw_params, 0)); | ||
1177 | #endif | ||
1178 | #ifdef ALSAAPI9 | ||
1179 | err = snd_pcm_hw_params_set_period_size_near(alsa_device.inhandle, | ||
1180 | hw_params, | ||
1181 | (snd_pcm_uframes_t) | ||
1182 | frag_size, 0); | ||
1183 | #else | ||
1184 | tmp_snd_pcm_uframes = frag_size; | ||
1185 | err = snd_pcm_hw_params_set_period_size_near(alsa_device.inhandle, | ||
1186 | hw_params, &tmp_snd_pcm_uframes, 0); | ||
1187 | #endif | ||
1188 | check_error(err, "snd_pcm_hw_params_set_period_size_near (input)"); | ||
1189 | // post("fragsize b %d", frag_size); | ||
1190 | // set the number of periods - ie numfrags | ||
1191 | // post("nfrags a %d", nfrags); | ||
1192 | #ifdef ALSAAPI9 | ||
1193 | err = snd_pcm_hw_params_set_periods_near(alsa_device.inhandle, | ||
1194 | hw_params, nfrags, 0); | ||
1195 | #else | ||
1196 | tmp_uint = nfrags; | ||
1197 | err = snd_pcm_hw_params_set_periods_near(alsa_device.inhandle, | ||
1198 | hw_params, &tmp_uint, 0); | ||
1199 | #endif | ||
1200 | check_error(err, "snd_pcm_hw_params_set_periods_near (input)"); | ||
1201 | // set the buffer size | ||
1202 | #ifdef ALSAAPI9 | ||
1203 | err = snd_pcm_hw_params_set_buffer_size_near(alsa_device.inhandle, | ||
1204 | hw_params, nfrags * frag_size); | ||
1205 | #else | ||
1206 | tmp_snd_pcm_uframes = nfrags * frag_size; | ||
1207 | err = snd_pcm_hw_params_set_buffer_size_near(alsa_device.inhandle, | ||
1208 | hw_params, &tmp_snd_pcm_uframes); | ||
1209 | #endif | ||
1210 | check_error(err, "snd_pcm_hw_params_set_buffer_size_near (input)"); | ||
1211 | |||
1212 | err = snd_pcm_hw_params(alsa_device.inhandle, hw_params); | ||
1213 | check_error(err, "snd_pcm_hw_params (input)"); | ||
1214 | |||
1215 | snd_pcm_hw_params_free(hw_params); | ||
1216 | |||
1217 | err = snd_pcm_sw_params_malloc(&sw_params); | ||
1218 | check_error(err, "snd_pcm_sw_params_malloc (input)"); | ||
1219 | err = snd_pcm_sw_params_current(alsa_device.inhandle, sw_params); | ||
1220 | check_error(err, "snd_pcm_sw_params_current (input)"); | ||
1221 | err = snd_pcm_sw_params_set_start_threshold(alsa_device.inhandle, | ||
1222 | sw_params, nfrags * frag_size); | ||
1223 | check_error(err, "snd_pcm_sw_params_set_start_threshold (input)"); | ||
1224 | err = snd_pcm_sw_params_set_stop_threshold(alsa_device.inhandle, | ||
1225 | sw_params, 0x7fffffff); | ||
1226 | check_error(err, "snd_pcm_sw_params_set_stop_threshold (input)"); | ||
1227 | err = snd_pcm_sw_params_set_avail_min(alsa_device.inhandle, sw_params, | ||
1228 | frag_size); | ||
1229 | check_error(err, "snd_pcm_sw_params_set_avail_min (input)"); | ||
1230 | err = snd_pcm_sw_params(alsa_device.inhandle, sw_params); | ||
1231 | check_error(err, "snd_pcm_sw_params (input)"); | ||
1232 | |||
1233 | snd_pcm_sw_params_free(sw_params); | ||
1234 | |||
1235 | snd_output_stdio_attach(&out, stderr, 0); | ||
1236 | #if 0 | ||
1237 | if (sys_verbose) | ||
1238 | { | ||
1239 | snd_pcm_dump_hw_setup(alsa_device.inhandle, out); | ||
1240 | snd_pcm_dump_sw_setup(alsa_device.inhandle, out); | ||
1241 | } | ||
1242 | #endif | ||
1243 | } | ||
1244 | |||
1245 | if (outchans) | ||
1246 | { | ||
1247 | int foo; | ||
1248 | if (sys_verbose) | ||
1249 | post("opening sound output..."); | ||
1250 | err = snd_pcm_hw_params_malloc(&hw_params); | ||
1251 | check_error(err, "snd_pcm_sw_params (output)"); | ||
1252 | |||
1253 | // get the default params | ||
1254 | err = snd_pcm_hw_params_any(alsa_device.outhandle, hw_params); | ||
1255 | check_error(err, "snd_pcm_hw_params_any (output)"); | ||
1256 | // set interleaved access - FIXME deal with other access types | ||
1257 | err = snd_pcm_hw_params_set_access(alsa_device.outhandle, hw_params, | ||
1258 | SND_PCM_ACCESS_RW_INTERLEAVED); | ||
1259 | check_error(err, "snd_pcm_hw_params_set_access (output)"); | ||
1260 | |||
1261 | /* try to set interleaved access */ | ||
1262 | err = snd_pcm_hw_params_set_access(alsa_device.outhandle, | ||
1263 | hw_params, SND_PCM_ACCESS_RW_INTERLEAVED); | ||
1264 | if (err < 0) | ||
1265 | { | ||
1266 | /* OK, so try non-interleaved */ | ||
1267 | err = snd_pcm_hw_params_set_access(alsa_device.outhandle, | ||
1268 | hw_params, SND_PCM_ACCESS_RW_NONINTERLEAVED); | ||
1269 | if (err >= 0) | ||
1270 | { | ||
1271 | post("using non-interleaved audio"); | ||
1272 | alsa_device.outnoninterleave = 1; | ||
1273 | } | ||
1274 | } | ||
1275 | check_error(err, "snd_pcm_hw_params_set_access (output)"); | ||
1276 | |||
1277 | |||
1278 | // Try to set 32 bit format first | ||
1279 | err = snd_pcm_hw_params_set_format(alsa_device.outhandle, hw_params, | ||
1280 | SND_PCM_FORMAT_S32); | ||
1281 | if (err < 0) | ||
1282 | { | ||
1283 | err = snd_pcm_hw_params_set_format(alsa_device.outhandle, | ||
1284 | hw_params,SND_PCM_FORMAT_S16); | ||
1285 | check_error(err, "snd_pcm_hw_params_set_format (output)"); | ||
1286 | /* fprintf(stderr, | ||
1287 | "PD-ALSA: 32 bit format not available - using 16\n"); */ | ||
1288 | alsa_samplewidth = 2; | ||
1289 | } | ||
1290 | else | ||
1291 | { | ||
1292 | alsa_samplewidth = 4; | ||
1293 | } | ||
1294 | // set the subformat | ||
1295 | err = snd_pcm_hw_params_set_subformat(alsa_device.outhandle, hw_params, | ||
1296 | SND_PCM_SUBFORMAT_STD); | ||
1297 | check_error(err, "snd_pcm_hw_params_set_subformat (output)"); | ||
1298 | // set the number of channels | ||
1299 | tmp_uint = outchans; | ||
1300 | err = snd_pcm_hw_params_set_channels_min(alsa_device.outhandle, | ||
1301 | hw_params, &tmp_uint); | ||
1302 | check_error(err, "snd_pcm_hw_params_set_channels (output)"); | ||
1303 | if (tmp_uint != (unsigned)outchans) | ||
1304 | post("alsa: set output channels to %d", tmp_uint); | ||
1305 | outchans = tmp_uint; | ||
1306 | // set the sampling rate | ||
1307 | err = snd_pcm_hw_params_set_rate_min(alsa_device.outhandle, hw_params, | ||
1308 | &rate, 0); | ||
1309 | check_error(err, "snd_pcm_hw_params_set_rate_min (output)"); | ||
1310 | #if 0 | ||
1311 | err = snd_pcm_hw_params_get_rate(hw_params, &subunitdir); | ||
1312 | post("output sample rate %d", err); | ||
1313 | #endif | ||
1314 | // set the period - ie frag size | ||
1315 | #if 0 | ||
1316 | post("output period was %d, min %d, max %d\n", | ||
1317 | snd_pcm_hw_params_get_period_size(hw_params, 0), | ||
1318 | snd_pcm_hw_params_get_period_size_min(hw_params, 0), | ||
1319 | snd_pcm_hw_params_get_period_size_max(hw_params, 0)); | ||
1320 | #endif | ||
1321 | // post("fragsize c %d", frag_size); | ||
1322 | #ifdef ALSAAPI9 | ||
1323 | err = snd_pcm_hw_params_set_period_size_near(alsa_device.outhandle, | ||
1324 | hw_params, | ||
1325 | (snd_pcm_uframes_t) | ||
1326 | frag_size, 0); | ||
1327 | #else | ||
1328 | tmp_snd_pcm_uframes = frag_size; | ||
1329 | err = snd_pcm_hw_params_set_period_size_near(alsa_device.outhandle, | ||
1330 | hw_params, &tmp_snd_pcm_uframes, 0); | ||
1331 | #endif | ||
1332 | // post("fragsize d %d", frag_size); | ||
1333 | check_error(err, "snd_pcm_hw_params_set_period_size_near (output)"); | ||
1334 | // set the number of periods - ie numfrags | ||
1335 | #ifdef ALSAAPI9 | ||
1336 | err = snd_pcm_hw_params_set_periods_near(alsa_device.outhandle, | ||
1337 | hw_params, nfrags, 0); | ||
1338 | #else | ||
1339 | tmp_uint = nfrags; | ||
1340 | err = snd_pcm_hw_params_set_periods_near(alsa_device.outhandle, | ||
1341 | hw_params, &tmp_uint, 0); | ||
1342 | #endif | ||
1343 | check_error(err, "snd_pcm_hw_params_set_periods_near (output)"); | ||
1344 | // set the buffer size | ||
1345 | #ifdef ALSAAPI9 | ||
1346 | err = snd_pcm_hw_params_set_buffer_size_near(alsa_device.outhandle, | ||
1347 | hw_params, nfrags * frag_size); | ||
1348 | #else | ||
1349 | tmp_snd_pcm_uframes = nfrags * frag_size; | ||
1350 | err = snd_pcm_hw_params_set_buffer_size_near(alsa_device.outhandle, | ||
1351 | hw_params, &tmp_snd_pcm_uframes); | ||
1352 | #endif | ||
1353 | check_error(err, "snd_pcm_hw_params_set_buffer_size_near (output)"); | ||
1354 | |||
1355 | err = snd_pcm_hw_params(alsa_device.outhandle, hw_params); | ||
1356 | check_error(err, "snd_pcm_hw_params (output)"); | ||
1357 | |||
1358 | snd_pcm_hw_params_free(hw_params); | ||
1359 | |||
1360 | err = snd_pcm_sw_params_malloc(&sw_params); | ||
1361 | check_error(err, "snd_pcm_sw_params_malloc (output)"); | ||
1362 | err = snd_pcm_sw_params_current(alsa_device.outhandle, sw_params); | ||
1363 | check_error(err, "snd_pcm_sw_params_current (output)"); | ||
1364 | err = snd_pcm_sw_params_set_start_threshold(alsa_device.outhandle, | ||
1365 | sw_params, nfrags * frag_size); | ||
1366 | check_error(err, "snd_pcm_sw_params_set_start_threshold (output)"); | ||
1367 | err = snd_pcm_sw_params_set_stop_threshold(alsa_device.outhandle, | ||
1368 | sw_params, 0x7fffffff); | ||
1369 | check_error(err, "snd_pcm_sw_params_set_stop_threshold (output)"); | ||
1370 | err = snd_pcm_sw_params_set_avail_min(alsa_device.outhandle, sw_params, | ||
1371 | frag_size); | ||
1372 | check_error(err, "snd_pcm_sw_params_set_avail_min (output)"); | ||
1373 | err = snd_pcm_sw_params(alsa_device.outhandle, sw_params); | ||
1374 | check_error(err, "snd_pcm_sw_params (output)"); | ||
1375 | snd_pcm_sw_params_free(sw_params); | ||
1376 | |||
1377 | snd_output_stdio_attach(&out, stderr, 0); | ||
1378 | #if 0 | ||
1379 | if (sys_verbose) | ||
1380 | { | ||
1381 | snd_pcm_dump_hw_setup(alsa_device.outhandle, out); | ||
1382 | snd_pcm_dump_sw_setup(alsa_device.outhandle, out); | ||
1383 | } | ||
1384 | #endif | ||
1385 | } | ||
1386 | |||
1387 | if (inchans) | ||
1388 | snd_pcm_prepare(alsa_device.inhandle); | ||
1389 | if (outchans) | ||
1390 | snd_pcm_prepare(alsa_device.outhandle); | ||
1391 | |||
1392 | // if duplex we can link the channels so they start together | ||
1393 | if (inchans && outchans) | ||
1394 | snd_pcm_link(alsa_device.inhandle, alsa_device.outhandle); | ||
1395 | |||
1396 | // set up the status variables | ||
1397 | err = snd_pcm_status_malloc(&in_status); | ||
1398 | check_error(err, "snd_pcm_status_malloc"); | ||
1399 | err = snd_pcm_status_malloc(&out_status); | ||
1400 | check_error(err, "snd_pcm_status_malloc"); | ||
1401 | |||
1402 | // set up the buffer | ||
1403 | if (alsa_snd_buf) | ||
1404 | free(alsa_snd_buf); | ||
1405 | alsa_snd_buf = (void *)malloc( | ||
1406 | sizeof(char) * alsa_samplewidth * DEFDACBLKSIZE * | ||
1407 | (outchans > inchans ? outchans : inchans)); | ||
1408 | memset(alsa_snd_buf, 0, sizeof(char) * alsa_samplewidth * DEFDACBLKSIZE * | ||
1409 | (outchans > inchans ? outchans : inchans)); | ||
1410 | /* make an array of pointers too in case we need them */ | ||
1411 | if (alsa_buf_ptrs) | ||
1412 | free(alsa_buf_ptrs); | ||
1413 | alsa_buf_ptrs = (void **)malloc( | ||
1414 | sizeof(void *) * (outchans > inchans ? outchans : inchans)); | ||
1415 | for (i = 0; i < (outchans > inchans ? outchans : inchans); i++) | ||
1416 | alsa_buf_ptrs[i] = (t_alsa_sample32 *)alsa_snd_buf + i * DEFDACBLKSIZE; | ||
1417 | |||
1418 | // fill the buffer with silence | ||
1419 | if (outchans) | ||
1420 | { | ||
1421 | i = (frag_size * nfrags)/DEFDACBLKSIZE + 1; | ||
1422 | while (i--) | ||
1423 | { | ||
1424 | if (alsa_device.outnoninterleave) | ||
1425 | snd_pcm_writen(alsa_device.outhandle, alsa_buf_ptrs, | ||
1426 | DEFDACBLKSIZE); | ||
1427 | else snd_pcm_writei(alsa_device.outhandle, alsa_snd_buf, | ||
1428 | DEFDACBLKSIZE); | ||
1429 | } | ||
1430 | /* confused about this: */ | ||
1431 | /* if ((err = snd_pcm_start(alsa_device.outhandle) < 0)) | ||
1432 | check_error(err, "output start failed\n"); */ | ||
1433 | } | ||
1434 | else if (inchans) | ||
1435 | { | ||
1436 | if (snd_pcm_start(alsa_device.inhandle) < 0) | ||
1437 | check_error(err, "input start failed\n"); | ||
1438 | } | ||
1439 | alsa_outchannels = outchans; | ||
1440 | alsa_inchannels = inchans; | ||
1441 | |||
1442 | return (!(inchans || outchans)); | ||
1443 | } | ||
1444 | |||
1445 | void alsa_close_audio(void) | ||
1446 | { | ||
1447 | int err; | ||
1448 | if (alsa_inchannels) | ||
1449 | { | ||
1450 | err = snd_pcm_close(alsa_device.inhandle); | ||
1451 | check_error(err, "snd_pcm_close (input)"); | ||
1452 | } | ||
1453 | if (alsa_outchannels) | ||
1454 | { | ||
1455 | err = snd_pcm_close(alsa_device.outhandle); | ||
1456 | check_error(err, "snd_pcm_close (output)"); | ||
1457 | } | ||
1458 | } | ||
1459 | |||
1460 | // #define DEBUG_ALSA_XFER | ||
1461 | |||
1462 | int alsa_send_dacs(void) | ||
1463 | { | ||
1464 | static int16_t *sp; | ||
1465 | static int xferno = 0; | ||
1466 | static int callno = 0; | ||
1467 | static double timenow; | ||
1468 | double timelast; | ||
1469 | t_sample *fp, *fp1, *fp2; | ||
1470 | int i, j, k, err, devno = 0; | ||
1471 | int inputcount = 0, outputcount = 0, inputlate = 0, outputlate = 0; | ||
1472 | int result; | ||
1473 | int inchannels = (sys_inchannels > alsa_inchannels ? | ||
1474 | alsa_inchannels : sys_inchannels); | ||
1475 | int outchannels = (sys_outchannels > alsa_outchannels ? | ||
1476 | alsa_outchannels : sys_outchannels); | ||
1477 | unsigned int intransfersize = DEFDACBLKSIZE; | ||
1478 | unsigned int outtransfersize = DEFDACBLKSIZE; | ||
1479 | |||
1480 | // get the status | ||
1481 | if (!inchannels && !outchannels) | ||
1482 | { | ||
1483 | return SENDDACS_NO; | ||
1484 | } | ||
1485 | |||
1486 | timelast = timenow; | ||
1487 | timenow = sys_getrealtime(); | ||
1488 | |||
1489 | #ifdef DEBUG_ALSA_XFER | ||
1490 | if (timenow - timelast > 0.050) | ||
1491 | fprintf(stderr, "(%d)", | ||
1492 | (int)(1000 * (timenow - timelast))), fflush(stderr); | ||
1493 | #endif | ||
1494 | |||
1495 | callno++; | ||
1496 | |||
1497 | alsa_checkiosync(); /* check I/O are in sync and data not late */ | ||
1498 | |||
1499 | if (alsa_inchannels) | ||
1500 | { | ||
1501 | snd_pcm_status(alsa_device.inhandle, in_status); | ||
1502 | if (snd_pcm_status_get_avail(in_status) < intransfersize) | ||
1503 | return SENDDACS_NO; | ||
1504 | } | ||
1505 | if (alsa_outchannels) | ||
1506 | { | ||
1507 | snd_pcm_status(alsa_device.outhandle, out_status); | ||
1508 | if (snd_pcm_status_get_avail(out_status) < outtransfersize) | ||
1509 | return SENDDACS_NO; | ||
1510 | } | ||
1511 | |||
1512 | /* do output */ | ||
1513 | if (alsa_outchannels) | ||
1514 | { | ||
1515 | fp = sys_soundout; | ||
1516 | if (alsa_samplewidth == 4) | ||
1517 | { | ||
1518 | if (alsa_device.outnoninterleave) | ||
1519 | { | ||
1520 | int n = outchannels * DEFDACBLKSIZE; | ||
1521 | for (i = 0, fp1 = fp; i < n; i++) | ||
1522 | { | ||
1523 | float s1 = *fp1 * INT32_MAX; | ||
1524 | ((t_alsa_sample32 *)alsa_snd_buf)[i] = CLIP32(s1); | ||
1525 | } | ||
1526 | n = alsa_outchannels * DEFDACBLKSIZE; | ||
1527 | for (; i < n; i++) | ||
1528 | ((t_alsa_sample32 *)alsa_snd_buf)[i] = 0; | ||
1529 | } | ||
1530 | else | ||
1531 | { | ||
1532 | for (i = 0, fp1 = fp; i < outchannels; i++, | ||
1533 | fp1 += DEFDACBLKSIZE) | ||
1534 | { | ||
1535 | for (j = i, k = DEFDACBLKSIZE, fp2 = fp1; k--; | ||
1536 | j += alsa_outchannels, fp2++) | ||
1537 | { | ||
1538 | float s1 = *fp2 * INT32_MAX; | ||
1539 | ((t_alsa_sample32 *)alsa_snd_buf)[j] = CLIP32(s1); | ||
1540 | } | ||
1541 | } | ||
1542 | } | ||
1543 | } | ||
1544 | else | ||
1545 | { | ||
1546 | for (i = 0, fp1 = fp; i < outchannels; i++, fp1 += DEFDACBLKSIZE) | ||
1547 | { | ||
1548 | for (j = i, k = DEFDACBLKSIZE, fp2 = fp1; k--; | ||
1549 | j += alsa_outchannels, fp2++) | ||
1550 | { | ||
1551 | int s = *fp2 * 32767.; | ||
1552 | if (s > 32767) | ||
1553 | s = 32767; | ||
1554 | else if (s < -32767) | ||
1555 | s = -32767; | ||
1556 | ((t_alsa_sample16 *)alsa_snd_buf)[j] = s; | ||
1557 | } | ||
1558 | } | ||
1559 | } | ||
1560 | |||
1561 | if (alsa_device.outnoninterleave) | ||
1562 | result = snd_pcm_writen(alsa_device.outhandle, alsa_buf_ptrs, | ||
1563 | outtransfersize); | ||
1564 | else result = snd_pcm_writei(alsa_device.outhandle, alsa_snd_buf, | ||
1565 | outtransfersize); | ||
1566 | |||
1567 | if (result != (int)outtransfersize) | ||
1568 | { | ||
1569 | #ifdef DEBUG_ALSA_XFER | ||
1570 | if (result >= 0 || errno == EAGAIN) | ||
1571 | fprintf(stderr, "ALSA: write returned %d of %d\n", | ||
1572 | result, outtransfersize); | ||
1573 | else fprintf(stderr, "ALSA: write: %s\n", | ||
1574 | snd_strerror(errno)); | ||
1575 | fprintf(stderr, | ||
1576 | "inputcount %d, outputcount %d, outbufsize %d\n", | ||
1577 | inputcount, outputcount, | ||
1578 | (ALSA_EXTRABUFFER + sys_advance_samples) | ||
1579 | * alsa_samplewidth * outchannels); | ||
1580 | #endif | ||
1581 | sys_log_error(ERR_DACSLEPT); | ||
1582 | return (SENDDACS_NO); | ||
1583 | } | ||
1584 | |||
1585 | /* zero out the output buffer */ | ||
1586 | memset(sys_soundout, 0, DEFDACBLKSIZE * sizeof(*sys_soundout) * | ||
1587 | sys_outchannels); | ||
1588 | if (sys_getrealtime() - timenow > 0.002) | ||
1589 | { | ||
1590 | #ifdef DEBUG_ALSA_XFER | ||
1591 | fprintf(stderr, "output %d took %d msec\n", | ||
1592 | callno, (int)(1000 * (timenow - timelast))), fflush(stderr); | ||
1593 | #endif | ||
1594 | timenow = sys_getrealtime(); | ||
1595 | sys_log_error(ERR_DACSLEPT); | ||
1596 | } | ||
1597 | } | ||
1598 | /* do input */ | ||
1599 | if (alsa_inchannels) | ||
1600 | { | ||
1601 | if (alsa_device.innoninterleave) | ||
1602 | result = snd_pcm_readn(alsa_device.inhandle, alsa_buf_ptrs, | ||
1603 | intransfersize); | ||
1604 | else result = snd_pcm_readi(alsa_device.inhandle, alsa_snd_buf, | ||
1605 | intransfersize); | ||
1606 | if (result < (int)intransfersize) | ||
1607 | { | ||
1608 | #ifdef DEBUG_ALSA_XFER | ||
1609 | if (result < 0) | ||
1610 | fprintf(stderr, | ||
1611 | "snd_pcm_read %d %d: %s\n", | ||
1612 | callno, xferno, snd_strerror(errno)); | ||
1613 | else fprintf(stderr, | ||
1614 | "snd_pcm_read %d %d returned only %d\n", | ||
1615 | callno, xferno, result); | ||
1616 | fprintf(stderr, | ||
1617 | "inputcount %d, outputcount %d, inbufsize %d\n", | ||
1618 | inputcount, outputcount, | ||
1619 | (ALSA_EXTRABUFFER + sys_advance_samples) | ||
1620 | * alsa_samplewidth * inchannels); | ||
1621 | #endif | ||
1622 | sys_log_error(ERR_ADCSLEPT); | ||
1623 | return (SENDDACS_NO); | ||
1624 | } | ||
1625 | fp = sys_soundin; | ||
1626 | if (alsa_samplewidth == 4) | ||
1627 | { | ||
1628 | if (alsa_device.innoninterleave) | ||
1629 | { | ||
1630 | int n = inchannels * DEFDACBLKSIZE; | ||
1631 | for (i = 0, fp1 = fp; i < n; i++) | ||
1632 | *fp1 = (float) ((t_alsa_sample32 *)alsa_snd_buf)[i] | ||
1633 | * (1./ INT32_MAX); | ||
1634 | } | ||
1635 | else | ||
1636 | { | ||
1637 | for (i = 0, fp1 = fp; i < inchannels; | ||
1638 | i++, fp1 += DEFDACBLKSIZE) | ||
1639 | { | ||
1640 | for (j = i, k = DEFDACBLKSIZE, fp2 = fp1; k--; | ||
1641 | j += alsa_inchannels, fp2++) | ||
1642 | *fp2 = (float) ((t_alsa_sample32 *)alsa_snd_buf)[j] | ||
1643 | * (1./ INT32_MAX); | ||
1644 | } | ||
1645 | } | ||
1646 | } | ||
1647 | else | ||
1648 | { | ||
1649 | for (i = 0, fp1 = fp; i < inchannels; i++, fp1 += DEFDACBLKSIZE) | ||
1650 | { | ||
1651 | for (j = i, k = DEFDACBLKSIZE, fp2 = fp1; k--; | ||
1652 | j += alsa_inchannels, fp2++) | ||
1653 | *fp2 = (float) ((t_alsa_sample16 *)alsa_snd_buf)[j] | ||
1654 | * 3.051850e-05; | ||
1655 | } | ||
1656 | } | ||
1657 | } | ||
1658 | xferno++; | ||
1659 | if (sys_getrealtime() - timenow > 0.002) | ||
1660 | { | ||
1661 | #ifdef DEBUG_ALSA_XFER | ||
1662 | fprintf(stderr, "routine took %d msec\n", | ||
1663 | (int)(1000 * (sys_getrealtime() - timenow))); | ||
1664 | #endif | ||
1665 | sys_log_error(ERR_ADCSLEPT); | ||
1666 | } | ||
1667 | return SENDDACS_YES; | ||
1668 | } | ||
1669 | |||
1670 | void alsa_printstate( void) | ||
1671 | { | ||
1672 | int i, result; | ||
1673 | snd_pcm_sframes_t indelay, outdelay; | ||
1674 | if (sys_audioapi != API_ALSA) | ||
1675 | { | ||
1676 | error("restart-audio: implemented for ALSA only."); | ||
1677 | return; | ||
1678 | } | ||
1679 | if (sys_inchannels) | ||
1680 | { | ||
1681 | result = snd_pcm_delay(alsa_device.inhandle, &indelay); | ||
1682 | if (result < 0) | ||
1683 | post("snd_pcm_delay 1 failed"); | ||
1684 | else post("in delay %d", indelay); | ||
1685 | } | ||
1686 | if (sys_outchannels) | ||
1687 | { | ||
1688 | result = snd_pcm_delay(alsa_device.outhandle, &outdelay); | ||
1689 | if (result < 0) | ||
1690 | post("snd_pcm_delay 2 failed"); | ||
1691 | else post("out delay %d", outdelay); | ||
1692 | } | ||
1693 | post("sum %d (%d mod 64)\n", indelay + outdelay, (indelay+outdelay)%64); | ||
1694 | |||
1695 | post("buf samples %d", alsa_buf_samps); | ||
1696 | } | ||
1697 | |||
1698 | |||
1699 | void alsa_resync( void) | ||
1700 | { | ||
1701 | int i, result; | ||
1702 | if (sys_audioapi != API_ALSA) | ||
1703 | { | ||
1704 | error("restart-audio: implemented for ALSA only."); | ||
1705 | return; | ||
1706 | } | ||
1707 | memset(alsa_snd_buf, 0, | ||
1708 | sizeof(char) * alsa_samplewidth * DEFDACBLKSIZE * sys_outchannels); | ||
1709 | for (i = 0; i < 1000000; i++) | ||
1710 | { | ||
1711 | if (alsa_device.outnoninterleave) | ||
1712 | result = snd_pcm_writen(alsa_device.outhandle, alsa_buf_ptrs, | ||
1713 | DEFDACBLKSIZE); | ||
1714 | else result = snd_pcm_writei(alsa_device.outhandle, alsa_snd_buf, | ||
1715 | DEFDACBLKSIZE); | ||
1716 | if (result != (int)DEFDACBLKSIZE) | ||
1717 | break; | ||
1718 | } | ||
1719 | post("%d written", i); | ||
1720 | } | ||
1721 | |||
1722 | void alsa_putzeros(int n) | ||
1723 | { | ||
1724 | int i, result; | ||
1725 | memset(alsa_snd_buf, 0, | ||
1726 | sizeof(char) * alsa_samplewidth * DEFDACBLKSIZE * alsa_outchannels); | ||
1727 | for (i = 0; i < n; i++) | ||
1728 | { | ||
1729 | if (alsa_device.outnoninterleave) | ||
1730 | result = snd_pcm_writen(alsa_device.outhandle, alsa_buf_ptrs, | ||
1731 | DEFDACBLKSIZE); | ||
1732 | else result = snd_pcm_writei(alsa_device.outhandle, alsa_snd_buf, | ||
1733 | DEFDACBLKSIZE); | ||
1734 | #if 0 | ||
1735 | if (result != DEFDACBLKSIZE) | ||
1736 | post("result %d", result); | ||
1737 | #endif | ||
1738 | } | ||
1739 | /* post ("putzeros %d", n); */ | ||
1740 | } | ||
1741 | |||
1742 | void alsa_getzeros(int n) | ||
1743 | { | ||
1744 | int i, result; | ||
1745 | for (i = 0; i < n; i++) | ||
1746 | { | ||
1747 | result = snd_pcm_readi(alsa_device.inhandle, alsa_snd_buf, | ||
1748 | DEFDACBLKSIZE); | ||
1749 | #if 0 | ||
1750 | if (result != DEFDACBLKSIZE) | ||
1751 | post("result %d", result); | ||
1752 | #endif | ||
1753 | } | ||
1754 | /* post ("getzeros %d", n); */ | ||
1755 | } | ||
1756 | |||
1757 | /* call this only if both input and output are open */ | ||
1758 | static void alsa_checkiosync( void) | ||
1759 | { | ||
1760 | int i, result, checkit = 1, giveup = 1000, alreadylogged = 0; | ||
1761 | snd_pcm_sframes_t indelay, outdelay, defect; | ||
1762 | |||
1763 | if (!(alsa_outchannels && alsa_inchannels)) | ||
1764 | return; | ||
1765 | while (checkit) | ||
1766 | { | ||
1767 | checkit = 0; | ||
1768 | if (giveup-- <= 0) | ||
1769 | return; | ||
1770 | result = snd_pcm_delay(alsa_device.outhandle, &outdelay); | ||
1771 | if (result < 0) | ||
1772 | { | ||
1773 | post("output snd_pcm_delay failed: %s", snd_strerror(result)); | ||
1774 | if (snd_pcm_status(alsa_device.outhandle, out_status) < 0) | ||
1775 | post("output snd_pcm_status failed"); | ||
1776 | else post("astate %d", | ||
1777 | snd_pcm_status_get_state(out_status)); | ||
1778 | return; | ||
1779 | } | ||
1780 | if (outdelay < 0) | ||
1781 | sys_log_error(ERR_DATALATE), alreadylogged = 1; | ||
1782 | |||
1783 | if (sys_inchannels) | ||
1784 | { | ||
1785 | result = snd_pcm_delay(alsa_device.inhandle, &indelay); | ||
1786 | if (result < 0) | ||
1787 | { | ||
1788 | post("input snd_pcm_delay failed"); | ||
1789 | return; | ||
1790 | } | ||
1791 | defect = indelay + outdelay - alsa_buf_samps; | ||
1792 | if (defect < -(3 * DEFDACBLKSIZE / 2) ) | ||
1793 | { | ||
1794 | checkit = 1; | ||
1795 | alsa_putzeros(1); | ||
1796 | if (!alreadylogged) | ||
1797 | sys_log_error(ERR_RESYNC), alreadylogged = 1; | ||
1798 | } | ||
1799 | else if (defect > 0) | ||
1800 | { | ||
1801 | checkit = 1; | ||
1802 | alsa_getzeros(1); | ||
1803 | if (!alreadylogged) | ||
1804 | sys_log_error(ERR_RESYNC), alreadylogged = 1; | ||
1805 | } | ||
1806 | /* if (alreadylogged) | ||
1807 | post("in %d out %d defect %d", indelay, outdelay, defect); */ | ||
1808 | } | ||
1809 | } | ||
1810 | } | ||
1811 | |||
1812 | static int alsa_nnames = 0; | ||
1813 | static char **alsa_names = 0; | ||
1814 | |||
1815 | void alsa_adddev(char *name) | ||
1816 | { | ||
1817 | if (alsa_nnames) | ||
1818 | alsa_names = (char **)t_resizebytes(alsa_names, | ||
1819 | alsa_nnames * sizeof(char *), | ||
1820 | (alsa_nnames+1) * sizeof(char *)); | ||
1821 | else alsa_names = (char **)t_getbytes(sizeof(char *)); | ||
1822 | alsa_names[alsa_nnames] = gensym(name)->s_name; | ||
1823 | alsa_nnames++; | ||
1824 | } | ||
1825 | |||
1826 | static void alsa_numbertoname(int devno, char *devname, int nchar) | ||
1827 | { | ||
1828 | int ndev = 0, cardno = -1; | ||
1829 | while (!snd_card_next(&cardno) && cardno >= 0) | ||
1830 | ndev++; | ||
1831 | if (devno < 2*ndev) | ||
1832 | { | ||
1833 | if (devno & 1) | ||
1834 | snprintf(devname, nchar, "plughw:%d", devno/2); | ||
1835 | else snprintf(devname, nchar, "hw:%d", devno/2); | ||
1836 | } | ||
1837 | else if (devno <2*ndev + alsa_nnames) | ||
1838 | snprintf(devname, nchar, "%s", alsa_names[devno - 2*ndev]); | ||
1839 | else snprintf(devname, nchar, "???"); | ||
1840 | } | ||
1841 | |||
1842 | /* For each hardware card found, we list two devices, the "hard" and | ||
1843 | "plug" one. The card scan is derived from portaudio code. */ | ||
1844 | void alsa_getdevs(char *indevlist, int *nindevs, | ||
1845 | char *outdevlist, int *noutdevs, int *canmulti, | ||
1846 | int maxndev, int devdescsize) | ||
1847 | { | ||
1848 | int ndev = 0, cardno = -1, i, j; | ||
1849 | *canmulti = 0; /* only one device; must be the same for input&output */ | ||
1850 | while (!snd_card_next(&cardno) && cardno >= 0) | ||
1851 | { | ||
1852 | snd_ctl_t *ctl; | ||
1853 | snd_ctl_card_info_t *info; | ||
1854 | char devname[80]; | ||
1855 | const char *desc; | ||
1856 | if (2 * ndev + 2 > maxndev) | ||
1857 | break; | ||
1858 | /* apparently, "cardno" is just a counter; but check that here */ | ||
1859 | if (ndev != cardno) | ||
1860 | fprintf(stderr, "oops: ALSA cards not reported in order?\n"); | ||
1861 | sprintf(devname, "hw:%d", cardno ); | ||
1862 | /* fprintf(stderr, "\ntry %s...\n", devname); */ | ||
1863 | if (snd_ctl_open(&ctl, devname, 0) >= 0) | ||
1864 | { | ||
1865 | snd_ctl_card_info_malloc(&info); | ||
1866 | snd_ctl_card_info(ctl, info); | ||
1867 | desc = snd_ctl_card_info_get_name(info); | ||
1868 | snd_ctl_card_info_free(info); | ||
1869 | } | ||
1870 | else | ||
1871 | { | ||
1872 | fprintf(stderr, "ALSA card scan error\n"); | ||
1873 | desc = "???"; | ||
1874 | } | ||
1875 | /* fprintf(stderr, "name: %s\n", snd_ctl_card_info_get_name(info)); */ | ||
1876 | sprintf(indevlist + 2*ndev * devdescsize, "%s (hardware)", desc); | ||
1877 | sprintf(indevlist + (2*ndev + 1) * devdescsize, "%s (plug-in)", desc); | ||
1878 | sprintf(outdevlist + 2*ndev * devdescsize, "%s (hardware)", desc); | ||
1879 | sprintf(outdevlist + (2*ndev + 1) * devdescsize, "%s (plug-in)", desc); | ||
1880 | ndev++; | ||
1881 | } | ||
1882 | for (i = 0, j = 2*ndev; i < alsa_nnames; i++, j++) | ||
1883 | { | ||
1884 | if (j >= maxndev) | ||
1885 | break; | ||
1886 | snprintf(indevlist + j * devdescsize, devdescsize, "%s", | ||
1887 | alsa_names[i]); | ||
1888 | } | ||
1889 | *nindevs = *noutdevs = j; | ||
1890 | } | ||