audio capture to wab files (malc)
authorbellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162>
Sun, 16 Jul 2006 18:57:03 +0000 (18:57 +0000)
committerbellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162>
Sun, 16 Jul 2006 18:57:03 +0000 (18:57 +0000)
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2059 c046a42c-6fe2-441c-8c8c-71466251a162

Makefile.target
audio/audio.c
audio/audio.h
audio/audio_int.h
audio/audio_template.h
audio/noaudio.c
audio/wavcapture.c
monitor.c

index eadd85c..912a83e 100644 (file)
@@ -321,6 +321,7 @@ endif
 ifdef CONFIG_ADLIB
 SOUND_HW += fmopl.o adlib.o
 endif
+AUDIODRV+= wavcapture.o
 
 # SCSI layer
 VL_OBJS+= scsi-disk.o cdrom.o lsi53c895a.o
index dd86c4d..9be77c0 100644 (file)
@@ -510,7 +510,8 @@ static void audio_print_settings (audsettings_t *as)
         AUD_log (NULL, "invalid(%d)", as->fmt);
         break;
     }
-    AUD_log (NULL, "endianness=");
+
+    AUD_log (NULL, " endianness=");
     switch (as->endianness) {
     case 0:
         AUD_log (NULL, "little");
@@ -525,7 +526,7 @@ static void audio_print_settings (audsettings_t *as)
     AUD_log (NULL, "\n");
 }
 
-static int audio_validate_settigs (audsettings_t *as)
+static int audio_validate_settings (audsettings_t *as)
 {
     int invalid;
 
@@ -654,15 +655,25 @@ static CaptureVoiceOut *audio_pcm_capture_find_specific (
     return NULL;
 }
 
-static void audio_notify_capture (CaptureVoiceOut *cap, int enabled)
+static void audio_notify_capture (CaptureVoiceOut *cap, audcnotification_e cmd)
 {
-    if (cap->hw.enabled != enabled) {
-        struct capture_callback *cb;
+    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;
-        for (cb = cap->cb_head.lh_first; cb; cb = cb->entries.le_next) {
-            cb->ops.state (cb->opaque, enabled);
-        }
+        cmd = enabled ? AUD_CNOTIFY_ENABLE : AUD_CNOTIFY_DISABLE;
+        audio_notify_capture (cap, cmd);
     }
 }
 
@@ -672,29 +683,40 @@ static void audio_recalc_and_notify_capture (CaptureVoiceOut *cap)
     SWVoiceOut *sw;
     int enabled = 0;
 
-    for (sw = hw->sw_cap_head.lh_first; sw; sw = sw->cap_entries.le_next) {
+    for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {
         if (sw->active) {
             enabled = 1;
             break;
         }
     }
-    audio_notify_capture (cap, enabled);
+    audio_capture_maybe_changed (cap, enabled);
 }
 
 static void audio_detach_capture (HWVoiceOut *hw)
 {
-    SWVoiceOut *sw;
+    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;
 
-    for (sw = hw->sw_cap_head.lh_first; sw; sw = sw->cap_entries.le_next) {
         if (sw->rate) {
             st_rate_stop (sw->rate);
             sw->rate = NULL;
         }
 
         LIST_REMOVE (sw, entries);
-        LIST_REMOVE (sw, cap_entries);
-        qemu_free (sw);
-        audio_recalc_and_notify_capture ((CaptureVoiceOut *) sw->hw);
+        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;
     }
 }
 
@@ -704,19 +726,21 @@ static int audio_attach_capture (AudioState *s, HWVoiceOut *hw)
 
     audio_detach_capture (hw);
     for (cap = s->cap_head.lh_first; cap; cap = cap->entries.le_next) {
+        SWVoiceCap *sc;
         SWVoiceOut *sw;
-        HWVoiceOut *hw_cap;
+        HWVoiceOut *hw_cap = &cap->hw;
 
-        hw_cap = &cap->hw;
-        sw = audio_calloc (AUDIO_FUNC, 1, sizeof (*sw));
-        if (!sw) {
+        sc = audio_calloc (AUDIO_FUNC, 1, sizeof (*sc));
+        if (!sc) {
             dolog ("Could not allocate soft capture voice (%zu bytes)\n",
-                   sizeof (*sw));
+                   sizeof (*sc));
             return -1;
         }
 
-        sw->info = hw->info;
+        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;
@@ -728,12 +752,14 @@ static int audio_attach_capture (AudioState *s, HWVoiceOut *hw)
             return -1;
         }
         LIST_INSERT_HEAD (&hw_cap->sw_head, sw, entries);
-        LIST_INSERT_HEAD (&hw->sw_cap_head, sw, cap_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_notify_capture (cap, 1);
-        }
-        else {
-            audio_recalc_and_notify_capture (cap);
+            audio_capture_maybe_changed (cap, 1);
         }
     }
     return 0;
