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