xref: /qemu/audio/wavaudio.c (revision 35f4b58c7a6e0a9708405cf96d505f1729b30c82)
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  */
2487ecb68bSpbrook #include "hw/hw.h"
2587ecb68bSpbrook #include "qemu-timer.h"
2687ecb68bSpbrook #include "audio.h"
2785571bc7Sbellard 
281d14ffa9Sbellard #define AUDIO_CAP "wav"
291d14ffa9Sbellard #include "audio_int.h"
30fb065187Sbellard 
311d14ffa9Sbellard typedef struct WAVVoiceOut {
321d14ffa9Sbellard     HWVoiceOut hw;
33fb065187Sbellard     QEMUFile *f;
34fb065187Sbellard     int64_t old_ticks;
35fb065187Sbellard     void *pcm_buf;
36fb065187Sbellard     int total_samples;
371d14ffa9Sbellard } WAVVoiceOut;
3885571bc7Sbellard 
3985571bc7Sbellard static struct {
40c0fe3827Sbellard     audsettings_t settings;
4185571bc7Sbellard     const char *wav_path;
4285571bc7Sbellard } conf = {
43c0fe3827Sbellard     {
44c0fe3827Sbellard         44100,
45c0fe3827Sbellard         2,
46f941aa25Sths         AUD_FMT_S16,
47ca9cc28cSbalrog         0
48c0fe3827Sbellard     },
49c0fe3827Sbellard     "qemu.wav"
5085571bc7Sbellard };
5185571bc7Sbellard 
521d14ffa9Sbellard static int wav_run_out (HWVoiceOut *hw)
5385571bc7Sbellard {
541d14ffa9Sbellard     WAVVoiceOut *wav = (WAVVoiceOut *) hw;
5585571bc7Sbellard     int rpos, live, decr, samples;
5685571bc7Sbellard     uint8_t *dst;
5785571bc7Sbellard     st_sample_t *src;
5885571bc7Sbellard     int64_t now = qemu_get_clock (vm_clock);
5985571bc7Sbellard     int64_t ticks = now - wav->old_ticks;
601d14ffa9Sbellard     int64_t bytes = (ticks * hw->info.bytes_per_second) / ticks_per_sec;
6185571bc7Sbellard 
621d14ffa9Sbellard     if (bytes > INT_MAX) {
631d14ffa9Sbellard         samples = INT_MAX >> hw->info.shift;
641d14ffa9Sbellard     }
651d14ffa9Sbellard     else {
661d14ffa9Sbellard         samples = bytes >> hw->info.shift;
671d14ffa9Sbellard     }
6885571bc7Sbellard 
691d14ffa9Sbellard     live = audio_pcm_hw_get_live_out (hw);
701d14ffa9Sbellard     if (!live) {
711d14ffa9Sbellard         return 0;
721d14ffa9Sbellard     }
7385571bc7Sbellard 
747372f88dSbellard     wav->old_ticks = now;
7585571bc7Sbellard     decr = audio_MIN (live, samples);
7685571bc7Sbellard     samples = decr;
7785571bc7Sbellard     rpos = hw->rpos;
7885571bc7Sbellard     while (samples) {
7985571bc7Sbellard         int left_till_end_samples = hw->samples - rpos;
8085571bc7Sbellard         int convert_samples = audio_MIN (samples, left_till_end_samples);
8185571bc7Sbellard 
821d14ffa9Sbellard         src = hw->mix_buf + rpos;
831d14ffa9Sbellard         dst = advance (wav->pcm_buf, rpos << hw->info.shift);
8485571bc7Sbellard 
8585571bc7Sbellard         hw->clip (dst, src, convert_samples);
861d14ffa9Sbellard         qemu_put_buffer (wav->f, dst, convert_samples << hw->info.shift);
8785571bc7Sbellard 
8885571bc7Sbellard         rpos = (rpos + convert_samples) % hw->samples;
8985571bc7Sbellard         samples -= convert_samples;
9085571bc7Sbellard         wav->total_samples += convert_samples;
9185571bc7Sbellard     }
9285571bc7Sbellard 
9385571bc7Sbellard     hw->rpos = rpos;
941d14ffa9Sbellard     return decr;
9585571bc7Sbellard }
9685571bc7Sbellard 
971d14ffa9Sbellard static int wav_write_out (SWVoiceOut *sw, void *buf, int len)
9885571bc7Sbellard {
991d14ffa9Sbellard     return audio_pcm_sw_write (sw, buf, len);
10085571bc7Sbellard }
10185571bc7Sbellard 
10285571bc7Sbellard /* VICE code: Store number as little endian. */
10385571bc7Sbellard static void le_store (uint8_t *buf, uint32_t val, int len)
10485571bc7Sbellard {
10585571bc7Sbellard     int i;
10685571bc7Sbellard     for (i = 0; i < len; i++) {
10785571bc7Sbellard         buf[i] = (uint8_t) (val & 0xff);
10885571bc7Sbellard         val >>= 8;
10985571bc7Sbellard     }
11085571bc7Sbellard }
11185571bc7Sbellard 
112c0fe3827Sbellard static int wav_init_out (HWVoiceOut *hw, audsettings_t *as)
11385571bc7Sbellard {
1141d14ffa9Sbellard     WAVVoiceOut *wav = (WAVVoiceOut *) hw;
115c0fe3827Sbellard     int bits16 = 0, stereo = 0;
11685571bc7Sbellard     uint8_t hdr[] = {
11785571bc7Sbellard         0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56,
11885571bc7Sbellard         0x45, 0x66, 0x6d, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00,
11985571bc7Sbellard         0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04,
12085571bc7Sbellard         0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00
12185571bc7Sbellard     };
122c0fe3827Sbellard     audsettings_t wav_as = conf.settings;
12385571bc7Sbellard 
124c0fe3827Sbellard     (void) as;
1251d14ffa9Sbellard 
126c0fe3827Sbellard     stereo = wav_as.nchannels == 2;
127c0fe3827Sbellard     switch (wav_as.fmt) {
12885571bc7Sbellard     case AUD_FMT_S8:
12985571bc7Sbellard     case AUD_FMT_U8:
1301d14ffa9Sbellard         bits16 = 0;
13185571bc7Sbellard         break;
13285571bc7Sbellard 
13385571bc7Sbellard     case AUD_FMT_S16:
13485571bc7Sbellard     case AUD_FMT_U16:
13585571bc7Sbellard         bits16 = 1;
13685571bc7Sbellard         break;
137f941aa25Sths 
138f941aa25Sths     case AUD_FMT_S32:
139f941aa25Sths     case AUD_FMT_U32:
140f941aa25Sths         dolog ("WAVE files can not handle 32bit formats\n");
141f941aa25Sths         return -1;
14285571bc7Sbellard     }
14385571bc7Sbellard 
14485571bc7Sbellard     hdr[34] = bits16 ? 0x10 : 0x08;
145c0fe3827Sbellard 
146d929eba5Sbellard     wav_as.endianness = 0;
147d929eba5Sbellard     audio_pcm_init_info (&hw->info, &wav_as);
148c0fe3827Sbellard 
149c0fe3827Sbellard     hw->samples = 1024;
150c0fe3827Sbellard     wav->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
1511d14ffa9Sbellard     if (!wav->pcm_buf) {
152c0fe3827Sbellard         dolog ("Could not allocate buffer (%d bytes)\n",
153c0fe3827Sbellard                hw->samples << hw->info.shift);
15485571bc7Sbellard         return -1;
1551d14ffa9Sbellard     }
15685571bc7Sbellard 
1571d14ffa9Sbellard     le_store (hdr + 22, hw->info.nchannels, 2);
1581d14ffa9Sbellard     le_store (hdr + 24, hw->info.freq, 4);
159c0fe3827Sbellard     le_store (hdr + 28, hw->info.freq << (bits16 + stereo), 4);
160c0fe3827Sbellard     le_store (hdr + 32, 1 << (bits16 + stereo), 2);
16185571bc7Sbellard 
162e70332b3Sbellard     wav->f = qemu_fopen (conf.wav_path, "wb");
16385571bc7Sbellard     if (!wav->f) {
1641d14ffa9Sbellard         dolog ("Failed to open wave file `%s'\nReason: %s\n",
16585571bc7Sbellard                conf.wav_path, strerror (errno));
1667372f88dSbellard         qemu_free (wav->pcm_buf);
1677372f88dSbellard         wav->pcm_buf = NULL;
16885571bc7Sbellard         return -1;
16985571bc7Sbellard     }
17085571bc7Sbellard 
17185571bc7Sbellard     qemu_put_buffer (wav->f, hdr, sizeof (hdr));
17285571bc7Sbellard     return 0;
17385571bc7Sbellard }
17485571bc7Sbellard 
1751d14ffa9Sbellard static void wav_fini_out (HWVoiceOut *hw)
17685571bc7Sbellard {
1771d14ffa9Sbellard     WAVVoiceOut *wav = (WAVVoiceOut *) hw;
17885571bc7Sbellard     uint8_t rlen[4];
17985571bc7Sbellard     uint8_t dlen[4];
18050903530Sbellard     uint32_t datalen = wav->total_samples << hw->info.shift;
18150903530Sbellard     uint32_t rifflen = datalen + 36;
18285571bc7Sbellard 
183c0fe3827Sbellard     if (!wav->f) {
18485571bc7Sbellard         return;
1851d14ffa9Sbellard     }
18685571bc7Sbellard 
18785571bc7Sbellard     le_store (rlen, rifflen, 4);
18885571bc7Sbellard     le_store (dlen, datalen, 4);
18985571bc7Sbellard 
19085571bc7Sbellard     qemu_fseek (wav->f, 4, SEEK_SET);
19185571bc7Sbellard     qemu_put_buffer (wav->f, rlen, 4);
19285571bc7Sbellard 
19385571bc7Sbellard     qemu_fseek (wav->f, 32, SEEK_CUR);
19485571bc7Sbellard     qemu_put_buffer (wav->f, dlen, 4);
19585571bc7Sbellard 
196e70332b3Sbellard     qemu_fclose (wav->f);
19785571bc7Sbellard     wav->f = NULL;
1987372f88dSbellard 
1997372f88dSbellard     qemu_free (wav->pcm_buf);
2007372f88dSbellard     wav->pcm_buf = NULL;
20185571bc7Sbellard }
20285571bc7Sbellard 
2031d14ffa9Sbellard static int wav_ctl_out (HWVoiceOut *hw, int cmd, ...)
20485571bc7Sbellard {
20585571bc7Sbellard     (void) hw;
20685571bc7Sbellard     (void) cmd;
20785571bc7Sbellard     return 0;
20885571bc7Sbellard }
20985571bc7Sbellard 
21085571bc7Sbellard static void *wav_audio_init (void)
21185571bc7Sbellard {
21285571bc7Sbellard     return &conf;
21385571bc7Sbellard }
21485571bc7Sbellard 
21585571bc7Sbellard static void wav_audio_fini (void *opaque)
21685571bc7Sbellard {
2171d14ffa9Sbellard     (void) opaque;
21885571bc7Sbellard     ldebug ("wav_fini");
21985571bc7Sbellard }
22085571bc7Sbellard 
2218869defeSblueswir1 static struct audio_option wav_options[] = {
222c0fe3827Sbellard     {"FREQUENCY", AUD_OPT_INT, &conf.settings.freq,
223c0fe3827Sbellard      "Frequency", NULL, 0},
224c0fe3827Sbellard 
225c0fe3827Sbellard     {"FORMAT", AUD_OPT_FMT, &conf.settings.fmt,
226c0fe3827Sbellard      "Format", NULL, 0},
227c0fe3827Sbellard 
228c0fe3827Sbellard     {"DAC_FIXED_CHANNELS", AUD_OPT_INT, &conf.settings.nchannels,
229c0fe3827Sbellard      "Number of channels (1 - mono, 2 - stereo)", NULL, 0},
230c0fe3827Sbellard 
2311d14ffa9Sbellard     {"PATH", AUD_OPT_STR, &conf.wav_path,
2321d14ffa9Sbellard      "Path to wave file", NULL, 0},
2331d14ffa9Sbellard     {NULL, 0, NULL, NULL, NULL, 0}
23485571bc7Sbellard };
23585571bc7Sbellard 
236*35f4b58cSblueswir1 static struct audio_pcm_ops wav_pcm_ops = {
2371d14ffa9Sbellard     wav_init_out,
2381d14ffa9Sbellard     wav_fini_out,
2391d14ffa9Sbellard     wav_run_out,
2401d14ffa9Sbellard     wav_write_out,
2411d14ffa9Sbellard     wav_ctl_out,
2421d14ffa9Sbellard 
2431d14ffa9Sbellard     NULL,
2441d14ffa9Sbellard     NULL,
2451d14ffa9Sbellard     NULL,
2461d14ffa9Sbellard     NULL,
2471d14ffa9Sbellard     NULL
2481d14ffa9Sbellard };
2491d14ffa9Sbellard 
2501d14ffa9Sbellard struct audio_driver wav_audio_driver = {
2511d14ffa9Sbellard     INIT_FIELD (name           = ) "wav",
2521d14ffa9Sbellard     INIT_FIELD (descr          = )
2531d14ffa9Sbellard     "WAV renderer http://wikipedia.org/wiki/WAV",
2541d14ffa9Sbellard     INIT_FIELD (options        = ) wav_options,
2551d14ffa9Sbellard     INIT_FIELD (init           = ) wav_audio_init,
2561d14ffa9Sbellard     INIT_FIELD (fini           = ) wav_audio_fini,
2571d14ffa9Sbellard     INIT_FIELD (pcm_ops        = ) &wav_pcm_ops,
2581d14ffa9Sbellard     INIT_FIELD (can_be_default = ) 0,
2591d14ffa9Sbellard     INIT_FIELD (max_voices_out = ) 1,
2601d14ffa9Sbellard     INIT_FIELD (max_voices_in  = ) 0,
2611d14ffa9Sbellard     INIT_FIELD (voice_size_out = ) sizeof (WAVVoiceOut),
2621d14ffa9Sbellard     INIT_FIELD (voice_size_in  = ) 0
26385571bc7Sbellard };
264