1 /*
2 * QEMU WAV audio driver
3 *
4 * Copyright (c) 2007 The Android Open Source Project
5 * Copyright (c) 2004-2005 Vassili Karpov (malc)
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a copy
8 * of this software and associated documentation files (the "Software"), to deal
9 * in the Software without restriction, including without limitation the rights
10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 * copies of the Software, and to permit persons to whom the Software is
12 * furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 * THE SOFTWARE.
24 */
25 #define AUDIO_CAP "wav"
26 #include "qemu-timer.h"
27 #include "audio_int.h"
28 #include "qemu_file.h"
29
30 #define WAV_AUDIO_IN 1
31
32 /** VOICE OUT (Saving to a .WAV file)
33 **/
34 typedef struct WAVVoiceOut {
35 HWVoiceOut hw;
36 QEMUFile *f;
37 int64_t old_ticks;
38 void *pcm_buf;
39 int total_samples;
40 } WAVVoiceOut;
41
42 static struct {
43 audsettings_t settings;
44 const char *wav_path;
45 } conf_out = {
46 {
47 44100,
48 2,
49 AUD_FMT_S16,
50 0
51 },
52 "qemu.wav"
53 };
54
wav_out_run(HWVoiceOut * hw)55 static int wav_out_run (HWVoiceOut *hw)
56 {
57 WAVVoiceOut *wav = (WAVVoiceOut *) hw;
58 int rpos, live, decr, samples;
59 uint8_t *dst;
60 st_sample_t *src;
61 int64_t now = qemu_get_clock (vm_clock);
62 int64_t ticks = now - wav->old_ticks;
63 int64_t bytes = (ticks * hw->info.bytes_per_second) / ticks_per_sec;
64
65 if (bytes > INT_MAX) {
66 samples = INT_MAX >> hw->info.shift;
67 }
68 else {
69 samples = bytes >> hw->info.shift;
70 }
71
72 live = audio_pcm_hw_get_live_out (hw);
73 if (!live) {
74 return 0;
75 }
76
77 wav->old_ticks = now;
78 decr = audio_MIN (live, samples);
79 samples = decr;
80 rpos = hw->rpos;
81 while (samples) {
82 int left_till_end_samples = hw->samples - rpos;
83 int convert_samples = audio_MIN (samples, left_till_end_samples);
84
85 src = hw->mix_buf + rpos;
86 dst = advance (wav->pcm_buf, rpos << hw->info.shift);
87
88 hw->clip (dst, src, convert_samples);
89 qemu_put_buffer (wav->f, dst, convert_samples << hw->info.shift);
90
91 rpos = (rpos + convert_samples) % hw->samples;
92 samples -= convert_samples;
93 wav->total_samples += convert_samples;
94 }
95
96 hw->rpos = rpos;
97 return decr;
98 }
99
wav_out_write(SWVoiceOut * sw,void * buf,int len)100 static int wav_out_write (SWVoiceOut *sw, void *buf, int len)
101 {
102 return audio_pcm_sw_write (sw, buf, len);
103 }
104
105 /* VICE code: Store number as little endian. */
le_store(uint8_t * buf,uint32_t val,int len)106 static void le_store (uint8_t *buf, uint32_t val, int len)
107 {
108 int i;
109 for (i = 0; i < len; i++) {
110 buf[i] = (uint8_t) (val & 0xff);
111 val >>= 8;
112 }
113 }
114
wav_out_init(HWVoiceOut * hw,audsettings_t * as)115 static int wav_out_init (HWVoiceOut *hw, audsettings_t *as)
116 {
117 WAVVoiceOut *wav = (WAVVoiceOut *) hw;
118 int bits16 = 0, stereo = 0;
119 uint8_t hdr[] = {
120 0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56,
121 0x45, 0x66, 0x6d, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00,
122 0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04,
123 0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00
124 };
125 audsettings_t wav_as = conf_out.settings;
126
127 (void) as;
128
129 stereo = wav_as.nchannels == 2;
130 switch (wav_as.fmt) {
131 case AUD_FMT_S8:
132 case AUD_FMT_U8:
133 bits16 = 0;
134 break;
135
136 case AUD_FMT_S16:
137 case AUD_FMT_U16:
138 bits16 = 1;
139 break;
140
141 case AUD_FMT_S32:
142 case AUD_FMT_U32:
143 dolog ("WAVE files can not handle 32bit formats\n");
144 return -1;
145 }
146
147 hdr[34] = bits16 ? 0x10 : 0x08;
148
149 wav_as.endianness = 0;
150 audio_pcm_init_info (&hw->info, &wav_as);
151
152 hw->samples = 1024;
153 wav->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
154 if (!wav->pcm_buf) {
155 dolog ("Could not allocate buffer (%d bytes)\n",
156 hw->samples << hw->info.shift);
157 return -1;
158 }
159
160 le_store (hdr + 22, hw->info.nchannels, 2);
161 le_store (hdr + 24, hw->info.freq, 4);
162 le_store (hdr + 28, hw->info.freq << (bits16 + stereo), 4);
163 le_store (hdr + 32, 1 << (bits16 + stereo), 2);
164
165 wav->f = qemu_fopen (conf_out.wav_path, "wb");
166 if (!wav->f) {
167 dolog ("Failed to open wave file `%s'\nReason: %s\n",
168 conf_out.wav_path, strerror (errno));
169 qemu_free (wav->pcm_buf);
170 wav->pcm_buf = NULL;
171 return -1;
172 }
173
174 qemu_put_buffer (wav->f, hdr, sizeof (hdr));
175 return 0;
176 }
177
wav_out_fini(HWVoiceOut * hw)178 static void wav_out_fini (HWVoiceOut *hw)
179 {
180 WAVVoiceOut *wav = (WAVVoiceOut *) hw;
181 uint8_t rlen[4];
182 uint8_t dlen[4];
183 uint32_t datalen = wav->total_samples << hw->info.shift;
184 uint32_t rifflen = datalen + 36;
185
186 if (!wav->f) {
187 return;
188 }
189
190 le_store (rlen, rifflen, 4);
191 le_store (dlen, datalen, 4);
192
193 qemu_fseek (wav->f, 4, SEEK_SET);
194 qemu_put_buffer (wav->f, rlen, 4);
195
196 qemu_fseek (wav->f, 32, SEEK_CUR);
197 qemu_put_buffer (wav->f, dlen, 4);
198
199 qemu_fclose (wav->f);
200 wav->f = NULL;
201
202 qemu_free (wav->pcm_buf);
203 wav->pcm_buf = NULL;
204 }
205
wav_out_ctl(HWVoiceOut * hw,int cmd,...)206 static int wav_out_ctl (HWVoiceOut *hw, int cmd, ...)
207 {
208 (void) hw;
209 (void) cmd;
210 return 0;
211 }
212
213
214 #if WAV_AUDIO_IN
215
216 /** WAV IN (Reading from a .WAV file)
217 **/
218
219 static struct {
220 const char *wav_path;
221 } conf_in = {
222 "qemu.wav"
223 };
224
225 typedef struct WAVVoiceIn {
226 HWVoiceIn hw;
227 QEMUFile* f;
228 int64_t old_ticks;
229 void* pcm_buf;
230 int total_samples;
231 int total_size;
232 } WAVVoiceIn;
233
234
235 static int
le_read(const uint8_t * p,int size)236 le_read( const uint8_t* p, int size ) {
237 int shift = 0;
238 int result = 0;
239 for ( ; size > 0; size-- ) {
240 result = result | (p[0] << shift);
241 p += 1;
242 shift += 8;
243 }
244 return result;
245 }
246
247 static int
wav_in_init(HWVoiceIn * hw,audsettings_t * as)248 wav_in_init (HWVoiceIn *hw, audsettings_t *as)
249 {
250 WAVVoiceIn* wav = (WAVVoiceIn *) hw;
251 const char* path = conf_in.wav_path;
252 uint8_t hdr[44];
253 audsettings_t wav_as = *as;
254 int nchannels, freq, format, bits;
255
256 wav->f = qemu_fopen (path, "rb");
257 if (wav->f == NULL) {
258 dolog("Failed to open wave file '%s'\nReason: %s\n", path,
259 strerror(errno));
260 return -1;
261 }
262
263 if (qemu_get_buffer (wav->f, hdr, sizeof(hdr)) != (int)sizeof(hdr)) {
264 dolog("File '%s' to be a .wav file\n", path);
265 goto Fail;
266 }
267
268 /* check that this is a wave file */
269 if ( hdr[0] != 'R' || hdr[1] != 'I' || hdr[2] != 'F' || hdr[3] != 'F' ||
270 hdr[8] != 'W' || hdr[9] != 'A' || hdr[10]!= 'V' || hdr[11]!= 'E' ||
271 hdr[12]!= 'f' || hdr[13]!= 'm' || hdr[14]!= 't' || hdr[15]!= ' ' ||
272 hdr[40]!= 'd' || hdr[41]!= 'a' || hdr[42]!= 't' || hdr[43]!= 'a') {
273 dolog("File '%s' is not a valid .wav file\n", path);
274 goto Fail;
275 }
276
277 nchannels = le_read( hdr+22, 2 );
278 freq = le_read( hdr+24, 4 );
279 format = le_read( hdr+32, 2 );
280 bits = le_read( hdr+34, 2 );
281
282 wav->total_size = le_read( hdr+40, 4 );
283
284 /* perform some sainty checks */
285 switch (nchannels) {
286 case 1:
287 case 2: break;
288 default:
289 dolog("unsupported number of channels (%d) in '%s'\n",
290 nchannels, path);
291 goto Fail;
292 }
293
294 switch (format) {
295 case 1:
296 case 2:
297 case 4: break;
298 default:
299 dolog("unsupported bytes per sample (%d) in '%s'\n",
300 format, path);
301 goto Fail;
302 }
303
304 if (format*8/nchannels != bits) {
305 dolog("invalid bits per sample (%d, expected %d) in '%s'\n",
306 bits, format*8/nchannels, path);
307 goto Fail;
308 }
309
310 wav_as.nchannels = nchannels;
311 wav_as.fmt = (bits == 8) ? AUD_FMT_U8 : AUD_FMT_S16;
312 wav_as.freq = freq;
313 wav_as.endianness = 0; /* always little endian */
314
315 audio_pcm_init_info (&hw->info, &wav_as);
316
317 hw->samples = 1024;
318 wav->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
319 if (!wav->pcm_buf) {
320 goto Fail;
321 }
322 return 0;
323
324 Fail:
325 qemu_fclose (wav->f);
326 wav->f = NULL;
327 return -1;
328 }
329
330
wav_in_fini(HWVoiceIn * hw)331 static void wav_in_fini (HWVoiceIn *hw)
332 {
333 WAVVoiceIn *wav = (WAVVoiceIn *) hw;
334
335 if (!wav->f) {
336 return;
337 }
338
339 qemu_fclose (wav->f);
340 wav->f = NULL;
341
342 qemu_free (wav->pcm_buf);
343 wav->pcm_buf = NULL;
344 }
345
wav_in_run(HWVoiceIn * hw)346 static int wav_in_run (HWVoiceIn *hw)
347 {
348 WAVVoiceIn* wav = (WAVVoiceIn *) hw;
349 int wpos, live, decr, samples;
350 uint8_t* src;
351 st_sample_t* dst;
352
353 int64_t now = qemu_get_clock (vm_clock);
354 int64_t ticks = now - wav->old_ticks;
355 int64_t bytes = (ticks * hw->info.bytes_per_second) / ticks_per_sec;
356
357 if (bytes > INT_MAX) {
358 samples = INT_MAX >> hw->info.shift;
359 }
360 else {
361 samples = bytes >> hw->info.shift;
362 }
363
364 live = audio_pcm_hw_get_live_in (hw);
365 if (!live) {
366 return 0;
367 }
368
369 wav->old_ticks = now;
370
371 decr = audio_MIN (live, samples);
372 samples = decr;
373 wpos = hw->wpos;
374 while (samples) {
375 int left_till_end_samples = hw->samples - wpos;
376 int convert_samples = audio_MIN (samples, left_till_end_samples);
377
378 dst = hw->conv_buf + wpos;
379 src = advance (wav->pcm_buf, wpos << hw->info.shift);
380
381 qemu_get_buffer (wav->f, src, convert_samples << hw->info.shift);
382 memcpy (dst, src, convert_samples << hw->info.shift);
383
384 wpos = (wpos + convert_samples) % hw->samples;
385 samples -= convert_samples;
386 wav->total_samples += convert_samples;
387 }
388
389 hw->wpos = wpos;
390 return decr;
391 }
392
wav_in_read(SWVoiceIn * sw,void * buf,int len)393 static int wav_in_read (SWVoiceIn *sw, void *buf, int len)
394 {
395 return audio_pcm_sw_read (sw, buf, len);
396 }
397
wav_in_ctl(HWVoiceIn * hw,int cmd,...)398 static int wav_in_ctl (HWVoiceIn *hw, int cmd, ...)
399 {
400 (void) hw;
401 (void) cmd;
402 return 0;
403 }
404
405 #endif /* WAV_AUDIO_IN */
406
407 /** COMMON CODE
408 **/
wav_audio_init(void)409 static void *wav_audio_init (void)
410 {
411 return &conf_out;
412 }
413
wav_audio_fini(void * opaque)414 static void wav_audio_fini (void *opaque)
415 {
416 (void) opaque;
417 ldebug ("wav_fini");
418 }
419
420 struct audio_option wav_options[] = {
421 {"FREQUENCY", AUD_OPT_INT, &conf_out.settings.freq,
422 "Frequency", NULL, 0},
423
424 {"FORMAT", AUD_OPT_FMT, &conf_out.settings.fmt,
425 "Format", NULL, 0},
426
427 {"DAC_FIXED_CHANNELS", AUD_OPT_INT, &conf_out.settings.nchannels,
428 "Number of channels (1 - mono, 2 - stereo)", NULL, 0},
429
430 {"PATH", AUD_OPT_STR, &conf_out.wav_path,
431 "Path to output .wav file", NULL, 0},
432
433 #if WAV_AUDIO_IN
434 {"IN_PATH", AUD_OPT_STR, &conf_in.wav_path,
435 "Path to input .wav file", NULL, 0},
436 #endif
437 {NULL, 0, NULL, NULL, NULL, 0}
438 };
439
440 struct audio_pcm_ops wav_pcm_ops = {
441 wav_out_init,
442 wav_out_fini,
443 wav_out_run,
444 wav_out_write,
445 wav_out_ctl,
446
447 #if WAV_AUDIO_IN
448 wav_in_init,
449 wav_in_fini,
450 wav_in_run,
451 wav_in_read,
452 wav_in_ctl
453 #else
454 NULL,
455 NULL,
456 NULL,
457 NULL,
458 NULL
459 #endif
460 };
461
462 struct audio_driver wav_audio_driver = {
463 INIT_FIELD (name = ) "wav",
464 INIT_FIELD (descr = )
465 "WAV file read/write (www.wikipedia.org/wiki/WAV)",
466 INIT_FIELD (options = ) wav_options,
467 INIT_FIELD (init = ) wav_audio_init,
468 INIT_FIELD (fini = ) wav_audio_fini,
469 INIT_FIELD (pcm_ops = ) &wav_pcm_ops,
470 INIT_FIELD (can_be_default = ) 0,
471 #if WAV_AUDIO_IN
472 INIT_FIELD (max_voices_in = ) 1,
473 INIT_FIELD (max_voices_out = ) 1,
474 INIT_FIELD (voice_size_out = ) sizeof (WAVVoiceOut),
475 INIT_FIELD (voice_size_in = ) sizeof (WAVVoiceIn)
476 #else
477 INIT_FIELD (max_voices_out = ) 1,
478 INIT_FIELD (max_voices_in = ) 0,
479 INIT_FIELD (voice_size_out = ) sizeof (WAVVoiceOut),
480 INIT_FIELD (voice_size_in = ) 0
481 #endif
482 };
483