audio fixes + initial audio capture support (malc)
authorbellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162>
Tue, 4 Jul 2006 16:51:32 +0000 (16:51 +0000)
committerbellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162>
Tue, 4 Jul 2006 16:51:32 +0000 (16:51 +0000)
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2040 c046a42c-6fe2-441c-8c8c-71466251a162

15 files changed:
audio/alsaaudio.c
audio/audio.c
audio/audio.h
audio/audio_int.h
audio/audio_template.h
audio/coreaudio.c
audio/dsound_template.h
audio/dsoundaudio.c
audio/fmodaudio.c
audio/noaudio.c
audio/ossaudio.c
audio/sdlaudio.c
audio/wavaudio.c
audio/wavcapture.c [new file with mode: 0644]
hw/es1370.c

index 30f1e50..2cac396 100644 (file)
@@ -61,8 +61,8 @@ static struct {
     .size_in_usec_in = 1,
     .size_in_usec_out = 1,
 #endif
-    .pcm_name_out = "hw:0,0",
-    .pcm_name_in = "hw:0,0",
+    .pcm_name_out = "default",
+    .pcm_name_in = "default",
 #ifdef HIGH_LATENCY
     .buffer_size_in = 400000,
     .period_size_in = 400000 / 4,
@@ -606,7 +606,6 @@ static int alsa_run_out (HWVoiceOut *hw)
                 }
             }
 
-            mixeng_clear (src, written);
             rpos = (rpos + written) % hw->samples;
             samples -= written;
             len -= written;
index f10025b..0de728c 100644 (file)
@@ -29,6 +29,7 @@
 /* #define DEBUG_PLIVE */
 /* #define DEBUG_LIVE */
 /* #define DEBUG_OUT */
+/* #define DEBUG_CAPTURE */
 
 #define SW_NAME(sw) (sw)->name ? (sw)->name : "unknown"
 
@@ -137,7 +138,7 @@ int audio_bug (const char *funcname, int cond)
     if (cond) {
         static int shown;
 
-        AUD_log (NULL, "Error a bug that was just triggered in %s\n", funcname);
+        AUD_log (NULL, "A bug was just triggered in %s\n", funcname);
         if (!shown) {
             shown = 1;
             AUD_log (NULL, "Save all your work and restart without audio\n");
@@ -621,6 +622,121 @@ void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len)
 }
 
 /*
+ * Capture
+ */
+static void noop_conv (st_sample_t *dst, const void *src,
+                       int samples, volume_t *vol)
+{
+    (void) src;
+    (void) dst;
+    (void) samples;
+    (void) vol;
+}
+
+static CaptureVoiceOut *audio_pcm_capture_find_specific (
+    AudioState *s,
+    audsettings_t *as,
+    int endian
+    )
+{
+    CaptureVoiceOut *cap;
+    int swap_endian = audio_need_to_swap_endian (endian);
+
+    for (cap = s->cap_head.lh_first; cap; cap = cap->entries.le_next) {
+        if ((cap->hw.info.swap_endian == swap_endian)
+            && audio_pcm_info_eq (&cap->hw.info, as)) {
+            return cap;
+        }
+    }
+    return NULL;
+}
+
+static void audio_notify_capture (CaptureVoiceOut *cap, int enabled)
+{
+    if (cap->hw.enabled != enabled) {
+        struct capture_callback *cb;
+
+        cap->hw.enabled = enabled;
+        for (cb = cap->cb_head.lh_first; cb; cb = cb->entries.le_next) {
+            cb->ops.state (cb->opaque, enabled);
+        }
+    }
+}
+
+static void audio_recalc_and_notify_capture (CaptureVoiceOut *cap)
+{
+    HWVoiceOut *hw = &cap->hw;
+    SWVoiceOut *sw;
+    int enabled = 0;
+
+    for (sw = hw->sw_cap_head.lh_first; sw; sw = sw->cap_entries.le_next) {
+        if (sw->active) {
+            enabled = 1;
+            break;
+        }
+    }
+    audio_notify_capture (cap, enabled);
+}
+
+static void audio_detach_capture (HWVoiceOut *hw)
+{
+    SWVoiceOut *sw;
+
+    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);
+    }
+}
+
+static int audio_attach_capture (AudioState *s, HWVoiceOut *hw)
+{
+    CaptureVoiceOut *cap;
+
+    audio_detach_capture (hw);
+    for (cap = s->cap_head.lh_first; cap; cap = cap->entries.le_next) {
+        SWVoiceOut *sw;
+        HWVoiceOut *hw_cap;
+
+        hw_cap = &cap->hw;
+        sw = audio_calloc (AUDIO_FUNC, 1, sizeof (*sw));
+        if (!sw) {
+            dolog ("Could not allocate soft capture voice (%zu bytes)\n",
+                   sizeof (*sw));
+            return -1;
+        }
+
+        sw->info = hw->info;
+        sw->hw = hw_cap;
+        sw->empty = 1;
+        sw->active = hw->enabled;
+        sw->conv = noop_conv;
+        sw->ratio = ((int64_t) hw_cap->info.freq << 32) / sw->info.freq;
+        sw->rate = st_rate_start (sw->info.freq, hw_cap->info.freq);
+        if (!sw->rate) {
+            dolog ("Could not start rate conversion for `%s'\n", SW_NAME (sw));
+            qemu_free (sw);
+            return -1;
+        }
+        LIST_INSERT_HEAD (&hw_cap->sw_head, sw, entries);
+        LIST_INSERT_HEAD (&hw->sw_cap_head, sw, cap_entries);
+        if (sw->active) {
+            audio_notify_capture (cap, 1);
+        }
+        else {
+            audio_recalc_and_notify_capture (cap);
+        }
+    }
+    return 0;
+}
+
+/*
  * Hard voice (capture)
  */
 static int audio_pcm_hw_find_min_in (HWVoiceIn *hw)
