xref: /qemu/audio/wavaudio.c (revision 7372f88dc171775c2918b3a874edf0a1d5266b19)
185571bc7Sbellard /*
285571bc7Sbellard  * QEMU WAV audio output driver
385571bc7Sbellard  *
485571bc7Sbellard  * Copyright (c) 2004 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 
26fb065187Sbellard #include "audio/audio_int.h"
27fb065187Sbellard 
28fb065187Sbellard typedef struct WAVVoice {
29fb065187Sbellard     HWVoice hw;
30fb065187Sbellard     QEMUFile *f;
31fb065187Sbellard     int64_t old_ticks;
32fb065187Sbellard     void *pcm_buf;
33fb065187Sbellard     int total_samples;
34fb065187Sbellard } WAVVoice;
35fb065187Sbellard 
36fb065187Sbellard #define dolog(...) AUD_log ("wav", __VA_ARGS__)
37fb065187Sbellard #ifdef DEBUG
38fb065187Sbellard #define ldebug(...) dolog (__VA_ARGS__)
39fb065187Sbellard #else
40fb065187Sbellard #define ldebug(...)
41fb065187Sbellard #endif
4285571bc7Sbellard 
4385571bc7Sbellard static struct {
4485571bc7Sbellard     const char *wav_path;
4585571bc7Sbellard } conf = {
4685571bc7Sbellard     .wav_path = "qemu.wav"
4785571bc7Sbellard };
4885571bc7Sbellard 
4985571bc7Sbellard static void wav_hw_run (HWVoice *hw)
5085571bc7Sbellard {
5185571bc7Sbellard     WAVVoice *wav = (WAVVoice *) hw;
5285571bc7Sbellard     int rpos, live, decr, samples;
5385571bc7Sbellard     uint8_t *dst;
5485571bc7Sbellard     st_sample_t *src;
5585571bc7Sbellard     int64_t now = qemu_get_clock (vm_clock);
5685571bc7Sbellard     int64_t ticks = now - wav->old_ticks;
5785571bc7Sbellard     int64_t bytes = (ticks * hw->bytes_per_second) / ticks_per_sec;
5885571bc7Sbellard 
5985571bc7Sbellard     if (bytes > INT_MAX)
6085571bc7Sbellard         samples = INT_MAX >> hw->shift;
6185571bc7Sbellard     else
6285571bc7Sbellard         samples = bytes >> hw->shift;
6385571bc7Sbellard 
6485571bc7Sbellard     live = pcm_hw_get_live (hw);
6585571bc7Sbellard     if (live <= 0)
6685571bc7Sbellard         return;
6785571bc7Sbellard 
68*7372f88dSbellard     wav->old_ticks = now;
6985571bc7Sbellard     decr = audio_MIN (live, samples);
7085571bc7Sbellard     samples = decr;
7185571bc7Sbellard     rpos = hw->rpos;
7285571bc7Sbellard     while (samples) {
7385571bc7Sbellard         int left_till_end_samples = hw->samples - rpos;
7485571bc7Sbellard         int convert_samples = audio_MIN (samples, left_till_end_samples);
7585571bc7Sbellard 
7685571bc7Sbellard         src = advance (hw->mix_buf, rpos * sizeof (st_sample_t));
7785571bc7Sbellard         dst = advance (wav->pcm_buf, rpos << hw->shift);
7885571bc7Sbellard 
7985571bc7Sbellard         hw->clip (dst, src, convert_samples);
8085571bc7Sbellard         qemu_put_buffer (wav->f, dst, convert_samples << hw->shift);
8185571bc7Sbellard         memset (src, 0, convert_samples * sizeof (st_sample_t));
8285571bc7Sbellard 
8385571bc7Sbellard         rpos = (rpos + convert_samples) % hw->samples;
8485571bc7Sbellard         samples -= convert_samples;
8585571bc7Sbellard         wav->total_samples += convert_samples;
8685571bc7Sbellard     }
8785571bc7Sbellard 
8885571bc7Sbellard     pcm_hw_dec_live (hw, decr);
8985571bc7Sbellard     hw->rpos = rpos;
9085571bc7Sbellard }
9185571bc7Sbellard 
9285571bc7Sbellard static int wav_hw_write (SWVoice *sw, void *buf, int len)
9385571bc7Sbellard {
9485571bc7Sbellard     return pcm_hw_write (sw, buf, len);
9585571bc7Sbellard }
9685571bc7Sbellard 
9785571bc7Sbellard /* VICE code: Store number as little endian. */
9885571bc7Sbellard static void le_store (uint8_t *buf, uint32_t val, int len)
9985571bc7Sbellard {
10085571bc7Sbellard     int i;
10185571bc7Sbellard     for (i = 0; i < len; i++) {
10285571bc7Sbellard         buf[i] = (uint8_t) (val & 0xff);
10385571bc7Sbellard         val >>= 8;
10485571bc7Sbellard     }
10585571bc7Sbellard }
10685571bc7Sbellard 
10785571bc7Sbellard static int wav_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt)
10885571bc7Sbellard {
10985571bc7Sbellard     WAVVoice *wav = (WAVVoice *) hw;
11085571bc7Sbellard     int bits16 = 0, stereo = audio_state.fixed_channels == 2;
11185571bc7Sbellard     uint8_t hdr[] = {
11285571bc7Sbellard         0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56,
11385571bc7Sbellard         0x45, 0x66, 0x6d, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00,
11485571bc7Sbellard         0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04,
11585571bc7Sbellard         0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00
11685571bc7Sbellard     };
11785571bc7Sbellard 
11885571bc7Sbellard     switch (audio_state.fixed_fmt) {
11985571bc7Sbellard     case AUD_FMT_S8:
12085571bc7Sbellard     case AUD_FMT_U8:
12185571bc7Sbellard         break;
12285571bc7Sbellard 
12385571bc7Sbellard     case AUD_FMT_S16:
12485571bc7Sbellard     case AUD_FMT_U16:
12585571bc7Sbellard         bits16 = 1;
12685571bc7Sbellard         break;
12785571bc7Sbellard     }
12885571bc7Sbellard 
12985571bc7Sbellard     hdr[34] = bits16 ? 0x10 : 0x08;
13085571bc7Sbellard     hw->freq = 44100;
13185571bc7Sbellard     hw->nchannels = stereo ? 2 : 1;
13285571bc7Sbellard     hw->fmt = bits16 ? AUD_FMT_S16 : AUD_FMT_U8;
13385571bc7Sbellard     hw->bufsize = 4096;
13485571bc7Sbellard     wav->pcm_buf = qemu_mallocz (hw->bufsize);
13585571bc7Sbellard     if (!wav->pcm_buf)
13685571bc7Sbellard         return -1;
13785571bc7Sbellard 
13885571bc7Sbellard     le_store (hdr + 22, hw->nchannels, 2);
13985571bc7Sbellard     le_store (hdr + 24, hw->freq, 4);
14085571bc7Sbellard     le_store (hdr + 28, hw->freq << (bits16 + stereo), 4);
14185571bc7Sbellard     le_store (hdr + 32, 1 << (bits16 + stereo), 2);
14285571bc7Sbellard 
14385571bc7Sbellard     wav->f = fopen (conf.wav_path, "wb");
14485571bc7Sbellard     if (!wav->f) {
14585571bc7Sbellard         dolog ("failed to open wave file `%s'\nReason: %s\n",
14685571bc7Sbellard                conf.wav_path, strerror (errno));
147*7372f88dSbellard         qemu_free (wav->pcm_buf);
148*7372f88dSbellard         wav->pcm_buf = NULL;
14985571bc7Sbellard         return -1;
15085571bc7Sbellard     }
15185571bc7Sbellard 
15285571bc7Sbellard     qemu_put_buffer (wav->f, hdr, sizeof (hdr));
15385571bc7Sbellard     return 0;
15485571bc7Sbellard }
15585571bc7Sbellard 
15685571bc7Sbellard static void wav_hw_fini (HWVoice *hw)
15785571bc7Sbellard {
15885571bc7Sbellard     WAVVoice *wav = (WAVVoice *) hw;
15985571bc7Sbellard     int stereo = hw->nchannels == 2;
16085571bc7Sbellard     uint8_t rlen[4];
16185571bc7Sbellard     uint8_t dlen[4];
16285571bc7Sbellard     uint32_t rifflen = (wav->total_samples << stereo) + 36;
16385571bc7Sbellard     uint32_t datalen = wav->total_samples << stereo;
16485571bc7Sbellard 
16585571bc7Sbellard     if (!wav->f || !hw->active)
16685571bc7Sbellard         return;
16785571bc7Sbellard 
16885571bc7Sbellard     le_store (rlen, rifflen, 4);
16985571bc7Sbellard     le_store (dlen, datalen, 4);
17085571bc7Sbellard 
17185571bc7Sbellard     qemu_fseek (wav->f, 4, SEEK_SET);
17285571bc7Sbellard     qemu_put_buffer (wav->f, rlen, 4);
17385571bc7Sbellard 
17485571bc7Sbellard     qemu_fseek (wav->f, 32, SEEK_CUR);
17585571bc7Sbellard     qemu_put_buffer (wav->f, dlen, 4);
17685571bc7Sbellard 
17785571bc7Sbellard     fclose (wav->f);
17885571bc7Sbellard     wav->f = NULL;
179*7372f88dSbellard 
180*7372f88dSbellard     qemu_free (wav->pcm_buf);
181*7372f88dSbellard     wav->pcm_buf = NULL;
18285571bc7Sbellard }
18385571bc7Sbellard 
18485571bc7Sbellard static int wav_hw_ctl (HWVoice *hw, int cmd, ...)
18585571bc7Sbellard {
18685571bc7Sbellard     (void) hw;
18785571bc7Sbellard     (void) cmd;
18885571bc7Sbellard     return 0;
18985571bc7Sbellard }
19085571bc7Sbellard 
19185571bc7Sbellard static void *wav_audio_init (void)
19285571bc7Sbellard {
19385571bc7Sbellard     return &conf;
19485571bc7Sbellard }
19585571bc7Sbellard 
19685571bc7Sbellard static void wav_audio_fini (void *opaque)
19785571bc7Sbellard {
19885571bc7Sbellard     ldebug ("wav_fini");
19985571bc7Sbellard }
20085571bc7Sbellard 
20185571bc7Sbellard struct pcm_ops wav_pcm_ops = {
20285571bc7Sbellard     wav_hw_init,
20385571bc7Sbellard     wav_hw_fini,
20485571bc7Sbellard     wav_hw_run,
20585571bc7Sbellard     wav_hw_write,
20685571bc7Sbellard     wav_hw_ctl
20785571bc7Sbellard };
20885571bc7Sbellard 
20985571bc7Sbellard struct audio_output_driver wav_output_driver = {
21085571bc7Sbellard     "wav",
21185571bc7Sbellard     wav_audio_init,
21285571bc7Sbellard     wav_audio_fini,
21385571bc7Sbellard     &wav_pcm_ops,
21485571bc7Sbellard     1,
21585571bc7Sbellard     1,
21685571bc7Sbellard     sizeof (WAVVoice)
21785571bc7Sbellard };
218