0.6.2-alt1
[qemu] / qemu-snapshot-2004-11-28_23 / audio / fmodaudio.c
1 /*
2  * QEMU FMOD audio output driver
3  * 
4  * Copyright (c) 2004 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 "vl.h"
27
28 #include "audio/audio_int.h"
29
30 typedef struct FMODVoice {
31     HWVoice hw;
32     unsigned int old_pos;
33     FSOUND_SAMPLE *fmod_sample;
34     int channel;
35 } FMODVoice;
36
37 #define dolog(...) AUD_log ("fmod", __VA_ARGS__)
38 #ifdef DEBUG
39 #define ldebug(...) dolog (__VA_ARGS__)
40 #else
41 #define ldebug(...)
42 #endif
43
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"
50
51 static struct {
52     int nb_samples;
53     int freq;
54     int nb_channels;
55     int bufsize;
56     int threshold;
57 } conf = {
58     2048,
59     44100,
60     1,
61     0,
62     128
63 };
64
65 #define errstr() FMOD_ErrorString (FSOUND_GetError ())
66
67 static int fmod_hw_write (SWVoice *sw, void *buf, int len)
68 {
69     return pcm_hw_write (sw, buf, len);
70 }
71
72 static void fmod_clear_sample (FMODVoice *fmd)
73 {
74     HWVoice *hw = &fmd->hw;
75     int status;
76     void *p1 = 0, *p2 = 0;
77     unsigned int len1 = 0, len2 = 0;
78
79     status = FSOUND_Sample_Lock (
80         fmd->fmod_sample,
81         0,
82         hw->samples << hw->shift,
83         &p1,
84         &p2,
85         &len1,
86         &len2
87         );
88
89     if (!status) {
90         dolog ("Failed to lock sample\nReason: %s\n", errstr ());
91         return;
92     }
93
94     if ((len1 & hw->align) || (len2 & hw->align)) {
95         dolog ("Locking sample returned unaligned length %d, %d\n",
96                len1, len2);
97         goto fail;
98     }
99
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);
103         goto fail;
104     }
105     pcm_hw_clear (hw, p1, hw->samples);
106
107  fail:
108     status = FSOUND_Sample_Unlock (fmd->fmod_sample, p1, p2, len1, len2);
109     if (!status) {
110         dolog ("Failed to unlock sample\nReason: %s\n", errstr ());
111     }
112 }
113
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)
116 {
117     int src_len1 = dst_len, src_len2 = 0, pos = src_pos + dst_len;
118     st_sample_t *src1 = src + src_pos, *src2 = 0;
119
120     if (src_pos + dst_len > src_size) {
121         src_len1 = src_size - src_pos;
122         src2 = src;
123         src_len2 = dst_len - src_len1;
124         pos = src_len2;
125     }
126
127     if (src_len1) {
128         hw->clip (dst, src1, src_len1);
129         memset (src1, 0, src_len1 * sizeof (st_sample_t));
130         advance (dst, src_len1);
131     }
132
133     if (src_len2) {
134         hw->clip (dst, src2, src_len2);
135         memset (src2, 0, src_len2 * sizeof (st_sample_t));
136     }
137     return pos;
138 }
139
140 static int fmod_unlock_sample (FMODVoice *fmd, void *p1, void *p2,
141                                unsigned int blen1, unsigned int blen2)
142 {
143     int status = FSOUND_Sample_Unlock (fmd->fmod_sample, p1, p2, blen1, blen2);
144     if (!status) {
145         dolog ("Failed to unlock sample\nReason: %s\n", errstr ());
146         return -1;
147     }
148     return 0;
149 }
150
151 static int fmod_lock_sample (FMODVoice *fmd, int pos, int len,
152                              void **p1, void **p2,
153                              unsigned int *blen1, unsigned int *blen2)
154 {
155     HWVoice *hw = &fmd->hw;
156     int status;
157
158     status = FSOUND_Sample_Lock (
159         fmd->fmod_sample,
160         pos << hw->shift,
161         len << hw->shift,
162         p1,
163         p2,
164         blen1,
165         blen2
166         );
167
168     if (!status) {
169         dolog ("Failed to lock sample\nReason: %s\n", errstr ());
170         return -1;
171     }
172
173     if ((*blen1 & hw->align) || (*blen2 & hw->align)) {
174         dolog ("Locking sample returned unaligned length %d, %d\n",
175                *blen1, *blen2);
176         fmod_unlock_sample (fmd, *p1, *p2, *blen1, *blen2);
177         return -1;
178     }
179     return 0;
180 }
181
182 static void fmod_hw_run (HWVoice *hw)
183 {
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;
189     int nb_active;
190
191     live = pcm_hw_get_live2 (hw, &nb_active);
192     if (live <= 0) {
193         return;
194     }
195
196     if (!hw->pending_disable
197         && nb_active
198         && conf.threshold
199         && live <= conf.threshold) {
200         ldebug ("live=%d nb_active=%d\n", live, nb_active);
201         return;
202     }
203
204     decr = live;
205
206 #if 1
207     if (fmd->channel >= 0) {
208         int pos2 = (fmd->old_pos + decr) % hw->samples;
209         int pos = FSOUND_GetCurrentPosition (fmd->channel);
210
211         if (fmd->old_pos < pos && pos2 >= pos) {
212             decr = pos - fmd->old_pos - (pos2 == pos) - 1;
213         }
214         else if (fmd->old_pos > pos && pos2 >= pos && pos2 < fmd->old_pos) {
215             decr = (hw->samples - fmd->old_pos) + pos - (pos2 == pos) - 1;
216         }
217 /*         ldebug ("pos=%d pos2=%d old=%d live=%d decr=%d\n", */
218 /*                 pos, pos2, fmd->old_pos, live, decr); */
219     }
220 #endif
221
222     if (decr <= 0) {
223         return;
224     }
225
226     if (fmod_lock_sample (fmd, fmd->old_pos, decr, &p1, &p2, &blen1, &blen2)) {
227         return;
228     }
229
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);
233     decr = len1 + len2;
234     rpos = hw->rpos;
235
236     if (len1) {
237         rpos = fmod_write_sample (hw, p1, hw->mix_buf, hw->samples, rpos, len1);
238     }
239
240     if (len2) {
241         rpos = fmod_write_sample (hw, p2, hw->mix_buf, hw->samples, rpos, len2);
242     }
243
244     fmod_unlock_sample (fmd, p1, p2, blen1, blen2);
245
246     pcm_hw_dec_live (hw, decr);
247     hw->rpos = rpos % hw->samples;
248     fmd->old_pos = (fmd->old_pos + decr) % hw->samples;
249 }
250
251 static int AUD_to_fmodfmt (audfmt_e fmt, int stereo)
252 {
253     int mode = FSOUND_LOOP_NORMAL;
254
255     switch (fmt) {
256     case AUD_FMT_S8:
257         mode |= FSOUND_SIGNED | FSOUND_8BITS;
258         break;
259
260     case AUD_FMT_U8:
261         mode |= FSOUND_UNSIGNED | FSOUND_8BITS;
262         break;
263
264     case AUD_FMT_S16:
265         mode |= FSOUND_SIGNED | FSOUND_16BITS;
266         break;
267
268     case AUD_FMT_U16:
269         mode |= FSOUND_UNSIGNED | FSOUND_16BITS;
270         break;
271
272     default:
273         dolog ("Internal logic error: Bad audio format %d\nAborting\n", fmt);
274         exit (EXIT_FAILURE);
275     }
276     mode |= stereo ? FSOUND_STEREO : FSOUND_MONO;
277     return mode;
278 }
279
280 static void fmod_hw_fini (HWVoice *hw)
281 {
282     FMODVoice *fmd = (FMODVoice *) hw;
283
284     if (fmd->fmod_sample) {
285         FSOUND_Sample_Free (fmd->fmod_sample);
286         fmd->fmod_sample = 0;
287
288         if (fmd->channel >= 0) {
289             FSOUND_StopSound (fmd->channel);
290         }
291     }
292 }
293
294 static int fmod_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt)
295 {
296     int bits16, mode, channel;
297     FMODVoice *fmd = (FMODVoice *) hw;
298
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 */
303         mode,                   /* mode */
304         freq,                   /* freq */
305         255,                    /* volume */
306         128,                    /* pan */
307         255                     /* priority */
308         );
309
310     if (!fmd->fmod_sample) {
311         dolog ("Failed to allocate FMOD sample\nReason: %s\n", errstr ());
312         return -1;
313     }
314
315     channel = FSOUND_PlaySoundEx (FSOUND_FREE, fmd->fmod_sample, 0, 1);
316     if (channel < 0) {
317         dolog ("Failed to start playing sound\nReason: %s\n", errstr ());
318         FSOUND_Sample_Free (fmd->fmod_sample);
319         return -1;
320     }
321     fmd->channel = channel;
322
323     hw->freq = freq;
324     hw->fmt = fmt;
325     hw->nchannels = nchannels;
326     bits16 = fmt == AUD_FMT_U16 || fmt == AUD_FMT_S16;
327     hw->bufsize = conf.nb_samples << (nchannels == 2) << bits16;
328     return 0;
329 }
330
331 static int fmod_hw_ctl (HWVoice *hw, int cmd, ...)
332 {
333     int status;
334     FMODVoice *fmd = (FMODVoice *) hw;
335
336     switch (cmd) {
337     case VOICE_ENABLE:
338         fmod_clear_sample (fmd);
339         status = FSOUND_SetPaused (fmd->channel, 0);
340         if (!status) {
341             dolog ("Failed to resume channel %d\nReason: %s\n",
342                    fmd->channel, errstr ());
343         }
344         break;
345
346     case VOICE_DISABLE:
347         status = FSOUND_SetPaused (fmd->channel, 1);
348         if (!status) {
349             dolog ("Failed to pause channel %d\nReason: %s\n",
350                    fmd->channel, errstr ());
351         }
352         break;
353     }
354     return 0;
355 }
356
357 static struct {
358     const char *name;
359     int type;
360 } drvtab[] = {
361     {"none", FSOUND_OUTPUT_NOSOUND},
362 #ifdef _WIN32
363     {"winmm", FSOUND_OUTPUT_WINMM},
364     {"dsound", FSOUND_OUTPUT_DSOUND},
365     {"a3d", FSOUND_OUTPUT_A3D},
366     {"asio", FSOUND_OUTPUT_ASIO},
367 #endif
368 #ifdef __linux__
369     {"oss", FSOUND_OUTPUT_OSS},
370     {"alsa", FSOUND_OUTPUT_ALSA},
371     {"esd", FSOUND_OUTPUT_ESD},
372 #endif
373 #ifdef __APPLE__
374     {"mac", FSOUND_OUTPUT_MAC},
375 #endif
376 #if 0
377     {"xbox", FSOUND_OUTPUT_XBOX},
378     {"ps2", FSOUND_OUTPUT_PS2},
379     {"gcube", FSOUND_OUTPUT_GC},
380 #endif
381     {"nort", FSOUND_OUTPUT_NOSOUND_NONREALTIME}
382 };
383
384 static void *fmod_audio_init (void)
385 {
386     int i;
387     double ver;
388     int status;
389     int output_type = -1;
390     const char *drv = audio_get_conf_str (QC_FMOD_DRV, NULL);
391
392     ver = FSOUND_GetVersion ();
393     if (ver < FMOD_VERSION) {
394         dolog ("Wrong FMOD version %f, need at least %f\n", ver, FMOD_VERSION);
395         return NULL;
396     }
397
398     if (drv) {
399         int found = 0;
400         for (i = 0; i < sizeof (drvtab) / sizeof (drvtab[0]); i++) {
401             if (!strcmp (drv, drvtab[i].name)) {
402                 output_type = drvtab[i].type;
403                 found = 1;
404                 break;
405             }
406         }
407         if (!found) {
408             dolog ("Unknown FMOD output driver `%s'\n", drv);
409         }
410     }
411
412     if (output_type != -1) {
413         status = FSOUND_SetOutput (output_type);
414         if (!status) {
415             dolog ("FSOUND_SetOutput(%d) failed\nReason: %s\n",
416                    output_type, errstr ());
417             return NULL;
418         }
419     }
420
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);
423     conf.nb_channels =
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);
430
431     if (conf.bufsize) {
432         status = FSOUND_SetBufferSize (conf.bufsize);
433         if (!status) {
434             dolog ("FSOUND_SetBufferSize (%d) failed\nReason: %s\n",
435                    conf.bufsize, errstr ());
436         }
437     }
438
439     status = FSOUND_Init (conf.freq, conf.nb_channels, 0);
440     if (!status) {
441         dolog ("FSOUND_Init failed\nReason: %s\n", errstr ());
442         return NULL;
443     }
444
445     return &conf;
446 }
447
448 static void fmod_audio_fini (void *opaque)
449 {
450     FSOUND_Close ();
451 }
452
453 struct pcm_ops fmod_pcm_ops = {
454     fmod_hw_init,
455     fmod_hw_fini,
456     fmod_hw_run,
457     fmod_hw_write,
458     fmod_hw_ctl
459 };
460
461 struct audio_output_driver fmod_output_driver = {
462     "fmod",
463     fmod_audio_init,
464     fmod_audio_fini,
465     &fmod_pcm_ops,
466     1,
467     INT_MAX,
468     sizeof (FMODVoice)
469 };