2 * QEMU FMOD audio driver
4 * Copyright (c) 2004-2005 Vassili Karpov (malc)
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:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
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
25 #include <fmod_errors.h>
26 #include "qemu-common.h"
29 #define AUDIO_CAP "fmod"
30 #include "audio_int.h"
32 typedef struct FMODVoiceOut
{
35 FSOUND_SAMPLE
*fmod_sample
;
39 typedef struct FMODVoiceIn
{
41 FSOUND_SAMPLE
*fmod_sample
;
53 .nb_samples
= 2048 * 2,
58 static void GCC_FMT_ATTR (1, 2) fmod_logerr (const char *fmt
, ...)
63 AUD_vlog (AUDIO_CAP
, fmt
, ap
);
66 AUD_log (AUDIO_CAP
, "Reason: %s\n",
67 FMOD_ErrorString (FSOUND_GetError ()));
70 static void GCC_FMT_ATTR (2, 3) fmod_logerr2 (
78 AUD_log (AUDIO_CAP
, "Could not initialize %s\n", typ
);
81 AUD_vlog (AUDIO_CAP
, fmt
, ap
);
84 AUD_log (AUDIO_CAP
, "Reason: %s\n",
85 FMOD_ErrorString (FSOUND_GetError ()));
88 static int fmod_write (SWVoiceOut
*sw
, void *buf
, int len
)
90 return audio_pcm_sw_write (sw
, buf
, len
);
93 static void fmod_clear_sample (FMODVoiceOut
*fmd
)
95 HWVoiceOut
*hw
= &fmd
->hw
;
97 void *p1
= 0, *p2
= 0;
98 unsigned int len1
= 0, len2
= 0;
100 status
= FSOUND_Sample_Lock (
103 hw
->samples
<< hw
->info
.shift
,
111 fmod_logerr ("Failed to lock sample\n");
115 if ((len1
& hw
->info
.align
) || (len2
& hw
->info
.align
)) {
116 dolog ("Lock returned misaligned length %d, %d, alignment %d\n",
117 len1
, len2
, hw
->info
.align
+ 1);
121 if ((len1
+ len2
) - (hw
->samples
<< hw
->info
.shift
)) {
122 dolog ("Lock returned incomplete length %d, %d\n",
123 len1
+ len2
, hw
->samples
<< hw
->info
.shift
);
127 audio_pcm_info_clear_buf (&hw
->info
, p1
, hw
->samples
);
130 status
= FSOUND_Sample_Unlock (fmd
->fmod_sample
, p1
, p2
, len1
, len2
);
132 fmod_logerr ("Failed to unlock sample\n");
136 static void fmod_write_sample (HWVoiceOut
*hw
, uint8_t *dst
, int dst_len
)
138 int src_len1
= dst_len
;
140 int pos
= hw
->rpos
+ dst_len
;
141 struct st_sample
*src1
= hw
->mix_buf
+ hw
->rpos
;
142 struct st_sample
*src2
= NULL
;
144 if (pos
> hw
->samples
) {
145 src_len1
= hw
->samples
- hw
->rpos
;
147 src_len2
= dst_len
- src_len1
;
152 hw
->clip (dst
, src1
, src_len1
);
156 dst
= advance (dst
, src_len1
<< hw
->info
.shift
);
157 hw
->clip (dst
, src2
, src_len2
);
160 hw
->rpos
= pos
% hw
->samples
;
163 static int fmod_unlock_sample (FSOUND_SAMPLE
*sample
, void *p1
, void *p2
,
164 unsigned int blen1
, unsigned int blen2
)
166 int status
= FSOUND_Sample_Unlock (sample
, p1
, p2
, blen1
, blen2
);
168 fmod_logerr ("Failed to unlock sample\n");
174 static int fmod_lock_sample (
175 FSOUND_SAMPLE
*sample
,
176 struct audio_pcm_info
*info
,
187 status
= FSOUND_Sample_Lock (
198 fmod_logerr ("Failed to lock sample\n");
202 if ((*blen1
& info
->align
) || (*blen2
& info
->align
)) {
203 dolog ("Lock returned misaligned length %d, %d, alignment %d\n",
204 *blen1
, *blen2
, info
->align
+ 1);
206 fmod_unlock_sample (sample
, *p1
, *p2
, *blen1
, *blen2
);
215 if (!*p1
&& *blen1
) {
216 dolog ("warning: !p1 && blen1=%d\n", *blen1
);
221 dolog ("warning: !p2 && blen2=%d\n", *blen2
);
228 static int fmod_run_out (HWVoiceOut
*hw
)
230 FMODVoiceOut
*fmd
= (FMODVoiceOut
*) hw
;
232 void *p1
= 0, *p2
= 0;
233 unsigned int blen1
= 0, blen2
= 0;
234 unsigned int len1
= 0, len2
= 0;
237 live
= audio_pcm_hw_get_live_out2 (hw
, &nb_live
);
242 if (!hw
->pending_disable
244 && (conf
.threshold
&& live
<= conf
.threshold
)) {
245 ldebug ("live=%d nb_live=%d\n", live
, nb_live
);
251 if (fmd
->channel
>= 0) {
253 int old_pos
= fmd
->old_pos
;
254 int ppos
= FSOUND_GetCurrentPosition (fmd
->channel
);
256 if (ppos
== old_pos
|| !ppos
) {
260 if ((old_pos
< ppos
) && ((old_pos
+ len
) > ppos
)) {
261 len
= ppos
- old_pos
;
264 if ((old_pos
> ppos
) && ((old_pos
+ len
) > (ppos
+ hw
->samples
))) {
265 len
= hw
->samples
- old_pos
+ ppos
;
270 if (audio_bug (AUDIO_FUNC
, decr
< 0)) {
271 dolog ("decr=%d live=%d ppos=%d old_pos=%d len=%d\n",
272 decr
, live
, ppos
, old_pos
, len
);
282 if (fmod_lock_sample (fmd
->fmod_sample
, &fmd
->hw
.info
,
289 len1
= blen1
>> hw
->info
.shift
;
290 len2
= blen2
>> hw
->info
.shift
;
291 ldebug ("%p %p %d %d %d %d\n", p1
, p2
, len1
, len2
, blen1
, blen2
);
295 fmod_write_sample (hw
, p1
, len1
);
299 fmod_write_sample (hw
, p2
, len2
);
302 fmod_unlock_sample (fmd
->fmod_sample
, p1
, p2
, blen1
, blen2
);
304 fmd
->old_pos
= (fmd
->old_pos
+ decr
) % hw
->samples
;
308 static int aud_to_fmodfmt (audfmt_e fmt
, int stereo
)
310 int mode
= FSOUND_LOOP_NORMAL
;
314 mode
|= FSOUND_SIGNED
| FSOUND_8BITS
;
318 mode
|= FSOUND_UNSIGNED
| FSOUND_8BITS
;
322 mode
|= FSOUND_SIGNED
| FSOUND_16BITS
;
326 mode
|= FSOUND_UNSIGNED
| FSOUND_16BITS
;
330 dolog ("Internal logic error: Bad audio format %d\n", fmt
);
334 mode
|= FSOUND_8BITS
;
336 mode
|= stereo
? FSOUND_STEREO
: FSOUND_MONO
;
340 static void fmod_fini_out (HWVoiceOut
*hw
)
342 FMODVoiceOut
*fmd
= (FMODVoiceOut
*) hw
;
344 if (fmd
->fmod_sample
) {
345 FSOUND_Sample_Free (fmd
->fmod_sample
);
346 fmd
->fmod_sample
= 0;
348 if (fmd
->channel
>= 0) {
349 FSOUND_StopSound (fmd
->channel
);
354 static int fmod_init_out (HWVoiceOut
*hw
, struct audsettings
*as
)
356 int bits16
, mode
, channel
;
357 FMODVoiceOut
*fmd
= (FMODVoiceOut
*) hw
;
358 struct audsettings obt_as
= *as
;
360 mode
= aud_to_fmodfmt (as
->fmt
, as
->nchannels
== 2 ? 1 : 0);
361 fmd
->fmod_sample
= FSOUND_Sample_Alloc (
362 FSOUND_FREE
, /* index */
363 conf
.nb_samples
, /* length */
371 if (!fmd
->fmod_sample
) {
372 fmod_logerr2 ("DAC", "Failed to allocate FMOD sample\n");
376 channel
= FSOUND_PlaySoundEx (FSOUND_FREE
, fmd
->fmod_sample
, 0, 1);
378 fmod_logerr2 ("DAC", "Failed to start playing sound\n");
379 FSOUND_Sample_Free (fmd
->fmod_sample
);
382 fmd
->channel
= channel
;
384 /* FMOD always operates on little endian frames? */
385 obt_as
.endianness
= 0;
386 audio_pcm_init_info (&hw
->info
, &obt_as
);
387 bits16
= (mode
& FSOUND_16BITS
) != 0;
388 hw
->samples
= conf
.nb_samples
;
392 static int fmod_ctl_out (HWVoiceOut
*hw
, int cmd
, ...)
395 FMODVoiceOut
*fmd
= (FMODVoiceOut
*) hw
;
399 fmod_clear_sample (fmd
);
400 status
= FSOUND_SetPaused (fmd
->channel
, 0);
402 fmod_logerr ("Failed to resume channel %d\n", fmd
->channel
);
407 status
= FSOUND_SetPaused (fmd
->channel
, 1);
409 fmod_logerr ("Failed to pause channel %d\n", fmd
->channel
);
416 static int fmod_init_in (HWVoiceIn
*hw
, struct audsettings
*as
)
419 FMODVoiceIn
*fmd
= (FMODVoiceIn
*) hw
;
420 struct audsettings obt_as
= *as
;
422 if (conf
.broken_adc
) {
426 mode
= aud_to_fmodfmt (as
->fmt
, as
->nchannels
== 2 ? 1 : 0);
427 fmd
->fmod_sample
= FSOUND_Sample_Alloc (
428 FSOUND_FREE
, /* index */
429 conf
.nb_samples
, /* length */
437 if (!fmd
->fmod_sample
) {
438 fmod_logerr2 ("ADC", "Failed to allocate FMOD sample\n");
442 /* FMOD always operates on little endian frames? */
443 obt_as
.endianness
= 0;
444 audio_pcm_init_info (&hw
->info
, &obt_as
);
445 bits16
= (mode
& FSOUND_16BITS
) != 0;
446 hw
->samples
= conf
.nb_samples
;
450 static void fmod_fini_in (HWVoiceIn
*hw
)
452 FMODVoiceIn
*fmd
= (FMODVoiceIn
*) hw
;
454 if (fmd
->fmod_sample
) {
455 FSOUND_Record_Stop ();
456 FSOUND_Sample_Free (fmd
->fmod_sample
);
457 fmd
->fmod_sample
= 0;
461 static int fmod_run_in (HWVoiceIn
*hw
)
463 FMODVoiceIn
*fmd
= (FMODVoiceIn
*) hw
;
464 int hwshift
= hw
->info
.shift
;
465 int live
, dead
, new_pos
, len
;
466 unsigned int blen1
= 0, blen2
= 0;
467 unsigned int len1
, len2
;
471 live
= audio_pcm_hw_get_live_in (hw
);
472 dead
= hw
->samples
- live
;
477 new_pos
= FSOUND_Record_GetPosition ();
479 fmod_logerr ("Could not get recording position\n");
483 len
= audio_ring_dist (new_pos
, hw
->wpos
, hw
->samples
);
487 len
= audio_MIN (len
, dead
);
489 if (fmod_lock_sample (fmd
->fmod_sample
, &fmd
->hw
.info
,
496 len1
= blen1
>> hwshift
;
497 len2
= blen2
>> hwshift
;
501 hw
->conv (hw
->conv_buf
+ hw
->wpos
, p1
, len1
, &nominal_volume
);
504 hw
->conv (hw
->conv_buf
, p2
, len2
, &nominal_volume
);
507 fmod_unlock_sample (fmd
->fmod_sample
, p1
, p2
, blen1
, blen2
);
508 hw
->wpos
= (hw
->wpos
+ decr
) % hw
->samples
;
516 { .name
= "none", .type
= FSOUND_OUTPUT_NOSOUND
},
518 { .name
= "winmm", .type
= FSOUND_OUTPUT_WINMM
},
519 { .name
= "dsound", .type
= FSOUND_OUTPUT_DSOUND
},
520 { .name
= "a3d", .type
= FSOUND_OUTPUT_A3D
},
521 { .name
= "asio", .type
= FSOUND_OUTPUT_ASIO
},
524 { .name
= "oss", .type
= FSOUND_OUTPUT_OSS
},
525 { .name
= "alsa", .type
= FSOUND_OUTPUT_ALSA
},
526 { .name
= "esd", .type
= FSOUND_OUTPUT_ESD
},
529 { .name
= "mac", .type
= FSOUND_OUTPUT_MAC
},
532 { .name
= "xbox", .type
= FSOUND_OUTPUT_XBOX
},
533 { .name
= "ps2", .type
= FSOUND_OUTPUT_PS2
},
534 { .name
= "gcube", .type
= FSOUND_OUTPUT_GC
},
536 { .name
= "none-realtime", .type
= FSOUND_OUTPUT_NOSOUND_NONREALTIME
}
539 static void *fmod_audio_init (void)
544 int output_type
= -1;
545 const char *drv
= conf
.drvname
;
547 ver
= FSOUND_GetVersion ();
548 if (ver
< FMOD_VERSION
) {
549 dolog ("Wrong FMOD version %f, need at least %f\n", ver
, FMOD_VERSION
);
555 dolog ("FMOD before 3.75 has bug preventing ADC from working\n"
556 "ADC will be disabled.\n");
563 for (i
= 0; i
< ARRAY_SIZE (drvtab
); i
++) {
564 if (!strcmp (drv
, drvtab
[i
].name
)) {
565 output_type
= drvtab
[i
].type
;
571 dolog ("Unknown FMOD driver `%s'\n", drv
);
572 dolog ("Valid drivers:\n");
573 for (i
= 0; i
< ARRAY_SIZE (drvtab
); i
++) {
574 dolog (" %s\n", drvtab
[i
].name
);
579 if (output_type
!= -1) {
580 status
= FSOUND_SetOutput (output_type
);
582 fmod_logerr ("FSOUND_SetOutput(%d) failed\n", output_type
);
588 status
= FSOUND_SetBufferSize (conf
.bufsize
);
590 fmod_logerr ("FSOUND_SetBufferSize (%d) failed\n", conf
.bufsize
);
594 status
= FSOUND_Init (conf
.freq
, conf
.nb_channels
, 0);
596 fmod_logerr ("FSOUND_Init failed\n");
603 static int fmod_read (SWVoiceIn
*sw
, void *buf
, int size
)
605 return audio_pcm_sw_read (sw
, buf
, size
);
608 static int fmod_ctl_in (HWVoiceIn
*hw
, int cmd
, ...)
611 FMODVoiceIn
*fmd
= (FMODVoiceIn
*) hw
;
615 status
= FSOUND_Record_StartSample (fmd
->fmod_sample
, 1);
617 fmod_logerr ("Failed to start recording\n");
622 status
= FSOUND_Record_Stop ();
624 fmod_logerr ("Failed to stop recording\n");
631 static void fmod_audio_fini (void *opaque
)
637 static struct audio_option fmod_options
[] = {
641 .valp
= &conf
.drvname
,
642 .descr
= "FMOD driver"
648 .descr
= "Default frequency"
653 .valp
= &conf
.nb_samples
,
654 .descr
= "Buffer size in samples"
659 .valp
= &conf
.nb_channels
,
660 .descr
= "Number of default channels (1 - mono, 2 - stereo)"
665 .valp
= &conf
.bufsize
,
666 .descr
= "(undocumented)"
672 .valp
= &conf
.threshold
,
673 .descr
= "(undocumented)"
676 { /* End of list */ }
679 static struct audio_pcm_ops fmod_pcm_ops
= {
680 .init_out
= fmod_init_out
,
681 .fini_out
= fmod_fini_out
,
682 .run_out
= fmod_run_out
,
684 .ctl_out
= fmod_ctl_out
,
686 .init_in
= fmod_init_in
,
687 .fini_in
= fmod_fini_in
,
688 .run_in
= fmod_run_in
,
690 .ctl_in
= fmod_ctl_in
693 struct audio_driver fmod_audio_driver
= {
695 .descr
= "FMOD 3.xx http://www.fmod.org",
696 .options
= fmod_options
,
697 .init
= fmod_audio_init
,
698 .fini
= fmod_audio_fini
,
699 .pcm_ops
= &fmod_pcm_ops
,
701 .max_voices_out
= INT_MAX
,
702 .max_voices_in
= INT_MAX
,
703 .voice_size_out
= sizeof (FMODVoiceOut
),
704 .voice_size_in
= sizeof (FMODVoiceIn
)