root / audio / fmodaudio.c @ 197bc219
History | View | Annotate | Download (17.4 kB)
1 | 85571bc7 | bellard | /*
|
---|---|---|---|
2 | 1d14ffa9 | bellard | * QEMU FMOD audio driver
|
3 | 1d14ffa9 | bellard | *
|
4 | 1d14ffa9 | bellard | * Copyright (c) 2004-2005 Vassili Karpov (malc)
|
5 | 1d14ffa9 | bellard | *
|
6 | 85571bc7 | bellard | * Permission is hereby granted, free of charge, to any person obtaining a copy
|
7 | 85571bc7 | bellard | * of this software and associated documentation files (the "Software"), to deal
|
8 | 85571bc7 | bellard | * in the Software without restriction, including without limitation the rights
|
9 | 85571bc7 | bellard | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10 | 85571bc7 | bellard | * copies of the Software, and to permit persons to whom the Software is
|
11 | 85571bc7 | bellard | * furnished to do so, subject to the following conditions:
|
12 | 85571bc7 | bellard | *
|
13 | 85571bc7 | bellard | * The above copyright notice and this permission notice shall be included in
|
14 | 85571bc7 | bellard | * all copies or substantial portions of the Software.
|
15 | 85571bc7 | bellard | *
|
16 | 85571bc7 | bellard | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17 | 85571bc7 | bellard | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
18 | 85571bc7 | bellard | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
19 | 85571bc7 | bellard | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
20 | 85571bc7 | bellard | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
21 | 85571bc7 | bellard | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
22 | 85571bc7 | bellard | * THE SOFTWARE.
|
23 | 85571bc7 | bellard | */
|
24 | 85571bc7 | bellard | #include <fmod.h> |
25 | 85571bc7 | bellard | #include <fmod_errors.h> |
26 | 749bc4bf | pbrook | #include "qemu-common.h" |
27 | 749bc4bf | pbrook | #include "audio.h" |
28 | 85571bc7 | bellard | |
29 | 1d14ffa9 | bellard | #define AUDIO_CAP "fmod" |
30 | 1d14ffa9 | bellard | #include "audio_int.h" |
31 | fb065187 | bellard | |
32 | 1d14ffa9 | bellard | typedef struct FMODVoiceOut { |
33 | 1d14ffa9 | bellard | HWVoiceOut hw; |
34 | fb065187 | bellard | unsigned int old_pos; |
35 | fb065187 | bellard | FSOUND_SAMPLE *fmod_sample; |
36 | fb065187 | bellard | int channel;
|
37 | 1d14ffa9 | bellard | } FMODVoiceOut; |
38 | 85571bc7 | bellard | |
39 | 1d14ffa9 | bellard | typedef struct FMODVoiceIn { |
40 | 1d14ffa9 | bellard | HWVoiceIn hw; |
41 | 1d14ffa9 | bellard | FSOUND_SAMPLE *fmod_sample; |
42 | 1d14ffa9 | bellard | } FMODVoiceIn; |
43 | 85571bc7 | bellard | |
44 | 85571bc7 | bellard | static struct { |
45 | 1d14ffa9 | bellard | const char *drvname; |
46 | 85571bc7 | bellard | int nb_samples;
|
47 | 85571bc7 | bellard | int freq;
|
48 | 85571bc7 | bellard | int nb_channels;
|
49 | 85571bc7 | bellard | int bufsize;
|
50 | 85571bc7 | bellard | int threshold;
|
51 | 1d14ffa9 | bellard | int broken_adc;
|
52 | 85571bc7 | bellard | } conf = { |
53 | 1a40d5e2 | Juan Quintela | .nb_samples = 2048 * 2, |
54 | 1a40d5e2 | Juan Quintela | .freq = 44100,
|
55 | 1a40d5e2 | Juan Quintela | .nb_channels = 2,
|
56 | 85571bc7 | bellard | }; |
57 | 85571bc7 | bellard | |
58 | 1d14ffa9 | bellard | static void GCC_FMT_ATTR (1, 2) fmod_logerr (const char *fmt, ...) |
59 | 1d14ffa9 | bellard | { |
60 | 1d14ffa9 | bellard | va_list ap; |
61 | 1d14ffa9 | bellard | |
62 | 1d14ffa9 | bellard | va_start (ap, fmt); |
63 | 1d14ffa9 | bellard | AUD_vlog (AUDIO_CAP, fmt, ap); |
64 | 1d14ffa9 | bellard | va_end (ap); |
65 | 1d14ffa9 | bellard | |
66 | 1d14ffa9 | bellard | AUD_log (AUDIO_CAP, "Reason: %s\n",
|
67 | 1d14ffa9 | bellard | FMOD_ErrorString (FSOUND_GetError ())); |
68 | 1d14ffa9 | bellard | } |
69 | 1d14ffa9 | bellard | |
70 | 1d14ffa9 | bellard | static void GCC_FMT_ATTR (2, 3) fmod_logerr2 ( |
71 | 1d14ffa9 | bellard | const char *typ, |
72 | 1d14ffa9 | bellard | const char *fmt, |
73 | 1d14ffa9 | bellard | ... |
74 | 1d14ffa9 | bellard | ) |
75 | 1d14ffa9 | bellard | { |
76 | 1d14ffa9 | bellard | va_list ap; |
77 | 1d14ffa9 | bellard | |
78 | c0fe3827 | bellard | AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
|
79 | 1d14ffa9 | bellard | |
80 | 1d14ffa9 | bellard | va_start (ap, fmt); |
81 | 1d14ffa9 | bellard | AUD_vlog (AUDIO_CAP, fmt, ap); |
82 | 1d14ffa9 | bellard | va_end (ap); |
83 | 1d14ffa9 | bellard | |
84 | 1d14ffa9 | bellard | AUD_log (AUDIO_CAP, "Reason: %s\n",
|
85 | 1d14ffa9 | bellard | FMOD_ErrorString (FSOUND_GetError ())); |
86 | 1d14ffa9 | bellard | } |
87 | 85571bc7 | bellard | |
88 | 1d14ffa9 | bellard | static int fmod_write (SWVoiceOut *sw, void *buf, int len) |
89 | 85571bc7 | bellard | { |
90 | 1d14ffa9 | bellard | return audio_pcm_sw_write (sw, buf, len);
|
91 | 85571bc7 | bellard | } |
92 | 85571bc7 | bellard | |
93 | 1d14ffa9 | bellard | static void fmod_clear_sample (FMODVoiceOut *fmd) |
94 | 85571bc7 | bellard | { |
95 | 1d14ffa9 | bellard | HWVoiceOut *hw = &fmd->hw; |
96 | 85571bc7 | bellard | int status;
|
97 | 85571bc7 | bellard | void *p1 = 0, *p2 = 0; |
98 | 85571bc7 | bellard | unsigned int len1 = 0, len2 = 0; |
99 | 85571bc7 | bellard | |
100 | 85571bc7 | bellard | status = FSOUND_Sample_Lock ( |
101 | 85571bc7 | bellard | fmd->fmod_sample, |
102 | 85571bc7 | bellard | 0,
|
103 | 1d14ffa9 | bellard | hw->samples << hw->info.shift, |
104 | 85571bc7 | bellard | &p1, |
105 | 85571bc7 | bellard | &p2, |
106 | 85571bc7 | bellard | &len1, |
107 | 85571bc7 | bellard | &len2 |
108 | 85571bc7 | bellard | ); |
109 | 85571bc7 | bellard | |
110 | 85571bc7 | bellard | if (!status) {
|
111 | 1d14ffa9 | bellard | fmod_logerr ("Failed to lock sample\n");
|
112 | 85571bc7 | bellard | return;
|
113 | 85571bc7 | bellard | } |
114 | 85571bc7 | bellard | |
115 | 1d14ffa9 | bellard | if ((len1 & hw->info.align) || (len2 & hw->info.align)) {
|
116 | 1d14ffa9 | bellard | dolog ("Lock returned misaligned length %d, %d, alignment %d\n",
|
117 | 1d14ffa9 | bellard | len1, len2, hw->info.align + 1);
|
118 | 85571bc7 | bellard | goto fail;
|
119 | 85571bc7 | bellard | } |
120 | 85571bc7 | bellard | |
121 | 1d14ffa9 | bellard | if ((len1 + len2) - (hw->samples << hw->info.shift)) {
|
122 | 1d14ffa9 | bellard | dolog ("Lock returned incomplete length %d, %d\n",
|
123 | 1d14ffa9 | bellard | len1 + len2, hw->samples << hw->info.shift); |
124 | 85571bc7 | bellard | goto fail;
|
125 | 85571bc7 | bellard | } |
126 | 1d14ffa9 | bellard | |
127 | 1d14ffa9 | bellard | audio_pcm_info_clear_buf (&hw->info, p1, hw->samples); |
128 | 85571bc7 | bellard | |
129 | 85571bc7 | bellard | fail:
|
130 | 85571bc7 | bellard | status = FSOUND_Sample_Unlock (fmd->fmod_sample, p1, p2, len1, len2); |
131 | 85571bc7 | bellard | if (!status) {
|
132 | 1d14ffa9 | bellard | fmod_logerr ("Failed to unlock sample\n");
|
133 | 85571bc7 | bellard | } |
134 | 85571bc7 | bellard | } |
135 | 85571bc7 | bellard | |
136 | 1d14ffa9 | bellard | static void fmod_write_sample (HWVoiceOut *hw, uint8_t *dst, int dst_len) |
137 | 85571bc7 | bellard | { |
138 | 1d14ffa9 | bellard | int src_len1 = dst_len;
|
139 | 1d14ffa9 | bellard | int src_len2 = 0; |
140 | 1d14ffa9 | bellard | int pos = hw->rpos + dst_len;
|
141 | 1ea879e5 | malc | struct st_sample *src1 = hw->mix_buf + hw->rpos;
|
142 | 1ea879e5 | malc | struct st_sample *src2 = NULL; |
143 | 1d14ffa9 | bellard | |
144 | 1d14ffa9 | bellard | if (pos > hw->samples) {
|
145 | 1d14ffa9 | bellard | src_len1 = hw->samples - hw->rpos; |
146 | 1d14ffa9 | bellard | src2 = hw->mix_buf; |
147 | 85571bc7 | bellard | src_len2 = dst_len - src_len1; |
148 | 85571bc7 | bellard | pos = src_len2; |
149 | 85571bc7 | bellard | } |
150 | 85571bc7 | bellard | |
151 | 85571bc7 | bellard | if (src_len1) {
|
152 | 85571bc7 | bellard | hw->clip (dst, src1, src_len1); |
153 | 85571bc7 | bellard | } |
154 | 85571bc7 | bellard | |
155 | 85571bc7 | bellard | if (src_len2) {
|
156 | 1d14ffa9 | bellard | dst = advance (dst, src_len1 << hw->info.shift); |
157 | 85571bc7 | bellard | hw->clip (dst, src2, src_len2); |
158 | 85571bc7 | bellard | } |
159 | 1d14ffa9 | bellard | |
160 | 1d14ffa9 | bellard | hw->rpos = pos % hw->samples; |
161 | 85571bc7 | bellard | } |
162 | 85571bc7 | bellard | |
163 | 1d14ffa9 | bellard | static int fmod_unlock_sample (FSOUND_SAMPLE *sample, void *p1, void *p2, |
164 | 85571bc7 | bellard | unsigned int blen1, unsigned int blen2) |
165 | 85571bc7 | bellard | { |
166 | 1d14ffa9 | bellard | int status = FSOUND_Sample_Unlock (sample, p1, p2, blen1, blen2);
|
167 | 85571bc7 | bellard | if (!status) {
|
168 | 1d14ffa9 | bellard | fmod_logerr ("Failed to unlock sample\n");
|
169 | 85571bc7 | bellard | return -1; |
170 | 85571bc7 | bellard | } |
171 | 85571bc7 | bellard | return 0; |
172 | 85571bc7 | bellard | } |
173 | 85571bc7 | bellard | |
174 | 1d14ffa9 | bellard | static int fmod_lock_sample ( |
175 | 1d14ffa9 | bellard | FSOUND_SAMPLE *sample, |
176 | 1d14ffa9 | bellard | struct audio_pcm_info *info,
|
177 | 1d14ffa9 | bellard | int pos,
|
178 | 1d14ffa9 | bellard | int len,
|
179 | 1d14ffa9 | bellard | void **p1,
|
180 | 1d14ffa9 | bellard | void **p2,
|
181 | 1d14ffa9 | bellard | unsigned int *blen1, |
182 | 1d14ffa9 | bellard | unsigned int *blen2 |
183 | 1d14ffa9 | bellard | ) |
184 | 85571bc7 | bellard | { |
185 | 85571bc7 | bellard | int status;
|
186 | 85571bc7 | bellard | |
187 | 85571bc7 | bellard | status = FSOUND_Sample_Lock ( |
188 | 1d14ffa9 | bellard | sample, |
189 | 1d14ffa9 | bellard | pos << info->shift, |
190 | 1d14ffa9 | bellard | len << info->shift, |
191 | 85571bc7 | bellard | p1, |
192 | 85571bc7 | bellard | p2, |
193 | 85571bc7 | bellard | blen1, |
194 | 85571bc7 | bellard | blen2 |
195 | 85571bc7 | bellard | ); |
196 | 85571bc7 | bellard | |
197 | 85571bc7 | bellard | if (!status) {
|
198 | 1d14ffa9 | bellard | fmod_logerr ("Failed to lock sample\n");
|
199 | 85571bc7 | bellard | return -1; |
200 | 85571bc7 | bellard | } |
201 | 85571bc7 | bellard | |
202 | 1d14ffa9 | bellard | if ((*blen1 & info->align) || (*blen2 & info->align)) {
|
203 | 1d14ffa9 | bellard | dolog ("Lock returned misaligned length %d, %d, alignment %d\n",
|
204 | 1d14ffa9 | bellard | *blen1, *blen2, info->align + 1);
|
205 | 1d14ffa9 | bellard | |
206 | 1d14ffa9 | bellard | fmod_unlock_sample (sample, *p1, *p2, *blen1, *blen2); |
207 | 1d14ffa9 | bellard | |
208 | 1d14ffa9 | bellard | *p1 = NULL - 1; |
209 | 1d14ffa9 | bellard | *p2 = NULL - 1; |
210 | 1d14ffa9 | bellard | *blen1 = ~0U;
|
211 | 1d14ffa9 | bellard | *blen2 = ~0U;
|
212 | 85571bc7 | bellard | return -1; |
213 | 85571bc7 | bellard | } |
214 | 1d14ffa9 | bellard | |
215 | 1d14ffa9 | bellard | if (!*p1 && *blen1) {
|
216 | 1d14ffa9 | bellard | dolog ("warning: !p1 && blen1=%d\n", *blen1);
|
217 | 1d14ffa9 | bellard | *blen1 = 0;
|
218 | 1d14ffa9 | bellard | } |
219 | 1d14ffa9 | bellard | |
220 | 1d14ffa9 | bellard | if (!p2 && *blen2) {
|
221 | 1d14ffa9 | bellard | dolog ("warning: !p2 && blen2=%d\n", *blen2);
|
222 | 1d14ffa9 | bellard | *blen2 = 0;
|
223 | 1d14ffa9 | bellard | } |
224 | 1d14ffa9 | bellard | |
225 | 85571bc7 | bellard | return 0; |
226 | 85571bc7 | bellard | } |
227 | 85571bc7 | bellard | |
228 | 1d14ffa9 | bellard | static int fmod_run_out (HWVoiceOut *hw) |
229 | 85571bc7 | bellard | { |
230 | 1d14ffa9 | bellard | FMODVoiceOut *fmd = (FMODVoiceOut *) hw; |
231 | 1d14ffa9 | bellard | int live, decr;
|
232 | 85571bc7 | bellard | void *p1 = 0, *p2 = 0; |
233 | 85571bc7 | bellard | unsigned int blen1 = 0, blen2 = 0; |
234 | 85571bc7 | bellard | unsigned int len1 = 0, len2 = 0; |
235 | 1d14ffa9 | bellard | int nb_live;
|
236 | 85571bc7 | bellard | |
237 | 1d14ffa9 | bellard | live = audio_pcm_hw_get_live_out2 (hw, &nb_live); |
238 | 1d14ffa9 | bellard | if (!live) {
|
239 | 1d14ffa9 | bellard | return 0; |
240 | 85571bc7 | bellard | } |
241 | 85571bc7 | bellard | |
242 | 85571bc7 | bellard | if (!hw->pending_disable
|
243 | 1d14ffa9 | bellard | && nb_live |
244 | 1d14ffa9 | bellard | && (conf.threshold && live <= conf.threshold)) { |
245 | 1d14ffa9 | bellard | ldebug ("live=%d nb_live=%d\n", live, nb_live);
|
246 | 1d14ffa9 | bellard | return 0; |
247 | 85571bc7 | bellard | } |
248 | 85571bc7 | bellard | |
249 | 85571bc7 | bellard | decr = live; |
250 | 85571bc7 | bellard | |
251 | 85571bc7 | bellard | if (fmd->channel >= 0) { |
252 | 1d14ffa9 | bellard | int len = decr;
|
253 | 1d14ffa9 | bellard | int old_pos = fmd->old_pos;
|
254 | 1d14ffa9 | bellard | int ppos = FSOUND_GetCurrentPosition (fmd->channel);
|
255 | 85571bc7 | bellard | |
256 | 1d14ffa9 | bellard | if (ppos == old_pos || !ppos) {
|
257 | 1d14ffa9 | bellard | return 0; |
258 | 85571bc7 | bellard | } |
259 | 1d14ffa9 | bellard | |
260 | 1d14ffa9 | bellard | if ((old_pos < ppos) && ((old_pos + len) > ppos)) {
|
261 | 1d14ffa9 | bellard | len = ppos - old_pos; |
262 | 1d14ffa9 | bellard | } |
263 | 1d14ffa9 | bellard | else {
|
264 | 1d14ffa9 | bellard | if ((old_pos > ppos) && ((old_pos + len) > (ppos + hw->samples))) {
|
265 | 1d14ffa9 | bellard | len = hw->samples - old_pos + ppos; |
266 | 1d14ffa9 | bellard | } |
267 | 1d14ffa9 | bellard | } |
268 | 1d14ffa9 | bellard | decr = len; |
269 | 1d14ffa9 | bellard | |
270 | 1d14ffa9 | bellard | if (audio_bug (AUDIO_FUNC, decr < 0)) { |
271 | 1d14ffa9 | bellard | dolog ("decr=%d live=%d ppos=%d old_pos=%d len=%d\n",
|
272 | 1d14ffa9 | bellard | decr, live, ppos, old_pos, len); |
273 | 1d14ffa9 | bellard | return 0; |
274 | 85571bc7 | bellard | } |
275 | 85571bc7 | bellard | } |
276 | 85571bc7 | bellard | |
277 | 1d14ffa9 | bellard | |
278 | 1d14ffa9 | bellard | if (!decr) {
|
279 | 1d14ffa9 | bellard | return 0; |
280 | 85571bc7 | bellard | } |
281 | 85571bc7 | bellard | |
282 | 1d14ffa9 | bellard | if (fmod_lock_sample (fmd->fmod_sample, &fmd->hw.info,
|
283 | 1d14ffa9 | bellard | fmd->old_pos, decr, |
284 | 1d14ffa9 | bellard | &p1, &p2, |
285 | 1d14ffa9 | bellard | &blen1, &blen2)) { |
286 | 1d14ffa9 | bellard | return 0; |
287 | 85571bc7 | bellard | } |
288 | 85571bc7 | bellard | |
289 | 1d14ffa9 | bellard | len1 = blen1 >> hw->info.shift; |
290 | 1d14ffa9 | bellard | len2 = blen2 >> hw->info.shift; |
291 | 85571bc7 | bellard | ldebug ("%p %p %d %d %d %d\n", p1, p2, len1, len2, blen1, blen2);
|
292 | 85571bc7 | bellard | decr = len1 + len2; |
293 | 85571bc7 | bellard | |
294 | 1d14ffa9 | bellard | if (p1 && len1) {
|
295 | 1d14ffa9 | bellard | fmod_write_sample (hw, p1, len1); |
296 | 85571bc7 | bellard | } |
297 | 85571bc7 | bellard | |
298 | 1d14ffa9 | bellard | if (p2 && len2) {
|
299 | 1d14ffa9 | bellard | fmod_write_sample (hw, p2, len2); |
300 | 85571bc7 | bellard | } |
301 | 85571bc7 | bellard | |
302 | 1d14ffa9 | bellard | fmod_unlock_sample (fmd->fmod_sample, p1, p2, blen1, blen2); |
303 | 85571bc7 | bellard | |
304 | 85571bc7 | bellard | fmd->old_pos = (fmd->old_pos + decr) % hw->samples; |
305 | 1d14ffa9 | bellard | return decr;
|
306 | 85571bc7 | bellard | } |
307 | 85571bc7 | bellard | |
308 | 1d14ffa9 | bellard | static int aud_to_fmodfmt (audfmt_e fmt, int stereo) |
309 | 85571bc7 | bellard | { |
310 | 85571bc7 | bellard | int mode = FSOUND_LOOP_NORMAL;
|
311 | 85571bc7 | bellard | |
312 | 85571bc7 | bellard | switch (fmt) {
|
313 | 85571bc7 | bellard | case AUD_FMT_S8:
|
314 | 85571bc7 | bellard | mode |= FSOUND_SIGNED | FSOUND_8BITS; |
315 | 85571bc7 | bellard | break;
|
316 | 85571bc7 | bellard | |
317 | 85571bc7 | bellard | case AUD_FMT_U8:
|
318 | 85571bc7 | bellard | mode |= FSOUND_UNSIGNED | FSOUND_8BITS; |
319 | 85571bc7 | bellard | break;
|
320 | 85571bc7 | bellard | |
321 | 85571bc7 | bellard | case AUD_FMT_S16:
|
322 | 85571bc7 | bellard | mode |= FSOUND_SIGNED | FSOUND_16BITS; |
323 | 85571bc7 | bellard | break;
|
324 | 85571bc7 | bellard | |
325 | 85571bc7 | bellard | case AUD_FMT_U16:
|
326 | 85571bc7 | bellard | mode |= FSOUND_UNSIGNED | FSOUND_16BITS; |
327 | 85571bc7 | bellard | break;
|
328 | 85571bc7 | bellard | |
329 | 85571bc7 | bellard | default:
|
330 | 1d14ffa9 | bellard | dolog ("Internal logic error: Bad audio format %d\n", fmt);
|
331 | 1d14ffa9 | bellard | #ifdef DEBUG_FMOD
|
332 | 1d14ffa9 | bellard | abort (); |
333 | 1d14ffa9 | bellard | #endif
|
334 | 1d14ffa9 | bellard | mode |= FSOUND_8BITS; |
335 | 85571bc7 | bellard | } |
336 | 85571bc7 | bellard | mode |= stereo ? FSOUND_STEREO : FSOUND_MONO; |
337 | 85571bc7 | bellard | return mode;
|
338 | 85571bc7 | bellard | } |
339 | 85571bc7 | bellard | |
340 | 1d14ffa9 | bellard | static void fmod_fini_out (HWVoiceOut *hw) |
341 | 85571bc7 | bellard | { |
342 | 1d14ffa9 | bellard | FMODVoiceOut *fmd = (FMODVoiceOut *) hw; |
343 | 85571bc7 | bellard | |
344 | 85571bc7 | bellard | if (fmd->fmod_sample) {
|
345 | 85571bc7 | bellard | FSOUND_Sample_Free (fmd->fmod_sample); |
346 | 85571bc7 | bellard | fmd->fmod_sample = 0;
|
347 | 85571bc7 | bellard | |
348 | 85571bc7 | bellard | if (fmd->channel >= 0) { |
349 | 85571bc7 | bellard | FSOUND_StopSound (fmd->channel); |
350 | 85571bc7 | bellard | } |
351 | 85571bc7 | bellard | } |
352 | 85571bc7 | bellard | } |
353 | 85571bc7 | bellard | |
354 | 1ea879e5 | malc | static int fmod_init_out (HWVoiceOut *hw, struct audsettings *as) |
355 | 85571bc7 | bellard | { |
356 | 85571bc7 | bellard | int bits16, mode, channel;
|
357 | 1d14ffa9 | bellard | FMODVoiceOut *fmd = (FMODVoiceOut *) hw; |
358 | 1ea879e5 | malc | struct audsettings obt_as = *as;
|
359 | 85571bc7 | bellard | |
360 | c0fe3827 | bellard | mode = aud_to_fmodfmt (as->fmt, as->nchannels == 2 ? 1 : 0); |
361 | 85571bc7 | bellard | fmd->fmod_sample = FSOUND_Sample_Alloc ( |
362 | 85571bc7 | bellard | FSOUND_FREE, /* index */
|
363 | 85571bc7 | bellard | conf.nb_samples, /* length */
|
364 | 85571bc7 | bellard | mode, /* mode */
|
365 | c0fe3827 | bellard | as->freq, /* freq */
|
366 | 85571bc7 | bellard | 255, /* volume */ |
367 | 85571bc7 | bellard | 128, /* pan */ |
368 | 85571bc7 | bellard | 255 /* priority */ |
369 | 85571bc7 | bellard | ); |
370 | 85571bc7 | bellard | |
371 | 85571bc7 | bellard | if (!fmd->fmod_sample) {
|
372 | 1d14ffa9 | bellard | fmod_logerr2 ("DAC", "Failed to allocate FMOD sample\n"); |
373 | 85571bc7 | bellard | return -1; |
374 | 85571bc7 | bellard | } |
375 | 85571bc7 | bellard | |
376 | 85571bc7 | bellard | channel = FSOUND_PlaySoundEx (FSOUND_FREE, fmd->fmod_sample, 0, 1); |
377 | 85571bc7 | bellard | if (channel < 0) { |
378 | 1d14ffa9 | bellard | fmod_logerr2 ("DAC", "Failed to start playing sound\n"); |
379 | 85571bc7 | bellard | FSOUND_Sample_Free (fmd->fmod_sample); |
380 | 85571bc7 | bellard | return -1; |
381 | 85571bc7 | bellard | } |
382 | 85571bc7 | bellard | fmd->channel = channel; |
383 | 85571bc7 | bellard | |
384 | 1d14ffa9 | bellard | /* FMOD always operates on little endian frames? */
|
385 | d929eba5 | bellard | obt_as.endianness = 0;
|
386 | d929eba5 | bellard | audio_pcm_init_info (&hw->info, &obt_as); |
387 | 1d14ffa9 | bellard | bits16 = (mode & FSOUND_16BITS) != 0;
|
388 | c0fe3827 | bellard | hw->samples = conf.nb_samples; |
389 | 85571bc7 | bellard | return 0; |
390 | 85571bc7 | bellard | } |
391 | 85571bc7 | bellard | |
392 | 1d14ffa9 | bellard | static int fmod_ctl_out (HWVoiceOut *hw, int cmd, ...) |
393 | 85571bc7 | bellard | { |
394 | 85571bc7 | bellard | int status;
|
395 | 1d14ffa9 | bellard | FMODVoiceOut *fmd = (FMODVoiceOut *) hw; |
396 | 85571bc7 | bellard | |
397 | 85571bc7 | bellard | switch (cmd) {
|
398 | 85571bc7 | bellard | case VOICE_ENABLE:
|
399 | 85571bc7 | bellard | fmod_clear_sample (fmd); |
400 | 85571bc7 | bellard | status = FSOUND_SetPaused (fmd->channel, 0);
|
401 | 85571bc7 | bellard | if (!status) {
|
402 | 1d14ffa9 | bellard | fmod_logerr ("Failed to resume channel %d\n", fmd->channel);
|
403 | 85571bc7 | bellard | } |
404 | 85571bc7 | bellard | break;
|
405 | 85571bc7 | bellard | |
406 | 85571bc7 | bellard | case VOICE_DISABLE:
|
407 | 85571bc7 | bellard | status = FSOUND_SetPaused (fmd->channel, 1);
|
408 | 85571bc7 | bellard | if (!status) {
|
409 | 1d14ffa9 | bellard | fmod_logerr ("Failed to pause channel %d\n", fmd->channel);
|
410 | 85571bc7 | bellard | } |
411 | 85571bc7 | bellard | break;
|
412 | 85571bc7 | bellard | } |
413 | 85571bc7 | bellard | return 0; |
414 | 85571bc7 | bellard | } |
415 | 85571bc7 | bellard | |
416 | 1ea879e5 | malc | static int fmod_init_in (HWVoiceIn *hw, struct audsettings *as) |
417 | 1d14ffa9 | bellard | { |
418 | 1d14ffa9 | bellard | int bits16, mode;
|
419 | 1d14ffa9 | bellard | FMODVoiceIn *fmd = (FMODVoiceIn *) hw; |
420 | 1ea879e5 | malc | struct audsettings obt_as = *as;
|
421 | 1d14ffa9 | bellard | |
422 | 1d14ffa9 | bellard | if (conf.broken_adc) {
|
423 | 1d14ffa9 | bellard | return -1; |
424 | 1d14ffa9 | bellard | } |
425 | 1d14ffa9 | bellard | |
426 | c0fe3827 | bellard | mode = aud_to_fmodfmt (as->fmt, as->nchannels == 2 ? 1 : 0); |
427 | 1d14ffa9 | bellard | fmd->fmod_sample = FSOUND_Sample_Alloc ( |
428 | 1d14ffa9 | bellard | FSOUND_FREE, /* index */
|
429 | 1d14ffa9 | bellard | conf.nb_samples, /* length */
|
430 | 1d14ffa9 | bellard | mode, /* mode */
|
431 | c0fe3827 | bellard | as->freq, /* freq */
|
432 | 1d14ffa9 | bellard | 255, /* volume */ |
433 | 1d14ffa9 | bellard | 128, /* pan */ |
434 | 1d14ffa9 | bellard | 255 /* priority */ |
435 | 1d14ffa9 | bellard | ); |
436 | 1d14ffa9 | bellard | |
437 | 1d14ffa9 | bellard | if (!fmd->fmod_sample) {
|
438 | 1d14ffa9 | bellard | fmod_logerr2 ("ADC", "Failed to allocate FMOD sample\n"); |
439 | 1d14ffa9 | bellard | return -1; |
440 | 1d14ffa9 | bellard | } |
441 | 1d14ffa9 | bellard | |
442 | 1d14ffa9 | bellard | /* FMOD always operates on little endian frames? */
|
443 | d929eba5 | bellard | obt_as.endianness = 0;
|
444 | d929eba5 | bellard | audio_pcm_init_info (&hw->info, &obt_as); |
445 | 1d14ffa9 | bellard | bits16 = (mode & FSOUND_16BITS) != 0;
|
446 | c0fe3827 | bellard | hw->samples = conf.nb_samples; |
447 | 1d14ffa9 | bellard | return 0; |
448 | 1d14ffa9 | bellard | } |
449 | 1d14ffa9 | bellard | |
450 | 1d14ffa9 | bellard | static void fmod_fini_in (HWVoiceIn *hw) |
451 | 1d14ffa9 | bellard | { |
452 | 1d14ffa9 | bellard | FMODVoiceIn *fmd = (FMODVoiceIn *) hw; |
453 | 1d14ffa9 | bellard | |
454 | 1d14ffa9 | bellard | if (fmd->fmod_sample) {
|
455 | 1d14ffa9 | bellard | FSOUND_Record_Stop (); |
456 | 1d14ffa9 | bellard | FSOUND_Sample_Free (fmd->fmod_sample); |
457 | 1d14ffa9 | bellard | fmd->fmod_sample = 0;
|
458 | 1d14ffa9 | bellard | } |
459 | 1d14ffa9 | bellard | } |
460 | 1d14ffa9 | bellard | |
461 | 1d14ffa9 | bellard | static int fmod_run_in (HWVoiceIn *hw) |
462 | 1d14ffa9 | bellard | { |
463 | 1d14ffa9 | bellard | FMODVoiceIn *fmd = (FMODVoiceIn *) hw; |
464 | 1d14ffa9 | bellard | int hwshift = hw->info.shift;
|
465 | 1d14ffa9 | bellard | int live, dead, new_pos, len;
|
466 | 1d14ffa9 | bellard | unsigned int blen1 = 0, blen2 = 0; |
467 | 1d14ffa9 | bellard | unsigned int len1, len2; |
468 | 1d14ffa9 | bellard | unsigned int decr; |
469 | 1d14ffa9 | bellard | void *p1, *p2;
|
470 | 1d14ffa9 | bellard | |
471 | 1d14ffa9 | bellard | live = audio_pcm_hw_get_live_in (hw); |
472 | 1d14ffa9 | bellard | dead = hw->samples - live; |
473 | 1d14ffa9 | bellard | if (!dead) {
|
474 | 1d14ffa9 | bellard | return 0; |
475 | 1d14ffa9 | bellard | } |
476 | 1d14ffa9 | bellard | |
477 | 1d14ffa9 | bellard | new_pos = FSOUND_Record_GetPosition (); |
478 | 1d14ffa9 | bellard | if (new_pos < 0) { |
479 | c0fe3827 | bellard | fmod_logerr ("Could not get recording position\n");
|
480 | 1d14ffa9 | bellard | return 0; |
481 | 1d14ffa9 | bellard | } |
482 | 1d14ffa9 | bellard | |
483 | 1d14ffa9 | bellard | len = audio_ring_dist (new_pos, hw->wpos, hw->samples); |
484 | 1d14ffa9 | bellard | if (!len) {
|
485 | 1d14ffa9 | bellard | return 0; |
486 | 1d14ffa9 | bellard | } |
487 | 1d14ffa9 | bellard | len = audio_MIN (len, dead); |
488 | 1d14ffa9 | bellard | |
489 | 1d14ffa9 | bellard | if (fmod_lock_sample (fmd->fmod_sample, &fmd->hw.info,
|
490 | 1d14ffa9 | bellard | hw->wpos, len, |
491 | 1d14ffa9 | bellard | &p1, &p2, |
492 | 1d14ffa9 | bellard | &blen1, &blen2)) { |
493 | 1d14ffa9 | bellard | return 0; |
494 | 1d14ffa9 | bellard | } |
495 | 1d14ffa9 | bellard | |
496 | 1d14ffa9 | bellard | len1 = blen1 >> hwshift; |
497 | 1d14ffa9 | bellard | len2 = blen2 >> hwshift; |
498 | 1d14ffa9 | bellard | decr = len1 + len2; |
499 | 1d14ffa9 | bellard | |
500 | 1d14ffa9 | bellard | if (p1 && blen1) {
|
501 | 1d14ffa9 | bellard | hw->conv (hw->conv_buf + hw->wpos, p1, len1, &nominal_volume); |
502 | 1d14ffa9 | bellard | } |
503 | 1d14ffa9 | bellard | if (p2 && len2) {
|
504 | 1d14ffa9 | bellard | hw->conv (hw->conv_buf, p2, len2, &nominal_volume); |
505 | 1d14ffa9 | bellard | } |
506 | 1d14ffa9 | bellard | |
507 | 1d14ffa9 | bellard | fmod_unlock_sample (fmd->fmod_sample, p1, p2, blen1, blen2); |
508 | 1d14ffa9 | bellard | hw->wpos = (hw->wpos + decr) % hw->samples; |
509 | 1d14ffa9 | bellard | return decr;
|
510 | 1d14ffa9 | bellard | } |
511 | 1d14ffa9 | bellard | |
512 | 85571bc7 | bellard | static struct { |
513 | 85571bc7 | bellard | const char *name; |
514 | 85571bc7 | bellard | int type;
|
515 | 85571bc7 | bellard | } drvtab[] = { |
516 | 98f9f48c | malc | { .name = "none", .type = FSOUND_OUTPUT_NOSOUND },
|
517 | 85571bc7 | bellard | #ifdef _WIN32
|
518 | 98f9f48c | malc | { .name = "winmm", .type = FSOUND_OUTPUT_WINMM },
|
519 | 98f9f48c | malc | { .name = "dsound", .type = FSOUND_OUTPUT_DSOUND },
|
520 | 98f9f48c | malc | { .name = "a3d", .type = FSOUND_OUTPUT_A3D },
|
521 | 98f9f48c | malc | { .name = "asio", .type = FSOUND_OUTPUT_ASIO },
|
522 | 85571bc7 | bellard | #endif
|
523 | 85571bc7 | bellard | #ifdef __linux__
|
524 | 98f9f48c | malc | { .name = "oss", .type = FSOUND_OUTPUT_OSS },
|
525 | 98f9f48c | malc | { .name = "alsa", .type = FSOUND_OUTPUT_ALSA },
|
526 | 98f9f48c | malc | { .name = "esd", .type = FSOUND_OUTPUT_ESD },
|
527 | 85571bc7 | bellard | #endif
|
528 | 85571bc7 | bellard | #ifdef __APPLE__
|
529 | 98f9f48c | malc | { .name = "mac", .type = FSOUND_OUTPUT_MAC },
|
530 | 85571bc7 | bellard | #endif
|
531 | 85571bc7 | bellard | #if 0
|
532 | 98f9f48c | malc | { .name = "xbox", .type = FSOUND_OUTPUT_XBOX },
|
533 | 98f9f48c | malc | { .name = "ps2", .type = FSOUND_OUTPUT_PS2 },
|
534 | 98f9f48c | malc | { .name = "gcube", .type = FSOUND_OUTPUT_GC },
|
535 | 85571bc7 | bellard | #endif
|
536 | 98f9f48c | malc | { .name = "none-realtime", .type = FSOUND_OUTPUT_NOSOUND_NONREALTIME }
|
537 | 85571bc7 | bellard | }; |
538 | 85571bc7 | bellard | |
539 | 85571bc7 | bellard | static void *fmod_audio_init (void) |
540 | 85571bc7 | bellard | { |
541 | 1d14ffa9 | bellard | size_t i; |
542 | 85571bc7 | bellard | double ver;
|
543 | 85571bc7 | bellard | int status;
|
544 | 85571bc7 | bellard | int output_type = -1; |
545 | 1d14ffa9 | bellard | const char *drv = conf.drvname; |
546 | 85571bc7 | bellard | |
547 | 85571bc7 | bellard | ver = FSOUND_GetVersion (); |
548 | 85571bc7 | bellard | if (ver < FMOD_VERSION) {
|
549 | 85571bc7 | bellard | dolog ("Wrong FMOD version %f, need at least %f\n", ver, FMOD_VERSION);
|
550 | 85571bc7 | bellard | return NULL; |
551 | 85571bc7 | bellard | } |
552 | 85571bc7 | bellard | |
553 | 1d14ffa9 | bellard | #ifdef __linux__
|
554 | 1d14ffa9 | bellard | if (ver < 3.75) { |
555 | 1d14ffa9 | bellard | dolog ("FMOD before 3.75 has bug preventing ADC from working\n"
|
556 | 1d14ffa9 | bellard | "ADC will be disabled.\n");
|
557 | 1d14ffa9 | bellard | conf.broken_adc = 1;
|
558 | 1d14ffa9 | bellard | } |
559 | 1d14ffa9 | bellard | #endif
|
560 | 1d14ffa9 | bellard | |
561 | 85571bc7 | bellard | if (drv) {
|
562 | 85571bc7 | bellard | int found = 0; |
563 | b1503cda | malc | for (i = 0; i < ARRAY_SIZE (drvtab); i++) { |
564 | 85571bc7 | bellard | if (!strcmp (drv, drvtab[i].name)) {
|
565 | 85571bc7 | bellard | output_type = drvtab[i].type; |
566 | 85571bc7 | bellard | found = 1;
|
567 | 85571bc7 | bellard | break;
|
568 | 85571bc7 | bellard | } |
569 | 85571bc7 | bellard | } |
570 | 85571bc7 | bellard | if (!found) {
|
571 | 1d14ffa9 | bellard | dolog ("Unknown FMOD driver `%s'\n", drv);
|
572 | 1d14ffa9 | bellard | dolog ("Valid drivers:\n");
|
573 | b1503cda | malc | for (i = 0; i < ARRAY_SIZE (drvtab); i++) { |
574 | 1d14ffa9 | bellard | dolog (" %s\n", drvtab[i].name);
|
575 | 1d14ffa9 | bellard | } |
576 | 85571bc7 | bellard | } |
577 | 85571bc7 | bellard | } |
578 | 85571bc7 | bellard | |
579 | 85571bc7 | bellard | if (output_type != -1) { |
580 | 85571bc7 | bellard | status = FSOUND_SetOutput (output_type); |
581 | 85571bc7 | bellard | if (!status) {
|
582 | 1d14ffa9 | bellard | fmod_logerr ("FSOUND_SetOutput(%d) failed\n", output_type);
|
583 | 85571bc7 | bellard | return NULL; |
584 | 85571bc7 | bellard | } |
585 | 85571bc7 | bellard | } |
586 | 85571bc7 | bellard | |
587 | 85571bc7 | bellard | if (conf.bufsize) {
|
588 | 85571bc7 | bellard | status = FSOUND_SetBufferSize (conf.bufsize); |
589 | 85571bc7 | bellard | if (!status) {
|
590 | 1d14ffa9 | bellard | fmod_logerr ("FSOUND_SetBufferSize (%d) failed\n", conf.bufsize);
|
591 | 85571bc7 | bellard | } |
592 | 85571bc7 | bellard | } |
593 | 85571bc7 | bellard | |
594 | 85571bc7 | bellard | status = FSOUND_Init (conf.freq, conf.nb_channels, 0);
|
595 | 85571bc7 | bellard | if (!status) {
|
596 | 1d14ffa9 | bellard | fmod_logerr ("FSOUND_Init failed\n");
|
597 | 85571bc7 | bellard | return NULL; |
598 | 85571bc7 | bellard | } |
599 | 85571bc7 | bellard | |
600 | 85571bc7 | bellard | return &conf;
|
601 | 85571bc7 | bellard | } |
602 | 85571bc7 | bellard | |
603 | 1d14ffa9 | bellard | static int fmod_read (SWVoiceIn *sw, void *buf, int size) |
604 | 1d14ffa9 | bellard | { |
605 | 1d14ffa9 | bellard | return audio_pcm_sw_read (sw, buf, size);
|
606 | 1d14ffa9 | bellard | } |
607 | 1d14ffa9 | bellard | |
608 | 1d14ffa9 | bellard | static int fmod_ctl_in (HWVoiceIn *hw, int cmd, ...) |
609 | 1d14ffa9 | bellard | { |
610 | 1d14ffa9 | bellard | int status;
|
611 | 1d14ffa9 | bellard | FMODVoiceIn *fmd = (FMODVoiceIn *) hw; |
612 | 1d14ffa9 | bellard | |
613 | 1d14ffa9 | bellard | switch (cmd) {
|
614 | 1d14ffa9 | bellard | case VOICE_ENABLE:
|
615 | 1d14ffa9 | bellard | status = FSOUND_Record_StartSample (fmd->fmod_sample, 1);
|
616 | 1d14ffa9 | bellard | if (!status) {
|
617 | 1d14ffa9 | bellard | fmod_logerr ("Failed to start recording\n");
|
618 | 1d14ffa9 | bellard | } |
619 | 1d14ffa9 | bellard | break;
|
620 | 1d14ffa9 | bellard | |
621 | 1d14ffa9 | bellard | case VOICE_DISABLE:
|
622 | 1d14ffa9 | bellard | status = FSOUND_Record_Stop (); |
623 | 1d14ffa9 | bellard | if (!status) {
|
624 | 1d14ffa9 | bellard | fmod_logerr ("Failed to stop recording\n");
|
625 | 1d14ffa9 | bellard | } |
626 | 1d14ffa9 | bellard | break;
|
627 | 1d14ffa9 | bellard | } |
628 | 1d14ffa9 | bellard | return 0; |
629 | 1d14ffa9 | bellard | } |
630 | 1d14ffa9 | bellard | |
631 | 85571bc7 | bellard | static void fmod_audio_fini (void *opaque) |
632 | 85571bc7 | bellard | { |
633 | 1d14ffa9 | bellard | (void) opaque;
|
634 | 85571bc7 | bellard | FSOUND_Close (); |
635 | 85571bc7 | bellard | } |
636 | 85571bc7 | bellard | |
637 | 1d14ffa9 | bellard | static struct audio_option fmod_options[] = { |
638 | 98f9f48c | malc | { |
639 | 98f9f48c | malc | .name = "DRV",
|
640 | 98f9f48c | malc | .tag = AUD_OPT_STR, |
641 | 98f9f48c | malc | .valp = &conf.drvname, |
642 | 98f9f48c | malc | .descr = "FMOD driver"
|
643 | 98f9f48c | malc | }, |
644 | 98f9f48c | malc | { |
645 | 98f9f48c | malc | .name = "FREQ",
|
646 | 98f9f48c | malc | .tag = AUD_OPT_INT, |
647 | 98f9f48c | malc | .valp = &conf.freq, |
648 | 98f9f48c | malc | .descr = "Default frequency"
|
649 | 98f9f48c | malc | }, |
650 | 98f9f48c | malc | { |
651 | 98f9f48c | malc | .name = "SAMPLES",
|
652 | 98f9f48c | malc | .tag = AUD_OPT_INT, |
653 | 98f9f48c | malc | .valp = &conf.nb_samples, |
654 | 98f9f48c | malc | .descr = "Buffer size in samples"
|
655 | 98f9f48c | malc | }, |
656 | 98f9f48c | malc | { |
657 | 98f9f48c | malc | .name = "CHANNELS",
|
658 | 98f9f48c | malc | .tag = AUD_OPT_INT, |
659 | 98f9f48c | malc | .valp = &conf.nb_channels, |
660 | 98f9f48c | malc | .descr = "Number of default channels (1 - mono, 2 - stereo)"
|
661 | 98f9f48c | malc | }, |
662 | 98f9f48c | malc | { |
663 | 98f9f48c | malc | .name = "BUFSIZE",
|
664 | 98f9f48c | malc | .tag = AUD_OPT_INT, |
665 | 98f9f48c | malc | .valp = &conf.bufsize, |
666 | 98f9f48c | malc | .descr = "(undocumented)"
|
667 | 98f9f48c | malc | } |
668 | 1d14ffa9 | bellard | #if 0
|
669 | 98f9f48c | malc | {
|
670 | 98f9f48c | malc | .name = "THRESHOLD",
|
671 | 98f9f48c | malc | .tag = AUD_OPT_INT,
|
672 | 98f9f48c | malc | .valp = &conf.threshold,
|
673 | 98f9f48c | malc | .descr = "(undocumented)"
|
674 | 98f9f48c | malc | }
|
675 | 1d14ffa9 | bellard | #endif
|
676 | 2700efa3 | Juan Quintela | { /* End of list */ }
|
677 | 1d14ffa9 | bellard | }; |
678 | 1d14ffa9 | bellard | |
679 | 35f4b58c | blueswir1 | static struct audio_pcm_ops fmod_pcm_ops = { |
680 | 1dd3e4d1 | Juan Quintela | .init_out = fmod_init_out, |
681 | 1dd3e4d1 | Juan Quintela | .fini_out = fmod_fini_out, |
682 | 1dd3e4d1 | Juan Quintela | .run_out = fmod_run_out, |
683 | 1dd3e4d1 | Juan Quintela | .write = fmod_write, |
684 | 1dd3e4d1 | Juan Quintela | .ctl_out = fmod_ctl_out, |
685 | 1dd3e4d1 | Juan Quintela | |
686 | 98f9f48c | malc | .init_in = fmod_init_in, |
687 | 98f9f48c | malc | .fini_in = fmod_fini_in, |
688 | 98f9f48c | malc | .run_in = fmod_run_in, |
689 | 98f9f48c | malc | .read = fmod_read, |
690 | 98f9f48c | malc | .ctl_in = fmod_ctl_in |
691 | 85571bc7 | bellard | }; |
692 | 85571bc7 | bellard | |
693 | 1d14ffa9 | bellard | struct audio_driver fmod_audio_driver = {
|
694 | bee37f32 | Juan Quintela | .name = "fmod",
|
695 | bee37f32 | Juan Quintela | .descr = "FMOD 3.xx http://www.fmod.org",
|
696 | bee37f32 | Juan Quintela | .options = fmod_options, |
697 | bee37f32 | Juan Quintela | .init = fmod_audio_init, |
698 | bee37f32 | Juan Quintela | .fini = fmod_audio_fini, |
699 | bee37f32 | Juan Quintela | .pcm_ops = &fmod_pcm_ops, |
700 | bee37f32 | Juan Quintela | .can_be_default = 1,
|
701 | bee37f32 | Juan Quintela | .max_voices_out = INT_MAX, |
702 | bee37f32 | Juan Quintela | .max_voices_in = INT_MAX, |
703 | bee37f32 | Juan Quintela | .voice_size_out = sizeof (FMODVoiceOut),
|
704 | bee37f32 | Juan Quintela | .voice_size_in = sizeof (FMODVoiceIn)
|
705 | 85571bc7 | bellard | }; |