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 **/
37typedef struct WAVVoiceOut {
38    HWVoiceOut hw;
39    QEMUFile *f;
40    int64_t old_ticks;
41    void *pcm_buf;
42    int total_samples;
43} WAVVoiceOut;
44
45static 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
58static 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
99static 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. */
105static 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
114static 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
177static 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
205static 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
224typedef 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
234static int
235le_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
246static int
247wav_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
323Fail:
324    qemu_fclose (wav->f);
325    wav->f = NULL;
326    return -1;
327}
328
329
330static 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
345static 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
392static int wav_in_read (SWVoiceIn *sw, void *buf, int len)
393{
394    return audio_pcm_sw_read (sw, buf, len);
395}
396
397static 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 **/
408static void *wav_audio_init (void)
409{
410    return &conf_out;
411}
412
413static void wav_audio_fini (void *opaque)
414{
415    (void) opaque;
416    ldebug ("wav_fini");
417}
418
419static 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
439struct 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
461struct 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