1#include "qemu/osdep.h"
2#include "qemu/qemu-print.h"
3#include "qapi/error.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
41 if (wav->f) {
42 le_store (rlen, rifflen, 4);
43 le_store (dlen, datalen, 4);
44
45 if (fseek (wav->f, 4, SEEK_SET)) {
46 error_report("wav_destroy: rlen fseek failed: %s",
47 strerror(errno));
48 goto doclose;
49 }
50 if (fwrite (rlen, 4, 1, wav->f) != 1) {
51 error_report("wav_destroy: rlen fwrite failed: %s",
52 strerror(errno));
53 goto doclose;
54 }
55 if (fseek (wav->f, 32, SEEK_CUR)) {
56 error_report("wav_destroy: dlen fseek failed: %s",
57 strerror(errno));
58 goto doclose;
59 }
60 if (fwrite (dlen, 1, 4, wav->f) != 4) {
61 error_report("wav_destroy: dlen fwrite failed: %s",
62 strerror(errno));
63 goto doclose;
64 }
65 doclose:
66 if (fclose (wav->f)) {
67 error_report("wav_destroy: fclose failed: %s", strerror(errno));
68 }
69 }
70
71 g_free (wav->path);
72}
73
74static void wav_capture(void *opaque, const void *buf, int size)
75{
76 WAVState *wav = opaque;
77
78 if (fwrite (buf, size, 1, wav->f) != 1) {
79 error_report("wav_capture: fwrite error: %s", strerror(errno));
80 }
81 wav->bytes += size;
82}
83
84static void wav_capture_destroy (void *opaque)
85{
86 WAVState *wav = opaque;
87
88 AUD_del_capture (wav->cap, wav);
89 g_free (wav);
90}
91
92static void wav_capture_info (void *opaque)
93{
94 WAVState *wav = opaque;
95 char *path = wav->path;
96
97 qemu_printf("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
102static struct capture_ops wav_capture_ops = {
103 .destroy = wav_capture_destroy,
104 .info = wav_capture_info
105};
106
107int wav_start_capture(AudioState *state, CaptureState *s, const char *path,
108 int freq, int bits, int nchannels)
109{
110 WAVState *wav;
111 uint8_t hdr[] = {
112 0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56,
113 0x45, 0x66, 0x6d, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00,
114 0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04,
115 0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00
116 };
117 struct audsettings as;
118 struct audio_capture_ops ops;
119 int stereo, bits16, shift;
120 CaptureVoiceOut *cap;
121
122 if (bits != 8 && bits != 16) {
123 error_report("incorrect bit count %d, must be 8 or 16", bits);
124 return -1;
125 }
126
127 if (nchannels != 1 && nchannels != 2) {
128 error_report("incorrect channel count %d, must be 1 or 2",
129 nchannels);
130 return -1;
131 }
132
133 stereo = nchannels == 2;
134 bits16 = bits == 16;
135
136 as.freq = freq;
137 as.nchannels = 1 << stereo;
138 as.fmt = bits16 ? AUDIO_FORMAT_S16 : AUDIO_FORMAT_U8;
139 as.endianness = 0;
140
141 ops.notify = wav_notify;
142 ops.capture = wav_capture;
143 ops.destroy = wav_destroy;
144
145 wav = g_malloc0 (sizeof (*wav));
146
147 shift = bits16 + stereo;
148 hdr[34] = bits16 ? 0x10 : 0x08;
149
150 le_store (hdr + 22, as.nchannels, 2);
151 le_store (hdr + 24, freq, 4);
152 le_store (hdr + 28, freq << shift, 4);
153 le_store (hdr + 32, 1 << shift, 2);
154
155 wav->f = fopen (path, "wb");
156 if (!wav->f) {
157 error_report("Failed to open wave file `%s': %s",
158 path, strerror(errno));
159 g_free (wav);
160 return -1;
161 }
162
163 wav->path = g_strdup (path);
164 wav->bits = bits;
165 wav->nchannels = nchannels;
166 wav->freq = freq;
167
168 if (fwrite (hdr, sizeof (hdr), 1, wav->f) != 1) {
169 error_report("Failed to write header: %s", strerror(errno));
170 goto error_free;
171 }
172
173 cap = AUD_add_capture(state, &as, &ops, wav);
174 if (!cap) {
175 error_report("Failed to add audio capture");
176 goto error_free;
177 }
178
179 wav->cap = cap;
180 s->opaque = wav;
181 s->ops = wav_capture_ops;
182 return 0;
183
184error_free:
185 g_free (wav->path);
186 if (fclose (wav->f)) {
187 error_report("Failed to close wave file: %s", strerror(errno));
188 }
189 g_free (wav);
190 return -1;
191}
192