All new accounts created on Gitlab now require administrator approval. If you invite any collaborators, please let Flux staff know so they can approve the accounts.

wavaudio.c 7.83 KB
Newer Older
bellard's avatar
bellard committed
1
/*
2 3 4 5
 * QEMU WAV audio driver
 *
 * Copyright (c) 2004-2005 Vassili Karpov (malc)
 *
bellard's avatar
bellard committed
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
pbrook's avatar
pbrook committed
24
#include "hw/hw.h"
25
#include "qemu/timer.h"
pbrook's avatar
pbrook committed
26
#include "audio.h"
bellard's avatar
bellard committed
27

28 29
#define AUDIO_CAP "wav"
#include "audio_int.h"
30

31 32
typedef struct WAVVoiceOut {
    HWVoiceOut hw;
33
    FILE *f;
34 35 36
    int64_t old_ticks;
    void *pcm_buf;
    int total_samples;
37
} WAVVoiceOut;
bellard's avatar
bellard committed
38 39

static struct {
malc's avatar
malc committed
40
    struct audsettings settings;
bellard's avatar
bellard committed
41 42
    const char *wav_path;
} conf = {
43 44 45 46
    .settings.freq      = 44100,
    .settings.nchannels = 2,
    .settings.fmt       = AUD_FMT_S16,
    .wav_path           = "qemu.wav"
bellard's avatar
bellard committed
47 48
};

malc's avatar
malc committed
49
static int wav_run_out (HWVoiceOut *hw, int live)
bellard's avatar
bellard committed
50
{
51
    WAVVoiceOut *wav = (WAVVoiceOut *) hw;
malc's avatar
malc committed
52
    int rpos, decr, samples;
bellard's avatar
bellard committed
53
    uint8_t *dst;
malc's avatar
malc committed
54
    struct st_sample *src;
55
    int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
bellard's avatar
bellard committed
56
    int64_t ticks = now - wav->old_ticks;
57 58
    int64_t bytes =
        muldiv64 (ticks, hw->info.bytes_per_second, get_ticks_per_sec ());
bellard's avatar
bellard committed
59

60 61 62 63 64 65
    if (bytes > INT_MAX) {
        samples = INT_MAX >> hw->info.shift;
    }
    else {
        samples = bytes >> hw->info.shift;
    }
bellard's avatar
bellard committed
66

bellard's avatar
bellard committed
67
    wav->old_ticks = now;
bellard's avatar
bellard committed
68 69 70 71 72 73 74
    decr = audio_MIN (live, samples);
    samples = decr;
    rpos = hw->rpos;
    while (samples) {
        int left_till_end_samples = hw->samples - rpos;
        int convert_samples = audio_MIN (samples, left_till_end_samples);

75 76
        src = hw->mix_buf + rpos;
        dst = advance (wav->pcm_buf, rpos << hw->info.shift);
bellard's avatar
bellard committed
77 78

        hw->clip (dst, src, convert_samples);
79 80 81 82
        if (fwrite (dst, convert_samples << hw->info.shift, 1, wav->f) != 1) {
            dolog ("wav_run_out: fwrite of %d bytes failed\nReaons: %s\n",
                   convert_samples << hw->info.shift, strerror (errno));
        }
bellard's avatar
bellard committed
83 84 85 86 87 88 89

        rpos = (rpos + convert_samples) % hw->samples;
        samples -= convert_samples;
        wav->total_samples += convert_samples;
    }

    hw->rpos = rpos;
90
    return decr;
bellard's avatar
bellard committed
91 92
}

93
static int wav_write_out (SWVoiceOut *sw, void *buf, int len)
bellard's avatar
bellard committed
94
{
95
    return audio_pcm_sw_write (sw, buf, len);
bellard's avatar
bellard committed
96 97 98 99 100 101 102 103 104 105 106 107
}

/* VICE code: Store number as little endian. */
static void le_store (uint8_t *buf, uint32_t val, int len)
{
    int i;
    for (i = 0; i < len; i++) {
        buf[i] = (uint8_t) (val & 0xff);
        val >>= 8;
    }
}

