xref: /openbmc/qemu/audio/wavcapture.c (revision b7092cda1b36ce687e65ab1831346f9529b781b8)
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