Statistics
| Branch: | Revision:

root / audio / fmodaudio.c @ fb065187

History | View | Annotate | Download (12.2 kB)

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
};