malc's avatar
malc committed
108
static int wav_init_out (HWVoiceOut *hw, struct audsettings *as)
bellard's avatar
bellard committed
109
{
110
    WAVVoiceOut *wav = (WAVVoiceOut *) hw;
bellard's avatar
bellard committed
111
    int bits16 = 0, stereo = 0;
bellard's avatar
bellard committed
112 113 114 115 116 117
    uint8_t hdr[] = {
        0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56,
        0x45, 0x66, 0x6d, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00,
        0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04,
        0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00
    };
malc's avatar
malc committed
118
    struct audsettings wav_as = conf.settings;
bellard's avatar
bellard committed
119

bellard's avatar
bellard committed
120
    (void) as;
121

bellard's avatar
bellard committed
122 123
    stereo = wav_as.nchannels == 2;
    switch (wav_as.fmt) {
bellard's avatar
bellard committed
124 125
    case AUD_FMT_S8:
    case AUD_FMT_U8:
126
        bits16 = 0;
bellard's avatar
bellard committed
127 128 129 130 131 132
        break;

    case AUD_FMT_S16:
    case AUD_FMT_U16:
        bits16 = 1;
        break;
133 134 135 136 137

    case AUD_FMT_S32:
    case AUD_FMT_U32:
        dolog ("WAVE files can not handle 32bit formats\n");
        return -1;
bellard's avatar
bellard committed
138 139 140
    }

    hdr[34] = bits16 ? 0x10 : 0x08;
bellard's avatar
bellard committed
141

bellard's avatar
bellard committed
142 143
    wav_as.endianness = 0;
    audio_pcm_init_info (&hw->info, &wav_as);
bellard's avatar
bellard committed
144 145 146

    hw->samples = 1024;
    wav->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
147
    if (!wav->pcm_buf) {
bellard's avatar
bellard committed
148 149
        dolog ("Could not allocate buffer (%d bytes)\n",
               hw->samples << hw->info.shift);
bellard's avatar
bellard committed
150
        return -1;
151
    }
bellard's avatar
bellard committed
152

153 154
    le_store (hdr + 22, hw->info.nchannels, 2);
    le_store (hdr + 24, hw->info.freq, 4);
bellard's avatar
bellard committed
155 156
    le_store (hdr + 28, hw->info.freq << (bits16 + stereo), 4);
    le_store (hdr + 32, 1 << (bits16 + stereo), 2);
bellard's avatar
bellard committed
157

158
    wav->f = fopen (conf.wav_path, "wb");
bellard's avatar
bellard committed
159
    if (!wav->f) {
160
        dolog ("Failed to open wave file `%s'\nReason: %s\n",
bellard's avatar
bellard committed
161
               conf.wav_path, strerror (errno));
162
        g_free (wav->pcm_buf);
bellard's avatar
bellard committed
163
        wav->pcm_buf = NULL;
bellard's avatar
bellard committed
164 165 166
        return -1;
    }

167 168 169 170 171
    if (fwrite (hdr, sizeof (hdr), 1, wav->f) != 1) {
        dolog ("wav_init_out: failed to write header\nReason: %s\n",
               strerror(errno));
        return -1;
    }
bellard's avatar
bellard committed
172 173 174
    return 0;
}

