aboutsummaryrefslogtreecommitdiff
path: root/audio
diff options
context:
space:
mode:
Diffstat (limited to 'audio')
-rw-r--r--audio/audio.c172
-rw-r--r--audio/audio.h23
-rw-r--r--audio/audio_int.h16
-rw-r--r--audio/audio_template.h4
-rw-r--r--audio/noaudio.c2
-rw-r--r--audio/wavcapture.c122
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;
}