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 **/ 37typedef struct WAVVoiceOut { 38 HWVoiceOut hw; 39 QEMUFile *f; 40 int64_t old_ticks; 41 void *pcm_buf; 42 int total_samples; 43} WAVVoiceOut; 44 45static 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 58static 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 99static 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. */ 105static 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 114static 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 177static 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 205static 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 224typedef 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 234static int 235le_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 246static int 247wav_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 323Fail: 324 qemu_fclose (wav->f); 325 wav->f = NULL; 326 return -1; 327} 328 329 330static 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 345static 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 392static int wav_in_read (SWVoiceIn *sw, void *buf, int len) 393{ 394 return audio_pcm_sw_read (sw, buf, len); 395} 396 397static 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 **/ 408static void *wav_audio_init (void) 409{ 410 return &conf_out; 411} 412 413static void wav_audio_fini (void *opaque) 414{ 415 (void) opaque; 416 ldebug ("wav_fini"); 417} 418 419static 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 439struct 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 461struct 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