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