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
7typedef 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
18static 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
27static void wav_notify (void *opaque, audcnotification_e cmd)
28{
29 (void) opaque;
30 (void) cmd;
31}
32
33static 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
75static 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
86static 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
94static 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
104static struct capture_ops wav_capture_ops = {
105 .destroy = wav_capture_destroy,
106 .info = wav_capture_info
107};
108
109int 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
188error_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