1 |
/* |
/* |
2 |
* QEMU WAV audio output driver |
* QEMU WAV audio driver |
3 |
* |
* |
4 |
* Copyright (c) 2004 Vassili Karpov (malc) |
* Copyright (c) 2004-2005 Vassili Karpov (malc) |
5 |
* |
* |
6 |
* Permission is hereby granted, free of charge, to any person obtaining a copy |
* 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 |
* of this software and associated documentation files (the "Software"), to deal |
8 |
* in the Software without restriction, including without limitation the rights |
* in the Software without restriction, including without limitation the rights |
23 |
*/ |
*/ |
24 |
#include "vl.h" |
#include "vl.h" |
25 |
|
|
26 |
#include "audio/audio_int.h" |
#define AUDIO_CAP "wav" |
27 |
|
#include "audio_int.h" |
28 |
|
|
29 |
typedef struct WAVVoice { |
typedef struct WAVVoiceOut { |
30 |
HWVoice hw; |
HWVoiceOut hw; |
31 |
QEMUFile *f; |
QEMUFile *f; |
32 |
int64_t old_ticks; |
int64_t old_ticks; |
33 |
void *pcm_buf; |
void *pcm_buf; |
34 |
int total_samples; |
int total_samples; |
35 |
} WAVVoice; |
} WAVVoiceOut; |
|
|
|
|
#define dolog(...) AUD_log ("wav", __VA_ARGS__) |
|
|
#ifdef DEBUG |
|
|
#define ldebug(...) dolog (__VA_ARGS__) |
|
|
#else |
|
|
#define ldebug(...) |
|
|
#endif |
|
36 |
|
|
37 |
static struct { |
static struct { |
38 |
const char *wav_path; |
const char *wav_path; |
40 |
.wav_path = "qemu.wav" |
.wav_path = "qemu.wav" |
41 |
}; |
}; |
42 |
|
|
43 |
static void wav_hw_run (HWVoice *hw) |
static int wav_run_out (HWVoiceOut *hw) |
44 |
{ |
{ |
45 |
WAVVoice *wav = (WAVVoice *) hw; |
WAVVoiceOut *wav = (WAVVoiceOut *) hw; |
46 |
int rpos, live, decr, samples; |
int rpos, live, decr, samples; |
47 |
uint8_t *dst; |
uint8_t *dst; |
48 |
st_sample_t *src; |
st_sample_t *src; |
49 |
int64_t now = qemu_get_clock (vm_clock); |
int64_t now = qemu_get_clock (vm_clock); |
50 |
int64_t ticks = now - wav->old_ticks; |
int64_t ticks = now - wav->old_ticks; |
51 |
int64_t bytes = (ticks * hw->bytes_per_second) / ticks_per_sec; |
int64_t bytes = (ticks * hw->info.bytes_per_second) / ticks_per_sec; |
52 |
|
|
53 |
if (bytes > INT_MAX) |
if (bytes > INT_MAX) { |
54 |
samples = INT_MAX >> hw->shift; |
samples = INT_MAX >> hw->info.shift; |
55 |
else |
} |
56 |
samples = bytes >> hw->shift; |
else { |
57 |
|
samples = bytes >> hw->info.shift; |
58 |
|
} |
59 |
|
|
60 |
live = pcm_hw_get_live (hw); |
live = audio_pcm_hw_get_live_out (hw); |
61 |
if (live <= 0) |
if (!live) { |
62 |
return; |
return 0; |
63 |
|
} |
64 |
|
|
65 |
wav->old_ticks = now; |
wav->old_ticks = now; |
66 |
decr = audio_MIN (live, samples); |
decr = audio_MIN (live, samples); |
70 |
int left_till_end_samples = hw->samples - rpos; |
int left_till_end_samples = hw->samples - rpos; |
71 |
int convert_samples = audio_MIN (samples, left_till_end_samples); |
int convert_samples = audio_MIN (samples, left_till_end_samples); |
72 |
|
|
73 |
src = advance (hw->mix_buf, rpos * sizeof (st_sample_t)); |
src = hw->mix_buf + rpos; |
74 |
dst = advance (wav->pcm_buf, rpos << hw->shift); |
dst = advance (wav->pcm_buf, rpos << hw->info.shift); |
75 |
|
|
76 |
hw->clip (dst, src, convert_samples); |
hw->clip (dst, src, convert_samples); |
77 |
qemu_put_buffer (wav->f, dst, convert_samples << hw->shift); |
qemu_put_buffer (wav->f, dst, convert_samples << hw->info.shift); |
78 |
memset (src, 0, convert_samples * sizeof (st_sample_t)); |
mixeng_clear (src, convert_samples); |
79 |
|
|
80 |
rpos = (rpos + convert_samples) % hw->samples; |
rpos = (rpos + convert_samples) % hw->samples; |
81 |
samples -= convert_samples; |
samples -= convert_samples; |
82 |
wav->total_samples += convert_samples; |
wav->total_samples += convert_samples; |
83 |
} |
} |
84 |
|
|
|
pcm_hw_dec_live (hw, decr); |
|
85 |
hw->rpos = rpos; |
hw->rpos = rpos; |
86 |
|
return decr; |
87 |
} |
} |
88 |
|
|
89 |
static int wav_hw_write (SWVoice *sw, void *buf, int len) |
static int wav_write_out (SWVoiceOut *sw, void *buf, int len) |
90 |
{ |
{ |
91 |
return pcm_hw_write (sw, buf, len); |
return audio_pcm_sw_write (sw, buf, len); |
92 |
} |
} |
93 |
|
|
94 |
/* VICE code: Store number as little endian. */ |
/* VICE code: Store number as little endian. */ |
101 |
} |
} |
102 |
} |
} |
103 |
|
|
104 |
static int wav_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt) |
static int wav_init_out (HWVoiceOut *hw, int freq, int nchannels, audfmt_e fmt) |
105 |
{ |
{ |
106 |
WAVVoice *wav = (WAVVoice *) hw; |
WAVVoiceOut *wav = (WAVVoiceOut *) hw; |
107 |
int bits16 = 0, stereo = audio_state.fixed_channels == 2; |
int bits16; |
108 |
uint8_t hdr[] = { |
uint8_t hdr[] = { |
109 |
0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56, |
0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56, |
110 |
0x45, 0x66, 0x6d, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, |
0x45, 0x66, 0x6d, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, |
112 |
0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00 |
0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00 |
113 |
}; |
}; |
114 |
|
|
115 |
switch (audio_state.fixed_fmt) { |
freq = audio_state.fixed_freq_out; |
116 |
|
fmt = audio_state.fixed_fmt_out; |
117 |
|
nchannels = audio_state.fixed_channels_out; |
118 |
|
|
119 |
|
switch (fmt) { |
120 |
case AUD_FMT_S8: |
case AUD_FMT_S8: |
121 |
case AUD_FMT_U8: |
case AUD_FMT_U8: |
122 |
|
bits16 = 0; |
123 |
break; |
break; |
124 |
|
|
125 |
case AUD_FMT_S16: |
case AUD_FMT_S16: |
126 |
case AUD_FMT_U16: |
case AUD_FMT_U16: |
127 |
bits16 = 1; |
bits16 = 1; |
128 |
break; |
break; |
129 |
|
|
130 |
|
default: |
131 |
|
dolog ("Internal logic error bad format %d\n", fmt); |
132 |
|
return -1; |
133 |
} |
} |
134 |
|
|
135 |
hdr[34] = bits16 ? 0x10 : 0x08; |
hdr[34] = bits16 ? 0x10 : 0x08; |
136 |
hw->freq = 44100; |
audio_pcm_init_info ( |
137 |
hw->nchannels = stereo ? 2 : 1; |
&hw->info, |
138 |
hw->fmt = bits16 ? AUD_FMT_S16 : AUD_FMT_U8; |
freq, |
139 |
|
nchannels, |
140 |
|
bits16 ? AUD_FMT_S16 : AUD_FMT_U8, |
141 |
|
audio_need_to_swap_endian (0) |
142 |
|
); |
143 |
hw->bufsize = 4096; |
hw->bufsize = 4096; |
144 |
wav->pcm_buf = qemu_mallocz (hw->bufsize); |
wav->pcm_buf = qemu_mallocz (hw->bufsize); |
145 |
if (!wav->pcm_buf) |
if (!wav->pcm_buf) { |
146 |
|
dolog ("Can not initialize WAV buffer of %d bytes\n", |
147 |
|
hw->bufsize); |
148 |
return -1; |
return -1; |
149 |
|
} |
150 |
|
|
151 |
le_store (hdr + 22, hw->nchannels, 2); |
le_store (hdr + 22, hw->info.nchannels, 2); |
152 |
le_store (hdr + 24, hw->freq, 4); |
le_store (hdr + 24, hw->info.freq, 4); |
153 |
le_store (hdr + 28, hw->freq << (bits16 + stereo), 4); |
le_store (hdr + 28, hw->info.freq << (bits16 + (nchannels == 2)), 4); |
154 |
le_store (hdr + 32, 1 << (bits16 + stereo), 2); |
le_store (hdr + 32, 1 << (bits16 + (nchannels == 2)), 2); |
155 |
|
|
156 |
wav->f = fopen (conf.wav_path, "wb"); |
wav->f = fopen (conf.wav_path, "wb"); |
157 |
if (!wav->f) { |
if (!wav->f) { |
158 |
dolog ("failed to open wave file `%s'\nReason: %s\n", |
dolog ("Failed to open wave file `%s'\nReason: %s\n", |
159 |
conf.wav_path, strerror (errno)); |
conf.wav_path, strerror (errno)); |
160 |
qemu_free (wav->pcm_buf); |
qemu_free (wav->pcm_buf); |
161 |
wav->pcm_buf = NULL; |
wav->pcm_buf = NULL; |
166 |
return 0; |
return 0; |
167 |
} |
} |
168 |
|
|
169 |
static void wav_hw_fini (HWVoice *hw) |
static void wav_fini_out (HWVoiceOut *hw) |
170 |
{ |
{ |
171 |
WAVVoice *wav = (WAVVoice *) hw; |
WAVVoiceOut *wav = (WAVVoiceOut *) hw; |
172 |
int stereo = hw->nchannels == 2; |
int stereo = hw->info.nchannels == 2; |
173 |
uint8_t rlen[4]; |
uint8_t rlen[4]; |
174 |
uint8_t dlen[4]; |
uint8_t dlen[4]; |
175 |
uint32_t rifflen = (wav->total_samples << stereo) + 36; |
uint32_t rifflen = (wav->total_samples << stereo) + 36; |
176 |
uint32_t datalen = wav->total_samples << stereo; |
uint32_t datalen = wav->total_samples << stereo; |
177 |
|
|
178 |
if (!wav->f || !hw->active) |
if (!wav->f || !hw->active) { |
179 |
return; |
return; |
180 |
|
} |
181 |
|
|
182 |
le_store (rlen, rifflen, 4); |
le_store (rlen, rifflen, 4); |
183 |
le_store (dlen, datalen, 4); |
le_store (dlen, datalen, 4); |
195 |
wav->pcm_buf = NULL; |
wav->pcm_buf = NULL; |
196 |
} |
} |
197 |
|
|
198 |
static int wav_hw_ctl (HWVoice *hw, int cmd, ...) |
static int wav_ctl_out (HWVoiceOut *hw, int cmd, ...) |
199 |
{ |
{ |
200 |
(void) hw; |
(void) hw; |
201 |
(void) cmd; |
(void) cmd; |
209 |
|
|
210 |
static void wav_audio_fini (void *opaque) |
static void wav_audio_fini (void *opaque) |
211 |
{ |
{ |
212 |
|
(void) opaque; |
213 |
ldebug ("wav_fini"); |
ldebug ("wav_fini"); |
214 |
} |
} |
215 |
|
|
216 |
struct pcm_ops wav_pcm_ops = { |
struct audio_option wav_options[] = { |
217 |
wav_hw_init, |
{"PATH", AUD_OPT_STR, &conf.wav_path, |
218 |
wav_hw_fini, |
"Path to wave file", NULL, 0}, |
219 |
wav_hw_run, |
{NULL, 0, NULL, NULL, NULL, 0} |
220 |
wav_hw_write, |
}; |
221 |
wav_hw_ctl |
|
222 |
|
struct audio_pcm_ops wav_pcm_ops = { |
223 |
|
wav_init_out, |
224 |
|
wav_fini_out, |
225 |
|
wav_run_out, |
226 |
|
wav_write_out, |
227 |
|
wav_ctl_out, |
228 |
|
|
229 |
|
NULL, |
230 |
|
NULL, |
231 |
|
NULL, |
232 |
|
NULL, |
233 |
|
NULL |
234 |
}; |
}; |
235 |
|
|
236 |
struct audio_output_driver wav_output_driver = { |
struct audio_driver wav_audio_driver = { |
237 |
"wav", |
INIT_FIELD (name = ) "wav", |
238 |
wav_audio_init, |
INIT_FIELD (descr = ) |
239 |
wav_audio_fini, |
"WAV renderer http://wikipedia.org/wiki/WAV", |
240 |
&wav_pcm_ops, |
INIT_FIELD (options = ) wav_options, |
241 |
1, |
INIT_FIELD (init = ) wav_audio_init, |
242 |
1, |
INIT_FIELD (fini = ) wav_audio_fini, |
243 |
sizeof (WAVVoice) |
INIT_FIELD (pcm_ops = ) &wav_pcm_ops, |
244 |
|
INIT_FIELD (can_be_default = ) 0, |
245 |
|
INIT_FIELD (max_voices_out = ) 1, |
246 |
|
INIT_FIELD (max_voices_in = ) 0, |
247 |
|
INIT_FIELD (voice_size_out = ) sizeof (WAVVoiceOut), |
248 |
|
INIT_FIELD (voice_size_in = ) 0 |
249 |
}; |
}; |