1#include "hw/hw.h"
2#include "monitor/monitor.h"
3#include "audio.h"
4
5typedef 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
16static 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
25static void wav_notify (void *opaque, audcnotification_e cmd)
26{
27 (void) opaque;
28 (void) cmd;
29}
30
31static 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
74static 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
85static void wav_capture_destroy (void *opaque)
86{
87 WAVState *wav = opaque;
88
89 AUD_del_capture (wav->cap, wav);
90}
91
92static 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
102static struct capture_ops wav_capture_ops = {
103 .destroy = wav_capture_destroy,
104 .info = wav_capture_info
105};
106
107int 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
186error_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