|
1 #include "hw/hw.h" |
|
2 #include "console.h" |
|
3 #include "audio.h" |
|
4 |
|
5 typedef struct { |
|
6 QEMUFile *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 |
|
39 if (wav->f) { |
|
40 le_store (rlen, rifflen, 4); |
|
41 le_store (dlen, datalen, 4); |
|
42 |
|
43 qemu_fseek (wav->f, 4, SEEK_SET); |
|
44 qemu_put_buffer (wav->f, rlen, 4); |
|
45 |
|
46 qemu_fseek (wav->f, 32, SEEK_CUR); |
|
47 qemu_put_buffer (wav->f, dlen, 4); |
|
48 qemu_fclose (wav->f); |
|
49 } |
|
50 |
|
51 qemu_free (wav->path); |
|
52 } |
|
53 |
|
54 static void wav_capture (void *opaque, void *buf, int size) |
|
55 { |
|
56 WAVState *wav = opaque; |
|
57 |
|
58 qemu_put_buffer (wav->f, buf, size); |
|
59 wav->bytes += size; |
|
60 } |
|
61 |
|
62 static void wav_capture_destroy (void *opaque) |
|
63 { |
|
64 WAVState *wav = opaque; |
|
65 |
|
66 AUD_del_capture (wav->cap, wav); |
|
67 } |
|
68 |
|
69 static void wav_capture_info (void *opaque) |
|
70 { |
|
71 WAVState *wav = opaque; |
|
72 char *path = wav->path; |
|
73 |
|
74 term_printf ("Capturing audio(%d,%d,%d) to %s: %d bytes\n", |
|
75 wav->freq, wav->bits, wav->nchannels, |
|
76 path ? path : "<not available>", wav->bytes); |
|
77 } |
|
78 |
|
79 static struct capture_ops wav_capture_ops = { |
|
80 .destroy = wav_capture_destroy, |
|
81 .info = wav_capture_info |
|
82 }; |
|
83 |
|
84 int wav_start_capture (CaptureState *s, const char *path, int freq, |
|
85 int bits, int nchannels) |
|
86 { |
|
87 WAVState *wav; |
|
88 uint8_t hdr[] = { |
|
89 0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56, |
|
90 0x45, 0x66, 0x6d, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, |
|
91 0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04, |
|
92 0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00 |
|
93 }; |
|
94 struct audsettings as; |
|
95 struct audio_capture_ops ops; |
|
96 int stereo, bits16, shift; |
|
97 CaptureVoiceOut *cap; |
|
98 |
|
99 if (bits != 8 && bits != 16) { |
|
100 term_printf ("incorrect bit count %d, must be 8 or 16\n", bits); |
|
101 return -1; |
|
102 } |
|
103 |
|
104 if (nchannels != 1 && nchannels != 2) { |
|
105 term_printf ("incorrect channel count %d, must be 1 or 2\n", |
|
106 nchannels); |
|
107 return -1; |
|
108 } |
|
109 |
|
110 stereo = nchannels == 2; |
|
111 bits16 = bits == 16; |
|
112 |
|
113 as.freq = freq; |
|
114 as.nchannels = 1 << stereo; |
|
115 as.fmt = bits16 ? AUD_FMT_S16 : AUD_FMT_U8; |
|
116 as.endianness = 0; |
|
117 |
|
118 ops.notify = wav_notify; |
|
119 ops.capture = wav_capture; |
|
120 ops.destroy = wav_destroy; |
|
121 |
|
122 wav = qemu_mallocz (sizeof (*wav)); |
|
123 if (!wav) { |
|
124 term_printf ("Could not allocate memory for wav capture (%zu bytes)", |
|
125 sizeof (*wav)); |
|
126 return -1; |
|
127 } |
|
128 |
|
129 shift = bits16 + stereo; |
|
130 hdr[34] = bits16 ? 0x10 : 0x08; |
|
131 |
|
132 le_store (hdr + 22, as.nchannels, 2); |
|
133 le_store (hdr + 24, freq, 4); |
|
134 le_store (hdr + 28, freq << shift, 4); |
|
135 le_store (hdr + 32, 1 << shift, 2); |
|
136 |
|
137 wav->f = qemu_fopen (path, "wb"); |
|
138 if (!wav->f) { |
|
139 term_printf ("Failed to open wave file `%s'\nReason: %s\n", |
|
140 path, strerror (errno)); |
|
141 qemu_free (wav); |
|
142 return -1; |
|
143 } |
|
144 |
|
145 wav->path = qemu_strdup (path); |
|
146 wav->bits = bits; |
|
147 wav->nchannels = nchannels; |
|
148 wav->freq = freq; |
|
149 |
|
150 qemu_put_buffer (wav->f, hdr, sizeof (hdr)); |
|
151 |
|
152 cap = AUD_add_capture (NULL, &as, &ops, wav); |
|
153 if (!cap) { |
|
154 term_printf ("Failed to add audio capture\n"); |
|
155 qemu_free (wav->path); |
|
156 qemu_fclose (wav->f); |
|
157 qemu_free (wav); |
|
158 return -1; |
|
159 } |
|
160 |
|
161 wav->cap = cap; |
|
162 s->opaque = wav; |
|
163 s->ops = wav_capture_ops; |
|
164 return 0; |
|
165 } |