]> git.proxmox.com Git - mirror_qemu.git/blob - audio/audio_legacy.c
b848001ff708f46c08790cf8a9082ab41f998b02
[mirror_qemu.git] / audio / audio_legacy.c
1 /*
2 * QEMU Audio subsystem: legacy configuration handling
3 *
4 * Copyright (c) 2015-2019 Zoltán Kővágó <DirtY.iCE.hu@gmail.com>
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 "qemu/osdep.h"
25 #include "audio.h"
26 #include "audio_int.h"
27 #include "qemu/cutils.h"
28 #include "qemu/timer.h"
29 #include "qapi/error.h"
30 #include "qapi/qapi-visit-audio.h"
31 #include "qapi/visitor-impl.h"
32
33 #define AUDIO_CAP "audio-legacy"
34 #include "audio_int.h"
35
36 static uint32_t toui32(const char *str)
37 {
38 unsigned long long ret;
39 if (parse_uint_full(str, &ret, 10) || ret > UINT32_MAX) {
40 dolog("Invalid integer value `%s'\n", str);
41 exit(1);
42 }
43 return ret;
44 }
45
46 /* helper functions to convert env variables */
47 static void get_bool(const char *env, bool *dst, bool *has_dst)
48 {
49 const char *val = getenv(env);
50 if (val) {
51 *dst = toui32(val) != 0;
52 *has_dst = true;
53 }
54 }
55
56 static void get_int(const char *env, uint32_t *dst, bool *has_dst)
57 {
58 const char *val = getenv(env);
59 if (val) {
60 *dst = toui32(val);
61 *has_dst = true;
62 }
63 }
64
65 static void get_str(const char *env, char **dst)
66 {
67 const char *val = getenv(env);
68 if (val) {
69 g_free(*dst);
70 *dst = g_strdup(val);
71 }
72 }
73
74 static void get_fmt(const char *env, AudioFormat *dst, bool *has_dst)
75 {
76 const char *val = getenv(env);
77 if (val) {
78 size_t i;
79 for (i = 0; AudioFormat_lookup.size; ++i) {
80 if (strcasecmp(val, AudioFormat_lookup.array[i]) == 0) {
81 *dst = i;
82 *has_dst = true;
83 return;
84 }
85 }
86
87 dolog("Invalid audio format `%s'\n", val);
88 exit(1);
89 }
90 }
91
92
93 #if defined(CONFIG_AUDIO_ALSA) || defined(CONFIG_AUDIO_DSOUND)
94 static void get_millis_to_usecs(const char *env, uint32_t *dst, bool *has_dst)
95 {
96 const char *val = getenv(env);
97 if (val) {
98 *dst = toui32(val) * 1000;
99 *has_dst = true;
100 }
101 }
102 #endif
103
104 #if defined(CONFIG_AUDIO_ALSA) || defined(CONFIG_AUDIO_COREAUDIO) || \
105 defined(CONFIG_AUDIO_PA) || defined(CONFIG_AUDIO_SDL) || \
106 defined(CONFIG_AUDIO_DSOUND) || defined(CONFIG_AUDIO_OSS)
107 static uint32_t frames_to_usecs(uint32_t frames,
108 AudiodevPerDirectionOptions *pdo)
109 {
110 uint32_t freq = pdo->has_frequency ? pdo->frequency : 44100;
111 return (frames * 1000000 + freq / 2) / freq;
112 }
113 #endif
114
115 #ifdef CONFIG_AUDIO_COREAUDIO
116 static void get_frames_to_usecs(const char *env, uint32_t *dst, bool *has_dst,
117 AudiodevPerDirectionOptions *pdo)
118 {
119 const char *val = getenv(env);
120 if (val) {
121 *dst = frames_to_usecs(toui32(val), pdo);
122 *has_dst = true;
123 }
124 }
125 #endif
126
127 #if defined(CONFIG_AUDIO_PA) || defined(CONFIG_AUDIO_SDL) || \
128 defined(CONFIG_AUDIO_DSOUND) || defined(CONFIG_AUDIO_OSS)
129 static uint32_t samples_to_usecs(uint32_t samples,
130 AudiodevPerDirectionOptions *pdo)
131 {
132 uint32_t channels = pdo->has_channels ? pdo->channels : 2;
133 return frames_to_usecs(samples / channels, pdo);
134 }
135 #endif
136
137 #if defined(CONFIG_AUDIO_PA) || defined(CONFIG_AUDIO_SDL)
138 static void get_samples_to_usecs(const char *env, uint32_t *dst, bool *has_dst,
139 AudiodevPerDirectionOptions *pdo)
140 {
141 const char *val = getenv(env);
142 if (val) {
143 *dst = samples_to_usecs(toui32(val), pdo);
144 *has_dst = true;
145 }
146 }
147 #endif
148
149 #if defined(CONFIG_AUDIO_DSOUND) || defined(CONFIG_AUDIO_OSS)
150 static uint32_t bytes_to_usecs(uint32_t bytes, AudiodevPerDirectionOptions *pdo)
151 {
152 AudioFormat fmt = pdo->has_format ? pdo->format : AUDIO_FORMAT_S16;
153 uint32_t bytes_per_sample = audioformat_bytes_per_sample(fmt);
154 return samples_to_usecs(bytes / bytes_per_sample, pdo);
155 }
156
157 static void get_bytes_to_usecs(const char *env, uint32_t *dst, bool *has_dst,
158 AudiodevPerDirectionOptions *pdo)
159 {
160 const char *val = getenv(env);
161 if (val) {
162 *dst = bytes_to_usecs(toui32(val), pdo);
163 *has_dst = true;
164 }
165 }
166 #endif
167
168 /* backend specific functions */
169
170 #ifdef CONFIG_AUDIO_ALSA
171 /* ALSA */
172 static void handle_alsa_per_direction(
173 AudiodevAlsaPerDirectionOptions *apdo, const char *prefix)
174 {
175 char buf[64];
176 size_t len = strlen(prefix);
177 bool size_in_usecs = false;
178 bool dummy;
179
180 memcpy(buf, prefix, len);
181 strcpy(buf + len, "TRY_POLL");
182 get_bool(buf, &apdo->try_poll, &apdo->has_try_poll);
183
184 strcpy(buf + len, "DEV");
185 get_str(buf, &apdo->dev);
186
187 strcpy(buf + len, "SIZE_IN_USEC");
188 get_bool(buf, &size_in_usecs, &dummy);
189
190 strcpy(buf + len, "PERIOD_SIZE");
191 get_int(buf, &apdo->period_length, &apdo->has_period_length);
192 if (apdo->has_period_length && !size_in_usecs) {
193 apdo->period_length = frames_to_usecs(
194 apdo->period_length,
195 qapi_AudiodevAlsaPerDirectionOptions_base(apdo));
196 }
197
198 strcpy(buf + len, "BUFFER_SIZE");
199 get_int(buf, &apdo->buffer_length, &apdo->has_buffer_length);
200 if (apdo->has_buffer_length && !size_in_usecs) {
201 apdo->buffer_length = frames_to_usecs(
202 apdo->buffer_length,
203 qapi_AudiodevAlsaPerDirectionOptions_base(apdo));
204 }
205 }
206
207 static void handle_alsa(Audiodev *dev)
208 {
209 AudiodevAlsaOptions *aopt = &dev->u.alsa;
210 handle_alsa_per_direction(aopt->in, "QEMU_ALSA_ADC_");
211 handle_alsa_per_direction(aopt->out, "QEMU_ALSA_DAC_");
212
213 get_millis_to_usecs("QEMU_ALSA_THRESHOLD",
214 &aopt->threshold, &aopt->has_threshold);
215 }
216 #endif
217
218 #ifdef CONFIG_AUDIO_COREAUDIO
219 /* coreaudio */
220 static void handle_coreaudio(Audiodev *dev)
221 {
222 get_frames_to_usecs(
223 "QEMU_COREAUDIO_BUFFER_SIZE",
224 &dev->u.coreaudio.out->buffer_length,
225 &dev->u.coreaudio.out->has_buffer_length,
226 qapi_AudiodevCoreaudioPerDirectionOptions_base(dev->u.coreaudio.out));
227 get_int("QEMU_COREAUDIO_BUFFER_COUNT",
228 &dev->u.coreaudio.out->buffer_count,
229 &dev->u.coreaudio.out->has_buffer_count);
230 }
231 #endif
232
233 #ifdef CONFIG_AUDIO_DSOUND
234 /* dsound */
235 static void handle_dsound(Audiodev *dev)
236 {
237 get_millis_to_usecs("QEMU_DSOUND_LATENCY_MILLIS",
238 &dev->u.dsound.latency, &dev->u.dsound.has_latency);
239 get_bytes_to_usecs("QEMU_DSOUND_BUFSIZE_OUT",
240 &dev->u.dsound.out->buffer_length,
241 &dev->u.dsound.out->has_buffer_length,
242 dev->u.dsound.out);
243 get_bytes_to_usecs("QEMU_DSOUND_BUFSIZE_IN",
244 &dev->u.dsound.in->buffer_length,
245 &dev->u.dsound.in->has_buffer_length,
246 dev->u.dsound.in);
247 }
248 #endif
249
250 #ifdef CONFIG_AUDIO_OSS
251 /* OSS */
252 static void handle_oss_per_direction(
253 AudiodevOssPerDirectionOptions *opdo, const char *try_poll_env,
254 const char *dev_env)
255 {
256 get_bool(try_poll_env, &opdo->try_poll, &opdo->has_try_poll);
257 get_str(dev_env, &opdo->dev);
258
259 get_bytes_to_usecs("QEMU_OSS_FRAGSIZE",
260 &opdo->buffer_length, &opdo->has_buffer_length,
261 qapi_AudiodevOssPerDirectionOptions_base(opdo));
262 get_int("QEMU_OSS_NFRAGS", &opdo->buffer_count,
263 &opdo->has_buffer_count);
264 }
265
266 static void handle_oss(Audiodev *dev)
267 {
268 AudiodevOssOptions *oopt = &dev->u.oss;
269 handle_oss_per_direction(oopt->in, "QEMU_AUDIO_ADC_TRY_POLL",
270 "QEMU_OSS_ADC_DEV");
271 handle_oss_per_direction(oopt->out, "QEMU_AUDIO_DAC_TRY_POLL",
272 "QEMU_OSS_DAC_DEV");
273
274 get_bool("QEMU_OSS_MMAP", &oopt->try_mmap, &oopt->has_try_mmap);
275 get_bool("QEMU_OSS_EXCLUSIVE", &oopt->exclusive, &oopt->has_exclusive);
276 get_int("QEMU_OSS_POLICY", &oopt->dsp_policy, &oopt->has_dsp_policy);
277 }
278 #endif
279
280 #ifdef CONFIG_AUDIO_PA
281 /* pulseaudio */
282 static void handle_pa_per_direction(
283 AudiodevPaPerDirectionOptions *ppdo, const char *env)
284 {
285 get_str(env, &ppdo->name);
286 }
287
288 static void handle_pa(Audiodev *dev)
289 {
290 handle_pa_per_direction(dev->u.pa.in, "QEMU_PA_SOURCE");
291 handle_pa_per_direction(dev->u.pa.out, "QEMU_PA_SINK");
292
293 get_samples_to_usecs(
294 "QEMU_PA_SAMPLES", &dev->u.pa.in->buffer_length,
295 &dev->u.pa.in->has_buffer_length,
296 qapi_AudiodevPaPerDirectionOptions_base(dev->u.pa.in));
297 get_samples_to_usecs(
298 "QEMU_PA_SAMPLES", &dev->u.pa.out->buffer_length,
299 &dev->u.pa.out->has_buffer_length,
300 qapi_AudiodevPaPerDirectionOptions_base(dev->u.pa.out));
301
302 get_str("QEMU_PA_SERVER", &dev->u.pa.server);
303 }
304 #endif
305
306 #ifdef CONFIG_AUDIO_SDL
307 /* SDL */
308 static void handle_sdl(Audiodev *dev)
309 {
310 /* SDL is output only */
311 get_samples_to_usecs("QEMU_SDL_SAMPLES", &dev->u.sdl.out->buffer_length,
312 &dev->u.sdl.out->has_buffer_length,
313 qapi_AudiodevSdlPerDirectionOptions_base(dev->u.sdl.out));
314 }
315 #endif
316
317 /* wav */
318 static void handle_wav(Audiodev *dev)
319 {
320 get_int("QEMU_WAV_FREQUENCY",
321 &dev->u.wav.out->frequency, &dev->u.wav.out->has_frequency);
322 get_fmt("QEMU_WAV_FORMAT", &dev->u.wav.out->format,
323 &dev->u.wav.out->has_format);
324 get_int("QEMU_WAV_DAC_FIXED_CHANNELS",
325 &dev->u.wav.out->channels, &dev->u.wav.out->has_channels);
326 get_str("QEMU_WAV_PATH", &dev->u.wav.path);
327 }
328
329 /* general */
330 static void handle_per_direction(
331 AudiodevPerDirectionOptions *pdo, const char *prefix)
332 {
333 char buf[64];
334 size_t len = strlen(prefix);
335
336 memcpy(buf, prefix, len);
337 strcpy(buf + len, "FIXED_SETTINGS");
338 get_bool(buf, &pdo->fixed_settings, &pdo->has_fixed_settings);
339
340 strcpy(buf + len, "FIXED_FREQ");
341 get_int(buf, &pdo->frequency, &pdo->has_frequency);
342
343 strcpy(buf + len, "FIXED_FMT");
344 get_fmt(buf, &pdo->format, &pdo->has_format);
345
346 strcpy(buf + len, "FIXED_CHANNELS");
347 get_int(buf, &pdo->channels, &pdo->has_channels);
348
349 strcpy(buf + len, "VOICES");
350 get_int(buf, &pdo->voices, &pdo->has_voices);
351 }
352
353 static AudiodevListEntry *legacy_opt(const char *drvname)
354 {
355 AudiodevListEntry *e = g_new0(AudiodevListEntry, 1);
356 e->dev = g_new0(Audiodev, 1);
357 e->dev->id = g_strdup(drvname);
358 e->dev->driver = qapi_enum_parse(
359 &AudiodevDriver_lookup, drvname, -1, &error_abort);
360
361 audio_create_pdos(e->dev);
362
363 handle_per_direction(audio_get_pdo_in(e->dev), "QEMU_AUDIO_ADC_");
364 handle_per_direction(audio_get_pdo_out(e->dev), "QEMU_AUDIO_DAC_");
365
366 /* Original description: Timer period in HZ (0 - use lowest possible) */
367 get_int("QEMU_AUDIO_TIMER_PERIOD",
368 &e->dev->timer_period, &e->dev->has_timer_period);
369 if (e->dev->has_timer_period && e->dev->timer_period) {
370 e->dev->timer_period = NANOSECONDS_PER_SECOND / 1000 /
371 e->dev->timer_period;
372 }
373
374 switch (e->dev->driver) {
375 #ifdef CONFIG_AUDIO_ALSA
376 case AUDIODEV_DRIVER_ALSA:
377 handle_alsa(e->dev);
378 break;
379 #endif
380
381 #ifdef CONFIG_AUDIO_COREAUDIO
382 case AUDIODEV_DRIVER_COREAUDIO:
383 handle_coreaudio(e->dev);
384 break;
385 #endif
386
387 #ifdef CONFIG_AUDIO_DSOUND
388 case AUDIODEV_DRIVER_DSOUND:
389 handle_dsound(e->dev);
390 break;
391 #endif
392
393 #ifdef CONFIG_AUDIO_OSS
394 case AUDIODEV_DRIVER_OSS:
395 handle_oss(e->dev);
396 break;
397 #endif
398
399 #ifdef CONFIG_AUDIO_PA
400 case AUDIODEV_DRIVER_PA:
401 handle_pa(e->dev);
402 break;
403 #endif
404
405 #ifdef CONFIG_AUDIO_SDL
406 case AUDIODEV_DRIVER_SDL:
407 handle_sdl(e->dev);
408 break;
409 #endif
410
411 case AUDIODEV_DRIVER_WAV:
412 handle_wav(e->dev);
413 break;
414
415 default:
416 break;
417 }
418
419 return e;
420 }
421
422 AudiodevListHead audio_handle_legacy_opts(void)
423 {
424 const char *drvname = getenv("QEMU_AUDIO_DRV");
425 AudiodevListHead head = QSIMPLEQ_HEAD_INITIALIZER(head);
426
427 if (drvname) {
428 AudiodevListEntry *e;
429 audio_driver *driver = audio_driver_lookup(drvname);
430 if (!driver) {
431 dolog("Unknown audio driver `%s'\n", drvname);
432 exit(1);
433 }
434 e = legacy_opt(drvname);
435 QSIMPLEQ_INSERT_TAIL(&head, e, next);
436 } else {
437 for (int i = 0; audio_prio_list[i]; i++) {
438 audio_driver *driver = audio_driver_lookup(audio_prio_list[i]);
439 if (driver && driver->can_be_default) {
440 AudiodevListEntry *e = legacy_opt(driver->name);
441 QSIMPLEQ_INSERT_TAIL(&head, e, next);
442 }
443 }
444 if (QSIMPLEQ_EMPTY(&head)) {
445 dolog("Internal error: no default audio driver available\n");
446 exit(1);
447 }
448 }
449
450 return head;
451 }
452
453 /* visitor to print -audiodev option */
454 typedef struct {
455 Visitor visitor;
456
457 bool comma;
458 GList *path;
459 } LegacyPrintVisitor;
460
461 static bool lv_start_struct(Visitor *v, const char *name, void **obj,
462 size_t size, Error **errp)
463 {
464 LegacyPrintVisitor *lv = (LegacyPrintVisitor *) v;
465 lv->path = g_list_append(lv->path, g_strdup(name));
466 return true;
467 }
468
469 static void lv_end_struct(Visitor *v, void **obj)
470 {
471 LegacyPrintVisitor *lv = (LegacyPrintVisitor *) v;
472 lv->path = g_list_delete_link(lv->path, g_list_last(lv->path));
473 }
474
475 static void lv_print_key(Visitor *v, const char *name)
476 {
477 GList *e;
478 LegacyPrintVisitor *lv = (LegacyPrintVisitor *) v;
479 if (lv->comma) {
480 putchar(',');
481 } else {
482 lv->comma = true;
483 }
484
485 for (e = lv->path; e; e = e->next) {
486 if (e->data) {
487 printf("%s.", (const char *) e->data);
488 }
489 }
490
491 printf("%s=", name);
492 }
493
494 static bool lv_type_int64(Visitor *v, const char *name, int64_t *obj,
495 Error **errp)
496 {
497 lv_print_key(v, name);
498 printf("%" PRIi64, *obj);
499 return true;
500 }
501
502 static bool lv_type_uint64(Visitor *v, const char *name, uint64_t *obj,
503 Error **errp)
504 {
505 lv_print_key(v, name);
506 printf("%" PRIu64, *obj);
507 return true;
508 }
509
510 static bool lv_type_bool(Visitor *v, const char *name, bool *obj, Error **errp)
511 {
512 lv_print_key(v, name);
513 printf("%s", *obj ? "on" : "off");
514 return true;
515 }
516
517 static bool lv_type_str(Visitor *v, const char *name, char **obj, Error **errp)
518 {
519 const char *str = *obj;
520 lv_print_key(v, name);
521
522 while (*str) {
523 if (*str == ',') {
524 putchar(',');
525 }
526 putchar(*str++);
527 }
528 return true;
529 }
530
531 static void lv_complete(Visitor *v, void *opaque)
532 {
533 LegacyPrintVisitor *lv = (LegacyPrintVisitor *) v;
534 assert(lv->path == NULL);
535 }
536
537 static void lv_free(Visitor *v)
538 {
539 LegacyPrintVisitor *lv = (LegacyPrintVisitor *) v;
540
541 g_list_free_full(lv->path, g_free);
542 g_free(lv);
543 }
544
545 static Visitor *legacy_visitor_new(void)
546 {
547 LegacyPrintVisitor *lv = g_new0(LegacyPrintVisitor, 1);
548
549 lv->visitor.start_struct = lv_start_struct;
550 lv->visitor.end_struct = lv_end_struct;
551 /* lists not supported */
552 lv->visitor.type_int64 = lv_type_int64;
553 lv->visitor.type_uint64 = lv_type_uint64;
554 lv->visitor.type_bool = lv_type_bool;
555 lv->visitor.type_str = lv_type_str;
556
557 lv->visitor.type = VISITOR_OUTPUT;
558 lv->visitor.complete = lv_complete;
559 lv->visitor.free = lv_free;
560
561 return &lv->visitor;
562 }
563
564 void audio_legacy_help(void)
565 {
566 AudiodevListHead head;
567 AudiodevListEntry *e;
568
569 printf("Environment variable based configuration deprecated.\n");
570 printf("Please use the new -audiodev option.\n");
571
572 head = audio_handle_legacy_opts();
573 printf("\nEquivalent -audiodev to your current environment variables:\n");
574 if (!getenv("QEMU_AUDIO_DRV")) {
575 printf("(Since you didn't specify QEMU_AUDIO_DRV, I'll list all "
576 "possibilities)\n");
577 }
578
579 QSIMPLEQ_FOREACH(e, &head, next) {
580 Visitor *v;
581 Audiodev *dev = e->dev;
582 printf("-audiodev ");
583
584 v = legacy_visitor_new();
585 visit_type_Audiodev(v, NULL, &dev, &error_abort);
586 visit_free(v);
587
588 printf("\n");
589 }
590 audio_free_audiodev_list(&head);
591 }