@@ -916,17 +1032,11 @@ void AUD_set_active_out (SWVoiceOut *sw, int on)
         SWVoiceOut *temp_sw;
 
         if (on) {
-            int total;
-
             hw->pending_disable = 0;
             if (!hw->enabled) {
                 hw->enabled = 1;
                 hw->pcm_ops->ctl_out (hw, VOICE_ENABLE);
             }
-
-            if (sw->empty) {
-                total = 0;
-            }
         }
         else {
             if (hw->enabled) {
@@ -940,6 +1050,13 @@ 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;
+            if (hw->enabled) {
+                audio_notify_capture ((CaptureVoiceOut *) temp_sw->hw, 1);
+            }
+        }
         sw->active = on;
     }
 }
@@ -1031,6 +1148,41 @@ static int audio_get_free (SWVoiceOut *sw)
     return (((int64_t) dead << 32) / sw->ratio) << sw->info.shift;
 }
 
+static void audio_capture_mix_and_clear (HWVoiceOut *hw, int rpos, int samples)
+{
+    int n;
+
+    if (hw->enabled) {
+        SWVoiceOut *sw;
+
+        for (sw = hw->sw_cap_head.lh_first; sw; sw = sw->cap_entries.le_next) {
+            int rpos2 = rpos;
+
+            n = samples;
+            while (n) {
+                int till_end_of_hw = hw->samples - rpos2;
+                int to_write = audio_MIN (till_end_of_hw, n);
+                int bytes = to_write << hw->info.shift;
+                int written;
+
+                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);
+                    break;
+                }
+                n -= to_write;
+                rpos2 = (rpos2 + to_write) % hw->samples;
+            }
+        }
+    }
+
+    n = audio_MIN (samples, hw->samples - rpos);
+    mixeng_clear (hw->mix_buf + rpos, n);
+    mixeng_clear (hw->mix_buf, samples - n);
+}
+
 static void audio_run_out (AudioState *s)
 {
     HWVoiceOut *hw = NULL;
@@ -1038,7 +1190,7 @@ static void audio_run_out (AudioState *s)
 
     while ((hw = audio_pcm_hw_find_any_enabled_out (s, hw))) {
         int played;
-        int live, free, nb_live, cleanup_required;
+        int live, free, nb_live, cleanup_required, prev_rpos;
 
         live = audio_pcm_hw_get_live_out2 (hw, &nb_live);
         if (!nb_live) {
@@ -1057,6 +1209,11 @@ static void audio_run_out (AudioState *s)
             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);
+            }
             continue;
         }
 
