Statistics
| Branch: | Revision:

root / audio / wavaudio.c @ d929eba5

History | View | Annotate | Download (6.6 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 "vl.h"
25

    
26
#define AUDIO_CAP "wav"
27
#include "audio_int.h"
28

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

    
37
static struct {
38
    audsettings_t settings;
39
    const char *wav_path;
40
} conf = {
41
    {
42
        44100,
43
        2,
44
        AUD_FMT_S16
45
    },
46
    "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
    st_sample_t *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) / 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, audsettings_t *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
    audsettings_t 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

    
136
    hdr[34] = bits16 ? 0x10 : 0x08;
137

    
138
    wav_as.endianness = 0;
139
    audio_pcm_init_info (&hw->info, &wav_as);
140

    
141
    hw->samples = 1024;
142
    wav->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
143
    if (!wav->pcm_buf) {
144
        dolog ("Could not allocate buffer (%d bytes)\n",
145
               hw->samples << hw->info.shift);
146
        return -1;
147
    }
148

    
149
    le_store (hdr + 22, hw->info.nchannels, 2);
150
    le_store (hdr + 24, hw->info.freq, 4);
151
    le_store (hdr + 28, hw->info.freq << (bits16 + stereo), 4);
152
    le_store (hdr + 32, 1 << (bits16 + stereo), 2);
153

    
154
    wav->f = fopen (conf.wav_path, "wb");
155
    if (!wav->f) {
156
        dolog ("Failed to open wave file `%s'\nReason: %s\n",
157
               conf.wav_path, strerror (errno));
158
        qemu_free (wav->pcm_buf);
159
        wav->pcm_buf = NULL;
160
        return -1;
161
    }
162

    
163
    qemu_put_buffer (wav->f, hdr, sizeof (hdr));
164
    return 0;
165
}
166

    
167
static void wav_fini_out (HWVoiceOut *hw)
168
{
169
    WAVVoiceOut *wav = (WAVVoiceOut *) hw;
170
    uint8_t rlen[4];
171
    uint8_t dlen[4];
172
    uint32_t datalen = wav->total_samples << hw->info.shift;
173
    uint32_t rifflen = datalen + 36;
174

    
175
    if (!wav->f) {
176
        return;
177
    }
178

    
179
    le_store (rlen, rifflen, 4);
180
    le_store (dlen, datalen, 4);
181

    
182
    qemu_fseek (wav->f, 4, SEEK_SET);
183
    qemu_put_buffer (wav->f, rlen, 4);
184

    
185
    qemu_fseek (wav->f, 32, SEEK_CUR);
186
    qemu_put_buffer (wav->f, dlen, 4);
187

    
188
    fclose (wav->f);
189
    wav->f = NULL;
190

    
191
    qemu_free (wav->pcm_buf);
192
    wav->pcm_buf = NULL;
193
}
194

    
195
static int wav_ctl_out (HWVoiceOut *hw, int cmd, ...)
196
{
197
    (void) hw;
198
    (void) cmd;
199
    return 0;
200
}
201

    
202
static void *wav_audio_init (void)
203
{
204
    return &conf;
205
}
206

    
207
static void wav_audio_fini (void *opaque)
208
{
209
    (void) opaque;
210
    ldebug ("wav_fini");
211
}
212

    
213
struct audio_option wav_options[] = {
214
    {"FREQUENCY", AUD_OPT_INT, &conf.settings.freq,
215
     "Frequency", NULL, 0},
216

    
217
    {"FORMAT", AUD_OPT_FMT, &conf.settings.fmt,
218
     "Format", NULL, 0},
219

    
220
    {"DAC_FIXED_CHANNELS", AUD_OPT_INT, &conf.settings.nchannels,
221
     "Number of channels (1 - mono, 2 - stereo)", NULL, 0},
222

    
223
    {"PATH", AUD_OPT_STR, &conf.wav_path,
224
     "Path to wave file", NULL, 0},
225
    {NULL, 0, NULL, NULL, NULL, 0}
226
};
227

    
228
struct audio_pcm_ops wav_pcm_ops = {
229
    wav_init_out,
230
    wav_fini_out,
231
    wav_run_out,
232
    wav_write_out,
233
    wav_ctl_out,
234

    
235
    NULL,
236
    NULL,
237
    NULL,
238
    NULL,
239
    NULL
240
};
241

    
242
struct audio_driver wav_audio_driver = {
243
    INIT_FIELD (name           = ) "wav",
244
    INIT_FIELD (descr          = )
245
    "WAV renderer http://wikipedia.org/wiki/WAV",
246
    INIT_FIELD (options        = ) wav_options,
247
    INIT_FIELD (init           = ) wav_audio_init,
248
    INIT_FIELD (fini           = ) wav_audio_fini,
249
    INIT_FIELD (pcm_ops        = ) &wav_pcm_ops,
250
    INIT_FIELD (can_be_default = ) 0,
251
    INIT_FIELD (max_voices_out = ) 1,
252
    INIT_FIELD (max_voices_in  = ) 0,
253
    INIT_FIELD (voice_size_out = ) sizeof (WAVVoiceOut),
254
    INIT_FIELD (voice_size_in  = ) 0
255
};