xref: /openbmc/qemu/audio/wavaudio.c (revision ebe15582)
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 size_t wav_run_out(HWVoiceOut *hw, size_t live)
44 {
45     WAVVoiceOut *wav = (WAVVoiceOut *) hw;
46     size_t 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 = 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 = 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 /* VICE code: Store number as little endian. */
88 static void le_store (uint8_t *buf, uint32_t val, int len)
89 {
90     int i;
91     for (i = 0; i < len; i++) {
92         buf[i] = (uint8_t) (val & 0xff);
93         val >>= 8;
94     }
95 }
96 
97 static int wav_init_out(HWVoiceOut *hw, struct audsettings *as,
98                         void *drv_opaque)
99 {
100     WAVVoiceOut *wav = (WAVVoiceOut *) hw;
101     int bits16 = 0, stereo = 0;
102     uint8_t hdr[] = {
103         0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56,
104         0x45, 0x66, 0x6d, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00,
105         0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04,
106         0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00
107     };
108     Audiodev *dev = drv_opaque;
109     AudiodevWavOptions *wopts = &dev->u.wav;
110     struct audsettings wav_as = audiodev_to_audsettings(dev->u.wav.out);
111     const char *wav_path = wopts->has_path ? wopts->path : "qemu.wav";
112 
113     stereo = wav_as.nchannels == 2;
114     switch (wav_as.fmt) {
115     case AUDIO_FORMAT_S8:
116     case AUDIO_FORMAT_U8:
117         bits16 = 0;
118         break;
119 
120     case AUDIO_FORMAT_S16:
121     case AUDIO_FORMAT_U16:
122         bits16 = 1;
123         break;
124 
125     case AUDIO_FORMAT_S32:
126     case AUDIO_FORMAT_U32:
127         dolog ("WAVE files can not handle 32bit formats\n");
128         return -1;
129 
130     default:
131         abort();
132     }
133 
134     hdr[34] = bits16 ? 0x10 : 0x08;
135 
136     wav_as.endianness = 0;
137     audio_pcm_init_info (&hw->info, &wav_as);
138 
139     hw->samples = 1024;
140     wav->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift);
141     if (!wav->pcm_buf) {
142         dolog("Could not allocate buffer (%zu bytes)\n",
143               hw->samples << hw->info.shift);
144         return -1;
145     }
146 
147     le_store (hdr + 22, hw->info.nchannels, 2);
148     le_store (hdr + 24, hw->info.freq, 4);
149     le_store (hdr + 28, hw->info.freq << (bits16 + stereo), 4);
150     le_store (hdr + 32, 1 << (bits16 + stereo), 2);
151 
152     wav->f = fopen(wav_path, "wb");
153     if (!wav->f) {
154         dolog ("Failed to open wave file `%s'\nReason: %s\n",
155                wav_path, strerror(errno));
156         g_free (wav->pcm_buf);
157         wav->pcm_buf = NULL;
158         return -1;
159     }
160 
161     if (fwrite (hdr, sizeof (hdr), 1, wav->f) != 1) {
162         dolog ("wav_init_out: failed to write header\nReason: %s\n",
163                strerror(errno));
164         return -1;
165     }
166     return 0;
167 }
168 
169 static void wav_fini_out (HWVoiceOut *hw)
170 {
171     WAVVoiceOut *wav = (WAVVoiceOut *) hw;
172     uint8_t rlen[4];
173     uint8_t dlen[4];
174     uint32_t datalen = wav->total_samples << hw->info.shift;
175     uint32_t rifflen = datalen + 36;
176 
177     if (!wav->f) {
178         return;
179     }
180 
181     le_store (rlen, rifflen, 4);
182     le_store (dlen, datalen, 4);
183 
184     if (fseek (wav->f, 4, SEEK_SET)) {
185         dolog ("wav_fini_out: fseek to rlen failed\nReason: %s\n",
186                strerror(errno));
187         goto doclose;
188     }
189     if (fwrite (rlen, 4, 1, wav->f) != 1) {
190         dolog ("wav_fini_out: failed to write rlen\nReason: %s\n",
191                strerror (errno));
192         goto doclose;
193     }
194     if (fseek (wav->f, 32, SEEK_CUR)) {
195         dolog ("wav_fini_out: fseek to dlen failed\nReason: %s\n",
196                strerror (errno));
197         goto doclose;
198     }
199     if (fwrite (dlen, 4, 1, wav->f) != 1) {
200         dolog ("wav_fini_out: failed to write dlen\nReaons: %s\n",
201                strerror (errno));
202         goto doclose;
203     }
204 
205  doclose:
206     if (fclose (wav->f))  {
207         dolog ("wav_fini_out: fclose %p failed\nReason: %s\n",
208                wav->f, strerror (errno));
209     }
210     wav->f = NULL;
211 
212     g_free (wav->pcm_buf);
213     wav->pcm_buf = NULL;
214 }
215 
216 static int wav_ctl_out (HWVoiceOut *hw, int cmd, ...)
217 {
218     (void) hw;
219     (void) cmd;
220     return 0;
221 }
222 
223 static void *wav_audio_init(Audiodev *dev)
224 {
225     assert(dev->driver == AUDIODEV_DRIVER_WAV);
226     return dev;
227 }
228 
229 static void wav_audio_fini (void *opaque)
230 {
231     ldebug ("wav_fini");
232 }
233 
234 static struct audio_pcm_ops wav_pcm_ops = {
235     .init_out = wav_init_out,
236     .fini_out = wav_fini_out,
237     .run_out  = wav_run_out,
238     .ctl_out  = wav_ctl_out,
239 };
240 
241 static struct audio_driver wav_audio_driver = {
242     .name           = "wav",
243     .descr          = "WAV renderer http://wikipedia.org/wiki/WAV",
244     .init           = wav_audio_init,
245     .fini           = wav_audio_fini,
246     .pcm_ops        = &wav_pcm_ops,
247     .can_be_default = 0,
248     .max_voices_out = 1,
249     .max_voices_in  = 0,
250     .voice_size_out = sizeof (WAVVoiceOut),
251     .voice_size_in  = 0
252 };
253 
254 static void register_audio_wav(void)
255 {
256     audio_driver_register(&wav_audio_driver);
257 }
258 type_init(register_audio_wav);
259