bellard | 85571bc | 2004-11-07 18:04:02 +0000 | [diff] [blame] | 1 | /* |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 2 | * QEMU WAV audio driver |
| 3 | * |
| 4 | * Copyright (c) 2004-2005 Vassili Karpov (malc) |
| 5 | * |
bellard | 85571bc | 2004-11-07 18:04:02 +0000 | [diff] [blame] | 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
| 7 | * of this software and associated documentation files (the "Software"), to deal |
| 8 | * in the Software without restriction, including without limitation the rights |
| 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| 10 | * copies of the Software, and to permit persons to whom the Software is |
| 11 | * furnished to do so, subject to the following conditions: |
| 12 | * |
| 13 | * The above copyright notice and this permission notice shall be included in |
| 14 | * all copies or substantial portions of the Software. |
| 15 | * |
| 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| 19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
| 22 | * THE SOFTWARE. |
| 23 | */ |
Markus Armbruster | 0b8fa32 | 2019-05-23 16:35:07 +0200 | [diff] [blame] | 24 | |
Peter Maydell | 6086a56 | 2016-01-18 17:33:52 +0000 | [diff] [blame] | 25 | #include "qemu/osdep.h" |
Paolo Bonzini | 87776ab | 2016-03-15 15:36:13 +0100 | [diff] [blame] | 26 | #include "qemu/host-utils.h" |
Markus Armbruster | 0b8fa32 | 2019-05-23 16:35:07 +0200 | [diff] [blame] | 27 | #include "qemu/module.h" |
Paolo Bonzini | 1de7afc | 2012-12-17 18:20:00 +0100 | [diff] [blame] | 28 | #include "qemu/timer.h" |
Kővágó, Zoltán | 0927d16 | 2019-03-08 23:34:24 +0100 | [diff] [blame] | 29 | #include "qapi/opts-visitor.h" |
pbrook | 87ecb68 | 2007-11-17 17:14:51 +0000 | [diff] [blame] | 30 | #include "audio.h" |
bellard | 85571bc | 2004-11-07 18:04:02 +0000 | [diff] [blame] | 31 | |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 32 | #define AUDIO_CAP "wav" |
| 33 | #include "audio_int.h" |
bellard | fb06518 | 2004-11-09 23:09:44 +0000 | [diff] [blame] | 34 | |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 35 | typedef struct WAVVoiceOut { |
| 36 | HWVoiceOut hw; |
Juan Quintela | 27acf66 | 2011-09-20 15:16:27 +0200 | [diff] [blame] | 37 | FILE *f; |
Kővágó, Zoltán | 857271a | 2019-09-19 23:24:21 +0200 | [diff] [blame] | 38 | RateCtl rate; |
bellard | fb06518 | 2004-11-09 23:09:44 +0000 | [diff] [blame] | 39 | int total_samples; |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 40 | } WAVVoiceOut; |
bellard | 85571bc | 2004-11-07 18:04:02 +0000 | [diff] [blame] | 41 | |
Kővágó, Zoltán | ef3612e | 2019-09-19 23:24:18 +0200 | [diff] [blame] | 42 | static size_t wav_write_out(HWVoiceOut *hw, void *buf, size_t len) |
bellard | 85571bc | 2004-11-07 18:04:02 +0000 | [diff] [blame] | 43 | { |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 44 | WAVVoiceOut *wav = (WAVVoiceOut *) hw; |
Kővágó, Zoltán | 857271a | 2019-09-19 23:24:21 +0200 | [diff] [blame] | 45 | int64_t bytes = audio_rate_get_bytes(&hw->info, &wav->rate, len); |
Kővágó, Zoltán | 2b9cce8 | 2019-10-13 21:58:02 +0200 | [diff] [blame^] | 46 | assert(bytes % hw->info.bytes_per_frame == 0); |
bellard | 85571bc | 2004-11-07 18:04:02 +0000 | [diff] [blame] | 47 | |
Kővágó, Zoltán | ef3612e | 2019-09-19 23:24:18 +0200 | [diff] [blame] | 48 | if (bytes && fwrite(buf, bytes, 1, wav->f) != 1) { |
| 49 | dolog("wav_write_out: fwrite of %" PRId64 " bytes failed\nReason: %s\n", |
| 50 | bytes, strerror(errno)); |
bellard | 85571bc | 2004-11-07 18:04:02 +0000 | [diff] [blame] | 51 | } |
| 52 | |
Kővágó, Zoltán | 2b9cce8 | 2019-10-13 21:58:02 +0200 | [diff] [blame^] | 53 | wav->total_samples += bytes / hw->info.bytes_per_frame; |
Kővágó, Zoltán | ef3612e | 2019-09-19 23:24:18 +0200 | [diff] [blame] | 54 | return bytes; |
bellard | 85571bc | 2004-11-07 18:04:02 +0000 | [diff] [blame] | 55 | } |
| 56 | |
bellard | 85571bc | 2004-11-07 18:04:02 +0000 | [diff] [blame] | 57 | /* VICE code: Store number as little endian. */ |
| 58 | static void le_store (uint8_t *buf, uint32_t val, int len) |
| 59 | { |
| 60 | int i; |
| 61 | for (i = 0; i < len; i++) { |
| 62 | buf[i] = (uint8_t) (val & 0xff); |
| 63 | val >>= 8; |
| 64 | } |
| 65 | } |
| 66 | |
Kővágó, Zoltán | 5706db1 | 2015-06-03 23:03:47 +0200 | [diff] [blame] | 67 | static int wav_init_out(HWVoiceOut *hw, struct audsettings *as, |
| 68 | void *drv_opaque) |
bellard | 85571bc | 2004-11-07 18:04:02 +0000 | [diff] [blame] | 69 | { |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 70 | WAVVoiceOut *wav = (WAVVoiceOut *) hw; |
bellard | c0fe382 | 2005-11-05 18:55:28 +0000 | [diff] [blame] | 71 | int bits16 = 0, stereo = 0; |
bellard | 85571bc | 2004-11-07 18:04:02 +0000 | [diff] [blame] | 72 | uint8_t hdr[] = { |
| 73 | 0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56, |
| 74 | 0x45, 0x66, 0x6d, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, |
| 75 | 0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04, |
| 76 | 0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00 |
| 77 | }; |
Kővágó, Zoltán | 0927d16 | 2019-03-08 23:34:24 +0100 | [diff] [blame] | 78 | Audiodev *dev = drv_opaque; |
| 79 | AudiodevWavOptions *wopts = &dev->u.wav; |
| 80 | struct audsettings wav_as = audiodev_to_audsettings(dev->u.wav.out); |
| 81 | const char *wav_path = wopts->has_path ? wopts->path : "qemu.wav"; |
bellard | 85571bc | 2004-11-07 18:04:02 +0000 | [diff] [blame] | 82 | |
bellard | c0fe382 | 2005-11-05 18:55:28 +0000 | [diff] [blame] | 83 | stereo = wav_as.nchannels == 2; |
| 84 | switch (wav_as.fmt) { |
Kővágó, Zoltán | 85bc585 | 2019-03-08 23:34:13 +0100 | [diff] [blame] | 85 | case AUDIO_FORMAT_S8: |
| 86 | case AUDIO_FORMAT_U8: |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 87 | bits16 = 0; |
bellard | 85571bc | 2004-11-07 18:04:02 +0000 | [diff] [blame] | 88 | break; |
| 89 | |
Kővágó, Zoltán | 85bc585 | 2019-03-08 23:34:13 +0100 | [diff] [blame] | 90 | case AUDIO_FORMAT_S16: |
| 91 | case AUDIO_FORMAT_U16: |
bellard | 85571bc | 2004-11-07 18:04:02 +0000 | [diff] [blame] | 92 | bits16 = 1; |
| 93 | break; |
ths | f941aa2 | 2007-02-17 22:19:29 +0000 | [diff] [blame] | 94 | |
Kővágó, Zoltán | 85bc585 | 2019-03-08 23:34:13 +0100 | [diff] [blame] | 95 | case AUDIO_FORMAT_S32: |
| 96 | case AUDIO_FORMAT_U32: |
ths | f941aa2 | 2007-02-17 22:19:29 +0000 | [diff] [blame] | 97 | dolog ("WAVE files can not handle 32bit formats\n"); |
| 98 | return -1; |
Kővágó, Zoltán | 85bc585 | 2019-03-08 23:34:13 +0100 | [diff] [blame] | 99 | |
| 100 | default: |
| 101 | abort(); |
bellard | 85571bc | 2004-11-07 18:04:02 +0000 | [diff] [blame] | 102 | } |
| 103 | |
| 104 | hdr[34] = bits16 ? 0x10 : 0x08; |
bellard | c0fe382 | 2005-11-05 18:55:28 +0000 | [diff] [blame] | 105 | |
bellard | d929eba | 2006-07-04 21:47:22 +0000 | [diff] [blame] | 106 | wav_as.endianness = 0; |
| 107 | audio_pcm_init_info (&hw->info, &wav_as); |
bellard | c0fe382 | 2005-11-05 18:55:28 +0000 | [diff] [blame] | 108 | |
| 109 | hw->samples = 1024; |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 110 | le_store (hdr + 22, hw->info.nchannels, 2); |
| 111 | le_store (hdr + 24, hw->info.freq, 4); |
bellard | c0fe382 | 2005-11-05 18:55:28 +0000 | [diff] [blame] | 112 | le_store (hdr + 28, hw->info.freq << (bits16 + stereo), 4); |
| 113 | le_store (hdr + 32, 1 << (bits16 + stereo), 2); |
bellard | 85571bc | 2004-11-07 18:04:02 +0000 | [diff] [blame] | 114 | |
Kővágó, Zoltán | 0927d16 | 2019-03-08 23:34:24 +0100 | [diff] [blame] | 115 | wav->f = fopen(wav_path, "wb"); |
bellard | 85571bc | 2004-11-07 18:04:02 +0000 | [diff] [blame] | 116 | if (!wav->f) { |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 117 | dolog ("Failed to open wave file `%s'\nReason: %s\n", |
Kővágó, Zoltán | 0927d16 | 2019-03-08 23:34:24 +0100 | [diff] [blame] | 118 | wav_path, strerror(errno)); |
bellard | 85571bc | 2004-11-07 18:04:02 +0000 | [diff] [blame] | 119 | return -1; |
| 120 | } |
| 121 | |
Juan Quintela | 27acf66 | 2011-09-20 15:16:27 +0200 | [diff] [blame] | 122 | if (fwrite (hdr, sizeof (hdr), 1, wav->f) != 1) { |
| 123 | dolog ("wav_init_out: failed to write header\nReason: %s\n", |
| 124 | strerror(errno)); |
| 125 | return -1; |
| 126 | } |
Kővágó, Zoltán | 857271a | 2019-09-19 23:24:21 +0200 | [diff] [blame] | 127 | |
| 128 | audio_rate_start(&wav->rate); |
bellard | 85571bc | 2004-11-07 18:04:02 +0000 | [diff] [blame] | 129 | return 0; |
| 130 | } |
| 131 | |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 132 | static void wav_fini_out (HWVoiceOut *hw) |
bellard | 85571bc | 2004-11-07 18:04:02 +0000 | [diff] [blame] | 133 | { |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 134 | WAVVoiceOut *wav = (WAVVoiceOut *) hw; |
bellard | 85571bc | 2004-11-07 18:04:02 +0000 | [diff] [blame] | 135 | uint8_t rlen[4]; |
| 136 | uint8_t dlen[4]; |
Kővágó, Zoltán | 2b9cce8 | 2019-10-13 21:58:02 +0200 | [diff] [blame^] | 137 | uint32_t datalen = wav->total_samples * hw->info.bytes_per_frame; |
bellard | 5090353 | 2005-11-20 16:22:16 +0000 | [diff] [blame] | 138 | uint32_t rifflen = datalen + 36; |
bellard | 85571bc | 2004-11-07 18:04:02 +0000 | [diff] [blame] | 139 | |
bellard | c0fe382 | 2005-11-05 18:55:28 +0000 | [diff] [blame] | 140 | if (!wav->f) { |
bellard | 85571bc | 2004-11-07 18:04:02 +0000 | [diff] [blame] | 141 | return; |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 142 | } |
bellard | 85571bc | 2004-11-07 18:04:02 +0000 | [diff] [blame] | 143 | |
| 144 | le_store (rlen, rifflen, 4); |
| 145 | le_store (dlen, datalen, 4); |
| 146 | |
Juan Quintela | 27acf66 | 2011-09-20 15:16:27 +0200 | [diff] [blame] | 147 | if (fseek (wav->f, 4, SEEK_SET)) { |
| 148 | dolog ("wav_fini_out: fseek to rlen failed\nReason: %s\n", |
| 149 | strerror(errno)); |
| 150 | goto doclose; |
| 151 | } |
| 152 | if (fwrite (rlen, 4, 1, wav->f) != 1) { |
| 153 | dolog ("wav_fini_out: failed to write rlen\nReason: %s\n", |
| 154 | strerror (errno)); |
| 155 | goto doclose; |
| 156 | } |
| 157 | if (fseek (wav->f, 32, SEEK_CUR)) { |
| 158 | dolog ("wav_fini_out: fseek to dlen failed\nReason: %s\n", |
| 159 | strerror (errno)); |
| 160 | goto doclose; |
| 161 | } |
| 162 | if (fwrite (dlen, 4, 1, wav->f) != 1) { |
| 163 | dolog ("wav_fini_out: failed to write dlen\nReaons: %s\n", |
| 164 | strerror (errno)); |
| 165 | goto doclose; |
| 166 | } |
bellard | 85571bc | 2004-11-07 18:04:02 +0000 | [diff] [blame] | 167 | |
Juan Quintela | 27acf66 | 2011-09-20 15:16:27 +0200 | [diff] [blame] | 168 | doclose: |
| 169 | if (fclose (wav->f)) { |
| 170 | dolog ("wav_fini_out: fclose %p failed\nReason: %s\n", |
| 171 | wav->f, strerror (errno)); |
| 172 | } |
bellard | 85571bc | 2004-11-07 18:04:02 +0000 | [diff] [blame] | 173 | wav->f = NULL; |
bellard | 85571bc | 2004-11-07 18:04:02 +0000 | [diff] [blame] | 174 | } |
| 175 | |
Kővágó, Zoltán | 571a8c5 | 2019-09-19 23:24:22 +0200 | [diff] [blame] | 176 | static void wav_enable_out(HWVoiceOut *hw, bool enable) |
bellard | 85571bc | 2004-11-07 18:04:02 +0000 | [diff] [blame] | 177 | { |
Kővágó, Zoltán | 857271a | 2019-09-19 23:24:21 +0200 | [diff] [blame] | 178 | WAVVoiceOut *wav = (WAVVoiceOut *) hw; |
| 179 | |
Kővágó, Zoltán | 571a8c5 | 2019-09-19 23:24:22 +0200 | [diff] [blame] | 180 | if (enable) { |
Kővágó, Zoltán | 857271a | 2019-09-19 23:24:21 +0200 | [diff] [blame] | 181 | audio_rate_start(&wav->rate); |
| 182 | } |
bellard | 85571bc | 2004-11-07 18:04:02 +0000 | [diff] [blame] | 183 | } |
| 184 | |
Kővágó, Zoltán | 7183022 | 2019-03-08 23:34:15 +0100 | [diff] [blame] | 185 | static void *wav_audio_init(Audiodev *dev) |
bellard | 85571bc | 2004-11-07 18:04:02 +0000 | [diff] [blame] | 186 | { |
Kővágó, Zoltán | 0927d16 | 2019-03-08 23:34:24 +0100 | [diff] [blame] | 187 | assert(dev->driver == AUDIODEV_DRIVER_WAV); |
| 188 | return dev; |
bellard | 85571bc | 2004-11-07 18:04:02 +0000 | [diff] [blame] | 189 | } |
| 190 | |
| 191 | static void wav_audio_fini (void *opaque) |
| 192 | { |
| 193 | ldebug ("wav_fini"); |
| 194 | } |
| 195 | |
blueswir1 | 35f4b58 | 2008-10-06 18:08:30 +0000 | [diff] [blame] | 196 | static struct audio_pcm_ops wav_pcm_ops = { |
Juan Quintela | 1dd3e4d | 2009-08-11 02:31:15 +0200 | [diff] [blame] | 197 | .init_out = wav_init_out, |
| 198 | .fini_out = wav_fini_out, |
Kővágó, Zoltán | ef3612e | 2019-09-19 23:24:18 +0200 | [diff] [blame] | 199 | .write = wav_write_out, |
Kővágó, Zoltán | 571a8c5 | 2019-09-19 23:24:22 +0200 | [diff] [blame] | 200 | .enable_out = wav_enable_out, |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 201 | }; |
| 202 | |
Gerd Hoffmann | d3893a3 | 2018-03-06 08:40:47 +0100 | [diff] [blame] | 203 | static struct audio_driver wav_audio_driver = { |
Juan Quintela | bee37f3 | 2009-08-11 02:31:14 +0200 | [diff] [blame] | 204 | .name = "wav", |
| 205 | .descr = "WAV renderer http://wikipedia.org/wiki/WAV", |
Juan Quintela | bee37f3 | 2009-08-11 02:31:14 +0200 | [diff] [blame] | 206 | .init = wav_audio_init, |
| 207 | .fini = wav_audio_fini, |
malc | 98f9f48 | 2009-08-11 20:48:02 +0400 | [diff] [blame] | 208 | .pcm_ops = &wav_pcm_ops, |
Juan Quintela | bee37f3 | 2009-08-11 02:31:14 +0200 | [diff] [blame] | 209 | .can_be_default = 0, |
| 210 | .max_voices_out = 1, |
| 211 | .max_voices_in = 0, |
| 212 | .voice_size_out = sizeof (WAVVoiceOut), |
| 213 | .voice_size_in = 0 |
bellard | 85571bc | 2004-11-07 18:04:02 +0000 | [diff] [blame] | 214 | }; |
Gerd Hoffmann | d3893a3 | 2018-03-06 08:40:47 +0100 | [diff] [blame] | 215 | |
| 216 | static void register_audio_wav(void) |
| 217 | { |
| 218 | audio_driver_register(&wav_audio_driver); |
| 219 | } |
| 220 | type_init(register_audio_wav); |