xref: /openbmc/qemu/audio/wavcapture.c (revision b957a1b0)
1 #include "hw/hw.h"
2 #include "monitor/monitor.h"
3 #include "audio.h"
4 
5 typedef struct {
6     FILE *f;
7     int bytes;
8     char *path;
9     int freq;
10     int bits;
11     int nchannels;
12     CaptureVoiceOut *cap;
13 } WAVState;
14 
15 /* VICE code: Store number as little endian. */
16 static void le_store (uint8_t *buf, uint32_t val, int len)
17 {
18     int i;
19     for (i = 0; i < len; i++) {
20         buf[i] = (uint8_t) (val & 0xff);
21         val >>= 8;
22     }
23 }
24 
25 static void wav_notify (void *opaque, audcnotification_e cmd)
26 {
27     (void) opaque;
28     (void) cmd;
29 }
30 
31 static void wav_destroy (void *opaque)
32 {
33     WAVState *wav = opaque;
34     uint8_t rlen[4];
35     uint8_t dlen[4];
36     uint32_t datalen = wav->bytes;
37     uint32_t rifflen = datalen + 36;
38     Monitor *mon = cur_mon;
39 
40     if (wav->f) {
41         le_store (rlen, rifflen, 4);
42         le_store (dlen, datalen, 4);
43 
44         if (fseek (wav->f, 4, SEEK_SET)) {
45             monitor_printf (mon, "wav_destroy: rlen fseek failed\nReason: %s\n",
46                             strerror (errno));
47             goto doclose;
48         }
49         if (fwrite (rlen, 4, 1, wav->f) != 1) {
50             monitor_printf (mon, "wav_destroy: rlen fwrite failed\nReason %s\n",
51                             strerror (errno));
52             goto doclose;
53         }
54         if (fseek (wav->f, 32, SEEK_CUR)) {
55             monitor_printf (mon, "wav_destroy: dlen fseek failed\nReason %s\n",
56                             strerror (errno));
57             goto doclose;
58         }
59         if (fwrite (dlen, 1, 4, wav->f) != 4) {
60             monitor_printf (mon, "wav_destroy: dlen fwrite failed\nReason %s\n",
61                             strerror (errno));
62             goto doclose;
63         }
64     doclose:
65         if (fclose (wav->f)) {
66             fprintf (stderr, "wav_destroy: fclose failed: %s",
67                      strerror (errno));
68         }
69     }
70 
71     g_free (wav->path);
72 }
73 
74 static void wav_capture (void *opaque, void *buf, int size)
75 {
76     WAVState *wav = opaque;
77 
78     if (fwrite (buf, size, 1, wav->f) != 1) {
79         monitor_printf (cur_mon, "wav_capture: fwrite error\nReason: %s",
80                         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 }
91 
92 static void wav_capture_info (void *opaque)
93 {
94     WAVState *wav = opaque;
95     char *path = wav->path;
96 
97     monitor_printf (cur_mon, "Capturing audio(%d,%d,%d) to %s: %d bytes\n",
98                     wav->freq, wav->bits, wav->nchannels,
99                     path ? path : "<not available>", wav->bytes);
100 }
101 
102 static struct capture_ops wav_capture_ops = {
103     .destroy = wav_capture_destroy,
104     .info = wav_capture_info
105 };
106 
107 int wav_start_capture (CaptureState *s, const char *path, int freq,
108                        int bits, int nchannels)
109 {
110     Monitor *mon = cur_mon;
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         monitor_printf (mon, "incorrect bit count %d, must be 8 or 16\n", bits);
125         return -1;
126     }
127 
128     if (nchannels != 1 && nchannels != 2) {
129         monitor_printf (mon, "incorrect channel count %d, must be 1 or 2\n",
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         monitor_printf (mon, "Failed to open wave file `%s'\nReason: %s\n",
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         monitor_printf (mon, "Failed to write header\nReason: %s\n",
171                         strerror (errno));
172         goto error_free;
173     }
174 
175     cap = AUD_add_capture (&as, &ops, wav);
176     if (!cap) {
177         monitor_printf (mon, "Failed to add audio capture\n");
178         goto error_free;
179     }
180 
181     wav->cap = cap;
182     s->opaque = wav;
183     s->ops = wav_capture_ops;
184     return 0;
185 
186 error_free:
187     g_free (wav->path);
188     if (fclose (wav->f)) {
189         monitor_printf (mon, "Failed to close wave file\nReason: %s\n",
190                         strerror (errno));
191     }
192     g_free (wav);
193     return -1;
194 }
195