xref: /openbmc/qemu/audio/wavcapture.c (revision b917da4c)
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 }
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     Monitor *mon = cur_mon;
112     WAVState *wav;
113     uint8_t hdr[] = {
114         0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56,
115         0x45, 0x66, 0x6d, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00,
116         0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04,
117         0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00
118     };
119     struct audsettings as;
120     struct audio_capture_ops ops;
121     int stereo, bits16, shift;
122     CaptureVoiceOut *cap;
123 
124     if (bits != 8 && bits != 16) {
125         monitor_printf (mon, "incorrect bit count %d, must be 8 or 16\n", bits);
126         return -1;
127     }
128 
129     if (nchannels != 1 && nchannels != 2) {
130         monitor_printf (mon, "incorrect channel count %d, must be 1 or 2\n",
131                         nchannels);
132         return -1;
133     }
134 
135     stereo = nchannels == 2;
136     bits16 = bits == 16;
137 
138     as.freq = freq;
139     as.nchannels = 1 << stereo;
140     as.fmt = bits16 ? AUD_FMT_S16 : AUD_FMT_U8;
141     as.endianness = 0;
142 
143     ops.notify = wav_notify;
144     ops.capture = wav_capture;
145     ops.destroy = wav_destroy;
146 
147     wav = g_malloc0 (sizeof (*wav));
148 
149     shift = bits16 + stereo;
150     hdr[34] = bits16 ? 0x10 : 0x08;
151 
152     le_store (hdr + 22, as.nchannels, 2);
153     le_store (hdr + 24, freq, 4);
154     le_store (hdr + 28, freq << shift, 4);
155     le_store (hdr + 32, 1 << shift, 2);
156 
157     wav->f = fopen (path, "wb");
158     if (!wav->f) {
159         monitor_printf (mon, "Failed to open wave file `%s'\nReason: %s\n",
160                         path, strerror (errno));
161         g_free (wav);
162         return -1;
163     }
164 
165     wav->path = g_strdup (path);
166     wav->bits = bits;
167     wav->nchannels = nchannels;
168     wav->freq = freq;
169 
170     if (fwrite (hdr, sizeof (hdr), 1, wav->f) != 1) {
171         monitor_printf (mon, "Failed to write header\nReason: %s\n",
172                         strerror (errno));
173         goto error_free;
174     }
175 
176     cap = AUD_add_capture (&as, &ops, wav);
177     if (!cap) {
178         monitor_printf (mon, "Failed to add audio capture\n");
179         goto error_free;
180     }
181 
182     wav->cap = cap;
183     s->opaque = wav;
184     s->ops = wav_capture_ops;
185     return 0;
186 
187 error_free:
188     g_free (wav->path);
189     if (fclose (wav->f)) {
190         monitor_printf (mon, "Failed to close wave file\nReason: %s\n",
191                         strerror (errno));
192     }
193     g_free (wav);
194     return -1;
195 }
196