xref: /openbmc/qemu/audio/wavcapture.c (revision dc5bd18f)
1 #include "qemu/osdep.h"
2 #include "hw/hw.h"
3 #include "monitor/monitor.h"
4 #include "qapi/error.h"
5 #include "qemu/error-report.h"
6 #include "audio.h"
7 
8 typedef struct {
9     FILE *f;
10     int bytes;
11     char *path;
12     int freq;
13     int bits;
14     int nchannels;
15     CaptureVoiceOut *cap;
16 } WAVState;
17 
18 /* VICE code: Store number as little endian. */
19 static void le_store (uint8_t *buf, uint32_t val, int len)
20 {
21     int i;
22     for (i = 0; i < len; i++) {
23         buf[i] = (uint8_t) (val & 0xff);
24         val >>= 8;
25     }
26 }
27 
28 static void wav_notify (void *opaque, audcnotification_e cmd)
29 {
30     (void) opaque;
31     (void) cmd;
32 }
33 
34 static void wav_destroy (void *opaque)
35 {
36     WAVState *wav = opaque;
37     uint8_t rlen[4];
38     uint8_t dlen[4];
39     uint32_t datalen = wav->bytes;
40     uint32_t rifflen = datalen + 36;
41     Monitor *mon = cur_mon;
42 
43     if (wav->f) {
44         le_store (rlen, rifflen, 4);
45         le_store (dlen, datalen, 4);
46 
47         if (fseek (wav->f, 4, SEEK_SET)) {
48             monitor_printf (mon, "wav_destroy: rlen fseek failed\nReason: %s\n",
49                             strerror (errno));
50             goto doclose;
51         }
52         if (fwrite (rlen, 4, 1, wav->f) != 1) {
53             monitor_printf (mon, "wav_destroy: rlen fwrite failed\nReason %s\n",
54                             strerror (errno));
55             goto doclose;
56         }
57         if (fseek (wav->f, 32, SEEK_CUR)) {
58             monitor_printf (mon, "wav_destroy: dlen fseek failed\nReason %s\n",
59                             strerror (errno));
60             goto doclose;
61         }
62         if (fwrite (dlen, 1, 4, wav->f) != 4) {
63             monitor_printf (mon, "wav_destroy: dlen fwrite failed\nReason %s\n",
64                             strerror (errno));
65             goto doclose;
66         }
67     doclose:
68         if (fclose (wav->f)) {
69             error_report("wav_destroy: fclose failed: %s", strerror(errno));
70         }
71     }
72 
73     g_free (wav->path);
74 }
75 
76 static void wav_capture (void *opaque, void *buf, int size)
77 {
78     WAVState *wav = opaque;
79 
80     if (fwrite (buf, size, 1, wav->f) != 1) {
81         monitor_printf (cur_mon, "wav_capture: fwrite error\nReason: %s",
82                         strerror (errno));
83     }
84     wav->bytes += size;
85 }
86 
87 static void wav_capture_destroy (void *opaque)
88 {
89     WAVState *wav = opaque;
90 
91     AUD_del_capture (wav->cap, wav);
92     g_free (wav);
93 }
94 
95 static void wav_capture_info (void *opaque)
96 {
97     WAVState *wav = opaque;
98     char *path = wav->path;
99 
100     monitor_printf (cur_mon, "Capturing audio(%d,%d,%d) to %s: %d bytes\n",
101                     wav->freq, wav->bits, wav->nchannels,
102                     path ? path : "<not available>", wav->bytes);
103 }
104 
105 static struct capture_ops wav_capture_ops = {
106     .destroy = wav_capture_destroy,
107     .info = wav_capture_info
108 };
109 
110 int wav_start_capture (CaptureState *s, const char *path, int freq,
111                        int bits, int nchannels)
112 {
113     Monitor *mon = cur_mon;
114     WAVState *wav;
115     uint8_t hdr[] = {
116         0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56,
117         0x45, 0x66, 0x6d, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00,
118         0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04,
119         0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00
120     };
121     struct audsettings as;
122     struct audio_capture_ops ops;
123     int stereo, bits16, shift;
124     CaptureVoiceOut *cap;
125 
126     if (bits != 8 && bits != 16) {
127         monitor_printf (mon, "incorrect bit count %d, must be 8 or 16\n", bits);
128         return -1;
129     }
130 
131     if (nchannels != 1 && nchannels != 2) {
132         monitor_printf (mon, "incorrect channel count %d, must be 1 or 2\n",
133                         nchannels);
134         return -1;
135     }
136 
137     stereo = nchannels == 2;
138     bits16 = bits == 16;
139 
140     as.freq = freq;
141     as.nchannels = 1 << stereo;
142     as.fmt = bits16 ? AUD_FMT_S16 : AUD_FMT_U8;
143     as.endianness = 0;
144 
145     ops.notify = wav_notify;
146     ops.capture = wav_capture;
147     ops.destroy = wav_destroy;
148 
149     wav = g_malloc0 (sizeof (*wav));
150 
151     shift = bits16 + stereo;
152     hdr[34] = bits16 ? 0x10 : 0x08;
153 
154     le_store (hdr + 22, as.nchannels, 2);
155     le_store (hdr + 24, freq, 4);
156     le_store (hdr + 28, freq << shift, 4);
157     le_store (hdr + 32, 1 << shift, 2);
158 
159     wav->f = fopen (path, "wb");
160     if (!wav->f) {
161         monitor_printf (mon, "Failed to open wave file `%s'\nReason: %s\n",
162                         path, strerror (errno));
163         g_free (wav);
164         return -1;
165     }
166 
167     wav->path = g_strdup (path);
168     wav->bits = bits;
169     wav->nchannels = nchannels;
170     wav->freq = freq;
171 
172     if (fwrite (hdr, sizeof (hdr), 1, wav->f) != 1) {
173         monitor_printf (mon, "Failed to write header\nReason: %s\n",
174                         strerror (errno));
175         goto error_free;
176     }
177 
178     cap = AUD_add_capture (&as, &ops, wav);
179     if (!cap) {
180         monitor_printf (mon, "Failed to add audio capture\n");
181         goto error_free;
182     }
183 
184     wav->cap = cap;
185     s->opaque = wav;
186     s->ops = wav_capture_ops;
187     return 0;
188 
189 error_free:
190     g_free (wav->path);
191     if (fclose (wav->f)) {
192         monitor_printf (mon, "Failed to close wave file\nReason: %s\n",
193                         strerror (errno));
194     }
195     g_free (wav);
196     return -1;
197 }
198