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