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 */ 2485571bc7Sbellard #include "vl.h" 2585571bc7Sbellard 261d14ffa9Sbellard #define AUDIO_CAP "wav" 271d14ffa9Sbellard #include "audio_int.h" 28fb065187Sbellard 291d14ffa9Sbellard typedef struct WAVVoiceOut { 301d14ffa9Sbellard HWVoiceOut hw; 31fb065187Sbellard QEMUFile *f; 32fb065187Sbellard int64_t old_ticks; 33fb065187Sbellard void *pcm_buf; 34fb065187Sbellard int total_samples; 351d14ffa9Sbellard } WAVVoiceOut; 3685571bc7Sbellard 3785571bc7Sbellard static struct { 38*c0fe3827Sbellard audsettings_t settings; 3985571bc7Sbellard const char *wav_path; 4085571bc7Sbellard } conf = { 41*c0fe3827Sbellard { 42*c0fe3827Sbellard 44100, 43*c0fe3827Sbellard 2, 44*c0fe3827Sbellard AUD_FMT_S16 45*c0fe3827Sbellard }, 46*c0fe3827Sbellard "qemu.wav" 4785571bc7Sbellard }; 4885571bc7Sbellard 491d14ffa9Sbellard static int wav_run_out (HWVoiceOut *hw) 5085571bc7Sbellard { 511d14ffa9Sbellard WAVVoiceOut *wav = (WAVVoiceOut *) hw; 5285571bc7Sbellard int rpos, live, decr, samples; 5385571bc7Sbellard uint8_t *dst; 5485571bc7Sbellard st_sample_t *src; 5585571bc7Sbellard int64_t now = qemu_get_clock (vm_clock); 5685571bc7Sbellard int64_t ticks = now - wav->old_ticks; 571d14ffa9Sbellard int64_t bytes = (ticks * hw->info.bytes_per_second) / ticks_per_sec; 5885571bc7Sbellard 591d14ffa9Sbellard if (bytes > INT_MAX) { 601d14ffa9Sbellard samples = INT_MAX >> hw->info.shift; 611d14ffa9Sbellard } 621d14ffa9Sbellard else { 631d14ffa9Sbellard samples = bytes >> hw->info.shift; 641d14ffa9Sbellard } 6585571bc7Sbellard 661d14ffa9Sbellard live = audio_pcm_hw_get_live_out (hw); 671d14ffa9Sbellard if (!live) { 681d14ffa9Sbellard return 0; 691d14ffa9Sbellard } 7085571bc7Sbellard 717372f88dSbellard wav->old_ticks = now; 7285571bc7Sbellard decr = audio_MIN (live, samples); 7385571bc7Sbellard samples = decr; 7485571bc7Sbellard rpos = hw->rpos; 7585571bc7Sbellard while (samples) { 7685571bc7Sbellard int left_till_end_samples = hw->samples - rpos; 7785571bc7Sbellard int convert_samples = audio_MIN (samples, left_till_end_samples); 7885571bc7Sbellard 791d14ffa9Sbellard src = hw->mix_buf + rpos; 801d14ffa9Sbellard dst = advance (wav->pcm_buf, rpos << hw->info.shift); 8185571bc7Sbellard 8285571bc7Sbellard hw->clip (dst, src, convert_samples); 831d14ffa9Sbellard qemu_put_buffer (wav->f, dst, convert_samples << hw->info.shift); 841d14ffa9Sbellard mixeng_clear (src, convert_samples); 8585571bc7Sbellard 8685571bc7Sbellard rpos = (rpos + convert_samples) % hw->samples; 8785571bc7Sbellard samples -= convert_samples; 8885571bc7Sbellard wav->total_samples += convert_samples; 8985571bc7Sbellard } 9085571bc7Sbellard 9185571bc7Sbellard hw->rpos = rpos; 921d14ffa9Sbellard return decr; 9385571bc7Sbellard } 9485571bc7Sbellard 951d14ffa9Sbellard static int wav_write_out (SWVoiceOut *sw, void *buf, int len) 9685571bc7Sbellard { 971d14ffa9Sbellard return audio_pcm_sw_write (sw, buf, len); 9885571bc7Sbellard } 9985571bc7Sbellard 10085571bc7Sbellard /* VICE code: Store number as little endian. */ 10185571bc7Sbellard static void le_store (uint8_t *buf, uint32_t val, int len) 10285571bc7Sbellard { 10385571bc7Sbellard int i; 10485571bc7Sbellard for (i = 0; i < len; i++) { 10585571bc7Sbellard buf[i] = (uint8_t) (val & 0xff); 10685571bc7Sbellard val >>= 8; 10785571bc7Sbellard } 10885571bc7Sbellard } 10985571bc7Sbellard 110*c0fe3827Sbellard static int wav_init_out (HWVoiceOut *hw, audsettings_t *as) 11185571bc7Sbellard { 1121d14ffa9Sbellard WAVVoiceOut *wav = (WAVVoiceOut *) hw; 113*c0fe3827Sbellard int bits16 = 0, stereo = 0; 11485571bc7Sbellard uint8_t hdr[] = { 11585571bc7Sbellard 0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56, 11685571bc7Sbellard 0x45, 0x66, 0x6d, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 11785571bc7Sbellard 0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04, 11885571bc7Sbellard 0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00 11985571bc7Sbellard }; 120*c0fe3827Sbellard audsettings_t wav_as = conf.settings; 12185571bc7Sbellard 122*c0fe3827Sbellard (void) as; 1231d14ffa9Sbellard 124*c0fe3827Sbellard stereo = wav_as.nchannels == 2; 125*c0fe3827Sbellard switch (wav_as.fmt) { 12685571bc7Sbellard case AUD_FMT_S8: 12785571bc7Sbellard case AUD_FMT_U8: 1281d14ffa9Sbellard bits16 = 0; 12985571bc7Sbellard break; 13085571bc7Sbellard 13185571bc7Sbellard case AUD_FMT_S16: 13285571bc7Sbellard case AUD_FMT_U16: 13385571bc7Sbellard bits16 = 1; 13485571bc7Sbellard break; 13585571bc7Sbellard } 13685571bc7Sbellard 13785571bc7Sbellard hdr[34] = bits16 ? 0x10 : 0x08; 138*c0fe3827Sbellard 139*c0fe3827Sbellard audio_pcm_init_info (&hw->info, &wav_as, audio_need_to_swap_endian (0)); 140*c0fe3827Sbellard 141*c0fe3827Sbellard hw->samples = 1024; 142*c0fe3827Sbellard wav->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift); 1431d14ffa9Sbellard if (!wav->pcm_buf) { 144*c0fe3827Sbellard dolog ("Could not allocate buffer (%d bytes)\n", 145*c0fe3827Sbellard hw->samples << hw->info.shift); 14685571bc7Sbellard return -1; 1471d14ffa9Sbellard } 14885571bc7Sbellard 1491d14ffa9Sbellard le_store (hdr + 22, hw->info.nchannels, 2); 1501d14ffa9Sbellard le_store (hdr + 24, hw->info.freq, 4); 151*c0fe3827Sbellard le_store (hdr + 28, hw->info.freq << (bits16 + stereo), 4); 152*c0fe3827Sbellard le_store (hdr + 32, 1 << (bits16 + stereo), 2); 15385571bc7Sbellard 15485571bc7Sbellard wav->f = fopen (conf.wav_path, "wb"); 15585571bc7Sbellard if (!wav->f) { 1561d14ffa9Sbellard dolog ("Failed to open wave file `%s'\nReason: %s\n", 15785571bc7Sbellard conf.wav_path, strerror (errno)); 1587372f88dSbellard qemu_free (wav->pcm_buf); 1597372f88dSbellard wav->pcm_buf = NULL; 16085571bc7Sbellard return -1; 16185571bc7Sbellard } 16285571bc7Sbellard 16385571bc7Sbellard qemu_put_buffer (wav->f, hdr, sizeof (hdr)); 16485571bc7Sbellard return 0; 16585571bc7Sbellard } 16685571bc7Sbellard 1671d14ffa9Sbellard static void wav_fini_out (HWVoiceOut *hw) 16885571bc7Sbellard { 1691d14ffa9Sbellard WAVVoiceOut *wav = (WAVVoiceOut *) hw; 1701d14ffa9Sbellard int stereo = hw->info.nchannels == 2; 17185571bc7Sbellard uint8_t rlen[4]; 17285571bc7Sbellard uint8_t dlen[4]; 17385571bc7Sbellard uint32_t rifflen = (wav->total_samples << stereo) + 36; 17485571bc7Sbellard uint32_t datalen = wav->total_samples << stereo; 17585571bc7Sbellard 176*c0fe3827Sbellard 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 18985571bc7Sbellard 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 2141d14ffa9Sbellard struct audio_option wav_options[] = { 215*c0fe3827Sbellard {"FREQUENCY", AUD_OPT_INT, &conf.settings.freq, 216*c0fe3827Sbellard "Frequency", NULL, 0}, 217*c0fe3827Sbellard 218*c0fe3827Sbellard {"FORMAT", AUD_OPT_FMT, &conf.settings.fmt, 219*c0fe3827Sbellard "Format", NULL, 0}, 220*c0fe3827Sbellard 221*c0fe3827Sbellard {"DAC_FIXED_CHANNELS", AUD_OPT_INT, &conf.settings.nchannels, 222*c0fe3827Sbellard "Number of channels (1 - mono, 2 - stereo)", NULL, 0}, 223*c0fe3827Sbellard 2241d14ffa9Sbellard {"PATH", AUD_OPT_STR, &conf.wav_path, 2251d14ffa9Sbellard "Path to wave file", NULL, 0}, 2261d14ffa9Sbellard {NULL, 0, NULL, NULL, NULL, 0} 22785571bc7Sbellard }; 22885571bc7Sbellard 2291d14ffa9Sbellard struct audio_pcm_ops wav_pcm_ops = { 2301d14ffa9Sbellard wav_init_out, 2311d14ffa9Sbellard wav_fini_out, 2321d14ffa9Sbellard wav_run_out, 2331d14ffa9Sbellard wav_write_out, 2341d14ffa9Sbellard wav_ctl_out, 2351d14ffa9Sbellard 2361d14ffa9Sbellard NULL, 2371d14ffa9Sbellard NULL, 2381d14ffa9Sbellard NULL, 2391d14ffa9Sbellard NULL, 2401d14ffa9Sbellard NULL 2411d14ffa9Sbellard }; 2421d14ffa9Sbellard 2431d14ffa9Sbellard struct audio_driver wav_audio_driver = { 2441d14ffa9Sbellard INIT_FIELD (name = ) "wav", 2451d14ffa9Sbellard INIT_FIELD (descr = ) 2461d14ffa9Sbellard "WAV renderer http://wikipedia.org/wiki/WAV", 2471d14ffa9Sbellard INIT_FIELD (options = ) wav_options, 2481d14ffa9Sbellard INIT_FIELD (init = ) wav_audio_init, 2491d14ffa9Sbellard INIT_FIELD (fini = ) wav_audio_fini, 2501d14ffa9Sbellard INIT_FIELD (pcm_ops = ) &wav_pcm_ops, 2511d14ffa9Sbellard INIT_FIELD (can_be_default = ) 0, 2521d14ffa9Sbellard INIT_FIELD (max_voices_out = ) 1, 2531d14ffa9Sbellard INIT_FIELD (max_voices_in = ) 0, 2541d14ffa9Sbellard INIT_FIELD (voice_size_out = ) sizeof (WAVVoiceOut), 2551d14ffa9Sbellard INIT_FIELD (voice_size_in = ) 0 25685571bc7Sbellard }; 257