@@ -1072,6 +1229,7 @@ static void audio_run_out (AudioState *s)
             continue;
         }
 
+        prev_rpos = hw->rpos;
         played = hw->pcm_ops->run_out (hw);
         if (audio_bug (AUDIO_FUNC, hw->rpos >= hw->samples)) {
             dolog ("hw->rpos=%d hw->samples=%d played=%d\n",
@@ -1085,6 +1243,7 @@ static void audio_run_out (AudioState *s)
 
         if (played) {
             hw->ts_helper += played;
+            audio_capture_mix_and_clear (hw, prev_rpos, played);
         }
 
         cleanup_required = 0;
@@ -1158,12 +1317,60 @@ static void audio_run_in (AudioState *s)
     }
 }
 
+static void audio_run_capture (AudioState *s)
+{
+    CaptureVoiceOut *cap;
+
+    for (cap = s->cap_head.lh_first; cap; cap = cap->entries.le_next) {
+        int live, rpos, captured;
+        HWVoiceOut *hw = &cap->hw;
+        SWVoiceOut *sw;
+
+        captured = live = audio_pcm_hw_get_live_out (hw);
+        rpos = hw->rpos;
+        while (live) {
+            int left = hw->samples - rpos;
+            int to_capture = audio_MIN (live, left);
+            st_sample_t *src;
+            struct capture_callback *cb;
+
+            src = hw->mix_buf + rpos;
+            hw->clip (cap->buf, src, to_capture);
+            mixeng_clear (src, to_capture);
+
+            for (cb = cap->cb_head.lh_first; cb; cb = cb->entries.le_next) {
+                cb->ops.capture (cb->opaque, cap->buf,
+                                 to_capture << hw->info.shift);
+            }
+            rpos = (rpos + to_capture) % hw->samples;
+            live -= to_capture;
+        }
+        hw->rpos = rpos;
+
+        for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {
+            if (!sw->active && sw->empty) {
+                continue;
+            }
+
+            if (audio_bug (AUDIO_FUNC, captured > sw->total_hw_samples_mixed)) {
+                dolog ("captured=%d sw->total_hw_samples_mixed=%d\n",
+                       captured, sw->total_hw_samples_mixed);
+                captured = sw->total_hw_samples_mixed;
+            }
+
+            sw->total_hw_samples_mixed -= captured;
+            sw->empty = sw->total_hw_samples_mixed == 0;
+        }
+    }
+}
+
 static void audio_timer (void *opaque)
 {
     AudioState *s = opaque;
 
     audio_run_out (s);
     audio_run_in (s);
+    audio_run_capture (s);
 
     qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + conf.period.ticks);
 }
@@ -1327,8 +1534,14 @@ static void audio_atexit (void)
     HWVoiceIn *hwi = NULL;
 
     while ((hwo = audio_pcm_hw_find_any_enabled_out (s, hwo))) {
+        SWVoiceOut *sw;
+
         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);
+        }
     }
 
     while ((hwi = audio_pcm_hw_find_any_enabled_in (s, hwi))) {
@@ -1383,6 +1596,7 @@ AudioState *AUD_init (void)
 
     LIST_INIT (&s->hw_head_out);
     LIST_INIT (&s->hw_head_in);
+    LIST_INIT (&s->cap_head);
     atexit (audio_atexit);
 
     s->ts = qemu_new_timer (vm_clock, audio_timer, s);
@@ -1479,3 +1693,100 @@ AudioState *AUD_init (void)
     qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + conf.period.ticks);
     return s;
 }
