]> git.proxmox.com Git - mirror_qemu.git/blame - audio/wavaudio.c
Merge remote-tracking branch 'remotes/riku/tags/pull-linux-user-20150616' into staging
[mirror_qemu.git] / audio / wavaudio.c
CommitLineData
85571bc7 1/*
1d14ffa9
FB
2 * QEMU WAV audio driver
3 *
4 * Copyright (c) 2004-2005 Vassili Karpov (malc)
5 *
85571bc7
FB
6 * 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 */
87ecb68b 24#include "hw/hw.h"
1de7afc9 25#include "qemu/timer.h"
87ecb68b 26#include "audio.h"
85571bc7 27
1d14ffa9
FB
28#define AUDIO_CAP "wav"
29#include "audio_int.h"
fb065187 30
1d14ffa9
FB
31typedef struct WAVVoiceOut {
32 HWVoiceOut hw;
27acf660 33 FILE *f;
fb065187
FB
34 int64_t old_ticks;
35 void *pcm_buf;
36 int total_samples;
1d14ffa9 37} WAVVoiceOut;
85571bc7 38
f2dcc6ce 39typedef struct {
1ea879e5 40 struct audsettings settings;
85571bc7 41 const char *wav_path;
f2dcc6ce 42} WAVConf;
85571bc7 43
bdff253c 44static int wav_run_out (HWVoiceOut *hw, int live)
85571bc7 45{
1d14ffa9 46 WAVVoiceOut *wav = (WAVVoiceOut *) hw;
bdff253c 47 int rpos, decr, samples;
85571bc7 48 uint8_t *dst;
1ea879e5 49 struct st_sample *src;
bc72ad67 50 int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
85571bc7 51 int64_t ticks = now - wav->old_ticks;
4f4cc0ef 52 int64_t bytes =
53 muldiv64 (ticks, hw->info.bytes_per_second, get_ticks_per_sec ());
85571bc7 54
1d14ffa9
FB
55 if (bytes > INT_MAX) {
56 samples = INT_MAX >> hw->info.shift;
57 }
58 else {
59 samples = bytes >> hw->info.shift;
60 }
85571bc7 61
7372f88d 62 wav->old_ticks = now;
85571bc7
FB
63 decr = audio_MIN (live, samples);
64 samples = decr;
65 rpos = hw->rpos;
66 while (samples) {
67 int left_till_end_samples = hw->samples - rpos;
68 int convert_samples = audio_MIN (samples, left_till_end_samples);
69
1d14ffa9
FB
70 src = hw->mix_buf + rpos;
71 dst = advance (wav->pcm_buf, rpos << hw->info.shift);
85571bc7
FB
72
73 hw->clip (dst, src, convert_samples);
27acf660
JQ
74 if (fwrite (dst, convert_samples << hw->info.shift, 1, wav->f) != 1) {
75 dolog ("wav_run_out: fwrite of %d bytes failed\nReaons: %s\n",
76 convert_samples << hw->info.shift, strerror (errno));
77 }
85571bc7
FB
78
79 rpos = (rpos + convert_samples) % hw->samples;
80 samples -= convert_samples;
81 wav->total_samples += convert_samples;
82 }
83
85571bc7 84 hw->rpos = rpos;
1d14ffa9 85 return decr;
85571bc7
FB
86}
87
1d14ffa9 88static int wav_write_out (SWVoiceOut *sw, void *buf, int len)
85571bc7 89{
1d14ffa9 90 return audio_pcm_sw_write (sw, buf, len);
85571bc7
FB
91}
92
85571bc7
FB
93/* VICE code: Store number as little endian. */
94static void le_store (uint8_t *buf, uint32_t val, int len)
95{
96 int i;
97 for (i = 0; i < len; i++) {
98 buf[i] = (uint8_t) (val & 0xff);
99 val >>= 8;
100 }
101}
102
5706db1d
KZ
103static int wav_init_out(HWVoiceOut *hw, struct audsettings *as,
104 void *drv_opaque)
85571bc7 105{
1d14ffa9 106 WAVVoiceOut *wav = (WAVVoiceOut *) hw;
c0fe3827 107 int bits16 = 0, stereo = 0;
85571bc7
FB
108 uint8_t hdr[] = {
109 0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56,
110 0x45, 0x66, 0x6d, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00,
111 0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04,
112 0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00
113 };
f2dcc6ce
KZ
114 WAVConf *conf = drv_opaque;
115 struct audsettings wav_as = conf->settings;
85571bc7 116
c0fe3827
FB
117 stereo = wav_as.nchannels == 2;
118 switch (wav_as.fmt) {
85571bc7
FB
119 case AUD_FMT_S8:
120 case AUD_FMT_U8:
1d14ffa9 121 bits16 = 0;
85571bc7
FB
122 break;
123
124 case AUD_FMT_S16:
125 case AUD_FMT_U16:
126 bits16 = 1;
127 break;
f941aa25
TS
128
129 case AUD_FMT_S32:
130 case AUD_FMT_U32:
131 dolog ("WAVE files can not handle 32bit formats\n");
132 return -1;
85571bc7
FB
133 }
134
135 hdr[34] = bits16 ? 0x10 : 0x08;
c0fe3827 136
d929eba5
FB
137 wav_as.endianness = 0;
138 audio_pcm_init_info (&hw->info, &wav_as);
c0fe3827
FB
139
140 hw->samples = 1024;
141 wav->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
1d14ffa9 142 if (!wav->pcm_buf) {
c0fe3827
FB
143 dolog ("Could not allocate buffer (%d bytes)\n",
144 hw->samples << hw->info.shift);
85571bc7 145 return -1;
1d14ffa9 146 }
85571bc7 147
1d14ffa9
FB
148 le_store (hdr + 22, hw->info.nchannels, 2);
149 le_store (hdr + 24, hw->info.freq, 4);
c0fe3827
FB
150 le_store (hdr + 28, hw->info.freq << (bits16 + stereo), 4);
151 le_store (hdr + 32, 1 << (bits16 + stereo), 2);
85571bc7 152
f2dcc6ce 153 wav->f = fopen (conf->wav_path, "wb");
85571bc7 154 if (!wav->f) {
1d14ffa9 155 dolog ("Failed to open wave file `%s'\nReason: %s\n",
f2dcc6ce 156 conf->wav_path, strerror (errno));
7267c094 157 g_free (wav->pcm_buf);
7372f88d 158 wav->pcm_buf = NULL;
85571bc7
FB
159 return -1;
160 }
161
27acf660
JQ
162 if (fwrite (hdr, sizeof (hdr), 1, wav->f) != 1) {
163 dolog ("wav_init_out: failed to write header\nReason: %s\n",
164 strerror(errno));
165 return -1;
166 }
85571bc7
FB
167 return 0;
168}
169
1d14ffa9 170static void wav_fini_out (HWVoiceOut *hw)
85571bc7 171{
1d14ffa9 172 WAVVoiceOut *wav = (WAVVoiceOut *) hw;
85571bc7
FB
173 uint8_t rlen[4];
174 uint8_t dlen[4];
50903530
FB
175 uint32_t datalen = wav->total_samples << hw->info.shift;
176 uint32_t rifflen = datalen + 36;
85571bc7 177
c0fe3827 178 if (!wav->f) {
85571bc7 179 return;
1d14ffa9 180 }
85571bc7
FB
181
182 le_store (rlen, rifflen, 4);
183 le_store (dlen, datalen, 4);
184
27acf660
JQ
185 if (fseek (wav->f, 4, SEEK_SET)) {
186 dolog ("wav_fini_out: fseek to rlen failed\nReason: %s\n",
187 strerror(errno));
188 goto doclose;
189 }
190 if (fwrite (rlen, 4, 1, wav->f) != 1) {
191 dolog ("wav_fini_out: failed to write rlen\nReason: %s\n",
192 strerror (errno));
193 goto doclose;
194 }
195 if (fseek (wav->f, 32, SEEK_CUR)) {
196 dolog ("wav_fini_out: fseek to dlen failed\nReason: %s\n",
197 strerror (errno));
198 goto doclose;
199 }
200 if (fwrite (dlen, 4, 1, wav->f) != 1) {
201 dolog ("wav_fini_out: failed to write dlen\nReaons: %s\n",
202 strerror (errno));
203 goto doclose;
204 }
85571bc7 205
27acf660
JQ
206 doclose:
207 if (fclose (wav->f)) {
208 dolog ("wav_fini_out: fclose %p failed\nReason: %s\n",
209 wav->f, strerror (errno));
210 }
85571bc7 211 wav->f = NULL;
7372f88d 212
7267c094 213 g_free (wav->pcm_buf);
7372f88d 214 wav->pcm_buf = NULL;
85571bc7
FB
215}
216
1d14ffa9 217static int wav_ctl_out (HWVoiceOut *hw, int cmd, ...)
85571bc7
FB
218{
219 (void) hw;
220 (void) cmd;
221 return 0;
222}
223
f2dcc6ce
KZ
224static WAVConf glob_conf = {
225 .settings.freq = 44100,
226 .settings.nchannels = 2,
227 .settings.fmt = AUD_FMT_S16,
228 .wav_path = "qemu.wav"
229};
230
85571bc7
FB
231static void *wav_audio_init (void)
232{
f2dcc6ce
KZ
233 WAVConf *conf = g_malloc(sizeof(WAVConf));
234 *conf = glob_conf;
235 return conf;
85571bc7
FB
236}
237
238static void wav_audio_fini (void *opaque)
239{
240 ldebug ("wav_fini");
f2dcc6ce 241 g_free(opaque);
85571bc7
FB
242}
243
8869defe 244static struct audio_option wav_options[] = {
98f9f48c 245 {
246 .name = "FREQUENCY",
247 .tag = AUD_OPT_INT,
f2dcc6ce 248 .valp = &glob_conf.settings.freq,
98f9f48c 249 .descr = "Frequency"
250 },
251 {
252 .name = "FORMAT",
253 .tag = AUD_OPT_FMT,
f2dcc6ce 254 .valp = &glob_conf.settings.fmt,
98f9f48c 255 .descr = "Format"
256 },
257 {
258 .name = "DAC_FIXED_CHANNELS",
259 .tag = AUD_OPT_INT,
f2dcc6ce 260 .valp = &glob_conf.settings.nchannels,
98f9f48c 261 .descr = "Number of channels (1 - mono, 2 - stereo)"
262 },
263 {
264 .name = "PATH",
265 .tag = AUD_OPT_STR,
f2dcc6ce 266 .valp = &glob_conf.wav_path,
98f9f48c 267 .descr = "Path to wave file"
268 },
2700efa3 269 { /* End of list */ }
1d14ffa9
FB
270};
271
35f4b58c 272static struct audio_pcm_ops wav_pcm_ops = {
1dd3e4d1
JQ
273 .init_out = wav_init_out,
274 .fini_out = wav_fini_out,
275 .run_out = wav_run_out,
276 .write = wav_write_out,
277 .ctl_out = wav_ctl_out,
85571bc7
FB
278};
279
1d14ffa9 280struct audio_driver wav_audio_driver = {
bee37f32
JQ
281 .name = "wav",
282 .descr = "WAV renderer http://wikipedia.org/wiki/WAV",
283 .options = wav_options,
284 .init = wav_audio_init,
285 .fini = wav_audio_fini,
98f9f48c 286 .pcm_ops = &wav_pcm_ops,
bee37f32
JQ
287 .can_be_default = 0,
288 .max_voices_out = 1,
289 .max_voices_in = 0,
290 .voice_size_out = sizeof (WAVVoiceOut),
291 .voice_size_in = 0
85571bc7 292};