📄 audio.c
字号:
if (!sw) { return 0; } live = sw->hw->total_samples_captured - sw->total_hw_samples_acquired; if (audio_bug (AUDIO_FUNC, live < 0 || live > sw->hw->samples)) { dolog ("live=%d sw->hw->samples=%d\n", live, sw->hw->samples); return 0; } ldebug ( "%s: get_avail live %d ret %lld\n", SW_NAME (sw), live, (((int64_t) live << 32) / sw->ratio) << sw->info.shift ); return (((int64_t) live << 32) / sw->ratio) << sw->info.shift;}static int audio_get_free (SWVoiceOut *sw){ int live, dead; if (!sw) { return 0; } live = sw->total_hw_samples_mixed; if (audio_bug (AUDIO_FUNC, live < 0 || live > sw->hw->samples)) { dolog ("live=%d sw->hw->samples=%d\n", live, sw->hw->samples); return 0; } dead = sw->hw->samples - live;#ifdef DEBUG_OUT dolog ("%s: get_free live %d dead %d ret %lld\n", SW_NAME (sw), live, dead, (((int64_t) dead << 32) / sw->ratio) << sw->info.shift);#endif return (((int64_t) dead << 32) / sw->ratio) << sw->info.shift;}static void audio_run_out (AudioState *s){ HWVoiceOut *hw = NULL; SWVoiceOut *sw; while ((hw = audio_pcm_hw_find_any_enabled_out (s, hw))) { int played; int live, free, nb_live, cleanup_required; live = audio_pcm_hw_get_live_out2 (hw, &nb_live); if (!nb_live) { live = 0; } if (audio_bug (AUDIO_FUNC, live < 0 || live > hw->samples)) { dolog ("live=%d hw->samples=%d\n", live, hw->samples); continue; } if (hw->pending_disable && !nb_live) {#ifdef DEBUG_OUT dolog ("Disabling voice\n");#endif hw->enabled = 0; hw->pending_disable = 0; hw->pcm_ops->ctl_out (hw, VOICE_DISABLE); continue; } if (!live) { for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) { if (sw->active) { free = audio_get_free (sw); if (free > 0) { sw->callback.fn (sw->callback.opaque, free); } } } continue; } played = hw->pcm_ops->run_out (hw); if (audio_bug (AUDIO_FUNC, hw->rpos >= hw->samples)) { dolog ("hw->rpos=%d hw->samples=%d played=%d\n", hw->rpos, hw->samples, played); hw->rpos = 0; }#ifdef DEBUG_OUT dolog ("played=%d\n", played);#endif if (played) { hw->ts_helper += played; } cleanup_required = 0; for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) { if (!sw->active && sw->empty) { continue; } if (audio_bug (AUDIO_FUNC, played > sw->total_hw_samples_mixed)) { dolog ("played=%d sw->total_hw_samples_mixed=%d\n", played, sw->total_hw_samples_mixed); played = sw->total_hw_samples_mixed; } sw->total_hw_samples_mixed -= played; if (!sw->total_hw_samples_mixed) { sw->empty = 1; cleanup_required |= !sw->active && !sw->callback.fn; } if (sw->active) { free = audio_get_free (sw); if (free > 0) { sw->callback.fn (sw->callback.opaque, free); } } } if (cleanup_required) { restart: for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) { if (!sw->active && !sw->callback.fn) {#ifdef DEBUG_PLIVE dolog ("Finishing with old voice\n");#endif audio_close_out (s, sw); goto restart; /* play it safe */ } } } }}static void audio_run_in (AudioState *s){ HWVoiceIn *hw = NULL; while ((hw = audio_pcm_hw_find_any_enabled_in (s, hw))) { SWVoiceIn *sw; int captured, min; captured = hw->pcm_ops->run_in (hw); min = audio_pcm_hw_find_min_in (hw); hw->total_samples_captured += captured - min; hw->ts_helper += captured; for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) { sw->total_hw_samples_acquired -= min; if (sw->active) { int avail; avail = audio_get_avail (sw); if (avail > 0) { sw->callback.fn (sw->callback.opaque, avail); } } } }}static void audio_timer (void *opaque){ AudioState *s = opaque; audio_run_out (s); audio_run_in (s); qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + conf.period.ticks);}static struct audio_option audio_options[] = { /* DAC */ {"DAC_FIXED_SETTINGS", AUD_OPT_BOOL, &conf.fixed_out.enabled, "Use fixed settings for host DAC", NULL, 0}, {"DAC_FIXED_FREQ", AUD_OPT_INT, &conf.fixed_out.settings.freq, "Frequency for fixed host DAC", NULL, 0}, {"DAC_FIXED_FMT", AUD_OPT_FMT, &conf.fixed_out.settings.fmt, "Format for fixed host DAC", NULL, 0}, {"DAC_FIXED_CHANNELS", AUD_OPT_INT, &conf.fixed_out.settings.nchannels, "Number of channels for fixed DAC (1 - mono, 2 - stereo)", NULL, 0}, {"DAC_VOICES", AUD_OPT_INT, &conf.fixed_out.nb_voices, "Number of voices for DAC", NULL, 0}, /* ADC */ {"ADC_FIXED_SETTINGS", AUD_OPT_BOOL, &conf.fixed_in.enabled, "Use fixed settings for host ADC", NULL, 0}, {"ADC_FIXED_FREQ", AUD_OPT_INT, &conf.fixed_in.settings.freq, "Frequency for fixed host ADC", NULL, 0}, {"ADC_FIXED_FMT", AUD_OPT_FMT, &conf.fixed_in.settings.fmt, "Format for fixed host ADC", NULL, 0}, {"ADC_FIXED_CHANNELS", AUD_OPT_INT, &conf.fixed_in.settings.nchannels, "Number of channels for fixed ADC (1 - mono, 2 - stereo)", NULL, 0}, {"ADC_VOICES", AUD_OPT_INT, &conf.fixed_in.nb_voices, "Number of voices for ADC", NULL, 0}, /* Misc */ {"TIMER_PERIOD", AUD_OPT_INT, &conf.period.hz, "Timer period in HZ (0 - use lowest possible)", NULL, 0}, {"PLIVE", AUD_OPT_BOOL, &conf.plive, "(undocumented)", NULL, 0}, {"LOG_TO_MONITOR", AUD_OPT_BOOL, &conf.log_to_monitor, "print logging messages to montior instead of stderr", NULL, 0}, {NULL, 0, NULL, NULL, NULL, 0}};static void audio_pp_nb_voices (const char *typ, int nb){ switch (nb) { case 0: printf ("Does not support %s\n", typ); break; case 1: printf ("One %s voice\n", typ); break; case INT_MAX: printf ("Theoretically supports many %s voices\n", typ); break; default: printf ("Theoretically supports upto %d %s voices\n", nb, typ); break; }}void AUD_help (void){ size_t i; audio_process_options ("AUDIO", audio_options); for (i = 0; i < sizeof (drvtab) / sizeof (drvtab[0]); i++) { struct audio_driver *d = drvtab[i]; if (d->options) { audio_process_options (d->name, d->options); } } printf ("Audio options:\n"); audio_print_options ("AUDIO", audio_options); printf ("\n"); printf ("Available drivers:\n"); for (i = 0; i < sizeof (drvtab) / sizeof (drvtab[0]); i++) { struct audio_driver *d = drvtab[i]; printf ("Name: %s\n", d->name); printf ("Description: %s\n", d->descr); audio_pp_nb_voices ("playback", d->max_voices_out); audio_pp_nb_voices ("capture", d->max_voices_in); if (d->options) { printf ("Options:\n"); audio_print_options (d->name, d->options); } else { printf ("No options\n"); } printf ("\n"); } printf ( "Options are settable through environment variables.\n" "Example:\n"#ifdef _WIN32 " set QEMU_AUDIO_DRV=wav\n" " set QEMU_WAV_PATH=c:\\tune.wav\n"#else " export QEMU_AUDIO_DRV=wav\n" " export QEMU_WAV_PATH=$HOME/tune.wav\n" "(for csh replace export with setenv in the above)\n"#endif " qemu ...\n\n" );}static int audio_driver_init (AudioState *s, struct audio_driver *drv){ if (drv->options) { audio_process_options (drv->name, drv->options); } s->drv_opaque = drv->init (); if (s->drv_opaque) { audio_init_nb_voices_out (s, drv); audio_init_nb_voices_in (s, drv); s->drv = drv; return 0; } else { dolog ("Could not init `%s' audio driver\n", drv->name); return -1; }}static void audio_vm_change_state_handler (void *opaque, int running){ AudioState *s = opaque; HWVoiceOut *hwo = NULL; HWVoiceIn *hwi = NULL; int op = running ? VOICE_ENABLE : VOICE_DISABLE; while ((hwo = audio_pcm_hw_find_any_enabled_out (s, hwo))) { hwo->pcm_ops->ctl_out (hwo, op); } while ((hwi = audio_pcm_hw_find_any_enabled_in (s, hwi))) { hwi->pcm_ops->ctl_in (hwi, op); }}static void audio_atexit (void){ AudioState *s = &glob_audio_state; HWVoiceOut *hwo = NULL; HWVoiceIn *hwi = NULL; while ((hwo = audio_pcm_hw_find_any_enabled_out (s, hwo))) { hwo->pcm_ops->ctl_out (hwo, VOICE_DISABLE); hwo->pcm_ops->fini_out (hwo); } while ((hwi = audio_pcm_hw_find_any_enabled_in (s, hwi))) { hwi->pcm_ops->ctl_in (hwi, VOICE_DISABLE); hwi->pcm_ops->fini_in (hwi); } if (s->drv) { s->drv->fini (s->drv_opaque); }}static void audio_save (QEMUFile *f, void *opaque){ (void) f; (void) opaque;}static int audio_load (QEMUFile *f, void *opaque, int version_id){ (void) f; (void) opaque; if (version_id != 1) { return -EINVAL; } return 0;}void AUD_register_card (AudioState *s, const char *name, QEMUSoundCard *card){ card->audio = s; card->name = qemu_strdup (name); memset (&card->entries, 0, sizeof (card->entries)); LIST_INSERT_HEAD (&s->card_head, card, entries);}void AUD_remove_card (QEMUSoundCard *card){ LIST_REMOVE (card, entries); card->audio = NULL; qemu_free (card->name);}AudioState *AUD_init (void){ size_t i; int done = 0; const char *drvname; AudioState *s = &glob_audio_state; LIST_INIT (&s->hw_head_out); LIST_INIT (&s->hw_head_in); atexit (audio_atexit); s->ts = qemu_new_timer (vm_clock, audio_timer, s); if (!s->ts) { dolog ("Could not create audio timer\n"); return NULL; } audio_process_options ("AUDIO", audio_options); s->nb_hw_voices_out = conf.fixed_out.nb_voices; s->nb_hw_voices_in = conf.fixed_in.nb_voices; if (s->nb_hw_voices_out <= 0) { dolog ("Bogus number of playback voices %d, setting to 1\n", s->nb_hw_voices_out); s->nb_hw_voices_out = 1; } if (s->nb_hw_voices_in <= 0) { dolog ("Bogus number of capture voices %d, setting to 0\n", s->nb_hw_voices_in); s->nb_hw_voices_in = 0; } { int def; drvname = audio_get_conf_str ("QEMU_AUDIO_DRV", NULL, &def); } if (drvname) { int found = 0; for (i = 0; i < sizeof (drvtab) / sizeof (drvtab[0]); i++) { if (!strcmp (drvname, drvtab[i]->name)) { done = !audio_driver_init (s, drvtab[i]); found = 1; break; } } if (!found) { dolog ("Unknown audio driver `%s'\n", drvname); dolog ("Run with -audio-help to list available drivers\n"); } } if (!done) { for (i = 0; !done && i < sizeof (drvtab) / sizeof (drvtab[0]); i++) { if (drvtab[i]->can_be_default) { done = !audio_driver_init (s, drvtab[i]); } } } if (!done) { done = !audio_driver_init (s, &no_audio_driver); if (!done) { dolog ("Could not initialize audio subsystem\n"); } else { dolog ("warning: Using timer based audio emulation\n"); } } if (done) { VMChangeStateEntry *e; if (conf.period.hz <= 0) { if (conf.period.hz < 0) { dolog ("warning: Timer period is negative - %d " "treating as zero\n", conf.period.hz); } conf.period.ticks = 1; } else { conf.period.ticks = ticks_per_sec / conf.period.hz; } e = qemu_add_vm_change_state_handler (audio_vm_change_state_handler, s); if (!e) { dolog ("warning: Could not register change state handler\n" "(Audio can continue looping even after stopping the VM)\n"); } } else { qemu_del_timer (s->ts); return NULL; } LIST_INIT (&s->card_head); register_savevm ("audio", 0, 1, audio_save, audio_load, s); qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + conf.period.ticks); return s;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -