Statistics
| Branch: | Revision:

root / audio / wavaudio.c @ 6ee093c9

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 = (ticks * hw->info.bytes_per_second) / get_ticks_per_sec();
58

    
59
    if (bytes > INT_MAX) {
60
        samples = INT_MAX >> hw->info.shift;
61
    }
62
    else {
63
        samples = bytes >> hw->info.shift;
64
    }
65

    
66
    live = audio_pcm_hw_get_live_out (hw);
67
    if (!live) {
68
        return 0;
69
    }
70

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

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

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

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

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

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

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

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

    
121
    (void) as;
122

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

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

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

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

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

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

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

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

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

    
172
static 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
    qemu_fseek (wav->f, 4, SEEK_SET);
188
    qemu_put_buffer (wav->f, rlen, 4);
189

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

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

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

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

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

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

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

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

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