16086a565SPeter Maydell #include "qemu/osdep.h"
2*947e4744SKevin Wolf #include "qemu/qemu-print.h"
3e688df6bSMarkus Armbruster #include "qapi/error.h"
4d49b6836SMarkus Armbruster #include "qemu/error-report.h"
587ecb68bSpbrook #include "audio.h"
68ead62cfSbellard
78ead62cfSbellard typedef struct {
8b04df2a4SJuan Quintela FILE *f;
98ead62cfSbellard int bytes;
10ec36b695Sbellard char *path;
11ec36b695Sbellard int freq;
12ec36b695Sbellard int bits;
13ec36b695Sbellard int nchannels;
14ec36b695Sbellard CaptureVoiceOut *cap;
158ead62cfSbellard } WAVState;
168ead62cfSbellard
178ead62cfSbellard /* VICE code: Store number as little endian. */
le_store(uint8_t * buf,uint32_t val,int len)188ead62cfSbellard static void le_store (uint8_t *buf, uint32_t val, int len)
198ead62cfSbellard {
208ead62cfSbellard int i;
218ead62cfSbellard for (i = 0; i < len; i++) {
228ead62cfSbellard buf[i] = (uint8_t) (val & 0xff);
238ead62cfSbellard val >>= 8;
248ead62cfSbellard }
258ead62cfSbellard }
268ead62cfSbellard
wav_notify(void * opaque,audcnotification_e cmd)27ec36b695Sbellard static void wav_notify (void *opaque, audcnotification_e cmd)
28ec36b695Sbellard {
29ec36b695Sbellard (void) opaque;
30ec36b695Sbellard (void) cmd;
31ec36b695Sbellard }
32ec36b695Sbellard
wav_destroy(void * opaque)33ec36b695Sbellard static void wav_destroy (void *opaque)
348ead62cfSbellard {
358ead62cfSbellard WAVState *wav = opaque;
368ead62cfSbellard uint8_t rlen[4];
378ead62cfSbellard uint8_t dlen[4];
388ead62cfSbellard uint32_t datalen = wav->bytes;
398ead62cfSbellard uint32_t rifflen = datalen + 36;
408ead62cfSbellard
41e84a4fedSbellard if (wav->f) {
428ead62cfSbellard le_store (rlen, rifflen, 4);
438ead62cfSbellard le_store (dlen, datalen, 4);
448ead62cfSbellard
45b04df2a4SJuan Quintela if (fseek (wav->f, 4, SEEK_SET)) {
468beaac12SDr. David Alan Gilbert error_report("wav_destroy: rlen fseek failed: %s",
47b04df2a4SJuan Quintela strerror(errno));
48b04df2a4SJuan Quintela goto doclose;
49b04df2a4SJuan Quintela }
50b04df2a4SJuan Quintela if (fwrite (rlen, 4, 1, wav->f) != 1) {
518beaac12SDr. David Alan Gilbert error_report("wav_destroy: rlen fwrite failed: %s",
52b04df2a4SJuan Quintela strerror(errno));
53b04df2a4SJuan Quintela goto doclose;
54b04df2a4SJuan Quintela }
55b04df2a4SJuan Quintela if (fseek (wav->f, 32, SEEK_CUR)) {
568beaac12SDr. David Alan Gilbert error_report("wav_destroy: dlen fseek failed: %s",
57b04df2a4SJuan Quintela strerror(errno));
58b04df2a4SJuan Quintela goto doclose;
59b04df2a4SJuan Quintela }
60b04df2a4SJuan Quintela if (fwrite (dlen, 1, 4, wav->f) != 4) {
618beaac12SDr. David Alan Gilbert error_report("wav_destroy: dlen fwrite failed: %s",
62b04df2a4SJuan Quintela strerror(errno));
63b04df2a4SJuan Quintela goto doclose;
64b04df2a4SJuan Quintela }
65b04df2a4SJuan Quintela doclose:
66b04df2a4SJuan Quintela if (fclose (wav->f)) {
6769e99504SLe Tan error_report("wav_destroy: fclose failed: %s", strerror(errno));
68b04df2a4SJuan Quintela }
698ead62cfSbellard }
70e84a4fedSbellard
717267c094SAnthony Liguori g_free (wav->path);
728ead62cfSbellard }
738ead62cfSbellard
wav_capture(void * opaque,const void * buf,int size)7457a878edSPhilippe Mathieu-Daudé static void wav_capture(void *opaque, const void *buf, int size)
758ead62cfSbellard {
768ead62cfSbellard WAVState *wav = opaque;
778ead62cfSbellard
78b04df2a4SJuan Quintela if (fwrite (buf, size, 1, wav->f) != 1) {
798beaac12SDr. David Alan Gilbert error_report("wav_capture: fwrite error: %s", strerror(errno));
80b04df2a4SJuan Quintela }
818ead62cfSbellard wav->bytes += size;
828ead62cfSbellard }
838ead62cfSbellard
wav_capture_destroy(void * opaque)84ec36b695Sbellard static void wav_capture_destroy (void *opaque)
85ec36b695Sbellard {
86ec36b695Sbellard WAVState *wav = opaque;
87ec36b695Sbellard
88ec36b695Sbellard AUD_del_capture (wav->cap, wav);
897bdfd907SMarc-André Lureau g_free (wav);
90ec36b695Sbellard }
91ec36b695Sbellard
wav_capture_info(void * opaque)92ec36b695Sbellard static void wav_capture_info (void *opaque)
93ec36b695Sbellard {
94ec36b695Sbellard WAVState *wav = opaque;
95ec36b695Sbellard char *path = wav->path;
96ec36b695Sbellard
97*947e4744SKevin Wolf qemu_printf("Capturing audio(%d,%d,%d) to %s: %d bytes\n",
98ec36b695Sbellard wav->freq, wav->bits, wav->nchannels,
99ec36b695Sbellard path ? path : "<not available>", wav->bytes);
100ec36b695Sbellard }
101ec36b695Sbellard
102ec36b695Sbellard static struct capture_ops wav_capture_ops = {
103ec36b695Sbellard .destroy = wav_capture_destroy,
104ec36b695Sbellard .info = wav_capture_info
105ec36b695Sbellard };
106ec36b695Sbellard
wav_start_capture(AudioState * state,CaptureState * s,const char * path,int freq,int bits,int nchannels)107ecd97e95SKővágó, Zoltán int wav_start_capture(AudioState *state, CaptureState *s, const char *path,
108ecd97e95SKővágó, Zoltán int freq, int bits, int nchannels)
1098ead62cfSbellard {
1108ead62cfSbellard WAVState *wav;
1118ead62cfSbellard uint8_t hdr[] = {
1128ead62cfSbellard 0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56,
1138ead62cfSbellard 0x45, 0x66, 0x6d, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00,
1148ead62cfSbellard 0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04,
1158ead62cfSbellard 0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00
1168ead62cfSbellard };
1171ea879e5Smalc struct audsettings as;
1188ead62cfSbellard struct audio_capture_ops ops;
119ec36b695Sbellard int stereo, bits16, shift;
120ec36b695Sbellard CaptureVoiceOut *cap;
1218ead62cfSbellard
122ec36b695Sbellard if (bits != 8 && bits != 16) {
1238beaac12SDr. David Alan Gilbert error_report("incorrect bit count %d, must be 8 or 16", bits);
124ec36b695Sbellard return -1;
125ec36b695Sbellard }
126ec36b695Sbellard
127ec36b695Sbellard if (nchannels != 1 && nchannels != 2) {
1288beaac12SDr. David Alan Gilbert error_report("incorrect channel count %d, must be 1 or 2",
12947dbd1f3Sbellard nchannels);
130ec36b695Sbellard return -1;
131ec36b695Sbellard }
132ec36b695Sbellard
133ec36b695Sbellard stereo = nchannels == 2;
134ec36b695Sbellard bits16 = bits == 16;
1358ead62cfSbellard
1368ead62cfSbellard as.freq = freq;
1378ead62cfSbellard as.nchannels = 1 << stereo;
13885bc5852SKővágó, Zoltán as.fmt = bits16 ? AUDIO_FORMAT_S16 : AUDIO_FORMAT_U8;
139d929eba5Sbellard as.endianness = 0;
1408ead62cfSbellard
141ec36b695Sbellard ops.notify = wav_notify;
142ec36b695Sbellard ops.capture = wav_capture;
143ec36b695Sbellard ops.destroy = wav_destroy;
1448ead62cfSbellard
1457267c094SAnthony Liguori wav = g_malloc0 (sizeof (*wav));
1468ead62cfSbellard
1478ead62cfSbellard shift = bits16 + stereo;
1488ead62cfSbellard hdr[34] = bits16 ? 0x10 : 0x08;
1498ead62cfSbellard
1508ead62cfSbellard le_store (hdr + 22, as.nchannels, 2);
1518ead62cfSbellard le_store (hdr + 24, freq, 4);
1528ead62cfSbellard le_store (hdr + 28, freq << shift, 4);
1538ead62cfSbellard le_store (hdr + 32, 1 << shift, 2);
1548ead62cfSbellard
155b04df2a4SJuan Quintela wav->f = fopen (path, "wb");
1568ead62cfSbellard if (!wav->f) {
1578beaac12SDr. David Alan Gilbert error_report("Failed to open wave file `%s': %s",
1588ead62cfSbellard path, strerror(errno));
1597267c094SAnthony Liguori g_free (wav);
160ec36b695Sbellard return -1;
1618ead62cfSbellard }
1628ead62cfSbellard
1637267c094SAnthony Liguori wav->path = g_strdup (path);
164ec36b695Sbellard wav->bits = bits;
165ec36b695Sbellard wav->nchannels = nchannels;
166ec36b695Sbellard wav->freq = freq;
167ec36b695Sbellard
168b04df2a4SJuan Quintela if (fwrite (hdr, sizeof (hdr), 1, wav->f) != 1) {
1698beaac12SDr. David Alan Gilbert error_report("Failed to write header: %s", strerror(errno));
170b04df2a4SJuan Quintela goto error_free;
171b04df2a4SJuan Quintela }
172ec36b695Sbellard
173ecd97e95SKővágó, Zoltán cap = AUD_add_capture(state, &as, &ops, wav);
174ec36b695Sbellard if (!cap) {
1758beaac12SDr. David Alan Gilbert error_report("Failed to add audio capture");
176b04df2a4SJuan Quintela goto error_free;
177ec36b695Sbellard }
178ec36b695Sbellard
179ec36b695Sbellard wav->cap = cap;
180ec36b695Sbellard s->opaque = wav;
181ec36b695Sbellard s->ops = wav_capture_ops;
182ec36b695Sbellard return 0;
183b04df2a4SJuan Quintela
184b04df2a4SJuan Quintela error_free:
185b04df2a4SJuan Quintela g_free (wav->path);
186b04df2a4SJuan Quintela if (fclose (wav->f)) {
1878beaac12SDr. David Alan Gilbert error_report("Failed to close wave file: %s", strerror(errno));
188b04df2a4SJuan Quintela }
189b04df2a4SJuan Quintela g_free (wav);
190b04df2a4SJuan Quintela return -1;
1918ead62cfSbellard }
192