📄 audio.c
字号:
} sw->total_hw_samples_mixed -= captured; sw->empty = sw->total_hw_samples_mixed == 0; } }}static void audio_timer (void *opaque){ AudioState *s = opaque; audio_run_out (s); audio_run_in (s); audio_run_capture (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 monitor 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))) { SWVoiceCap *sc; hwo->pcm_ops->ctl_out (hwo, VOICE_DISABLE); hwo->pcm_ops->fini_out (hwo); for (sc = hwo->cap_head.lh_first; sc; sc = sc->entries.le_next) { CaptureVoiceOut *cap = sc->cap; struct capture_callback *cb; for (cb = cap->cb_head.lh_first; cb; cb = cb->entries.le_next) { cb->ops.destroy (cb->opaque); } } } 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); LIST_INIT (&s->cap_head); 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;}CaptureVoiceOut *AUD_add_capture ( AudioState *s, audsettings_t *as, struct audio_capture_ops *ops, void *cb_opaque ){ CaptureVoiceOut *cap; struct capture_callback *cb; if (!s) { /* XXX suppress */ s = &glob_audio_state; } if (audio_validate_settings (as)) { dolog ("Invalid settings were passed when trying to add capture\n"); audio_print_settings (as); goto err0; } cb = audio_calloc (AUDIO_FUNC, 1, sizeof (*cb)); if (!cb) { dolog ("Could not allocate capture callback information, size %zu\n", sizeof (*cb)); goto err0; } cb->ops = *ops; cb->opaque = cb_opaque; cap = audio_pcm_capture_find_specific (s, as); if (cap) { LIST_INSERT_HEAD (&cap->cb_head, cb, entries); return cap; } else { HWVoiceOut *hw; CaptureVoiceOut *cap; cap = audio_calloc (AUDIO_FUNC, 1, sizeof (*cap)); if (!cap) { dolog ("Could not allocate capture voice, size %zu\n", sizeof (*cap)); goto err1; } hw = &cap->hw; LIST_INIT (&hw->sw_head); LIST_INIT (&cap->cb_head); /* XXX find a more elegant way */ hw->samples = 4096 * 4; hw->mix_buf = audio_calloc (AUDIO_FUNC, hw->samples, sizeof (st_sample_t)); if (!hw->mix_buf) { dolog ("Could not allocate capture mix buffer (%d samples)\n", hw->samples); goto err2; } audio_pcm_init_info (&hw->info, as); cap->buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift); if (!cap->buf) { dolog ("Could not allocate capture buffer " "(%d samples, each %d bytes)\n", hw->samples, 1 << hw->info.shift); goto err3; } hw->clip = mixeng_clip [hw->info.nchannels == 2] [hw->info.sign] [hw->info.swap_endianness] [audio_bits_to_index (hw->info.bits)]; LIST_INSERT_HEAD (&s->cap_head, cap, entries); LIST_INSERT_HEAD (&cap->cb_head, cb, entries); hw = NULL; while ((hw = audio_pcm_hw_find_any_out (s, hw))) { audio_attach_capture (s, hw); } return cap; err3: qemu_free (cap->hw.mix_buf); err2: qemu_free (cap); err1: qemu_free (cb); err0: return NULL; }}void AUD_del_capture (CaptureVoiceOut *cap, void *cb_opaque){ struct capture_callback *cb; for (cb = cap->cb_head.lh_first; cb; cb = cb->entries.le_next) { if (cb->opaque == cb_opaque) { cb->ops.destroy (cb_opaque); LIST_REMOVE (cb, entries); qemu_free (cb); if (!cap->cb_head.lh_first) { SWVoiceOut *sw = cap->hw.sw_head.lh_first, *sw1; while (sw) { SWVoiceCap *sc = (SWVoiceCap *) sw;#ifdef DEBUG_CAPTURE dolog ("freeing %s\n", sw->name);#endif sw1 = sw->entries.le_next; if (sw->rate) { st_rate_stop (sw->rate); sw->rate = NULL; } LIST_REMOVE (sw, entries); LIST_REMOVE (sc, entries); qemu_free (sc); sw = sw1; } LIST_REMOVE (cap, entries); qemu_free (cap); } return; } }}void AUD_set_volume_out (SWVoiceOut *sw, int mute, uint8_t lvol, uint8_t rvol){ if (sw) { sw->vol.mute = mute; sw->vol.l = nominal_volume.l * lvol / 255; sw->vol.r = nominal_volume.r * rvol / 255; }}void AUD_set_volume_in (SWVoiceIn *sw, int mute, uint8_t lvol, uint8_t rvol){ if (sw) { sw->vol.mute = mute; sw->vol.l = nominal_volume.l * lvol / 255; sw->vol.r = nominal_volume.r * rvol / 255; }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -