Statistics
| Branch: | Revision:

root / audio / wavaudio.c @ c5d6edc3

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 "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
        mixeng_clear (src, convert_samples);
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, audsettings_t *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
    audsettings_t 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

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

    
139
    audio_pcm_init_info (&hw->info, &wav_as, audio_need_to_swap_endian (0));
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
};