OSDN Git Service

audio merge (malc)
authorbellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162>
Sun, 20 Nov 2005 16:24:34 +0000 (16:24 +0000)
committerbellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162>
Sun, 20 Nov 2005 16:24:34 +0000 (16:24 +0000)
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1636 c046a42c-6fe2-441c-8c8c-71466251a162

audio/alsaaudio.c
audio/audio.c
audio/audio.h
audio/audio_int.h
audio/audio_template.h
audio/ossaudio.c

index f7748ca..3ab264e 100644 (file)
@@ -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_ctl_out (HWVoiceOut *hw, int cmd, ...)
+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, ...)
+{
     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,22 +799,36 @@ 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) {
+    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 = (dead - (hw->samples - hw->wpos));
+        bufs[1].len = (decr - (hw->samples - hw->wpos));
     }
     else {
-        bufs[0].len = dead;
+        bufs[0].len = decr;
     }
 
-
     for (i = 0; i < 2; ++i) {
         void *src;
         st_sample_t *dst;
@@ -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}
 };
 
index eba4fdb..7634535 100644 (file)
@@ -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;
 }
index 682d0e0..169b5f6 100644 (file)
@@ -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);
index 8fee7b9..ca240cc 100644 (file)
@@ -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;
index be32c68..23d0242 100644 (file)
  */
 
 #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 (glue (s->nb_hw_voices_, TYPE) > max_voices) {
+        if (!max_voices) {
+#ifdef DAC
+            dolog ("Driver `%s' does not support " NAME "\n", drv->name);
+#endif
+        }
+        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 (audio_bug (AUDIO_FUNC, hw->samples <= 0)) {
-        dolog ("hw->samples=%d\n", hw->samples);
+    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;
     }
 
-    LIST_INIT (&hw->sw_head);
+    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
-    hw->clip =
-        mixeng_clip
+    samples = sw->hw->samples;
 #else
-    hw->conv =
-        mixeng_conv
+    samples = ((int64_t) sw->hw->samples << 32) / sw->ratio;
 #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)) {
-        glue (hw->pcm_ops->fini_, TYPE) (hw);
+    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 (s->nb_hw_voices_, TYPE)) {
+        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 (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
index d78e590..7d12f9e 100644 (file)
@@ -75,11 +75,11 @@ static void GCC_FMT_ATTR (2, 3) oss_logerr (int err, const char *fmt, ...)
 {
     va_list ap;
 
+    va_start (ap, fmt);
     AUD_vlog (AUDIO_CAP, fmt, ap);
+    va_end (ap);
 
-    va_start (ap, fmt);
     AUD_log (AUDIO_CAP, "Reason: %s\n", strerror (err));
-    va_end (ap);
 }
 
 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;