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