xref: /qemu/audio/wavaudio.c (revision 0927d1665738df3b7b33d8e8790d52bd20c63506)
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  */
246086a565SPeter Maydell #include "qemu/osdep.h"
2587776ab7SPaolo Bonzini #include "qemu/host-utils.h"
261de7afc9SPaolo Bonzini #include "qemu/timer.h"
27*0927d166SKővágó, Zoltán #include "qapi/opts-visitor.h"
2887ecb68bSpbrook #include "audio.h"
2985571bc7Sbellard 
301d14ffa9Sbellard #define AUDIO_CAP "wav"
311d14ffa9Sbellard #include "audio_int.h"
32fb065187Sbellard 
331d14ffa9Sbellard typedef struct WAVVoiceOut {
341d14ffa9Sbellard     HWVoiceOut hw;
3527acf660SJuan Quintela     FILE *f;
36fb065187Sbellard     int64_t old_ticks;
37fb065187Sbellard     void *pcm_buf;
38fb065187Sbellard     int total_samples;
391d14ffa9Sbellard } WAVVoiceOut;
4085571bc7Sbellard 
41bdff253cSmalc static int wav_run_out (HWVoiceOut *hw, int live)
4285571bc7Sbellard {
431d14ffa9Sbellard     WAVVoiceOut *wav = (WAVVoiceOut *) hw;
44bdff253cSmalc     int rpos, decr, samples;
4585571bc7Sbellard     uint8_t *dst;
461ea879e5Smalc     struct st_sample *src;
47bc72ad67SAlex Bligh     int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
4885571bc7Sbellard     int64_t ticks = now - wav->old_ticks;
494f4cc0efSmalc     int64_t bytes =
5073bcb24dSRutuja Shah         muldiv64(ticks, hw->info.bytes_per_second, NANOSECONDS_PER_SECOND);
5185571bc7Sbellard 
521d14ffa9Sbellard     if (bytes > INT_MAX) {
531d14ffa9Sbellard         samples = INT_MAX >> hw->info.shift;
541d14ffa9Sbellard     }
551d14ffa9Sbellard     else {
561d14ffa9Sbellard         samples = bytes >> hw->info.shift;
571d14ffa9Sbellard     }
5885571bc7Sbellard 
597372f88dSbellard     wav->old_ticks = now;
6085571bc7Sbellard     decr = audio_MIN (live, samples);
6185571bc7Sbellard     samples = decr;
6285571bc7Sbellard     rpos = hw->rpos;
6385571bc7Sbellard     while (samples) {
6485571bc7Sbellard         int left_till_end_samples = hw->samples - rpos;
6585571bc7Sbellard         int convert_samples = audio_MIN (samples, left_till_end_samples);
6685571bc7Sbellard 
671d14ffa9Sbellard         src = hw->mix_buf + rpos;
681d14ffa9Sbellard         dst = advance (wav->pcm_buf, rpos << hw->info.shift);
6985571bc7Sbellard 
7085571bc7Sbellard         hw->clip (dst, src, convert_samples);
7127acf660SJuan Quintela         if (fwrite (dst, convert_samples << hw->info.shift, 1, wav->f) != 1) {
7227acf660SJuan Quintela             dolog ("wav_run_out: fwrite of %d bytes failed\nReaons: %s\n",
7327acf660SJuan Quintela                    convert_samples << hw->info.shift, strerror (errno));
7427acf660SJuan Quintela         }
7585571bc7Sbellard 
7685571bc7Sbellard         rpos = (rpos + convert_samples) % hw->samples;
7785571bc7Sbellard         samples -= convert_samples;
7885571bc7Sbellard         wav->total_samples += convert_samples;
7985571bc7Sbellard     }
8085571bc7Sbellard 
8185571bc7Sbellard     hw->rpos = rpos;
821d14ffa9Sbellard     return decr;
8385571bc7Sbellard }
8485571bc7Sbellard 
851d14ffa9Sbellard static int wav_write_out (SWVoiceOut *sw, void *buf, int len)
8685571bc7Sbellard {
871d14ffa9Sbellard     return audio_pcm_sw_write (sw, buf, len);
8885571bc7Sbellard }
8985571bc7Sbellard 
9085571bc7Sbellard /* VICE code: Store number as little endian. */
9185571bc7Sbellard static void le_store (uint8_t *buf, uint32_t val, int len)
9285571bc7Sbellard {
9385571bc7Sbellard     int i;
9485571bc7Sbellard     for (i = 0; i < len; i++) {
9585571bc7Sbellard         buf[i] = (uint8_t) (val & 0xff);
9685571bc7Sbellard         val >>= 8;
9785571bc7Sbellard     }
9885571bc7Sbellard }
9985571bc7Sbellard 
1005706db1dSKővágó, Zoltán static int wav_init_out(HWVoiceOut *hw, struct audsettings *as,
1015706db1dSKővágó, Zoltán                         void *drv_opaque)
10285571bc7Sbellard {
1031d14ffa9Sbellard     WAVVoiceOut *wav = (WAVVoiceOut *) hw;
104c0fe3827Sbellard     int bits16 = 0, stereo = 0;
10585571bc7Sbellard     uint8_t hdr[] = {
10685571bc7Sbellard         0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56,
10785571bc7Sbellard         0x45, 0x66, 0x6d, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00,
10885571bc7Sbellard         0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04,
10985571bc7Sbellard         0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00
11085571bc7Sbellard     };
111*0927d166SKővágó, Zoltán     Audiodev *dev = drv_opaque;
112*0927d166SKővágó, Zoltán     AudiodevWavOptions *wopts = &dev->u.wav;
113*0927d166SKővágó, Zoltán     struct audsettings wav_as = audiodev_to_audsettings(dev->u.wav.out);
114*0927d166SKővágó, Zoltán     const char *wav_path = wopts->has_path ? wopts->path : "qemu.wav";
11585571bc7Sbellard 
116c0fe3827Sbellard     stereo = wav_as.nchannels == 2;
117c0fe3827Sbellard     switch (wav_as.fmt) {
11885bc5852SKővágó, Zoltán     case AUDIO_FORMAT_S8:
11985bc5852SKővágó, Zoltán     case AUDIO_FORMAT_U8:
1201d14ffa9Sbellard         bits16 = 0;
12185571bc7Sbellard         break;
12285571bc7Sbellard 
12385bc5852SKővágó, Zoltán     case AUDIO_FORMAT_S16:
12485bc5852SKővágó, Zoltán     case AUDIO_FORMAT_U16:
12585571bc7Sbellard         bits16 = 1;
12685571bc7Sbellard         break;
127f941aa25Sths 
12885bc5852SKővágó, Zoltán     case AUDIO_FORMAT_S32:
12985bc5852SKővágó, Zoltán     case AUDIO_FORMAT_U32:
130f941aa25Sths         dolog ("WAVE files can not handle 32bit formats\n");
131f941aa25Sths         return -1;
13285bc5852SKővágó, Zoltán 
13385bc5852SKővágó, Zoltán     default:
13485bc5852SKővágó, Zoltán         abort();
13585571bc7Sbellard     }
13685571bc7Sbellard 
13785571bc7Sbellard     hdr[34] = bits16 ? 0x10 : 0x08;
138c0fe3827Sbellard 
139d929eba5Sbellard     wav_as.endianness = 0;
140d929eba5Sbellard     audio_pcm_init_info (&hw->info, &wav_as);
141c0fe3827Sbellard 
142c0fe3827Sbellard     hw->samples = 1024;
143470bcabdSAlistair Francis     wav->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift);
1441d14ffa9Sbellard     if (!wav->pcm_buf) {
145c0fe3827Sbellard         dolog ("Could not allocate buffer (%d bytes)\n",
146c0fe3827Sbellard                hw->samples << hw->info.shift);
14785571bc7Sbellard         return -1;
1481d14ffa9Sbellard     }
14985571bc7Sbellard 
1501d14ffa9Sbellard     le_store (hdr + 22, hw->info.nchannels, 2);
1511d14ffa9Sbellard     le_store (hdr + 24, hw->info.freq, 4);
152c0fe3827Sbellard     le_store (hdr + 28, hw->info.freq << (bits16 + stereo), 4);
153c0fe3827Sbellard     le_store (hdr + 32, 1 << (bits16 + stereo), 2);
15485571bc7Sbellard 
155*0927d166SKővágó, Zoltán     wav->f = fopen(wav_path, "wb");
15685571bc7Sbellard     if (!wav->f) {
1571d14ffa9Sbellard         dolog ("Failed to open wave file `%s'\nReason: %s\n",
158*0927d166SKővágó, Zoltán                wav_path, strerror(errno));
1597267c094SAnthony Liguori         g_free (wav->pcm_buf);
1607372f88dSbellard         wav->pcm_buf = NULL;
16185571bc7Sbellard         return -1;
16285571bc7Sbellard     }
16385571bc7Sbellard 
16427acf660SJuan Quintela     if (fwrite (hdr, sizeof (hdr), 1, wav->f) != 1) {
16527acf660SJuan Quintela         dolog ("wav_init_out: failed to write header\nReason: %s\n",
16627acf660SJuan Quintela                strerror(errno));
16727acf660SJuan Quintela         return -1;
16827acf660SJuan Quintela     }
16985571bc7Sbellard     return 0;
17085571bc7Sbellard }
17185571bc7Sbellard 
1721d14ffa9Sbellard static void wav_fini_out (HWVoiceOut *hw)
17385571bc7Sbellard {
1741d14ffa9Sbellard     WAVVoiceOut *wav = (WAVVoiceOut *) hw;
17585571bc7Sbellard     uint8_t rlen[4];
17685571bc7Sbellard     uint8_t dlen[4];
17750903530Sbellard     uint32_t datalen = wav->total_samples << hw->info.shift;
17850903530Sbellard     uint32_t rifflen = datalen + 36;
17985571bc7Sbellard 
180c0fe3827Sbellard     if (!wav->f) {
18185571bc7Sbellard         return;
1821d14ffa9Sbellard     }
18385571bc7Sbellard 
18485571bc7Sbellard     le_store (rlen, rifflen, 4);
18585571bc7Sbellard     le_store (dlen, datalen, 4);
18685571bc7Sbellard 
18727acf660SJuan Quintela     if (fseek (wav->f, 4, SEEK_SET)) {
18827acf660SJuan Quintela         dolog ("wav_fini_out: fseek to rlen failed\nReason: %s\n",
18927acf660SJuan Quintela                strerror(errno));
19027acf660SJuan Quintela         goto doclose;
19127acf660SJuan Quintela     }
19227acf660SJuan Quintela     if (fwrite (rlen, 4, 1, wav->f) != 1) {
19327acf660SJuan Quintela         dolog ("wav_fini_out: failed to write rlen\nReason: %s\n",
19427acf660SJuan Quintela                strerror (errno));
19527acf660SJuan Quintela         goto doclose;
19627acf660SJuan Quintela     }
19727acf660SJuan Quintela     if (fseek (wav->f, 32, SEEK_CUR)) {
19827acf660SJuan Quintela         dolog ("wav_fini_out: fseek to dlen failed\nReason: %s\n",
19927acf660SJuan Quintela                strerror (errno));
20027acf660SJuan Quintela         goto doclose;
20127acf660SJuan Quintela     }
20227acf660SJuan Quintela     if (fwrite (dlen, 4, 1, wav->f) != 1) {
20327acf660SJuan Quintela         dolog ("wav_fini_out: failed to write dlen\nReaons: %s\n",
20427acf660SJuan Quintela                strerror (errno));
20527acf660SJuan Quintela         goto doclose;
20627acf660SJuan Quintela     }
20785571bc7Sbellard 
20827acf660SJuan Quintela  doclose:
20927acf660SJuan Quintela     if (fclose (wav->f))  {
21027acf660SJuan Quintela         dolog ("wav_fini_out: fclose %p failed\nReason: %s\n",
21127acf660SJuan Quintela                wav->f, strerror (errno));
21227acf660SJuan Quintela     }
21385571bc7Sbellard     wav->f = NULL;
2147372f88dSbellard 
2157267c094SAnthony Liguori     g_free (wav->pcm_buf);
2167372f88dSbellard     wav->pcm_buf = NULL;
21785571bc7Sbellard }
21885571bc7Sbellard 
2191d14ffa9Sbellard static int wav_ctl_out (HWVoiceOut *hw, int cmd, ...)
22085571bc7Sbellard {
22185571bc7Sbellard     (void) hw;
22285571bc7Sbellard     (void) cmd;
22385571bc7Sbellard     return 0;
22485571bc7Sbellard }
22585571bc7Sbellard 
22671830221SKővágó, Zoltán static void *wav_audio_init(Audiodev *dev)
22785571bc7Sbellard {
228*0927d166SKővágó, Zoltán     assert(dev->driver == AUDIODEV_DRIVER_WAV);
229*0927d166SKővágó, Zoltán     return dev;
23085571bc7Sbellard }
23185571bc7Sbellard 
23285571bc7Sbellard static void wav_audio_fini (void *opaque)
23385571bc7Sbellard {
23485571bc7Sbellard     ldebug ("wav_fini");
23585571bc7Sbellard }
23685571bc7Sbellard 
23735f4b58cSblueswir1 static struct audio_pcm_ops wav_pcm_ops = {
2381dd3e4d1SJuan Quintela     .init_out = wav_init_out,
2391dd3e4d1SJuan Quintela     .fini_out = wav_fini_out,
2401dd3e4d1SJuan Quintela     .run_out  = wav_run_out,
2411dd3e4d1SJuan Quintela     .write    = wav_write_out,
2421dd3e4d1SJuan Quintela     .ctl_out  = wav_ctl_out,
2431d14ffa9Sbellard };
2441d14ffa9Sbellard 
245d3893a39SGerd Hoffmann static struct audio_driver wav_audio_driver = {
246bee37f32SJuan Quintela     .name           = "wav",
247bee37f32SJuan Quintela     .descr          = "WAV renderer http://wikipedia.org/wiki/WAV",
248bee37f32SJuan Quintela     .init           = wav_audio_init,
249bee37f32SJuan Quintela     .fini           = wav_audio_fini,
250bee37f32SJuan Quintela     .pcm_ops        = &wav_pcm_ops,
251bee37f32SJuan Quintela     .can_be_default = 0,
252bee37f32SJuan Quintela     .max_voices_out = 1,
253bee37f32SJuan Quintela     .max_voices_in  = 0,
254bee37f32SJuan Quintela     .voice_size_out = sizeof (WAVVoiceOut),
255bee37f32SJuan Quintela     .voice_size_in  = 0
25685571bc7Sbellard };
257d3893a39SGerd Hoffmann 
258d3893a39SGerd Hoffmann static void register_audio_wav(void)
259d3893a39SGerd Hoffmann {
260d3893a39SGerd Hoffmann     audio_driver_register(&wav_audio_driver);
261d3893a39SGerd Hoffmann }
262d3893a39SGerd Hoffmann type_init(register_audio_wav);
263