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 */ 240b8fa32fSMarkus Armbruster 256086a565SPeter Maydell #include "qemu/osdep.h" 2687776ab7SPaolo Bonzini #include "qemu/host-utils.h" 270b8fa32fSMarkus Armbruster #include "qemu/module.h" 281de7afc9SPaolo Bonzini #include "qemu/timer.h" 290927d166SKővágó, Zoltán #include "qapi/opts-visitor.h" 3087ecb68bSpbrook #include "audio.h" 3185571bc7Sbellard 321d14ffa9Sbellard #define AUDIO_CAP "wav" 331d14ffa9Sbellard #include "audio_int.h" 34fb065187Sbellard 351d14ffa9Sbellard typedef struct WAVVoiceOut { 361d14ffa9Sbellard HWVoiceOut hw; 3727acf660SJuan Quintela FILE *f; 38857271a2SKővágó, Zoltán RateCtl rate; 39fb065187Sbellard int total_samples; 401d14ffa9Sbellard } WAVVoiceOut; 4185571bc7Sbellard 42ef3612e1SKővágó, Zoltán static size_t wav_write_out(HWVoiceOut *hw, void *buf, size_t len) 4385571bc7Sbellard { 441d14ffa9Sbellard WAVVoiceOut *wav = (WAVVoiceOut *) hw; 45857271a2SKővágó, Zoltán int64_t bytes = audio_rate_get_bytes(&hw->info, &wav->rate, len); 462b9cce8cSKővágó, Zoltán assert(bytes % hw->info.bytes_per_frame == 0); 4785571bc7Sbellard 48ef3612e1SKővágó, Zoltán if (bytes && fwrite(buf, bytes, 1, wav->f) != 1) { 49ef3612e1SKővágó, Zoltán dolog("wav_write_out: fwrite of %" PRId64 " bytes failed\nReason: %s\n", 50ef3612e1SKővágó, Zoltán bytes, strerror(errno)); 5127acf660SJuan Quintela } 5285571bc7Sbellard 532b9cce8cSKővágó, Zoltán wav->total_samples += bytes / hw->info.bytes_per_frame; 54ef3612e1SKővágó, Zoltán return bytes; 5585571bc7Sbellard } 5685571bc7Sbellard 5785571bc7Sbellard /* VICE code: Store number as little endian. */ 5885571bc7Sbellard static void le_store (uint8_t *buf, uint32_t val, int len) 5985571bc7Sbellard { 6085571bc7Sbellard int i; 6185571bc7Sbellard for (i = 0; i < len; i++) { 6285571bc7Sbellard buf[i] = (uint8_t) (val & 0xff); 6385571bc7Sbellard val >>= 8; 6485571bc7Sbellard } 6585571bc7Sbellard } 6685571bc7Sbellard 675706db1dSKővágó, Zoltán static int wav_init_out(HWVoiceOut *hw, struct audsettings *as, 685706db1dSKővágó, Zoltán void *drv_opaque) 6985571bc7Sbellard { 701d14ffa9Sbellard WAVVoiceOut *wav = (WAVVoiceOut *) hw; 71c0fe3827Sbellard int bits16 = 0, stereo = 0; 7285571bc7Sbellard uint8_t hdr[] = { 7385571bc7Sbellard 0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56, 7485571bc7Sbellard 0x45, 0x66, 0x6d, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 7585571bc7Sbellard 0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04, 7685571bc7Sbellard 0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00 7785571bc7Sbellard }; 780927d166SKővágó, Zoltán Audiodev *dev = drv_opaque; 790927d166SKővágó, Zoltán AudiodevWavOptions *wopts = &dev->u.wav; 800927d166SKővágó, Zoltán struct audsettings wav_as = audiodev_to_audsettings(dev->u.wav.out); 810927d166SKővágó, Zoltán const char *wav_path = wopts->has_path ? wopts->path : "qemu.wav"; 8285571bc7Sbellard 83c0fe3827Sbellard stereo = wav_as.nchannels == 2; 84c0fe3827Sbellard switch (wav_as.fmt) { 8585bc5852SKővágó, Zoltán case AUDIO_FORMAT_S8: 8685bc5852SKővágó, Zoltán case AUDIO_FORMAT_U8: 871d14ffa9Sbellard bits16 = 0; 8885571bc7Sbellard break; 8985571bc7Sbellard 9085bc5852SKővágó, Zoltán case AUDIO_FORMAT_S16: 9185bc5852SKővágó, Zoltán case AUDIO_FORMAT_U16: 9285571bc7Sbellard bits16 = 1; 9385571bc7Sbellard break; 94f941aa25Sths 9585bc5852SKővágó, Zoltán case AUDIO_FORMAT_S32: 9685bc5852SKővágó, Zoltán case AUDIO_FORMAT_U32: 97f941aa25Sths dolog ("WAVE files can not handle 32bit formats\n"); 98f941aa25Sths return -1; 9985bc5852SKővágó, Zoltán 10085bc5852SKővágó, Zoltán default: 10185bc5852SKővágó, Zoltán abort(); 10285571bc7Sbellard } 10385571bc7Sbellard 10485571bc7Sbellard hdr[34] = bits16 ? 0x10 : 0x08; 105c0fe3827Sbellard 106d929eba5Sbellard wav_as.endianness = 0; 107d929eba5Sbellard audio_pcm_init_info (&hw->info, &wav_as); 108c0fe3827Sbellard 109c0fe3827Sbellard hw->samples = 1024; 1101d14ffa9Sbellard le_store (hdr + 22, hw->info.nchannels, 2); 1111d14ffa9Sbellard le_store (hdr + 24, hw->info.freq, 4); 112c0fe3827Sbellard le_store (hdr + 28, hw->info.freq << (bits16 + stereo), 4); 113c0fe3827Sbellard le_store (hdr + 32, 1 << (bits16 + stereo), 2); 11485571bc7Sbellard 1150927d166SKővágó, Zoltán wav->f = fopen(wav_path, "wb"); 11685571bc7Sbellard if (!wav->f) { 1171d14ffa9Sbellard dolog ("Failed to open wave file `%s'\nReason: %s\n", 1180927d166SKővágó, Zoltán wav_path, strerror(errno)); 11985571bc7Sbellard return -1; 12085571bc7Sbellard } 12185571bc7Sbellard 12227acf660SJuan Quintela if (fwrite (hdr, sizeof (hdr), 1, wav->f) != 1) { 12327acf660SJuan Quintela dolog ("wav_init_out: failed to write header\nReason: %s\n", 12427acf660SJuan Quintela strerror(errno)); 12527acf660SJuan Quintela return -1; 12627acf660SJuan Quintela } 127857271a2SKővágó, Zoltán 128857271a2SKővágó, Zoltán audio_rate_start(&wav->rate); 12985571bc7Sbellard return 0; 13085571bc7Sbellard } 13185571bc7Sbellard 1321d14ffa9Sbellard static void wav_fini_out (HWVoiceOut *hw) 13385571bc7Sbellard { 1341d14ffa9Sbellard WAVVoiceOut *wav = (WAVVoiceOut *) hw; 13585571bc7Sbellard uint8_t rlen[4]; 13685571bc7Sbellard uint8_t dlen[4]; 1372b9cce8cSKővágó, Zoltán uint32_t datalen = wav->total_samples * hw->info.bytes_per_frame; 13850903530Sbellard uint32_t rifflen = datalen + 36; 13985571bc7Sbellard 140c0fe3827Sbellard if (!wav->f) { 14185571bc7Sbellard return; 1421d14ffa9Sbellard } 14385571bc7Sbellard 14485571bc7Sbellard le_store (rlen, rifflen, 4); 14585571bc7Sbellard le_store (dlen, datalen, 4); 14685571bc7Sbellard 14727acf660SJuan Quintela if (fseek (wav->f, 4, SEEK_SET)) { 14827acf660SJuan Quintela dolog ("wav_fini_out: fseek to rlen failed\nReason: %s\n", 14927acf660SJuan Quintela strerror(errno)); 15027acf660SJuan Quintela goto doclose; 15127acf660SJuan Quintela } 15227acf660SJuan Quintela if (fwrite (rlen, 4, 1, wav->f) != 1) { 15327acf660SJuan Quintela dolog ("wav_fini_out: failed to write rlen\nReason: %s\n", 15427acf660SJuan Quintela strerror (errno)); 15527acf660SJuan Quintela goto doclose; 15627acf660SJuan Quintela } 15727acf660SJuan Quintela if (fseek (wav->f, 32, SEEK_CUR)) { 15827acf660SJuan Quintela dolog ("wav_fini_out: fseek to dlen failed\nReason: %s\n", 15927acf660SJuan Quintela strerror (errno)); 16027acf660SJuan Quintela goto doclose; 16127acf660SJuan Quintela } 16227acf660SJuan Quintela if (fwrite (dlen, 4, 1, wav->f) != 1) { 16327acf660SJuan Quintela dolog ("wav_fini_out: failed to write dlen\nReaons: %s\n", 16427acf660SJuan Quintela strerror (errno)); 16527acf660SJuan Quintela goto doclose; 16627acf660SJuan Quintela } 16785571bc7Sbellard 16827acf660SJuan Quintela doclose: 16927acf660SJuan Quintela if (fclose (wav->f)) { 17027acf660SJuan Quintela dolog ("wav_fini_out: fclose %p failed\nReason: %s\n", 17127acf660SJuan Quintela wav->f, strerror (errno)); 17227acf660SJuan Quintela } 17385571bc7Sbellard wav->f = NULL; 17485571bc7Sbellard } 17585571bc7Sbellard 176571a8c52SKővágó, Zoltán static void wav_enable_out(HWVoiceOut *hw, bool enable) 17785571bc7Sbellard { 178857271a2SKővágó, Zoltán WAVVoiceOut *wav = (WAVVoiceOut *) hw; 179857271a2SKővágó, Zoltán 180571a8c52SKővágó, Zoltán if (enable) { 181857271a2SKővágó, Zoltán audio_rate_start(&wav->rate); 182857271a2SKővágó, Zoltán } 18385571bc7Sbellard } 18485571bc7Sbellard 18571830221SKővágó, Zoltán static void *wav_audio_init(Audiodev *dev) 18685571bc7Sbellard { 1870927d166SKővágó, Zoltán assert(dev->driver == AUDIODEV_DRIVER_WAV); 1880927d166SKővágó, Zoltán return dev; 18985571bc7Sbellard } 19085571bc7Sbellard 19185571bc7Sbellard static void wav_audio_fini (void *opaque) 19285571bc7Sbellard { 19385571bc7Sbellard ldebug ("wav_fini"); 19485571bc7Sbellard } 19585571bc7Sbellard 19635f4b58cSblueswir1 static struct audio_pcm_ops wav_pcm_ops = { 1971dd3e4d1SJuan Quintela .init_out = wav_init_out, 1981dd3e4d1SJuan Quintela .fini_out = wav_fini_out, 199ef3612e1SKővágó, Zoltán .write = wav_write_out, 200*fdc8c5f4SVolker Rümelin .run_buffer_out = audio_generic_run_buffer_out, 201571a8c52SKővágó, Zoltán .enable_out = wav_enable_out, 2021d14ffa9Sbellard }; 2031d14ffa9Sbellard 204d3893a39SGerd Hoffmann static struct audio_driver wav_audio_driver = { 205bee37f32SJuan Quintela .name = "wav", 206bee37f32SJuan Quintela .descr = "WAV renderer http://wikipedia.org/wiki/WAV", 207bee37f32SJuan Quintela .init = wav_audio_init, 208bee37f32SJuan Quintela .fini = wav_audio_fini, 209bee37f32SJuan Quintela .pcm_ops = &wav_pcm_ops, 210bee37f32SJuan Quintela .can_be_default = 0, 211bee37f32SJuan Quintela .max_voices_out = 1, 212bee37f32SJuan Quintela .max_voices_in = 0, 213bee37f32SJuan Quintela .voice_size_out = sizeof (WAVVoiceOut), 214bee37f32SJuan Quintela .voice_size_in = 0 21585571bc7Sbellard }; 216d3893a39SGerd Hoffmann 217d3893a39SGerd Hoffmann static void register_audio_wav(void) 218d3893a39SGerd Hoffmann { 219d3893a39SGerd Hoffmann audio_driver_register(&wav_audio_driver); 220d3893a39SGerd Hoffmann } 221d3893a39SGerd Hoffmann type_init(register_audio_wav); 222