1 | /* |
---|
2 | * QEMU ALSA audio driver |
---|
3 | * |
---|
4 | * Copyright (c) 2005 Vassili Karpov (malc) |
---|
5 | * |
---|
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
---|
7 | * of this software and associated documentation files (the "Software"), to deal |
---|
8 | * in the Software without restriction, including without limitation the rights |
---|
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
---|
10 | * copies of the Software, and to permit persons to whom the Software is |
---|
11 | * furnished to do so, subject to the following conditions: |
---|
12 | * |
---|
13 | * The above copyright notice and this permission notice shall be included in |
---|
14 | * all copies or substantial portions of the Software. |
---|
15 | * |
---|
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
---|
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
---|
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
---|
19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
---|
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
---|
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
---|
22 | * THE SOFTWARE. |
---|
23 | */ |
---|
24 | #include <alsa/asoundlib.h> |
---|
25 | #include "vl.h" |
---|
26 | |
---|
27 | #define AUDIO_CAP "alsa" |
---|
28 | #include "audio_int.h" |
---|
29 | |
---|
30 | typedef struct ALSAVoiceOut { |
---|
31 | HWVoiceOut hw; |
---|
32 | void *pcm_buf; |
---|
33 | snd_pcm_t *handle; |
---|
34 | } ALSAVoiceOut; |
---|
35 | |
---|
36 | typedef struct ALSAVoiceIn { |
---|
37 | HWVoiceIn hw; |
---|
38 | snd_pcm_t *handle; |
---|
39 | void *pcm_buf; |
---|
40 | } ALSAVoiceIn; |
---|
41 | |
---|
42 | static struct { |
---|
43 | int size_in_usec_in; |
---|
44 | int size_in_usec_out; |
---|
45 | const char *pcm_name_in; |
---|
46 | const char *pcm_name_out; |
---|
47 | unsigned int buffer_size_in; |
---|
48 | unsigned int period_size_in; |
---|
49 | unsigned int buffer_size_out; |
---|
50 | unsigned int period_size_out; |
---|
51 | unsigned int threshold; |
---|
52 | |
---|
53 | int buffer_size_in_overriden; |
---|
54 | int period_size_in_overriden; |
---|
55 | |
---|
56 | int buffer_size_out_overriden; |
---|
57 | int period_size_out_overriden; |
---|
58 | int verbose; |
---|
59 | } conf = { |
---|
60 | #ifdef HIGH_LATENCY |
---|
61 | .size_in_usec_in = 1, |
---|
62 | .size_in_usec_out = 1, |
---|
63 | #endif |
---|
64 | .pcm_name_out = "default", |
---|
65 | .pcm_name_in = "default", |
---|
66 | #ifdef HIGH_LATENCY |
---|
67 | .buffer_size_in = 400000, |
---|
68 | .period_size_in = 400000 / 4, |
---|
69 | .buffer_size_out = 400000, |
---|
70 | .period_size_out = 400000 / 4, |
---|
71 | #else |
---|
72 | #define DEFAULT_BUFFER_SIZE 1024 |
---|
73 | #define DEFAULT_PERIOD_SIZE 256 |
---|
74 | .buffer_size_in = DEFAULT_BUFFER_SIZE * 4, |
---|
75 | .period_size_in = DEFAULT_PERIOD_SIZE * 4, |
---|
76 | .buffer_size_out = DEFAULT_BUFFER_SIZE, |
---|
77 | .period_size_out = DEFAULT_PERIOD_SIZE, |
---|
78 | .buffer_size_in_overriden = 0, |
---|
79 | .buffer_size_out_overriden = 0, |
---|
80 | .period_size_in_overriden = 0, |
---|
81 | .period_size_out_overriden = 0, |
---|
82 | #endif |
---|
83 | .threshold = 0, |
---|
84 | .verbose = 0 |
---|
85 | }; |
---|
86 | |
---|
87 | struct alsa_params_req { |
---|
88 | int freq; |
---|
89 | audfmt_e fmt; |
---|
90 | int nchannels; |
---|
91 | unsigned int buffer_size; |
---|
92 | unsigned int period_size; |
---|
93 | }; |
---|
94 | |
---|
95 | struct alsa_params_obt { |
---|
96 | int freq; |
---|
97 | audfmt_e fmt; |
---|
98 | int nchannels; |
---|
99 | snd_pcm_uframes_t samples; |
---|
100 | }; |
---|
101 | |
---|
102 | static void GCC_FMT_ATTR (2, 3) alsa_logerr (int err, const char *fmt, ...) |
---|
103 | { |
---|
104 | va_list ap; |
---|
105 | |
---|
106 | va_start (ap, fmt); |
---|
107 | AUD_vlog (AUDIO_CAP, fmt, ap); |
---|
108 | va_end (ap); |
---|
109 | |
---|
110 | AUD_log (AUDIO_CAP, "Reason: %s\n", snd_strerror (err)); |
---|
111 | } |
---|
112 | |
---|
113 | static void GCC_FMT_ATTR (3, 4) alsa_logerr2 ( |
---|
114 | int err, |
---|
115 | const char *typ, |
---|
116 | const char *fmt, |
---|
117 | ... |
---|
118 | ) |
---|
119 | { |
---|
120 | va_list ap; |
---|
121 | |
---|
122 | AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ); |
---|
123 | |
---|
124 | va_start (ap, fmt); |
---|
125 | AUD_vlog (AUDIO_CAP, fmt, ap); |
---|
126 | va_end (ap); |
---|
127 | |
---|
128 | AUD_log (AUDIO_CAP, "Reason: %s\n", snd_strerror (err)); |
---|
129 | } |
---|
130 | |
---|
131 | static void alsa_anal_close (snd_pcm_t **handlep) |
---|
132 | { |
---|
133 | int err = snd_pcm_close (*handlep); |
---|
134 | if (err) { |
---|
135 | alsa_logerr (err, "Failed to close PCM handle %p\n", *handlep); |
---|
136 | } |
---|
137 | *handlep = NULL; |
---|
138 | } |
---|
139 | |
---|
140 | static int alsa_write (SWVoiceOut *sw, void *buf, int len) |
---|
141 | { |
---|
142 | return audio_pcm_sw_write (sw, buf, len); |
---|
143 | } |
---|
144 | |
---|
145 | static int aud_to_alsafmt (audfmt_e fmt) |
---|
146 | { |
---|
147 | switch (fmt) { |
---|
148 | case AUD_FMT_S8: |
---|
149 | return SND_PCM_FORMAT_S8; |
---|
150 | |
---|
151 | case AUD_FMT_U8: |
---|
152 | return SND_PCM_FORMAT_U8; |
---|
153 | |
---|
154 | case AUD_FMT_S16: |
---|
155 | return SND_PCM_FORMAT_S16_LE; |
---|
156 | |
---|
157 | case AUD_FMT_U16: |
---|
158 | return SND_PCM_FORMAT_U16_LE; |
---|
159 | |
---|
160 | default: |
---|
161 | dolog ("Internal logic error: Bad audio format %d\n", fmt); |
---|
162 | #ifdef DEBUG_AUDIO |
---|
163 | abort (); |
---|
164 | #endif |
---|
165 | return SND_PCM_FORMAT_U8; |
---|
166 | } |
---|
167 | } |
---|
168 | |
---|
169 | static int alsa_to_audfmt (int alsafmt, audfmt_e *fmt, int *endianness) |
---|
170 | { |
---|
171 | switch (alsafmt) { |
---|
172 | case SND_PCM_FORMAT_S8: |
---|
173 | *endianness = 0; |
---|
174 | *fmt = AUD_FMT_S8; |
---|
175 | break; |
---|
176 | |
---|
177 | case SND_PCM_FORMAT_U8: |
---|
178 | *endianness = 0; |
---|
179 | *fmt = AUD_FMT_U8; |
---|
180 | break; |
---|
181 | |
---|
182 | case SND_PCM_FORMAT_S16_LE: |
---|
183 | *endianness = 0; |
---|
184 | *fmt = AUD_FMT_S16; |
---|
185 | break; |
---|
186 | |
---|
187 | case SND_PCM_FORMAT_U16_LE: |
---|
188 | *endianness = 0; |
---|
189 | *fmt = AUD_FMT_U16; |
---|
190 | break; |
---|
191 | |
---|
192 | case SND_PCM_FORMAT_S16_BE: |
---|
193 | *endianness = 1; |
---|
194 | *fmt = AUD_FMT_S16; |
---|
195 | break; |
---|
196 | |
---|
197 | case SND_PCM_FORMAT_U16_BE: |
---|
198 | *endianness = 1; |
---|
199 | *fmt = AUD_FMT_U16; |
---|
200 | break; |
---|
201 | |
---|
202 | default: |
---|
203 | dolog ("Unrecognized audio format %d\n", alsafmt); |
---|
204 | return -1; |
---|
205 | } |
---|
206 | |
---|
207 | return 0; |
---|
208 | } |
---|
209 | |
---|
210 | #if defined DEBUG_MISMATCHES || defined DEBUG |
---|
211 | static void alsa_dump_info (struct alsa_params_req *req, |
---|
212 | struct alsa_params_obt *obt) |
---|
213 | { |
---|
214 | dolog ("parameter | requested value | obtained value\n"); |
---|
215 | dolog ("format | %10d | %10d\n", req->fmt, obt->fmt); |
---|
216 | dolog ("channels | %10d | %10d\n", |
---|
217 | req->nchannels, obt->nchannels); |
---|
218 | dolog ("frequency | %10d | %10d\n", req->freq, obt->freq); |
---|
219 | dolog ("============================================\n"); |
---|
220 | dolog ("requested: buffer size %d period size %d\n", |
---|
221 | req->buffer_size, req->period_size); |
---|
222 | dolog ("obtained: samples %ld\n", obt->samples); |
---|
223 | } |
---|
224 | #endif |
---|
225 | |
---|
226 | static void alsa_set_threshold (snd_pcm_t *handle, snd_pcm_uframes_t threshold) |
---|
227 | { |
---|
228 | int err; |
---|
229 | snd_pcm_sw_params_t *sw_params; |
---|
230 | |
---|
231 | snd_pcm_sw_params_alloca (&sw_params); |
---|
232 | |
---|
233 | err = snd_pcm_sw_params_current (handle, sw_params); |
---|
234 | if (err < 0) { |
---|
235 | dolog ("Could not fully initialize DAC\n"); |
---|
236 | alsa_logerr (err, "Failed to get current software parameters\n"); |
---|
237 | return; |
---|
238 | } |
---|
239 | |
---|
240 | err = snd_pcm_sw_params_set_start_threshold (handle, sw_params, threshold); |
---|
241 | if (err < 0) { |
---|
242 | dolog ("Could not fully initialize DAC\n"); |
---|
243 | alsa_logerr (err, "Failed to set software threshold to %ld\n", |
---|
244 | threshold); |
---|
245 | return; |
---|
246 | } |
---|
247 | |
---|
248 | err = snd_pcm_sw_params (handle, sw_params); |
---|
249 | if (err < 0) { |
---|
250 | dolog ("Could not fully initialize DAC\n"); |
---|
251 | alsa_logerr (err, "Failed to set software parameters\n"); |
---|
252 | return; |
---|
253 | } |
---|
254 | } |
---|
255 | |
---|
256 | static int alsa_open (int in, struct alsa_params_req *req, |
---|
257 | struct alsa_params_obt *obt, snd_pcm_t **handlep) |
---|
258 | { |
---|
259 | snd_pcm_t *handle; |
---|
260 | snd_pcm_hw_params_t *hw_params; |
---|
261 | int err, freq, nchannels; |
---|
262 | const char *pcm_name = in ? conf.pcm_name_in : conf.pcm_name_out; |
---|
263 | unsigned int period_size, buffer_size; |
---|
264 | snd_pcm_uframes_t obt_buffer_size; |
---|
265 | const char *typ = in ? "ADC" : "DAC"; |
---|
266 | |
---|
267 | freq = req->freq; |
---|
268 | period_size = req->period_size; |
---|
269 | buffer_size = req->buffer_size; |
---|
270 | nchannels = req->nchannels; |
---|
271 | |
---|
272 | snd_pcm_hw_params_alloca (&hw_params); |
---|
273 | |
---|
274 | err = snd_pcm_open ( |
---|
275 | &handle, |
---|
276 | pcm_name, |
---|
277 | in ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK, |
---|
278 | SND_PCM_NONBLOCK |
---|
279 | ); |
---|
280 | if (err < 0) { |
---|
281 | alsa_logerr2 (err, typ, "Failed to open `%s':\n", pcm_name); |
---|
282 | return -1; |
---|
283 | } |
---|
284 | |
---|
285 | err = snd_pcm_hw_params_any (handle, hw_params); |
---|
286 | if (err < 0) { |
---|
287 | alsa_logerr2 (err, typ, "Failed to initialize hardware parameters\n"); |
---|
288 | goto err; |
---|
289 | } |
---|
290 | |
---|
291 | err = snd_pcm_hw_params_set_access ( |
---|
292 | handle, |
---|
293 | hw_params, |
---|
294 | SND_PCM_ACCESS_RW_INTERLEAVED |
---|
295 | ); |
---|
296 | if (err < 0) { |
---|
297 | alsa_logerr2 (err, typ, "Failed to set access type\n"); |
---|
298 | goto err; |
---|
299 | } |
---|
300 | |
---|
301 | err = snd_pcm_hw_params_set_format (handle, hw_params, req->fmt); |
---|
302 | if (err < 0) { |
---|
303 | alsa_logerr2 (err, typ, "Failed to set format %d\n", req->fmt); |
---|
304 | goto err; |
---|
305 | } |
---|
306 | |
---|
307 | err = snd_pcm_hw_params_set_rate_near (handle, hw_params, &freq, 0); |
---|
308 | if (err < 0) { |
---|
309 | alsa_logerr2 (err, typ, "Failed to set frequency %d\n", req->freq); |
---|
310 | goto err; |
---|
311 | } |
---|
312 | |
---|
313 | err = snd_pcm_hw_params_set_channels_near ( |
---|
314 | handle, |
---|
315 | hw_params, |
---|
316 | &nchannels |
---|
317 | ); |
---|
318 | if (err < 0) { |
---|
319 | alsa_logerr2 (err, typ, "Failed to set number of channels %d\n", |
---|
320 | req->nchannels); |
---|
321 | goto err; |
---|
322 | } |
---|
323 | |
---|
324 | if (nchannels != 1 && nchannels != 2) { |
---|
325 | alsa_logerr2 (err, typ, |
---|
326 | "Can not handle obtained number of channels %d\n", |
---|
327 | nchannels); |
---|
328 | goto err; |
---|
329 | } |
---|
330 | |
---|
331 | if (!((in && conf.size_in_usec_in) || (!in && conf.size_in_usec_out))) { |
---|
332 | if (!buffer_size) { |
---|
333 | buffer_size = DEFAULT_BUFFER_SIZE; |
---|
334 | period_size= DEFAULT_PERIOD_SIZE; |
---|
335 | } |
---|
336 | } |
---|
337 | |
---|
338 | if (buffer_size) { |
---|
339 | if ((in && conf.size_in_usec_in) || (!in && conf.size_in_usec_out)) { |
---|
340 | if (period_size) { |
---|
341 | err = snd_pcm_hw_params_set_period_time_near ( |
---|
342 | handle, |
---|
343 | hw_params, |
---|
344 | &period_size, |
---|
345 | 0 |
---|
346 | ); |
---|
347 | if (err < 0) { |
---|
348 | alsa_logerr2 (err, typ, |
---|
349 | "Failed to set period time %d\n", |
---|
350 | req->period_size); |
---|
351 | goto err; |
---|
352 | } |
---|
353 | } |
---|
354 | |
---|
355 | err = snd_pcm_hw_params_set_buffer_time_near ( |
---|
356 | handle, |
---|
357 | hw_params, |
---|
358 | &buffer_size, |
---|
359 | 0 |
---|
360 | ); |
---|
361 | |
---|
362 | if (err < 0) { |
---|
363 | alsa_logerr2 (err, typ, |
---|
364 | "Failed to set buffer time %d\n", |
---|
365 | req->buffer_size); |
---|
366 | goto err; |
---|
367 | } |
---|
368 | } |
---|
369 | else { |
---|
370 | int dir; |
---|
371 | snd_pcm_uframes_t minval; |
---|
372 | |
---|
373 | if (period_size) { |
---|
374 | minval = period_size; |
---|
375 | dir = 0; |
---|
376 | |
---|
377 | err = snd_pcm_hw_params_get_period_size_min ( |
---|
378 | hw_params, |
---|
379 | &minval, |
---|
380 | &dir |
---|
381 | ); |
---|
382 | if (err < 0) { |
---|
383 | alsa_logerr ( |
---|
384 | err, |
---|
385 | "Could not get minmal period size for %s\n", |
---|
386 | typ |
---|
387 | ); |
---|
388 | } |
---|
389 | else { |
---|
390 | if (period_size < minval) { |
---|
391 | if ((in && conf.period_size_in_overriden) |
---|
392 | || (!in && conf.period_size_out_overriden)) { |
---|
393 | dolog ("%s period size(%d) is less " |
---|
394 | "than minmal period size(%ld)\n", |
---|
395 | typ, |
---|
396 | period_size, |
---|
397 | minval); |
---|
398 | } |
---|
399 | period_size = minval; |
---|
400 | } |
---|
401 | } |
---|
402 | |
---|
403 | err = snd_pcm_hw_params_set_period_size ( |
---|
404 | handle, |
---|
405 | hw_params, |
---|
406 | period_size, |
---|
407 | 0 |
---|
408 | ); |
---|
409 | if (err < 0) { |
---|
410 | alsa_logerr2 (err, typ, "Failed to set period size %d\n", |
---|
411 | req->period_size); |
---|
412 | goto err; |
---|
413 | } |
---|
414 | } |
---|
415 | |
---|
416 | minval = buffer_size; |
---|
417 | err = snd_pcm_hw_params_get_buffer_size_min ( |
---|
418 | hw_params, |
---|
419 | &minval |
---|
420 | ); |
---|
421 | if (err < 0) { |
---|
422 | alsa_logerr (err, "Could not get minmal buffer size for %s\n", |
---|
423 | typ); |
---|
424 | } |
---|
425 | else { |
---|
426 | if (buffer_size < minval) { |
---|
427 | if ((in && conf.buffer_size_in_overriden) |
---|
428 | || (!in && conf.buffer_size_out_overriden)) { |
---|
429 | dolog ( |
---|
430 | "%s buffer size(%d) is less " |
---|
431 | "than minimal buffer size(%ld)\n", |
---|
432 | typ, |
---|
433 | buffer_size, |
---|
434 | minval |
---|
435 | ); |
---|
436 | } |
---|
437 | buffer_size = minval; |
---|
438 | } |
---|
439 | } |
---|
440 | |
---|
441 | err = snd_pcm_hw_params_set_buffer_size ( |
---|
442 | handle, |
---|
443 | hw_params, |
---|
444 | buffer_size |
---|
445 | ); |
---|
446 | if (err < 0) { |
---|
447 | alsa_logerr2 (err, typ, "Failed to set buffer size %d\n", |
---|
448 | req->buffer_size); |
---|
449 | goto err; |
---|
450 | } |
---|
451 | } |
---|
452 | } |
---|
453 | else { |
---|
454 | dolog ("warning: Buffer size is not set\n"); |
---|
455 | } |
---|
456 | |
---|
457 | err = snd_pcm_hw_params (handle, hw_params); |
---|
458 | if (err < 0) { |
---|
459 | alsa_logerr2 (err, typ, "Failed to apply audio parameters\n"); |
---|
460 | goto err; |
---|
461 | } |
---|
462 | |
---|
463 | err = snd_pcm_hw_params_get_buffer_size (hw_params, &obt_buffer_size); |
---|
464 | if (err < 0) { |
---|
465 | alsa_logerr2 (err, typ, "Failed to get buffer size\n"); |
---|
466 | goto err; |
---|
467 | } |
---|
468 | |
---|
469 | err = snd_pcm_prepare (handle); |
---|
470 | if (err < 0) { |
---|
471 | alsa_logerr2 (err, typ, "Could not prepare handle %p\n", handle); |
---|
472 | goto err; |
---|
473 | } |
---|
474 | |
---|
475 | if (!in && conf.threshold) { |
---|
476 | snd_pcm_uframes_t threshold; |
---|
477 | int bytes_per_sec; |
---|
478 | |
---|
479 | bytes_per_sec = freq |
---|
480 | << (nchannels == 2) |
---|
481 | << (req->fmt == AUD_FMT_S16 || req->fmt == AUD_FMT_U16); |
---|
482 | |
---|
483 | threshold = (conf.threshold * bytes_per_sec) / 1000; |
---|
484 | alsa_set_threshold (handle, threshold); |
---|
485 | } |
---|
486 | |
---|
487 | obt->fmt = req->fmt; |
---|
488 | obt->nchannels = nchannels; |
---|
489 | obt->freq = freq; |
---|
490 | obt->samples = obt_buffer_size; |
---|
491 | *handlep = handle; |
---|
492 | |
---|
493 | #if defined DEBUG_MISMATCHES || defined DEBUG |
---|
494 | if (obt->fmt != req->fmt || |
---|
495 | obt->nchannels != req->nchannels || |
---|
496 | obt->freq != req->freq) { |
---|
497 | dolog ("Audio paramters mismatch for %s\n", typ); |
---|
498 | alsa_dump_info (req, obt); |
---|
499 | } |
---|
500 | #endif |
---|
501 | |
---|
502 | #ifdef DEBUG |
---|
503 | alsa_dump_info (req, obt); |
---|
504 | #endif |
---|
505 | return 0; |
---|
506 | |
---|
507 | err: |
---|
508 | alsa_anal_close (&handle); |
---|
509 | return -1; |
---|
510 | } |
---|
511 | |
---|
512 | static int alsa_recover (snd_pcm_t *handle) |
---|
513 | { |
---|
514 | int err = snd_pcm_prepare (handle); |
---|
515 | if (err < 0) { |
---|
516 | alsa_logerr (err, "Failed to prepare handle %p\n", handle); |
---|
517 | return -1; |
---|
518 | } |
---|
519 | return 0; |
---|
520 | } |
---|
521 | |
---|
522 | static snd_pcm_sframes_t alsa_get_avail (snd_pcm_t *handle) |
---|
523 | { |
---|
524 | snd_pcm_sframes_t avail; |
---|
525 | |
---|
526 | avail = snd_pcm_avail_update (handle); |
---|
527 | if (avail < 0) { |
---|
528 | if (avail == -EPIPE) { |
---|
529 | if (!alsa_recover (handle)) { |
---|
530 | avail = snd_pcm_avail_update (handle); |
---|
531 | } |
---|
532 | } |
---|
533 | |
---|
534 | if (avail < 0) { |
---|
535 | alsa_logerr (avail, |
---|
536 | "Could not obtain number of available frames\n"); |
---|
537 | return -1; |
---|
538 | } |
---|
539 | } |
---|
540 | |
---|
541 | return avail; |
---|
542 | } |
---|
543 | |
---|
544 | static int alsa_run_out (HWVoiceOut *hw) |
---|
545 | { |
---|
546 | ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw; |
---|
547 | int rpos, live, decr; |
---|
548 | int samples; |
---|
549 | uint8_t *dst; |
---|
550 | st_sample_t *src; |
---|
551 | snd_pcm_sframes_t avail; |
---|
552 | |
---|
553 | live = audio_pcm_hw_get_live_out (hw); |
---|
554 | if (!live) { |
---|
555 | return 0; |
---|
556 | } |
---|
557 | |
---|
558 | avail = alsa_get_avail (alsa->handle); |
---|
559 | if (avail < 0) { |
---|
560 | dolog ("Could not get number of available playback frames\n"); |
---|
561 | return 0; |
---|
562 | } |
---|
563 | |
---|
564 | decr = audio_MIN (live, avail); |
---|
565 | samples = decr; |
---|
566 | rpos = hw->rpos; |
---|
567 | while (samples) { |
---|
568 | int left_till_end_samples = hw->samples - rpos; |
---|
569 | int len = audio_MIN (samples, left_till_end_samples); |
---|
570 | snd_pcm_sframes_t written; |
---|
571 | |
---|
572 | src = hw->mix_buf + rpos; |
---|
573 | dst = advance (alsa->pcm_buf, rpos << hw->info.shift); |
---|
574 | |
---|
575 | hw->clip (dst, src, len); |
---|
576 | |
---|
577 | while (len) { |
---|
578 | written = snd_pcm_writei (alsa->handle, dst, len); |
---|
579 | |
---|
580 | if (written <= 0) { |
---|
581 | switch (written) { |
---|
582 | case 0: |
---|
583 | if (conf.verbose) { |
---|
584 | dolog ("Failed to write %d frames (wrote zero)\n", len); |
---|
585 | } |
---|
586 | goto exit; |
---|
587 | |
---|
588 | case -EPIPE: |
---|
589 | if (alsa_recover (alsa->handle)) { |
---|
590 | alsa_logerr (written, "Failed to write %d frames\n", |
---|
591 | len); |
---|
592 | goto exit; |
---|
593 | } |
---|
594 | if (conf.verbose) { |
---|
595 | dolog ("Recovering from playback xrun\n"); |
---|
596 | } |
---|
597 | continue; |
---|
598 | |
---|
599 | case -EAGAIN: |
---|
600 | goto exit; |
---|
601 | |
---|
602 | default: |
---|
603 | alsa_logerr (written, "Failed to write %d frames to %p\n", |
---|
604 | len, dst); |
---|
605 | goto exit; |
---|
606 | } |
---|
607 | } |
---|
608 | |
---|
609 | rpos = (rpos + written) % hw->samples; |
---|
610 | samples -= written; |
---|
611 | len -= written; |
---|
612 | dst = advance (dst, written << hw->info.shift); |
---|
613 | src += written; |
---|
614 | } |
---|
615 | } |
---|
616 | |
---|
617 | exit: |
---|
618 | hw->rpos = rpos; |
---|
619 | return decr; |
---|
620 | } |
---|
621 | |
---|
622 | static void alsa_fini_out (HWVoiceOut *hw) |
---|
623 | { |
---|
624 | ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw; |
---|
625 | |
---|
626 | ldebug ("alsa_fini\n"); |
---|
627 | alsa_anal_close (&alsa->handle); |
---|
628 | |
---|
629 | if (alsa->pcm_buf) { |
---|
630 | qemu_free (alsa->pcm_buf); |
---|
631 | alsa->pcm_buf = NULL; |
---|
632 | } |
---|
633 | } |
---|
634 | |
---|
635 | static int alsa_init_out (HWVoiceOut *hw, audsettings_t *as) |
---|
636 | { |
---|
637 | ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw; |
---|
638 | struct alsa_params_req req; |
---|
639 | struct alsa_params_obt obt; |
---|
640 | audfmt_e effective_fmt; |
---|
641 | int endianness; |
---|
642 | int err; |
---|
643 | snd_pcm_t *handle; |
---|
644 | audsettings_t obt_as; |
---|
645 | |
---|
646 | req.fmt = aud_to_alsafmt (as->fmt); |
---|
647 | req.freq = as->freq; |
---|
648 | req.nchannels = as->nchannels; |
---|
649 | req.period_size = conf.period_size_out; |
---|
650 | req.buffer_size = conf.buffer_size_out; |
---|
651 | |
---|
652 | if (alsa_open (0, &req, &obt, &handle)) { |
---|
653 | return -1; |
---|
654 | } |
---|
655 | |
---|
656 | err = alsa_to_audfmt (obt.fmt, &effective_fmt, &endianness); |
---|
657 | if (err) { |
---|
658 | alsa_anal_close (&handle); |
---|
659 | return -1; |
---|
660 | } |
---|
661 | |
---|
662 | obt_as.freq = obt.freq; |
---|
663 | obt_as.nchannels = obt.nchannels; |
---|
664 | obt_as.fmt = effective_fmt; |
---|
665 | obt_as.endianness = endianness; |
---|
666 | |
---|
667 | audio_pcm_init_info (&hw->info, &obt_as); |
---|
668 | hw->samples = obt.samples; |
---|
669 | |
---|
670 | alsa->pcm_buf = audio_calloc (AUDIO_FUNC, obt.samples, 1 << hw->info.shift); |
---|
671 | if (!alsa->pcm_buf) { |
---|
672 | dolog ("Could not allocate DAC buffer (%d samples, each %d bytes)\n", |
---|
673 | hw->samples, 1 << hw->info.shift); |
---|
674 | alsa_anal_close (&handle); |
---|
675 | return -1; |
---|
676 | } |
---|
677 | |
---|
678 | alsa->handle = handle; |
---|
679 | return 0; |
---|
680 | } |
---|
681 | |
---|
682 | static int alsa_voice_ctl (snd_pcm_t *handle, const char *typ, int pause) |
---|
683 | { |
---|
684 | int err; |
---|
685 | |
---|
686 | if (pause) { |
---|
687 | err = snd_pcm_drop (handle); |
---|
688 | if (err < 0) { |
---|
689 | alsa_logerr (err, "Could not stop %s\n", typ); |
---|
690 | return -1; |
---|
691 | } |
---|
692 | } |
---|
693 | else { |
---|
694 | err = snd_pcm_prepare (handle); |
---|
695 | if (err < 0) { |
---|
696 | alsa_logerr (err, "Could not prepare handle for %s\n", typ); |
---|
697 | return -1; |
---|
698 | } |
---|
699 | } |
---|
700 | |
---|
701 | return 0; |
---|
702 | } |
---|
703 | |
---|
704 | static int alsa_ctl_out (HWVoiceOut *hw, int cmd, ...) |
---|
705 | { |
---|
706 | ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw; |
---|
707 | |
---|
708 | switch (cmd) { |
---|
709 | case VOICE_ENABLE: |
---|
710 | ldebug ("enabling voice\n"); |
---|
711 | return alsa_voice_ctl (alsa->handle, "playback", 0); |
---|
712 | |
---|
713 | case VOICE_DISABLE: |
---|
714 | ldebug ("disabling voice\n"); |
---|
715 | return alsa_voice_ctl (alsa->handle, "playback", 1); |
---|
716 | } |
---|
717 | |
---|
718 | return -1; |
---|
719 | } |
---|
720 | |
---|
721 | static int alsa_init_in (HWVoiceIn *hw, audsettings_t *as) |
---|
722 | { |
---|
723 | ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw; |
---|
724 | struct alsa_params_req req; |
---|
725 | struct alsa_params_obt obt; |
---|
726 | int endianness; |
---|
727 | int err; |
---|
728 | audfmt_e effective_fmt; |
---|
729 | snd_pcm_t *handle; |
---|
730 | audsettings_t obt_as; |
---|
731 | |
---|
732 | req.fmt = aud_to_alsafmt (as->fmt); |
---|
733 | req.freq = as->freq; |
---|
734 | req.nchannels = as->nchannels; |
---|
735 | req.period_size = conf.period_size_in; |
---|
736 | req.buffer_size = conf.buffer_size_in; |
---|
737 | |
---|
738 | if (alsa_open (1, &req, &obt, &handle)) { |
---|
739 | return -1; |
---|
740 | } |
---|
741 | |
---|
742 | err = alsa_to_audfmt (obt.fmt, &effective_fmt, &endianness); |
---|
743 | if (err) { |
---|
744 | alsa_anal_close (&handle); |
---|
745 | return -1; |
---|
746 | } |
---|
747 | |
---|
748 | obt_as.freq = obt.freq; |
---|
749 | obt_as.nchannels = obt.nchannels; |
---|
750 | obt_as.fmt = effective_fmt; |
---|
751 | obt_as.endianness = endianness; |
---|
752 | |
---|
753 | audio_pcm_init_info (&hw->info, &obt_as); |
---|
754 | hw->samples = obt.samples; |
---|
755 | |
---|
756 | alsa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift); |
---|
757 | if (!alsa->pcm_buf) { |
---|
758 | dolog ("Could not allocate ADC buffer (%d samples, each %d bytes)\n", |
---|
759 | hw->samples, 1 << hw->info.shift); |
---|
760 | alsa_anal_close (&handle); |
---|
761 | return -1; |
---|
762 | } |
---|
763 | |
---|
764 | alsa->handle = handle; |
---|
765 | return 0; |
---|
766 | } |
---|
767 | |
---|
768 | static void alsa_fini_in (HWVoiceIn *hw) |
---|
769 | { |
---|
770 | ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw; |
---|
771 | |
---|
772 | alsa_anal_close (&alsa->handle); |
---|
773 | |
---|
774 | if (alsa->pcm_buf) { |
---|
775 | qemu_free (alsa->pcm_buf); |
---|
776 | alsa->pcm_buf = NULL; |
---|
777 | } |
---|
778 | } |
---|
779 | |
---|
780 | static int alsa_run_in (HWVoiceIn *hw) |
---|
781 | { |
---|
782 | ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw; |
---|
783 | int hwshift = hw->info.shift; |
---|
784 | int i; |
---|
785 | int live = audio_pcm_hw_get_live_in (hw); |
---|
786 | int dead = hw->samples - live; |
---|
787 | int decr; |
---|
788 | struct { |
---|
789 | int add; |
---|
790 | int len; |
---|
791 | } bufs[2] = { |
---|
792 | { hw->wpos, 0 }, |
---|
793 | { 0, 0 } |
---|
794 | }; |
---|
795 | snd_pcm_sframes_t avail; |
---|
796 | snd_pcm_uframes_t read_samples = 0; |
---|
797 | |
---|
798 | if (!dead) { |
---|
799 | return 0; |
---|
800 | } |
---|
801 | |
---|
802 | avail = alsa_get_avail (alsa->handle); |
---|
803 | if (avail < 0) { |
---|
804 | dolog ("Could not get number of captured frames\n"); |
---|
805 | return 0; |
---|
806 | } |
---|
807 | |
---|
808 | if (!avail && (snd_pcm_state (alsa->handle) == SND_PCM_STATE_PREPARED)) { |
---|
809 | avail = hw->samples; |
---|
810 | } |
---|
811 | |
---|
812 | decr = audio_MIN (dead, avail); |
---|
813 | if (!decr) { |
---|
814 | return 0; |
---|
815 | } |
---|
816 | |
---|
817 | if (hw->wpos + decr > hw->samples) { |
---|
818 | bufs[0].len = (hw->samples - hw->wpos); |
---|
819 | bufs[1].len = (decr - (hw->samples - hw->wpos)); |
---|
820 | } |
---|
821 | else { |
---|
822 | bufs[0].len = decr; |
---|
823 | } |
---|
824 | |
---|
825 | for (i = 0; i < 2; ++i) { |
---|
826 | void *src; |
---|
827 | st_sample_t *dst; |
---|
828 | snd_pcm_sframes_t nread; |
---|
829 | snd_pcm_uframes_t len; |
---|
830 | |
---|
831 | len = bufs[i].len; |
---|
832 | |
---|
833 | src = advance (alsa->pcm_buf, bufs[i].add << hwshift); |
---|
834 | dst = hw->conv_buf + bufs[i].add; |
---|
835 | |
---|
836 | while (len) { |
---|
837 | nread = snd_pcm_readi (alsa->handle, src, len); |
---|
838 | |
---|
839 | if (nread <= 0) { |
---|
840 | switch (nread) { |
---|
841 | case 0: |
---|
842 | if (conf.verbose) { |
---|
843 | dolog ("Failed to read %ld frames (read zero)\n", len); |
---|
844 | } |
---|
845 | goto exit; |
---|
846 | |
---|
847 | case -EPIPE: |
---|
848 | if (alsa_recover (alsa->handle)) { |
---|
849 | alsa_logerr (nread, "Failed to read %ld frames\n", len); |
---|
850 | goto exit; |
---|
851 | } |
---|
852 | if (conf.verbose) { |
---|
853 | dolog ("Recovering from capture xrun\n"); |
---|
854 | } |
---|
855 | continue; |
---|
856 | |
---|
857 | case -EAGAIN: |
---|
858 | goto exit; |
---|
859 | |
---|
860 | default: |
---|
861 | alsa_logerr ( |
---|
862 | nread, |
---|
863 | "Failed to read %ld frames from %p\n", |
---|
864 | len, |
---|
865 | src |
---|
866 | ); |
---|
867 | goto exit; |
---|
868 | } |
---|
869 | } |
---|
870 | |
---|
871 | hw->conv (dst, src, nread, &nominal_volume); |
---|
872 | |
---|
873 | src = advance (src, nread << hwshift); |
---|
874 | dst += nread; |
---|
875 | |
---|
876 | read_samples += nread; |
---|
877 | len -= nread; |
---|
878 | } |
---|
879 | } |
---|
880 | |
---|
881 | exit: |
---|
882 | hw->wpos = (hw->wpos + read_samples) % hw->samples; |
---|
883 | return read_samples; |
---|
884 | } |
---|
885 | |
---|
886 | static int alsa_read (SWVoiceIn *sw, void *buf, int size) |
---|
887 | { |
---|
888 | return audio_pcm_sw_read (sw, buf, size); |
---|
889 | } |
---|
890 | |
---|
891 | static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...) |
---|
892 | { |
---|
893 | ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw; |
---|
894 | |
---|
895 | switch (cmd) { |
---|
896 | case VOICE_ENABLE: |
---|
897 | ldebug ("enabling voice\n"); |
---|
898 | return alsa_voice_ctl (alsa->handle, "capture", 0); |
---|
899 | |
---|
900 | case VOICE_DISABLE: |
---|
901 | ldebug ("disabling voice\n"); |
---|
902 | return alsa_voice_ctl (alsa->handle, "capture", 1); |
---|
903 | } |
---|
904 | |
---|
905 | return -1; |
---|
906 | } |
---|
907 | |
---|
908 | static void *alsa_audio_init (void) |
---|
909 | { |
---|
910 | return &conf; |
---|
911 | } |
---|
912 | |
---|
913 | static void alsa_audio_fini (void *opaque) |
---|
914 | { |
---|
915 | (void) opaque; |
---|
916 | } |
---|
917 | |
---|
918 | static struct audio_option alsa_options[] = { |
---|
919 | {"DAC_SIZE_IN_USEC", AUD_OPT_BOOL, &conf.size_in_usec_out, |
---|
920 | "DAC period/buffer size in microseconds (otherwise in frames)", NULL, 0}, |
---|
921 | {"DAC_PERIOD_SIZE", AUD_OPT_INT, &conf.period_size_out, |
---|
922 | "DAC period size", &conf.period_size_out_overriden, 0}, |
---|
923 | {"DAC_BUFFER_SIZE", AUD_OPT_INT, &conf.buffer_size_out, |
---|
924 | "DAC buffer size", &conf.buffer_size_out_overriden, 0}, |
---|
925 | |
---|
926 | {"ADC_SIZE_IN_USEC", AUD_OPT_BOOL, &conf.size_in_usec_in, |
---|
927 | "ADC period/buffer size in microseconds (otherwise in frames)", NULL, 0}, |
---|
928 | {"ADC_PERIOD_SIZE", AUD_OPT_INT, &conf.period_size_in, |
---|
929 | "ADC period size", &conf.period_size_in_overriden, 0}, |
---|
930 | {"ADC_BUFFER_SIZE", AUD_OPT_INT, &conf.buffer_size_in, |
---|
931 | "ADC buffer size", &conf.buffer_size_in_overriden, 0}, |
---|
932 | |
---|
933 | {"THRESHOLD", AUD_OPT_INT, &conf.threshold, |
---|
934 | "(undocumented)", NULL, 0}, |
---|
935 | |
---|
936 | {"DAC_DEV", AUD_OPT_STR, &conf.pcm_name_out, |
---|
937 | "DAC device name (for instance dmix)", NULL, 0}, |
---|
938 | |
---|
939 | {"ADC_DEV", AUD_OPT_STR, &conf.pcm_name_in, |
---|
940 | "ADC device name", NULL, 0}, |
---|
941 | |
---|
942 | {"VERBOSE", AUD_OPT_BOOL, &conf.verbose, |
---|
943 | "Behave in a more verbose way", NULL, 0}, |
---|
944 | |
---|
945 | {NULL, 0, NULL, NULL, NULL, 0} |
---|
946 | }; |
---|
947 | |
---|
948 | static struct audio_pcm_ops alsa_pcm_ops = { |
---|
949 | alsa_init_out, |
---|
950 | alsa_fini_out, |
---|
951 | alsa_run_out, |
---|
952 | alsa_write, |
---|
953 | alsa_ctl_out, |
---|
954 | |
---|
955 | alsa_init_in, |
---|
956 | alsa_fini_in, |
---|
957 | alsa_run_in, |
---|
958 | alsa_read, |
---|
959 | alsa_ctl_in |
---|
960 | }; |
---|
961 | |
---|
962 | struct audio_driver alsa_audio_driver = { |
---|
963 | INIT_FIELD (name = ) "alsa", |
---|
964 | INIT_FIELD (descr = ) "ALSA http://www.alsa-project.org", |
---|
965 | INIT_FIELD (options = ) alsa_options, |
---|
966 | INIT_FIELD (init = ) alsa_audio_init, |
---|
967 | INIT_FIELD (fini = ) alsa_audio_fini, |
---|
968 | INIT_FIELD (pcm_ops = ) &alsa_pcm_ops, |
---|
969 | INIT_FIELD (can_be_default = ) 1, |
---|
970 | INIT_FIELD (max_voices_out = ) INT_MAX, |
---|
971 | INIT_FIELD (max_voices_in = ) INT_MAX, |
---|
972 | INIT_FIELD (voice_size_out = ) sizeof (ALSAVoiceOut), |
---|
973 | INIT_FIELD (voice_size_in = ) sizeof (ALSAVoiceIn) |
---|
974 | }; |
---|