175
static void wav_fini_out (HWVoiceOut *hw)
bellard's avatar
bellard committed
176
{
177
    WAVVoiceOut *wav = (WAVVoiceOut *) hw;
bellard's avatar
bellard committed
178 179
    uint8_t rlen[4];
    uint8_t dlen[4];
bellard's avatar
bellard committed
180 181
    uint32_t datalen = wav->total_samples << hw->info.shift;
    uint32_t rifflen = datalen + 36;
bellard's avatar
bellard committed
182

bellard's avatar
bellard committed
183
    if (!wav->f) {
bellard's avatar
bellard committed
184
        return;
185
    }
bellard's avatar
bellard committed
186 187 188 189

    le_store (rlen, rifflen, 4);
    le_store (dlen, datalen, 4);

190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209
    if (fseek (wav->f, 4, SEEK_SET)) {
        dolog ("wav_fini_out: fseek to rlen failed\nReason: %s\n",
               strerror(errno));
        goto doclose;
    }
    if (fwrite (rlen, 4, 1, wav->f) != 1) {
        dolog ("wav_fini_out: failed to write rlen\nReason: %s\n",
               strerror (errno));
        goto doclose;
    }
    if (fseek (wav->f, 32, SEEK_CUR)) {
        dolog ("wav_fini_out: fseek to dlen failed\nReason: %s\n",
               strerror (errno));
        goto doclose;
    }
    if (fwrite (dlen, 4, 1, wav->f) != 1) {
        dolog ("wav_fini_out: failed to write dlen\nReaons: %s\n",
               strerror (errno));
        goto doclose;
    }
bellard's avatar
bellard committed
210

211 212 213 214 215
 doclose:
    if (fclose (wav->f))  {
        dolog ("wav_fini_out: fclose %p failed\nReason: %s\n",
               wav->f, strerror (errno));
    }
bellard's avatar
bellard committed
216
    wav->f = NULL;
bellard's avatar
bellard committed
217

218
    g_free (wav->pcm_buf);
bellard's avatar
bellard committed
219
    wav->pcm_buf = NULL;
bellard's avatar
bellard committed
220 221
}

222
static int wav_ctl_out (HWVoiceOut *hw, int cmd, ...)
bellard's avatar
bellard committed
223 224 225 226 227 228 229 230 231 232 233 234 235
{
    (void) hw;
    (void) cmd;
    return 0;
}

static void *wav_audio_init (void)
{
    return &conf;
}

static void wav_audio_fini (void *opaque)
{
236
    (void) opaque;
bellard's avatar
bellard committed
237 238 239
    ldebug ("wav_fini");
}

blueswir1's avatar
blueswir1 committed
240
static struct audio_option wav_options[] = {
malc's avatar
malc committed
241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264
    {
        .name  = "FREQUENCY",
        .tag   = AUD_OPT_INT,
        .valp  = &conf.settings.freq,
        .descr = "Frequency"
    },
    {
        .name  = "FORMAT",
        .tag   = AUD_OPT_FMT,
        .valp  = &conf.settings.fmt,
        .descr = "Format"
    },
    {
        .name  = "DAC_FIXED_CHANNELS",
        .tag   = AUD_OPT_INT,
        .valp  = &conf.settings.nchannels,
        .descr = "Number of channels (1 - mono, 2 - stereo)"
    },
    {
        .name  = "PATH",
        .tag   = AUD_OPT_STR,
        .valp  = &conf.wav_path,
        .descr = "Path to wave file"
    },
265
    { /* End of list */ }
266 267
};

268
static struct audio_pcm_ops wav_pcm_ops = {
269 270 271 272 273
    .init_out = wav_init_out,
    .fini_out = wav_fini_out,
    .run_out  = wav_run_out,
    .write    = wav_write_out,
    .ctl_out  = wav_ctl_out,
bellard's avatar
bellard committed
274 275
};

276
struct audio_driver wav_audio_driver = {
277 278 279 280 281
    .name           = "wav",
    .descr          = "WAV renderer http://wikipedia.org/wiki/WAV",
    .options        = wav_options,
    .init           = wav_audio_init,
    .fini           = wav_audio_fini,
malc's avatar
malc committed
282
    .pcm_ops        = &wav_pcm_ops,
283 284 285 286 287
    .can_be_default = 0,
    .max_voices_out = 1,
    .max_voices_in  = 0,
    .voice_size_out = sizeof (WAVVoiceOut),
    .voice_size_in  = 0
bellard's avatar
bellard committed
288
};