+
+int AUD_add_capture (
+    AudioState *s,
+    audsettings_t *as,
+    int endian,
+    struct audio_capture_ops *ops,
+    void *cb_opaque
+    )
+{
+    CaptureVoiceOut *cap;
+    struct capture_callback *cb;
+
+    if (!s) {
+        /* XXX suppress */
+        s = &glob_audio_state;
+    }
+
+    if (audio_validate_settigs (as)) {
+        dolog ("Invalid settings were passed when trying to add capture\n");
+        audio_print_settings (as);
+        return -1;
+    }
+
+    cb = audio_calloc (AUDIO_FUNC, 1, sizeof (*cb));
+    if (!cb) {
+        dolog ("Could not allocate capture callback information, size %zu\n",
+               sizeof (*cb));
+        goto err0;
+    }
+    cb->ops = *ops;
+    cb->opaque = cb_opaque;
+
+    cap = audio_pcm_capture_find_specific (s, as, endian);
+    if (cap) {
+        LIST_INSERT_HEAD (&cap->cb_head, cb, entries);
+        return 0;
+    }
+    else {
+        HWVoiceOut *hw;
+        CaptureVoiceOut *cap;
+
+        cap = audio_calloc (AUDIO_FUNC, 1, sizeof (*cap));
+        if (!cap) {
+            dolog ("Could not allocate capture voice, size %zu\n",
+                   sizeof (*cap));
+            goto err1;
+        }
+
+        hw = &cap->hw;
+        LIST_INIT (&hw->sw_head);
+        LIST_INIT (&cap->cb_head);
+
+        /* XXX find a more elegant way */
+        hw->samples = 4096 * 4;
+        hw->mix_buf = audio_calloc (AUDIO_FUNC, hw->samples,
+                                    sizeof (st_sample_t));
+        if (!hw->mix_buf) {
+            dolog ("Could not allocate capture mix buffer (%d samples)\n",
+                   hw->samples);
+            goto err2;
+        }
+
+        audio_pcm_init_info (&hw->info, as, endian);
+
+        cap->buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
+        if (!cap->buf) {
+            dolog ("Could not allocate capture buffer "
+                   "(%d samples, each %d bytes)\n",
+                   hw->samples, 1 << hw->info.shift);
+            goto err3;
+        }
+
+        hw->clip = mixeng_clip
+            [hw->info.nchannels == 2]
+            [hw->info.sign]
+            [hw->info.swap_endian]
+            [hw->info.bits == 16];
+
+        LIST_INSERT_HEAD (&s->cap_head, cap, entries);
+        LIST_INSERT_HEAD (&cap->cb_head, cb, entries);
+
+        hw = NULL;
+        while ((hw = audio_pcm_hw_find_any_out (s, hw))) {
+            audio_attach_capture (s, hw);
+        }
+        return 0;
+
+    err3:
+        qemu_free (cap->hw.mix_buf);
+    err2:
+        qemu_free (cap);
+    err1:
+        qemu_free (cb);
+    err0:
+        return -1;
+    }
+}
index 169b5f6..4e1a694 100644 (file)
@@ -41,6 +41,11 @@ typedef struct {
     audfmt_e fmt;
 } audsettings_t;
 
+struct audio_capture_ops {
+    void (*state) (void *opaque, int enabled);
+    void (*capture) (void *opaque, void *buf, int size);
+};
+
 typedef struct AudioState AudioState;
 typedef struct SWVoiceOut SWVoiceOut;
 typedef struct SWVoiceIn SWVoiceIn;
@@ -66,6 +71,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 (
+    AudioState *s,
+    audsettings_t *as,
+    int endian,
+    struct audio_capture_ops *ops,
+    void *opaque
+    );
 
 SWVoiceOut *AUD_open_out (
     QEMUSoundCard *card,
@@ -111,7 +123,7 @@ static inline void *advance (void *p, int incr)
 }
 
 uint32_t popcount (uint32_t u);
-inline uint32_t lsbindex (uint32_t u);
+uint32_t lsbindex (uint32_t u);
 
 #ifdef __GNUC__
 #define audio_MIN(a, b) ( __extension__ ({      \
