• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * QEMU WAV audio driver
3  *
4  * Copyright (c) 2007 The Android Open Source Project
5  * Copyright (c) 2004-2005 Vassili Karpov (malc)
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a copy
8  * of this software and associated documentation files (the "Software"), to deal
9  * in the Software without restriction, including without limitation the rights
10  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11  * copies of the Software, and to permit persons to whom the Software is
12  * furnished to do so, subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included in
15  * all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23  * THE SOFTWARE.
24  */
25 #include "hw/hw.h"
26 #include "qemu-timer.h"
27 #include "audio.h"
28 
29 #define AUDIO_CAP "wav"
30 #include "audio_int.h"
31 #include "qemu_file.h"
32 
33 #define  WAV_AUDIO_IN  1
34 
35 /** VOICE OUT  (Saving to a .WAV file)
36  **/
37 typedef struct WAVVoiceOut {
38     HWVoiceOut hw;
39     QEMUFile *f;
40     int64_t old_ticks;
41     void *pcm_buf;
42     int total_samples;
43 } WAVVoiceOut;
44 
45 static struct {
46     struct audsettings settings;
47     const char *wav_path;
48 } conf_out = {
49     {
50         44100,
51         2,
52         AUD_FMT_S16,
53         0
54     },
55     "qemu.wav"
56 };
57 
wav_out_run(HWVoiceOut * hw,int live)58 static int wav_out_run (HWVoiceOut *hw, int live)
59 {
60     WAVVoiceOut *wav = (WAVVoiceOut *) hw;
61     int rpos, decr, samples;
62     uint8_t *dst;
63     struct st_sample *src;
64     int64_t now = qemu_get_clock (vm_clock);
65     int64_t ticks = now - wav->old_ticks;
66     int64_t bytes =
67         muldiv64 (ticks, hw->info.bytes_per_second, get_ticks_per_sec ());
68 
69     if (bytes > INT_MAX) {
70         samples = INT_MAX >> hw->info.shift;
71     }
72     else {
73         samples = bytes >> hw->info.shift;
74     }
75 
76     wav->old_ticks = now;
77     decr = audio_MIN (live, samples);
78     samples = decr;
79     rpos = hw->rpos;
80     while (samples) {
81         int left_till_end_samples = hw->samples - rpos;
82         int convert_samples = audio_MIN (samples, left_till_end_samples);
83 
84         src = hw->mix_buf + rpos;
85         dst = advance (wav->pcm_buf, rpos << hw->info.shift);
86 
87         hw->clip (dst, src, convert_samples);
88         qemu_put_buffer (wav->f, dst, convert_samples << hw->info.shift);
89 
90         rpos = (rpos + convert_samples) % hw->samples;
91         samples -= convert_samples;
92         wav->total_samples += convert_samples;
93     }
94 
95     hw->rpos = rpos;
96     return decr;
97 }
98 
wav_out_write(SWVoiceOut * sw,void * buf,int len)99 static int wav_out_write (SWVoiceOut *sw, void *buf, int len)
100 {
101     return audio_pcm_sw_write (sw, buf, len);
102 }
103 
104 /* VICE code: Store number as little endian. */
le_store(uint8_t * buf,uint32_t val,int len)105 static void le_store (uint8_t *buf, uint32_t val, int len)
106 {
107     int i;
108     for (i = 0; i < len; i++) {
109         buf[i] = (uint8_t) (val & 0xff);
110         val >>= 8;
111     }
112 }
113 
wav_out_init(HWVoiceOut * hw,struct audsettings * as)114 static int wav_out_init (HWVoiceOut *hw, struct audsettings *as)
115 {
116     WAVVoiceOut *wav = (WAVVoiceOut *) hw;
117     int bits16 = 0, stereo = 0;
118     uint8_t hdr[] = {
119         0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56,
120         0x45, 0x66, 0x6d, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00,
121         0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04,
122         0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00
123     };
124     struct audsettings wav_as = conf_out.settings;
125 
126     (void) as;
127 
128     stereo = wav_as.nchannels == 2;
129     switch (wav_as.fmt) {
130     case AUD_FMT_S8:
131     case AUD_FMT_U8:
132         bits16 = 0;
133         break;
134 
135     case AUD_FMT_S16:
136     case AUD_FMT_U16:
137         bits16 = 1;
138         break;
139 
140     case AUD_FMT_S32:
141     case AUD_FMT_U32:
142         dolog ("WAVE files can not handle 32bit formats\n");
143         return -1;
144     }
145 
146     hdr[34] = bits16 ? 0x10 : 0x08;
147 
148     wav_as.endianness = 0;
149     audio_pcm_init_info (&hw->info, &wav_as);
150 
151     hw->samples = 1024;
152     wav->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
153     if (!wav->pcm_buf) {
154         dolog ("Could not allocate buffer (%d bytes)\n",
155                hw->samples << hw->info.shift);
156         return -1;
157     }
158 
159     le_store (hdr + 22, hw->info.nchannels, 2);
160     le_store (hdr + 24, hw->info.freq, 4);
161     le_store (hdr + 28, hw->info.freq << (bits16 + stereo), 4);
162     le_store (hdr + 32, 1 << (bits16 + stereo), 2);
163 
164     wav->f = qemu_fopen (conf_out.wav_path, "wb");
165     if (!wav->f) {
166         dolog ("Failed to open wave file `%s'\nReason: %s\n",
167                conf_out.wav_path, strerror (errno));
168         qemu_free (wav->pcm_buf);
169         wav->pcm_buf = NULL;
170         return -1;
171     }
172 
173     qemu_put_buffer (wav->f, hdr, sizeof (hdr));
174     return 0;
175 }
176 
wav_out_fini(HWVoiceOut * hw)177 static void wav_out_fini (HWVoiceOut *hw)
178 {
179     WAVVoiceOut *wav = (WAVVoiceOut *) hw;
180     uint8_t rlen[4];
181     uint8_t dlen[4];
182     uint32_t datalen = wav->total_samples << hw->info.shift;
183     uint32_t rifflen = datalen + 36;
184 
185     if (!wav->f) {
186         return;
187     }
188 
189     le_store (rlen, rifflen, 4);
190     le_store (dlen, datalen, 4);
191 
192     qemu_fseek (wav->f, 4, SEEK_SET);
193     qemu_put_buffer (wav->f, rlen, 4);
194 
195     qemu_fseek (wav->f, 32, SEEK_CUR);
196     qemu_put_buffer (wav->f, dlen, 4);
197 
198     qemu_fclose (wav->f);
199     wav->f = NULL;
200 
201     qemu_free (wav->pcm_buf);
202     wav->pcm_buf = NULL;
203 }
204 
wav_out_ctl(HWVoiceOut * hw,int cmd,...)205 static int wav_out_ctl (HWVoiceOut *hw, int cmd, ...)
206 {
207     (void) hw;
208     (void) cmd;
209     return 0;
210 }
211 
212 
213 #if WAV_AUDIO_IN
214 
215 /** WAV IN (Reading from a .WAV file)
216  **/
217 
218  static struct {
219     const char *wav_path;
220 } conf_in = {
221     "qemu.wav"
222 };
223 
224 typedef struct WAVVoiceIn {
225     HWVoiceIn  hw;
226     QEMUFile*  f;
227     int64_t    old_ticks;
228     void*      pcm_buf;
229     int        total_samples;
230     int        total_size;
231 } WAVVoiceIn;
232 
233 
234 static int
le_read(const uint8_t * p,int size)235 le_read( const uint8_t*  p, int  size ) {
236     int  shift  = 0;
237     int  result = 0;
238     for ( ; size > 0; size-- ) {
239         result = result | (p[0] << shift);
240         p     += 1;
241         shift += 8;
242     }
243     return  result;
244 }
245 
246 static int
wav_in_init(HWVoiceIn * hw,struct audsettings * as)247 wav_in_init (HWVoiceIn *hw, struct audsettings *as)
248 {
249     WAVVoiceIn*  wav = (WAVVoiceIn *) hw;
250     const char*  path = conf_in.wav_path;
251     uint8_t      hdr[44];
252     struct audsettings wav_as = *as;
253     int           nchannels, freq, format, bits;
254 
255     wav->f = qemu_fopen (path, "rb");
256     if (wav->f == NULL) {
257         dolog("Failed to open wave file '%s'\nReason: %s\n", path,
258               strerror(errno));
259         return -1;
260     }
261 
262     if (qemu_get_buffer (wav->f, hdr, sizeof(hdr)) != (int)sizeof(hdr)) {
263         dolog("File '%s' to be a .wav file\n", path);
264         goto Fail;
265     }
266 
267     /* check that this is a wave file */
268     if ( hdr[0] != 'R' || hdr[1] != 'I' || hdr[2] != 'F' || hdr[3] != 'F' ||
269          hdr[8] != 'W' || hdr[9] != 'A' || hdr[10]!= 'V' || hdr[11]!= 'E' ||
270          hdr[12]!= 'f' || hdr[13]!= 'm' || hdr[14]!= 't' || hdr[15]!= ' ' ||
271          hdr[40]!= 'd' || hdr[41]!= 'a' || hdr[42]!= 't' || hdr[43]!= 'a') {
272          dolog("File '%s' is not a valid .wav file\n", path);
273          goto Fail;
274     }
275 
276     nchannels   = le_read( hdr+22, 2 );
277     freq        = le_read( hdr+24, 4 );
278     format      = le_read( hdr+32, 2 );
279     bits        = le_read( hdr+34, 2 );
280 
281     wav->total_size = le_read( hdr+40, 4 );
282 
283     /* perform some sainty checks */
284     switch (nchannels) {
285         case 1:
286         case 2: break;
287         default:
288             dolog("unsupported number of channels (%d) in '%s'\n",
289                   nchannels, path);
290             goto Fail;
291     }
292 
293     switch (format) {
294         case 1:
295         case 2:
296         case 4: break;
297         default:
298             dolog("unsupported bytes per sample (%d) in '%s'\n",
299                   format, path);
300             goto Fail;
301     }
302 
303     if (format*8/nchannels != bits) {
304         dolog("invalid bits per sample (%d, expected %d) in '%s'\n",
305               bits, format*8/nchannels, path);
306         goto Fail;
307     }
308 
309     wav_as.nchannels  = nchannels;
310     wav_as.fmt        = (bits == 8) ? AUD_FMT_U8 : AUD_FMT_S16;
311     wav_as.freq       = freq;
312     wav_as.endianness = 0;  /* always little endian */
313 
314     audio_pcm_init_info (&hw->info, &wav_as);
315 
316     hw->samples  = 1024;
317     wav->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
318     if (!wav->pcm_buf) {
319         goto Fail;
320     }
321     return 0;
322 
323 Fail:
324     qemu_fclose (wav->f);
325     wav->f = NULL;
326     return -1;
327 }
328 
329 
wav_in_fini(HWVoiceIn * hw)330 static void wav_in_fini (HWVoiceIn *hw)
331 {
332     WAVVoiceIn *wav = (WAVVoiceIn *) hw;
333 
334     if (!wav->f) {
335         return;
336     }
337 
338     qemu_fclose (wav->f);
339     wav->f = NULL;
340 
341     qemu_free (wav->pcm_buf);
342     wav->pcm_buf = NULL;
343 }
344 
wav_in_run(HWVoiceIn * hw)345 static int wav_in_run (HWVoiceIn *hw)
346 {
347     WAVVoiceIn*   wav = (WAVVoiceIn *) hw;
348     int           wpos, live, decr, samples;
349     uint8_t*      src;
350     struct st_sample*  dst;
351 
352     int64_t  now   = qemu_get_clock (vm_clock);
353     int64_t  ticks = now - wav->old_ticks;
354     int64_t  bytes = muldiv64(ticks, hw->info.bytes_per_second, get_ticks_per_sec());
355 
356     if (bytes > INT_MAX) {
357         samples = INT_MAX >> hw->info.shift;
358     }
359     else {
360         samples = bytes >> hw->info.shift;
361     }
362 
363     live = audio_pcm_hw_get_live_in (hw);
364     if (!live) {
365         return 0;
366     }
367 
368     wav->old_ticks = now;
369 
370     decr    = audio_MIN (live, samples);
371     samples = decr;
372     wpos    = hw->wpos;
373     while (samples) {
374         int left_till_end_samples = hw->samples - wpos;
375         int convert_samples       = audio_MIN (samples, left_till_end_samples);
376 
377         dst = hw->conv_buf + wpos;
378         src = advance (wav->pcm_buf, wpos << hw->info.shift);
379 
380         qemu_get_buffer (wav->f, src, convert_samples << hw->info.shift);
381         memcpy (dst, src, convert_samples << hw->info.shift);
382 
383         wpos                = (wpos + convert_samples) % hw->samples;
384         samples            -= convert_samples;
385         wav->total_samples += convert_samples;
386     }
387 
388     hw->wpos = wpos;
389     return decr;
390 }
391 
wav_in_read(SWVoiceIn * sw,void * buf,int len)392 static int wav_in_read (SWVoiceIn *sw, void *buf, int len)
393 {
394     return audio_pcm_sw_read (sw, buf, len);
395 }
396 
wav_in_ctl(HWVoiceIn * hw,int cmd,...)397 static int wav_in_ctl (HWVoiceIn *hw, int cmd, ...)
398 {
399     (void) hw;
400     (void) cmd;
401     return 0;
402 }
403 
404 #endif  /* WAV_AUDIO_IN */
405 
406 /** COMMON CODE
407  **/
wav_audio_init(void)408 static void *wav_audio_init (void)
409 {
410     return &conf_out;
411 }
412 
wav_audio_fini(void * opaque)413 static void wav_audio_fini (void *opaque)
414 {
415     (void) opaque;
416     ldebug ("wav_fini");
417 }
418 
419 static struct audio_option wav_options[] = {
420     {"FREQUENCY", AUD_OPT_INT, &conf_out.settings.freq,
421      "Frequency", NULL, 0},
422 
423     {"FORMAT", AUD_OPT_FMT, &conf_out.settings.fmt,
424      "Format", NULL, 0},
425 
426     {"DAC_FIXED_CHANNELS", AUD_OPT_INT, &conf_out.settings.nchannels,
427      "Number of channels (1 - mono, 2 - stereo)", NULL, 0},
428 
429     {"PATH", AUD_OPT_STR, &conf_out.wav_path,
430      "Path to output .wav file", NULL, 0},
431 
432 #if WAV_AUDIO_IN
433     {"IN_PATH", AUD_OPT_STR, &conf_in.wav_path,
434      "Path to input .wav file", NULL, 0},
435 #endif
436     {NULL, 0, NULL, NULL, NULL, 0}
437 };
438 
439 struct audio_pcm_ops wav_pcm_ops = {
440     wav_out_init,
441     wav_out_fini,
442     wav_out_run,
443     wav_out_write,
444     wav_out_ctl,
445 
446 #if WAV_AUDIO_IN
447     wav_in_init,
448     wav_in_fini,
449     wav_in_run,
450     wav_in_read,
451     wav_in_ctl
452 #else
453     NULL,
454     NULL,
455     NULL,
456     NULL,
457     NULL
458 #endif
459 };
460 
461 struct audio_driver wav_audio_driver = {
462     INIT_FIELD (name           = ) "wav",
463     INIT_FIELD (descr          = )
464     "WAV file read/write (www.wikipedia.org/wiki/WAV)",
465     INIT_FIELD (options        = ) wav_options,
466     INIT_FIELD (init           = ) wav_audio_init,
467     INIT_FIELD (fini           = ) wav_audio_fini,
468     INIT_FIELD (pcm_ops        = ) &wav_pcm_ops,
469     INIT_FIELD (can_be_default = ) 0,
470 #if WAV_AUDIO_IN
471     INIT_FIELD (max_voices_in  = ) 1,
472     INIT_FIELD (max_voices_out = ) 1,
473     INIT_FIELD (voice_size_out = ) sizeof (WAVVoiceOut),
474     INIT_FIELD (voice_size_in  = ) sizeof (WAVVoiceIn)
475 #else
476     INIT_FIELD (max_voices_out = ) 1,
477     INIT_FIELD (max_voices_in  = ) 0,
478     INIT_FIELD (voice_size_out = ) sizeof (WAVVoiceOut),
479     INIT_FIELD (voice_size_in  = ) 0
480 #endif
481 };
482