@@ -915,6 +941,9 @@ int audio_pcm_sw_write (SWVoiceOut *sw, void *buf, int size)
     }
 
     if (live == hwsamples) {
+#ifdef DEBUG_OUT
+        dolog ("%s is full %d\n", sw->name, live);
+#endif
         return 0;
     }
 
@@ -1033,6 +1062,7 @@ void AUD_set_active_out (SWVoiceOut *sw, int on)
     hw = sw->hw;
     if (sw->active != on) {
         SWVoiceOut *temp_sw;
+        SWVoiceCap *sc;
 
         if (on) {
             hw->pending_disable = 0;
@@ -1053,11 +1083,11 @@ void AUD_set_active_out (SWVoiceOut *sw, int on)
                 hw->pending_disable = nb_active == 1;
             }
         }
-        for (temp_sw = hw->sw_cap_head.lh_first; temp_sw;
-             temp_sw = temp_sw->entries.le_next) {
-            temp_sw->active = hw->enabled;
+
+        for (sc = hw->cap_head.lh_first; sc; sc = sc->entries.le_next) {
+            sc->sw.active = hw->enabled;
             if (hw->enabled) {
-                audio_notify_capture ((CaptureVoiceOut *) temp_sw->hw, 1);
+                audio_capture_maybe_changed (sc->cap, 1);
             }
         }
         sw->active = on;
@@ -1156,9 +1186,10 @@ static void audio_capture_mix_and_clear (HWVoiceOut *hw, int rpos, int samples)
     int n;
 
     if (hw->enabled) {
-        SWVoiceOut *sw;
+        SWVoiceCap *sc;
 
-        for (sw = hw->sw_cap_head.lh_first; sw; sw = sw->cap_entries.le_next) {
+        for (sc = hw->cap_head.lh_first; sc; sc = sc->entries.le_next) {
+            SWVoiceOut *sw = &sc->sw;
             int rpos2 = rpos;
 
             n = samples;
@@ -1171,8 +1202,9 @@ static void audio_capture_mix_and_clear (HWVoiceOut *hw, int rpos, int samples)
                 sw->buf = hw->mix_buf + rpos2;
                 written = audio_pcm_sw_write (sw, NULL, bytes);
                 if (written - bytes) {
-                    dolog ("Could not mix %d bytes into a capture buffer",
-                           bytes);
+                    dolog ("Could not mix %d bytes into a capture "
+                           "buffer, mixed %d\n",
+                           bytes, written);
                     break;
                 }
                 n -= to_write;
@@ -1206,16 +1238,16 @@ static void audio_run_out (AudioState *s)
         }
 
         if (hw->pending_disable && !nb_live) {
+            SWVoiceCap *sc;
 #ifdef DEBUG_OUT
             dolog ("Disabling voice\n");
 #endif
             hw->enabled = 0;
             hw->pending_disable = 0;
             hw->pcm_ops->ctl_out (hw, VOICE_DISABLE);
-            for (sw = hw->sw_cap_head.lh_first; sw;
-                 sw = sw->cap_entries.le_next) {
-                sw->active = 0;
-                audio_recalc_and_notify_capture ((CaptureVoiceOut *) sw->hw);
+            for (sc = hw->cap_head.lh_first; sc; sc = sc->entries.le_next) {
+                sc->sw.active = 0;
+                audio_recalc_and_notify_capture (sc->cap);
             }
             continue;
         }
@@ -1277,15 +1309,18 @@ static void audio_run_out (AudioState *s)
         }
 
         if (cleanup_required) {
-        restart:
-            for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {
+            SWVoiceOut *sw1;
+
+            sw = hw->sw_head.lh_first;
+            while (sw) {
+                sw1 = 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 */
                 }
+                sw = sw1;
             }
         }
     }
@@ -1537,13 +1572,18 @@ static void audio_atexit (void)
     HWVoiceIn *hwi = NULL;
 
     while ((hwo = audio_pcm_hw_find_any_enabled_out (s, hwo))) {
-        SWVoiceOut *sw;
+        SWVoiceCap *sc;
 
         hwo->pcm_ops->ctl_out (hwo, VOICE_DISABLE);
         hwo->pcm_ops->fini_out (hwo);
 
-        for (sw = hwo->sw_cap_head.lh_first; sw; sw = sw->entries.le_next) {
-            audio_notify_capture ((CaptureVoiceOut *) sw->hw, 0);
+        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);
+            }
         }
     }
 
