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