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; |
Volker Rümelin | 613fe02 | 2022-09-23 20:36:36 +0200 | [diff] [blame] | 45 | int64_t bytes = audio_rate_get_bytes(&wav->rate, &hw->info, 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); |
Markus Armbruster | ceb19c8 | 2022-11-04 17:06:49 +0100 | [diff] [blame] | 81 | const char *wav_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 | |
Daniel P. Berrangé | 5b4edd7 | 2021-03-03 17:43:13 +0000 | [diff] [blame] | 100 | case AUDIO_FORMAT_F32: |
| 101 | dolog("WAVE files can not handle float formats\n"); |
| 102 | return -1; |
| 103 | |
Kővágó, Zoltán | 85bc585 | 2019-03-08 23:34:13 +0100 | [diff] [blame] | 104 | default: |
| 105 | abort(); |
bellard | 85571bc | 2004-11-07 18:04:02 +0000 | [diff] [blame] | 106 | } |
| 107 | |
| 108 | hdr[34] = bits16 ? 0x10 : 0x08; |
bellard | c0fe382 | 2005-11-05 18:55:28 +0000 | [diff] [blame] | 109 | |
bellard | d929eba | 2006-07-04 21:47:22 +0000 | [diff] [blame] | 110 | wav_as.endianness = 0; |
| 111 | audio_pcm_init_info (&hw->info, &wav_as); |
bellard | c0fe382 | 2005-11-05 18:55:28 +0000 | [diff] [blame] | 112 | |
| 113 | hw->samples = 1024; |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 114 | le_store (hdr + 22, hw->info.nchannels, 2); |
| 115 | le_store (hdr + 24, hw->info.freq, 4); |
bellard | c0fe382 | 2005-11-05 18:55:28 +0000 | [diff] [blame] | 116 | le_store (hdr + 28, hw->info.freq << (bits16 + stereo), 4); |
| 117 | le_store (hdr + 32, 1 << (bits16 + stereo), 2); |
bellard | 85571bc | 2004-11-07 18:04:02 +0000 | [diff] [blame] | 118 | |
Kővágó, Zoltán | 0927d16 | 2019-03-08 23:34:24 +0100 | [diff] [blame] | 119 | wav->f = fopen(wav_path, "wb"); |
bellard | 85571bc | 2004-11-07 18:04:02 +0000 | [diff] [blame] | 120 | if (!wav->f) { |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 121 | 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] | 122 | wav_path, strerror(errno)); |
bellard | 85571bc | 2004-11-07 18:04:02 +0000 | [diff] [blame] | 123 | return -1; |
| 124 | } |
| 125 | |
Juan Quintela | 27acf66 | 2011-09-20 15:16:27 +0200 | [diff] [blame] | 126 | if (fwrite (hdr, sizeof (hdr), 1, wav->f) != 1) { |
| 127 | dolog ("wav_init_out: failed to write header\nReason: %s\n", |
| 128 | strerror(errno)); |
| 129 | return -1; |
| 130 | } |
Kővágó, Zoltán | 857271a | 2019-09-19 23:24:21 +0200 | [diff] [blame] | 131 | |
| 132 | audio_rate_start(&wav->rate); |
bellard | 85571bc | 2004-11-07 18:04:02 +0000 | [diff] [blame] | 133 | return 0; |
| 134 | } |
| 135 | |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 136 | static void wav_fini_out (HWVoiceOut *hw) |
bellard | 85571bc | 2004-11-07 18:04:02 +0000 | [diff] [blame] | 137 | { |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 138 | WAVVoiceOut *wav = (WAVVoiceOut *) hw; |
bellard | 85571bc | 2004-11-07 18:04:02 +0000 | [diff] [blame] | 139 | uint8_t rlen[4]; |
| 140 | uint8_t dlen[4]; |
Kővágó, Zoltán | 2b9cce8 | 2019-10-13 21:58:02 +0200 | [diff] [blame] | 141 | uint32_t datalen = wav->total_samples * hw->info.bytes_per_frame; |
bellard | 5090353 | 2005-11-20 16:22:16 +0000 | [diff] [blame] | 142 | uint32_t rifflen = datalen + 36; |
bellard | 85571bc | 2004-11-07 18:04:02 +0000 | [diff] [blame] | 143 | |
bellard | c0fe382 | 2005-11-05 18:55:28 +0000 | [diff] [blame] | 144 | if (!wav->f) { |
bellard | 85571bc | 2004-11-07 18:04:02 +0000 | [diff] [blame] | 145 | return; |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 146 | } |
bellard | 85571bc | 2004-11-07 18:04:02 +0000 | [diff] [blame] | 147 | |
| 148 | le_store (rlen, rifflen, 4); |
| 149 | le_store (dlen, datalen, 4); |
| 150 | |
Juan Quintela | 27acf66 | 2011-09-20 15:16:27 +0200 | [diff] [blame] | 151 | if (fseek (wav->f, 4, SEEK_SET)) { |
| 152 | dolog ("wav_fini_out: fseek to rlen failed\nReason: %s\n", |
| 153 | strerror(errno)); |
| 154 | goto doclose; |
| 155 | } |
| 156 | if (fwrite (rlen, 4, 1, wav->f) != 1) { |
| 157 | dolog ("wav_fini_out: failed to write rlen\nReason: %s\n", |
| 158 | strerror (errno)); |
| 159 | goto doclose; |
| 160 | } |
| 161 | if (fseek (wav->f, 32, SEEK_CUR)) { |
| 162 | dolog ("wav_fini_out: fseek to dlen failed\nReason: %s\n", |
| 163 | strerror (errno)); |
| 164 | goto doclose; |
| 165 | } |
| 166 | if (fwrite (dlen, 4, 1, wav->f) != 1) { |
| 167 | dolog ("wav_fini_out: failed to write dlen\nReaons: %s\n", |
| 168 | strerror (errno)); |
| 169 | goto doclose; |
| 170 | } |
bellard | 85571bc | 2004-11-07 18:04:02 +0000 | [diff] [blame] | 171 | |
Juan Quintela | 27acf66 | 2011-09-20 15:16:27 +0200 | [diff] [blame] | 172 | doclose: |
| 173 | if (fclose (wav->f)) { |
| 174 | dolog ("wav_fini_out: fclose %p failed\nReason: %s\n", |
| 175 | wav->f, strerror (errno)); |
| 176 | } |
bellard | 85571bc | 2004-11-07 18:04:02 +0000 | [diff] [blame] | 177 | wav->f = NULL; |
bellard | 85571bc | 2004-11-07 18:04:02 +0000 | [diff] [blame] | 178 | } |
| 179 | |
Kővágó, Zoltán | 571a8c5 | 2019-09-19 23:24:22 +0200 | [diff] [blame] | 180 | static void wav_enable_out(HWVoiceOut *hw, bool enable) |
bellard | 85571bc | 2004-11-07 18:04:02 +0000 | [diff] [blame] | 181 | { |
Kővágó, Zoltán | 857271a | 2019-09-19 23:24:21 +0200 | [diff] [blame] | 182 | WAVVoiceOut *wav = (WAVVoiceOut *) hw; |
| 183 | |
Kővágó, Zoltán | 571a8c5 | 2019-09-19 23:24:22 +0200 | [diff] [blame] | 184 | if (enable) { |
Kővágó, Zoltán | 857271a | 2019-09-19 23:24:21 +0200 | [diff] [blame] | 185 | audio_rate_start(&wav->rate); |
| 186 | } |
bellard | 85571bc | 2004-11-07 18:04:02 +0000 | [diff] [blame] | 187 | } |
| 188 | |
Paolo Bonzini | f606173 | 2023-09-22 19:13:44 +0200 | [diff] [blame] | 189 | static void *wav_audio_init(Audiodev *dev, Error **errp) |
bellard | 85571bc | 2004-11-07 18:04:02 +0000 | [diff] [blame] | 190 | { |
Kővágó, Zoltán | 0927d16 | 2019-03-08 23:34:24 +0100 | [diff] [blame] | 191 | assert(dev->driver == AUDIODEV_DRIVER_WAV); |
| 192 | return dev; |
bellard | 85571bc | 2004-11-07 18:04:02 +0000 | [diff] [blame] | 193 | } |
| 194 | |
| 195 | static void wav_audio_fini (void *opaque) |
| 196 | { |
| 197 | ldebug ("wav_fini"); |
| 198 | } |
| 199 | |
blueswir1 | 35f4b58 | 2008-10-06 18:08:30 +0000 | [diff] [blame] | 200 | static struct audio_pcm_ops wav_pcm_ops = { |
Juan Quintela | 1dd3e4d | 2009-08-11 02:31:15 +0200 | [diff] [blame] | 201 | .init_out = wav_init_out, |
| 202 | .fini_out = wav_fini_out, |
Kővágó, Zoltán | ef3612e | 2019-09-19 23:24:18 +0200 | [diff] [blame] | 203 | .write = wav_write_out, |
Volker Rümelin | 9833438 | 2022-03-01 20:13:06 +0100 | [diff] [blame] | 204 | .buffer_get_free = audio_generic_buffer_get_free, |
Volker Rümelin | fdc8c5f | 2020-01-23 08:49:39 +0100 | [diff] [blame] | 205 | .run_buffer_out = audio_generic_run_buffer_out, |
Kővágó, Zoltán | 571a8c5 | 2019-09-19 23:24:22 +0200 | [diff] [blame] | 206 | .enable_out = wav_enable_out, |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 207 | }; |
| 208 | |
Gerd Hoffmann | d3893a3 | 2018-03-06 08:40:47 +0100 | [diff] [blame] | 209 | static struct audio_driver wav_audio_driver = { |
Juan Quintela | bee37f3 | 2009-08-11 02:31:14 +0200 | [diff] [blame] | 210 | .name = "wav", |
| 211 | .descr = "WAV renderer http://wikipedia.org/wiki/WAV", |
Juan Quintela | bee37f3 | 2009-08-11 02:31:14 +0200 | [diff] [blame] | 212 | .init = wav_audio_init, |
| 213 | .fini = wav_audio_fini, |
malc | 98f9f48 | 2009-08-11 20:48:02 +0400 | [diff] [blame] | 214 | .pcm_ops = &wav_pcm_ops, |
Juan Quintela | bee37f3 | 2009-08-11 02:31:14 +0200 | [diff] [blame] | 215 | .max_voices_out = 1, |
| 216 | .max_voices_in = 0, |
| 217 | .voice_size_out = sizeof (WAVVoiceOut), |
| 218 | .voice_size_in = 0 |
bellard | 85571bc | 2004-11-07 18:04:02 +0000 | [diff] [blame] | 219 | }; |
Gerd Hoffmann | d3893a3 | 2018-03-06 08:40:47 +0100 | [diff] [blame] | 220 | |
| 221 | static void register_audio_wav(void) |
| 222 | { |
| 223 | audio_driver_register(&wav_audio_driver); |
| 224 | } |
| 225 | type_init(register_audio_wav); |