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}
92
93static 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
103static struct capture_ops wav_capture_ops = {
104 .destroy = wav_capture_destroy,
105 .info = wav_capture_info
106};
107
108int 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
187error_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