Statistics
| Branch: | Revision:

root / audio / wavaudio.c @ 4f4cc0ef

History | View | Annotate | Download (7 kB)

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

    
31
typedef struct WAVVoiceOut {
32
    HWVoiceOut hw;
33
    QEMUFile *f;
34
    int64_t old_ticks;
35
    void *pcm_buf;
36
    int total_samples;
37
} WAVVoiceOut;
38

    
39
static 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

    
49
static int wav_run_out (HWVoiceOut *hw)
50
{
51
    WAVVoiceOut *wav = (WAVVoiceOut *) hw;
52
    int rpos, live, decr, samples;
53
    uint8_t *dst;
54
    struct st_sample *src;
55
    int64_t now = qemu_get_clock (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
    live = audio_pcm_hw_get_live_out (hw);
68
    if (!live) {
69
        return 0;
70
    }
71

    
72
    wav->old_ticks = now;
73
    decr = audio_MIN (live, samples);
74
    samples = decr;
75
    rpos = hw->rpos;
76
    while (samples) {
77
        int left_till_end_samples = hw->samples - rpos;
78
        int convert_samples = audio_MIN (samples, left_till_end_samples);
79

    
80
        src = hw->mix_buf + rpos;
81
        dst = advance (wav->pcm_buf, rpos << hw->info.shift);
82

    
83
        hw->clip (dst, src, convert_samples);
84
        qemu_put_buffer (wav->f, dst, convert_samples << hw->info.shift);
85

    
86
        rpos = (rpos + convert_samples) % hw->samples;
87
        samples -= convert_samples;
88
        wav->total_samples += convert_samples;
89
    }
90

    
91
    hw->rpos = rpos;
92
    return decr;
93
}
94

    
95
static int wav_write_out (SWVoiceOut *sw, void *buf, int len)
96
{
97
    return audio_pcm_sw_write (sw, buf, len);
98
}
99

    
100
/* VICE code: Store number as little endian. */
101
static void le_store (uint8_t *buf, uint32_t val, int len)
102
{
103
    int i;
104
    for (i = 0; i < len; i++) {
105
        buf[i] = (uint8_t) (val & 0xff);
106
        val >>= 8;
107
    }
108
}
109

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

    
122
    (void) as;
123

    
124
    stereo = wav_as.nchannels == 2;
125
    switch (wav_as.fmt) {
126
    case AUD_FMT_S8:
127
    case AUD_FMT_U8:
128
        bits16 = 0;
129
        break;
130

    
131
    case AUD_FMT_S16:
132
    case AUD_FMT_U16:
133
        bits16 = 1;
134
        break;
135

    
136
    case AUD_FMT_S32:
137
    case AUD_FMT_U32:
138
        dolog ("WAVE files can not handle 32bit formats\n");
139
        return -1;
140
    }
141

    
142
    hdr[34] = bits16 ? 0x10 : 0x08;
143

    
144
    wav_as.endianness = 0;
145
    audio_pcm_init_info (&hw->info, &wav_as);
146

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

    
155
    le_store (hdr + 22, hw->info.nchannels, 2);
156
    le_store (hdr + 24, hw->info.freq, 4);
157
    le_store (hdr + 28, hw->info.freq << (bits16 + stereo), 4);
158
    le_store (hdr + 32, 1 << (bits16 + stereo), 2);
159

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

    
169
    qemu_put_buffer (wav->f, hdr, sizeof (hdr));
170
    return 0;
171
}
172

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

    
181
    if (!wav->f) {
182
        return;
183
    }
184

    
185
    le_store (rlen, rifflen, 4);
186
    le_store (dlen, datalen, 4);
187

    
188
    qemu_fseek (wav->f, 4, SEEK_SET);
189
    qemu_put_buffer (wav->f, rlen, 4);
190

    
191
    qemu_fseek (wav->f, 32, SEEK_CUR);
192
    qemu_put_buffer (wav->f, dlen, 4);
193

    
194
    qemu_fclose (wav->f);
195
    wav->f = NULL;
196

    
197
    qemu_free (wav->pcm_buf);
198
    wav->pcm_buf = NULL;
199
}
200

    
201
static int wav_ctl_out (HWVoiceOut *hw, int cmd, ...)
202
{
203
    (void) hw;
204
    (void) cmd;
205
    return 0;
206
}
207

    
208
static void *wav_audio_init (void)
209
{
210
    return &conf;
211
}
212

    
213
static void wav_audio_fini (void *opaque)
214
{
215
    (void) opaque;
216
    ldebug ("wav_fini");
217
}
218

    
219
static struct audio_option wav_options[] = {
220
    {
221
        .name  = "FREQUENCY",
222
        .tag   = AUD_OPT_INT,
223
        .valp  = &conf.settings.freq,
224
        .descr = "Frequency"
225
    },
226
    {
227
        .name  = "FORMAT",
228
        .tag   = AUD_OPT_FMT,
229
        .valp  = &conf.settings.fmt,
230
        .descr = "Format"
231
    },
232
    {
233
        .name  = "DAC_FIXED_CHANNELS",
234
        .tag   = AUD_OPT_INT,
235
        .valp  = &conf.settings.nchannels,
236
        .descr = "Number of channels (1 - mono, 2 - stereo)"
237
    },
238
    {
239
        .name  = "PATH",
240
        .tag   = AUD_OPT_STR,
241
        .valp  = &conf.wav_path,
242
        .descr = "Path to wave file"
243
    },
244
    { /* End of list */ }
245
};
246

    
247
static struct audio_pcm_ops wav_pcm_ops = {
248
    .init_out = wav_init_out,
249
    .fini_out = wav_fini_out,
250
    .run_out  = wav_run_out,
251
    .write    = wav_write_out,
252
    .ctl_out  = wav_ctl_out,
253
};
254

    
255
struct audio_driver wav_audio_driver = {
256
    .name           = "wav",
257
    .descr          = "WAV renderer http://wikipedia.org/wiki/WAV",
258
    .options        = wav_options,
259
    .init           = wav_audio_init,
260
    .fini           = wav_audio_fini,
261
    .pcm_ops        = &wav_pcm_ops,
262
    .can_be_default = 0,
263
    .max_voices_out = 1,
264
    .max_voices_in  = 0,
265
    .voice_size_out = sizeof (WAVVoiceOut),
266
    .voice_size_in  = 0
267
};