xref: /qemu/audio/wavaudio.c (revision 1d14ffa97eacd3cb722271eaf6f093038396eac4)
185571bc7Sbellard /*
2*1d14ffa9Sbellard  * QEMU WAV audio driver
385571bc7Sbellard  *
4*1d14ffa9Sbellard  * 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  */
2485571bc7Sbellard #include "vl.h"
2585571bc7Sbellard 
26*1d14ffa9Sbellard #define AUDIO_CAP "wav"
27*1d14ffa9Sbellard #include "audio_int.h"
28fb065187Sbellard 
29*1d14ffa9Sbellard typedef struct WAVVoiceOut {
30*1d14ffa9Sbellard     HWVoiceOut hw;
31fb065187Sbellard     QEMUFile *f;
32fb065187Sbellard     int64_t old_ticks;
33fb065187Sbellard     void *pcm_buf;
34fb065187Sbellard     int total_samples;
35*1d14ffa9Sbellard } WAVVoiceOut;
3685571bc7Sbellard 
3785571bc7Sbellard static struct {
3885571bc7Sbellard     const char *wav_path;
3985571bc7Sbellard } conf = {
4085571bc7Sbellard     .wav_path = "qemu.wav"
4185571bc7Sbellard };
4285571bc7Sbellard 
43*1d14ffa9Sbellard static int wav_run_out (HWVoiceOut *hw)
4485571bc7Sbellard {
45*1d14ffa9Sbellard     WAVVoiceOut *wav = (WAVVoiceOut *) hw;
4685571bc7Sbellard     int rpos, live, decr, samples;
4785571bc7Sbellard     uint8_t *dst;
4885571bc7Sbellard     st_sample_t *src;
4985571bc7Sbellard     int64_t now = qemu_get_clock (vm_clock);
5085571bc7Sbellard     int64_t ticks = now - wav->old_ticks;
51*1d14ffa9Sbellard     int64_t bytes = (ticks * hw->info.bytes_per_second) / ticks_per_sec;
5285571bc7Sbellard 
53*1d14ffa9Sbellard     if (bytes > INT_MAX) {
54*1d14ffa9Sbellard         samples = INT_MAX >> hw->info.shift;
55*1d14ffa9Sbellard     }
56*1d14ffa9Sbellard     else {
57*1d14ffa9Sbellard         samples = bytes >> hw->info.shift;
58*1d14ffa9Sbellard     }
5985571bc7Sbellard 
60*1d14ffa9Sbellard     live = audio_pcm_hw_get_live_out (hw);
61*1d14ffa9Sbellard     if (!live) {
62*1d14ffa9Sbellard         return 0;
63*1d14ffa9Sbellard     }
6485571bc7Sbellard 
657372f88dSbellard     wav->old_ticks = now;
6685571bc7Sbellard     decr = audio_MIN (live, samples);
6785571bc7Sbellard     samples = decr;
6885571bc7Sbellard     rpos = hw->rpos;
6985571bc7Sbellard     while (samples) {
7085571bc7Sbellard         int left_till_end_samples = hw->samples - rpos;
7185571bc7Sbellard         int convert_samples = audio_MIN (samples, left_till_end_samples);
7285571bc7Sbellard 
73*1d14ffa9Sbellard         src = hw->mix_buf + rpos;
74*1d14ffa9Sbellard         dst = advance (wav->pcm_buf, rpos << hw->info.shift);
7585571bc7Sbellard 
7685571bc7Sbellard         hw->clip (dst, src, convert_samples);
77*1d14ffa9Sbellard         qemu_put_buffer (wav->f, dst, convert_samples << hw->info.shift);
78*1d14ffa9Sbellard         mixeng_clear (src, convert_samples);
7985571bc7Sbellard 
8085571bc7Sbellard         rpos = (rpos + convert_samples) % hw->samples;
8185571bc7Sbellard         samples -= convert_samples;
8285571bc7Sbellard         wav->total_samples += convert_samples;
8385571bc7Sbellard     }
8485571bc7Sbellard 
8585571bc7Sbellard     hw->rpos = rpos;
86*1d14ffa9Sbellard     return decr;
8785571bc7Sbellard }
8885571bc7Sbellard 
89*1d14ffa9Sbellard static int wav_write_out (SWVoiceOut *sw, void *buf, int len)
9085571bc7Sbellard {
91*1d14ffa9Sbellard     return audio_pcm_sw_write (sw, buf, len);
9285571bc7Sbellard }
9385571bc7Sbellard 
9485571bc7Sbellard /* VICE code: Store number as little endian. */
9585571bc7Sbellard static void le_store (uint8_t *buf, uint32_t val, int len)
9685571bc7Sbellard {
9785571bc7Sbellard     int i;
9885571bc7Sbellard     for (i = 0; i < len; i++) {
9985571bc7Sbellard         buf[i] = (uint8_t) (val & 0xff);
10085571bc7Sbellard         val >>= 8;
10185571bc7Sbellard     }
10285571bc7Sbellard }
10385571bc7Sbellard 
104*1d14ffa9Sbellard static int wav_init_out (HWVoiceOut *hw, int freq, int nchannels, audfmt_e fmt)
10585571bc7Sbellard {
106*1d14ffa9Sbellard     WAVVoiceOut *wav = (WAVVoiceOut *) hw;
107*1d14ffa9Sbellard     int bits16;
10885571bc7Sbellard     uint8_t hdr[] = {
10985571bc7Sbellard         0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56,
11085571bc7Sbellard         0x45, 0x66, 0x6d, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00,
11185571bc7Sbellard         0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04,
11285571bc7Sbellard         0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00
11385571bc7Sbellard     };
11485571bc7Sbellard 
115*1d14ffa9Sbellard     freq = audio_state.fixed_freq_out;
116*1d14ffa9Sbellard     fmt = audio_state.fixed_fmt_out;
117*1d14ffa9Sbellard     nchannels = audio_state.fixed_channels_out;
118*1d14ffa9Sbellard 
119*1d14ffa9Sbellard     switch (fmt) {
12085571bc7Sbellard     case AUD_FMT_S8:
12185571bc7Sbellard     case AUD_FMT_U8:
122*1d14ffa9Sbellard         bits16 = 0;
12385571bc7Sbellard         break;
12485571bc7Sbellard 
12585571bc7Sbellard     case AUD_FMT_S16:
12685571bc7Sbellard     case AUD_FMT_U16:
12785571bc7Sbellard         bits16 = 1;
12885571bc7Sbellard         break;
129*1d14ffa9Sbellard 
130*1d14ffa9Sbellard     default:
131*1d14ffa9Sbellard         dolog ("Internal logic error bad format %d\n", fmt);
132*1d14ffa9Sbellard         return -1;
13385571bc7Sbellard     }
13485571bc7Sbellard 
13585571bc7Sbellard     hdr[34] = bits16 ? 0x10 : 0x08;
136*1d14ffa9Sbellard     audio_pcm_init_info (
137*1d14ffa9Sbellard         &hw->info,
138*1d14ffa9Sbellard         freq,
139*1d14ffa9Sbellard         nchannels,
140*1d14ffa9Sbellard         bits16 ? AUD_FMT_S16 : AUD_FMT_U8,
141*1d14ffa9Sbellard         audio_need_to_swap_endian (0)
142*1d14ffa9Sbellard         );
14385571bc7Sbellard     hw->bufsize = 4096;
14485571bc7Sbellard     wav->pcm_buf = qemu_mallocz (hw->bufsize);
145*1d14ffa9Sbellard     if (!wav->pcm_buf) {
146*1d14ffa9Sbellard         dolog ("Can not initialize WAV buffer of %d bytes\n",
147*1d14ffa9Sbellard                hw->bufsize);
14885571bc7Sbellard         return -1;
149*1d14ffa9Sbellard     }
15085571bc7Sbellard 
151*1d14ffa9Sbellard     le_store (hdr + 22, hw->info.nchannels, 2);
152*1d14ffa9Sbellard     le_store (hdr + 24, hw->info.freq, 4);
153*1d14ffa9Sbellard     le_store (hdr + 28, hw->info.freq << (bits16 + (nchannels == 2)), 4);
154*1d14ffa9Sbellard     le_store (hdr + 32, 1 << (bits16 + (nchannels == 2)), 2);
15585571bc7Sbellard 
15685571bc7Sbellard     wav->f = fopen (conf.wav_path, "wb");
15785571bc7Sbellard     if (!wav->f) {
158*1d14ffa9Sbellard         dolog ("Failed to open wave file `%s'\nReason: %s\n",
15985571bc7Sbellard                conf.wav_path, strerror (errno));
1607372f88dSbellard         qemu_free (wav->pcm_buf);
1617372f88dSbellard         wav->pcm_buf = NULL;
16285571bc7Sbellard         return -1;
16385571bc7Sbellard     }
16485571bc7Sbellard 
16585571bc7Sbellard     qemu_put_buffer (wav->f, hdr, sizeof (hdr));
16685571bc7Sbellard     return 0;
16785571bc7Sbellard }
16885571bc7Sbellard 
169*1d14ffa9Sbellard static void wav_fini_out (HWVoiceOut *hw)
17085571bc7Sbellard {
171*1d14ffa9Sbellard     WAVVoiceOut *wav = (WAVVoiceOut *) hw;
172*1d14ffa9Sbellard     int stereo = hw->info.nchannels == 2;
17385571bc7Sbellard     uint8_t rlen[4];
17485571bc7Sbellard     uint8_t dlen[4];
17585571bc7Sbellard     uint32_t rifflen = (wav->total_samples << stereo) + 36;
17685571bc7Sbellard     uint32_t datalen = wav->total_samples << stereo;
17785571bc7Sbellard 
178*1d14ffa9Sbellard     if (!wav->f || !hw->active) {
17985571bc7Sbellard         return;
180*1d14ffa9Sbellard     }
18185571bc7Sbellard 
18285571bc7Sbellard     le_store (rlen, rifflen, 4);
18385571bc7Sbellard     le_store (dlen, datalen, 4);
18485571bc7Sbellard 
18585571bc7Sbellard     qemu_fseek (wav->f, 4, SEEK_SET);
18685571bc7Sbellard     qemu_put_buffer (wav->f, rlen, 4);
18785571bc7Sbellard 
18885571bc7Sbellard     qemu_fseek (wav->f, 32, SEEK_CUR);
18985571bc7Sbellard     qemu_put_buffer (wav->f, dlen, 4);
19085571bc7Sbellard 
19185571bc7Sbellard     fclose (wav->f);
19285571bc7Sbellard     wav->f = NULL;
1937372f88dSbellard 
1947372f88dSbellard     qemu_free (wav->pcm_buf);
1957372f88dSbellard     wav->pcm_buf = NULL;
19685571bc7Sbellard }
19785571bc7Sbellard 
198*1d14ffa9Sbellard static int wav_ctl_out (HWVoiceOut *hw, int cmd, ...)
19985571bc7Sbellard {
20085571bc7Sbellard     (void) hw;
20185571bc7Sbellard     (void) cmd;
20285571bc7Sbellard     return 0;
20385571bc7Sbellard }
20485571bc7Sbellard 
20585571bc7Sbellard static void *wav_audio_init (void)
20685571bc7Sbellard {
20785571bc7Sbellard     return &conf;
20885571bc7Sbellard }
20985571bc7Sbellard 
21085571bc7Sbellard static void wav_audio_fini (void *opaque)
21185571bc7Sbellard {
212*1d14ffa9Sbellard     (void) opaque;
21385571bc7Sbellard     ldebug ("wav_fini");
21485571bc7Sbellard }
21585571bc7Sbellard 
216*1d14ffa9Sbellard struct audio_option wav_options[] = {
217*1d14ffa9Sbellard     {"PATH", AUD_OPT_STR, &conf.wav_path,
218*1d14ffa9Sbellard      "Path to wave file", NULL, 0},
219*1d14ffa9Sbellard     {NULL, 0, NULL, NULL, NULL, 0}
22085571bc7Sbellard };
22185571bc7Sbellard 
222*1d14ffa9Sbellard struct audio_pcm_ops wav_pcm_ops = {
223*1d14ffa9Sbellard     wav_init_out,
224*1d14ffa9Sbellard     wav_fini_out,
225*1d14ffa9Sbellard     wav_run_out,
226*1d14ffa9Sbellard     wav_write_out,
227*1d14ffa9Sbellard     wav_ctl_out,
228*1d14ffa9Sbellard 
229*1d14ffa9Sbellard     NULL,
230*1d14ffa9Sbellard     NULL,
231*1d14ffa9Sbellard     NULL,
232*1d14ffa9Sbellard     NULL,
233*1d14ffa9Sbellard     NULL
234*1d14ffa9Sbellard };
235*1d14ffa9Sbellard 
236*1d14ffa9Sbellard struct audio_driver wav_audio_driver = {
237*1d14ffa9Sbellard     INIT_FIELD (name           = ) "wav",
238*1d14ffa9Sbellard     INIT_FIELD (descr          = )
239*1d14ffa9Sbellard     "WAV renderer http://wikipedia.org/wiki/WAV",
240*1d14ffa9Sbellard     INIT_FIELD (options        = ) wav_options,
241*1d14ffa9Sbellard     INIT_FIELD (init           = ) wav_audio_init,
242*1d14ffa9Sbellard     INIT_FIELD (fini           = ) wav_audio_fini,
243*1d14ffa9Sbellard     INIT_FIELD (pcm_ops        = ) &wav_pcm_ops,
244*1d14ffa9Sbellard     INIT_FIELD (can_be_default = ) 0,
245*1d14ffa9Sbellard     INIT_FIELD (max_voices_out = ) 1,
246*1d14ffa9Sbellard     INIT_FIELD (max_voices_in  = ) 0,
247*1d14ffa9Sbellard     INIT_FIELD (voice_size_out = ) sizeof (WAVVoiceOut),
248*1d14ffa9Sbellard     INIT_FIELD (voice_size_in  = ) 0
24985571bc7Sbellard };
250