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 "qemu/osdep.h"
  25#include "qemu/host-utils.h"
  26#include "qemu/timer.h"
  27#include "qapi/opts-visitor.h"
  28#include "audio.h"
  29
  30#define AUDIO_CAP "wav"
  31#include "audio_int.h"
  32
  33typedef struct WAVVoiceOut {
  34    HWVoiceOut hw;
  35    FILE *f;
  36    int64_t old_ticks;
  37    void *pcm_buf;
  38    int total_samples;
  39} WAVVoiceOut;
  40
  41static int wav_run_out (HWVoiceOut *hw, int live)
  42{
  43    WAVVoiceOut *wav = (WAVVoiceOut *) hw;
  44    int rpos, decr, samples;
  45    uint8_t *dst;
  46    struct st_sample *src;
  47    int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
  48    int64_t ticks = now - wav->old_ticks;
  49    int64_t bytes =
  50        muldiv64(ticks, hw->info.bytes_per_second, NANOSECONDS_PER_SECOND);
  51
  52    if (bytes > INT_MAX) {
  53        samples = INT_MAX >> hw->info.shift;
  54    }
  55    else {
  56        samples = bytes >> hw->info.shift;
  57    }
  58
  59    wav->old_ticks = now;
  60    decr = audio_MIN (live, samples);
  61    samples = decr;
  62    rpos = hw->rpos;
  63    while (samples) {
  64        int left_till_end_samples = hw->samples - rpos;
  65        int convert_samples = audio_MIN (samples, left_till_end_samples);
  66
  67        src = hw->mix_buf + rpos;
  68        dst = advance (wav->pcm_buf, rpos << hw->info.shift);
  69
  70        hw->clip (dst, src, convert_samples);
  71        if (fwrite (dst, convert_samples << hw->info.shift, 1, wav->f) != 1) {
  72            dolog ("wav_run_out: fwrite of %d bytes failed\nReaons: %s\n",
  73                   convert_samples << hw->info.shift, strerror (errno));
  74        }
  75
  76        rpos = (rpos + convert_samples) % hw->samples;
  77        samples -= convert_samples;
  78        wav->total_samples += convert_samples;
  79    }
  80
  81    hw->rpos = rpos;
  82    return decr;
  83}
  84
  85static int wav_write_out (SWVoiceOut *sw, void *buf, int len)
  86{
  87    return audio_pcm_sw_write (sw, buf, len);
  88}
  89
  90/* VICE code: Store number as little endian. */
  91static void le_store (uint8_t *buf, uint32_t val, int len)
  92{
  93    int i;
  94    for (i = 0; i < len; i++) {
  95        buf[i] = (uint8_t) (val & 0xff);
  96        val >>= 8;
  97    }
  98}
  99
 100static int wav_init_out(HWVoiceOut *hw, struct audsettings *as,
 101                        void *drv_opaque)
 102{
 103    WAVVoiceOut *wav = (WAVVoiceOut *) hw;
 104    int bits16 = 0, stereo = 0;
 105    uint8_t hdr[] = {
 106        0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56,
 107        0x45, 0x66, 0x6d, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00,
 108        0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04,
 109        0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00
 110    };
 111    Audiodev *dev = drv_opaque;
 112    AudiodevWavOptions *wopts = &dev->u.wav;
 113    struct audsettings wav_as = audiodev_to_audsettings(dev->u.wav.out);
 114    const char *wav_path = wopts->has_path ? wopts->path : "qemu.wav";
 115
 116    stereo = wav_as.nchannels == 2;
 117    switch (wav_as.fmt) {
 118    case AUDIO_FORMAT_S8:
 119    case AUDIO_FORMAT_U8:
 120        bits16 = 0;
 121        break;
 122
 123    case AUDIO_FORMAT_S16:
 124    case AUDIO_FORMAT_U16:
 125        bits16 = 1;
 126        break;
 127
 128    case AUDIO_FORMAT_S32:
 129    case AUDIO_FORMAT_U32:
 130        dolog ("WAVE files can not handle 32bit formats\n");
 131        return -1;
 132
 133    default:
 134        abort();
 135    }
 136
 137    hdr[34] = bits16 ? 0x10 : 0x08;
 138
 139    wav_as.endianness = 0;
 140    audio_pcm_init_info (&hw->info, &wav_as);
 141
 142    hw->samples = 1024;
 143    wav->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift);
 144    if (!wav->pcm_buf) {
 145        dolog ("Could not allocate buffer (%d bytes)\n",
 146               hw->samples << hw->info.shift);
 147        return -1;
 148    }
 149
 150    le_store (hdr + 22, hw->info.nchannels, 2);
 151    le_store (hdr + 24, hw->info.freq, 4);
 152    le_store (hdr + 28, hw->info.freq << (bits16 + stereo), 4);
 153    le_store (hdr + 32, 1 << (bits16 + stereo), 2);
 154
 155    wav->f = fopen(wav_path, "wb");
 156    if (!wav->f) {
 157        dolog ("Failed to open wave file `%s'\nReason: %s\n",
 158               wav_path, strerror(errno));
 159        g_free (wav->pcm_buf);
 160        wav->pcm_buf = NULL;
 161        return -1;
 162    }
 163
 164    if (fwrite (hdr, sizeof (hdr), 1, wav->f) != 1) {
 165        dolog ("wav_init_out: failed to write header\nReason: %s\n",
 166               strerror(errno));
 167        return -1;
 168    }
 169    return 0;
 170}
 171
 172static void wav_fini_out (HWVoiceOut *hw)
 173{
 174    WAVVoiceOut *wav = (WAVVoiceOut *) hw;
 175    uint8_t rlen[4];
 176    uint8_t dlen[4];
 177    uint32_t datalen = wav->total_samples << hw->info.shift;
 178    uint32_t rifflen = datalen + 36;
 179
 180    if (!wav->f) {
 181        return;
 182    }
 183
 184    le_store (rlen, rifflen, 4);
 185    le_store (dlen, datalen, 4);
 186
 187    if (fseek (wav->f, 4, SEEK_SET)) {
 188        dolog ("wav_fini_out: fseek to rlen failed\nReason: %s\n",
 189               strerror(errno));
 190        goto doclose;
 191    }
 192    if (fwrite (rlen, 4, 1, wav->f) != 1) {
 193        dolog ("wav_fini_out: failed to write rlen\nReason: %s\n",
 194               strerror (errno));
 195        goto doclose;
 196    }
 197    if (fseek (wav->f, 32, SEEK_CUR)) {
 198        dolog ("wav_fini_out: fseek to dlen failed\nReason: %s\n",
 199               strerror (errno));
 200        goto doclose;
 201    }
 202    if (fwrite (dlen, 4, 1, wav->f) != 1) {
 203        dolog ("wav_fini_out: failed to write dlen\nReaons: %s\n",
 204               strerror (errno));
 205        goto doclose;
 206    }
 207
 208 doclose:
 209    if (fclose (wav->f))  {
 210        dolog ("wav_fini_out: fclose %p failed\nReason: %s\n",
 211               wav->f, strerror (errno));
 212    }
 213    wav->f = NULL;
 214
 215    g_free (wav->pcm_buf);
 216    wav->pcm_buf = NULL;
 217}
 218
 219static int wav_ctl_out (HWVoiceOut *hw, int cmd, ...)
 220{
 221    (void) hw;
 222    (void) cmd;
 223    return 0;
 224}
 225
 226static void *wav_audio_init(Audiodev *dev)
 227{
 228    assert(dev->driver == AUDIODEV_DRIVER_WAV);
 229    return dev;
 230}
 231
 232static void wav_audio_fini (void *opaque)
 233{
 234    ldebug ("wav_fini");
 235}
 236
 237static struct audio_pcm_ops wav_pcm_ops = {
 238    .init_out = wav_init_out,
 239    .fini_out = wav_fini_out,
 240    .run_out  = wav_run_out,
 241    .write    = wav_write_out,
 242    .ctl_out  = wav_ctl_out,
 243};
 244
 245static struct audio_driver wav_audio_driver = {
 246    .name           = "wav",
 247    .descr          = "WAV renderer http://wikipedia.org/wiki/WAV",
 248    .init           = wav_audio_init,
 249    .fini           = wav_audio_fini,
 250    .pcm_ops        = &wav_pcm_ops,
 251    .can_be_default = 0,
 252    .max_voices_out = 1,
 253    .max_voices_in  = 0,
 254    .voice_size_out = sizeof (WAVVoiceOut),
 255    .voice_size_in  = 0
 256};
 257
 258static void register_audio_wav(void)
 259{
 260    audio_driver_register(&wav_audio_driver);
 261}
 262type_init(register_audio_wav);
 263