@@ -1697,7 +1737,7 @@ AudioState *AUD_init (void)
     return s;
 }
 
-int AUD_add_capture (
+CaptureVoiceOut *AUD_add_capture (
     AudioState *s,
     audsettings_t *as,
     struct audio_capture_ops *ops,
@@ -1712,10 +1752,10 @@ int AUD_add_capture (
         s = &glob_audio_state;
     }
 
-    if (audio_validate_settigs (as)) {
+    if (audio_validate_settings (as)) {
         dolog ("Invalid settings were passed when trying to add capture\n");
         audio_print_settings (as);
-        return -1;
+        goto err0;
     }
 
     cb = audio_calloc (AUDIO_FUNC, 1, sizeof (*cb));
@@ -1730,7 +1770,7 @@ int AUD_add_capture (
     cap = audio_pcm_capture_find_specific (s, as);
     if (cap) {
         LIST_INSERT_HEAD (&cap->cb_head, cb, entries);
-        return 0;
+        return cap;
     }
     else {
         HWVoiceOut *hw;
@@ -1780,7 +1820,7 @@ int AUD_add_capture (
         while ((hw = audio_pcm_hw_find_any_out (s, hw))) {
             audio_attach_capture (s, hw);
         }
-        return 0;
+        return cap;
 
     err3:
         qemu_free (cap->hw.mix_buf);
@@ -1789,6 +1829,38 @@ int AUD_add_capture (
     err1:
         qemu_free (cb);
     err0:
-        return -1;
+        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) {
+#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);
+                    sw = sw1;
+                }
+                LIST_REMOVE (cap, entries);
+                qemu_free (cap);
+            }
+            return;
+        }
     }
 }
index 14fa3bc..c097f39 100644 (file)
@@ -49,13 +49,31 @@ typedef struct {
     int endianness;
 } audsettings_t;
 
+typedef enum {
+    AUD_CNOTIFY_ENABLE,
+    AUD_CNOTIFY_DISABLE
+} audcnotification_e;
+
 struct audio_capture_ops {
-    void (*state) (void *opaque, int enabled);
+    void (*notify) (void *opaque, audcnotification_e cmd);
     void (*capture) (void *opaque, void *buf, int size);
+    void (*destroy) (void *opaque);
 };
 
+struct capture_ops {
+    void (*info) (void *opaque);
+    void (*destroy) (void *opaque);
+};
+
+typedef struct CaptureState {
+    void *opaque;
+    struct capture_ops ops;
+    LIST_ENTRY (CaptureState) entries;
+} CaptureState;
+
 typedef struct AudioState AudioState;
 typedef struct SWVoiceOut SWVoiceOut;
+typedef struct CaptureVoiceOut CaptureVoiceOut;
 typedef struct SWVoiceIn SWVoiceIn;
 
 typedef struct QEMUSoundCard {
@@ -79,12 +97,13 @@ AudioState *AUD_init (void);
 void AUD_help (void);
 void AUD_register_card (AudioState *s, const char *name, QEMUSoundCard *card);
 void AUD_remove_card (QEMUSoundCard *card);
-int AUD_add_capture (
+CaptureVoiceOut *AUD_add_capture (
     AudioState *s,
     audsettings_t *as,
     struct audio_capture_ops *ops,
     void *opaque
     );
+void AUD_del_capture (CaptureVoiceOut *cap, void *cb_opaque);
 
 SWVoiceOut *AUD_open_out (
     QEMUSoundCard *card,
index f5dcb2c..1a15d4c 100644 (file)
@@ -64,10 +64,11 @@ struct audio_pcm_info {
     int swap_endianness;
 };
 
+typedef struct SWVoiceCap SWVoiceCap;
+
 typedef struct HWVoiceOut {
     int enabled;
     int pending_disable;
-    int valid;
     struct audio_pcm_info info;
 
     f_sample *clip;
@@ -79,7 +80,7 @@ typedef struct HWVoiceOut {
 
     int samples;
     LIST_HEAD (sw_out_listhead, SWVoiceOut) sw_head;
-    LIST_HEAD (sw_cap_listhead, SWVoiceOut) sw_cap_head;
+    LIST_HEAD (sw_cap_listhead, SWVoiceCap) cap_head;
     struct audio_pcm_ops *pcm_ops;
     LIST_ENTRY (HWVoiceOut) entries;
 } HWVoiceOut;
@@ -116,7 +117,6 @@ struct SWVoiceOut {
     volume_t vol;
     struct audio_callback callback;
     LIST_ENTRY (SWVoiceOut) entries;
-    LIST_ENTRY (SWVoiceOut) cap_entries;
 };
 
 struct SWVoiceIn {
@@ -168,12 +168,18 @@ struct capture_callback {
     LIST_ENTRY (capture_callback) entries;
 };
 
-typedef struct CaptureVoiceOut {
+struct CaptureVoiceOut {
     HWVoiceOut hw;
     void *buf;
     LIST_HEAD (cb_listhead, capture_callback) cb_head;
     LIST_ENTRY (CaptureVoiceOut) entries;
-} CaptureVoiceOut;
+};
+
+struct SWVoiceCap {
+    SWVoiceOut sw;
+    CaptureVoiceOut *cap;
+    LIST_ENTRY (SWVoiceCap) entries;
+};
 
 struct AudioState {
     struct audio_driver *drv;
index 04b47be..13e1c3e 100644 (file)
@@ -269,7 +269,7 @@ static HW *glue (audio_pcm_hw_add_new_, TYPE) (AudioState *s, audsettings_t *as)
     hw->pcm_ops = drv->pcm_ops;
     LIST_INIT (&hw->sw_head);
 #ifdef DAC
-    LIST_INIT (&hw->sw_cap_head);
+    LIST_INIT (&hw->cap_head);
 #endif
     if (glue (hw->pcm_ops->init_, TYPE) (hw, as)) {
         goto err0;
@@ -426,7 +426,7 @@ SW *glue (AUD_open_, TYPE) (
 
     s = card->audio;
 
-    if (audio_bug (AUDIO_FUNC, audio_validate_settigs (as))) {
+    if (audio_bug (AUDIO_FUNC, audio_validate_settings (as))) {
         audio_print_settings (as);
         goto fail;
     }
index 8fb15a2..a3423e5 100644 (file)
@@ -102,7 +102,7 @@ static int no_run_in (HWVoiceIn *hw)
     NoVoiceIn *no = (NoVoiceIn *) hw;
     int live = audio_pcm_hw_get_live_in (hw);
     int dead = hw->samples - live;
-    int samples;
+    int samples = 0;
 
     if (dead) {
         int64_t now = qemu_get_clock (vm_clock);
index 7458a5e..748b580 100644 (file)
@@ -3,6 +3,11 @@
 typedef struct {
     QEMUFile *f;
     int bytes;
+    char *path;
+    int freq;
+    int bits;
+    int nchannels;
+    CaptureVoiceOut *cap;
 } WAVState;
 
 /* VICE code: Store number as little endian. */
@@ -15,35 +20,39 @@ static void le_store (uint8_t *buf, uint32_t val, int len)
     }
 }
 
-static void wav_state_cb (void *opaque, int enabled)
+static void wav_notify (void *opaque, audcnotification_e cmd)
 {
-    WAVState *wav = opaque;
+    (void) opaque;
+    (void) cmd;
+}
 
-    if (!enabled) {
-        uint8_t rlen[4];
-        uint8_t dlen[4];
-        uint32_t datalen = wav->bytes;
-        uint32_t rifflen = datalen + 36;
+static void wav_destroy (void *opaque)
+{
+    WAVState *wav = opaque;
+    uint8_t rlen[4];
+    uint8_t dlen[4];
+    uint32_t datalen = wav->bytes;
+    uint32_t rifflen = datalen + 36;
 
-        if (!wav->f) {
-            return;
-        }
+    if (!wav->f) {
+        return;
+    }
 
-        le_store (rlen, rifflen, 4);
-        le_store (dlen, datalen, 4);
+    le_store (rlen, rifflen, 4);
+    le_store (dlen, datalen, 4);
 
-        qemu_fseek (wav->f, 4, SEEK_SET);
-        qemu_put_buffer (wav->f, rlen, 4);
+    qemu_fseek (wav->f, 4, SEEK_SET);
+    qemu_put_buffer (wav->f, rlen, 4);
 
-        qemu_fseek (wav->f, 32, SEEK_CUR);
-        qemu_put_buffer (wav->f, dlen, 4);
-    }
-    else {
-        qemu_fseek (wav->f, 0, SEEK_END);
+    qemu_fseek (wav->f, 32, SEEK_CUR);
+    qemu_put_buffer (wav->f, dlen, 4);
+    fclose (wav->f);
+    if (wav->path) {
+        qemu_free (wav->path);
     }
 }
 
-static void wav_capture_cb (void *opaque, void *buf, int size)
+static void wav_capture (void *opaque, void *buf, int size)
 {
     WAVState *wav = opaque;
 
@@ -51,7 +60,30 @@ static void wav_capture_cb (void *opaque, void *buf, int size)
     wav->bytes += size;
 }
 
-void wav_capture (const char *path, int freq, int bits16, int stereo)
+static void wav_capture_destroy (void *opaque)
+{
+    WAVState *wav = opaque;
+
+    AUD_del_capture (wav->cap, wav);
+}
+
+static void wav_capture_info (void *opaque)
+{
+    WAVState *wav = opaque;
+    char *path = wav->path;
+
+    term_printf ("Capturing audio(%d,%d,%d) to %s: %d bytes\n",
+                 wav->freq, wav->bits, wav->nchannels,
+                 path ? path : "<not available>", wav->bytes);
+}
+
+static struct capture_ops wav_capture_ops = {
+    .destroy = wav_capture_destroy,
+    .info = wav_capture_info
+};
+
+int wav_start_capture (CaptureState *s, const char *path, int freq,
+                       int bits, int nchannels)
 {
     WAVState *wav;
     uint8_t hdr[] = {
@@ -62,23 +94,35 @@ void wav_capture (const char *path, int freq, int bits16, int stereo)
     };
     audsettings_t as;
     struct audio_capture_ops ops;
-    int shift;
+    int stereo, bits16, shift;
+    CaptureVoiceOut *cap;
+
+    if (bits != 8 && bits != 16) {
+        term_printf ("incorrect bit count %d, must be 8 or 16\n", bits);
+        return -1;
+    }
+
+    if (nchannels != 1 && nchannels != 2) {
+        term_printf ("incorrect channel count %d, must be 1 or 2\n", bits);
+        return -1;
+    }
 
-    stereo = !!stereo;
-    bits16 = !!bits16;
+    stereo = nchannels == 2;
+    bits16 = bits == 16;
 
     as.freq = freq;
     as.nchannels = 1 << stereo;
     as.fmt = bits16 ? AUD_FMT_S16 : AUD_FMT_U8;
     as.endianness = 0;
 
-    ops.state = wav_state_cb;
-    ops.capture = wav_capture_cb;
+    ops.notify = wav_notify;
+    ops.capture = wav_capture;
+    ops.destroy = wav_destroy;
 
     wav = qemu_mallocz (sizeof (*wav));
     if (!wav) {
         AUD_log ("wav", "Could not allocate memory (%zu bytes)", sizeof (*wav));
-        return;
+        return -1;
     }
 
     shift = bits16 + stereo;
@@ -91,12 +135,28 @@ void wav_capture (const char *path, int freq, int bits16, int stereo)
 
     wav->f = fopen (path, "wb");
     if (!wav->f) {
-        AUD_log ("wav", "Failed to open wave file `%s'\nReason: %s\n",
-                 path, strerror (errno));
+        term_printf ("Failed to open wave file `%s'\nReason: %s\n",
+                     path, strerror (errno));
         qemu_free (wav);
-        return;
+        return -1;
     }
 
+    wav->path = qemu_strdup (path);
+    wav->bits = bits;
+    wav->nchannels = nchannels;
+    wav->freq = freq;
+
     qemu_put_buffer (wav->f, hdr, sizeof (hdr));
-    AUD_add_capture (NULL, &as, &ops, wav);
+
+    cap = AUD_add_capture (NULL, &as, &ops, wav);
+    if (!cap) {
+        term_printf ("Failed to add audio capture\n");
+        qemu_free (wav);
+        return -1;
+    }
+
+    wav->cap = cap;
+    s->opaque = wav;
+    s->ops = wav_capture_ops;
+    return 0;
 }
index 763acb7..09a0299 100644 (file)
--- a/monitor.c
+++ b/monitor.c
@@ -1077,6 +1077,64 @@ static void do_info_profile(void)
 }
 #endif
 
+/* Capture support */
+static LIST_HEAD (capture_list_head, CaptureState) capture_head;
+
+static void do_info_capture (void)
+{
+    int i;
+    CaptureState *s;
+
+    for (s = capture_head.lh_first, i = 0; s; s = s->entries.le_next, ++i) {
+        term_printf ("[%d]: ", i);
+        s->ops.info (s->opaque);
+    }
+}
+
+static void do_stop_capture (int n)
+{
+    int i;
+    CaptureState *s;
+
+    for (s = capture_head.lh_first, i = 0; s; s = s->entries.le_next, ++i) {
+        if (i == n) {
+            s->ops.destroy (s->opaque);
+            LIST_REMOVE (s, entries);
+            qemu_free (s);
+            return;
+        }
+    }
+}
+
+#ifdef HAS_AUDIO
+int wav_start_capture (CaptureState *s, const char *path, int freq,
+                       int bits, int nchannels);
+
+static void do_wav_capture (const char *path,
+                            int has_freq, int freq,
+                            int has_bits, int bits,
+                            int has_channels, int nchannels)
+{
+    CaptureState *s;
+
+    s = qemu_mallocz (sizeof (*s));
+    if (!s) {
+        term_printf ("Not enough memory to add wave capture\n");
+        return;
+    }
+
+    freq = has_freq ? freq : 44100;
+    bits = has_bits ? bits : 16;
+    nchannels = has_channels ? nchannels : 2;
+
+    if (wav_start_capture (s, path, freq, bits, nchannels)) {
+        term_printf ("Faied to add wave capture\n");
+        qemu_free (s);
+    }
+    LIST_INSERT_HEAD (&capture_head, s, entries);
+}
+#endif
+
 static term_cmd_t term_cmds[] = {
     { "help|?", "s?", do_help, 
       "[cmd]", "show the help" },
@@ -1133,6 +1191,13 @@ static term_cmd_t term_cmds[] = {
       "dx dy [dz]", "send mouse move events" },
     { "mouse_button", "i", do_mouse_button, 
       "state", "change mouse button state (1=L, 2=M, 4=R)" },
+#ifdef HAS_AUDIO
+    { "wavcapture", "si?i?i?", do_wav_capture,
+      "path [frequency bits channels]",
+      "capture audio to a wave file (default frequency=44100 bits=16 channels=2)" },
+#endif
+     { "stopcapture", "i", do_stop_capture,
+       "capture index", "stop capture" },
     { NULL, NULL, }, 
 };
 
@@ -1171,6 +1236,8 @@ static term_cmd_t info_cmds[] = {
       "", "show host USB devices", },
     { "profile", "", do_info_profile,
       "", "show profiling information", },
+    { "capture", "", do_info_capture,
+      "show capture information" },
     { NULL, NULL, },
 };
 
@@ -2081,6 +2148,9 @@ static void monitor_handle_command(const char *cmdline)
     case 6:
         cmd->handler(args[0], args[1], args[2], args[3], args[4], args[5]);
         break;
+    case 7:
+        cmd->handler(args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
+        break;
     default:
         term_printf("unsupported number of arguments: %d\n", nb_args);
         goto fail;