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 */ 2487ecb68bSpbrook #include "hw/hw.h" 2587ecb68bSpbrook #include "qemu-timer.h" 2687ecb68bSpbrook #include "audio.h" 2785571bc7Sbellard 281d14ffa9Sbellard #define AUDIO_CAP "wav" 291d14ffa9Sbellard #include "audio_int.h" 30fb065187Sbellard 311d14ffa9Sbellard typedef struct WAVVoiceOut { 321d14ffa9Sbellard HWVoiceOut hw; 33fb065187Sbellard QEMUFile *f; 34fb065187Sbellard int64_t old_ticks; 35fb065187Sbellard void *pcm_buf; 36fb065187Sbellard int total_samples; 371d14ffa9Sbellard } WAVVoiceOut; 3885571bc7Sbellard 3985571bc7Sbellard static struct { 401ea879e5Smalc struct audsettings settings; 4185571bc7Sbellard const char *wav_path; 4285571bc7Sbellard } conf = { 431a40d5e2SJuan Quintela .settings.freq = 44100, 441a40d5e2SJuan Quintela .settings.nchannels = 2, 451a40d5e2SJuan Quintela .settings.fmt = AUD_FMT_S16, 461a40d5e2SJuan Quintela .wav_path = "qemu.wav" 4785571bc7Sbellard }; 4885571bc7Sbellard 49*bdff253cSmalc static int wav_run_out (HWVoiceOut *hw, int live) 5085571bc7Sbellard { 511d14ffa9Sbellard WAVVoiceOut *wav = (WAVVoiceOut *) hw; 52*bdff253cSmalc int rpos, decr, samples; 5385571bc7Sbellard uint8_t *dst; 541ea879e5Smalc struct st_sample *src; 5585571bc7Sbellard int64_t now = qemu_get_clock (vm_clock); 5685571bc7Sbellard int64_t ticks = now - wav->old_ticks; 574f4cc0efSmalc int64_t bytes = 584f4cc0efSmalc muldiv64 (ticks, hw->info.bytes_per_second, get_ticks_per_sec ()); 5985571bc7Sbellard 601d14ffa9Sbellard if (bytes > INT_MAX) { 611d14ffa9Sbellard samples = INT_MAX >> hw->info.shift; 621d14ffa9Sbellard } 631d14ffa9Sbellard else { 641d14ffa9Sbellard samples = bytes >> hw->info.shift; 651d14ffa9Sbellard } 6685571bc7Sbellard 677372f88dSbellard wav->old_ticks = now; 6885571bc7Sbellard decr = audio_MIN (live, samples); 6985571bc7Sbellard samples = decr; 7085571bc7Sbellard rpos = hw->rpos; 7185571bc7Sbellard while (samples) { 7285571bc7Sbellard int left_till_end_samples = hw->samples - rpos; 7385571bc7Sbellard int convert_samples = audio_MIN (samples, left_till_end_samples); 7485571bc7Sbellard 751d14ffa9Sbellard src = hw->mix_buf + rpos; 761d14ffa9Sbellard dst = advance (wav->pcm_buf, rpos << hw->info.shift); 7785571bc7Sbellard 7885571bc7Sbellard hw->clip (dst, src, convert_samples); 791d14ffa9Sbellard qemu_put_buffer (wav->f, dst, convert_samples << hw->info.shift); 8085571bc7Sbellard 8185571bc7Sbellard rpos = (rpos + convert_samples) % hw->samples; 8285571bc7Sbellard samples -= convert_samples; 8385571bc7Sbellard wav->total_samples += convert_samples; 8485571bc7Sbellard } 8585571bc7Sbellard 8685571bc7Sbellard hw->rpos = rpos; 871d14ffa9Sbellard return decr; 8885571bc7Sbellard } 8985571bc7Sbellard 901d14ffa9Sbellard static int wav_write_out (SWVoiceOut *sw, void *buf, int len) 9185571bc7Sbellard { 921d14ffa9Sbellard return audio_pcm_sw_write (sw, buf, len); 9385571bc7Sbellard } 9485571bc7Sbellard 9585571bc7Sbellard /* VICE code: Store number as little endian. */ 9685571bc7Sbellard static void le_store (uint8_t *buf, uint32_t val, int len) 9785571bc7Sbellard { 9885571bc7Sbellard int i; 9985571bc7Sbellard for (i = 0; i < len; i++) { 10085571bc7Sbellard buf[i] = (uint8_t) (val & 0xff); 10185571bc7Sbellard val >>= 8; 10285571bc7Sbellard } 10385571bc7Sbellard } 10485571bc7Sbellard 1051ea879e5Smalc static int wav_init_out (HWVoiceOut *hw, struct audsettings *as) 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 }; 1151ea879e5Smalc struct audsettings wav_as = conf.settings; 11685571bc7Sbellard 117c0fe3827Sbellard (void) as; 1181d14ffa9Sbellard 119c0fe3827Sbellard stereo = wav_as.nchannels == 2; 120c0fe3827Sbellard switch (wav_as.fmt) { 12185571bc7Sbellard case AUD_FMT_S8: 12285571bc7Sbellard case AUD_FMT_U8: 1231d14ffa9Sbellard bits16 = 0; 12485571bc7Sbellard break; 12585571bc7Sbellard 12685571bc7Sbellard case AUD_FMT_S16: 12785571bc7Sbellard case AUD_FMT_U16: 12885571bc7Sbellard bits16 = 1; 12985571bc7Sbellard break; 130f941aa25Sths 131f941aa25Sths case AUD_FMT_S32: 132f941aa25Sths case AUD_FMT_U32: 133f941aa25Sths dolog ("WAVE files can not handle 32bit formats\n"); 134f941aa25Sths return -1; 13585571bc7Sbellard } 13685571bc7Sbellard 13785571bc7Sbellard hdr[34] = bits16 ? 0x10 : 0x08; 138c0fe3827Sbellard 139d929eba5Sbellard wav_as.endianness = 0; 140d929eba5Sbellard audio_pcm_init_info (&hw->info, &wav_as); 141c0fe3827Sbellard 142c0fe3827Sbellard hw->samples = 1024; 143c0fe3827Sbellard wav->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift); 1441d14ffa9Sbellard if (!wav->pcm_buf) { 145c0fe3827Sbellard dolog ("Could not allocate buffer (%d bytes)\n", 146c0fe3827Sbellard hw->samples << hw->info.shift); 14785571bc7Sbellard return -1; 1481d14ffa9Sbellard } 14985571bc7Sbellard 1501d14ffa9Sbellard le_store (hdr + 22, hw->info.nchannels, 2); 1511d14ffa9Sbellard le_store (hdr + 24, hw->info.freq, 4); 152c0fe3827Sbellard le_store (hdr + 28, hw->info.freq << (bits16 + stereo), 4); 153c0fe3827Sbellard le_store (hdr + 32, 1 << (bits16 + stereo), 2); 15485571bc7Sbellard 155e70332b3Sbellard wav->f = qemu_fopen (conf.wav_path, "wb"); 15685571bc7Sbellard if (!wav->f) { 1571d14ffa9Sbellard dolog ("Failed to open wave file `%s'\nReason: %s\n", 15885571bc7Sbellard conf.wav_path, strerror (errno)); 1597372f88dSbellard qemu_free (wav->pcm_buf); 1607372f88dSbellard wav->pcm_buf = NULL; 16185571bc7Sbellard return -1; 16285571bc7Sbellard } 16385571bc7Sbellard 16485571bc7Sbellard qemu_put_buffer (wav->f, hdr, sizeof (hdr)); 16585571bc7Sbellard return 0; 16685571bc7Sbellard } 16785571bc7Sbellard 1681d14ffa9Sbellard static void wav_fini_out (HWVoiceOut *hw) 16985571bc7Sbellard { 1701d14ffa9Sbellard WAVVoiceOut *wav = (WAVVoiceOut *) hw; 17185571bc7Sbellard uint8_t rlen[4]; 17285571bc7Sbellard uint8_t dlen[4]; 17350903530Sbellard uint32_t datalen = wav->total_samples << hw->info.shift; 17450903530Sbellard uint32_t rifflen = datalen + 36; 17585571bc7Sbellard 176c0fe3827Sbellard if (!wav->f) { 17785571bc7Sbellard return; 1781d14ffa9Sbellard } 17985571bc7Sbellard 18085571bc7Sbellard le_store (rlen, rifflen, 4); 18185571bc7Sbellard le_store (dlen, datalen, 4); 18285571bc7Sbellard 18385571bc7Sbellard qemu_fseek (wav->f, 4, SEEK_SET); 18485571bc7Sbellard qemu_put_buffer (wav->f, rlen, 4); 18585571bc7Sbellard 18685571bc7Sbellard qemu_fseek (wav->f, 32, SEEK_CUR); 18785571bc7Sbellard qemu_put_buffer (wav->f, dlen, 4); 18885571bc7Sbellard 189e70332b3Sbellard qemu_fclose (wav->f); 19085571bc7Sbellard wav->f = NULL; 1917372f88dSbellard 1927372f88dSbellard qemu_free (wav->pcm_buf); 1937372f88dSbellard wav->pcm_buf = NULL; 19485571bc7Sbellard } 19585571bc7Sbellard 1961d14ffa9Sbellard static int wav_ctl_out (HWVoiceOut *hw, int cmd, ...) 19785571bc7Sbellard { 19885571bc7Sbellard (void) hw; 19985571bc7Sbellard (void) cmd; 20085571bc7Sbellard return 0; 20185571bc7Sbellard } 20285571bc7Sbellard 20385571bc7Sbellard static void *wav_audio_init (void) 20485571bc7Sbellard { 20585571bc7Sbellard return &conf; 20685571bc7Sbellard } 20785571bc7Sbellard 20885571bc7Sbellard static void wav_audio_fini (void *opaque) 20985571bc7Sbellard { 2101d14ffa9Sbellard (void) opaque; 21185571bc7Sbellard ldebug ("wav_fini"); 21285571bc7Sbellard } 21385571bc7Sbellard 2148869defeSblueswir1 static struct audio_option wav_options[] = { 21598f9f48cSmalc { 21698f9f48cSmalc .name = "FREQUENCY", 2172700efa3SJuan Quintela .tag = AUD_OPT_INT, 2182700efa3SJuan Quintela .valp = &conf.settings.freq, 21998f9f48cSmalc .descr = "Frequency" 22098f9f48cSmalc }, 22198f9f48cSmalc { 22298f9f48cSmalc .name = "FORMAT", 2232700efa3SJuan Quintela .tag = AUD_OPT_FMT, 2242700efa3SJuan Quintela .valp = &conf.settings.fmt, 22598f9f48cSmalc .descr = "Format" 22698f9f48cSmalc }, 22798f9f48cSmalc { 22898f9f48cSmalc .name = "DAC_FIXED_CHANNELS", 2292700efa3SJuan Quintela .tag = AUD_OPT_INT, 2302700efa3SJuan Quintela .valp = &conf.settings.nchannels, 23198f9f48cSmalc .descr = "Number of channels (1 - mono, 2 - stereo)" 23298f9f48cSmalc }, 23398f9f48cSmalc { 23498f9f48cSmalc .name = "PATH", 2352700efa3SJuan Quintela .tag = AUD_OPT_STR, 2362700efa3SJuan Quintela .valp = &conf.wav_path, 23798f9f48cSmalc .descr = "Path to wave file" 23898f9f48cSmalc }, 2392700efa3SJuan Quintela { /* End of list */ } 24085571bc7Sbellard }; 24185571bc7Sbellard 24235f4b58cSblueswir1 static struct audio_pcm_ops wav_pcm_ops = { 2431dd3e4d1SJuan Quintela .init_out = wav_init_out, 2441dd3e4d1SJuan Quintela .fini_out = wav_fini_out, 2451dd3e4d1SJuan Quintela .run_out = wav_run_out, 2461dd3e4d1SJuan Quintela .write = wav_write_out, 2471dd3e4d1SJuan Quintela .ctl_out = wav_ctl_out, 2481d14ffa9Sbellard }; 2491d14ffa9Sbellard 2501d14ffa9Sbellard struct audio_driver wav_audio_driver = { 251bee37f32SJuan Quintela .name = "wav", 252bee37f32SJuan Quintela .descr = "WAV renderer http://wikipedia.org/wiki/WAV", 253bee37f32SJuan Quintela .options = wav_options, 254bee37f32SJuan Quintela .init = wav_audio_init, 255bee37f32SJuan Quintela .fini = wav_audio_fini, 256bee37f32SJuan Quintela .pcm_ops = &wav_pcm_ops, 257bee37f32SJuan Quintela .can_be_default = 0, 258bee37f32SJuan Quintela .max_voices_out = 1, 259bee37f32SJuan Quintela .max_voices_in = 0, 260bee37f32SJuan Quintela .voice_size_out = sizeof (WAVVoiceOut), 261bee37f32SJuan Quintela .voice_size_in = 0 26285571bc7Sbellard }; 263