📄 audio.c
字号:
/* * QEMU Audio subsystem * * Copyright (c) 2003-2005 Vassili Karpov (malc) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */#include "vl.h"#define AUDIO_CAP "audio"#include "audio_int.h"/* #define DEBUG_PLIVE *//* #define DEBUG_LIVE *//* #define DEBUG_OUT *//* #define DEBUG_CAPTURE */#define SW_NAME(sw) (sw)->name ? (sw)->name : "unknown"static struct audio_driver *drvtab[] = {#ifdef CONFIG_OSS &oss_audio_driver,#endif#ifdef CONFIG_ALSA &alsa_audio_driver,#endif#ifdef CONFIG_COREAUDIO &coreaudio_audio_driver,#endif#ifdef CONFIG_DSOUND &dsound_audio_driver,#endif#ifdef CONFIG_FMOD &fmod_audio_driver,#endif#ifdef CONFIG_SDL &sdl_audio_driver,#endif &no_audio_driver, &wav_audio_driver};struct fixed_settings { int enabled; int nb_voices; int greedy; audsettings_t settings;};static struct { struct fixed_settings fixed_out; struct fixed_settings fixed_in; union { int hz; int64_t ticks; } period; int plive; int log_to_monitor;} conf = { { /* DAC fixed settings */ 1, /* enabled */ 1, /* nb_voices */ 1, /* greedy */ { 44100, /* freq */ 2, /* nchannels */ AUD_FMT_S16 /* fmt */ } }, { /* ADC fixed settings */ 1, /* enabled */ 1, /* nb_voices */ 1, /* greedy */ { 44100, /* freq */ 2, /* nchannels */ AUD_FMT_S16 /* fmt */ } }, { 0 }, /* period */ 0, /* plive */ 0 /* log_to_monitor */};static AudioState glob_audio_state;volume_t nominal_volume = { 0,#ifdef FLOAT_MIXENG 1.0, 1.0#else UINT_MAX, UINT_MAX#endif};/* http://www.df.lth.se/~john_e/gems/gem002d.html *//* http://www.multi-platforms.com/Tips/PopCount.htm */uint32_t popcount (uint32_t u){ u = ((u&0x55555555) + ((u>>1)&0x55555555)); u = ((u&0x33333333) + ((u>>2)&0x33333333)); u = ((u&0x0f0f0f0f) + ((u>>4)&0x0f0f0f0f)); u = ((u&0x00ff00ff) + ((u>>8)&0x00ff00ff)); u = ( u&0x0000ffff) + (u>>16); return u;}inline uint32_t lsbindex (uint32_t u){ return popcount ((u&-u)-1);}#ifdef AUDIO_IS_FLAWLESS_AND_NO_CHECKS_ARE_REQURIED#error No its not#elseint audio_bug (const char *funcname, int cond){ if (cond) { static int shown; AUD_log (NULL, "A bug was just triggered in %s\n", funcname); if (!shown) { shown = 1; AUD_log (NULL, "Save all your work and restart without audio\n"); AUD_log (NULL, "Please send bug report to malc@pulsesoft.com\n"); AUD_log (NULL, "I am sorry\n"); } AUD_log (NULL, "Context:\n");#if defined AUDIO_BREAKPOINT_ON_BUG# if defined HOST_I386# if defined __GNUC__ __asm__ ("int3");# elif defined _MSC_VER _asm _emit 0xcc;# else abort ();# endif# else abort ();# endif#endif } return cond;}#endifvoid *audio_calloc (const char *funcname, int nmemb, size_t size){ int cond; size_t len; len = nmemb * size; cond = !nmemb || !size; cond |= nmemb < 0; cond |= len < size; if (audio_bug ("audio_calloc", cond)) { AUD_log (NULL, "%s passed invalid arguments to audio_calloc\n", funcname); AUD_log (NULL, "nmemb=%d size=%zu (len=%zu)\n", nmemb, size, len); return NULL; } return qemu_mallocz (len);}static char *audio_alloc_prefix (const char *s){ const char qemu_prefix[] = "QEMU_"; size_t len; char *r; if (!s) { return NULL; } len = strlen (s); r = qemu_malloc (len + sizeof (qemu_prefix)); if (r) { size_t i; char *u = r + sizeof (qemu_prefix) - 1; strcpy (r, qemu_prefix); strcat (r, s); for (i = 0; i < len; ++i) { u[i] = toupper ((uint8_t)u[i]); } } return r;}const char *audio_audfmt_to_string (audfmt_e fmt){ switch (fmt) { case AUD_FMT_U8: return "U8"; case AUD_FMT_U16: return "U16"; case AUD_FMT_S8: return "S8"; case AUD_FMT_S16: return "S16"; } dolog ("Bogus audfmt %d returning S16\n", fmt); return "S16";}audfmt_e audio_string_to_audfmt (const char *s, audfmt_e defval, int *defaultp){ if (!strcasecmp (s, "u8")) { *defaultp = 0; return AUD_FMT_U8; } else if (!strcasecmp (s, "u16")) { *defaultp = 0; return AUD_FMT_U16; } else if (!strcasecmp (s, "s8")) { *defaultp = 0; return AUD_FMT_S8; } else if (!strcasecmp (s, "s16")) { *defaultp = 0; return AUD_FMT_S16; } else { dolog ("Bogus audio format `%s' using %s\n", s, audio_audfmt_to_string (defval)); *defaultp = 1; return defval; }}static audfmt_e audio_get_conf_fmt (const char *envname, audfmt_e defval, int *defaultp){ const char *var = getenv (envname); if (!var) { *defaultp = 1; return defval; } return audio_string_to_audfmt (var, defval, defaultp);}static int audio_get_conf_int (const char *key, int defval, int *defaultp){ int val; char *strval; strval = getenv (key); if (strval) { *defaultp = 0; val = atoi (strval); return val; } else { *defaultp = 1; return defval; }}static const char *audio_get_conf_str (const char *key, const char *defval, int *defaultp){ const char *val = getenv (key); if (!val) { *defaultp = 1; return defval; } else { *defaultp = 0; return val; }}void AUD_vlog (const char *cap, const char *fmt, va_list ap){ if (conf.log_to_monitor) { if (cap) { term_printf ("%s: ", cap); } term_vprintf (fmt, ap); } else { if (cap) { fprintf (stderr, "%s: ", cap); } vfprintf (stderr, fmt, ap); }}void AUD_log (const char *cap, const char *fmt, ...){ va_list ap; va_start (ap, fmt); AUD_vlog (cap, fmt, ap); va_end (ap);}static void audio_print_options (const char *prefix, struct audio_option *opt){ char *uprefix; if (!prefix) { dolog ("No prefix specified\n"); return; } if (!opt) { dolog ("No options\n"); return; } uprefix = audio_alloc_prefix (prefix); for (; opt->name; opt++) { const char *state = "default"; printf (" %s_%s: ", uprefix, opt->name); if (opt->overridenp && *opt->overridenp) { state = "current"; } switch (opt->tag) { case AUD_OPT_BOOL: { int *intp = opt->valp; printf ("boolean, %s = %d\n", state, *intp ? 1 : 0); } break; case AUD_OPT_INT: { int *intp = opt->valp; printf ("integer, %s = %d\n", state, *intp); } break; case AUD_OPT_FMT: { audfmt_e *fmtp = opt->valp; printf ( "format, %s = %s, (one of: U8 S8 U16 S16)\n", state, audio_audfmt_to_string (*fmtp) ); } break; case AUD_OPT_STR: { const char **strp = opt->valp; printf ("string, %s = %s\n", state, *strp ? *strp : "(not set)"); } break; default: printf ("???\n"); dolog ("Bad value tag for option %s_%s %d\n", uprefix, opt->name, opt->tag); break; } printf (" %s\n", opt->descr); } qemu_free (uprefix);}static void audio_process_options (const char *prefix, struct audio_option *opt){ char *optname; const char qemu_prefix[] = "QEMU_"; size_t preflen; if (audio_bug (AUDIO_FUNC, !prefix)) { dolog ("prefix = NULL\n"); return; } if (audio_bug (AUDIO_FUNC, !opt)) { dolog ("opt = NULL\n"); return; } preflen = strlen (prefix); for (; opt->name; opt++) { size_t len, i; int def; if (!opt->valp) { dolog ("Option value pointer for `%s' is not set\n", opt->name); continue; } len = strlen (opt->name); /* len of opt->name + len of prefix + size of qemu_prefix * (includes trailing zero) + zero + underscore (on behalf of * sizeof) */ optname = qemu_malloc (len + preflen + sizeof (qemu_prefix) + 1); if (!optname) { dolog ("Could not allocate memory for option name `%s'\n", opt->name); continue; } strcpy (optname, qemu_prefix); /* copy while upper-casing, including trailing zero */ for (i = 0; i <= preflen; ++i) { optname[i + sizeof (qemu_prefix) - 1] = toupper ((uint8_t)prefix[i]); } strcat (optname, "_"); strcat (optname, opt->name); def = 1; switch (opt->tag) { case AUD_OPT_BOOL: case AUD_OPT_INT: { int *intp = opt->valp; *intp = audio_get_conf_int (optname, *intp, &def); } break; case AUD_OPT_FMT: { audfmt_e *fmtp = opt->valp; *fmtp = audio_get_conf_fmt (optname, *fmtp, &def); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -