📄 audio.c
字号:
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); } break; case AUD_OPT_STR: { const char **strp = opt->valp; *strp = audio_get_conf_str (optname, *strp, &def); } break; default: dolog ("Bad value tag for option `%s' - %d\n", optname, opt->tag); break; } if (!opt->overriddenp) { opt->overriddenp = &opt->overridden; } *opt->overriddenp = !def; qemu_free (optname); }}static void audio_print_settings (audsettings_t *as){ dolog ("frequency=%d nchannels=%d fmt=", as->freq, as->nchannels); switch (as->fmt) { case AUD_FMT_S8: AUD_log (NULL, "S8"); break; case AUD_FMT_U8: AUD_log (NULL, "U8"); break; case AUD_FMT_S16: AUD_log (NULL, "S16"); break; case AUD_FMT_U16: AUD_log (NULL, "U16"); break; default: AUD_log (NULL, "invalid(%d)", as->fmt); break; } AUD_log (NULL, " endianness="); switch (as->endianness) { case 0: AUD_log (NULL, "little"); break; case 1: AUD_log (NULL, "big"); break; default: AUD_log (NULL, "invalid"); break; } AUD_log (NULL, "\n");}static int audio_validate_settings (audsettings_t *as){ int invalid; invalid = as->nchannels != 1 && as->nchannels != 2; invalid |= as->endianness != 0 && as->endianness != 1; switch (as->fmt) { case AUD_FMT_S8: case AUD_FMT_U8: case AUD_FMT_S16: case AUD_FMT_U16: case AUD_FMT_S32: case AUD_FMT_U32: break; default: invalid = 1; break; } invalid |= as->freq <= 0; return invalid ? -1 : 0;}static int audio_pcm_info_eq (struct audio_pcm_info *info, audsettings_t *as){ int bits = 8, sign = 0; switch (as->fmt) { case AUD_FMT_S8: sign = 1; case AUD_FMT_U8: break; case AUD_FMT_S16: sign = 1; case AUD_FMT_U16: bits = 16; break; case AUD_FMT_S32: sign = 1; case AUD_FMT_U32: bits = 32; break; } return info->freq == as->freq && info->nchannels == as->nchannels && info->sign == sign && info->bits == bits && info->swap_endianness == (as->endianness != AUDIO_HOST_ENDIANNESS);}void audio_pcm_init_info (struct audio_pcm_info *info, audsettings_t *as){ int bits = 8, sign = 0, shift = 0; switch (as->fmt) { case AUD_FMT_S8: sign = 1; case AUD_FMT_U8: break; case AUD_FMT_S16: sign = 1; case AUD_FMT_U16: bits = 16; shift = 1; break; case AUD_FMT_S32: sign = 1; case AUD_FMT_U32: bits = 32; shift = 2; break; } info->freq = as->freq; info->bits = bits; info->sign = sign; info->nchannels = as->nchannels; info->shift = (as->nchannels == 2) + shift; info->align = (1 << info->shift) - 1; info->bytes_per_second = info->freq << info->shift; info->swap_endianness = (as->endianness != AUDIO_HOST_ENDIANNESS);}void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len){ if (!len) { return; } if (info->sign) { memset (buf, 0x00, len << info->shift); } else { switch (info->bits) { case 8: memset (buf, 0x80, len << info->shift); break; case 16: { int i; uint16_t *p = buf; int shift = info->nchannels - 1; short s = INT16_MAX; if (info->swap_endianness) { s = bswap16 (s); } for (i = 0; i < len << shift; i++) { p[i] = s; } } break; case 32: { int i; uint32_t *p = buf; int shift = info->nchannels - 1; int32_t s = INT32_MAX; if (info->swap_endianness) { s = bswap32 (s); } for (i = 0; i < len << shift; i++) { p[i] = s; } } break; default: AUD_log (NULL, "audio_pcm_info_clear_buf: invalid bits %d\n", info->bits); break; } }}/* * Capture */static void noop_conv (st_sample_t *dst, const void *src, int samples, volume_t *vol){ (void) src; (void) dst; (void) samples; (void) vol;}static CaptureVoiceOut *audio_pcm_capture_find_specific ( AudioState *s, audsettings_t *as ){ CaptureVoiceOut *cap; for (cap = s->cap_head.lh_first; cap; cap = cap->entries.le_next) { if (audio_pcm_info_eq (&cap->hw.info, as)) { return cap; } } return NULL;}static void audio_notify_capture (CaptureVoiceOut *cap, audcnotification_e cmd){ struct capture_callback *cb;#ifdef DEBUG_CAPTURE dolog ("notification %d sent\n", cmd);#endif for (cb = cap->cb_head.lh_first; cb; cb = cb->entries.le_next) { cb->ops.notify (cb->opaque, cmd); }}static void audio_capture_maybe_changed (CaptureVoiceOut *cap, int enabled){ if (cap->hw.enabled != enabled) { audcnotification_e cmd; cap->hw.enabled = enabled; cmd = enabled ? AUD_CNOTIFY_ENABLE : AUD_CNOTIFY_DISABLE; audio_notify_capture (cap, cmd); }}static void audio_recalc_and_notify_capture (CaptureVoiceOut *cap){ HWVoiceOut *hw = &cap->hw; SWVoiceOut *sw; int enabled = 0; for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) { if (sw->active) { enabled = 1; break; } } audio_capture_maybe_changed (cap, enabled);}static void audio_detach_capture (HWVoiceOut *hw){ SWVoiceCap *sc = hw->cap_head.lh_first; while (sc) { SWVoiceCap *sc1 = sc->entries.le_next; SWVoiceOut *sw = &sc->sw; CaptureVoiceOut *cap = sc->cap; int was_active = sw->active; if (sw->rate) { st_rate_stop (sw->rate); sw->rate = NULL; } LIST_REMOVE (sw, entries); LIST_REMOVE (sc, entries); qemu_free (sc); if (was_active) { /* We have removed soft voice from the capture: this might have changed the overall status of the capture since this might have been the only active voice */ audio_recalc_and_notify_capture (cap); } sc = sc1; }}static int audio_attach_capture (AudioState *s, HWVoiceOut *hw){ CaptureVoiceOut *cap; audio_detach_capture (hw); for (cap = s->cap_head.lh_first; cap; cap = cap->entries.le_next) { SWVoiceCap *sc; SWVoiceOut *sw; HWVoiceOut *hw_cap = &cap->hw; sc = audio_calloc (AUDIO_FUNC, 1, sizeof (*sc)); if (!sc) { dolog ("Could not allocate soft capture voice (%zu bytes)\n", sizeof (*sc)); return -1; } sc->cap = cap; sw = &sc->sw; sw->hw = hw_cap; sw->info = hw->info; sw->empty = 1; sw->active = hw->enabled; sw->conv = noop_conv; sw->ratio = ((int64_t) hw_cap->info.freq << 32) / sw->info.freq; sw->rate = st_rate_start (sw->info.freq, hw_cap->info.freq); if (!sw->rate) { dolog ("Could not start rate conversion for `%s'\n", SW_NAME (sw)); qemu_free (sw); return -1; } LIST_INSERT_HEAD (&hw_cap->sw_head, sw, entries); LIST_INSERT_HEAD (&hw->cap_head, sc, entries);#ifdef DEBUG_CAPTURE asprintf (&sw->name, "for %p %d,%d,%d", hw, sw->info.freq, sw->info.bits, sw->info.nchannels); dolog ("Added %s active = %d\n", sw->name, sw->active);#endif if (sw->active) { audio_capture_maybe_changed (cap, 1); } } return 0;}/* * Hard voice (capture) */static int audio_pcm_hw_find_min_in (HWVoiceIn *hw){ SWVoiceIn *sw; int m = hw->total_samples_captured; for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) { if (sw->active) { m = audio_MIN (m, sw->total_hw_samples_acquired); } } return m;}int audio_pcm_hw_get_live_in (HWVoiceIn *hw){ int live = hw->total_samples_captured - audio_pcm_hw_find_min_in (hw); if (audio_bug (AUDIO_FUNC, live < 0 || live > hw->samples)) { dolog ("live=%d hw->samples=%d\n", live, hw->samples); return 0; } return live;}/* * Soft voice (capture) */static int audio_pcm_sw_get_rpos_in (SWVoiceIn *sw){ HWVoiceIn *hw = sw->hw; int live = hw->total_samples_captured - sw->total_hw_samples_acquired; int rpos; if (audio_bug (AUDIO_FUNC, live < 0 || live > hw->samples)) { dolog ("live=%d hw->samples=%d\n", live, hw->samples); return 0; } rpos = hw->wpos - live; if (rpos >= 0) { return rpos; } else { return hw->samples + rpos; }}int audio_pcm_sw_read (SWVoiceIn *sw, void *buf, int size){ HWVoiceIn *hw = sw->hw; int samples, live, ret = 0, swlim, isamp, osamp, rpos, total = 0; st_sample_t *src, *dst = sw->buf; rpos = audio_pcm_sw_get_rpos_in (sw) % hw->samples; live = hw->total_samples_captured - sw->total_hw_samples_acquired; if (audio_bug (AUDIO_FUNC, live < 0 || live > hw->samples)) { dolog ("live_in=%d hw->samples=%d\n", live, hw->samples); return 0; } samples = size >> sw->info.shift; if (!live) { return 0; } swlim = (live * sw->ratio) >> 32; swlim = audio_MIN (swlim, samples); while (swlim) { src = hw->conv_buf + rpos; isamp = hw->wpos - rpos; /* XXX: <= ? */ if (isamp <= 0) { isamp = hw->samples - rpos; } if (!isamp) { break; } osamp = swlim; if (audio_bug (AUDIO_FUNC, osamp < 0)) { dolog ("osamp=%d\n", osamp); return 0; } st_rate_flow (sw->rate, src, dst, &isamp, &osamp); swlim -= osamp; rpos = (rpos + isamp) % hw->samples; dst += osamp; ret += osamp; total += isamp; } sw->clip (buf, sw->buf, ret); sw->total_hw_samples_acquired += total; return ret << sw->info.shift;}/* * Hard voice (playback) */static int audio_pcm_hw_find_min_out (HWVoiceOut *hw, int *nb_livep){ SWVoiceOut *sw; int m = INT_MAX; int nb_live = 0; for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) { if (sw->active || !sw->empty) { m = audio_MIN (m, sw->total_hw_samples_mixed); nb_live += 1; } } *nb_livep = nb_live; return m;}int audio_pcm_hw_get_live_out2 (HWVoiceOut *hw, int *nb_live){ int smin; smin = audio_pcm_hw_find_min_out (hw, nb_live); if (!*nb_live) { return 0; } else { int live = smin; if (audio_bug (AUDIO_FUNC, live < 0 || live > hw->samples)) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -