blob: e50dac292667a3bacd0f6167c05a400d4a8b9c79 [file] [log] [blame]
bellard85571bc2004-11-07 18:04:02 +00001/*
bellard1d14ffa2005-10-30 18:58:22 +00002 * QEMU WAV audio driver
3 *
4 * Copyright (c) 2004-2005 Vassili Karpov (malc)
5 *
bellard85571bc2004-11-07 18:04:02 +00006 * 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 */
pbrook87ecb682007-11-17 17:14:51 +000024#include "hw/hw.h"
25#include "qemu-timer.h"
26#include "audio.h"
bellard85571bc2004-11-07 18:04:02 +000027
bellard1d14ffa2005-10-30 18:58:22 +000028#define AUDIO_CAP "wav"
29#include "audio_int.h"
bellardfb065182004-11-09 23:09:44 +000030
bellard1d14ffa2005-10-30 18:58:22 +000031typedef struct WAVVoiceOut {
32 HWVoiceOut hw;
bellardfb065182004-11-09 23:09:44 +000033 QEMUFile *f;
34 int64_t old_ticks;
35 void *pcm_buf;
36 int total_samples;
bellard1d14ffa2005-10-30 18:58:22 +000037} WAVVoiceOut;
bellard85571bc2004-11-07 18:04:02 +000038
39static struct {
malc1ea879e2008-12-03 22:48:44 +000040 struct audsettings settings;
bellard85571bc2004-11-07 18:04:02 +000041 const char *wav_path;
42} conf = {
bellardc0fe3822005-11-05 18:55:28 +000043 {
44 44100,
45 2,
thsf941aa22007-02-17 22:19:29 +000046 AUD_FMT_S16,
balrogca9cc282008-01-14 04:24:29 +000047 0
bellardc0fe3822005-11-05 18:55:28 +000048 },
49 "qemu.wav"
bellard85571bc2004-11-07 18:04:02 +000050};
51
bellard1d14ffa2005-10-30 18:58:22 +000052static int wav_run_out (HWVoiceOut *hw)
bellard85571bc2004-11-07 18:04:02 +000053{
bellard1d14ffa2005-10-30 18:58:22 +000054 WAVVoiceOut *wav = (WAVVoiceOut *) hw;
bellard85571bc2004-11-07 18:04:02 +000055 int rpos, live, decr, samples;
56 uint8_t *dst;
malc1ea879e2008-12-03 22:48:44 +000057 struct st_sample *src;
bellard85571bc2004-11-07 18:04:02 +000058 int64_t now = qemu_get_clock (vm_clock);
59 int64_t ticks = now - wav->old_ticks;
bellard1d14ffa2005-10-30 18:58:22 +000060 int64_t bytes = (ticks * hw->info.bytes_per_second) / ticks_per_sec;
bellard85571bc2004-11-07 18:04:02 +000061
bellard1d14ffa2005-10-30 18:58:22 +000062 if (bytes > INT_MAX) {
63 samples = INT_MAX >> hw->info.shift;
64 }
65 else {
66 samples = bytes >> hw->info.shift;
67 }
bellard85571bc2004-11-07 18:04:02 +000068
bellard1d14ffa2005-10-30 18:58:22 +000069 live = audio_pcm_hw_get_live_out (hw);
70 if (!live) {
71 return 0;
72 }
bellard85571bc2004-11-07 18:04:02 +000073
bellard7372f882004-11-11 16:55:09 +000074 wav->old_ticks = now;
bellard85571bc2004-11-07 18:04:02 +000075 decr = audio_MIN (live, samples);
76 samples = decr;
77 rpos = hw->rpos;
78 while (samples) {
79 int left_till_end_samples = hw->samples - rpos;
80 int convert_samples = audio_MIN (samples, left_till_end_samples);
81
bellard1d14ffa2005-10-30 18:58:22 +000082 src = hw->mix_buf + rpos;
83 dst = advance (wav->pcm_buf, rpos << hw->info.shift);
bellard85571bc2004-11-07 18:04:02 +000084
85 hw->clip (dst, src, convert_samples);
bellard1d14ffa2005-10-30 18:58:22 +000086 qemu_put_buffer (wav->f, dst, convert_samples << hw->info.shift);
bellard85571bc2004-11-07 18:04:02 +000087
88 rpos = (rpos + convert_samples) % hw->samples;
89 samples -= convert_samples;
90 wav->total_samples += convert_samples;
91 }
92
bellard85571bc2004-11-07 18:04:02 +000093 hw->rpos = rpos;
bellard1d14ffa2005-10-30 18:58:22 +000094 return decr;
bellard85571bc2004-11-07 18:04:02 +000095}
96
bellard1d14ffa2005-10-30 18:58:22 +000097static int wav_write_out (SWVoiceOut *sw, void *buf, int len)
bellard85571bc2004-11-07 18:04:02 +000098{
bellard1d14ffa2005-10-30 18:58:22 +000099 return audio_pcm_sw_write (sw, buf, len);
bellard85571bc2004-11-07 18:04:02 +0000100}
101
bellard85571bc2004-11-07 18:04:02 +0000102/* VICE code: Store number as little endian. */
103static void le_store (uint8_t *buf, uint32_t val, int len)
104{
105 int i;
106 for (i = 0; i < len; i++) {
107 buf[i] = (uint8_t) (val & 0xff);
108 val >>= 8;
109 }
110}
111
malc1ea879e2008-12-03 22:48:44 +0000112static int wav_init_out (HWVoiceOut *hw, struct audsettings *as)
bellard85571bc2004-11-07 18:04:02 +0000113{
bellard1d14ffa2005-10-30 18:58:22 +0000114 WAVVoiceOut *wav = (WAVVoiceOut *) hw;
bellardc0fe3822005-11-05 18:55:28 +0000115 int bits16 = 0, stereo = 0;
bellard85571bc2004-11-07 18:04:02 +0000116 uint8_t hdr[] = {
117 0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56,
118 0x45, 0x66, 0x6d, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00,
119 0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04,
120 0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00
121 };
malc1ea879e2008-12-03 22:48:44 +0000122 struct audsettings wav_as = conf.settings;
bellard85571bc2004-11-07 18:04:02 +0000123
bellardc0fe3822005-11-05 18:55:28 +0000124 (void) as;
bellard1d14ffa2005-10-30 18:58:22 +0000125
bellardc0fe3822005-11-05 18:55:28 +0000126 stereo = wav_as.nchannels == 2;
127 switch (wav_as.fmt) {
bellard85571bc2004-11-07 18:04:02 +0000128 case AUD_FMT_S8:
129 case AUD_FMT_U8:
bellard1d14ffa2005-10-30 18:58:22 +0000130 bits16 = 0;
bellard85571bc2004-11-07 18:04:02 +0000131 break;
132
133 case AUD_FMT_S16:
134 case AUD_FMT_U16:
135 bits16 = 1;
136 break;
thsf941aa22007-02-17 22:19:29 +0000137
138 case AUD_FMT_S32:
139 case AUD_FMT_U32:
140 dolog ("WAVE files can not handle 32bit formats\n");
141 return -1;
bellard85571bc2004-11-07 18:04:02 +0000142 }
143
144 hdr[34] = bits16 ? 0x10 : 0x08;
bellardc0fe3822005-11-05 18:55:28 +0000145
bellardd929eba2006-07-04 21:47:22 +0000146 wav_as.endianness = 0;
147 audio_pcm_init_info (&hw->info, &wav_as);
bellardc0fe3822005-11-05 18:55:28 +0000148
149 hw->samples = 1024;
150 wav->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
bellard1d14ffa2005-10-30 18:58:22 +0000151 if (!wav->pcm_buf) {
bellardc0fe3822005-11-05 18:55:28 +0000152 dolog ("Could not allocate buffer (%d bytes)\n",
153 hw->samples << hw->info.shift);
bellard85571bc2004-11-07 18:04:02 +0000154 return -1;
bellard1d14ffa2005-10-30 18:58:22 +0000155 }
bellard85571bc2004-11-07 18:04:02 +0000156
bellard1d14ffa2005-10-30 18:58:22 +0000157 le_store (hdr + 22, hw->info.nchannels, 2);
158 le_store (hdr + 24, hw->info.freq, 4);
bellardc0fe3822005-11-05 18:55:28 +0000159 le_store (hdr + 28, hw->info.freq << (bits16 + stereo), 4);
160 le_store (hdr + 32, 1 << (bits16 + stereo), 2);
bellard85571bc2004-11-07 18:04:02 +0000161
bellarde70332b2006-08-05 21:31:46 +0000162 wav->f = qemu_fopen (conf.wav_path, "wb");
bellard85571bc2004-11-07 18:04:02 +0000163 if (!wav->f) {
bellard1d14ffa2005-10-30 18:58:22 +0000164 dolog ("Failed to open wave file `%s'\nReason: %s\n",
bellard85571bc2004-11-07 18:04:02 +0000165 conf.wav_path, strerror (errno));
bellard7372f882004-11-11 16:55:09 +0000166 qemu_free (wav->pcm_buf);
167 wav->pcm_buf = NULL;
bellard85571bc2004-11-07 18:04:02 +0000168 return -1;
169 }
170
171 qemu_put_buffer (wav->f, hdr, sizeof (hdr));
172 return 0;
173}
174
bellard1d14ffa2005-10-30 18:58:22 +0000175static void wav_fini_out (HWVoiceOut *hw)
bellard85571bc2004-11-07 18:04:02 +0000176{
bellard1d14ffa2005-10-30 18:58:22 +0000177 WAVVoiceOut *wav = (WAVVoiceOut *) hw;
bellard85571bc2004-11-07 18:04:02 +0000178 uint8_t rlen[4];
179 uint8_t dlen[4];
bellard50903532005-11-20 16:22:16 +0000180 uint32_t datalen = wav->total_samples << hw->info.shift;
181 uint32_t rifflen = datalen + 36;
bellard85571bc2004-11-07 18:04:02 +0000182
bellardc0fe3822005-11-05 18:55:28 +0000183 if (!wav->f) {
bellard85571bc2004-11-07 18:04:02 +0000184 return;
bellard1d14ffa2005-10-30 18:58:22 +0000185 }
bellard85571bc2004-11-07 18:04:02 +0000186
187 le_store (rlen, rifflen, 4);
188 le_store (dlen, datalen, 4);
189
190 qemu_fseek (wav->f, 4, SEEK_SET);
191 qemu_put_buffer (wav->f, rlen, 4);
192
193 qemu_fseek (wav->f, 32, SEEK_CUR);
194 qemu_put_buffer (wav->f, dlen, 4);
195
bellarde70332b2006-08-05 21:31:46 +0000196 qemu_fclose (wav->f);
bellard85571bc2004-11-07 18:04:02 +0000197 wav->f = NULL;
bellard7372f882004-11-11 16:55:09 +0000198
199 qemu_free (wav->pcm_buf);
200 wav->pcm_buf = NULL;
bellard85571bc2004-11-07 18:04:02 +0000201}
202
bellard1d14ffa2005-10-30 18:58:22 +0000203static int wav_ctl_out (HWVoiceOut *hw, int cmd, ...)
bellard85571bc2004-11-07 18:04:02 +0000204{
205 (void) hw;
206 (void) cmd;
207 return 0;
208}
209
210static void *wav_audio_init (void)
211{
212 return &conf;
213}
214
215static void wav_audio_fini (void *opaque)
216{
bellard1d14ffa2005-10-30 18:58:22 +0000217 (void) opaque;
bellard85571bc2004-11-07 18:04:02 +0000218 ldebug ("wav_fini");
219}
220
blueswir18869def2008-10-05 10:01:05 +0000221static struct audio_option wav_options[] = {
bellardc0fe3822005-11-05 18:55:28 +0000222 {"FREQUENCY", AUD_OPT_INT, &conf.settings.freq,
223 "Frequency", NULL, 0},
224
225 {"FORMAT", AUD_OPT_FMT, &conf.settings.fmt,
226 "Format", NULL, 0},
227
228 {"DAC_FIXED_CHANNELS", AUD_OPT_INT, &conf.settings.nchannels,
229 "Number of channels (1 - mono, 2 - stereo)", NULL, 0},
230
bellard1d14ffa2005-10-30 18:58:22 +0000231 {"PATH", AUD_OPT_STR, &conf.wav_path,
232 "Path to wave file", NULL, 0},
233 {NULL, 0, NULL, NULL, NULL, 0}
bellard85571bc2004-11-07 18:04:02 +0000234};
235
blueswir135f4b582008-10-06 18:08:30 +0000236static struct audio_pcm_ops wav_pcm_ops = {
bellard1d14ffa2005-10-30 18:58:22 +0000237 wav_init_out,
238 wav_fini_out,
239 wav_run_out,
240 wav_write_out,
241 wav_ctl_out,
242
243 NULL,
244 NULL,
245 NULL,
246 NULL,
247 NULL
248};
249
250struct audio_driver wav_audio_driver = {
251 INIT_FIELD (name = ) "wav",
252 INIT_FIELD (descr = )
253 "WAV renderer http://wikipedia.org/wiki/WAV",
254 INIT_FIELD (options = ) wav_options,
255 INIT_FIELD (init = ) wav_audio_init,
256 INIT_FIELD (fini = ) wav_audio_fini,
257 INIT_FIELD (pcm_ops = ) &wav_pcm_ops,
258 INIT_FIELD (can_be_default = ) 0,
259 INIT_FIELD (max_voices_out = ) 1,
260 INIT_FIELD (max_voices_in = ) 0,
261 INIT_FIELD (voice_size_out = ) sizeof (WAVVoiceOut),
262 INIT_FIELD (voice_size_in = ) 0
bellard85571bc2004-11-07 18:04:02 +0000263};