提交 ec36b695 编写于 作者: B bellard

audio capture to wab files (malc)


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2059 c046a42c-6fe2-441c-8c8c-71466251a162
上级 63301264
...@@ -321,6 +321,7 @@ endif ...@@ -321,6 +321,7 @@ endif
ifdef CONFIG_ADLIB ifdef CONFIG_ADLIB
SOUND_HW += fmopl.o adlib.o SOUND_HW += fmopl.o adlib.o
endif endif
AUDIODRV+= wavcapture.o
# SCSI layer # SCSI layer
VL_OBJS+= scsi-disk.o cdrom.o lsi53c895a.o VL_OBJS+= scsi-disk.o cdrom.o lsi53c895a.o
......
...@@ -510,7 +510,8 @@ static void audio_print_settings (audsettings_t *as) ...@@ -510,7 +510,8 @@ static void audio_print_settings (audsettings_t *as)
AUD_log (NULL, "invalid(%d)", as->fmt); AUD_log (NULL, "invalid(%d)", as->fmt);
break; break;
} }
AUD_log (NULL, "endianness=");
AUD_log (NULL, " endianness=");
switch (as->endianness) { switch (as->endianness) {
case 0: case 0:
AUD_log (NULL, "little"); AUD_log (NULL, "little");
...@@ -525,7 +526,7 @@ static void audio_print_settings (audsettings_t *as) ...@@ -525,7 +526,7 @@ static void audio_print_settings (audsettings_t *as)
AUD_log (NULL, "\n"); AUD_log (NULL, "\n");
} }
static int audio_validate_settigs (audsettings_t *as) static int audio_validate_settings (audsettings_t *as)
{ {
int invalid; int invalid;
...@@ -654,15 +655,25 @@ static CaptureVoiceOut *audio_pcm_capture_find_specific ( ...@@ -654,15 +655,25 @@ static CaptureVoiceOut *audio_pcm_capture_find_specific (
return NULL; 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; cap->hw.enabled = enabled;
for (cb = cap->cb_head.lh_first; cb; cb = cb->entries.le_next) { cmd = enabled ? AUD_CNOTIFY_ENABLE : AUD_CNOTIFY_DISABLE;
cb->ops.state (cb->opaque, enabled); audio_notify_capture (cap, cmd);
}
} }
} }
...@@ -672,29 +683,40 @@ static void audio_recalc_and_notify_capture (CaptureVoiceOut *cap) ...@@ -672,29 +683,40 @@ static void audio_recalc_and_notify_capture (CaptureVoiceOut *cap)
SWVoiceOut *sw; SWVoiceOut *sw;
int enabled = 0; 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) { if (sw->active) {
enabled = 1; enabled = 1;
break; break;
} }
} }
audio_notify_capture (cap, enabled); audio_capture_maybe_changed (cap, enabled);
} }
static void audio_detach_capture (HWVoiceOut *hw) 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) { if (sw->rate) {
st_rate_stop (sw->rate); st_rate_stop (sw->rate);
sw->rate = NULL; sw->rate = NULL;
} }
LIST_REMOVE (sw, entries); LIST_REMOVE (sw, entries);
LIST_REMOVE (sw, cap_entries); LIST_REMOVE (sc, entries);
qemu_free (sw); qemu_free (sc);
audio_recalc_and_notify_capture ((CaptureVoiceOut *) sw->hw); 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) ...@@ -704,19 +726,21 @@ static int audio_attach_capture (AudioState *s, HWVoiceOut *hw)
audio_detach_capture (hw); audio_detach_capture (hw);
for (cap = s->cap_head.lh_first; cap; cap = cap->entries.le_next) { for (cap = s->cap_head.lh_first; cap; cap = cap->entries.le_next) {
SWVoiceCap *sc;
SWVoiceOut *sw; SWVoiceOut *sw;
HWVoiceOut *hw_cap; HWVoiceOut *hw_cap = &cap->hw;
hw_cap = &cap->hw; sc = audio_calloc (AUDIO_FUNC, 1, sizeof (*sc));
sw = audio_calloc (AUDIO_FUNC, 1, sizeof (*sw)); if (!sc) {
if (!sw) {
dolog ("Could not allocate soft capture voice (%zu bytes)\n", dolog ("Could not allocate soft capture voice (%zu bytes)\n",
sizeof (*sw)); sizeof (*sc));
return -1; return -1;
} }
sw->info = hw->info; sc->cap = cap;
sw = &sc->sw;
sw->hw = hw_cap; sw->hw = hw_cap;
sw->info = hw->info;
sw->empty = 1; sw->empty = 1;
sw->active = hw->enabled; sw->active = hw->enabled;
sw->conv = noop_conv; sw->conv = noop_conv;
...@@ -728,12 +752,14 @@ static int audio_attach_capture (AudioState *s, HWVoiceOut *hw) ...@@ -728,12 +752,14 @@ static int audio_attach_capture (AudioState *s, HWVoiceOut *hw)
return -1; return -1;
} }
LIST_INSERT_HEAD (&hw_cap->sw_head, sw, entries); 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) { if (sw->active) {
audio_notify_capture (cap, 1); audio_capture_maybe_changed (cap, 1);
}
else {
audio_recalc_and_notify_capture (cap);
} }
} }
return 0; return 0;
...@@ -915,6 +941,9 @@ int audio_pcm_sw_write (SWVoiceOut *sw, void *buf, int size) ...@@ -915,6 +941,9 @@ int audio_pcm_sw_write (SWVoiceOut *sw, void *buf, int size)
} }
if (live == hwsamples) { if (live == hwsamples) {
#ifdef DEBUG_OUT
dolog ("%s is full %d\n", sw->name, live);
#endif
return 0; return 0;
} }
...@@ -1033,6 +1062,7 @@ void AUD_set_active_out (SWVoiceOut *sw, int on) ...@@ -1033,6 +1062,7 @@ void AUD_set_active_out (SWVoiceOut *sw, int on)
hw = sw->hw; hw = sw->hw;
if (sw->active != on) { if (sw->active != on) {
SWVoiceOut *temp_sw; SWVoiceOut *temp_sw;
SWVoiceCap *sc;
if (on) { if (on) {
hw->pending_disable = 0; hw->pending_disable = 0;
...@@ -1053,11 +1083,11 @@ void AUD_set_active_out (SWVoiceOut *sw, int on) ...@@ -1053,11 +1083,11 @@ void AUD_set_active_out (SWVoiceOut *sw, int on)
hw->pending_disable = nb_active == 1; hw->pending_disable = nb_active == 1;
} }
} }
for (temp_sw = hw->sw_cap_head.lh_first; temp_sw;
temp_sw = temp_sw->entries.le_next) { for (sc = hw->cap_head.lh_first; sc; sc = sc->entries.le_next) {
temp_sw->active = hw->enabled; sc->sw.active = hw->enabled;
if (hw->enabled) { if (hw->enabled) {
audio_notify_capture ((CaptureVoiceOut *) temp_sw->hw, 1); audio_capture_maybe_changed (sc->cap, 1);
} }
} }
sw->active = on; sw->active = on;
...@@ -1156,9 +1186,10 @@ static void audio_capture_mix_and_clear (HWVoiceOut *hw, int rpos, int samples) ...@@ -1156,9 +1186,10 @@ static void audio_capture_mix_and_clear (HWVoiceOut *hw, int rpos, int samples)
int n; int n;
if (hw->enabled) { 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; int rpos2 = rpos;
n = samples; n = samples;
...@@ -1171,8 +1202,9 @@ static void audio_capture_mix_and_clear (HWVoiceOut *hw, int rpos, int samples) ...@@ -1171,8 +1202,9 @@ static void audio_capture_mix_and_clear (HWVoiceOut *hw, int rpos, int samples)
sw->buf = hw->mix_buf + rpos2; sw->buf = hw->mix_buf + rpos2;
written = audio_pcm_sw_write (sw, NULL, bytes); written = audio_pcm_sw_write (sw, NULL, bytes);
if (written - bytes) { if (written - bytes) {
dolog ("Could not mix %d bytes into a capture buffer", dolog ("Could not mix %d bytes into a capture "
bytes); "buffer, mixed %d\n",
bytes, written);
break; break;
} }
n -= to_write; n -= to_write;
...@@ -1206,16 +1238,16 @@ static void audio_run_out (AudioState *s) ...@@ -1206,16 +1238,16 @@ static void audio_run_out (AudioState *s)
} }
if (hw->pending_disable && !nb_live) { if (hw->pending_disable && !nb_live) {
SWVoiceCap *sc;
#ifdef DEBUG_OUT #ifdef DEBUG_OUT
dolog ("Disabling voice\n"); dolog ("Disabling voice\n");
#endif #endif
hw->enabled = 0; hw->enabled = 0;
hw->pending_disable = 0; hw->pending_disable = 0;
hw->pcm_ops->ctl_out (hw, VOICE_DISABLE); hw->pcm_ops->ctl_out (hw, VOICE_DISABLE);
for (sw = hw->sw_cap_head.lh_first; sw; for (sc = hw->cap_head.lh_first; sc; sc = sc->entries.le_next) {
sw = sw->cap_entries.le_next) { sc->sw.active = 0;
sw->active = 0; audio_recalc_and_notify_capture (sc->cap);
audio_recalc_and_notify_capture ((CaptureVoiceOut *) sw->hw);
} }
continue; continue;
} }
...@@ -1277,15 +1309,18 @@ static void audio_run_out (AudioState *s) ...@@ -1277,15 +1309,18 @@ static void audio_run_out (AudioState *s)
} }
if (cleanup_required) { if (cleanup_required) {
restart: SWVoiceOut *sw1;
for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {
sw = hw->sw_head.lh_first;
while (sw) {
sw1 = sw->entries.le_next;
if (!sw->active && !sw->callback.fn) { if (!sw->active && !sw->callback.fn) {
#ifdef DEBUG_PLIVE #ifdef DEBUG_PLIVE
dolog ("Finishing with old voice\n"); dolog ("Finishing with old voice\n");
#endif #endif
audio_close_out (s, sw); audio_close_out (s, sw);
goto restart; /* play it safe */
} }
sw = sw1;
} }
} }
} }
...@@ -1537,13 +1572,18 @@ static void audio_atexit (void) ...@@ -1537,13 +1572,18 @@ static void audio_atexit (void)
HWVoiceIn *hwi = NULL; HWVoiceIn *hwi = NULL;
while ((hwo = audio_pcm_hw_find_any_enabled_out (s, hwo))) { 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->ctl_out (hwo, VOICE_DISABLE);
hwo->pcm_ops->fini_out (hwo); hwo->pcm_ops->fini_out (hwo);
for (sw = hwo->sw_cap_head.lh_first; sw; sw = sw->entries.le_next) { for (sc = hwo->cap_head.lh_first; sc; sc = sc->entries.le_next) {
audio_notify_capture ((CaptureVoiceOut *) sw->hw, 0); 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) ...@@ -1697,7 +1737,7 @@ AudioState *AUD_init (void)
return s; return s;
} }
int AUD_add_capture ( CaptureVoiceOut *AUD_add_capture (
AudioState *s, AudioState *s,
audsettings_t *as, audsettings_t *as,
struct audio_capture_ops *ops, struct audio_capture_ops *ops,
...@@ -1712,10 +1752,10 @@ int AUD_add_capture ( ...@@ -1712,10 +1752,10 @@ int AUD_add_capture (
s = &glob_audio_state; 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"); dolog ("Invalid settings were passed when trying to add capture\n");
audio_print_settings (as); audio_print_settings (as);
return -1; goto err0;
} }
cb = audio_calloc (AUDIO_FUNC, 1, sizeof (*cb)); cb = audio_calloc (AUDIO_FUNC, 1, sizeof (*cb));
...@@ -1730,7 +1770,7 @@ int AUD_add_capture ( ...@@ -1730,7 +1770,7 @@ int AUD_add_capture (
cap = audio_pcm_capture_find_specific (s, as); cap = audio_pcm_capture_find_specific (s, as);
if (cap) { if (cap) {
LIST_INSERT_HEAD (&cap->cb_head, cb, entries); LIST_INSERT_HEAD (&cap->cb_head, cb, entries);
return 0; return cap;
} }
else { else {
HWVoiceOut *hw; HWVoiceOut *hw;
...@@ -1780,7 +1820,7 @@ int AUD_add_capture ( ...@@ -1780,7 +1820,7 @@ int AUD_add_capture (
while ((hw = audio_pcm_hw_find_any_out (s, hw))) { while ((hw = audio_pcm_hw_find_any_out (s, hw))) {
audio_attach_capture (s, hw); audio_attach_capture (s, hw);
} }
return 0; return cap;
err3: err3:
qemu_free (cap->hw.mix_buf); qemu_free (cap->hw.mix_buf);
...@@ -1789,6 +1829,38 @@ int AUD_add_capture ( ...@@ -1789,6 +1829,38 @@ int AUD_add_capture (
err1: err1:
qemu_free (cb); qemu_free (cb);
err0: 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;
}
} }
} }
...@@ -49,13 +49,31 @@ typedef struct { ...@@ -49,13 +49,31 @@ typedef struct {
int endianness; int endianness;
} audsettings_t; } audsettings_t;
typedef enum {
AUD_CNOTIFY_ENABLE,
AUD_CNOTIFY_DISABLE
} audcnotification_e;
struct audio_capture_ops { 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 (*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 AudioState AudioState;
typedef struct SWVoiceOut SWVoiceOut; typedef struct SWVoiceOut SWVoiceOut;
typedef struct CaptureVoiceOut CaptureVoiceOut;
typedef struct SWVoiceIn SWVoiceIn; typedef struct SWVoiceIn SWVoiceIn;
typedef struct QEMUSoundCard { typedef struct QEMUSoundCard {
...@@ -79,12 +97,13 @@ AudioState *AUD_init (void); ...@@ -79,12 +97,13 @@ AudioState *AUD_init (void);
void AUD_help (void); void AUD_help (void);
void AUD_register_card (AudioState *s, const char *name, QEMUSoundCard *card); void AUD_register_card (AudioState *s, const char *name, QEMUSoundCard *card);
void AUD_remove_card (QEMUSoundCard *card); void AUD_remove_card (QEMUSoundCard *card);
int AUD_add_capture ( CaptureVoiceOut *AUD_add_capture (
AudioState *s, AudioState *s,
audsettings_t *as, audsettings_t *as,
struct audio_capture_ops *ops, struct audio_capture_ops *ops,
void *opaque void *opaque
); );
void AUD_del_capture (CaptureVoiceOut *cap, void *cb_opaque);
SWVoiceOut *AUD_open_out ( SWVoiceOut *AUD_open_out (
QEMUSoundCard *card, QEMUSoundCard *card,
......
...@@ -64,10 +64,11 @@ struct audio_pcm_info { ...@@ -64,10 +64,11 @@ struct audio_pcm_info {
int swap_endianness; int swap_endianness;
}; };
typedef struct SWVoiceCap SWVoiceCap;
typedef struct HWVoiceOut { typedef struct HWVoiceOut {
int enabled; int enabled;
int pending_disable; int pending_disable;
int valid;
struct audio_pcm_info info; struct audio_pcm_info info;
f_sample *clip; f_sample *clip;
...@@ -79,7 +80,7 @@ typedef struct HWVoiceOut { ...@@ -79,7 +80,7 @@ typedef struct HWVoiceOut {
int samples; int samples;
LIST_HEAD (sw_out_listhead, SWVoiceOut) sw_head; 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; struct audio_pcm_ops *pcm_ops;
LIST_ENTRY (HWVoiceOut) entries; LIST_ENTRY (HWVoiceOut) entries;
} HWVoiceOut; } HWVoiceOut;
...@@ -116,7 +117,6 @@ struct SWVoiceOut { ...@@ -116,7 +117,6 @@ struct SWVoiceOut {
volume_t vol; volume_t vol;
struct audio_callback callback; struct audio_callback callback;
LIST_ENTRY (SWVoiceOut) entries; LIST_ENTRY (SWVoiceOut) entries;
LIST_ENTRY (SWVoiceOut) cap_entries;
}; };
struct SWVoiceIn { struct SWVoiceIn {
...@@ -168,12 +168,18 @@ struct capture_callback { ...@@ -168,12 +168,18 @@ struct capture_callback {
LIST_ENTRY (capture_callback) entries; LIST_ENTRY (capture_callback) entries;
}; };
typedef struct CaptureVoiceOut { struct CaptureVoiceOut {
HWVoiceOut hw; HWVoiceOut hw;
void *buf; void *buf;
LIST_HEAD (cb_listhead, capture_callback) cb_head; LIST_HEAD (cb_listhead, capture_callback) cb_head;
LIST_ENTRY (CaptureVoiceOut) entries; LIST_ENTRY (CaptureVoiceOut) entries;
} CaptureVoiceOut; };
struct SWVoiceCap {
SWVoiceOut sw;
CaptureVoiceOut *cap;
LIST_ENTRY (SWVoiceCap) entries;
};
struct AudioState { struct AudioState {
struct audio_driver *drv; struct audio_driver *drv;
......
...@@ -269,7 +269,7 @@ static HW *glue (audio_pcm_hw_add_new_, TYPE) (AudioState *s, audsettings_t *as) ...@@ -269,7 +269,7 @@ static HW *glue (audio_pcm_hw_add_new_, TYPE) (AudioState *s, audsettings_t *as)
hw->pcm_ops = drv->pcm_ops; hw->pcm_ops = drv->pcm_ops;
LIST_INIT (&hw->sw_head); LIST_INIT (&hw->sw_head);
#ifdef DAC #ifdef DAC
LIST_INIT (&hw->sw_cap_head); LIST_INIT (&hw->cap_head);
#endif #endif
if (glue (hw->pcm_ops->init_, TYPE) (hw, as)) { if (glue (hw->pcm_ops->init_, TYPE) (hw, as)) {
goto err0; goto err0;
...@@ -426,7 +426,7 @@ SW *glue (AUD_open_, TYPE) ( ...@@ -426,7 +426,7 @@ SW *glue (AUD_open_, TYPE) (
s = card->audio; 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); audio_print_settings (as);
goto fail; goto fail;
} }
......
...@@ -102,7 +102,7 @@ static int no_run_in (HWVoiceIn *hw) ...@@ -102,7 +102,7 @@ static int no_run_in (HWVoiceIn *hw)
NoVoiceIn *no = (NoVoiceIn *) hw; NoVoiceIn *no = (NoVoiceIn *) hw;
int live = audio_pcm_hw_get_live_in (hw); int live = audio_pcm_hw_get_live_in (hw);
int dead = hw->samples - live; int dead = hw->samples - live;
int samples; int samples = 0;
if (dead) { if (dead) {
int64_t now = qemu_get_clock (vm_clock); int64_t now = qemu_get_clock (vm_clock);
......
...@@ -3,6 +3,11 @@ ...@@ -3,6 +3,11 @@
typedef struct { typedef struct {
QEMUFile *f; QEMUFile *f;
int bytes; int bytes;
char *path;
int freq;
int bits;
int nchannels;
CaptureVoiceOut *cap;
} WAVState; } WAVState;
/* VICE code: Store number as little endian. */ /* VICE code: Store number as little endian. */
...@@ -15,35 +20,39 @@ static void le_store (uint8_t *buf, uint32_t val, int len) ...@@ -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) { static void wav_destroy (void *opaque)
uint8_t rlen[4]; {
uint8_t dlen[4]; WAVState *wav = opaque;
uint32_t datalen = wav->bytes; uint8_t rlen[4];
uint32_t rifflen = datalen + 36; uint8_t dlen[4];
uint32_t datalen = wav->bytes;
uint32_t rifflen = datalen + 36;
if (!wav->f) { if (!wav->f) {
return; return;
} }
le_store (rlen, rifflen, 4); le_store (rlen, rifflen, 4);
le_store (dlen, datalen, 4); le_store (dlen, datalen, 4);
qemu_fseek (wav->f, 4, SEEK_SET); qemu_fseek (wav->f, 4, SEEK_SET);
qemu_put_buffer (wav->f, rlen, 4); qemu_put_buffer (wav->f, rlen, 4);
qemu_fseek (wav->f, 32, SEEK_CUR); qemu_fseek (wav->f, 32, SEEK_CUR);
qemu_put_buffer (wav->f, dlen, 4); qemu_put_buffer (wav->f, dlen, 4);
} fclose (wav->f);
else { if (wav->path) {
qemu_fseek (wav->f, 0, SEEK_END); 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; WAVState *wav = opaque;
...@@ -51,7 +60,30 @@ static void wav_capture_cb (void *opaque, void *buf, int size) ...@@ -51,7 +60,30 @@ static void wav_capture_cb (void *opaque, void *buf, int size)
wav->bytes += 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; WAVState *wav;
uint8_t hdr[] = { uint8_t hdr[] = {
...@@ -62,23 +94,35 @@ void wav_capture (const char *path, int freq, int bits16, int stereo) ...@@ -62,23 +94,35 @@ void wav_capture (const char *path, int freq, int bits16, int stereo)
}; };
audsettings_t as; audsettings_t as;
struct audio_capture_ops ops; 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; stereo = nchannels == 2;
bits16 = !!bits16; bits16 = bits == 16;
as.freq = freq; as.freq = freq;
as.nchannels = 1 << stereo; as.nchannels = 1 << stereo;
as.fmt = bits16 ? AUD_FMT_S16 : AUD_FMT_U8; as.fmt = bits16 ? AUD_FMT_S16 : AUD_FMT_U8;
as.endianness = 0; as.endianness = 0;
ops.state = wav_state_cb; ops.notify = wav_notify;
ops.capture = wav_capture_cb; ops.capture = wav_capture;
ops.destroy = wav_destroy;
wav = qemu_mallocz (sizeof (*wav)); wav = qemu_mallocz (sizeof (*wav));
if (!wav) { if (!wav) {
AUD_log ("wav", "Could not allocate memory (%zu bytes)", sizeof (*wav)); AUD_log ("wav", "Could not allocate memory (%zu bytes)", sizeof (*wav));
return; return -1;
} }
shift = bits16 + stereo; shift = bits16 + stereo;
...@@ -91,12 +135,28 @@ void wav_capture (const char *path, int freq, int bits16, int stereo) ...@@ -91,12 +135,28 @@ void wav_capture (const char *path, int freq, int bits16, int stereo)
wav->f = fopen (path, "wb"); wav->f = fopen (path, "wb");
if (!wav->f) { if (!wav->f) {
AUD_log ("wav", "Failed to open wave file `%s'\nReason: %s\n", term_printf ("Failed to open wave file `%s'\nReason: %s\n",
path, strerror (errno)); path, strerror (errno));
qemu_free (wav); 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)); 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;
} }
...@@ -1077,6 +1077,64 @@ static void do_info_profile(void) ...@@ -1077,6 +1077,64 @@ static void do_info_profile(void)
} }
#endif #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[] = { static term_cmd_t term_cmds[] = {
{ "help|?", "s?", do_help, { "help|?", "s?", do_help,
"[cmd]", "show the help" }, "[cmd]", "show the help" },
...@@ -1133,6 +1191,13 @@ static term_cmd_t term_cmds[] = { ...@@ -1133,6 +1191,13 @@ static term_cmd_t term_cmds[] = {
"dx dy [dz]", "send mouse move events" }, "dx dy [dz]", "send mouse move events" },
{ "mouse_button", "i", do_mouse_button, { "mouse_button", "i", do_mouse_button,
"state", "change mouse button state (1=L, 2=M, 4=R)" }, "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, }, { NULL, NULL, },
}; };
...@@ -1171,6 +1236,8 @@ static term_cmd_t info_cmds[] = { ...@@ -1171,6 +1236,8 @@ static term_cmd_t info_cmds[] = {
"", "show host USB devices", }, "", "show host USB devices", },
{ "profile", "", do_info_profile, { "profile", "", do_info_profile,
"", "show profiling information", }, "", "show profiling information", },
{ "capture", "", do_info_capture,
"show capture information" },
{ NULL, NULL, }, { NULL, NULL, },
}; };
...@@ -2081,6 +2148,9 @@ static void monitor_handle_command(const char *cmdline) ...@@ -2081,6 +2148,9 @@ static void monitor_handle_command(const char *cmdline)
case 6: case 6:
cmd->handler(args[0], args[1], args[2], args[3], args[4], args[5]); cmd->handler(args[0], args[1], args[2], args[3], args[4], args[5]);
break; break;
case 7:
cmd->handler(args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
break;
default: default:
term_printf("unsupported number of arguments: %d\n", nb_args); term_printf("unsupported number of arguments: %d\n", nb_args);
goto fail; goto fail;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册