index ca240cc..c01c16a 100644 (file)
@@ -79,6 +79,7 @@ typedef struct HWVoiceOut {
 
     int samples;
     LIST_HEAD (sw_out_listhead, SWVoiceOut) sw_head;
+    LIST_HEAD (sw_cap_listhead, SWVoiceOut) sw_cap_head;
     struct audio_pcm_ops *pcm_ops;
     LIST_ENTRY (HWVoiceOut) entries;
 } HWVoiceOut;
@@ -115,6 +116,7 @@ struct SWVoiceOut {
     volume_t vol;
     struct audio_callback callback;
     LIST_ENTRY (SWVoiceOut) entries;
+    LIST_ENTRY (SWVoiceOut) cap_entries;
 };
 
 struct SWVoiceIn {
@@ -160,14 +162,28 @@ struct audio_pcm_ops {
     int  (*ctl_in)  (HWVoiceIn *hw, int cmd, ...);
 };
 
+struct capture_callback {
+    struct audio_capture_ops ops;
+    void *opaque;
+    LIST_ENTRY (capture_callback) entries;
+};
+
+typedef struct CaptureVoiceOut {
+    HWVoiceOut hw;
+    void *buf;
+    LIST_HEAD (cb_listhead, capture_callback) cb_head;
+    LIST_ENTRY (CaptureVoiceOut) entries;
+} CaptureVoiceOut;
+
 struct AudioState {
     struct audio_driver *drv;
     void *drv_opaque;
 
     QEMUTimer *ts;
-    LIST_HEAD (card_head, QEMUSoundCard) card_head;
+    LIST_HEAD (card_listhead, QEMUSoundCard) card_head;
     LIST_HEAD (hw_in_listhead, HWVoiceIn) hw_head_in;
     LIST_HEAD (hw_out_listhead, HWVoiceOut) hw_head_out;
+    LIST_HEAD (cap_listhead, CaptureVoiceOut) cap_head;
     int nb_hw_voices_out;
     int nb_hw_voices_in;
 };
index 419a4aa..04b3023 100644 (file)
@@ -200,6 +200,9 @@ static void glue (audio_pcm_hw_gc_, TYPE) (AudioState *s, HW **hwp)
     HW *hw = *hwp;
 
     if (!hw->sw_head.lh_first) {
+#ifdef DAC
+        audio_detach_capture (hw);
+#endif
         LIST_REMOVE (hw, entries);
         glue (s->nb_hw_voices_, TYPE) += 1;
         glue (audio_pcm_hw_free_resources_ ,TYPE) (hw);
@@ -266,7 +269,9 @@ 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);
+#endif
     if (glue (hw->pcm_ops->init_, TYPE) (hw, as)) {
         goto err0;
     }
@@ -292,6 +297,9 @@ static HW *glue (audio_pcm_hw_add_new_, TYPE) (AudioState *s, audsettings_t *as)
 
     LIST_INSERT_HEAD (&s->glue (hw_head_, TYPE), hw, entries);
     glue (s->nb_hw_voices_, TYPE) -= 1;
+#ifdef DAC
+    audio_attach_capture (s, hw);
+#endif
     return hw;
 
  err1:
@@ -542,7 +550,7 @@ uint64_t glue (AUD_get_elapsed_usec_, TYPE) (SW *sw, QEMUAudioTimeStamp *ts)
 
     cur_ts = sw->hw->ts_helper;
     old_ts = ts->old_ts;
-    /* dolog ("cur %" PRId64 " old %" PRId64 "\n", cur_ts, old_ts); */
+    /* dolog ("cur %lld old %lld\n", cur_ts, old_ts); */
 
     if (cur_ts >= old_ts) {
         delta = cur_ts - old_ts;
index 534fb3e..34e416d 100644 (file)
@@ -275,8 +275,6 @@ static OSStatus audioDeviceIOProc(
 #endif
     }
 
-    /* cleanup */
-    mixeng_clear (src, frameCount);
     rpos = (rpos + frameCount) % hw->samples;
     core->decr += frameCount;
     core->rpos = rpos;
index 38ba5b9..96f7cc7 100644 (file)
@@ -70,7 +70,13 @@ static int glue (dsound_lock_, TYPE) (
     int i;
     LPVOID p1 = NULL, p2 = NULL;
     DWORD blen1 = 0, blen2 = 0;
+    DWORD flag;
 
+#ifdef DSBTYPE_IN
+    flag = entire ? DSCBLOCK_ENTIREBUFFER : 0;
+#else
+    flag = entire ? DSBLOCK_ENTIREBUFFER : 0;
+#endif
     for (i = 0; i < conf.lock_retries; ++i) {
         hr = glue (IFACE, _Lock) (
             buf,
@@ -80,13 +86,7 @@ static int glue (dsound_lock_, TYPE) (
             &blen1,
             &p2,
             &blen2,
-            (entire
-#ifdef DSBTYPE_IN
-             ? DSCBLOCK_ENTIREBUFFER
-#else
-             ? DSBLOCK_ENTIREBUFFER
-#endif
-             : 0)
+            flag
             );
 
         if (FAILED (hr)) {
index 63c5a50..90a0333 100644 (file)
@@ -453,13 +453,11 @@ static void dsound_write_sample (HWVoiceOut *hw, uint8_t *dst, int dst_len)
 
     if (src_len1) {
         hw->clip (dst, src1, src_len1);
-        mixeng_clear (src1, src_len1);
     }
 
     if (src_len2) {
         dst = advance (dst, src_len1 << hw->info.shift);
         hw->clip (dst, src2, src_len2);
-        mixeng_clear (src2, src_len2);
     }
 
     hw->rpos = pos % hw->samples;
@@ -987,6 +985,12 @@ static void *dsound_audio_init (void)
     hr = IDirectSound_Initialize (s->dsound, NULL);
     if (FAILED (hr)) {
         dsound_logerr (hr, "Could not initialize DirectSound\n");
+
+        hr = IDirectSound_Release (s->dsound);
+        if (FAILED (hr)) {
+            dsound_logerr (hr, "Could not release DirectSound\n");
+        }
+        s->dsound = NULL;
         return NULL;
     }
 
index 072d8a8..23f2677 100644 (file)
@@ -153,13 +153,11 @@ static void fmod_write_sample (HWVoiceOut *hw, uint8_t *dst, int dst_len)
 
     if (src_len1) {
         hw->clip (dst, src1, src_len1);
-        mixeng_clear (src1, src_len1);
     }
 
     if (src_len2) {
         dst = advance (dst, src_len1 << hw->info.shift);
         hw->clip (dst, src2, src_len2);
-        mixeng_clear (src2, src_len2);
     }
 
     hw->rpos = pos % hw->samples;
index aa35811..314f617 100644 (file)
@@ -40,22 +40,21 @@ static int no_run_out (HWVoiceOut *hw)
 {
     NoVoiceOut *no = (NoVoiceOut *) hw;
     int live, decr, samples;
-    int64_t now = qemu_get_clock (vm_clock);
-    int64_t ticks = now - no->old_ticks;
-    int64_t bytes = (ticks * hw->info.bytes_per_second) / ticks_per_sec;
-
-    if (bytes > INT_MAX) {
-        samples = INT_MAX >> hw->info.shift;
-    }
-    else {
-        samples = bytes >> hw->info.shift;
-    }
+    int64_t now;
+    int64_t ticks;
+    int64_t bytes;
 
     live = audio_pcm_hw_get_live_out (&no->hw);
     if (!live) {
         return 0;
     }
 
+    now = qemu_get_clock (vm_clock);
+    ticks = now - no->old_ticks;
+    bytes = (ticks * hw->info.bytes_per_second) / ticks_per_sec;
+    bytes = audio_MIN (bytes, INT_MAX);
+    samples = bytes >> hw->info.shift;
+
     no->old_ticks = now;
     decr = audio_MIN (live, samples);
     hw->rpos = (hw->rpos + decr) % hw->samples;
@@ -101,17 +100,20 @@ static void no_fini_in (HWVoiceIn *hw)
 static int no_run_in (HWVoiceIn *hw)
 {
     NoVoiceIn *no = (NoVoiceIn *) hw;
-    int64_t now = qemu_get_clock (vm_clock);
-    int64_t ticks = now - no->old_ticks;
-    int64_t bytes = (ticks * hw->info.bytes_per_second) / ticks_per_sec;
     int live = audio_pcm_hw_get_live_in (hw);
     int dead = hw->samples - live;
     int samples;
 
-    bytes = audio_MIN (bytes, INT_MAX);
-    samples = bytes >> hw->info.shift;
-    samples = audio_MIN (samples, dead);
+    if (dead) {
+        int64_t now = qemu_get_clock (vm_clock);
+        int64_t ticks = now - no->old_ticks;
+        int64_t bytes = (ticks * hw->info.bytes_per_second) / ticks_per_sec;
 
+        no->old_ticks = now;
+        bytes = audio_MIN (bytes, INT_MAX);
+        samples = bytes >> hw->info.shift;
+        samples = audio_MIN (samples, dead);
+    }
     return samples;
 }
 
index 7d12f9e..0bdc8ea 100644 (file)
@@ -55,12 +55,14 @@ static struct {
     int fragsize;
     const char *devpath_out;
     const char *devpath_in;
+    int debug;
 } conf = {
     .try_mmap = 0,
     .nfrags = 4,
     .fragsize = 4096,
     .devpath_out = "/dev/dsp",
-    .devpath_in = "/dev/dsp"
+    .devpath_in = "/dev/dsp",
+    .debug = 0
 };
 
 struct oss_params {
@@ -324,9 +326,20 @@ static int oss_run_out (HWVoiceOut *hw)
             return 0;
         }
 
-        if (abinfo.bytes < 0 || abinfo.bytes > bufsize) {
-            ldebug ("warning: Invalid available size, size=%d bufsize=%d\n",
-                    abinfo.bytes, bufsize);
+        if (abinfo.bytes > bufsize) {
+            if (conf.debug) {
+                dolog ("warning: Invalid available size, size=%d bufsize=%d\n"
+                       "please report your OS/audio hw to malc@pulsesoft.com\n",
+                       abinfo.bytes, bufsize);
+            }
+            abinfo.bytes = bufsize;
+        }
+
+        if (abinfo.bytes < 0) {
+            if (conf.debug) {
+                dolog ("warning: Invalid available size, size=%d bufsize=%d\n",
+                       abinfo.bytes, bufsize);
+            }
             return 0;
         }
 
@@ -369,15 +382,12 @@ static int oss_run_out (HWVoiceOut *hw)
                            "alignment %d\n",
                            wbytes, written, hw->info.align + 1);
                 }
-                mixeng_clear (src, wsamples);
                 decr -= wsamples;
                 rpos = (rpos + wsamples) % hw->samples;
                 break;
             }
         }
 
-        mixeng_clear (src, convert_samples);
-
         rpos = (rpos + convert_samples) % hw->samples;
         samples -= convert_samples;
     }
@@ -730,6 +740,8 @@ static struct audio_option oss_options[] = {
      "Path to DAC device", NULL, 0},
     {"ADC_DEV", AUD_OPT_STR, &conf.devpath_in,
      "Path to ADC device", NULL, 0},
+    {"DEBUG", AUD_OPT_BOOL, &conf.debug,
+     "Turn on some debugging messages", NULL, 0},
     {NULL, 0, NULL, NULL, NULL, 0}
 };
 
index 713c784..9fe2128 100644 (file)
@@ -240,7 +240,6 @@ static void sdl_callback (void *opaque, Uint8 *buf, int len)
 
             /* dolog ("in callback to_mix %d, chunk %d\n", to_mix, chunk); */
             hw->clip (buf, src, chunk);
-            mixeng_clear (src, chunk);
             sdl->rpos = (sdl->rpos + chunk) % hw->samples;
             to_mix -= chunk;
             buf += chunk << hw->info.shift;
index 18d2bb0..ca1e99f 100644 (file)
@@ -81,7 +81,6 @@ static int wav_run_out (HWVoiceOut *hw)
 
         hw->clip (dst, src, convert_samples);
         qemu_put_buffer (wav->f, dst, convert_samples << hw->info.shift);
-        mixeng_clear (src, convert_samples);
 
         rpos = (rpos + convert_samples) % hw->samples;
         samples -= convert_samples;
diff --git a/audio/wavcapture.c b/audio/wavcapture.c
new file mode 100644 (file)
index 0000000..33f04c5
--- /dev/null
@@ -0,0 +1,101 @@
+#include "vl.h"
+
+typedef struct {
+    QEMUFile *f;
+    int bytes;
+} WAVState;
+
+/* VICE code: Store number as little endian. */
+static void le_store (uint8_t *buf, uint32_t val, int len)
+{
+    int i;
+    for (i = 0; i < len; i++) {
+        buf[i] = (uint8_t) (val & 0xff);
+        val >>= 8;
+    }
+}
+
+static void wav_state_cb (void *opaque, int enabled)
+{
+    WAVState *wav = opaque;
+
+    if (!enabled) {
+        uint8_t rlen[4];
+        uint8_t dlen[4];
+        uint32_t datalen = wav->bytes;
+        uint32_t rifflen = datalen + 36;
+
+        if (!wav->f) {
+            return;
+        }
+
+        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, 32, SEEK_CUR);
+        qemu_put_buffer (wav->f, dlen, 4);
+    }
+    else {
+        qemu_fseek (wav->f, 0, SEEK_END);
+    }
+}
+
+static void wav_capture_cb (void *opaque, void *buf, int size)
+{
+    WAVState *wav = opaque;
+
+    qemu_put_buffer (wav->f, buf, size);
+    wav->bytes += size;
+}
+
+void wav_capture (const char *path, int freq, int bits16, int stereo)
+{
+    WAVState *wav;
+    uint8_t hdr[] = {
+        0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56,
+        0x45, 0x66, 0x6d, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00,
+        0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04,
+        0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00
+    };
+    audsettings_t as;
+    struct audio_capture_ops ops;
+    int shift;
+
+    stereo = !!stereo;
+    bits16 = !!bits16;
+
+    as.freq = freq;
+    as.nchannels = 1 << stereo;
+    as.fmt = bits16 ? AUD_FMT_S16 : AUD_FMT_U8;
+
+    ops.state = wav_state_cb;
+    ops.capture = wav_capture_cb;
+
+    wav = qemu_mallocz (sizeof (*wav));
+    if (!wav) {
+        AUD_log ("wav", "Could not allocate memory (%zu bytes)", sizeof (*wav));
+        return;
+    }
+
+    shift = bits16 + stereo;
+    hdr[34] = bits16 ? 0x10 : 0x08;
+
+    le_store (hdr + 22, as.nchannels, 2);
+    le_store (hdr + 24, freq, 4);
+    le_store (hdr + 28, freq << shift, 4);
+    le_store (hdr + 32, 1 << shift, 2);
+
+    wav->f = fopen (path, "wb");
+    if (!wav->f) {
+        AUD_log ("wav", "Failed to open wave file `%s'\nReason: %s\n",
+                 path, strerror (errno));
+        qemu_free (wav);
+        return;
+    }
+
+    qemu_put_buffer (wav->f, hdr, sizeof (hdr));
+    AUD_add_capture (NULL, &as, 0, &ops, wav);
+}
index 9fddd9d..2aa2db9 100644 (file)
@@ -479,9 +479,10 @@ static inline uint32_t es1370_fixup (ES1370State *s, uint32_t addr)
 IO_WRITE_PROTO (es1370_writeb)
 {
     ES1370State *s = opaque;
-    addr = es1370_fixup (s, addr);
     uint32_t shift, mask;
 
+    addr = es1370_fixup (s, addr);
+
     switch (addr) {
     case ES1370_REG_CONTROL:
     case ES1370_REG_CONTROL + 1: