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