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