185571bc7Sbellard /* 285571bc7Sbellard * QEMU WAV audio output driver 385571bc7Sbellard * 485571bc7Sbellard * Copyright (c) 2004 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 26*fb065187Sbellard #include "audio/audio_int.h" 27*fb065187Sbellard 28*fb065187Sbellard typedef struct WAVVoice { 29*fb065187Sbellard HWVoice hw; 30*fb065187Sbellard QEMUFile *f; 31*fb065187Sbellard int64_t old_ticks; 32*fb065187Sbellard void *pcm_buf; 33*fb065187Sbellard int total_samples; 34*fb065187Sbellard } WAVVoice; 35*fb065187Sbellard 36*fb065187Sbellard #define dolog(...) AUD_log ("wav", __VA_ARGS__) 37*fb065187Sbellard #ifdef DEBUG 38*fb065187Sbellard #define ldebug(...) dolog (__VA_ARGS__) 39*fb065187Sbellard #else 40*fb065187Sbellard #define ldebug(...) 41*fb065187Sbellard #endif 4285571bc7Sbellard 4385571bc7Sbellard static struct { 4485571bc7Sbellard const char *wav_path; 4585571bc7Sbellard } conf = { 4685571bc7Sbellard .wav_path = "qemu.wav" 4785571bc7Sbellard }; 4885571bc7Sbellard 4985571bc7Sbellard static void wav_hw_run (HWVoice *hw) 5085571bc7Sbellard { 5185571bc7Sbellard WAVVoice *wav = (WAVVoice *) 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; 5785571bc7Sbellard int64_t bytes = (ticks * hw->bytes_per_second) / ticks_per_sec; 5885571bc7Sbellard wav->old_ticks = now; 5985571bc7Sbellard 6085571bc7Sbellard if (bytes > INT_MAX) 6185571bc7Sbellard samples = INT_MAX >> hw->shift; 6285571bc7Sbellard else 6385571bc7Sbellard samples = bytes >> hw->shift; 6485571bc7Sbellard 6585571bc7Sbellard live = pcm_hw_get_live (hw); 6685571bc7Sbellard if (live <= 0) 6785571bc7Sbellard return; 6885571bc7Sbellard 6985571bc7Sbellard decr = audio_MIN (live, samples); 7085571bc7Sbellard samples = decr; 7185571bc7Sbellard rpos = hw->rpos; 7285571bc7Sbellard while (samples) { 7385571bc7Sbellard int left_till_end_samples = hw->samples - rpos; 7485571bc7Sbellard int convert_samples = audio_MIN (samples, left_till_end_samples); 7585571bc7Sbellard 7685571bc7Sbellard src = advance (hw->mix_buf, rpos * sizeof (st_sample_t)); 7785571bc7Sbellard dst = advance (wav->pcm_buf, rpos << hw->shift); 7885571bc7Sbellard 7985571bc7Sbellard hw->clip (dst, src, convert_samples); 8085571bc7Sbellard qemu_put_buffer (wav->f, dst, convert_samples << hw->shift); 8185571bc7Sbellard memset (src, 0, convert_samples * sizeof (st_sample_t)); 8285571bc7Sbellard 8385571bc7Sbellard rpos = (rpos + convert_samples) % hw->samples; 8485571bc7Sbellard samples -= convert_samples; 8585571bc7Sbellard wav->total_samples += convert_samples; 8685571bc7Sbellard } 8785571bc7Sbellard 8885571bc7Sbellard pcm_hw_dec_live (hw, decr); 8985571bc7Sbellard hw->rpos = rpos; 9085571bc7Sbellard } 9185571bc7Sbellard 9285571bc7Sbellard static int wav_hw_write (SWVoice *sw, void *buf, int len) 9385571bc7Sbellard { 9485571bc7Sbellard return pcm_hw_write (sw, buf, len); 9585571bc7Sbellard } 9685571bc7Sbellard 9785571bc7Sbellard 9885571bc7Sbellard /* VICE code: Store number as little endian. */ 9985571bc7Sbellard static void le_store (uint8_t *buf, uint32_t val, int len) 10085571bc7Sbellard { 10185571bc7Sbellard int i; 10285571bc7Sbellard for (i = 0; i < len; i++) { 10385571bc7Sbellard buf[i] = (uint8_t) (val & 0xff); 10485571bc7Sbellard val >>= 8; 10585571bc7Sbellard } 10685571bc7Sbellard } 10785571bc7Sbellard 10885571bc7Sbellard static int wav_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt) 10985571bc7Sbellard { 11085571bc7Sbellard WAVVoice *wav = (WAVVoice *) hw; 11185571bc7Sbellard int bits16 = 0, stereo = audio_state.fixed_channels == 2; 11285571bc7Sbellard uint8_t hdr[] = { 11385571bc7Sbellard 0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56, 11485571bc7Sbellard 0x45, 0x66, 0x6d, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 11585571bc7Sbellard 0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04, 11685571bc7Sbellard 0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00 11785571bc7Sbellard }; 11885571bc7Sbellard 11985571bc7Sbellard switch (audio_state.fixed_fmt) { 12085571bc7Sbellard case AUD_FMT_S8: 12185571bc7Sbellard case AUD_FMT_U8: 12285571bc7Sbellard break; 12385571bc7Sbellard 12485571bc7Sbellard case AUD_FMT_S16: 12585571bc7Sbellard case AUD_FMT_U16: 12685571bc7Sbellard bits16 = 1; 12785571bc7Sbellard break; 12885571bc7Sbellard } 12985571bc7Sbellard 13085571bc7Sbellard hdr[34] = bits16 ? 0x10 : 0x08; 13185571bc7Sbellard hw->freq = 44100; 13285571bc7Sbellard hw->nchannels = stereo ? 2 : 1; 13385571bc7Sbellard hw->fmt = bits16 ? AUD_FMT_S16 : AUD_FMT_U8; 13485571bc7Sbellard hw->bufsize = 4096; 13585571bc7Sbellard wav->pcm_buf = qemu_mallocz (hw->bufsize); 13685571bc7Sbellard if (!wav->pcm_buf) 13785571bc7Sbellard return -1; 13885571bc7Sbellard 13985571bc7Sbellard le_store (hdr + 22, hw->nchannels, 2); 14085571bc7Sbellard le_store (hdr + 24, hw->freq, 4); 14185571bc7Sbellard le_store (hdr + 28, hw->freq << (bits16 + stereo), 4); 14285571bc7Sbellard le_store (hdr + 32, 1 << (bits16 + stereo), 2); 14385571bc7Sbellard 14485571bc7Sbellard wav->f = fopen (conf.wav_path, "wb"); 14585571bc7Sbellard if (!wav->f) { 14685571bc7Sbellard dolog ("failed to open wave file `%s'\nReason: %s\n", 14785571bc7Sbellard conf.wav_path, strerror (errno)); 14885571bc7Sbellard return -1; 14985571bc7Sbellard } 15085571bc7Sbellard 15185571bc7Sbellard qemu_put_buffer (wav->f, hdr, sizeof (hdr)); 15285571bc7Sbellard return 0; 15385571bc7Sbellard } 15485571bc7Sbellard 15585571bc7Sbellard static void wav_hw_fini (HWVoice *hw) 15685571bc7Sbellard { 15785571bc7Sbellard WAVVoice *wav = (WAVVoice *) hw; 15885571bc7Sbellard int stereo = hw->nchannels == 2; 15985571bc7Sbellard uint8_t rlen[4]; 16085571bc7Sbellard uint8_t dlen[4]; 16185571bc7Sbellard uint32_t rifflen = (wav->total_samples << stereo) + 36; 16285571bc7Sbellard uint32_t datalen = wav->total_samples << stereo; 16385571bc7Sbellard 16485571bc7Sbellard if (!wav->f || !hw->active) 16585571bc7Sbellard return; 16685571bc7Sbellard 16785571bc7Sbellard le_store (rlen, rifflen, 4); 16885571bc7Sbellard le_store (dlen, datalen, 4); 16985571bc7Sbellard 17085571bc7Sbellard qemu_fseek (wav->f, 4, SEEK_SET); 17185571bc7Sbellard qemu_put_buffer (wav->f, rlen, 4); 17285571bc7Sbellard 17385571bc7Sbellard qemu_fseek (wav->f, 32, SEEK_CUR); 17485571bc7Sbellard qemu_put_buffer (wav->f, dlen, 4); 17585571bc7Sbellard 17685571bc7Sbellard fclose (wav->f); 17785571bc7Sbellard wav->f = NULL; 17885571bc7Sbellard } 17985571bc7Sbellard 18085571bc7Sbellard static int wav_hw_ctl (HWVoice *hw, int cmd, ...) 18185571bc7Sbellard { 18285571bc7Sbellard (void) hw; 18385571bc7Sbellard (void) cmd; 18485571bc7Sbellard return 0; 18585571bc7Sbellard } 18685571bc7Sbellard 18785571bc7Sbellard static void *wav_audio_init (void) 18885571bc7Sbellard { 18985571bc7Sbellard return &conf; 19085571bc7Sbellard } 19185571bc7Sbellard 19285571bc7Sbellard static void wav_audio_fini (void *opaque) 19385571bc7Sbellard { 19485571bc7Sbellard ldebug ("wav_fini"); 19585571bc7Sbellard } 19685571bc7Sbellard 19785571bc7Sbellard struct pcm_ops wav_pcm_ops = { 19885571bc7Sbellard wav_hw_init, 19985571bc7Sbellard wav_hw_fini, 20085571bc7Sbellard wav_hw_run, 20185571bc7Sbellard wav_hw_write, 20285571bc7Sbellard wav_hw_ctl 20385571bc7Sbellard }; 20485571bc7Sbellard 20585571bc7Sbellard struct audio_output_driver wav_output_driver = { 20685571bc7Sbellard "wav", 20785571bc7Sbellard wav_audio_init, 20885571bc7Sbellard wav_audio_fini, 20985571bc7Sbellard &wav_pcm_ops, 21085571bc7Sbellard 1, 21185571bc7Sbellard 1, 21285571bc7Sbellard sizeof (WAVVoice) 21385571bc7Sbellard }; 214