xref: /openbmc/qemu/audio/wavaudio.c (revision 3d9569b8)
1 /*
2  * QEMU WAV audio driver
3  *
4  * Copyright (c) 2004-2005 Vassili Karpov (malc)
5  *
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  */
24 
25 #include "qemu/osdep.h"
26 #include "qemu/host-utils.h"
27 #include "qemu/module.h"
28 #include "qemu/timer.h"
29 #include "qapi/opts-visitor.h"
30 #include "audio.h"
31 
32 #define AUDIO_CAP "wav"
33 #include "audio_int.h"
34 
35 typedef struct WAVVoiceOut {
36     HWVoiceOut hw;
37     FILE *f;
38     int64_t old_ticks;
39     void *pcm_buf;
40     int total_samples;
41 } WAVVoiceOut;
42 
43 static int wav_run_out (HWVoiceOut *hw, int live)
44 {
45     WAVVoiceOut *wav = (WAVVoiceOut *) hw;
46     int rpos, decr, samples;
47     uint8_t *dst;
48     struct st_sample *src;
49     int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
50     int64_t ticks = now - wav->old_ticks;
51     int64_t bytes =
52         muldiv64(ticks, hw->info.bytes_per_second, NANOSECONDS_PER_SECOND);
53 
54     if (bytes > INT_MAX) {
55         samples = INT_MAX >> hw->info.shift;
56     }
57     else {
58         samples = bytes >> hw->info.shift;
59     }
60 
61     wav->old_ticks = now;
62     decr = audio_MIN (live, samples);
63     samples = decr;
64     rpos = hw->rpos;
65     while (samples) {
66         int left_till_end_samples = hw->samples - rpos;
67         int convert_samples = audio_MIN (samples, left_till_end_samples);
68 
69         src = hw->mix_buf + rpos;
70         dst = advance (wav->pcm_buf, rpos << hw->info.shift);
71 
72         hw->clip (dst, src, convert_samples);
73         if (fwrite (dst, convert_samples << hw->info.shift, 1, wav->f) != 1) {
74             dolog ("wav_run_out: fwrite of %d bytes failed\nReaons: %s\n",
75                    convert_samples << hw->info.shift, strerror (errno));
76         }
77 
78         rpos = (rpos + convert_samples) % hw->samples;
79         samples -= convert_samples;
80         wav->total_samples += convert_samples;
81     }
82 
83     hw->rpos = rpos;
84     return decr;
85 }
86 
87 static int wav_write_out (SWVoiceOut *sw, void *buf, int len)
88 {
89     return audio_pcm_sw_write (sw, buf, len);
90 }
91 
92 /* VICE code: Store number as little endian. */
93 static void le_store (uint8_t *buf, uint32_t val, int len)
94 {
95     int i;
96     for (i = 0; i < len; i++) {
97         buf[i] = (uint8_t) (val & 0xff);
98         val >>= 8;
99     }
100 }
101 
102 static int wav_init_out(HWVoiceOut *hw, struct audsettings *as,
103                         void *drv_opaque)
104 {
105     WAVVoiceOut *wav = (WAVVoiceOut *) hw;
106     int bits16 = 0, stereo = 0;
107     uint8_t hdr[] = {
108         0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56,
109         0x45, 0x66, 0x6d, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00,
110         0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04,
111         0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00
112     };
113     Audiodev *dev = drv_opaque;
114     AudiodevWavOptions *wopts = &dev->u.wav;
115     struct audsettings wav_as = audiodev_to_audsettings(dev->u.wav.out);
116     const char *wav_path = wopts->has_path ? wopts->path : "qemu.wav";
117 
118     stereo = wav_as.nchannels == 2;
119     switch (wav_as.fmt) {
120     case AUDIO_FORMAT_S8:
121     case AUDIO_FORMAT_U8:
122         bits16 = 0;
123         break;
124 
125     case AUDIO_FORMAT_S16:
126     case AUDIO_FORMAT_U16:
127         bits16 = 1;
128         break;
129 
130     case AUDIO_FORMAT_S32:
131     case AUDIO_FORMAT_U32:
132         dolog ("WAVE files can not handle 32bit formats\n");
133         return -1;
134 
135     default:
136         abort();
137     }
138 
139     hdr[34] = bits16 ? 0x10 : 0x08;
140 
141     wav_as.endianness = 0;
142     audio_pcm_init_info (&hw->info, &wav_as);
143 
144     hw->samples = 1024;
145     wav->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift);
146     if (!wav->pcm_buf) {
147         dolog ("Could not allocate buffer (%d bytes)\n",
148                hw->samples << hw->info.shift);
149         return -1;
150     }
151 
152     le_store (hdr + 22, hw->info.nchannels, 2);
153     le_store (hdr + 24, hw->info.freq, 4);
154     le_store (hdr + 28, hw->info.freq << (bits16 + stereo), 4);
155     le_store (hdr + 32, 1 << (bits16 + stereo), 2);
156 
157     wav->f = fopen(wav_path, "wb");
158     if (!wav->f) {
159         dolog ("Failed to open wave file `%s'\nReason: %s\n",
160                wav_path, strerror(errno));
161         g_free (wav->pcm_buf);
162         wav->pcm_buf = NULL;
163         return -1;
164     }
165 
166     if (fwrite (hdr, sizeof (hdr), 1, wav->f) != 1) {
167         dolog ("wav_init_out: failed to write header\nReason: %s\n",
168                strerror(errno));
169         return -1;
170     }
171     return 0;
172 }
173 
174 static void wav_fini_out (HWVoiceOut *hw)
175 {
176     WAVVoiceOut *wav = (WAVVoiceOut *) hw;
177     uint8_t rlen[4];
178     uint8_t dlen[4];
179     uint32_t datalen = wav->total_samples << hw->info.shift;
180     uint32_t rifflen = datalen + 36;
181 
182     if (!wav->f) {
183         return;
184     }
185 
186     le_store (rlen, rifflen, 4);
187     le_store (dlen, datalen, 4);
188 
189     if (fseek (wav->f, 4, SEEK_SET)) {
190         dolog ("wav_fini_out: fseek to rlen failed\nReason: %s\n",
191                strerror(errno));
192         goto doclose;
193     }
194     if (fwrite (rlen, 4, 1, wav->f) != 1) {
195         dolog ("wav_fini_out: failed to write rlen\nReason: %s\n",
196                strerror (errno));
197         goto doclose;
198     }
199     if (fseek (wav->f, 32, SEEK_CUR)) {
200         dolog ("wav_fini_out: fseek to dlen failed\nReason: %s\n",
201                strerror (errno));
202         goto doclose;
203     }
204     if (fwrite (dlen, 4, 1, wav->f) != 1) {
205         dolog ("wav_fini_out: failed to write dlen\nReaons: %s\n",
206                strerror (errno));
207         goto doclose;
208     }
209 
210  doclose:
211     if (fclose (wav->f))  {
212         dolog ("wav_fini_out: fclose %p failed\nReason: %s\n",
213                wav->f, strerror (errno));
214     }
215     wav->f = NULL;
216 
217     g_free (wav->pcm_buf);
218     wav->pcm_buf = NULL;
219 }
220 
221 static int wav_ctl_out (HWVoiceOut *hw, int cmd, ...)
222 {
223     (void) hw;
224     (void) cmd;
225     return 0;
226 }
227 
228 static void *wav_audio_init(Audiodev *dev)
229 {
230     assert(dev->driver == AUDIODEV_DRIVER_WAV);
231     return dev;
232 }
233 
234 static void wav_audio_fini (void *opaque)
235 {
236     ldebug ("wav_fini");
237 }
238 
239 static struct audio_pcm_ops wav_pcm_ops = {
240     .init_out = wav_init_out,
241     .fini_out = wav_fini_out,
242     .run_out  = wav_run_out,
243     .write    = wav_write_out,
244     .ctl_out  = wav_ctl_out,
245 };
246 
247 static struct audio_driver wav_audio_driver = {
248     .name           = "wav",
249     .descr          = "WAV renderer http://wikipedia.org/wiki/WAV",
250     .init           = wav_audio_init,
251     .fini           = wav_audio_fini,
252     .pcm_ops        = &wav_pcm_ops,
253     .can_be_default = 0,
254     .max_voices_out = 1,
255     .max_voices_in  = 0,
256     .voice_size_out = sizeof (WAVVoiceOut),
257     .voice_size_in  = 0
258 };
259 
260 static void register_audio_wav(void)
261 {
262     audio_driver_register(&wav_audio_driver);
263 }
264 type_init(register_audio_wav);
265