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