• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
fmod_logerr(const char * fmt,...)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 
fmod_logerr2(const char * typ,const char * fmt,...)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 
fmod_write(SWVoiceOut * sw,void * buf,int len)92 static int fmod_write (SWVoiceOut *sw, void *buf, int len)
93 {
94     return audio_pcm_sw_write (sw, buf, len);
95 }
96 
fmod_clear_sample(FMODVoiceOut * fmd)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 
fmod_write_sample(HWVoiceOut * hw,uint8_t * dst,int dst_len)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 
fmod_unlock_sample(FSOUND_SAMPLE * sample,void * p1,void * p2,unsigned int blen1,unsigned int blen2)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 
fmod_lock_sample(FSOUND_SAMPLE * sample,struct audio_pcm_info * info,int pos,int len,void ** p1,void ** p2,unsigned int * blen1,unsigned int * blen2)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 
fmod_run_out(HWVoiceOut * hw)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 
aud_to_fmodfmt(audfmt_e fmt,int stereo)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 
fmod_fini_out(HWVoiceOut * hw)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 
fmod_init_out(HWVoiceOut * hw,struct audsettings * as)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 
fmod_ctl_out(HWVoiceOut * hw,int cmd,...)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 
fmod_init_in(HWVoiceIn * hw,struct audsettings * as)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 
fmod_fini_in(HWVoiceIn * hw)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 
fmod_run_in(HWVoiceIn * hw)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 
fmod_audio_init(void)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 
fmod_read(SWVoiceIn * sw,void * buf,int size)607 static int fmod_read (SWVoiceIn *sw, void *buf, int size)
608 {
609     return audio_pcm_sw_read (sw, buf, size);
610 }
611 
fmod_ctl_in(HWVoiceIn * hw,int cmd,...)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 
fmod_audio_fini(void * opaque)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 };
687