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