Home | History | Annotate | Download | only in audio
      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 
     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 
     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. */
    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 
    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 
    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 
    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
    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
    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 
    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 
    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 
    392 static int wav_in_read (SWVoiceIn *sw, void *buf, int len)
    393 {
    394     return audio_pcm_sw_read (sw, buf, len);
    395 }
    396 
    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  **/
    408 static void *wav_audio_init (void)
    409 {
    410     return &conf_out;
    411 }
    412 
    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