Statistics
| Branch: | Revision:

root / audio / fmodaudio.c @ c0fe3827

History | View | Annotate | Download (16.8 kB)

1
/*
2
 * QEMU FMOD audio driver
3
 *
4
 * Copyright (c) 2004-2005 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
#define AUDIO_CAP "fmod"
29
#include "audio_int.h"
30

    
31
typedef struct FMODVoiceOut {
32
    HWVoiceOut hw;
33
    unsigned int old_pos;
34
    FSOUND_SAMPLE *fmod_sample;
35
    int channel;
36
} FMODVoiceOut;
37

    
38
typedef struct FMODVoiceIn {
39
    HWVoiceIn hw;
40
    FSOUND_SAMPLE *fmod_sample;
41
} FMODVoiceIn;
42

    
43
static struct {
44
    const char *drvname;
45
    int nb_samples;
46
    int freq;
47
    int nb_channels;
48
    int bufsize;
49
    int threshold;
50
    int broken_adc;
51
} conf = {
52
    NULL,
53
    2048 * 2,
54
    44100,
55
    2,
56
    0,
57
    0,
58
    0
59
};
60

    
61
static void GCC_FMT_ATTR (1, 2) fmod_logerr (const char *fmt, ...)
62
{
63
    va_list ap;
64

    
65
    va_start (ap, fmt);
66
    AUD_vlog (AUDIO_CAP, fmt, ap);
67
    va_end (ap);
68

    
69
    AUD_log (AUDIO_CAP, "Reason: %s\n",
70
             FMOD_ErrorString (FSOUND_GetError ()));
71
}
72

    
73
static void GCC_FMT_ATTR (2, 3) fmod_logerr2 (
74
    const char *typ,
75
    const char *fmt,
76
    ...
77
    )
78
{
79
    va_list ap;
80

    
81
    AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
82

    
83
    va_start (ap, fmt);
84
    AUD_vlog (AUDIO_CAP, fmt, ap);
85
    va_end (ap);
86

    
87
    AUD_log (AUDIO_CAP, "Reason: %s\n",
88
             FMOD_ErrorString (FSOUND_GetError ()));
89
}
90

    
91
static int fmod_write (SWVoiceOut *sw, void *buf, int len)
92
{
93
    return audio_pcm_sw_write (sw, buf, len);
94
}
95

    
96
static void fmod_clear_sample (FMODVoiceOut *fmd)
97
{
98
    HWVoiceOut *hw = &fmd->hw;
99
    int status;
100
    void *p1 = 0, *p2 = 0;
101
    unsigned int len1 = 0, len2 = 0;
102

    
103
    status = FSOUND_Sample_Lock (
104
        fmd->fmod_sample,
105
        0,
106
        hw->samples << hw->info.shift,
107
        &p1,
108
        &p2,
109
        &len1,
110
        &len2
111
        );
112

    
113
    if (!status) {
114
        fmod_logerr ("Failed to lock sample\n");
115
        return;
116
    }
117

    
118
    if ((len1 & hw->info.align) || (len2 & hw->info.align)) {
119
        dolog ("Lock returned misaligned length %d, %d, alignment %d\n",
120
               len1, len2, hw->info.align + 1);
121
        goto fail;
122
    }
123

    
124
    if ((len1 + len2) - (hw->samples << hw->info.shift)) {
125
        dolog ("Lock returned incomplete length %d, %d\n",
126
               len1 + len2, hw->samples << hw->info.shift);
127
        goto fail;
128
    }
129

    
130
    audio_pcm_info_clear_buf (&hw->info, p1, hw->samples);
131

    
132
 fail:
133
    status = FSOUND_Sample_Unlock (fmd->fmod_sample, p1, p2, len1, len2);
134
    if (!status) {
135
        fmod_logerr ("Failed to unlock sample\n");
136
    }
137
}
138

    
139
static void fmod_write_sample (HWVoiceOut *hw, uint8_t *dst, int dst_len)
140
{
141
    int src_len1 = dst_len;
142
    int src_len2 = 0;
143
    int pos = hw->rpos + dst_len;
144
    st_sample_t *src1 = hw->mix_buf + hw->rpos;
145
    st_sample_t *src2 = NULL;
146

    
147
    if (pos > hw->samples) {
148
        src_len1 = hw->samples - hw->rpos;
149
        src2 = hw->mix_buf;
150
        src_len2 = dst_len - src_len1;
151
        pos = src_len2;
152
    }
153

    
154
    if (src_len1) {
155
        hw->clip (dst, src1, src_len1);
156
        mixeng_clear (src1, src_len1);
157
    }
158

    
159
    if (src_len2) {
160
        dst = advance (dst, src_len1 << hw->info.shift);
161
        hw->clip (dst, src2, src_len2);
162
        mixeng_clear (src2, src_len2);
163
    }
164

    
165
    hw->rpos = pos % hw->samples;
166
}
167

    
168
static int fmod_unlock_sample (FSOUND_SAMPLE *sample, void *p1, void *p2,
169
                               unsigned int blen1, unsigned int blen2)
170
{
171
    int status = FSOUND_Sample_Unlock (sample, p1, p2, blen1, blen2);
172
    if (!status) {
173
        fmod_logerr ("Failed to unlock sample\n");
174
        return -1;
175
    }
176
    return 0;
177
}
178

    
179
static int fmod_lock_sample (
180
    FSOUND_SAMPLE *sample,
181
    struct audio_pcm_info *info,
182
    int pos,
183
    int len,
184
    void **p1,
185
    void **p2,
186
    unsigned int *blen1,
187
    unsigned int *blen2
188
    )
189
{
190
    int status;
191

    
192
    status = FSOUND_Sample_Lock (
193
        sample,
194
        pos << info->shift,
195
        len << info->shift,
196
        p1,
197
        p2,
198
        blen1,
199
        blen2
200
        );
201

    
202
    if (!status) {
203
        fmod_logerr ("Failed to lock sample\n");
204
        return -1;
205
    }
206

    
207
    if ((*blen1 & info->align) || (*blen2 & info->align)) {
208
        dolog ("Lock returned misaligned length %d, %d, alignment %d\n",
209
               *blen1, *blen2, info->align + 1);
210

    
211
        fmod_unlock_sample (sample, *p1, *p2, *blen1, *blen2);
212

    
213
        *p1 = NULL - 1;
214
        *p2 = NULL - 1;
215
        *blen1 = ~0U;
216
        *blen2 = ~0U;
217
        return -1;
218
    }
219

    
220
    if (!*p1 && *blen1) {
221
        dolog ("warning: !p1 && blen1=%d\n", *blen1);
222
        *blen1 = 0;
223
    }
224

    
225
    if (!p2 && *blen2) {
226
        dolog ("warning: !p2 && blen2=%d\n", *blen2);
227
        *blen2 = 0;
228
    }
229

    
230
    return 0;
231
}
232

    
233
static int fmod_run_out (HWVoiceOut *hw)
234
{
235
    FMODVoiceOut *fmd = (FMODVoiceOut *) hw;
236
    int live, decr;
237
    void *p1 = 0, *p2 = 0;
238
    unsigned int blen1 = 0, blen2 = 0;
239
    unsigned int len1 = 0, len2 = 0;
240
    int nb_live;
241

    
242
    live = audio_pcm_hw_get_live_out2 (hw, &nb_live);
243
    if (!live) {
244
        return 0;
245
    }
246

    
247
    if (!hw->pending_disable
248
        && nb_live
249
        && (conf.threshold && live <= conf.threshold)) {
250
        ldebug ("live=%d nb_live=%d\n", live, nb_live);
251
        return 0;
252
    }
253

    
254
    decr = live;
255

    
256
    if (fmd->channel >= 0) {
257
        int len = decr;
258
        int old_pos = fmd->old_pos;
259
        int ppos = FSOUND_GetCurrentPosition (fmd->channel);
260

    
261
        if (ppos == old_pos || !ppos) {
262
            return 0;
263
        }
264

    
265
        if ((old_pos < ppos) && ((old_pos + len) > ppos)) {
266
            len = ppos - old_pos;
267
        }
268
        else {
269
            if ((old_pos > ppos) && ((old_pos + len) > (ppos + hw->samples))) {
270
                len = hw->samples - old_pos + ppos;
271
            }
272
        }
273
        decr = len;
274

    
275
        if (audio_bug (AUDIO_FUNC, decr < 0)) {
276
            dolog ("decr=%d live=%d ppos=%d old_pos=%d len=%d\n",
277
                   decr, live, ppos, old_pos, len);
278
            return 0;
279
        }
280
    }
281

    
282

    
283
    if (!decr) {
284
        return 0;
285
    }
286

    
287
    if (fmod_lock_sample (fmd->fmod_sample, &fmd->hw.info,
288
                          fmd->old_pos, decr,
289
                          &p1, &p2,
290
                          &blen1, &blen2)) {
291
        return 0;
292
    }
293

    
294
    len1 = blen1 >> hw->info.shift;
295
    len2 = blen2 >> hw->info.shift;
296
    ldebug ("%p %p %d %d %d %d\n", p1, p2, len1, len2, blen1, blen2);
297
    decr = len1 + len2;
298

    
299
    if (p1 && len1) {
300
        fmod_write_sample (hw, p1, len1);
301
    }
302

    
303
    if (p2 && len2) {
304
        fmod_write_sample (hw, p2, len2);
305
    }
306

    
307
    fmod_unlock_sample (fmd->fmod_sample, p1, p2, blen1, blen2);
308

    
309
    fmd->old_pos = (fmd->old_pos + decr) % hw->samples;
310
    return decr;
311
}
312

    
313
static int aud_to_fmodfmt (audfmt_e fmt, int stereo)
314
{
315
    int mode = FSOUND_LOOP_NORMAL;
316

    
317
    switch (fmt) {
318
    case AUD_FMT_S8:
319
        mode |= FSOUND_SIGNED | FSOUND_8BITS;
320
        break;
321

    
322
    case AUD_FMT_U8:
323
        mode |= FSOUND_UNSIGNED | FSOUND_8BITS;
324
        break;
325

    
326
    case AUD_FMT_S16:
327
        mode |= FSOUND_SIGNED | FSOUND_16BITS;
328
        break;
329

    
330
    case AUD_FMT_U16:
331
        mode |= FSOUND_UNSIGNED | FSOUND_16BITS;
332
        break;
333

    
334
    default:
335
        dolog ("Internal logic error: Bad audio format %d\n", fmt);
336
#ifdef DEBUG_FMOD
337
        abort ();
338
#endif
339
        mode |= FSOUND_8BITS;
340
    }
341
    mode |= stereo ? FSOUND_STEREO : FSOUND_MONO;
342
    return mode;
343
}
344

    
345
static void fmod_fini_out (HWVoiceOut *hw)
346
{
347
    FMODVoiceOut *fmd = (FMODVoiceOut *) hw;
348

    
349
    if (fmd->fmod_sample) {
350
        FSOUND_Sample_Free (fmd->fmod_sample);
351
        fmd->fmod_sample = 0;
352

    
353
        if (fmd->channel >= 0) {
354
            FSOUND_StopSound (fmd->channel);
355
        }
356
    }
357
}
358

    
359
static int fmod_init_out (HWVoiceOut *hw, audsettings_t *as)
360
{
361
    int bits16, mode, channel;
362
    FMODVoiceOut *fmd = (FMODVoiceOut *) hw;
363

    
364
    mode = aud_to_fmodfmt (as->fmt, as->nchannels == 2 ? 1 : 0);
365
    fmd->fmod_sample = FSOUND_Sample_Alloc (
366
        FSOUND_FREE,            /* index */
