diff options
Diffstat (limited to 'audio')
-rw-r--r-- | audio/audio.c | 172 | ||||
-rw-r--r-- | audio/audio.h | 23 | ||||
-rw-r--r-- | audio/audio_int.h | 16 | ||||
-rw-r--r-- | audio/audio_template.h | 4 | ||||
-rw-r--r-- | audio/noaudio.c | 2 | ||||
-rw-r--r-- | audio/wavcapture.c | 122 |
6 files changed, 248 insertions, 91 deletions
diff --git a/audio/audio.c b/audio/audio.c index dd86c4d7a7..9be77c09d0 100644 --- a/audio/audio.c +++ b/audio/audio.c @@ -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; + } } } diff --git a/audio/audio.h b/audio/audio.h index 14fa3bce96..c097f391bb 100644 --- a/audio/audio.h +++ b/audio/audio.h @@ -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, diff --git a/audio/audio_int.h b/audio/audio_int.h index f5dcb2c705..1a15d4ced8 100644 --- a/audio/audio_int.h +++ b/audio/audio_int.h @@ -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; diff --git a/audio/audio_template.h b/audio/audio_template.h index 04b47befee..13e1c3efbb 100644 --- a/audio/audio_template.h +++ b/audio/audio_template.h @@ -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; } diff --git a/audio/noaudio.c b/audio/noaudio.c index 8fb15a224d..a3423e5eb1 100644 --- a/audio/noaudio.c +++ b/audio/noaudio.c @@ -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); diff --git a/audio/wavcapture.c b/audio/wavcapture.c index 7458a5e3e6..748b58006e 100644 --- a/audio/wavcapture.c +++ b/audio/wavcapture.c @@ -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; } |