qemu/audio/fmodaudio.c
<<
>>
Prefs
   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 "qemu-common.h"
  27#include "audio.h"
  28
  29#define AUDIO_CAP "fmod"
  30#include "audio_int.h"
  31
  32typedef struct FMODVoiceOut {
  33    HWVoiceOut hw;
  34    unsigned int old_pos;
  35    FSOUND_SAMPLE *fmod_sample;
  36    int channel;
  37} FMODVoiceOut;
  38
  39typedef struct FMODVoiceIn {
  40    HWVoiceIn hw;
  41    FSOUND_SAMPLE *fmod_sample;
  42} FMODVoiceIn;
  43
  44static struct {
  45    const char *drvname;
  46    int nb_samples;
  47    int freq;
  48    int nb_channels;
  49    int bufsize;
  50    int broken_adc;
  51} conf = {
  52    .nb_samples  = 2048 * 2,
  53    .freq        = 44100,
  54    .nb_channels = 2,
  55};
  56
  57static void GCC_FMT_ATTR (1, 2) fmod_logerr (const char *fmt, ...)
  58{
  59    va_list ap;
  60
  61    va_start (ap, fmt);
  62    AUD_vlog (AUDIO_CAP, fmt, ap);
  63    va_end (ap);
  64
  65    AUD_log (AUDIO_CAP, "Reason: %s\n",
  66             FMOD_ErrorString (FSOUND_GetError ()));
  67}
  68
  69static void GCC_FMT_ATTR (2, 3) fmod_logerr2 (
  70    const char *typ,
  71    const char *fmt,
  72    ...
  73    )
  74{
  75    va_list ap;
  76
  77    AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
  78
  79    va_start (ap, fmt);
  80    AUD_vlog (AUDIO_CAP, fmt, ap);
  81    va_end (ap);
  82
  83    AUD_log (AUDIO_CAP, "Reason: %s\n",
  84             FMOD_ErrorString (FSOUND_GetError ()));
  85}
  86
  87static int fmod_write (SWVoiceOut *sw, void *buf, int len)
  88{
  89    return audio_pcm_sw_write (sw, buf, len);
  90}
  91
  92static void fmod_clear_sample (FMODVoiceOut *fmd)
  93{
  94    HWVoiceOut *hw = &fmd->hw;
  95    int status;
  96    void *p1 = 0, *p2 = 0;
  97    unsigned int len1 = 0, len2 = 0;
  98
  99    status = FSOUND_Sample_Lock (
 100        fmd->fmod_sample,
 101        0,
 102        hw->samples << hw->info.shift,
 103        &p1,
 104        &p2,
 105        &len1,
 106        &len2
 107        );
 108
 109    if (!status) {
 110        fmod_logerr ("Failed to lock sample\n");
 111        return;
 112    }
 113
 114    if ((len1 & hw->info.align) || (len2 & hw->info.align)) {
 115        dolog ("Lock returned misaligned length %d, %d, alignment %d\n",
 116               len1, len2, hw->info.align + 1);
 117        goto fail;
 118    }
 119
 120    if ((len1 + len2) - (hw->samples << hw->info.shift)) {
 121        dolog ("Lock returned incomplete length %d, %d\n",
 122               len1 + len2, hw->samples << hw->info.shift);
 123        goto fail;
 124    }
 125
 126    audio_pcm_info_clear_buf (&hw->info, p1, hw->samples);
 127
 128 fail:
 129    status = FSOUND_Sample_Unlock (fmd->fmod_sample, p1, p2, len1, len2);
 130    if (!status) {
 131        fmod_logerr ("Failed to unlock sample\n");
 132    }
 133}
 134
 135static void fmod_write_sample (HWVoiceOut *hw, uint8_t *dst, int dst_len)
 136{
 137    int src_len1 = dst_len;
 138    int src_len2 = 0;
 139    int pos = hw->rpos + dst_len;
 140    struct st_sample *src1 = hw->mix_buf + hw->rpos;
 141    struct st_sample *src2 = NULL;
 142
 143    if (pos > hw->samples) {
 144        src_len1 = hw->samples - hw->rpos;
 145        src2 = hw->mix_buf;
 146        src_len2 = dst_len - src_len1;
 147        pos = src_len2;
 148    }
 149
 150    if (src_len1) {
 151        hw->clip (dst, src1, src_len1);
 152    }
 153
 154    if (src_len2) {
 155        dst = advance (dst, src_len1 << hw->info.shift);
 156        hw->clip (dst, src2, src_len2);
 157    }
 158
 159    hw->rpos = pos % hw->samples;
 160}
 161
 162static int fmod_unlock_sample (FSOUND_SAMPLE *sample, void *p1, void *p2,
 163                               unsigned int blen1, unsigned int blen2)
 164{
 165    int status = FSOUND_Sample_Unlock (sample, p1, p2, blen1, blen2);
 166    if (!status) {
 167        fmod_logerr ("Failed to unlock sample\n");
 168        return -1;
 169    }
 170    return 0;
 171}
 172
 173static int fmod_lock_sample (
 174    FSOUND_SAMPLE *sample,
 175    struct audio_pcm_info *info,
 176    int pos,
 177    int len,
 178    void **p1,
 179    void **p2,
 180    unsigned int *blen1,
 181    unsigned int *blen2
 182    )
 183{
 184    int status;
 185
 186    status = FSOUND_Sample_Lock (
 187        sample,
 188        pos << info->shift,
 189        len << info->shift,
 190        p1,
 191        p2,
 192        blen1,
 193        blen2
 194        );
 195
 196    if (!status) {
 197        fmod_logerr ("Failed to lock sample\n");
 198        return -1;
 199    }
 200
 201    if ((*blen1 & info->align) || (*blen2 & info->align)) {
 202        dolog ("Lock returned misaligned length %d, %d, alignment %d\n",
 203               *blen1, *blen2, info->align + 1);
 204
 205        fmod_unlock_sample (sample, *p1, *p2, *blen1, *blen2);
 206
 207        *p1 = NULL - 1;
 208        *p2 = NULL - 1;
 209        *blen1 = ~0U;
 210        *blen2 = ~0U;
 211        return -1;
 212    }
 213
 214    if (!*p1 && *blen1) {
 215        dolog ("warning: !p1 && blen1=%d\n", *blen1);
 216        *blen1 = 0;
 217    }
 218
 219    if (!p2 && *blen2) {
 220        dolog ("warning: !p2 && blen2=%d\n", *blen2);
 221        *blen2 = 0;
 222    }
 223
 224    return 0;
 225}
 226
 227static int fmod_run_out (HWVoiceOut *hw, int live)
 228{
 229    FMODVoiceOut *fmd = (FMODVoiceOut *) hw;
 230    int decr;
 231    void *p1 = 0, *p2 = 0;
 232    unsigned int blen1 = 0, blen2 = 0;
 233    unsigned int len1 = 0, len2 = 0;
 234
 235    if (!hw->pending_disable) {
 236        return 0;
 237    }
 238
 239    decr = live;
 240
 241    if (fmd->channel >= 0) {
 242        int len = decr;
 243        int old_pos = fmd->old_pos;
 244        int ppos = FSOUND_GetCurrentPosition (fmd->channel);
 245
 246        if (ppos == old_pos || !ppos) {
 247            return 0;
 248        }
 249
 250        if ((old_pos < ppos) && ((old_pos + len) > ppos)) {
 251            len = ppos - old_pos;
 252        }
 253        else {
 254            if ((old_pos > ppos) && ((old_pos + len) > (ppos + hw->samples))) {
 255                len = hw->samples - old_pos + ppos;
 256            }
 257        }
 258        decr = len;
 259
 260        if (audio_bug (AUDIO_FUNC, decr < 0)) {
 261            dolog ("decr=%d live=%d ppos=%d old_pos=%d len=%d\n",
 262                   decr, live, ppos, old_pos, len);
 263            return 0;
 264        }
 265    }
 266
 267
 268    if (!decr) {
 269        return 0;
 270    }
 271
 272    if (fmod_lock_sample (fmd->fmod_sample, &fmd->hw.info,
 273                          fmd->old_pos, decr,
 274                          &p1, &p2,
 275                          &blen1, &blen2)) {
 276        return 0;
 277    }
 278
 279    len1 = blen1 >> hw->info.shift;
 280    len2 = blen2 >> hw->info.shift;
 281    ldebug ("%p %p %d %d %d %d\n", p1, p2, len1, len2, blen1, blen2);
 282    decr = len1 + len2;
 283
 284    if (p1 && len1) {
 285        fmod_write_sample (hw, p1, len1);
 286    }
 287
 288    if (p2 && len2) {
 289        fmod_write_sample (hw, p2, len2);
 290    }
 291
 292    fmod_unlock_sample (fmd->fmod_sample, p1, p2, blen1, blen2);
 293
 294    fmd->old_pos = (fmd->old_pos + decr) % hw->samples;
 295    return decr;
 296}
 297
 298static int aud_to_fmodfmt (audfmt_e fmt, int stereo)
 299{
 300    int mode = FSOUND_LOOP_NORMAL;
 301
 302    switch (fmt) {
 303    case AUD_FMT_S8:
 304        mode |= FSOUND_SIGNED | FSOUND_8BITS;
 305        break;
 306
 307    case AUD_FMT_U8:
 308        mode |= FSOUND_UNSIGNED | FSOUND_8BITS;
 309        break;
 310
 311    case AUD_FMT_S16:
 312        mode |= FSOUND_SIGNED | FSOUND_16BITS;
 313        break;
 314
 315    case AUD_FMT_U16:
 316        mode |= FSOUND_UNSIGNED | FSOUND_16BITS;
 317        break;
 318
 319    default:
 320        dolog ("Internal logic error: Bad audio format %d\n", fmt);
 321#ifdef DEBUG_FMOD
 322        abort ();
 323#endif
 324        mode |= FSOUND_8BITS;
 325    }
 326    mode |= stereo ? FSOUND_STEREO : FSOUND_MONO;
 327    return mode;
 328}
 329
 330static void fmod_fini_out (HWVoiceOut *hw)
 331{
 332    FMODVoiceOut *fmd = (FMODVoiceOut *) hw;
 333
 334    if (fmd->fmod_sample) {
 335        FSOUND_Sample_Free (fmd->fmod_sample);
 336        fmd->fmod_sample = 0;
 337
 338        if (fmd->channel >= 0) {
 339            FSOUND_StopSound (fmd->channel);
 340        }
 341    }
 342}
 343
 344static int fmod_init_out (HWVoiceOut *hw, struct audsettings *as)
 345{
 346    int bits16, mode, channel;
 347    FMODVoiceOut *fmd = (FMODVoiceOut *) hw;
 348    struct audsettings obt_as = *as;
 349
 350    mode = aud_to_fmodfmt (as->fmt, as->nchannels == 2 ? 1 : 0);
 351    fmd->fmod_sample = FSOUND_Sample_Alloc (
 352        FSOUND_FREE,            /* index */
 353        conf.nb_samples,        /* length */
 354        mode,                   /* mode */
 355        as->freq,               /* freq */
 356        255,                    /* volume */
 357        128,                    /* pan */
 358        255                     /* priority */
 359        );
 360
 361    if (!fmd->fmod_sample) {
 362        fmod_logerr2 ("DAC", "Failed to allocate FMOD sample\n");
 363        return -1;
 364    }
 365
 366    channel = FSOUND_PlaySoundEx (FSOUND_FREE, fmd->fmod_sample, 0, 1);
 367    if (channel < 0) {
 368        fmod_logerr2 ("DAC", "Failed to start playing sound\n");
 369        FSOUND_Sample_Free (fmd->fmod_sample);
 370        return -1;
 371    }
 372    fmd->channel = channel;
 373
 374    /* FMOD always operates on little endian frames? */
 375    obt_as.endianness = 0;
 376    audio_pcm_init_info (&hw->info, &obt_as);
 377    bits16 = (mode & FSOUND_16BITS) != 0;
 378    hw->samples = conf.nb_samples;
 379    return 0;
 380}
 381
 382static int fmod_ctl_out (HWVoiceOut *hw, int cmd, ...)
 383{
 384    int status;
 385    FMODVoiceOut *fmd = (FMODVoiceOut *) hw;
 386
 387    switch (cmd) {
 388    case VOICE_ENABLE:
 389        fmod_clear_sample (fmd);
 390        status = FSOUND_SetPaused (fmd->channel, 0);
 391        if (!status) {
 392            fmod_logerr ("Failed to resume channel %d\n", fmd->channel);
 393        }
 394        break;
 395
 396    case VOICE_DISABLE:
 397        status = FSOUND_SetPaused (fmd->channel, 1);
 398        if (!status) {
 399            fmod_logerr ("Failed to pause channel %d\n", fmd->channel);
 400        }
 401        break;
 402    }
 403    return 0;
 404}
 405
 406static int fmod_init_in (HWVoiceIn *hw, struct audsettings *as)
 407{
 408    int bits16, mode;
 409    FMODVoiceIn *fmd = (FMODVoiceIn *) hw;
 410    struct audsettings obt_as = *as;
 411
 412    if (conf.broken_adc) {
 413        return -1;
 414    }
 415
 416    mode = aud_to_fmodfmt (as->fmt, as->nchannels == 2 ? 1 : 0);
 417    fmd->fmod_sample = FSOUND_Sample_Alloc (
 418        FSOUND_FREE,            /* index */
 419        conf.nb_samples,        /* length */
 420        mode,                   /* mode */
 421        as->freq,               /* freq */
 422        255,                    /* volume */
 423        128,                    /* pan */
 424        255                     /* priority */
 425        );
 426
 427    if (!fmd->fmod_sample) {
 428        fmod_logerr2 ("ADC", "Failed to allocate FMOD sample\n");
 429        return -1;
 430    }
 431
 432    /* FMOD always operates on little endian frames? */
 433    obt_as.endianness = 0;
 434    audio_pcm_init_info (&hw->info, &obt_as);
 435    bits16 = (mode & FSOUND_16BITS) != 0;
 436    hw->samples = conf.nb_samples;
 437    return 0;
 438}
 439
 440static void fmod_fini_in (HWVoiceIn *hw)
 441{
 442    FMODVoiceIn *fmd = (FMODVoiceIn *) hw;
 443
 444    if (fmd->fmod_sample) {
 445        FSOUND_Record_Stop ();
 446        FSOUND_Sample_Free (fmd->fmod_sample);
 447        fmd->fmod_sample = 0;
 448    }
 449}
 450
 451static int fmod_run_in (HWVoiceIn *hw)
 452{
 453    FMODVoiceIn *fmd = (FMODVoiceIn *) hw;
 454    int hwshift = hw->info.shift;
 455    int live, dead, new_pos, len;
 456    unsigned int blen1 = 0, blen2 = 0;
 457    unsigned int len1, len2;
 458    unsigned int decr;
 459    void *p1, *p2;
 460
 461    live = audio_pcm_hw_get_live_in (hw);
 462    dead = hw->samples - live;
 463    if (!dead) {
 464        return 0;
 465    }
 466
 467    new_pos = FSOUND_Record_GetPosition ();
 468    if (new_pos < 0) {
 469        fmod_logerr ("Could not get recording position\n");
 470        return 0;
 471    }
 472
 473    len = audio_ring_dist (new_pos,  hw->wpos, hw->samples);
 474    if (!len) {
 475        return 0;
 476    }
 477    len = audio_MIN (len, dead);
 478
 479    if (fmod_lock_sample (fmd->fmod_sample, &fmd->hw.info,
 480                          hw->wpos, len,
 481                          &p1, &p2,
 482                          &blen1, &blen2)) {
 483        return 0;
 484    }
 485
 486    len1 = blen1 >> hwshift;
 487    len2 = blen2 >> hwshift;
 488    decr = len1 + len2;
 489
 490    if (p1 && blen1) {
 491        hw->conv (hw->conv_buf + hw->wpos, p1, len1);
 492    }
 493    if (p2 && len2) {
 494        hw->conv (hw->conv_buf, p2, len2);
 495    }
 496
 497    fmod_unlock_sample (fmd->fmod_sample, p1, p2, blen1, blen2);
 498    hw->wpos = (hw->wpos + decr) % hw->samples;
 499    return decr;
 500}
 501
 502static struct {
 503    const char *name;
 504    int type;
 505} drvtab[] = {
 506    { .name = "none",   .type = FSOUND_OUTPUT_NOSOUND },
 507#ifdef _WIN32
 508    { .name = "winmm",  .type = FSOUND_OUTPUT_WINMM   },
 509    { .name = "dsound", .type = FSOUND_OUTPUT_DSOUND  },
 510    { .name = "a3d",    .type = FSOUND_OUTPUT_A3D     },
 511    { .name = "asio",   .type = FSOUND_OUTPUT_ASIO    },
 512#endif
 513#ifdef __linux__
 514    { .name = "oss",    .type = FSOUND_OUTPUT_OSS     },
 515    { .name = "alsa",   .type = FSOUND_OUTPUT_ALSA    },
 516    { .name = "esd",    .type = FSOUND_OUTPUT_ESD     },
 517#endif
 518#ifdef __APPLE__
 519    { .name = "mac",    .type = FSOUND_OUTPUT_MAC     },
 520#endif
 521#if 0
 522    { .name = "xbox",   .type = FSOUND_OUTPUT_XBOX    },
 523    { .name = "ps2",    .type = FSOUND_OUTPUT_PS2     },
 524    { .name = "gcube",  .type = FSOUND_OUTPUT_GC      },
 525#endif
 526    { .name = "none-realtime", .type = FSOUND_OUTPUT_NOSOUND_NONREALTIME }
 527};
 528
 529static void *fmod_audio_init (void)
 530{
 531    size_t i;
 532    double ver;
 533    int status;
 534    int output_type = -1;
 535    const char *drv = conf.drvname;
 536
 537    ver = FSOUND_GetVersion ();
 538    if (ver < FMOD_VERSION) {
 539        dolog ("Wrong FMOD version %f, need at least %f\n", ver, FMOD_VERSION);
 540        return NULL;
 541    }
 542
 543#ifdef __linux__
 544    if (ver < 3.75) {
 545        dolog ("FMOD before 3.75 has bug preventing ADC from working\n"
 546               "ADC will be disabled.\n");
 547        conf.broken_adc = 1;
 548    }
 549#endif
 550
 551    if (drv) {
 552        int found = 0;
 553        for (i = 0; i < ARRAY_SIZE (drvtab); i++) {
 554            if (!strcmp (drv, drvtab[i].name)) {
 555                output_type = drvtab[i].type;
 556                found = 1;
 557                break;
 558            }
 559        }
 560        if (!found) {
 561            dolog ("Unknown FMOD driver `%s'\n", drv);
 562            dolog ("Valid drivers:\n");
 563            for (i = 0; i < ARRAY_SIZE (drvtab); i++) {
 564                dolog ("  %s\n", drvtab[i].name);
 565            }
 566        }
 567    }
 568
 569    if (output_type != -1) {
 570        status = FSOUND_SetOutput (output_type);
 571        if (!status) {
 572            fmod_logerr ("FSOUND_SetOutput(%d) failed\n", output_type);
 573            return NULL;
 574        }
 575    }
 576
 577    if (conf.bufsize) {
 578        status = FSOUND_SetBufferSize (conf.bufsize);
 579        if (!status) {
 580            fmod_logerr ("FSOUND_SetBufferSize (%d) failed\n", conf.bufsize);
 581        }
 582    }
 583
 584    status = FSOUND_Init (conf.freq, conf.nb_channels, 0);
 585    if (!status) {
 586        fmod_logerr ("FSOUND_Init failed\n");
 587        return NULL;
 588    }
 589
 590    return &conf;
 591}
 592
 593static int fmod_read (SWVoiceIn *sw, void *buf, int size)
 594{
 595    return audio_pcm_sw_read (sw, buf, size);
 596}
 597
 598static int fmod_ctl_in (HWVoiceIn *hw, int cmd, ...)
 599{
 600    int status;
 601    FMODVoiceIn *fmd = (FMODVoiceIn *) hw;
 602
 603    switch (cmd) {
 604    case VOICE_ENABLE:
 605        status = FSOUND_Record_StartSample (fmd->fmod_sample, 1);
 606        if (!status) {
 607            fmod_logerr ("Failed to start recording\n");
 608        }
 609        break;
 610
 611    case VOICE_DISABLE:
 612        status = FSOUND_Record_Stop ();
 613        if (!status) {
 614            fmod_logerr ("Failed to stop recording\n");
 615        }
 616        break;
 617    }
 618    return 0;
 619}
 620
 621static void fmod_audio_fini (void *opaque)
 622{
 623    (void) opaque;
 624    FSOUND_Close ();
 625}
 626
 627static struct audio_option fmod_options[] = {
 628    {
 629        .name  = "DRV",
 630        .tag   = AUD_OPT_STR,
 631        .valp  = &conf.drvname,
 632        .descr = "FMOD driver"
 633    },
 634    {
 635        .name  = "FREQ",
 636        .tag   = AUD_OPT_INT,
 637        .valp  = &conf.freq,
 638        .descr = "Default frequency"
 639    },
 640    {
 641        .name  = "SAMPLES",
 642        .tag   = AUD_OPT_INT,
 643        .valp  = &conf.nb_samples,
 644        .descr = "Buffer size in samples"
 645    },
 646    {
 647        .name  = "CHANNELS",
 648        .tag   = AUD_OPT_INT,
 649        .valp  = &conf.nb_channels,
 650        .descr = "Number of default channels (1 - mono, 2 - stereo)"
 651    },
 652    {
 653        .name  = "BUFSIZE",
 654        .tag   = AUD_OPT_INT,
 655        .valp  = &conf.bufsize,
 656        .descr = "(undocumented)"
 657    },
 658    { /* End of list */ }
 659};
 660
 661static struct audio_pcm_ops fmod_pcm_ops = {
 662    .init_out = fmod_init_out,
 663    .fini_out = fmod_fini_out,
 664    .run_out  = fmod_run_out,
 665    .write    = fmod_write,
 666    .ctl_out  = fmod_ctl_out,
 667
 668    .init_in  = fmod_init_in,
 669    .fini_in  = fmod_fini_in,
 670    .run_in   = fmod_run_in,
 671    .read     = fmod_read,
 672    .ctl_in   = fmod_ctl_in
 673};
 674
 675struct audio_driver fmod_audio_driver = {
 676    .name           = "fmod",
 677    .descr          = "FMOD 3.xx http://www.fmod.org",
 678    .options        = fmod_options,
 679    .init           = fmod_audio_init,
 680    .fini           = fmod_audio_fini,
 681    .pcm_ops        = &fmod_pcm_ops,
 682    .can_be_default = 1,
 683    .max_voices_out = INT_MAX,
 684    .max_voices_in  = INT_MAX,
 685    .voice_size_out = sizeof (FMODVoiceOut),
 686    .voice_size_in  = sizeof (FMODVoiceIn)
 687};
 688