Statistics
| Branch: | Revision:

root / audio / wavaudio.c @ bee37f32

History | View | Annotate | Download (6.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
    {
44
        44100,
45
        2,
46
        AUD_FMT_S16,
47
        0
48
    },
49
    "qemu.wav"
50
};
51

    
52
static int wav_run_out (HWVoiceOut *hw)
53
{
54
    WAVVoiceOut *wav = (WAVVoiceOut *) hw;
55
    int rpos, live, decr, samples;
56
    uint8_t *dst;
57
    struct st_sample *src;
58
    int64_t now = qemu_get_clock (vm_clock);
59
    int64_t ticks = now - wav->old_ticks;
60
    int64_t bytes = (ticks * hw->info.bytes_per_second) / ticks_per_sec;
61

    
62
    if (bytes > INT_MAX) {
63
        samples = INT_MAX >> hw->info.shift;
64
    }
65
    else {
66
        samples = bytes >> hw->info.shift;
67
    }
68

    
69
    live = audio_pcm_hw_get_live_out (hw);
70
    if (!live) {
71
        return 0;
72
    }
73

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

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

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

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

    
93
    hw->rpos = rpos;
94
    return decr;
95
}
96

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

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

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

    
124
    (void) as;
125

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

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

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

    
144
    hdr[34] = bits16 ? 0x10 : 0x08;
145

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

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

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

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

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

    
175
static 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
    qemu_fseek (wav->f, 4, SEEK_SET);
191
    qemu_put_buffer (wav->f, rlen, 4);
192

    
193
    qemu_fseek (wav->f, 32, SEEK_CUR);
194
    qemu_put_buffer (wav->f, dlen, 4);
195

    
196
    qemu_fclose (wav->f);
197
    wav->f = NULL;
198

    
199
    qemu_free (wav->pcm_buf);
200
    wav->pcm_buf = NULL;
201
}
202

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

    
210
static void *wav_audio_init (void)
211
{
212
    return &conf;
213
}
214

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

    
221
static struct audio_option wav_options[] = {
222
    {"FREQUENCY", AUD_OPT_INT, &conf.settings.freq,
223
     "Frequency", NULL, 0},
224

    
225
    {"FORMAT", AUD_OPT_FMT, &conf.settings.fmt,
226
     "Format", NULL, 0},
227

    
228
    {"DAC_FIXED_CHANNELS", AUD_OPT_INT, &conf.settings.nchannels,
229
     "Number of channels (1 - mono, 2 - stereo)", NULL, 0},
230

    
231
    {"PATH", AUD_OPT_STR, &conf.wav_path,
232
     "Path to wave file", NULL, 0},
233
    {NULL, 0, NULL, NULL, NULL, 0}
234
};
235

    
236
static struct audio_pcm_ops wav_pcm_ops = {
237
    wav_init_out,
238
    wav_fini_out,
239
    wav_run_out,
240
    wav_write_out,
241
    wav_ctl_out,
242

    
243
    NULL,
244
    NULL,
245
    NULL,
246
    NULL,
247
    NULL
248
};
249

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