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 
     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 
     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 
    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. */
    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 
    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 
    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 
    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
    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
    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 
    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 
    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 
    403 static int wav_in_read (SWVoiceIn *sw, void *buf, int len)
    404 {
    405     return audio_pcm_sw_read (sw, buf, len);
    406 }
    407 
    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  **/
    419 static void *wav_audio_init (void)
    420 {
    421     return &conf_out;
    422 }
    423 
    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