qemu/audio/wavaudio.c
<<
>>
Prefs
   1/*
   2 * QEMU WAV audio driver
   3 *
   4 * Copyright (c) 2004-2005 Vassili Karpov (malc)
   5 *
   6 * Permission is hereby granted, free of charge, to any person obtaining a copy
   7 * of this software and associated documentation files (the "Software"), to deal
   8 * in the Software without restriction, including without limitation the rights
   9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 * copies of the Software, and to permit persons to whom the Software is
  11 * furnished to do so, subject to the following conditions:
  12 *
  13 * The above copyright notice and this permission notice shall be included in
  14 * all copies or substantial portions of the Software.
  15 *
  16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 * THE SOFTWARE.
  23 */
  24#include "hw/hw.h"
  25#include "qemu/timer.h"
  26#include "audio.h"
  27
  28#define AUDIO_CAP "wav"
  29#include "audio_int.h"
  30
  31typedef struct WAVVoiceOut {
  32    HWVoiceOut hw;
  33    FILE *f;
  34    int64_t old_ticks;
  35    void *pcm_buf;
  36    int total_samples;
  37} WAVVoiceOut;
  38
  39static struct {
  40    struct audsettings settings;
  41    const char *wav_path;
  42} conf = {
  43    .settings.freq      = 44100,
  44    .settings.nchannels = 2,
  45    .settings.fmt       = AUD_FMT_S16,
  46    .wav_path           = "qemu.wav"
  47};
  48
  49static int wav_run_out (HWVoiceOut *hw, int live)
  50{
  51    WAVVoiceOut *wav = (WAVVoiceOut *) hw;
  52    int rpos, decr, samples;
  53    uint8_t *dst;
  54    struct st_sample *src;
  55    int64_t now = qemu_get_clock_ns (vm_clock);
  56    int64_t ticks = now - wav->old_ticks;
  57    int64_t bytes =
  58        muldiv64 (ticks, hw->info.bytes_per_second, get_ticks_per_sec ());
  59
  60    if (bytes > INT_MAX) {
  61        samples = INT_MAX >> hw->info.shift;
  62    }
  63    else {
  64        samples = bytes >> hw->info.shift;
  65    }
  66
  67    wav->old_ticks = now;
  68    decr = audio_MIN (live, samples);
  69    samples = decr;
  70    rpos = hw->rpos;
  71    while (samples) {
  72        int left_till_end_samples = hw->samples - rpos;
  73        int convert_samples = audio_MIN (samples, left_till_end_samples);
  74
  75        src = hw->mix_buf + rpos;
  76        dst = advance (wav->pcm_buf, rpos << hw->info.shift);
  77
  78        hw->clip (dst, src, convert_samples);
  79        if (fwrite (dst, convert_samples << hw->info.shift, 1, wav->f) != 1) {
  80            dolog ("wav_run_out: fwrite of %d bytes failed\nReaons: %s\n",
  81                   convert_samples << hw->info.shift, strerror (errno));
  82        }
  83
  84        rpos = (rpos + convert_samples) % hw->samples;
  85        samples -= convert_samples;
  86        wav->total_samples += convert_samples;
  87    }
  88
  89    hw->rpos = rpos;
  90    return decr;
  91}
  92
  93static int wav_write_out (SWVoiceOut *sw, void *buf, int len)
  94{
  95    return audio_pcm_sw_write (sw, buf, len);
  96}
  97
  98/* VICE code: Store number as little endian. */
  99static void le_store (uint8_t *buf, uint32_t val, int len)
 100{
 101    int i;
 102    for (i = 0; i < len; i++) {
 103        buf[i] = (uint8_t) (val & 0xff);
 104        val >>= 8;
 105    }
 106}
 107
 108static int wav_init_out (HWVoiceOut *hw, struct audsettings *as)
 109{
 110    WAVVoiceOut *wav = (WAVVoiceOut *) hw;
 111    int bits16 = 0, stereo = 0;
 112    uint8_t hdr[] = {
 113        0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56,
 114        0x45, 0x66, 0x6d, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00,
 115        0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04,
 116        0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00
 117    };
 118    struct audsettings wav_as = conf.settings;
 119
 120    (void) as;
 121
 122    stereo = wav_as.nchannels == 2;
 123    switch (wav_as.fmt) {
 124    case AUD_FMT_S8:
 125    case AUD_FMT_U8:
 126        bits16 = 0;
 127        break;
 128
 129    case AUD_FMT_S16:
 130    case AUD_FMT_U16:
 131        bits16 = 1;
 132        break;
 133
 134    case AUD_FMT_S32:
 135    case AUD_FMT_U32:
 136        dolog ("WAVE files can not handle 32bit formats\n");
 137        return -1;
 138    }
 139
 140    hdr[34] = bits16 ? 0x10 : 0x08;
 141
 142    wav_as.endianness = 0;
 143    audio_pcm_init_info (&hw->info, &wav_as);
 144
 145    hw->samples = 1024;
 146    wav->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
 147    if (!wav->pcm_buf) {
 148        dolog ("Could not allocate buffer (%d bytes)\n",
 149               hw->samples << hw->info.shift);
 150        return -1;
 151    }
 152
 153    le_store (hdr + 22, hw->info.nchannels, 2);
 154    le_store (hdr + 24, hw->info.freq, 4);
 155    le_store (hdr + 28, hw->info.freq << (bits16 + stereo), 4);
 156    le_store (hdr + 32, 1 << (bits16 + stereo), 2);
 157
 158    wav->f = fopen (conf.wav_path, "wb");
 159    if (!wav->f) {
 160        dolog ("Failed to open wave file `%s'\nReason: %s\n",
 161               conf.wav_path, strerror (errno));
 162        g_free (wav->pcm_buf);
 163        wav->pcm_buf = NULL;
 164        return -1;
 165    }
 166
 167    if (fwrite (hdr, sizeof (hdr), 1, wav->f) != 1) {
 168        dolog ("wav_init_out: failed to write header\nReason: %s\n",
 169               strerror(errno));
 170        return -1;
 171    }
 172    return 0;
 173}
 174
 175static void wav_fini_out (HWVoiceOut *hw)
 176{
 177    WAVVoiceOut *wav = (WAVVoiceOut *) hw;
 178    uint8_t rlen[4];
 179    uint8_t dlen[4];
 180    uint32_t datalen = wav->total_samples << hw->info.shift;
 181    uint32_t rifflen = datalen + 36;
 182
 183    if (!wav->f) {
 184        return;
 185    }
 186
 187    le_store (rlen, rifflen, 4);
 188    le_store (dlen, datalen, 4);
 189
 190    if (fseek (wav->f, 4, SEEK_SET)) {
 191        dolog ("wav_fini_out: fseek to rlen failed\nReason: %s\n",
 192               strerror(errno));
 193        goto doclose;
 194    }
 195    if (fwrite (rlen, 4, 1, wav->f) != 1) {
 196        dolog ("wav_fini_out: failed to write rlen\nReason: %s\n",
 197               strerror (errno));
 198        goto doclose;
 199    }
 200    if (fseek (wav->f, 32, SEEK_CUR)) {
 201        dolog ("wav_fini_out: fseek to dlen failed\nReason: %s\n",
 202               strerror (errno));
 203        goto doclose;
 204    }
 205    if (fwrite (dlen, 4, 1, wav->f) != 1) {
 206        dolog ("wav_fini_out: failed to write dlen\nReaons: %s\n",
 207               strerror (errno));
 208        goto doclose;
 209    }
 210
 211 doclose:
 212    if (fclose (wav->f))  {
 213        dolog ("wav_fini_out: fclose %p failed\nReason: %s\n",
 214               wav->f, strerror (errno));
 215    }
 216    wav->f = NULL;
 217
 218    g_free (wav->pcm_buf);
 219    wav->pcm_buf = NULL;
 220}
 221
 222static int wav_ctl_out (HWVoiceOut *hw, int cmd, ...)
 223{
 224    (void) hw;
 225    (void) cmd;
 226    return 0;
 227}
 228
 229static void *wav_audio_init (void)
 230{
 231    return &conf;
 232}
 233
 234static void wav_audio_fini (void *opaque)
 235{
 236    (void) opaque;
 237    ldebug ("wav_fini");
 238}
 239
 240static struct audio_option wav_options[] = {
 241    {
 242        .name  = "FREQUENCY",
 243        .tag   = AUD_OPT_INT,
 244        .valp  = &conf.settings.freq,
 245        .descr = "Frequency"
 246    },
 247    {
 248        .name  = "FORMAT",
 249        .tag   = AUD_OPT_FMT,
 250        .valp  = &conf.settings.fmt,
 251        .descr = "Format"
 252    },
 253    {
 254        .name  = "DAC_FIXED_CHANNELS",
 255        .tag   = AUD_OPT_INT,
 256        .valp  = &conf.settings.nchannels,
 257        .descr = "Number of channels (1 - mono, 2 - stereo)"
 258    },
 259    {
 260        .name  = "PATH",
 261        .tag   = AUD_OPT_STR,
 262        .valp  = &conf.wav_path,
 263        .descr = "Path to wave file"
 264    },
 265    { /* End of list */ }
 266};
 267
 268static struct audio_pcm_ops wav_pcm_ops = {
 269    .init_out = wav_init_out,
 270    .fini_out = wav_fini_out,
 271    .run_out  = wav_run_out,
 272    .write    = wav_write_out,
 273    .ctl_out  = wav_ctl_out,
 274};
 275
 276struct audio_driver wav_audio_driver = {
 277    .name           = "wav",
 278    .descr          = "WAV renderer http://wikipedia.org/wiki/WAV",
 279    .options        = wav_options,
 280    .init           = wav_audio_init,
 281    .fini           = wav_audio_fini,
 282    .pcm_ops        = &wav_pcm_ops,
 283    .can_be_default = 0,
 284    .max_voices_out = 1,
 285    .max_voices_in  = 0,
 286    .voice_size_out = sizeof (WAVVoiceOut),
 287    .voice_size_in  = 0
 288};
 289