367
        conf.nb_samples,        /* length */
368
        mode,                   /* mode */
369
        as->freq,               /* freq */
370
        255,                    /* volume */
371
        128,                    /* pan */
372
        255                     /* priority */
373
        );
374

    
375
    if (!fmd->fmod_sample) {
376
        fmod_logerr2 ("DAC", "Failed to allocate FMOD sample\n");
377
        return -1;
378
    }
379

    
380
    channel = FSOUND_PlaySoundEx (FSOUND_FREE, fmd->fmod_sample, 0, 1);
381
    if (channel < 0) {
382
        fmod_logerr2 ("DAC", "Failed to start playing sound\n");
383
        FSOUND_Sample_Free (fmd->fmod_sample);
384
        return -1;
385
    }
386
    fmd->channel = channel;
387

    
388
    /* FMOD always operates on little endian frames? */
389
    audio_pcm_init_info (&hw->info, as, audio_need_to_swap_endian (0));
390
    bits16 = (mode & FSOUND_16BITS) != 0;
391
    hw->samples = conf.nb_samples;
392
    return 0;
393
}
394

    
395
static int fmod_ctl_out (HWVoiceOut *hw, int cmd, ...)
396
{
397
    int status;
398
    FMODVoiceOut *fmd = (FMODVoiceOut *) hw;
399

    
400
    switch (cmd) {
401
    case VOICE_ENABLE:
402
        fmod_clear_sample (fmd);
403
        status = FSOUND_SetPaused (fmd->channel, 0);
404
        if (!status) {
405
            fmod_logerr ("Failed to resume channel %d\n", fmd->channel);
406
        }
407
        break;
408

    
409
    case VOICE_DISABLE:
410
        status = FSOUND_SetPaused (fmd->channel, 1);
411
        if (!status) {
412
            fmod_logerr ("Failed to pause channel %d\n", fmd->channel);
413
        }
414
        break;
415
    }
416
    return 0;
417
}
418

    
419
static int fmod_init_in (HWVoiceIn *hw, audsettings_t *as)
420
{
421
    int bits16, mode;
422
    FMODVoiceIn *fmd = (FMODVoiceIn *) hw;
423

    
424
    if (conf.broken_adc) {
425
        return -1;
426
    }
427

    
428
    mode = aud_to_fmodfmt (as->fmt, as->nchannels == 2 ? 1 : 0);
429
    fmd->fmod_sample = FSOUND_Sample_Alloc (
430
        FSOUND_FREE,            /* index */
431
        conf.nb_samples,        /* length */
432
        mode,                   /* mode */
433
        as->freq,               /* freq */
434
        255,                    /* volume */
435
        128,                    /* pan */
436
        255                     /* priority */
437
        );
438

    
439
    if (!fmd->fmod_sample) {
440
        fmod_logerr2 ("ADC", "Failed to allocate FMOD sample\n");
441
        return -1;
442
    }
443

    
444
    /* FMOD always operates on little endian frames? */
445
    audio_pcm_init_info (&hw->info, as, audio_need_to_swap_endian (0));
446
    bits16 = (mode & FSOUND_16BITS) != 0;
447
    hw->samples = conf.nb_samples;
448
    return 0;
449
}
450

    
451
static void fmod_fini_in (HWVoiceIn *hw)
452
{
453
    FMODVoiceIn *fmd = (FMODVoiceIn *) hw;
454

    
455
    if (fmd->fmod_sample) {
456
        FSOUND_Record_Stop ();
457
        FSOUND_Sample_Free (fmd->fmod_sample);
458
        fmd->fmod_sample = 0;
459
    }
460
}
461

    
462
static int fmod_run_in (HWVoiceIn *hw)
463
{
464
    FMODVoiceIn *fmd = (FMODVoiceIn *) hw;
465
    int hwshift = hw->info.shift;
466
    int live, dead, new_pos, len;
467
    unsigned int blen1 = 0, blen2 = 0;
468
    unsigned int len1, len2;
469
    unsigned int decr;
470
    void *p1, *p2;
471

    
472
    live = audio_pcm_hw_get_live_in (hw);
473
    dead = hw->samples - live;
474
    if (!dead) {
475
        return 0;
476
    }
477

    
478
    new_pos = FSOUND_Record_GetPosition ();
479
    if (new_pos < 0) {
480
        fmod_logerr ("Could not get recording position\n");
481
        return 0;
482
    }
483

    
484
    len = audio_ring_dist (new_pos,  hw->wpos, hw->samples);
485
    if (!len) {
486
        return 0;
487
    }
488
    len = audio_MIN (len, dead);
489

    
490
    if (fmod_lock_sample (fmd->fmod_sample, &fmd->hw.info,
491
                          hw->wpos, len,
492
                          &p1, &p2,
493
                          &blen1, &blen2)) {
494
        return 0;
495
    }
496

    
497
    len1 = blen1 >> hwshift;
498
    len2 = blen2 >> hwshift;
499
    decr = len1 + len2;
500

    
501
    if (p1 && blen1) {
502
        hw->conv (hw->conv_buf + hw->wpos, p1, len1, &nominal_volume);
503
    }
504
    if (p2 && len2) {
505
        hw->conv (hw->conv_buf, p2, len2, &nominal_volume);
506
    }
507

    
508
    fmod_unlock_sample (fmd->fmod_sample, p1, p2, blen1, blen2);
509
    hw->wpos = (hw->wpos + decr) % hw->samples;
510
    return decr;
511
}
512

    
513
static struct {
514
    const char *name;
515
    int type;
516
} drvtab[] = {
517
    {"none", FSOUND_OUTPUT_NOSOUND},
518
#ifdef _WIN32
519
    {"winmm", FSOUND_OUTPUT_WINMM},
520
    {"dsound", FSOUND_OUTPUT_DSOUND},
521
    {"a3d", FSOUND_OUTPUT_A3D},
522
    {"asio", FSOUND_OUTPUT_ASIO},
523
#endif
524
#ifdef __linux__
525
    {"oss", FSOUND_OUTPUT_OSS},
526
    {"alsa", FSOUND_OUTPUT_ALSA},
527
    {"esd", FSOUND_OUTPUT_ESD},
528
#endif
529
#ifdef __APPLE__
530
    {"mac", FSOUND_OUTPUT_MAC},
531
#endif
532
#if 0
533
    {"xbox", FSOUND_OUTPUT_XBOX},
534
    {"ps2", FSOUND_OUTPUT_PS2},
535
    {"gcube", FSOUND_OUTPUT_GC},
536
#endif
537
    {"none-realtime", FSOUND_OUTPUT_NOSOUND_NONREALTIME}
538
};
539

    
540
static void *fmod_audio_init (void)
541
{
542
    size_t i;
543
    double ver;
544
    int status;
545
    int output_type = -1;
546
    const char *drv = conf.drvname;
547

    
548
    ver = FSOUND_GetVersion ();
549
    if (ver < FMOD_VERSION) {
550
        dolog ("Wrong FMOD version %f, need at least %f\n", ver, FMOD_VERSION);
551
        return NULL;
552
    }
553

    
554
#ifdef __linux__
555
    if (ver < 3.75) {
556
        dolog ("FMOD before 3.75 has bug preventing ADC from working\n"
557
               "ADC will be disabled.\n");
558
        conf.broken_adc = 1;
559
    }
560
#endif
561

    
562
    if (drv) {
563
        int found = 0;
564
        for (i = 0; i < sizeof (drvtab) / sizeof (drvtab[0]); i++) {
565
            if (!strcmp (drv, drvtab[i].name)) {
566
                output_type = drvtab[i].type;
567
                found = 1;
568
                break;
569
            }
570
        }
571
        if (!found) {
572
            dolog ("Unknown FMOD driver `%s'\n", drv);
573
            dolog ("Valid drivers:\n");
574
            for (i = 0; i < sizeof (drvtab) / sizeof (drvtab[0]); i++) {
575
                dolog ("  %s\n", drvtab[i].name);
576
            }
577
        }
578
    }
579

    
580
    if (output_type != -1) {
581
        status = FSOUND_SetOutput (output_type);
582
        if (!status) {
583
            fmod_logerr ("FSOUND_SetOutput(%d) failed\n", output_type);
584
            return NULL;
585
        }
586
    }
587

    
588
    if (conf.bufsize) {
589
        status = FSOUND_SetBufferSize (conf.bufsize);
590
        if (!status) {
591
            fmod_logerr ("FSOUND_SetBufferSize (%d) failed\n", conf.bufsize);
592
        }
593
    }
594

    
595
    status = FSOUND_Init (conf.freq, conf.nb_channels, 0);
596
    if (!status) {
597
        fmod_logerr ("FSOUND_Init failed\n");
598
        return NULL;
599
    }
600

    
601
    return &conf;
602
}
603

    
604
static int fmod_read (SWVoiceIn *sw, void *buf, int size)
605
{
606
    return audio_pcm_sw_read (sw, buf, size);
607
}
608

    
609
static int fmod_ctl_in (HWVoiceIn *hw, int cmd, ...)
610
{
611
    int status;
612
    FMODVoiceIn *fmd = (FMODVoiceIn *) hw;
613

    
614
    switch (cmd) {
615
    case VOICE_ENABLE:
616
        status = FSOUND_Record_StartSample (fmd->fmod_sample, 1);
617
        if (!status) {
618
            fmod_logerr ("Failed to start recording\n");
619
        }
620
        break;
621

    
622
    case VOICE_DISABLE:
623
        status = FSOUND_Record_Stop ();
624
        if (!status) {
625
            fmod_logerr ("Failed to stop recording\n");
626
        }
627
        break;
628
    }
629
    return 0;
630
}
631

    
632
static void fmod_audio_fini (void *opaque)
633
{
634
    (void) opaque;
635
    FSOUND_Close ();
636
}
637

    
638
static struct audio_option fmod_options[] = {
639
    {"DRV", AUD_OPT_STR, &conf.drvname,
640
     "FMOD driver", NULL, 0},
641
    {"FREQ", AUD_OPT_INT, &conf.freq,
642
     "Default frequency", NULL, 0},
643
    {"SAMPLES", AUD_OPT_INT, &conf.nb_samples,
644
     "Buffer size in samples", NULL, 0},
645
    {"CHANNELS", AUD_OPT_INT, &conf.nb_channels,
646
     "Number of default channels (1 - mono, 2 - stereo)", NULL, 0},
647
    {"BUFSIZE", AUD_OPT_INT, &conf.bufsize,
648
     "(undocumented)", NULL, 0},
649
#if 0
650
    {"THRESHOLD", AUD_OPT_INT, &conf.threshold,
651
     "(undocumented)"},
652
#endif
653

    
654
    {NULL, 0, NULL, NULL, NULL, 0}
655
};
656

    
657
static struct audio_pcm_ops fmod_pcm_ops = {
658
    fmod_init_out,
659
    fmod_fini_out,
660
    fmod_run_out,
661
    fmod_write,
662
    fmod_ctl_out,
663

    
664
    fmod_init_in,
665
    fmod_fini_in,
666
    fmod_run_in,
667
    fmod_read,
668
    fmod_ctl_in
669
};
670

    
671
struct audio_driver fmod_audio_driver = {
672
    INIT_FIELD (name           = ) "fmod",
673
    INIT_FIELD (descr          = ) "FMOD 3.xx http://www.fmod.org",
674
    INIT_FIELD (options        = ) fmod_options,
675
    INIT_FIELD (init           = ) fmod_audio_init,
676
    INIT_FIELD (fini           = ) fmod_audio_fini,
677
    INIT_FIELD (pcm_ops        = ) &fmod_pcm_ops,
678
    INIT_FIELD (can_be_default = ) 1,
679
    INIT_FIELD (max_voices_out = ) INT_MAX,
680
    INIT_FIELD (max_voices_in  = ) INT_MAX,
681
    INIT_FIELD (voice_size_out = ) sizeof (FMODVoiceOut),
682
    INIT_FIELD (voice_size_in  = ) sizeof (FMODVoiceIn)
683
};