From 571ec3d68ddfa230f1c60eba1f7e24f5a3ffb03b Mon Sep 17 00:00:00 2001 From: bellard Date: Sun, 20 Nov 2005 16:24:34 +0000 Subject: [PATCH] audio merge (malc) git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1636 c046a42c-6fe2-441c-8c8c-71466251a162 --- audio/alsaaudio.c | 222 +++++++++++++++----------- audio/audio.c | 348 ++++++++--------------------------------- audio/audio.h | 6 +- audio/audio_int.h | 2 +- audio/audio_template.h | 276 +++++++++++++++++++++++--------- audio/ossaudio.c | 10 +- 6 files changed, 406 insertions(+), 458 deletions(-) diff --git a/audio/alsaaudio.c b/audio/alsaaudio.c index f7748ca82f..3ab264e01b 100644 --- a/audio/alsaaudio.c +++ b/audio/alsaaudio.c @@ -31,15 +31,12 @@ typedef struct ALSAVoiceOut { HWVoiceOut hw; void *pcm_buf; snd_pcm_t *handle; - int can_pause; - int was_enabled; } ALSAVoiceOut; typedef struct ALSAVoiceIn { HWVoiceIn hw; snd_pcm_t *handle; void *pcm_buf; - int can_pause; } ALSAVoiceIn; static struct { @@ -58,6 +55,7 @@ static struct { int buffer_size_out_overriden; int period_size_out_overriden; + int verbose; } conf = { #ifdef HIGH_LATENCY .size_in_usec_in = 1, @@ -73,8 +71,8 @@ static struct { #else #define DEFAULT_BUFFER_SIZE 1024 #define DEFAULT_PERIOD_SIZE 256 - .buffer_size_in = DEFAULT_BUFFER_SIZE, - .period_size_in = DEFAULT_PERIOD_SIZE, + .buffer_size_in = DEFAULT_BUFFER_SIZE * 4, + .period_size_in = DEFAULT_PERIOD_SIZE * 4, .buffer_size_out = DEFAULT_BUFFER_SIZE, .period_size_out = DEFAULT_PERIOD_SIZE, .buffer_size_in_overriden = 0, @@ -82,7 +80,8 @@ static struct { .period_size_in_overriden = 0, .period_size_out_overriden = 0, #endif - .threshold = 0 + .threshold = 0, + .verbose = 0 }; struct alsa_params_req { @@ -97,7 +96,6 @@ struct alsa_params_obt { int freq; audfmt_e fmt; int nchannels; - int can_pause; snd_pcm_uframes_t samples; }; @@ -474,12 +472,6 @@ static int alsa_open (int in, struct alsa_params_req *req, goto err; } - obt->can_pause = snd_pcm_hw_params_can_pause (hw_params); - if (obt->can_pause < 0) { - alsa_logerr (err, "Could not get pause capability for %s\n", typ); - obt->can_pause = 0; - } - if (!in && conf.threshold) { snd_pcm_uframes_t threshold; int bytes_per_sec; @@ -527,6 +519,28 @@ static int alsa_recover (snd_pcm_t *handle) return 0; } +static snd_pcm_sframes_t alsa_get_avail (snd_pcm_t *handle) +{ + snd_pcm_sframes_t avail; + + avail = snd_pcm_avail_update (handle); + if (avail < 0) { + if (avail == -EPIPE) { + if (!alsa_recover (handle)) { + avail = snd_pcm_avail_update (handle); + } + } + + if (avail < 0) { + alsa_logerr (avail, + "Could not obtain number of available frames\n"); + return -1; + } + } + + return avail; +} + static int alsa_run_out (HWVoiceOut *hw) { ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw; @@ -541,57 +555,53 @@ static int alsa_run_out (HWVoiceOut *hw) return 0; } - avail = snd_pcm_avail_update (alsa->handle); + avail = alsa_get_avail (alsa->handle); if (avail < 0) { - if (avail == -EPIPE) { - if (!alsa_recover (alsa->handle)) { - avail = snd_pcm_avail_update (alsa->handle); - if (avail >= 0) { - goto ok; - } - } - } - - alsa_logerr (avail, "Could not get amount free space\n"); + dolog ("Could not get number of available playback frames\n"); return 0; } - ok: decr = audio_MIN (live, avail); samples = decr; rpos = hw->rpos; while (samples) { int left_till_end_samples = hw->samples - rpos; - int convert_samples = audio_MIN (samples, left_till_end_samples); + int len = audio_MIN (samples, left_till_end_samples); snd_pcm_sframes_t written; src = hw->mix_buf + rpos; dst = advance (alsa->pcm_buf, rpos << hw->info.shift); - hw->clip (dst, src, convert_samples); + hw->clip (dst, src, len); - while (convert_samples) { - written = snd_pcm_writei (alsa->handle, dst, convert_samples); + while (len) { + written = snd_pcm_writei (alsa->handle, dst, len); - if (written < 0) { + if (written <= 0) { switch (written) { - case -EPIPE: - if (!alsa_recover (alsa->handle)) { - continue; + case 0: + if (conf.verbose) { + dolog ("Failed to write %d frames (wrote zero)\n", len); } - dolog ("Failed to write %d frames to %p, " - "handle %p not prepared\n", - convert_samples, - dst, - alsa->handle); goto exit; - case -EAGAIN: + case -EPIPE: + if (alsa_recover (alsa->handle)) { + alsa_logerr (written, "Failed to write %d frames\n", + len); + goto exit; + } + if (conf.verbose) { + dolog ("Recovering from playback xrun\n"); + } continue; + case -EAGAIN: + goto exit; + default: alsa_logerr (written, "Failed to write %d frames to %p\n", - convert_samples, dst); + len, dst); goto exit; } } @@ -599,7 +609,7 @@ static int alsa_run_out (HWVoiceOut *hw) mixeng_clear (src, written); rpos = (rpos + written) % hw->samples; samples -= written; - convert_samples -= written; + len -= written; dst = advance (dst, written << hw->info.shift); src += written; } @@ -659,7 +669,6 @@ static int alsa_init_out (HWVoiceOut *hw, audsettings_t *as) &obt_as, audio_need_to_swap_endian (endianness) ); - alsa->can_pause = obt.can_pause; hw->samples = obt.samples; alsa->pcm_buf = audio_calloc (AUDIO_FUNC, obt.samples, 1 << hw->info.shift); @@ -671,46 +680,46 @@ static int alsa_init_out (HWVoiceOut *hw, audsettings_t *as) } alsa->handle = handle; - alsa->was_enabled = 0; + return 0; +} + +static int alsa_voice_ctl (snd_pcm_t *handle, const char *typ, int pause) +{ + int err; + + if (pause) { + err = snd_pcm_drop (handle); + if (err < 0) { + alsa_logerr (err, "Could not stop %s", typ); + return -1; + } + } + else { + err = snd_pcm_prepare (handle); + if (err < 0) { + alsa_logerr (err, "Could not prepare handle for %s", typ); + return -1; + } + } + return 0; } static int alsa_ctl_out (HWVoiceOut *hw, int cmd, ...) { - int err; ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw; switch (cmd) { case VOICE_ENABLE: ldebug ("enabling voice\n"); - audio_pcm_info_clear_buf (&hw->info, alsa->pcm_buf, hw->samples); - if (alsa->can_pause) { - /* Why this was_enabled madness is needed at all?? */ - if (alsa->was_enabled) { - err = snd_pcm_pause (alsa->handle, 0); - if (err < 0) { - alsa_logerr (err, "Failed to resume playing\n"); - /* not fatal really */ - } - } - else { - alsa->was_enabled = 1; - } - } - break; + return alsa_voice_ctl (alsa->handle, "playback", 0); case VOICE_DISABLE: ldebug ("disabling voice\n"); - if (alsa->can_pause) { - err = snd_pcm_pause (alsa->handle, 1); - if (err < 0) { - alsa_logerr (err, "Failed to stop playing\n"); - /* not fatal really */ - } - } - break; + return alsa_voice_ctl (alsa->handle, "playback", 1); } - return 0; + + return -1; } static int alsa_init_in (HWVoiceIn *hw, audsettings_t *as) @@ -749,7 +758,6 @@ static int alsa_init_in (HWVoiceIn *hw, audsettings_t *as) &obt_as, audio_need_to_swap_endian (endianness) ); - alsa->can_pause = obt.can_pause; hw->samples = obt.samples; alsa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift); @@ -783,6 +791,7 @@ static int alsa_run_in (HWVoiceIn *hw) int i; int live = audio_pcm_hw_get_live_in (hw); int dead = hw->samples - live; + int decr; struct { int add; int len; @@ -790,21 +799,35 @@ static int alsa_run_in (HWVoiceIn *hw) { hw->wpos, 0 }, { 0, 0 } }; - + snd_pcm_sframes_t avail; snd_pcm_uframes_t read_samples = 0; if (!dead) { return 0; } - if (hw->wpos + dead > hw->samples) { - bufs[0].len = (hw->samples - hw->wpos); - bufs[1].len = (dead - (hw->samples - hw->wpos)); - } - else { - bufs[0].len = dead; + avail = alsa_get_avail (alsa->handle); + if (avail < 0) { + dolog ("Could not get number of captured frames\n"); + return 0; } + if (!avail && (snd_pcm_state (alsa->handle) == SND_PCM_STATE_PREPARED)) { + avail = hw->samples; + } + + decr = audio_MIN (dead, avail); + if (!decr) { + return 0; + } + + if (hw->wpos + decr > hw->samples) { + bufs[0].len = (hw->samples - hw->wpos); + bufs[1].len = (decr - (hw->samples - hw->wpos)); + } + else { + bufs[0].len = decr; + } for (i = 0; i < 2; ++i) { void *src; @@ -820,24 +843,27 @@ static int alsa_run_in (HWVoiceIn *hw) while (len) { nread = snd_pcm_readi (alsa->handle, src, len); - if (nread < 0) { + if (nread <= 0) { switch (nread) { - case -EPIPE: - if (!alsa_recover (alsa->handle)) { - continue; + case 0: + if (conf.verbose) { + dolog ("Failed to read %ld frames (read zero)\n", len); } - dolog ( - "Failed to read %ld frames from %p, " - "handle %p not prepared\n", - len, - src, - alsa->handle - ); goto exit; - case -EAGAIN: + case -EPIPE: + if (alsa_recover (alsa->handle)) { + alsa_logerr (nread, "Failed to read %ld frames\n", len); + goto exit; + } + if (conf.verbose) { + dolog ("Recovering from capture xrun\n"); + } continue; + case -EAGAIN: + goto exit; + default: alsa_logerr ( nread, @@ -871,9 +897,19 @@ static int alsa_read (SWVoiceIn *sw, void *buf, int size) static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...) { - (void) hw; - (void) cmd; - return 0; + ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw; + + switch (cmd) { + case VOICE_ENABLE: + ldebug ("enabling voice\n"); + return alsa_voice_ctl (alsa->handle, "capture", 0); + + case VOICE_DISABLE: + ldebug ("disabling voice\n"); + return alsa_voice_ctl (alsa->handle, "capture", 1); + } + + return -1; } static void *alsa_audio_init (void) @@ -909,6 +945,10 @@ static struct audio_option alsa_options[] = { {"ADC_DEV", AUD_OPT_STR, &conf.pcm_name_in, "ADC device name", NULL, 0}, + + {"VERBOSE", AUD_OPT_BOOL, &conf.verbose, + "Behave in a more verbose way", NULL, 0}, + {NULL, 0, NULL, NULL, NULL, 0} }; diff --git a/audio/audio.c b/audio/audio.c index eba4fdb121..7634535230 100644 --- a/audio/audio.c +++ b/audio/audio.c @@ -96,7 +96,7 @@ static struct { { 0 }, /* period */ 0, /* plive */ - 0 + 0 /* log_to_monitor */ }; static AudioState glob_audio_state; @@ -623,25 +623,6 @@ void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len) /* * Hard voice (capture) */ -static void audio_pcm_hw_free_resources_in (HWVoiceIn *hw) -{ - if (hw->conv_buf) { - qemu_free (hw->conv_buf); - } - hw->conv_buf = NULL; -} - -static int audio_pcm_hw_alloc_resources_in (HWVoiceIn *hw) -{ - hw->conv_buf = audio_calloc (AUDIO_FUNC, hw->samples, sizeof (st_sample_t)); - if (!hw->conv_buf) { - dolog ("Could not allocate ADC conversion buffer (%d samples)\n", - hw->samples); - return -1; - } - return 0; -} - static int audio_pcm_hw_find_min_in (HWVoiceIn *hw) { SWVoiceIn *sw; @@ -668,64 +649,6 @@ int audio_pcm_hw_get_live_in (HWVoiceIn *hw) /* * Soft voice (capture) */ -static void audio_pcm_sw_free_resources_in (SWVoiceIn *sw) -{ - if (sw->conv_buf) { - qemu_free (sw->conv_buf); - } - - if (sw->rate) { - st_rate_stop (sw->rate); - } - - sw->conv_buf = NULL; - sw->rate = NULL; -} - -static int audio_pcm_sw_alloc_resources_in (SWVoiceIn *sw) -{ - int samples = ((int64_t) sw->hw->samples << 32) / sw->ratio; - sw->conv_buf = audio_calloc (AUDIO_FUNC, samples, sizeof (st_sample_t)); - if (!sw->conv_buf) { - dolog ("Could not allocate buffer for `%s' (%d samples)\n", - SW_NAME (sw), samples); - return -1; - } - - sw->rate = st_rate_start (sw->hw->info.freq, sw->info.freq); - if (!sw->rate) { - qemu_free (sw->conv_buf); - sw->conv_buf = NULL; - return -1; - } - return 0; -} - -static int audio_pcm_sw_init_in ( - SWVoiceIn *sw, - HWVoiceIn *hw, - const char *name, - audsettings_t *as - ) -{ - /* None of the cards emulated by QEMU are big-endian - hence following shortcut */ - audio_pcm_init_info (&sw->info, as, audio_need_to_swap_endian (0)); - sw->hw = hw; - sw->ratio = ((int64_t) sw->info.freq << 32) / sw->hw->info.freq; - - sw->clip = - mixeng_clip - [sw->info.nchannels == 2] - [sw->info.sign] - [sw->info.swap_endian] - [sw->info.bits == 16]; - - sw->name = qemu_strdup (name); - audio_pcm_sw_free_resources_in (sw); - return audio_pcm_sw_alloc_resources_in (sw); -} - static int audio_pcm_sw_get_rpos_in (SWVoiceIn *sw) { HWVoiceIn *hw = sw->hw; @@ -750,7 +673,7 @@ 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->conv_buf; + st_sample_t *src, *dst = sw->buf; rpos = audio_pcm_sw_get_rpos_in (sw) % hw->samples; @@ -794,7 +717,7 @@ int audio_pcm_sw_read (SWVoiceIn *sw, void *buf, int size) total += isamp; } - sw->clip (buf, sw->conv_buf, ret); + sw->clip (buf, sw->buf, ret); sw->total_hw_samples_acquired += total; return ret << sw->info.shift; } @@ -802,27 +725,6 @@ int audio_pcm_sw_read (SWVoiceIn *sw, void *buf, int size) /* * Hard voice (playback) */ -static void audio_pcm_hw_free_resources_out (HWVoiceOut *hw) -{ - if (hw->mix_buf) { - qemu_free (hw->mix_buf); - } - - hw->mix_buf = NULL; -} - -static int audio_pcm_hw_alloc_resources_out (HWVoiceOut *hw) -{ - hw->mix_buf = audio_calloc (AUDIO_FUNC, hw->samples, sizeof (st_sample_t)); - if (!hw->mix_buf) { - dolog ("Could not allocate DAC mixing buffer (%d samples)\n", - hw->samples); - return -1; - } - - return 0; -} - static int audio_pcm_hw_find_min_out (HWVoiceOut *hw, int *nb_livep) { SWVoiceOut *sw; @@ -876,66 +778,6 @@ int audio_pcm_hw_get_live_out (HWVoiceOut *hw) /* * Soft voice (playback) */ -static void audio_pcm_sw_free_resources_out (SWVoiceOut *sw) -{ - if (sw->buf) { - qemu_free (sw->buf); - } - - if (sw->rate) { - st_rate_stop (sw->rate); - } - - sw->buf = NULL; - sw->rate = NULL; -} - -static int audio_pcm_sw_alloc_resources_out (SWVoiceOut *sw) -{ - sw->buf = audio_calloc (AUDIO_FUNC, sw->hw->samples, sizeof (st_sample_t)); - if (!sw->buf) { - dolog ("Could not allocate buffer for `%s' (%d samples)\n", - SW_NAME (sw), sw->hw->samples); - return -1; - } - - sw->rate = st_rate_start (sw->info.freq, sw->hw->info.freq); - if (!sw->rate) { - qemu_free (sw->buf); - sw->buf = NULL; - return -1; - } - return 0; -} - -static int audio_pcm_sw_init_out ( - SWVoiceOut *sw, - HWVoiceOut *hw, - const char *name, - audsettings_t *as - ) -{ - /* None of the cards emulated by QEMU are big-endian - hence following shortcut */ - audio_pcm_init_info (&sw->info, as, audio_need_to_swap_endian (0)); - sw->hw = hw; - sw->empty = 1; - sw->active = 0; - sw->ratio = ((int64_t) sw->hw->info.freq << 32) / sw->info.freq; - sw->total_hw_samples_mixed = 0; - - sw->conv = - mixeng_conv - [sw->info.nchannels == 2] - [sw->info.sign] - [sw->info.swap_endian] - [sw->info.bits == 16]; - sw->name = qemu_strdup (name); - - audio_pcm_sw_free_resources_out (sw); - return audio_pcm_sw_alloc_resources_out (sw); -} - int audio_pcm_sw_write (SWVoiceOut *sw, void *buf, int size) { int hwsamples, samples, isamp, osamp, wpos, live, dead, left, swlim, blck; @@ -1316,6 +1158,16 @@ static void audio_run_in (AudioState *s) } } +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, @@ -1356,13 +1208,31 @@ static struct audio_option audio_options[] = { {"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; @@ -1387,37 +1257,8 @@ void AUD_help (void) printf ("Name: %s\n", d->name); printf ("Description: %s\n", d->descr); - switch (d->max_voices_out) { - case 0: - printf ("Does not support DAC\n"); - break; - case 1: - printf ("One DAC voice\n"); - break; - case INT_MAX: - printf ("Theoretically supports many DAC voices\n"); - break; - default: - printf ("Theoretically supports upto %d DAC voices\n", - d->max_voices_out); - break; - } - - switch (d->max_voices_in) { - case 0: - printf ("Does not support ADC\n"); - break; - case 1: - printf ("One ADC voice\n"); - break; - case INT_MAX: - printf ("Theoretically supports many ADC voices\n"); - break; - default: - printf ("Theoretically supports upto %d ADC voices\n", - d->max_voices_in); - break; - } + audio_pp_nb_voices ("playback", d->max_voices_out); + audio_pp_nb_voices ("capture", d->max_voices_in); if (d->options) { printf ("Options:\n"); @@ -1434,7 +1275,7 @@ void AUD_help (void) "Example:\n" #ifdef _WIN32 " set QEMU_AUDIO_DRV=wav\n" - " set QEMU_WAV_PATH=c:/tune.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" @@ -1444,16 +1285,6 @@ void AUD_help (void) ); } -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 int audio_driver_init (AudioState *s, struct audio_driver *drv) { if (drv->options) { @@ -1462,62 +1293,8 @@ static int audio_driver_init (AudioState *s, struct audio_driver *drv) s->drv_opaque = drv->init (); if (s->drv_opaque) { - if (s->nb_hw_voices_out > drv->max_voices_out) { - if (!drv->max_voices_out) { - dolog ("`%s' does not support DAC\n", drv->name); - } - else { - dolog ( - "`%s' does not support %d multiple DAC voicess\n" - "Resetting to %d\n", - drv->name, - s->nb_hw_voices_out, - drv->max_voices_out - ); - } - s->nb_hw_voices_out = drv->max_voices_out; - } - - - if (!drv->voice_size_in && drv->max_voices_in) { - ldebug ("warning: No ADC voice size defined for `%s'\n", - drv->name); - drv->max_voices_in = 0; - } - - if (!drv->voice_size_out && drv->max_voices_out) { - ldebug ("warning: No DAC voice size defined for `%s'\n", - drv->name); - } - - if (drv->voice_size_in && !drv->max_voices_in) { - ldebug ("warning: `%s' ADC voice size %d, zero voices \n", - drv->name, drv->voice_size_out); - } - - if (drv->voice_size_out && !drv->max_voices_out) { - ldebug ("warning: `%s' DAC voice size %d, zero voices \n", - drv->name, drv->voice_size_in); - } - - if (s->nb_hw_voices_in > drv->max_voices_in) { - if (!drv->max_voices_in) { - ldebug ("`%s' does not support ADC\n", drv->name); - } - else { - dolog ( - "`%s' does not support %d multiple ADC voices\n" - "Resetting to %d\n", - drv->name, - s->nb_hw_voices_in, - drv->max_voices_in - ); - } - s->nb_hw_voices_in = drv->max_voices_in; - } - - LIST_INIT (&s->hw_head_out); - LIST_INIT (&s->hw_head_in); + audio_init_nb_voices_out (s, drv); + audio_init_nb_voices_in (s, drv); s->drv = drv; return 0; } @@ -1549,25 +1326,13 @@ static void audio_atexit (void) HWVoiceOut *hwo = NULL; HWVoiceIn *hwi = NULL; - while ((hwo = audio_pcm_hw_find_any_out (s, hwo))) { - if (!hwo->pcm_ops) { - continue; - } - - if (hwo->enabled) { - hwo->pcm_ops->ctl_out (hwo, VOICE_DISABLE); - } + 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_in (s, hwi))) { - if (!hwi->pcm_ops) { - continue; - } - - if (hwi->enabled) { - hwi->pcm_ops->ctl_in (hwi, VOICE_DISABLE); - } + 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); } @@ -1616,21 +1381,31 @@ AudioState *AUD_init (void) 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 DAC voices %d\n", + 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 ADC voices %d\n", + dolog ("Bogus number of capture voices %d, setting to 0\n", s->nb_hw_voices_in); - s->nb_hw_voices_in = 1; + s->nb_hw_voices_in = 0; } { @@ -1638,12 +1413,6 @@ AudioState *AUD_init (void) drvname = audio_get_conf_str ("QEMU_AUDIO_DRV", NULL, &def); } - s->ts = qemu_new_timer (vm_clock, audio_timer, s); - if (!s->ts) { - dolog ("Could not create audio timer\n"); - return NULL; - } - if (drvname) { int found = 0; @@ -1680,6 +1449,8 @@ AudioState *AUD_init (void) } if (done) { + VMChangeStateEntry *e; + if (conf.period.hz <= 0) { if (conf.period.hz < 0) { dolog ("warning: Timer period is negative - %d " @@ -1692,7 +1463,11 @@ AudioState *AUD_init (void) conf.period.ticks = ticks_per_sec / conf.period.hz; } - qemu_add_vm_change_state_handler (audio_vm_change_state_handler, s); + 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); @@ -1701,7 +1476,6 @@ AudioState *AUD_init (void) LIST_INIT (&s->card_head); register_savevm ("audio", 0, 1, audio_save, audio_load, s); - atexit (audio_atexit); qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + conf.period.ticks); return s; } diff --git a/audio/audio.h b/audio/audio.h index 682d0e0008..169b5f636a 100644 --- a/audio/audio.h +++ b/audio/audio.h @@ -73,7 +73,8 @@ SWVoiceOut *AUD_open_out ( const char *name, void *callback_opaque, audio_callback_fn_t callback_fn, - audsettings_t *settings + audsettings_t *settings, + int sw_endian ); void AUD_close_out (QEMUSoundCard *card, SWVoiceOut *sw); @@ -91,7 +92,8 @@ SWVoiceIn *AUD_open_in ( const char *name, void *callback_opaque, audio_callback_fn_t callback_fn, - audsettings_t *settings + audsettings_t *settings, + int sw_endian ); void AUD_close_in (QEMUSoundCard *card, SWVoiceIn *sw); diff --git a/audio/audio_int.h b/audio/audio_int.h index 8fee7b96fa..ca240ccc7b 100644 --- a/audio/audio_int.h +++ b/audio/audio_int.h @@ -123,7 +123,7 @@ struct SWVoiceIn { int64_t ratio; void *rate; int total_hw_samples_acquired; - st_sample_t *conv_buf; + st_sample_t *buf; f_sample *clip; HWVoiceIn *hw; char *name; diff --git a/audio/audio_template.h b/audio/audio_template.h index be32c68b3b..23d024201a 100644 --- a/audio/audio_template.h +++ b/audio/audio_template.h @@ -23,52 +23,159 @@ */ #ifdef DAC +#define NAME "playback" +#define HWBUF hw->mix_buf #define TYPE out -#define HW glue (HWVoice, Out) -#define SW glue (SWVoice, Out) +#define HW HWVoiceOut +#define SW SWVoiceOut #else +#define NAME "capture" #define TYPE in -#define HW glue (HWVoice, In) -#define SW glue (SWVoice, In) +#define HW HWVoiceIn +#define SW SWVoiceIn +#define HWBUF hw->conv_buf #endif -static int glue (audio_pcm_hw_init_, TYPE) ( - HW *hw, - audsettings_t *as +static void glue (audio_init_nb_voices_, TYPE) ( + AudioState *s, + struct audio_driver *drv ) { - glue (audio_pcm_hw_free_resources_, TYPE) (hw); + int max_voices = glue (drv->max_voices_, TYPE); + int voice_size = glue (drv->voice_size_, TYPE); - if (glue (hw->pcm_ops->init_, TYPE) (hw, as)) { - return -1; - } - - if (audio_bug (AUDIO_FUNC, hw->samples <= 0)) { - dolog ("hw->samples=%d\n", hw->samples); - return -1; - } - - LIST_INIT (&hw->sw_head); + if (glue (s->nb_hw_voices_, TYPE) > max_voices) { + if (!max_voices) { #ifdef DAC - hw->clip = - mixeng_clip -#else - hw->conv = - mixeng_conv + dolog ("Driver `%s' does not support " NAME "\n", drv->name); #endif - [hw->info.nchannels == 2] - [hw->info.sign] - [hw->info.swap_endian] - [hw->info.bits == 16]; + } + else { + dolog ("Driver `%s' does not support %d " NAME " voices, max %d\n", + drv->name, + glue (s->nb_hw_voices_, TYPE), + max_voices); + } + glue (s->nb_hw_voices_, TYPE) = max_voices; + } - if (glue (audio_pcm_hw_alloc_resources_, TYPE) (hw)) { - glue (hw->pcm_ops->fini_, TYPE) (hw); + if (audio_bug (AUDIO_FUNC, !voice_size && max_voices)) { + dolog ("drv=`%s' voice_size=0 max_voices=%d\n", + drv->name, max_voices); + glue (s->nb_hw_voices_, TYPE) = 0; + } + + if (audio_bug (AUDIO_FUNC, voice_size && !max_voices)) { + dolog ("drv=`%s' voice_size=%d max_voices=0\n", + drv->name, voice_size); + } +} + +static void glue (audio_pcm_hw_free_resources_, TYPE) (HW *hw) +{ + if (HWBUF) { + qemu_free (HWBUF); + } + + HWBUF = NULL; +} + +static int glue (audio_pcm_hw_alloc_resources_, TYPE) (HW *hw) +{ + HWBUF = audio_calloc (AUDIO_FUNC, hw->samples, sizeof (st_sample_t)); + if (!HWBUF) { + dolog ("Could not allocate " NAME " buffer (%d samples)\n", + hw->samples); return -1; } return 0; } +static void glue (audio_pcm_sw_free_resources_, TYPE) (SW *sw) +{ + if (sw->buf) { + qemu_free (sw->buf); + } + + if (sw->rate) { + st_rate_stop (sw->rate); + } + + sw->buf = NULL; + sw->rate = NULL; +} + +static int glue (audio_pcm_sw_alloc_resources_, TYPE) (SW *sw) +{ + int samples; + +#ifdef DAC + samples = sw->hw->samples; +#else + samples = ((int64_t) sw->hw->samples << 32) / sw->ratio; +#endif + + sw->buf = audio_calloc (AUDIO_FUNC, samples, sizeof (st_sample_t)); + if (!sw->buf) { + dolog ("Could not allocate buffer for `%s' (%d samples)\n", + SW_NAME (sw), samples); + return -1; + } + +#ifdef DAC + sw->rate = st_rate_start (sw->info.freq, sw->hw->info.freq); +#else + sw->rate = st_rate_start (sw->hw->info.freq, sw->info.freq); +#endif + if (!sw->rate) { + qemu_free (sw->buf); + sw->buf = NULL; + return -1; + } + return 0; +} + +static int glue (audio_pcm_sw_init_, TYPE) ( + SW *sw, + HW *hw, + const char *name, + audsettings_t *as, + int endian + ) +{ + int err; + + audio_pcm_init_info (&sw->info, as, audio_need_to_swap_endian (endian)); + sw->hw = hw; + sw->active = 0; +#ifdef DAC + sw->ratio = ((int64_t) sw->hw->info.freq << 32) / sw->info.freq; + sw->total_hw_samples_mixed = 0; + sw->empty = 1; +#else + sw->ratio = ((int64_t) sw->info.freq << 32) / sw->hw->info.freq; +#endif + +#ifdef DAC + sw->conv = mixeng_conv +#else + sw->clip = mixeng_clip +#endif + [sw->info.nchannels == 2] + [sw->info.sign] + [sw->info.swap_endian] + [sw->info.bits == 16]; + + sw->name = qemu_strdup (name); + err = glue (audio_pcm_sw_alloc_resources_, TYPE) (sw); + if (err) { + qemu_free (sw->name); + sw->name = NULL; + } + return err; +} + static void glue (audio_pcm_sw_fini_, TYPE) (SW *sw) { glue (audio_pcm_sw_free_resources_, TYPE) (sw); @@ -117,31 +224,6 @@ static HW *glue (audio_pcm_hw_find_any_enabled_, TYPE) (AudioState *s, HW *hw) return NULL; } -static HW *glue (audio_pcm_hw_find_any_passive_, TYPE) (AudioState *s) -{ - if (glue (s->nb_hw_voices_, TYPE)) { - struct audio_driver *drv = s->drv; - - if (audio_bug (AUDIO_FUNC, !drv)) { - dolog ("No host audio driver\n"); - return NULL; - } - - HW *hw = audio_calloc (AUDIO_FUNC, 1, glue (drv->voice_size_, TYPE)); - if (!hw) { - dolog ("Can not allocate voice `%s' size %d\n", - drv->name, glue (drv->voice_size_, TYPE)); - return NULL; - } - - LIST_INSERT_HEAD (&s->glue (hw_head_, TYPE), hw, entries); - glue (s->nb_hw_voices_, TYPE) -= 1; - return hw; - } - - return NULL; -} - static HW *glue (audio_pcm_hw_find_specific_, TYPE) ( AudioState *s, HW *hw, @@ -159,23 +241,63 @@ static HW *glue (audio_pcm_hw_find_specific_, TYPE) ( static HW *glue (audio_pcm_hw_add_new_, TYPE) (AudioState *s, audsettings_t *as) { HW *hw; + struct audio_driver *drv = s->drv; - hw = glue (audio_pcm_hw_find_any_passive_, TYPE) (s); - if (hw) { - hw->pcm_ops = s->drv->pcm_ops; - if (!hw->pcm_ops) { - return NULL; - } - - if (glue (audio_pcm_hw_init_, TYPE) (hw, as)) { - glue (audio_pcm_hw_gc_, TYPE) (s, &hw); - return NULL; - } - else { - return hw; - } + if (!glue (s->nb_hw_voices_, TYPE)) { + return NULL; } + if (audio_bug (AUDIO_FUNC, !drv)) { + dolog ("No host audio driver\n"); + return NULL; + } + + if (audio_bug (AUDIO_FUNC, !drv->pcm_ops)) { + dolog ("Host audio driver without pcm_ops\n"); + return NULL; + } + + hw = audio_calloc (AUDIO_FUNC, 1, glue (drv->voice_size_, TYPE)); + if (!hw) { + dolog ("Can not allocate voice `%s' size %d\n", + drv->name, glue (drv->voice_size_, TYPE)); + return NULL; + } + + hw->pcm_ops = drv->pcm_ops; + LIST_INIT (&hw->sw_head); + + if (glue (hw->pcm_ops->init_, TYPE) (hw, as)) { + goto err0; + } + + if (audio_bug (AUDIO_FUNC, hw->samples <= 0)) { + dolog ("hw->samples=%d\n", hw->samples); + goto err1; + } + +#ifdef DAC + hw->clip = mixeng_clip +#else + hw->conv = mixeng_conv +#endif + [hw->info.nchannels == 2] + [hw->info.sign] + [hw->info.swap_endian] + [hw->info.bits == 16]; + + if (glue (audio_pcm_hw_alloc_resources_, TYPE) (hw)) { + goto err1; + } + + LIST_INSERT_HEAD (&s->glue (hw_head_, TYPE), hw, entries); + glue (s->nb_hw_voices_, TYPE) -= 1; + return hw; + + err1: + glue (hw->pcm_ops->fini_, TYPE) (hw); + err0: + qemu_free (hw); return NULL; } @@ -206,7 +328,8 @@ static HW *glue (audio_pcm_hw_add_, TYPE) (AudioState *s, audsettings_t *as) static SW *glue (audio_pcm_create_voice_pair_, TYPE) ( AudioState *s, const char *sw_name, - audsettings_t *as + audsettings_t *as, + int sw_endian ) { SW *sw; @@ -234,7 +357,7 @@ static SW *glue (audio_pcm_create_voice_pair_, TYPE) ( glue (audio_pcm_hw_add_sw_, TYPE) (hw, sw); - if (glue (audio_pcm_sw_init_, TYPE) (sw, hw, sw_name, as)) { + if (glue (audio_pcm_sw_init_, TYPE) (sw, hw, sw_name, as, sw_endian)) { goto err3; } @@ -256,6 +379,7 @@ static void glue (audio_close_, TYPE) (AudioState *s, SW *sw) glue (audio_pcm_hw_gc_, TYPE) (s, &sw->hw); qemu_free (sw); } + void glue (AUD_close_, TYPE) (QEMUSoundCard *card, SW *sw) { if (sw) { @@ -275,7 +399,8 @@ SW *glue (AUD_open_, TYPE) ( const char *name, void *callback_opaque , audio_callback_fn_t callback_fn, - audsettings_t *as + audsettings_t *as, + int sw_endian ) { AudioState *s; @@ -347,15 +472,16 @@ SW *glue (AUD_open_, TYPE) ( goto fail; } - if (glue (audio_pcm_sw_init_, TYPE) (sw, hw, name, as)) { + glue (audio_pcm_sw_fini_, TYPE) (sw); + if (glue (audio_pcm_sw_init_, TYPE) (sw, hw, name, as, sw_endian)) { goto fail; } } else { - sw = glue (audio_pcm_create_voice_pair_, TYPE) (s, name, as); + sw = glue (audio_pcm_create_voice_pair_, TYPE) (s, name, as, sw_endian); if (!sw) { dolog ("Failed to create voice `%s'\n", name); - goto fail; + return NULL; } } @@ -435,3 +561,5 @@ uint64_t glue (AUD_get_elapsed_usec_, TYPE) (SW *sw, QEMUAudioTimeStamp *ts) #undef TYPE #undef HW #undef SW +#undef HWBUF +#undef NAME diff --git a/audio/ossaudio.c b/audio/ossaudio.c index d78e59019d..7d12f9e34a 100644 --- a/audio/ossaudio.c +++ b/audio/ossaudio.c @@ -75,11 +75,11 @@ static void GCC_FMT_ATTR (2, 3) oss_logerr (int err, const char *fmt, ...) { va_list ap; - AUD_vlog (AUDIO_CAP, fmt, ap); - va_start (ap, fmt); - AUD_log (AUDIO_CAP, "Reason: %s\n", strerror (err)); + AUD_vlog (AUDIO_CAP, fmt, ap); va_end (ap); + + AUD_log (AUDIO_CAP, "Reason: %s\n", strerror (err)); } static void GCC_FMT_ATTR (3, 4) oss_logerr2 ( @@ -422,6 +422,8 @@ static int oss_init_out (HWVoiceOut *hw, audsettings_t *as) audfmt_e effective_fmt; audsettings_t obt_as; + oss->fd = -1; + req.fmt = aud_to_ossfmt (as->fmt); req.freq = as->freq; req.nchannels = as->nchannels; @@ -565,6 +567,8 @@ static int oss_init_in (HWVoiceIn *hw, audsettings_t *as) audfmt_e effective_fmt; audsettings_t obt_as; + oss->fd = -1; + req.fmt = aud_to_ossfmt (as->fmt); req.freq = as->freq; req.nchannels = as->nchannels;