From 8ead62cfc21f61a32677892c721674e06e9f6153 Mon Sep 17 00:00:00 2001 From: bellard Date: Tue, 4 Jul 2006 16:51:32 +0000 Subject: audio fixes + initial audio capture support (malc) git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2040 c046a42c-6fe2-441c-8c8c-71466251a162 --- audio/alsaaudio.c | 5 +- audio/audio.c | 327 ++++++++++++++++++++++++++++++++++++++++++++++-- audio/audio.h | 14 ++- audio/audio_int.h | 18 ++- audio/audio_template.h | 12 +- audio/coreaudio.c | 2 - audio/dsound_template.h | 14 +-- audio/dsoundaudio.c | 8 +- audio/fmodaudio.c | 2 - audio/noaudio.c | 34 ++--- audio/ossaudio.c | 26 ++-- audio/sdlaudio.c | 1 - audio/wavaudio.c | 1 - audio/wavcapture.c | 101 +++++++++++++++ hw/es1370.c | 3 +- 15 files changed, 514 insertions(+), 54 deletions(-) create mode 100644 audio/wavcapture.c diff --git a/audio/alsaaudio.c b/audio/alsaaudio.c index 30f1e5076f..2cac396b26 100644 --- a/audio/alsaaudio.c +++ b/audio/alsaaudio.c @@ -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; diff --git a/audio/audio.c b/audio/audio.c index f10025bf40..0de728cc5f 100644 --- a/audio/audio.c +++ b/audio/audio.c @@ -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"); @@ -620,6 +621,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) */ @@ -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; + } +} diff --git a/audio/audio.h b/audio/audio.h index 169b5f636a..4e1a694d07 100644 --- a/audio/audio.h +++ b/audio/audio.h @@ -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__ ({ \ diff --git a/audio/audio_int.h b/audio/audio_int.h index ca240ccc7b..c01c16add6 100644 --- a/audio/audio_int.h +++ b/audio/audio_int.h @@ -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; }; diff --git a/audio/audio_template.h b/audio/audio_template.h index 419a4aa463..04b30239db 100644 --- a/audio/audio_template.h +++ b/audio/audio_template.h @@ -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; diff --git a/audio/coreaudio.c b/audio/coreaudio.c index 534fb3ef7c..34e416d93a 100644 --- a/audio/coreaudio.c +++ b/audio/coreaudio.c @@ -275,8 +275,6 @@ static OSStatus audioDeviceIOProc( #endif } - /* cleanup */ - mixeng_clear (src, frameCount); rpos = (rpos + frameCount) % hw->samples; core->decr += frameCount; core->rpos = rpos; diff --git a/audio/dsound_template.h b/audio/dsound_template.h index 38ba5b9ca0..96f7cc7fa1 100644 --- a/audio/dsound_template.h +++ b/audio/dsound_template.h @@ -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)) { diff --git a/audio/dsoundaudio.c b/audio/dsoundaudio.c index 63c5a50573..90a0333f13 100644 --- a/audio/dsoundaudio.c +++ b/audio/dsoundaudio.c @@ -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; } diff --git a/audio/fmodaudio.c b/audio/fmodaudio.c index 072d8a830a..23f267753f 100644 --- a/audio/fmodaudio.c +++ b/audio/fmodaudio.c @@ -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; diff --git a/audio/noaudio.c b/audio/noaudio.c index aa3581168d..314f6177ad 100644 --- a/audio/noaudio.c +++ b/audio/noaudio.c @@ -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; } diff --git a/audio/ossaudio.c b/audio/ossaudio.c index 7d12f9e34a..0bdc8eaab1 100644 --- a/audio/ossaudio.c +++ b/audio/ossaudio.c @@ -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} }; diff --git a/audio/sdlaudio.c b/audio/sdlaudio.c index 713c7849d8..9fe212833f 100644 --- a/audio/sdlaudio.c +++ b/audio/sdlaudio.c @@ -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; diff --git a/audio/wavaudio.c b/audio/wavaudio.c index 18d2bb0c74..ca1e99f4ce 100644 --- a/audio/wavaudio.c +++ b/audio/wavaudio.c @@ -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 index 0000000000..33f04c50b3 --- /dev/null +++ b/audio/wavcapture.c @@ -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); +} diff --git a/hw/es1370.c b/hw/es1370.c index 9fddd9d8b3..2aa2db9eb7 100644 --- a/hw/es1370.c +++ b/hw/es1370.c @@ -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: -- cgit v1.2.3