2 * QEMU FMOD audio output driver
4 * Copyright (c) 2004 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>
28 #include "audio/audio_int.h"
30 typedef struct FMODVoice
{
33 FSOUND_SAMPLE
*fmod_sample
;
37 #define dolog(...) AUD_log ("fmod", __VA_ARGS__)
39 #define ldebug(...) dolog (__VA_ARGS__)
44 #define QC_FMOD_DRV "QEMU_FMOD_DRV"
45 #define QC_FMOD_FREQ "QEMU_FMOD_FREQ"
46 #define QC_FMOD_SAMPLES "QEMU_FMOD_SAMPLES"
47 #define QC_FMOD_CHANNELS "QEMU_FMOD_CHANNELS"
48 #define QC_FMOD_BUFSIZE "QEMU_FMOD_BUFSIZE"
49 #define QC_FMOD_THRESHOLD "QEMU_FMOD_THRESHOLD"
65 #define errstr() FMOD_ErrorString (FSOUND_GetError ())
67 static int fmod_hw_write (SWVoice
*sw
, void *buf
, int len
)
69 return pcm_hw_write (sw
, buf
, len
);
72 static void fmod_clear_sample (FMODVoice
*fmd
)
74 HWVoice
*hw
= &fmd
->hw
;
76 void *p1
= 0, *p2
= 0;
77 unsigned int len1
= 0, len2
= 0;
79 status
= FSOUND_Sample_Lock (
82 hw
->samples
<< hw
->shift
,
90 dolog ("Failed to lock sample\nReason: %s\n", errstr ());
94 if ((len1
& hw
->align
) || (len2
& hw
->align
)) {
95 dolog ("Locking sample returned unaligned length %d, %d\n",
100 if (len1
+ len2
!= hw
->samples
<< hw
->shift
) {
101 dolog ("Locking sample returned incomplete length %d, %d\n",
102 len1
+ len2
, hw
->samples
<< hw
->shift
);
105 pcm_hw_clear (hw
, p1
, hw
->samples
);
108 status
= FSOUND_Sample_Unlock (fmd
->fmod_sample
, p1
, p2
, len1
, len2
);
110 dolog ("Failed to unlock sample\nReason: %s\n", errstr ());
114 static int fmod_write_sample (HWVoice
*hw
, uint8_t *dst
, st_sample_t
*src
,
115 int src_size
, int src_pos
, int dst_len
)
117 int src_len1
= dst_len
, src_len2
= 0, pos
= src_pos
+ dst_len
;
118 st_sample_t
*src1
= src
+ src_pos
, *src2
= 0;
120 if (src_pos
+ dst_len
> src_size
) {
121 src_len1
= src_size
- src_pos
;
123 src_len2
= dst_len
- src_len1
;
128 hw
->clip (dst
, src1
, src_len1
);
129 memset (src1
, 0, src_len1
* sizeof (st_sample_t
));
130 advance (dst
, src_len1
);
134 hw
->clip (dst
, src2
, src_len2
);
135 memset (src2
, 0, src_len2
* sizeof (st_sample_t
));
140 static int fmod_unlock_sample (FMODVoice
*fmd
, void *p1
, void *p2
,
141 unsigned int blen1
, unsigned int blen2
)
143 int status
= FSOUND_Sample_Unlock (fmd
->fmod_sample
, p1
, p2
, blen1
, blen2
);
145 dolog ("Failed to unlock sample\nReason: %s\n", errstr ());
151 static int fmod_lock_sample (FMODVoice
*fmd
, int pos
, int len
,
152 void **p1
, void **p2
,
153 unsigned int *blen1
, unsigned int *blen2
)
155 HWVoice
*hw
= &fmd
->hw
;
158 status
= FSOUND_Sample_Lock (
169 dolog ("Failed to lock sample\nReason: %s\n", errstr ());
173 if ((*blen1
& hw
->align
) || (*blen2
& hw
->align
)) {
174 dolog ("Locking sample returned unaligned length %d, %d\n",
176 fmod_unlock_sample (fmd
, *p1
, *p2
, *blen1
, *blen2
);
182 static void fmod_hw_run (HWVoice
*hw
)
184 FMODVoice
*fmd
= (FMODVoice
*) hw
;
185 int rpos
, live
, decr
;
186 void *p1
= 0, *p2
= 0;
187 unsigned int blen1
= 0, blen2
= 0;
188 unsigned int len1
= 0, len2
= 0;
191 live
= pcm_hw_get_live2 (hw
, &nb_active
);
196 if (!hw
->pending_disable
199 && live
<= conf
.threshold
) {
200 ldebug ("live=%d nb_active=%d\n", live
, nb_active
);
207 if (fmd
->channel
>= 0) {
208 int pos2
= (fmd
->old_pos
+ decr
) % hw
->samples
;
209 int pos
= FSOUND_GetCurrentPosition (fmd
->channel
);
211 if (fmd
->old_pos
< pos
&& pos2
>= pos
) {
212 decr
= pos
- fmd
->old_pos
- (pos2
== pos
) - 1;
214 else if (fmd
->old_pos
> pos
&& pos2
>= pos
&& pos2
< fmd
->old_pos
) {
215 decr
= (hw
->samples
- fmd
->old_pos
) + pos
- (pos2
== pos
) - 1;
217 /* ldebug ("pos=%d pos2=%d old=%d live=%d decr=%d\n", */
218 /* pos, pos2, fmd->old_pos, live, decr); */
226 if (fmod_lock_sample (fmd
, fmd
->old_pos
, decr
, &p1
, &p2
, &blen1
, &blen2
)) {
230 len1
= blen1
>> hw
->shift
;
231 len2
= blen2
>> hw
->shift
;
232 ldebug ("%p %p %d %d %d %d\n", p1
, p2
, len1
, len2
, blen1
, blen2
);
237 rpos
= fmod_write_sample (hw
, p1
, hw
->mix_buf
, hw
->samples
, rpos
, len1
);
241 rpos
= fmod_write_sample (hw
, p2
, hw
->mix_buf
, hw
->samples
, rpos
, len2
);
244 fmod_unlock_sample (fmd
, p1
, p2
, blen1
, blen2
);
246 pcm_hw_dec_live (hw
, decr
);
247 hw
->rpos
= rpos
% hw
->samples
;
248 fmd
->old_pos
= (fmd
->old_pos
+ decr
) % hw
->samples
;
251 static int AUD_to_fmodfmt (audfmt_e fmt
, int stereo
)
253 int mode
= FSOUND_LOOP_NORMAL
;
257 mode
|= FSOUND_SIGNED
| FSOUND_8BITS
;
261 mode
|= FSOUND_UNSIGNED
| FSOUND_8BITS
;
265 mode
|= FSOUND_SIGNED
| FSOUND_16BITS
;
269 mode
|= FSOUND_UNSIGNED
| FSOUND_16BITS
;
273 dolog ("Internal logic error: Bad audio format %d\nAborting\n", fmt
);
276 mode
|= stereo
? FSOUND_STEREO
: FSOUND_MONO
;
280 static void fmod_hw_fini (HWVoice
*hw
)
282 FMODVoice
*fmd
= (FMODVoice
*) hw
;
284 if (fmd
->fmod_sample
) {
285 FSOUND_Sample_Free (fmd
->fmod_sample
);
286 fmd
->fmod_sample
= 0;
288 if (fmd
->channel
>= 0) {
289 FSOUND_StopSound (fmd
->channel
);
294 static int fmod_hw_init (HWVoice
*hw
, int freq
, int nchannels
, audfmt_e fmt
)
296 int bits16
, mode
, channel
;
297 FMODVoice
*fmd
= (FMODVoice
*) hw
;
299 mode
= AUD_to_fmodfmt (fmt
, nchannels
== 2 ? 1 : 0);
300 fmd
->fmod_sample
= FSOUND_Sample_Alloc (
301 FSOUND_FREE
, /* index */
302 conf
.nb_samples
, /* length */
310 if (!fmd
->fmod_sample
) {
311 dolog ("Failed to allocate FMOD sample\nReason: %s\n", errstr ());
315 channel
= FSOUND_PlaySoundEx (FSOUND_FREE
, fmd
->fmod_sample
, 0, 1);
317 dolog ("Failed to start playing sound\nReason: %s\n", errstr ());
318 FSOUND_Sample_Free (fmd
->fmod_sample
);
321 fmd
->channel
= channel
;
325 hw
->nchannels
= nchannels
;
326 bits16
= fmt
== AUD_FMT_U16
|| fmt
== AUD_FMT_S16
;
327 hw
->bufsize
= conf
.nb_samples
<< (nchannels
== 2) << bits16
;
331 static int fmod_hw_ctl (HWVoice
*hw
, int cmd
, ...)
334 FMODVoice
*fmd
= (FMODVoice
*) hw
;
338 fmod_clear_sample (fmd
);
339 status
= FSOUND_SetPaused (fmd
->channel
, 0);
341 dolog ("Failed to resume channel %d\nReason: %s\n",
342 fmd
->channel
, errstr ());
347 status
= FSOUND_SetPaused (fmd
->channel
, 1);
349 dolog ("Failed to pause channel %d\nReason: %s\n",
350 fmd
->channel
, errstr ());
361 {"none", FSOUND_OUTPUT_NOSOUND
},
363 {"winmm", FSOUND_OUTPUT_WINMM
},
364 {"dsound", FSOUND_OUTPUT_DSOUND
},
365 {"a3d", FSOUND_OUTPUT_A3D
},
366 {"asio", FSOUND_OUTPUT_ASIO
},
369 {"oss", FSOUND_OUTPUT_OSS
},
370 {"alsa", FSOUND_OUTPUT_ALSA
},
371 {"esd", FSOUND_OUTPUT_ESD
},
374 {"mac", FSOUND_OUTPUT_MAC
},
377 {"xbox", FSOUND_OUTPUT_XBOX
},
378 {"ps2", FSOUND_OUTPUT_PS2
},
379 {"gcube", FSOUND_OUTPUT_GC
},
381 {"nort", FSOUND_OUTPUT_NOSOUND_NONREALTIME
}
384 static void *fmod_audio_init (void)
389 int output_type
= -1;
390 const char *drv
= audio_get_conf_str (QC_FMOD_DRV
, NULL
);
392 ver
= FSOUND_GetVersion ();
393 if (ver
< FMOD_VERSION
) {
394 dolog ("Wrong FMOD version %f, need at least %f\n", ver
, FMOD_VERSION
);
400 for (i
= 0; i
< sizeof (drvtab
) / sizeof (drvtab
[0]); i
++) {
401 if (!strcmp (drv
, drvtab
[i
].name
)) {
402 output_type
= drvtab
[i
].type
;
408 dolog ("Unknown FMOD output driver `%s'\n", drv
);
412 if (output_type
!= -1) {
413 status
= FSOUND_SetOutput (output_type
);
415 dolog ("FSOUND_SetOutput(%d) failed\nReason: %s\n",
416 output_type
, errstr ());
421 conf
.freq
= audio_get_conf_int (QC_FMOD_FREQ
, conf
.freq
);
422 conf
.nb_samples
= audio_get_conf_int (QC_FMOD_SAMPLES
, conf
.nb_samples
);
424 audio_get_conf_int (QC_FMOD_CHANNELS
,
425 (audio_state
.nb_hw_voices
> 1
426 ? audio_state
.nb_hw_voices
427 : conf
.nb_channels
));
428 conf
.bufsize
= audio_get_conf_int (QC_FMOD_BUFSIZE
, conf
.bufsize
);
429 conf
.threshold
= audio_get_conf_int (QC_FMOD_THRESHOLD
, conf
.threshold
);
432 status
= FSOUND_SetBufferSize (conf
.bufsize
);
434 dolog ("FSOUND_SetBufferSize (%d) failed\nReason: %s\n",
435 conf
.bufsize
, errstr ());
439 status
= FSOUND_Init (conf
.freq
, conf
.nb_channels
, 0);
441 dolog ("FSOUND_Init failed\nReason: %s\n", errstr ());
448 static void fmod_audio_fini (void *opaque
)
453 struct pcm_ops fmod_pcm_ops
= {
461 struct audio_output_driver fmod_output_driver
= {