|
|
1.1 root 1: /*
2: * QEMU Audio subsystem
1.1.1.2 root 3: *
4: * Copyright (c) 2003-2005 Vassili Karpov (malc)
5: *
1.1 root 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: */
1.1.1.5 root 24: #include "hw/hw.h"
25: #include "audio.h"
1.1.1.7 ! root 26: #include "monitor.h"
1.1.1.5 root 27: #include "qemu-timer.h"
28: #include "sysemu.h"
1.1 root 29:
1.1.1.2 root 30: #define AUDIO_CAP "audio"
31: #include "audio_int.h"
1.1 root 32:
1.1.1.2 root 33: /* #define DEBUG_PLIVE */
34: /* #define DEBUG_LIVE */
35: /* #define DEBUG_OUT */
1.1.1.3 root 36: /* #define DEBUG_CAPTURE */
1.1 root 37:
1.1.1.2 root 38: #define SW_NAME(sw) (sw)->name ? (sw)->name : "unknown"
39:
40: static struct audio_driver *drvtab[] = {
1.1.1.6 root 41: AUDIO_DRIVERS
1.1.1.2 root 42: &no_audio_driver,
43: &wav_audio_driver
44: };
45:
46: struct fixed_settings {
47: int enabled;
48: int nb_voices;
49: int greedy;
1.1.1.6 root 50: struct audsettings settings;
1.1.1.2 root 51: };
52:
53: static struct {
54: struct fixed_settings fixed_out;
55: struct fixed_settings fixed_in;
56: union {
1.1.1.6 root 57: int hertz;
1.1.1.2 root 58: int64_t ticks;
59: } period;
60: int plive;
61: int log_to_monitor;
62: } conf = {
63: { /* DAC fixed settings */
64: 1, /* enabled */
65: 1, /* nb_voices */
66: 1, /* greedy */
67: {
68: 44100, /* freq */
69: 2, /* nchannels */
1.1.1.5 root 70: AUD_FMT_S16, /* fmt */
71: AUDIO_HOST_ENDIANNESS
1.1.1.2 root 72: }
73: },
74:
75: { /* ADC fixed settings */
76: 1, /* enabled */
77: 1, /* nb_voices */
78: 1, /* greedy */
79: {
80: 44100, /* freq */
81: 2, /* nchannels */
1.1.1.5 root 82: AUD_FMT_S16, /* fmt */
83: AUDIO_HOST_ENDIANNESS
1.1.1.2 root 84: }
85: },
86:
1.1.1.6 root 87: { 250 }, /* period */
1.1.1.2 root 88: 0, /* plive */
89: 0 /* log_to_monitor */
90: };
1.1 root 91:
1.1.1.2 root 92: static AudioState glob_audio_state;
93:
1.1.1.6 root 94: struct mixeng_volume nominal_volume = {
1.1.1.2 root 95: 0,
96: #ifdef FLOAT_MIXENG
97: 1.0,
98: 1.0
99: #else
1.1.1.6 root 100: 1ULL << 32,
101: 1ULL << 32
1.1.1.2 root 102: #endif
1.1 root 103: };
104:
105: /* http://www.df.lth.se/~john_e/gems/gem002d.html */
106: /* http://www.multi-platforms.com/Tips/PopCount.htm */
107: uint32_t popcount (uint32_t u)
108: {
109: u = ((u&0x55555555) + ((u>>1)&0x55555555));
110: u = ((u&0x33333333) + ((u>>2)&0x33333333));
111: u = ((u&0x0f0f0f0f) + ((u>>4)&0x0f0f0f0f));
112: u = ((u&0x00ff00ff) + ((u>>8)&0x00ff00ff));
113: u = ( u&0x0000ffff) + (u>>16);
114: return u;
115: }
116:
117: inline uint32_t lsbindex (uint32_t u)
118: {
119: return popcount ((u&-u)-1);
120: }
121:
1.1.1.2 root 122: #ifdef AUDIO_IS_FLAWLESS_AND_NO_CHECKS_ARE_REQURIED
123: #error No its not
124: #else
125: int audio_bug (const char *funcname, int cond)
126: {
127: if (cond) {
128: static int shown;
129:
1.1.1.3 root 130: AUD_log (NULL, "A bug was just triggered in %s\n", funcname);
1.1.1.2 root 131: if (!shown) {
132: shown = 1;
133: AUD_log (NULL, "Save all your work and restart without audio\n");
134: AUD_log (NULL, "Please send bug report to [email protected]\n");
135: AUD_log (NULL, "I am sorry\n");
136: }
137: AUD_log (NULL, "Context:\n");
138:
139: #if defined AUDIO_BREAKPOINT_ON_BUG
140: # if defined HOST_I386
141: # if defined __GNUC__
142: __asm__ ("int3");
143: # elif defined _MSC_VER
144: _asm _emit 0xcc;
145: # else
146: abort ();
147: # endif
148: # else
149: abort ();
150: # endif
151: #endif
152: }
153:
154: return cond;
155: }
156: #endif
157:
1.1.1.5 root 158: static inline int audio_bits_to_index (int bits)
159: {
160: switch (bits) {
161: case 8:
162: return 0;
163:
164: case 16:
165: return 1;
166:
167: case 32:
168: return 2;
169:
170: default:
171: audio_bug ("bits_to_index", 1);
172: AUD_log (NULL, "invalid bits %d\n", bits);
173: return 0;
174: }
175: }
176:
1.1.1.2 root 177: void *audio_calloc (const char *funcname, int nmemb, size_t size)
178: {
179: int cond;
180: size_t len;
181:
182: len = nmemb * size;
183: cond = !nmemb || !size;
184: cond |= nmemb < 0;
185: cond |= len < size;
186:
187: if (audio_bug ("audio_calloc", cond)) {
188: AUD_log (NULL, "%s passed invalid arguments to audio_calloc\n",
189: funcname);
190: AUD_log (NULL, "nmemb=%d size=%zu (len=%zu)\n", nmemb, size, len);
191: return NULL;
192: }
193:
194: return qemu_mallocz (len);
195: }
196:
197: static char *audio_alloc_prefix (const char *s)
1.1 root 198: {
1.1.1.2 root 199: const char qemu_prefix[] = "QEMU_";
1.1.1.6 root 200: size_t len, i;
201: char *r, *u;
1.1.1.2 root 202:
203: if (!s) {
204: return NULL;
205: }
206:
207: len = strlen (s);
208: r = qemu_malloc (len + sizeof (qemu_prefix));
209:
1.1.1.6 root 210: u = r + sizeof (qemu_prefix) - 1;
1.1.1.2 root 211:
1.1.1.6 root 212: pstrcpy (r, len + sizeof (qemu_prefix), qemu_prefix);
213: pstrcat (r, len + sizeof (qemu_prefix), s);
1.1.1.2 root 214:
1.1.1.6 root 215: for (i = 0; i < len; ++i) {
216: u[i] = qemu_toupper(u[i]);
1.1.1.2 root 217: }
1.1.1.6 root 218:
1.1.1.2 root 219: return r;
220: }
221:
1.1.1.5 root 222: static const char *audio_audfmt_to_string (audfmt_e fmt)
1.1.1.2 root 223: {
224: switch (fmt) {
225: case AUD_FMT_U8:
226: return "U8";
227:
228: case AUD_FMT_U16:
229: return "U16";
230:
231: case AUD_FMT_S8:
232: return "S8";
233:
234: case AUD_FMT_S16:
235: return "S16";
1.1.1.5 root 236:
237: case AUD_FMT_U32:
238: return "U32";
239:
240: case AUD_FMT_S32:
241: return "S32";
1.1.1.2 root 242: }
243:
244: dolog ("Bogus audfmt %d returning S16\n", fmt);
245: return "S16";
246: }
247:
1.1.1.5 root 248: static audfmt_e audio_string_to_audfmt (const char *s, audfmt_e defval,
249: int *defaultp)
1.1.1.2 root 250: {
251: if (!strcasecmp (s, "u8")) {
252: *defaultp = 0;
253: return AUD_FMT_U8;
254: }
255: else if (!strcasecmp (s, "u16")) {
256: *defaultp = 0;
257: return AUD_FMT_U16;
258: }
1.1.1.5 root 259: else if (!strcasecmp (s, "u32")) {
260: *defaultp = 0;
261: return AUD_FMT_U32;
262: }
1.1.1.2 root 263: else if (!strcasecmp (s, "s8")) {
264: *defaultp = 0;
265: return AUD_FMT_S8;
266: }
267: else if (!strcasecmp (s, "s16")) {
268: *defaultp = 0;
269: return AUD_FMT_S16;
270: }
1.1.1.5 root 271: else if (!strcasecmp (s, "s32")) {
272: *defaultp = 0;
273: return AUD_FMT_S32;
274: }
1.1.1.2 root 275: else {
276: dolog ("Bogus audio format `%s' using %s\n",
277: s, audio_audfmt_to_string (defval));
278: *defaultp = 1;
279: return defval;
280: }
281: }
282:
283: static audfmt_e audio_get_conf_fmt (const char *envname,
284: audfmt_e defval,
285: int *defaultp)
286: {
287: const char *var = getenv (envname);
288: if (!var) {
289: *defaultp = 1;
290: return defval;
291: }
292: return audio_string_to_audfmt (var, defval, defaultp);
293: }
294:
295: static int audio_get_conf_int (const char *key, int defval, int *defaultp)
296: {
297: int val;
1.1 root 298: char *strval;
299:
300: strval = getenv (key);
301: if (strval) {
1.1.1.2 root 302: *defaultp = 0;
1.1 root 303: val = atoi (strval);
1.1.1.2 root 304: return val;
305: }
306: else {
307: *defaultp = 1;
308: return defval;
1.1 root 309: }
310: }
311:
1.1.1.2 root 312: static const char *audio_get_conf_str (const char *key,
313: const char *defval,
314: int *defaultp)
1.1 root 315: {
316: const char *val = getenv (key);
1.1.1.2 root 317: if (!val) {
318: *defaultp = 1;
1.1 root 319: return defval;
1.1.1.2 root 320: }
321: else {
322: *defaultp = 0;
1.1 root 323: return val;
1.1.1.2 root 324: }
325: }
326:
327: void AUD_vlog (const char *cap, const char *fmt, va_list ap)
328: {
329: if (conf.log_to_monitor) {
330: if (cap) {
1.1.1.7 ! root 331: monitor_printf(cur_mon, "%s: ", cap);
1.1.1.2 root 332: }
333:
1.1.1.7 ! root 334: monitor_vprintf(cur_mon, fmt, ap);
1.1.1.2 root 335: }
336: else {
337: if (cap) {
338: fprintf (stderr, "%s: ", cap);
339: }
340:
341: vfprintf (stderr, fmt, ap);
342: }
1.1 root 343: }
344:
345: void AUD_log (const char *cap, const char *fmt, ...)
346: {
347: va_list ap;
1.1.1.2 root 348:
1.1 root 349: va_start (ap, fmt);
1.1.1.2 root 350: AUD_vlog (cap, fmt, ap);
1.1 root 351: va_end (ap);
352: }
353:
1.1.1.2 root 354: static void audio_print_options (const char *prefix,
355: struct audio_option *opt)
1.1 root 356: {
1.1.1.2 root 357: char *uprefix;
358:
359: if (!prefix) {
360: dolog ("No prefix specified\n");
361: return;
362: }
363:
364: if (!opt) {
365: dolog ("No options\n");
366: return;
367: }
368:
369: uprefix = audio_alloc_prefix (prefix);
370:
371: for (; opt->name; opt++) {
372: const char *state = "default";
373: printf (" %s_%s: ", uprefix, opt->name);
374:
1.1.1.5 root 375: if (opt->overriddenp && *opt->overriddenp) {
1.1.1.2 root 376: state = "current";
377: }
378:
379: switch (opt->tag) {
380: case AUD_OPT_BOOL:
381: {
382: int *intp = opt->valp;
383: printf ("boolean, %s = %d\n", state, *intp ? 1 : 0);
384: }
385: break;
386:
387: case AUD_OPT_INT:
388: {
389: int *intp = opt->valp;
390: printf ("integer, %s = %d\n", state, *intp);
391: }
392: break;
393:
394: case AUD_OPT_FMT:
395: {
396: audfmt_e *fmtp = opt->valp;
397: printf (
1.1.1.6 root 398: "format, %s = %s, (one of: U8 S8 U16 S16 U32 S32)\n",
1.1.1.2 root 399: state,
400: audio_audfmt_to_string (*fmtp)
401: );
402: }
403: break;
404:
405: case AUD_OPT_STR:
406: {
407: const char **strp = opt->valp;
408: printf ("string, %s = %s\n",
409: state,
410: *strp ? *strp : "(not set)");
411: }
412: break;
413:
414: default:
415: printf ("???\n");
416: dolog ("Bad value tag for option %s_%s %d\n",
417: uprefix, opt->name, opt->tag);
418: break;
419: }
420: printf (" %s\n", opt->descr);
421: }
422:
423: qemu_free (uprefix);
1.1 root 424: }
425:
1.1.1.2 root 426: static void audio_process_options (const char *prefix,
427: struct audio_option *opt)
1.1 root 428: {
1.1.1.2 root 429: char *optname;
430: const char qemu_prefix[] = "QEMU_";
1.1.1.6 root 431: size_t preflen, optlen;
1.1.1.2 root 432:
433: if (audio_bug (AUDIO_FUNC, !prefix)) {
434: dolog ("prefix = NULL\n");
435: return;
436: }
437:
438: if (audio_bug (AUDIO_FUNC, !opt)) {
439: dolog ("opt = NULL\n");
440: return;
441: }
442:
443: preflen = strlen (prefix);
444:
445: for (; opt->name; opt++) {
446: size_t len, i;
447: int def;
448:
449: if (!opt->valp) {
450: dolog ("Option value pointer for `%s' is not set\n",
451: opt->name);
452: continue;
453: }
454:
455: len = strlen (opt->name);
456: /* len of opt->name + len of prefix + size of qemu_prefix
457: * (includes trailing zero) + zero + underscore (on behalf of
458: * sizeof) */
1.1.1.6 root 459: optlen = len + preflen + sizeof (qemu_prefix) + 1;
460: optname = qemu_malloc (optlen);
1.1.1.2 root 461:
1.1.1.6 root 462: pstrcpy (optname, optlen, qemu_prefix);
1.1.1.2 root 463:
464: /* copy while upper-casing, including trailing zero */
465: for (i = 0; i <= preflen; ++i) {
1.1.1.6 root 466: optname[i + sizeof (qemu_prefix) - 1] = qemu_toupper(prefix[i]);
1.1.1.2 root 467: }
1.1.1.6 root 468: pstrcat (optname, optlen, "_");
469: pstrcat (optname, optlen, opt->name);
1.1.1.2 root 470:
471: def = 1;
472: switch (opt->tag) {
473: case AUD_OPT_BOOL:
474: case AUD_OPT_INT:
475: {
476: int *intp = opt->valp;
477: *intp = audio_get_conf_int (optname, *intp, &def);
478: }
479: break;
480:
481: case AUD_OPT_FMT:
482: {
483: audfmt_e *fmtp = opt->valp;
484: *fmtp = audio_get_conf_fmt (optname, *fmtp, &def);
485: }
486: break;
487:
488: case AUD_OPT_STR:
489: {
490: const char **strp = opt->valp;
491: *strp = audio_get_conf_str (optname, *strp, &def);
492: }
493: break;
1.1 root 494:
1.1.1.2 root 495: default:
496: dolog ("Bad value tag for option `%s' - %d\n",
497: optname, opt->tag);
498: break;
499: }
500:
1.1.1.5 root 501: if (!opt->overriddenp) {
502: opt->overriddenp = &opt->overridden;
1.1.1.2 root 503: }
1.1.1.5 root 504: *opt->overriddenp = !def;
1.1.1.2 root 505: qemu_free (optname);
506: }
507: }
508:
1.1.1.6 root 509: static void audio_print_settings (struct audsettings *as)
1.1.1.2 root 510: {
511: dolog ("frequency=%d nchannels=%d fmt=", as->freq, as->nchannels);
512:
513: switch (as->fmt) {
514: case AUD_FMT_S8:
515: AUD_log (NULL, "S8");
516: break;
517: case AUD_FMT_U8:
518: AUD_log (NULL, "U8");
519: break;
520: case AUD_FMT_S16:
521: AUD_log (NULL, "S16");
522: break;
523: case AUD_FMT_U16:
524: AUD_log (NULL, "U16");
525: break;
1.1.1.6 root 526: case AUD_FMT_S32:
527: AUD_log (NULL, "S32");
528: break;
529: case AUD_FMT_U32:
530: AUD_log (NULL, "U32");
531: break;
1.1.1.2 root 532: default:
533: AUD_log (NULL, "invalid(%d)", as->fmt);
534: break;
535: }
1.1.1.3 root 536:
537: AUD_log (NULL, " endianness=");
538: switch (as->endianness) {
539: case 0:
540: AUD_log (NULL, "little");
541: break;
542: case 1:
543: AUD_log (NULL, "big");
544: break;
545: default:
546: AUD_log (NULL, "invalid");
547: break;
548: }
1.1.1.2 root 549: AUD_log (NULL, "\n");
550: }
551:
1.1.1.6 root 552: static int audio_validate_settings (struct audsettings *as)
1.1.1.2 root 553: {
554: int invalid;
555:
556: invalid = as->nchannels != 1 && as->nchannels != 2;
1.1.1.3 root 557: invalid |= as->endianness != 0 && as->endianness != 1;
1.1.1.2 root 558:
559: switch (as->fmt) {
560: case AUD_FMT_S8:
561: case AUD_FMT_U8:
562: case AUD_FMT_S16:
563: case AUD_FMT_U16:
1.1.1.5 root 564: case AUD_FMT_S32:
565: case AUD_FMT_U32:
1.1.1.2 root 566: break;
567: default:
568: invalid = 1;
569: break;
570: }
571:
572: invalid |= as->freq <= 0;
1.1.1.3 root 573: return invalid ? -1 : 0;
1.1 root 574: }
575:
1.1.1.6 root 576: static int audio_pcm_info_eq (struct audio_pcm_info *info, struct audsettings *as)
1.1 root 577: {
1.1.1.2 root 578: int bits = 8, sign = 0;
579:
580: switch (as->fmt) {
581: case AUD_FMT_S8:
582: sign = 1;
583: case AUD_FMT_U8:
584: break;
585:
586: case AUD_FMT_S16:
587: sign = 1;
588: case AUD_FMT_U16:
589: bits = 16;
590: break;
1.1.1.5 root 591:
592: case AUD_FMT_S32:
593: sign = 1;
594: case AUD_FMT_U32:
595: bits = 32;
596: break;
1.1.1.2 root 597: }
598: return info->freq == as->freq
599: && info->nchannels == as->nchannels
600: && info->sign == sign
1.1.1.3 root 601: && info->bits == bits
602: && info->swap_endianness == (as->endianness != AUDIO_HOST_ENDIANNESS);
1.1 root 603: }
604:
1.1.1.6 root 605: void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as)
1.1 root 606: {
1.1.1.5 root 607: int bits = 8, sign = 0, shift = 0;
1.1 root 608:
1.1.1.2 root 609: switch (as->fmt) {
1.1 root 610: case AUD_FMT_S8:
611: sign = 1;
612: case AUD_FMT_U8:
613: break;
614:
615: case AUD_FMT_S16:
616: sign = 1;
617: case AUD_FMT_U16:
618: bits = 16;
1.1.1.5 root 619: shift = 1;
620: break;
621:
622: case AUD_FMT_S32:
623: sign = 1;
624: case AUD_FMT_U32:
625: bits = 32;
626: shift = 2;
1.1 root 627: break;
628: }
629:
1.1.1.2 root 630: info->freq = as->freq;
631: info->bits = bits;
632: info->sign = sign;
633: info->nchannels = as->nchannels;
1.1.1.5 root 634: info->shift = (as->nchannels == 2) + shift;
1.1.1.2 root 635: info->align = (1 << info->shift) - 1;
636: info->bytes_per_second = info->freq << info->shift;
1.1.1.3 root 637: info->swap_endianness = (as->endianness != AUDIO_HOST_ENDIANNESS);
1.1 root 638: }
639:
1.1.1.2 root 640: void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len)
1.1 root 641: {
1.1.1.2 root 642: if (!len) {
643: return;
644: }
645:
646: if (info->sign) {
1.1.1.4 root 647: memset (buf, 0x00, len << info->shift);
1.1.1.2 root 648: }
649: else {
1.1.1.5 root 650: switch (info->bits) {
651: case 8:
1.1.1.4 root 652: memset (buf, 0x80, len << info->shift);
1.1.1.5 root 653: break;
1.1.1.2 root 654:
1.1.1.5 root 655: case 16:
656: {
657: int i;
658: uint16_t *p = buf;
659: int shift = info->nchannels - 1;
660: short s = INT16_MAX;
661:
662: if (info->swap_endianness) {
663: s = bswap16 (s);
664: }
665:
666: for (i = 0; i < len << shift; i++) {
667: p[i] = s;
668: }
1.1.1.2 root 669: }
1.1.1.5 root 670: break;
671:
672: case 32:
673: {
674: int i;
675: uint32_t *p = buf;
676: int shift = info->nchannels - 1;
677: int32_t s = INT32_MAX;
678:
679: if (info->swap_endianness) {
680: s = bswap32 (s);
681: }
1.1.1.2 root 682:
1.1.1.5 root 683: for (i = 0; i < len << shift; i++) {
684: p[i] = s;
685: }
1.1.1.2 root 686: }
1.1.1.5 root 687: break;
688:
689: default:
690: AUD_log (NULL, "audio_pcm_info_clear_buf: invalid bits %d\n",
691: info->bits);
692: break;
1.1.1.2 root 693: }
694: }
1.1 root 695: }
696:
1.1.1.2 root 697: /*
1.1.1.3 root 698: * Capture
699: */
1.1.1.6 root 700: static void noop_conv (struct st_sample *dst, const void *src,
701: int samples, struct mixeng_volume *vol)
1.1.1.3 root 702: {
703: (void) src;
704: (void) dst;
705: (void) samples;
706: (void) vol;
707: }
708:
709: static CaptureVoiceOut *audio_pcm_capture_find_specific (
1.1.1.6 root 710: struct audsettings *as
1.1.1.3 root 711: )
712: {
713: CaptureVoiceOut *cap;
1.1.1.7 ! root 714: AudioState *s = &glob_audio_state;
1.1.1.3 root 715:
716: for (cap = s->cap_head.lh_first; cap; cap = cap->entries.le_next) {
717: if (audio_pcm_info_eq (&cap->hw.info, as)) {
718: return cap;
719: }
720: }
721: return NULL;
722: }
723:
724: static void audio_notify_capture (CaptureVoiceOut *cap, audcnotification_e cmd)
725: {
726: struct capture_callback *cb;
727:
728: #ifdef DEBUG_CAPTURE
729: dolog ("notification %d sent\n", cmd);
730: #endif
731: for (cb = cap->cb_head.lh_first; cb; cb = cb->entries.le_next) {
732: cb->ops.notify (cb->opaque, cmd);
733: }
734: }
735:
736: static void audio_capture_maybe_changed (CaptureVoiceOut *cap, int enabled)
737: {
738: if (cap->hw.enabled != enabled) {
739: audcnotification_e cmd;
740: cap->hw.enabled = enabled;
741: cmd = enabled ? AUD_CNOTIFY_ENABLE : AUD_CNOTIFY_DISABLE;
742: audio_notify_capture (cap, cmd);
743: }
744: }
745:
746: static void audio_recalc_and_notify_capture (CaptureVoiceOut *cap)
747: {
748: HWVoiceOut *hw = &cap->hw;
749: SWVoiceOut *sw;
750: int enabled = 0;
751:
752: for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {
753: if (sw->active) {
754: enabled = 1;
755: break;
756: }
757: }
758: audio_capture_maybe_changed (cap, enabled);
759: }
760:
761: static void audio_detach_capture (HWVoiceOut *hw)
762: {
763: SWVoiceCap *sc = hw->cap_head.lh_first;
764:
765: while (sc) {
766: SWVoiceCap *sc1 = sc->entries.le_next;
767: SWVoiceOut *sw = &sc->sw;
768: CaptureVoiceOut *cap = sc->cap;
769: int was_active = sw->active;
770:
771: if (sw->rate) {
772: st_rate_stop (sw->rate);
773: sw->rate = NULL;
774: }
775:
776: LIST_REMOVE (sw, entries);
777: LIST_REMOVE (sc, entries);
778: qemu_free (sc);
779: if (was_active) {
780: /* We have removed soft voice from the capture:
781: this might have changed the overall status of the capture
782: since this might have been the only active voice */
783: audio_recalc_and_notify_capture (cap);
784: }
785: sc = sc1;
786: }
787: }
788:
1.1.1.7 ! root 789: static int audio_attach_capture (HWVoiceOut *hw)
1.1.1.3 root 790: {
1.1.1.7 ! root 791: AudioState *s = &glob_audio_state;
1.1.1.3 root 792: CaptureVoiceOut *cap;
793:
794: audio_detach_capture (hw);
795: for (cap = s->cap_head.lh_first; cap; cap = cap->entries.le_next) {
796: SWVoiceCap *sc;
797: SWVoiceOut *sw;
798: HWVoiceOut *hw_cap = &cap->hw;
799:
800: sc = audio_calloc (AUDIO_FUNC, 1, sizeof (*sc));
801: if (!sc) {
802: dolog ("Could not allocate soft capture voice (%zu bytes)\n",
803: sizeof (*sc));
804: return -1;
805: }
806:
807: sc->cap = cap;
808: sw = &sc->sw;
809: sw->hw = hw_cap;
810: sw->info = hw->info;
811: sw->empty = 1;
812: sw->active = hw->enabled;
813: sw->conv = noop_conv;
814: sw->ratio = ((int64_t) hw_cap->info.freq << 32) / sw->info.freq;
815: sw->rate = st_rate_start (sw->info.freq, hw_cap->info.freq);
816: if (!sw->rate) {
817: dolog ("Could not start rate conversion for `%s'\n", SW_NAME (sw));
818: qemu_free (sw);
819: return -1;
820: }
821: LIST_INSERT_HEAD (&hw_cap->sw_head, sw, entries);
822: LIST_INSERT_HEAD (&hw->cap_head, sc, entries);
823: #ifdef DEBUG_CAPTURE
824: asprintf (&sw->name, "for %p %d,%d,%d",
825: hw, sw->info.freq, sw->info.bits, sw->info.nchannels);
826: dolog ("Added %s active = %d\n", sw->name, sw->active);
827: #endif
828: if (sw->active) {
829: audio_capture_maybe_changed (cap, 1);
830: }
831: }
832: return 0;
833: }
834:
835: /*
1.1.1.2 root 836: * Hard voice (capture)
837: */
838: static int audio_pcm_hw_find_min_in (HWVoiceIn *hw)
1.1 root 839: {
1.1.1.2 root 840: SWVoiceIn *sw;
841: int m = hw->total_samples_captured;
842:
843: for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {
844: if (sw->active) {
845: m = audio_MIN (m, sw->total_hw_samples_acquired);
846: }
1.1 root 847: }
1.1.1.2 root 848: return m;
1.1 root 849: }
850:
1.1.1.2 root 851: int audio_pcm_hw_get_live_in (HWVoiceIn *hw)
1.1 root 852: {
1.1.1.2 root 853: int live = hw->total_samples_captured - audio_pcm_hw_find_min_in (hw);
854: if (audio_bug (AUDIO_FUNC, live < 0 || live > hw->samples)) {
855: dolog ("live=%d hw->samples=%d\n", live, hw->samples);
856: return 0;
857: }
858: return live;
1.1 root 859: }
860:
1.1.1.2 root 861: /*
862: * Soft voice (capture)
863: */
864: static int audio_pcm_sw_get_rpos_in (SWVoiceIn *sw)
1.1 root 865: {
1.1.1.2 root 866: HWVoiceIn *hw = sw->hw;
867: int live = hw->total_samples_captured - sw->total_hw_samples_acquired;
868: int rpos;
1.1 root 869:
1.1.1.2 root 870: if (audio_bug (AUDIO_FUNC, live < 0 || live > hw->samples)) {
871: dolog ("live=%d hw->samples=%d\n", live, hw->samples);
872: return 0;
1.1 root 873: }
874:
1.1.1.2 root 875: rpos = hw->wpos - live;
876: if (rpos >= 0) {
877: return rpos;
878: }
879: else {
880: return hw->samples + rpos;
881: }
1.1 root 882: }
883:
1.1.1.2 root 884: int audio_pcm_sw_read (SWVoiceIn *sw, void *buf, int size)
1.1 root 885: {
1.1.1.2 root 886: HWVoiceIn *hw = sw->hw;
887: int samples, live, ret = 0, swlim, isamp, osamp, rpos, total = 0;
1.1.1.6 root 888: struct st_sample *src, *dst = sw->buf;
1.1 root 889:
1.1.1.2 root 890: rpos = audio_pcm_sw_get_rpos_in (sw) % hw->samples;
891:
892: live = hw->total_samples_captured - sw->total_hw_samples_acquired;
893: if (audio_bug (AUDIO_FUNC, live < 0 || live > hw->samples)) {
894: dolog ("live_in=%d hw->samples=%d\n", live, hw->samples);
895: return 0;
896: }
897:
898: samples = size >> sw->info.shift;
899: if (!live) {
900: return 0;
901: }
902:
903: swlim = (live * sw->ratio) >> 32;
904: swlim = audio_MIN (swlim, samples);
905:
906: while (swlim) {
907: src = hw->conv_buf + rpos;
908: isamp = hw->wpos - rpos;
909: /* XXX: <= ? */
910: if (isamp <= 0) {
911: isamp = hw->samples - rpos;
1.1 root 912: }
1.1.1.2 root 913:
914: if (!isamp) {
915: break;
916: }
917: osamp = swlim;
918:
919: if (audio_bug (AUDIO_FUNC, osamp < 0)) {
920: dolog ("osamp=%d\n", osamp);
921: return 0;
922: }
923:
924: st_rate_flow (sw->rate, src, dst, &isamp, &osamp);
925: swlim -= osamp;
926: rpos = (rpos + isamp) % hw->samples;
927: dst += osamp;
928: ret += osamp;
929: total += isamp;
1.1 root 930: }
931:
1.1.1.2 root 932: sw->clip (buf, sw->buf, ret);
933: sw->total_hw_samples_acquired += total;
934: return ret << sw->info.shift;
1.1 root 935: }
936:
1.1.1.2 root 937: /*
938: * Hard voice (playback)
939: */
940: static int audio_pcm_hw_find_min_out (HWVoiceOut *hw, int *nb_livep)
1.1 root 941: {
1.1.1.2 root 942: SWVoiceOut *sw;
943: int m = INT_MAX;
944: int nb_live = 0;
1.1 root 945:
1.1.1.2 root 946: for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {
947: if (sw->active || !sw->empty) {
948: m = audio_MIN (m, sw->total_hw_samples_mixed);
949: nb_live += 1;
1.1 root 950: }
951: }
1.1.1.2 root 952:
953: *nb_livep = nb_live;
954: return m;
1.1 root 955: }
956:
1.1.1.2 root 957: int audio_pcm_hw_get_live_out2 (HWVoiceOut *hw, int *nb_live)
1.1 root 958: {
1.1.1.2 root 959: int smin;
1.1 root 960:
1.1.1.2 root 961: smin = audio_pcm_hw_find_min_out (hw, nb_live);
1.1 root 962:
1.1.1.2 root 963: if (!*nb_live) {
964: return 0;
965: }
966: else {
967: int live = smin;
1.1 root 968:
1.1.1.2 root 969: if (audio_bug (AUDIO_FUNC, live < 0 || live > hw->samples)) {
970: dolog ("live=%d hw->samples=%d\n", live, hw->samples);
971: return 0;
1.1 root 972: }
1.1.1.2 root 973: return live;
1.1 root 974: }
975: }
976:
1.1.1.2 root 977: int audio_pcm_hw_get_live_out (HWVoiceOut *hw)
978: {
979: int nb_live;
980: int live;
981:
982: live = audio_pcm_hw_get_live_out2 (hw, &nb_live);
983: if (audio_bug (AUDIO_FUNC, live < 0 || live > hw->samples)) {
984: dolog ("live=%d hw->samples=%d\n", live, hw->samples);
985: return 0;
986: }
987: return live;
988: }
989:
990: /*
991: * Soft voice (playback)
992: */
993: int audio_pcm_sw_write (SWVoiceOut *sw, void *buf, int size)
1.1 root 994: {
995: int hwsamples, samples, isamp, osamp, wpos, live, dead, left, swlim, blck;
1.1.1.2 root 996: int ret = 0, pos = 0, total = 0;
997:
998: if (!sw) {
1.1 root 999: return size;
1.1.1.2 root 1000: }
1.1 root 1001:
1002: hwsamples = sw->hw->samples;
1003:
1.1.1.2 root 1004: live = sw->total_hw_samples_mixed;
1005: if (audio_bug (AUDIO_FUNC, live < 0 || live > hwsamples)){
1006: dolog ("live=%d hw->samples=%d\n", live, hwsamples);
1007: return 0;
1.1 root 1008: }
1.1.1.2 root 1009:
1010: if (live == hwsamples) {
1.1.1.3 root 1011: #ifdef DEBUG_OUT
1012: dolog ("%s is full %d\n", sw->name, live);
1013: #endif
1.1.1.2 root 1014: return 0;
1015: }
1016:
1017: wpos = (sw->hw->rpos + live) % hwsamples;
1018: samples = size >> sw->info.shift;
1019:
1.1 root 1020: dead = hwsamples - live;
1.1.1.2 root 1021: swlim = ((int64_t) dead << 32) / sw->ratio;
1.1 root 1022: swlim = audio_MIN (swlim, samples);
1.1.1.2 root 1023: if (swlim) {
1024: sw->conv (sw->buf, buf, swlim, &sw->vol);
1025: }
1.1 root 1026:
1027: while (swlim) {
1028: dead = hwsamples - live;
1029: left = hwsamples - wpos;
1030: blck = audio_MIN (dead, left);
1031: if (!blck) {
1032: break;
1033: }
1034: isamp = swlim;
1035: osamp = blck;
1.1.1.2 root 1036: st_rate_flow_mix (
1037: sw->rate,
1038: sw->buf + pos,
1039: sw->hw->mix_buf + wpos,
1040: &isamp,
1041: &osamp
1042: );
1.1 root 1043: ret += isamp;
1044: swlim -= isamp;
1045: pos += isamp;
1046: live += osamp;
1.1.1.2 root 1047: wpos = (wpos + osamp) % hwsamples;
1048: total += osamp;
1.1 root 1049: }
1050:
1.1.1.2 root 1051: sw->total_hw_samples_mixed += total;
1052: sw->empty = sw->total_hw_samples_mixed == 0;
1.1 root 1053:
1.1.1.2 root 1054: #ifdef DEBUG_OUT
1055: dolog (
1056: "%s: write size %d ret %d total sw %d\n",
1057: SW_NAME (sw),
1058: size >> sw->info.shift,
1059: ret,
1060: sw->total_hw_samples_mixed
1061: );
1062: #endif
1.1 root 1063:
1.1.1.2 root 1064: return ret << sw->info.shift;
1.1 root 1065: }
1066:
1.1.1.2 root 1067: #ifdef DEBUG_AUDIO
1068: static void audio_pcm_print_info (const char *cap, struct audio_pcm_info *info)
1.1 root 1069: {
1.1.1.2 root 1070: dolog ("%s: bits %d, sign %d, freq %d, nchan %d\n",
1071: cap, info->bits, info->sign, info->freq, info->nchannels);
1.1 root 1072: }
1.1.1.2 root 1073: #endif
1.1 root 1074:
1.1.1.2 root 1075: #define DAC
1076: #include "audio_template.h"
1077: #undef DAC
1078: #include "audio_template.h"
1.1 root 1079:
1.1.1.2 root 1080: int AUD_write (SWVoiceOut *sw, void *buf, int size)
1.1 root 1081: {
1.1.1.2 root 1082: int bytes;
1083:
1084: if (!sw) {
1085: /* XXX: Consider options */
1086: return size;
1.1 root 1087: }
1088:
1.1.1.2 root 1089: if (!sw->hw->enabled) {
1090: dolog ("Writing to disabled voice %s\n", SW_NAME (sw));
1091: return 0;
1.1 root 1092: }
1.1.1.2 root 1093:
1094: bytes = sw->hw->pcm_ops->write (sw, buf, size);
1095: return bytes;
1.1 root 1096: }
1097:
1.1.1.2 root 1098: int AUD_read (SWVoiceIn *sw, void *buf, int size)
1.1 root 1099: {
1.1.1.2 root 1100: int bytes;
1101:
1102: if (!sw) {
1103: /* XXX: Consider options */
1104: return size;
1.1 root 1105: }
1106:
1.1.1.2 root 1107: if (!sw->hw->enabled) {
1108: dolog ("Reading from disabled voice %s\n", SW_NAME (sw));
1109: return 0;
1.1 root 1110: }
1.1.1.2 root 1111:
1112: bytes = sw->hw->pcm_ops->read (sw, buf, size);
1113: return bytes;
1.1 root 1114: }
1115:
1.1.1.2 root 1116: int AUD_get_buffer_size_out (SWVoiceOut *sw)
1.1 root 1117: {
1.1.1.2 root 1118: return sw->hw->samples << sw->hw->info.shift;
1.1 root 1119: }
1120:
1.1.1.2 root 1121: void AUD_set_active_out (SWVoiceOut *sw, int on)
1.1 root 1122: {
1.1.1.2 root 1123: HWVoiceOut *hw;
1.1 root 1124:
1.1.1.2 root 1125: if (!sw) {
1126: return;
1.1 root 1127: }
1128:
1.1.1.2 root 1129: hw = sw->hw;
1130: if (sw->active != on) {
1.1.1.6 root 1131: AudioState *s = &glob_audio_state;
1.1.1.2 root 1132: SWVoiceOut *temp_sw;
1.1.1.3 root 1133: SWVoiceCap *sc;
1.1 root 1134:
1.1.1.2 root 1135: if (on) {
1136: hw->pending_disable = 0;
1137: if (!hw->enabled) {
1138: hw->enabled = 1;
1.1.1.6 root 1139: if (s->vm_running) {
1140: hw->pcm_ops->ctl_out (hw, VOICE_ENABLE);
1141: }
1.1.1.2 root 1142: }
1.1 root 1143: }
1.1.1.2 root 1144: else {
1145: if (hw->enabled) {
1146: int nb_active = 0;
1147:
1148: for (temp_sw = hw->sw_head.lh_first; temp_sw;
1149: temp_sw = temp_sw->entries.le_next) {
1150: nb_active += temp_sw->active != 0;
1151: }
1.1 root 1152:
1.1.1.2 root 1153: hw->pending_disable = nb_active == 1;
1154: }
1155: }
1.1.1.3 root 1156:
1157: for (sc = hw->cap_head.lh_first; sc; sc = sc->entries.le_next) {
1158: sc->sw.active = hw->enabled;
1159: if (hw->enabled) {
1160: audio_capture_maybe_changed (sc->cap, 1);
1161: }
1162: }
1.1.1.2 root 1163: sw->active = on;
1164: }
1.1 root 1165: }
1166:
1.1.1.2 root 1167: void AUD_set_active_in (SWVoiceIn *sw, int on)
1.1 root 1168: {
1.1.1.2 root 1169: HWVoiceIn *hw;
1.1 root 1170:
1.1.1.2 root 1171: if (!sw) {
1172: return;
1173: }
1.1 root 1174:
1.1.1.2 root 1175: hw = sw->hw;
1176: if (sw->active != on) {
1.1.1.6 root 1177: AudioState *s = &glob_audio_state;
1.1.1.2 root 1178: SWVoiceIn *temp_sw;
1.1 root 1179:
1.1.1.2 root 1180: if (on) {
1181: if (!hw->enabled) {
1182: hw->enabled = 1;
1.1.1.6 root 1183: if (s->vm_running) {
1184: hw->pcm_ops->ctl_in (hw, VOICE_ENABLE);
1185: }
1.1.1.2 root 1186: }
1187: sw->total_hw_samples_acquired = hw->total_samples_captured;
1.1 root 1188: }
1.1.1.2 root 1189: else {
1190: if (hw->enabled) {
1191: int nb_active = 0;
1192:
1193: for (temp_sw = hw->sw_head.lh_first; temp_sw;
1194: temp_sw = temp_sw->entries.le_next) {
1195: nb_active += temp_sw->active != 0;
1196: }
1.1 root 1197:
1.1.1.2 root 1198: if (nb_active == 1) {
1199: hw->enabled = 0;
1200: hw->pcm_ops->ctl_in (hw, VOICE_DISABLE);
1201: }
1.1 root 1202: }
1203: }
1.1.1.2 root 1204: sw->active = on;
1.1 root 1205: }
1.1.1.2 root 1206: }
1207:
1208: static int audio_get_avail (SWVoiceIn *sw)
1209: {
1210: int live;
1211:
1212: if (!sw) {
1213: return 0;
1.1 root 1214: }
1.1.1.2 root 1215:
1216: live = sw->hw->total_samples_captured - sw->total_hw_samples_acquired;
1217: if (audio_bug (AUDIO_FUNC, live < 0 || live > sw->hw->samples)) {
1218: dolog ("live=%d sw->hw->samples=%d\n", live, sw->hw->samples);
1219: return 0;
1220: }
1221:
1222: ldebug (
1.1.1.3 root 1223: "%s: get_avail live %d ret %" PRId64 "\n",
1.1.1.2 root 1224: SW_NAME (sw),
1225: live, (((int64_t) live << 32) / sw->ratio) << sw->info.shift
1226: );
1227:
1228: return (((int64_t) live << 32) / sw->ratio) << sw->info.shift;
1.1 root 1229: }
1230:
1.1.1.2 root 1231: static int audio_get_free (SWVoiceOut *sw)
1.1 root 1232: {
1.1.1.2 root 1233: int live, dead;
1.1 root 1234:
1.1.1.2 root 1235: if (!sw) {
1236: return 0;
1237: }
1.1 root 1238:
1.1.1.2 root 1239: live = sw->total_hw_samples_mixed;
1.1 root 1240:
1.1.1.2 root 1241: if (audio_bug (AUDIO_FUNC, live < 0 || live > sw->hw->samples)) {
1242: dolog ("live=%d sw->hw->samples=%d\n", live, sw->hw->samples);
1243: return 0;
1244: }
1.1 root 1245:
1.1.1.2 root 1246: dead = sw->hw->samples - live;
1.1 root 1247:
1.1.1.2 root 1248: #ifdef DEBUG_OUT
1.1.1.3 root 1249: dolog ("%s: get_free live %d dead %d ret %" PRId64 "\n",
1.1.1.2 root 1250: SW_NAME (sw),
1251: live, dead, (((int64_t) dead << 32) / sw->ratio) << sw->info.shift);
1252: #endif
1.1 root 1253:
1.1.1.2 root 1254: return (((int64_t) dead << 32) / sw->ratio) << sw->info.shift;
1.1 root 1255: }
1256:
1.1.1.3 root 1257: static void audio_capture_mix_and_clear (HWVoiceOut *hw, int rpos, int samples)
1258: {
1259: int n;
1260:
1261: if (hw->enabled) {
1262: SWVoiceCap *sc;
1263:
1264: for (sc = hw->cap_head.lh_first; sc; sc = sc->entries.le_next) {
1265: SWVoiceOut *sw = &sc->sw;
1266: int rpos2 = rpos;
1267:
1268: n = samples;
1269: while (n) {
1270: int till_end_of_hw = hw->samples - rpos2;
1271: int to_write = audio_MIN (till_end_of_hw, n);
1272: int bytes = to_write << hw->info.shift;
1273: int written;
1274:
1275: sw->buf = hw->mix_buf + rpos2;
1276: written = audio_pcm_sw_write (sw, NULL, bytes);
1277: if (written - bytes) {
1278: dolog ("Could not mix %d bytes into a capture "
1279: "buffer, mixed %d\n",
1280: bytes, written);
1281: break;
1282: }
1283: n -= to_write;
1284: rpos2 = (rpos2 + to_write) % hw->samples;
1285: }
1286: }
1287: }
1288:
1289: n = audio_MIN (samples, hw->samples - rpos);
1290: mixeng_clear (hw->mix_buf + rpos, n);
1291: mixeng_clear (hw->mix_buf, samples - n);
1292: }
1293:
1.1.1.2 root 1294: static void audio_run_out (AudioState *s)
1.1 root 1295: {
1.1.1.2 root 1296: HWVoiceOut *hw = NULL;
1297: SWVoiceOut *sw;
1.1 root 1298:
1.1.1.7 ! root 1299: while ((hw = audio_pcm_hw_find_any_enabled_out (hw))) {
1.1.1.2 root 1300: int played;
1.1.1.3 root 1301: int live, free, nb_live, cleanup_required, prev_rpos;
1.1 root 1302:
1.1.1.2 root 1303: live = audio_pcm_hw_get_live_out2 (hw, &nb_live);
1304: if (!nb_live) {
1305: live = 0;
1306: }
1.1 root 1307:
1.1.1.2 root 1308: if (audio_bug (AUDIO_FUNC, live < 0 || live > hw->samples)) {
1309: dolog ("live=%d hw->samples=%d\n", live, hw->samples);
1310: continue;
1311: }
1312:
1313: if (hw->pending_disable && !nb_live) {
1.1.1.3 root 1314: SWVoiceCap *sc;
1.1.1.2 root 1315: #ifdef DEBUG_OUT
1316: dolog ("Disabling voice\n");
1317: #endif
1318: hw->enabled = 0;
1319: hw->pending_disable = 0;
1320: hw->pcm_ops->ctl_out (hw, VOICE_DISABLE);
1.1.1.3 root 1321: for (sc = hw->cap_head.lh_first; sc; sc = sc->entries.le_next) {
1322: sc->sw.active = 0;
1323: audio_recalc_and_notify_capture (sc->cap);
1324: }
1.1.1.2 root 1325: continue;
1326: }
1.1 root 1327:
1.1.1.2 root 1328: if (!live) {
1329: for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {
1330: if (sw->active) {
1331: free = audio_get_free (sw);
1332: if (free > 0) {
1333: sw->callback.fn (sw->callback.opaque, free);
1334: }
1335: }
1.1 root 1336: }
1.1.1.2 root 1337: continue;
1.1 root 1338: }
1.1.1.2 root 1339:
1.1.1.3 root 1340: prev_rpos = hw->rpos;
1.1.1.2 root 1341: played = hw->pcm_ops->run_out (hw);
1342: if (audio_bug (AUDIO_FUNC, hw->rpos >= hw->samples)) {
1343: dolog ("hw->rpos=%d hw->samples=%d played=%d\n",
1344: hw->rpos, hw->samples, played);
1345: hw->rpos = 0;
1.1 root 1346: }
1347:
1.1.1.2 root 1348: #ifdef DEBUG_OUT
1349: dolog ("played=%d\n", played);
1350: #endif
1.1 root 1351:
1.1.1.2 root 1352: if (played) {
1353: hw->ts_helper += played;
1.1.1.3 root 1354: audio_capture_mix_and_clear (hw, prev_rpos, played);
1.1.1.2 root 1355: }
1.1 root 1356:
1.1.1.2 root 1357: cleanup_required = 0;
1358: for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {
1359: if (!sw->active && sw->empty) {
1360: continue;
1361: }
1.1 root 1362:
1.1.1.2 root 1363: if (audio_bug (AUDIO_FUNC, played > sw->total_hw_samples_mixed)) {
1364: dolog ("played=%d sw->total_hw_samples_mixed=%d\n",
1365: played, sw->total_hw_samples_mixed);
1366: played = sw->total_hw_samples_mixed;
1367: }
1.1 root 1368:
1.1.1.2 root 1369: sw->total_hw_samples_mixed -= played;
1.1 root 1370:
1.1.1.2 root 1371: if (!sw->total_hw_samples_mixed) {
1372: sw->empty = 1;
1373: cleanup_required |= !sw->active && !sw->callback.fn;
1374: }
1.1 root 1375:
1.1.1.2 root 1376: if (sw->active) {
1377: free = audio_get_free (sw);
1378: if (free > 0) {
1379: sw->callback.fn (sw->callback.opaque, free);
1380: }
1.1 root 1381: }
1382: }
1383:
1.1.1.2 root 1384: if (cleanup_required) {
1.1.1.3 root 1385: SWVoiceOut *sw1;
1386:
1387: sw = hw->sw_head.lh_first;
1388: while (sw) {
1389: sw1 = sw->entries.le_next;
1.1.1.2 root 1390: if (!sw->active && !sw->callback.fn) {
1391: #ifdef DEBUG_PLIVE
1392: dolog ("Finishing with old voice\n");
1393: #endif
1.1.1.7 ! root 1394: audio_close_out (sw);
1.1 root 1395: }
1.1.1.3 root 1396: sw = sw1;
1.1 root 1397: }
1398: }
1399: }
1400: }
1401:
1.1.1.2 root 1402: static void audio_run_in (AudioState *s)
1.1 root 1403: {
1.1.1.2 root 1404: HWVoiceIn *hw = NULL;
1.1 root 1405:
1.1.1.7 ! root 1406: while ((hw = audio_pcm_hw_find_any_enabled_in (hw))) {
1.1.1.2 root 1407: SWVoiceIn *sw;
1408: int captured, min;
1.1 root 1409:
1.1.1.2 root 1410: captured = hw->pcm_ops->run_in (hw);
1.1 root 1411:
1.1.1.2 root 1412: min = audio_pcm_hw_find_min_in (hw);
1413: hw->total_samples_captured += captured - min;
1414: hw->ts_helper += captured;
1.1 root 1415:
1.1.1.2 root 1416: for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {
1417: sw->total_hw_samples_acquired -= min;
1.1 root 1418:
1.1.1.2 root 1419: if (sw->active) {
1420: int avail;
1.1 root 1421:
1.1.1.2 root 1422: avail = audio_get_avail (sw);
1423: if (avail > 0) {
1424: sw->callback.fn (sw->callback.opaque, avail);
1425: }
1426: }
1427: }
1428: }
1.1 root 1429: }
1430:
1.1.1.3 root 1431: static void audio_run_capture (AudioState *s)
1432: {
1433: CaptureVoiceOut *cap;
1434:
1435: for (cap = s->cap_head.lh_first; cap; cap = cap->entries.le_next) {
1436: int live, rpos, captured;
1437: HWVoiceOut *hw = &cap->hw;
1438: SWVoiceOut *sw;
1439:
1440: captured = live = audio_pcm_hw_get_live_out (hw);
1441: rpos = hw->rpos;
1442: while (live) {
1443: int left = hw->samples - rpos;
1444: int to_capture = audio_MIN (live, left);
1.1.1.6 root 1445: struct st_sample *src;
1.1.1.3 root 1446: struct capture_callback *cb;
1447:
1448: src = hw->mix_buf + rpos;
1449: hw->clip (cap->buf, src, to_capture);
1450: mixeng_clear (src, to_capture);
1451:
1452: for (cb = cap->cb_head.lh_first; cb; cb = cb->entries.le_next) {
1453: cb->ops.capture (cb->opaque, cap->buf,
1454: to_capture << hw->info.shift);
1455: }
1456: rpos = (rpos + to_capture) % hw->samples;
1457: live -= to_capture;
1458: }
1459: hw->rpos = rpos;
1460:
1461: for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {
1462: if (!sw->active && sw->empty) {
1463: continue;
1464: }
1465:
1466: if (audio_bug (AUDIO_FUNC, captured > sw->total_hw_samples_mixed)) {
1467: dolog ("captured=%d sw->total_hw_samples_mixed=%d\n",
1468: captured, sw->total_hw_samples_mixed);
1469: captured = sw->total_hw_samples_mixed;
1470: }
1471:
1472: sw->total_hw_samples_mixed -= captured;
1473: sw->empty = sw->total_hw_samples_mixed == 0;
1474: }
1475: }
1476: }
1477:
1.1.1.2 root 1478: static void audio_timer (void *opaque)
1.1 root 1479: {
1.1.1.2 root 1480: AudioState *s = opaque;
1481:
1482: audio_run_out (s);
1483: audio_run_in (s);
1.1.1.3 root 1484: audio_run_capture (s);
1.1.1.2 root 1485:
1486: qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + conf.period.ticks);
1.1 root 1487: }
1488:
1.1.1.2 root 1489: static struct audio_option audio_options[] = {
1490: /* DAC */
1491: {"DAC_FIXED_SETTINGS", AUD_OPT_BOOL, &conf.fixed_out.enabled,
1492: "Use fixed settings for host DAC", NULL, 0},
1.1 root 1493:
1.1.1.2 root 1494: {"DAC_FIXED_FREQ", AUD_OPT_INT, &conf.fixed_out.settings.freq,
1495: "Frequency for fixed host DAC", NULL, 0},
1.1 root 1496:
1.1.1.2 root 1497: {"DAC_FIXED_FMT", AUD_OPT_FMT, &conf.fixed_out.settings.fmt,
1498: "Format for fixed host DAC", NULL, 0},
1.1 root 1499:
1.1.1.2 root 1500: {"DAC_FIXED_CHANNELS", AUD_OPT_INT, &conf.fixed_out.settings.nchannels,
1501: "Number of channels for fixed DAC (1 - mono, 2 - stereo)", NULL, 0},
1.1 root 1502:
1.1.1.2 root 1503: {"DAC_VOICES", AUD_OPT_INT, &conf.fixed_out.nb_voices,
1504: "Number of voices for DAC", NULL, 0},
1505:
1506: /* ADC */
1507: {"ADC_FIXED_SETTINGS", AUD_OPT_BOOL, &conf.fixed_in.enabled,
1508: "Use fixed settings for host ADC", NULL, 0},
1509:
1510: {"ADC_FIXED_FREQ", AUD_OPT_INT, &conf.fixed_in.settings.freq,
1511: "Frequency for fixed host ADC", NULL, 0},
1512:
1513: {"ADC_FIXED_FMT", AUD_OPT_FMT, &conf.fixed_in.settings.fmt,
1514: "Format for fixed host ADC", NULL, 0},
1515:
1516: {"ADC_FIXED_CHANNELS", AUD_OPT_INT, &conf.fixed_in.settings.nchannels,
1517: "Number of channels for fixed ADC (1 - mono, 2 - stereo)", NULL, 0},
1518:
1519: {"ADC_VOICES", AUD_OPT_INT, &conf.fixed_in.nb_voices,
1520: "Number of voices for ADC", NULL, 0},
1521:
1522: /* Misc */
1.1.1.6 root 1523: {"TIMER_PERIOD", AUD_OPT_INT, &conf.period.hertz,
1.1.1.2 root 1524: "Timer period in HZ (0 - use lowest possible)", NULL, 0},
1525:
1526: {"PLIVE", AUD_OPT_BOOL, &conf.plive,
1527: "(undocumented)", NULL, 0},
1528:
1529: {"LOG_TO_MONITOR", AUD_OPT_BOOL, &conf.log_to_monitor,
1.1.1.6 root 1530: "print logging messages to monitor instead of stderr", NULL, 0},
1.1.1.2 root 1531:
1532: {NULL, 0, NULL, NULL, NULL, 0}
1533: };
1534:
1535: static void audio_pp_nb_voices (const char *typ, int nb)
1536: {
1537: switch (nb) {
1538: case 0:
1539: printf ("Does not support %s\n", typ);
1540: break;
1541: case 1:
1542: printf ("One %s voice\n", typ);
1543: break;
1544: case INT_MAX:
1545: printf ("Theoretically supports many %s voices\n", typ);
1546: break;
1547: default:
1548: printf ("Theoretically supports upto %d %s voices\n", nb, typ);
1549: break;
1.1 root 1550: }
1.1.1.2 root 1551:
1.1 root 1552: }
1553:
1.1.1.2 root 1554: void AUD_help (void)
1.1 root 1555: {
1.1.1.2 root 1556: size_t i;
1.1 root 1557:
1.1.1.2 root 1558: audio_process_options ("AUDIO", audio_options);
1.1.1.6 root 1559: for (i = 0; i < ARRAY_SIZE (drvtab); i++) {
1.1.1.2 root 1560: struct audio_driver *d = drvtab[i];
1561: if (d->options) {
1562: audio_process_options (d->name, d->options);
1.1 root 1563: }
1564: }
1565:
1.1.1.2 root 1566: printf ("Audio options:\n");
1567: audio_print_options ("AUDIO", audio_options);
1568: printf ("\n");
1569:
1570: printf ("Available drivers:\n");
1571:
1.1.1.6 root 1572: for (i = 0; i < ARRAY_SIZE (drvtab); i++) {
1.1.1.2 root 1573: struct audio_driver *d = drvtab[i];
1574:
1575: printf ("Name: %s\n", d->name);
1576: printf ("Description: %s\n", d->descr);
1577:
1578: audio_pp_nb_voices ("playback", d->max_voices_out);
1579: audio_pp_nb_voices ("capture", d->max_voices_in);
1580:
1581: if (d->options) {
1582: printf ("Options:\n");
1583: audio_print_options (d->name, d->options);
1.1 root 1584: }
1585: else {
1.1.1.2 root 1586: printf ("No options\n");
1.1 root 1587: }
1.1.1.2 root 1588: printf ("\n");
1.1 root 1589: }
1590:
1.1.1.2 root 1591: printf (
1592: "Options are settable through environment variables.\n"
1593: "Example:\n"
1594: #ifdef _WIN32
1595: " set QEMU_AUDIO_DRV=wav\n"
1596: " set QEMU_WAV_PATH=c:\\tune.wav\n"
1597: #else
1598: " export QEMU_AUDIO_DRV=wav\n"
1599: " export QEMU_WAV_PATH=$HOME/tune.wav\n"
1600: "(for csh replace export with setenv in the above)\n"
1.1 root 1601: #endif
1.1.1.2 root 1602: " qemu ...\n\n"
1603: );
1604: }
1.1 root 1605:
1.1.1.2 root 1606: static int audio_driver_init (AudioState *s, struct audio_driver *drv)
1.1 root 1607: {
1.1.1.2 root 1608: if (drv->options) {
1609: audio_process_options (drv->name, drv->options);
1.1 root 1610: }
1.1.1.2 root 1611: s->drv_opaque = drv->init ();
1612:
1613: if (s->drv_opaque) {
1.1.1.7 ! root 1614: audio_init_nb_voices_out (drv);
! 1615: audio_init_nb_voices_in (drv);
1.1.1.2 root 1616: s->drv = drv;
1.1 root 1617: return 0;
1618: }
1.1.1.2 root 1619: else {
1620: dolog ("Could not init `%s' audio driver\n", drv->name);
1621: return -1;
1622: }
1.1 root 1623: }
1624:
1.1.1.6 root 1625: static void audio_vm_change_state_handler (void *opaque, int running,
1626: int reason)
1.1 root 1627: {
1.1.1.2 root 1628: AudioState *s = opaque;
1629: HWVoiceOut *hwo = NULL;
1630: HWVoiceIn *hwi = NULL;
1631: int op = running ? VOICE_ENABLE : VOICE_DISABLE;
1.1 root 1632:
1.1.1.6 root 1633: s->vm_running = running;
1.1.1.7 ! root 1634: while ((hwo = audio_pcm_hw_find_any_enabled_out (hwo))) {
1.1.1.2 root 1635: hwo->pcm_ops->ctl_out (hwo, op);
1636: }
1.1 root 1637:
1.1.1.7 ! root 1638: while ((hwi = audio_pcm_hw_find_any_enabled_in (hwi))) {
1.1.1.2 root 1639: hwi->pcm_ops->ctl_in (hwi, op);
1.1 root 1640: }
1641: }
1642:
1643: static void audio_atexit (void)
1644: {
1.1.1.2 root 1645: AudioState *s = &glob_audio_state;
1646: HWVoiceOut *hwo = NULL;
1647: HWVoiceIn *hwi = NULL;
1.1 root 1648:
1.1.1.7 ! root 1649: while ((hwo = audio_pcm_hw_find_any_enabled_out (hwo))) {
1.1.1.3 root 1650: SWVoiceCap *sc;
1651:
1.1.1.2 root 1652: hwo->pcm_ops->ctl_out (hwo, VOICE_DISABLE);
1653: hwo->pcm_ops->fini_out (hwo);
1.1.1.3 root 1654:
1655: for (sc = hwo->cap_head.lh_first; sc; sc = sc->entries.le_next) {
1656: CaptureVoiceOut *cap = sc->cap;
1657: struct capture_callback *cb;
1658:
1659: for (cb = cap->cb_head.lh_first; cb; cb = cb->entries.le_next) {
1660: cb->ops.destroy (cb->opaque);
1661: }
1662: }
1.1.1.2 root 1663: }
1664:
1.1.1.7 ! root 1665: while ((hwi = audio_pcm_hw_find_any_enabled_in (hwi))) {
1.1.1.2 root 1666: hwi->pcm_ops->ctl_in (hwi, VOICE_DISABLE);
1667: hwi->pcm_ops->fini_in (hwi);
1668: }
1.1 root 1669:
1.1.1.2 root 1670: if (s->drv) {
1671: s->drv->fini (s->drv_opaque);
1.1 root 1672: }
1673: }
1674:
1675: static void audio_save (QEMUFile *f, void *opaque)
1676: {
1.1.1.2 root 1677: (void) f;
1678: (void) opaque;
1.1 root 1679: }
1680:
1681: static int audio_load (QEMUFile *f, void *opaque, int version_id)
1682: {
1.1.1.2 root 1683: (void) f;
1684: (void) opaque;
1685:
1686: if (version_id != 1) {
1.1 root 1687: return -EINVAL;
1.1.1.2 root 1688: }
1.1 root 1689:
1690: return 0;
1691: }
1692:
1.1.1.7 ! root 1693: static void audio_init (void)
1.1.1.2 root 1694: {
1695: size_t i;
1.1 root 1696: int done = 0;
1697: const char *drvname;
1.1.1.2 root 1698: AudioState *s = &glob_audio_state;
1699:
1.1.1.7 ! root 1700: if (s->drv) {
! 1701: return;
! 1702: }
! 1703:
1.1.1.2 root 1704: LIST_INIT (&s->hw_head_out);
1705: LIST_INIT (&s->hw_head_in);
1.1.1.3 root 1706: LIST_INIT (&s->cap_head);
1.1.1.2 root 1707: atexit (audio_atexit);
1708:
1709: s->ts = qemu_new_timer (vm_clock, audio_timer, s);
1710: if (!s->ts) {
1.1.1.7 ! root 1711: hw_error("Could not create audio timer\n");
1.1.1.2 root 1712: }
1713:
1714: audio_process_options ("AUDIO", audio_options);
1715:
1716: s->nb_hw_voices_out = conf.fixed_out.nb_voices;
1717: s->nb_hw_voices_in = conf.fixed_in.nb_voices;
1718:
1719: if (s->nb_hw_voices_out <= 0) {
1720: dolog ("Bogus number of playback voices %d, setting to 1\n",
1721: s->nb_hw_voices_out);
1722: s->nb_hw_voices_out = 1;
1723: }
1.1 root 1724:
1.1.1.2 root 1725: if (s->nb_hw_voices_in <= 0) {
1726: dolog ("Bogus number of capture voices %d, setting to 0\n",
1727: s->nb_hw_voices_in);
1728: s->nb_hw_voices_in = 0;
1729: }
1730:
1731: {
1732: int def;
1733: drvname = audio_get_conf_str ("QEMU_AUDIO_DRV", NULL, &def);
1.1 root 1734: }
1735:
1736: if (drvname) {
1737: int found = 0;
1.1.1.2 root 1738:
1.1.1.6 root 1739: for (i = 0; i < ARRAY_SIZE (drvtab); i++) {
1.1 root 1740: if (!strcmp (drvname, drvtab[i]->name)) {
1.1.1.2 root 1741: done = !audio_driver_init (s, drvtab[i]);
1.1 root 1742: found = 1;
1743: break;
1744: }
1745: }
1.1.1.2 root 1746:
1.1 root 1747: if (!found) {
1748: dolog ("Unknown audio driver `%s'\n", drvname);
1.1.1.2 root 1749: dolog ("Run with -audio-help to list available drivers\n");
1.1 root 1750: }
1751: }
1752:
1753: if (!done) {
1.1.1.6 root 1754: for (i = 0; !done && i < ARRAY_SIZE (drvtab); i++) {
1.1.1.2 root 1755: if (drvtab[i]->can_be_default) {
1756: done = !audio_driver_init (s, drvtab[i]);
1757: }
1.1 root 1758: }
1759: }
1760:
1761: if (!done) {
1.1.1.2 root 1762: done = !audio_driver_init (s, &no_audio_driver);
1763: if (!done) {
1.1.1.7 ! root 1764: hw_error("Could not initialize audio subsystem\n");
1.1.1.2 root 1765: }
1766: else {
1767: dolog ("warning: Using timer based audio emulation\n");
1768: }
1769: }
1770:
1.1.1.7 ! root 1771: VMChangeStateEntry *e;
1.1.1.2 root 1772:
1.1.1.7 ! root 1773: if (conf.period.hertz <= 0) {
! 1774: if (conf.period.hertz < 0) {
! 1775: dolog ("warning: Timer period is negative - %d "
! 1776: "treating as zero\n",
! 1777: conf.period.hertz);
! 1778: }
! 1779: conf.period.ticks = 1;
! 1780: } else {
! 1781: conf.period.ticks = ticks_per_sec / conf.period.hertz;
! 1782: }
! 1783:
! 1784: e = qemu_add_vm_change_state_handler (audio_vm_change_state_handler, s);
! 1785: if (!e) {
! 1786: dolog ("warning: Could not register change state handler\n"
! 1787: "(Audio can continue looping even after stopping the VM)\n");
1.1.1.2 root 1788: }
1789:
1790: LIST_INIT (&s->card_head);
1791: register_savevm ("audio", 0, 1, audio_save, audio_load, s);
1792: qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + conf.period.ticks);
1.1 root 1793: }
1.1.1.3 root 1794:
1.1.1.7 ! root 1795: void AUD_register_card (const char *name, QEMUSoundCard *card)
! 1796: {
! 1797: audio_init ();
! 1798: card->name = qemu_strdup (name);
! 1799: memset (&card->entries, 0, sizeof (card->entries));
! 1800: LIST_INSERT_HEAD (&glob_audio_state.card_head, card, entries);
! 1801: }
! 1802:
! 1803: void AUD_remove_card (QEMUSoundCard *card)
! 1804: {
! 1805: LIST_REMOVE (card, entries);
! 1806: qemu_free (card->name);
! 1807: }
! 1808:
! 1809:
1.1.1.3 root 1810: CaptureVoiceOut *AUD_add_capture (
1.1.1.6 root 1811: struct audsettings *as,
1.1.1.3 root 1812: struct audio_capture_ops *ops,
1813: void *cb_opaque
1814: )
1815: {
1.1.1.7 ! root 1816: AudioState *s = &glob_audio_state;
1.1.1.3 root 1817: CaptureVoiceOut *cap;
1818: struct capture_callback *cb;
1819:
1820: if (audio_validate_settings (as)) {
1821: dolog ("Invalid settings were passed when trying to add capture\n");
1822: audio_print_settings (as);
1823: goto err0;
1824: }
1825:
1826: cb = audio_calloc (AUDIO_FUNC, 1, sizeof (*cb));
1827: if (!cb) {
1828: dolog ("Could not allocate capture callback information, size %zu\n",
1829: sizeof (*cb));
1830: goto err0;
1831: }
1832: cb->ops = *ops;
1833: cb->opaque = cb_opaque;
1834:
1.1.1.7 ! root 1835: cap = audio_pcm_capture_find_specific (as);
1.1.1.3 root 1836: if (cap) {
1837: LIST_INSERT_HEAD (&cap->cb_head, cb, entries);
1838: return cap;
1839: }
1840: else {
1841: HWVoiceOut *hw;
1842: CaptureVoiceOut *cap;
1843:
1844: cap = audio_calloc (AUDIO_FUNC, 1, sizeof (*cap));
1845: if (!cap) {
1846: dolog ("Could not allocate capture voice, size %zu\n",
1847: sizeof (*cap));
1848: goto err1;
1849: }
1850:
1851: hw = &cap->hw;
1852: LIST_INIT (&hw->sw_head);
1853: LIST_INIT (&cap->cb_head);
1854:
1855: /* XXX find a more elegant way */
1856: hw->samples = 4096 * 4;
1857: hw->mix_buf = audio_calloc (AUDIO_FUNC, hw->samples,
1.1.1.6 root 1858: sizeof (struct st_sample));
1.1.1.3 root 1859: if (!hw->mix_buf) {
1860: dolog ("Could not allocate capture mix buffer (%d samples)\n",
1861: hw->samples);
1862: goto err2;
1863: }
1864:
1865: audio_pcm_init_info (&hw->info, as);
1866:
1867: cap->buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
1868: if (!cap->buf) {
1869: dolog ("Could not allocate capture buffer "
1870: "(%d samples, each %d bytes)\n",
1871: hw->samples, 1 << hw->info.shift);
1872: goto err3;
1873: }
1874:
1875: hw->clip = mixeng_clip
1876: [hw->info.nchannels == 2]
1877: [hw->info.sign]
1878: [hw->info.swap_endianness]
1.1.1.5 root 1879: [audio_bits_to_index (hw->info.bits)];
1.1.1.3 root 1880:
1881: LIST_INSERT_HEAD (&s->cap_head, cap, entries);
1882: LIST_INSERT_HEAD (&cap->cb_head, cb, entries);
1883:
1884: hw = NULL;
1.1.1.7 ! root 1885: while ((hw = audio_pcm_hw_find_any_out (hw))) {
! 1886: audio_attach_capture (hw);
1.1.1.3 root 1887: }
1888: return cap;
1889:
1890: err3:
1891: qemu_free (cap->hw.mix_buf);
1892: err2:
1893: qemu_free (cap);
1894: err1:
1895: qemu_free (cb);
1896: err0:
1897: return NULL;
1898: }
1899: }
1900:
1901: void AUD_del_capture (CaptureVoiceOut *cap, void *cb_opaque)
1902: {
1903: struct capture_callback *cb;
1904:
1905: for (cb = cap->cb_head.lh_first; cb; cb = cb->entries.le_next) {
1906: if (cb->opaque == cb_opaque) {
1907: cb->ops.destroy (cb_opaque);
1908: LIST_REMOVE (cb, entries);
1909: qemu_free (cb);
1910:
1911: if (!cap->cb_head.lh_first) {
1912: SWVoiceOut *sw = cap->hw.sw_head.lh_first, *sw1;
1913:
1914: while (sw) {
1915: SWVoiceCap *sc = (SWVoiceCap *) sw;
1916: #ifdef DEBUG_CAPTURE
1917: dolog ("freeing %s\n", sw->name);
1918: #endif
1919:
1920: sw1 = sw->entries.le_next;
1921: if (sw->rate) {
1922: st_rate_stop (sw->rate);
1923: sw->rate = NULL;
1924: }
1925: LIST_REMOVE (sw, entries);
1926: LIST_REMOVE (sc, entries);
1927: qemu_free (sc);
1928: sw = sw1;
1929: }
1930: LIST_REMOVE (cap, entries);
1931: qemu_free (cap);
1932: }
1933: return;
1934: }
1935: }
1936: }
1.1.1.6 root 1937:
1938: void AUD_set_volume_out (SWVoiceOut *sw, int mute, uint8_t lvol, uint8_t rvol)
1939: {
1940: if (sw) {
1941: sw->vol.mute = mute;
1942: sw->vol.l = nominal_volume.l * lvol / 255;
1943: sw->vol.r = nominal_volume.r * rvol / 255;
1944: }
1945: }
1946:
1947: void AUD_set_volume_in (SWVoiceIn *sw, int mute, uint8_t lvol, uint8_t rvol)
1948: {
1949: if (sw) {
1950: sw->vol.mute = mute;
1951: sw->vol.l = nominal_volume.l * lvol / 255;
1952: sw->vol.r = nominal_volume.r * rvol / 255;
1953: }
1954: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.