185571bc7Sbellard /* 21d14ffa9Sbellard * QEMU WAV audio driver 385571bc7Sbellard * 41d14ffa9Sbellard * Copyright (c) 2004-2005 Vassili Karpov (malc) 585571bc7Sbellard * 685571bc7Sbellard * Permission is hereby granted, free of charge, to any person obtaining a copy 785571bc7Sbellard * of this software and associated documentation files (the "Software"), to deal 885571bc7Sbellard * in the Software without restriction, including without limitation the rights 985571bc7Sbellard * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 1085571bc7Sbellard * copies of the Software, and to permit persons to whom the Software is 1185571bc7Sbellard * furnished to do so, subject to the following conditions: 1285571bc7Sbellard * 1385571bc7Sbellard * The above copyright notice and this permission notice shall be included in 1485571bc7Sbellard * all copies or substantial portions of the Software. 1585571bc7Sbellard * 1685571bc7Sbellard * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1785571bc7Sbellard * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1885571bc7Sbellard * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 1985571bc7Sbellard * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 2085571bc7Sbellard * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 2185571bc7Sbellard * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 2285571bc7Sbellard * THE SOFTWARE. 2385571bc7Sbellard */ 246086a565SPeter Maydell #include "qemu/osdep.h" 2587776ab7SPaolo Bonzini #include "qemu/host-utils.h" 261de7afc9SPaolo Bonzini #include "qemu/timer.h" 2787ecb68bSpbrook #include "audio.h" 2885571bc7Sbellard 291d14ffa9Sbellard #define AUDIO_CAP "wav" 301d14ffa9Sbellard #include "audio_int.h" 31fb065187Sbellard 321d14ffa9Sbellard typedef struct WAVVoiceOut { 331d14ffa9Sbellard HWVoiceOut hw; 3427acf660SJuan Quintela FILE *f; 35fb065187Sbellard int64_t old_ticks; 36fb065187Sbellard void *pcm_buf; 37fb065187Sbellard int total_samples; 381d14ffa9Sbellard } WAVVoiceOut; 3985571bc7Sbellard 40f2dcc6ceSKővágó, Zoltán typedef struct { 411ea879e5Smalc struct audsettings settings; 4285571bc7Sbellard const char *wav_path; 43f2dcc6ceSKővágó, Zoltán } WAVConf; 4485571bc7Sbellard 45bdff253cSmalc static int wav_run_out (HWVoiceOut *hw, int live) 4685571bc7Sbellard { 471d14ffa9Sbellard WAVVoiceOut *wav = (WAVVoiceOut *) hw; 48bdff253cSmalc int rpos, decr, samples; 4985571bc7Sbellard uint8_t *dst; 501ea879e5Smalc struct st_sample *src; 51bc72ad67SAlex Bligh int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 5285571bc7Sbellard int64_t ticks = now - wav->old_ticks; 534f4cc0efSmalc int64_t bytes = 5473bcb24dSRutuja Shah muldiv64(ticks, hw->info.bytes_per_second, NANOSECONDS_PER_SECOND); 5585571bc7Sbellard 561d14ffa9Sbellard if (bytes > INT_MAX) { 571d14ffa9Sbellard samples = INT_MAX >> hw->info.shift; 581d14ffa9Sbellard } 591d14ffa9Sbellard else { 601d14ffa9Sbellard samples = bytes >> hw->info.shift; 611d14ffa9Sbellard } 6285571bc7Sbellard 637372f88dSbellard wav->old_ticks = now; 6485571bc7Sbellard decr = audio_MIN (live, samples); 6585571bc7Sbellard samples = decr; 6685571bc7Sbellard rpos = hw->rpos; 6785571bc7Sbellard while (samples) { 6885571bc7Sbellard int left_till_end_samples = hw->samples - rpos; 6985571bc7Sbellard int convert_samples = audio_MIN (samples, left_till_end_samples); 7085571bc7Sbellard 711d14ffa9Sbellard src = hw->mix_buf + rpos; 721d14ffa9Sbellard dst = advance (wav->pcm_buf, rpos << hw->info.shift); 7385571bc7Sbellard 7485571bc7Sbellard hw->clip (dst, src, convert_samples); 7527acf660SJuan Quintela if (fwrite (dst, convert_samples << hw->info.shift, 1, wav->f) != 1) { 7627acf660SJuan Quintela dolog ("wav_run_out: fwrite of %d bytes failed\nReaons: %s\n", 7727acf660SJuan Quintela convert_samples << hw->info.shift, strerror (errno)); 7827acf660SJuan Quintela } 7985571bc7Sbellard 8085571bc7Sbellard rpos = (rpos + convert_samples) % hw->samples; 8185571bc7Sbellard samples -= convert_samples; 8285571bc7Sbellard wav->total_samples += convert_samples; 8385571bc7Sbellard } 8485571bc7Sbellard 8585571bc7Sbellard hw->rpos = rpos; 861d14ffa9Sbellard return decr; 8785571bc7Sbellard } 8885571bc7Sbellard 891d14ffa9Sbellard static int wav_write_out (SWVoiceOut *sw, void *buf, int len) 9085571bc7Sbellard { 911d14ffa9Sbellard return audio_pcm_sw_write (sw, buf, len); 9285571bc7Sbellard } 9385571bc7Sbellard 9485571bc7Sbellard /* VICE code: Store number as little endian. */ 9585571bc7Sbellard static void le_store (uint8_t *buf, uint32_t val, int len) 9685571bc7Sbellard { 9785571bc7Sbellard int i; 9885571bc7Sbellard for (i = 0; i < len; i++) { 9985571bc7Sbellard buf[i] = (uint8_t) (val & 0xff); 10085571bc7Sbellard val >>= 8; 10185571bc7Sbellard } 10285571bc7Sbellard } 10385571bc7Sbellard 1045706db1dSKővágó, Zoltán static int wav_init_out(HWVoiceOut *hw, struct audsettings *as, 1055706db1dSKővágó, Zoltán void *drv_opaque) 10685571bc7Sbellard { 1071d14ffa9Sbellard WAVVoiceOut *wav = (WAVVoiceOut *) hw; 108c0fe3827Sbellard int bits16 = 0, stereo = 0; 10985571bc7Sbellard uint8_t hdr[] = { 11085571bc7Sbellard 0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56, 11185571bc7Sbellard 0x45, 0x66, 0x6d, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 11285571bc7Sbellard 0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04, 11385571bc7Sbellard 0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00 11485571bc7Sbellard }; 115f2dcc6ceSKővágó, Zoltán WAVConf *conf = drv_opaque; 116f2dcc6ceSKővágó, Zoltán struct audsettings wav_as = conf->settings; 11785571bc7Sbellard 118c0fe3827Sbellard stereo = wav_as.nchannels == 2; 119c0fe3827Sbellard switch (wav_as.fmt) { 120*85bc5852SKővágó, Zoltán case AUDIO_FORMAT_S8: 121*85bc5852SKővágó, Zoltán case AUDIO_FORMAT_U8: 1221d14ffa9Sbellard bits16 = 0; 12385571bc7Sbellard break; 12485571bc7Sbellard 125*85bc5852SKővágó, Zoltán case AUDIO_FORMAT_S16: 126*85bc5852SKővágó, Zoltán case AUDIO_FORMAT_U16: 12785571bc7Sbellard bits16 = 1; 12885571bc7Sbellard break; 129f941aa25Sths 130*85bc5852SKővágó, Zoltán case AUDIO_FORMAT_S32: 131*85bc5852SKővágó, Zoltán case AUDIO_FORMAT_U32: 132f941aa25Sths dolog ("WAVE files can not handle 32bit formats\n"); 133f941aa25Sths return -1; 134*85bc5852SKővágó, Zoltán 135*85bc5852SKővágó, Zoltán default: 136*85bc5852SKővágó, Zoltán abort(); 13785571bc7Sbellard } 13885571bc7Sbellard 13985571bc7Sbellard hdr[34] = bits16 ? 0x10 : 0x08; 140c0fe3827Sbellard 141d929eba5Sbellard wav_as.endianness = 0; 142d929eba5Sbellard audio_pcm_init_info (&hw->info, &wav_as); 143c0fe3827Sbellard 144c0fe3827Sbellard hw->samples = 1024; 145470bcabdSAlistair Francis wav->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift); 1461d14ffa9Sbellard if (!wav->pcm_buf) { 147c0fe3827Sbellard dolog ("Could not allocate buffer (%d bytes)\n", 148c0fe3827Sbellard hw->samples << hw->info.shift); 14985571bc7Sbellard return -1; 1501d14ffa9Sbellard } 15185571bc7Sbellard 1521d14ffa9Sbellard le_store (hdr + 22, hw->info.nchannels, 2); 1531d14ffa9Sbellard le_store (hdr + 24, hw->info.freq, 4); 154c0fe3827Sbellard le_store (hdr + 28, hw->info.freq << (bits16 + stereo), 4); 155c0fe3827Sbellard le_store (hdr + 32, 1 << (bits16 + stereo), 2); 15685571bc7Sbellard 157f2dcc6ceSKővágó, Zoltán wav->f = fopen (conf->wav_path, "wb"); 15885571bc7Sbellard if (!wav->f) { 1591d14ffa9Sbellard dolog ("Failed to open wave file `%s'\nReason: %s\n", 160f2dcc6ceSKővágó, Zoltán conf->wav_path, strerror (errno)); 1617267c094SAnthony Liguori g_free (wav->pcm_buf); 1627372f88dSbellard wav->pcm_buf = NULL; 16385571bc7Sbellard return -1; 16485571bc7Sbellard } 16585571bc7Sbellard 16627acf660SJuan Quintela if (fwrite (hdr, sizeof (hdr), 1, wav->f) != 1) { 16727acf660SJuan Quintela dolog ("wav_init_out: failed to write header\nReason: %s\n", 16827acf660SJuan Quintela strerror(errno)); 16927acf660SJuan Quintela return -1; 17027acf660SJuan Quintela } 17185571bc7Sbellard return 0; 17285571bc7Sbellard } 17385571bc7Sbellard 1741d14ffa9Sbellard static void wav_fini_out (HWVoiceOut *hw) 17585571bc7Sbellard { 1761d14ffa9Sbellard WAVVoiceOut *wav = (WAVVoiceOut *) hw; 17785571bc7Sbellard uint8_t rlen[4]; 17885571bc7Sbellard uint8_t dlen[4]; 17950903530Sbellard uint32_t datalen = wav->total_samples << hw->info.shift; 18050903530Sbellard uint32_t rifflen = datalen + 36; 18185571bc7Sbellard 182c0fe3827Sbellard if (!wav->f) { 18385571bc7Sbellard return; 1841d14ffa9Sbellard } 18585571bc7Sbellard 18685571bc7Sbellard le_store (rlen, rifflen, 4); 18785571bc7Sbellard le_store (dlen, datalen, 4); 18885571bc7Sbellard 18927acf660SJuan Quintela if (fseek (wav->f, 4, SEEK_SET)) { 19027acf660SJuan Quintela dolog ("wav_fini_out: fseek to rlen failed\nReason: %s\n", 19127acf660SJuan Quintela strerror(errno)); 19227acf660SJuan Quintela goto doclose; 19327acf660SJuan Quintela } 19427acf660SJuan Quintela if (fwrite (rlen, 4, 1, wav->f) != 1) { 19527acf660SJuan Quintela dolog ("wav_fini_out: failed to write rlen\nReason: %s\n", 19627acf660SJuan Quintela strerror (errno)); 19727acf660SJuan Quintela goto doclose; 19827acf660SJuan Quintela } 19927acf660SJuan Quintela if (fseek (wav->f, 32, SEEK_CUR)) { 20027acf660SJuan Quintela dolog ("wav_fini_out: fseek to dlen failed\nReason: %s\n", 20127acf660SJuan Quintela strerror (errno)); 20227acf660SJuan Quintela goto doclose; 20327acf660SJuan Quintela } 20427acf660SJuan Quintela if (fwrite (dlen, 4, 1, wav->f) != 1) { 20527acf660SJuan Quintela dolog ("wav_fini_out: failed to write dlen\nReaons: %s\n", 20627acf660SJuan Quintela strerror (errno)); 20727acf660SJuan Quintela goto doclose; 20827acf660SJuan Quintela } 20985571bc7Sbellard 21027acf660SJuan Quintela doclose: 21127acf660SJuan Quintela if (fclose (wav->f)) { 21227acf660SJuan Quintela dolog ("wav_fini_out: fclose %p failed\nReason: %s\n", 21327acf660SJuan Quintela wav->f, strerror (errno)); 21427acf660SJuan Quintela } 21585571bc7Sbellard wav->f = NULL; 2167372f88dSbellard 2177267c094SAnthony Liguori g_free (wav->pcm_buf); 2187372f88dSbellard wav->pcm_buf = NULL; 21985571bc7Sbellard } 22085571bc7Sbellard 2211d14ffa9Sbellard static int wav_ctl_out (HWVoiceOut *hw, int cmd, ...) 22285571bc7Sbellard { 22385571bc7Sbellard (void) hw; 22485571bc7Sbellard (void) cmd; 22585571bc7Sbellard return 0; 22685571bc7Sbellard } 22785571bc7Sbellard 228f2dcc6ceSKővágó, Zoltán static WAVConf glob_conf = { 229f2dcc6ceSKővágó, Zoltán .settings.freq = 44100, 230f2dcc6ceSKővágó, Zoltán .settings.nchannels = 2, 231*85bc5852SKővágó, Zoltán .settings.fmt = AUDIO_FORMAT_S16, 232f2dcc6ceSKővágó, Zoltán .wav_path = "qemu.wav" 233f2dcc6ceSKővágó, Zoltán }; 234f2dcc6ceSKővágó, Zoltán 23585571bc7Sbellard static void *wav_audio_init (void) 23685571bc7Sbellard { 237f2dcc6ceSKővágó, Zoltán WAVConf *conf = g_malloc(sizeof(WAVConf)); 238f2dcc6ceSKővágó, Zoltán *conf = glob_conf; 239f2dcc6ceSKővágó, Zoltán return conf; 24085571bc7Sbellard } 24185571bc7Sbellard 24285571bc7Sbellard static void wav_audio_fini (void *opaque) 24385571bc7Sbellard { 24485571bc7Sbellard ldebug ("wav_fini"); 245f2dcc6ceSKővágó, Zoltán g_free(opaque); 24685571bc7Sbellard } 24785571bc7Sbellard 2488869defeSblueswir1 static struct audio_option wav_options[] = { 24998f9f48cSmalc { 25098f9f48cSmalc .name = "FREQUENCY", 2512700efa3SJuan Quintela .tag = AUD_OPT_INT, 252f2dcc6ceSKővágó, Zoltán .valp = &glob_conf.settings.freq, 25398f9f48cSmalc .descr = "Frequency" 25498f9f48cSmalc }, 25598f9f48cSmalc { 25698f9f48cSmalc .name = "FORMAT", 2572700efa3SJuan Quintela .tag = AUD_OPT_FMT, 258f2dcc6ceSKővágó, Zoltán .valp = &glob_conf.settings.fmt, 25998f9f48cSmalc .descr = "Format" 26098f9f48cSmalc }, 26198f9f48cSmalc { 26298f9f48cSmalc .name = "DAC_FIXED_CHANNELS", 2632700efa3SJuan Quintela .tag = AUD_OPT_INT, 264f2dcc6ceSKővágó, Zoltán .valp = &glob_conf.settings.nchannels, 26598f9f48cSmalc .descr = "Number of channels (1 - mono, 2 - stereo)" 26698f9f48cSmalc }, 26798f9f48cSmalc { 26898f9f48cSmalc .name = "PATH", 2692700efa3SJuan Quintela .tag = AUD_OPT_STR, 270f2dcc6ceSKővágó, Zoltán .valp = &glob_conf.wav_path, 27198f9f48cSmalc .descr = "Path to wave file" 27298f9f48cSmalc }, 2732700efa3SJuan Quintela { /* End of list */ } 27485571bc7Sbellard }; 27585571bc7Sbellard 27635f4b58cSblueswir1 static struct audio_pcm_ops wav_pcm_ops = { 2771dd3e4d1SJuan Quintela .init_out = wav_init_out, 2781dd3e4d1SJuan Quintela .fini_out = wav_fini_out, 2791dd3e4d1SJuan Quintela .run_out = wav_run_out, 2801dd3e4d1SJuan Quintela .write = wav_write_out, 2811dd3e4d1SJuan Quintela .ctl_out = wav_ctl_out, 2821d14ffa9Sbellard }; 2831d14ffa9Sbellard 284d3893a39SGerd Hoffmann static struct audio_driver wav_audio_driver = { 285bee37f32SJuan Quintela .name = "wav", 286bee37f32SJuan Quintela .descr = "WAV renderer http://wikipedia.org/wiki/WAV", 287bee37f32SJuan Quintela .options = wav_options, 288bee37f32SJuan Quintela .init = wav_audio_init, 289bee37f32SJuan Quintela .fini = wav_audio_fini, 290bee37f32SJuan Quintela .pcm_ops = &wav_pcm_ops, 291bee37f32SJuan Quintela .can_be_default = 0, 292bee37f32SJuan Quintela .max_voices_out = 1, 293bee37f32SJuan Quintela .max_voices_in = 0, 294bee37f32SJuan Quintela .voice_size_out = sizeof (WAVVoiceOut), 295bee37f32SJuan Quintela .voice_size_in = 0 29685571bc7Sbellard }; 297d3893a39SGerd Hoffmann 298d3893a39SGerd Hoffmann static void register_audio_wav(void) 299d3893a39SGerd Hoffmann { 300d3893a39SGerd Hoffmann audio_driver_register(&wav_audio_driver); 301d3893a39SGerd Hoffmann } 302d3893a39SGerd Hoffmann type_init(register_audio_wav); 303