(linenum→info "unix/slp.c:2238")

qemu/0.9.1/audio/fmodaudio.c

    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: 
   32: typedef struct FMODVoiceOut {
   33:     HWVoiceOut hw;
   34:     unsigned int old_pos;
   35:     FSOUND_SAMPLE *fmod_sample;
   36:     int channel;
   37: } FMODVoiceOut;
   38: 
   39: typedef struct FMODVoiceIn {
   40:     HWVoiceIn hw;
   41:     FSOUND_SAMPLE *fmod_sample;
   42: } FMODVoiceIn;
   43: 
   44: static struct {
   45:     const char *drvname;
   46:     int nb_samples;
   47:     int freq;
   48:     int nb_channels;
   49:     int bufsize;
   50:     int threshold;
   51:     int broken_adc;
   52: } conf = {
   53:     NULL,
   54:     2048 * 2,
   55:     44100,
   56:     2,
   57:     0,
   58:     0,
   59:     0
   60: };
   61: 
   62: static void GCC_FMT_ATTR (1, 2) fmod_logerr (const char *fmt, ...)
   63: {
   64:     va_list ap;
   65: 
   66:     va_start (ap, fmt);
   67:     AUD_vlog (AUDIO_CAP, fmt, ap);
   68:     va_end (ap);
   69: 
   70:     AUD_log (AUDIO_CAP, "Reason: %s\n",
   71:              FMOD_ErrorString (FSOUND_GetError ()));
   72: }
   73: 
   74: static void GCC_FMT_ATTR (2, 3) fmod_logerr2 (
   75:     const char *typ,
   76:     const char *fmt,
   77:     ...
   78:     )
   79: {
   80:     va_list ap;
   81: 
   82:     AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
   83: 
   84:     va_start (ap, fmt);
   85:     AUD_vlog (AUDIO_CAP, fmt, ap);
   86:     va_end (ap);
   87: 
   88:     AUD_log (AUDIO_CAP, "Reason: %s\n",
   89:              FMOD_ErrorString (FSOUND_GetError ()));
   90: }
   91: 
   92: static int fmod_write (SWVoiceOut *sw, void *buf, int len)
   93: {
   94:     return audio_pcm_sw_write (sw, buf, len);
   95: }
   96: 
   97: static void fmod_clear_sample (FMODVoiceOut *fmd)
   98: {
   99:     HWVoiceOut *hw = &fmd->hw;
  100:     int status;
  101:     void *p1 = 0, *p2 = 0;
  102:     unsigned int len1 = 0, len2 = 0;
  103: 
  104:     status = FSOUND_Sample_Lock (
  105:         fmd->fmod_sample,
  106:         0,
  107:         hw->samples << hw->info.shift,
  108:         &p1,
  109:         &p2,
  110:         &len1,
  111:         &len2
  112:         );
  113: 
  114:     if (!status) {
  115:         fmod_logerr ("Failed to lock sample\n");
  116:         return;
  117:     }
  118: 
  119:     if ((len1 & hw->info.align) || (len2 & hw->info.align)) {
  120:         dolog ("Lock returned misaligned length %d, %d, alignment %d\n",
  121:                len1, len2, hw->info.align + 1);
  122:         goto fail;
  123:     }
  124: 
  125:     if ((len1 + len2) - (hw->samples << hw->info.shift)) {
  126:         dolog ("Lock returned incomplete length %d, %d\n",
  127:                len1 + len2, hw->samples << hw->info.shift);
  128:         goto fail;
  129:     }
  130: 
  131:     audio_pcm_info_clear_buf (&hw->info, p1, hw->samples);
  132: 
  133:  fail:
  134:     status = FSOUND_Sample_Unlock (fmd->fmod_sample, p1, p2, len1, len2);
  135:     if (!status) {
  136:         fmod_logerr ("Failed to unlock sample\n");
  137:     }
  138: }
  139: 
  140: static void fmod_write_sample (HWVoiceOut *hw, uint8_t *dst, int dst_len)
  141: {
  142:     int src_len1 = dst_len;
  143:     int src_len2 = 0;
  144:     int pos = hw->rpos + dst_len;
  145:     st_sample_t *src1 = hw->mix_buf + hw->rpos;
  146:     st_sample_t *src2 = NULL;
  147: 
  148:     if (pos > hw->samples) {
  149:         src_len1 = hw->samples - hw->rpos;
  150:         src2 = hw->mix_buf;
  151:         src_len2 = dst_len - src_len1;
  152:         pos = src_len2;
  153:     }
  154: 
  155:     if (src_len1) {
  156:         hw->clip (dst, 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:     }
  163: 
  164:     hw->rpos = pos % hw->samples;
  165: }
  166: 
  167: static int fmod_unlock_sample (FSOUND_SAMPLE *sample, void *p1, void *p2,
  168:                                unsigned int blen1, unsigned int blen2)
  169: {
  170:     int status = FSOUND_Sample_Unlock (sample, p1, p2, blen1, blen2);
  171:     if (!status) {
  172:         fmod_logerr ("Failed to unlock sample\n");
  173:         return -1;
  174:     }
  175:     return 0;
  176: }
  177: 
  178: static int fmod_lock_sample (
  179:     FSOUND_SAMPLE *sample,
  180:     struct audio_pcm_info *info,
  181:     int pos,
  182:     int len,
  183:     void **p1,
  184:     void **p2,
  185:     unsigned int *blen1,
  186:     unsigned int *blen2
  187:     )
  188: {
  189:     int status;
  190: 
  191:     status = FSOUND_Sample_Lock (
  192:         sample,
  193:         pos << info->shift,
  194:         len << info->shift,
  195:         p1,
  196:         p2,
  197:         blen1,
  198:         blen2
  199:         );
  200: 
  201:     if (!status) {
  202:         fmod_logerr ("Failed to lock sample\n");
  203:         return -1;
  204:     }
  205: 
  206:     if ((*blen1 & info->align) || (*blen2 & info->align)) {
  207:         dolog ("Lock returned misaligned length %d, %d, alignment %d\n",
  208:                *blen1, *blen2, info->align + 1);
  209: 
  210:         fmod_unlock_sample (sample, *p1, *p2, *blen1, *blen2);
  211: 
  212:         *p1 = NULL - 1;
  213:         *p2 = NULL - 1;
  214:         *blen1 = ~0U;
  215:         *blen2 = ~0U;
  216:         return -1;
  217:     }
  218: 
  219:     if (!*p1 && *blen1) {
  220:         dolog ("warning: !p1 && blen1=%d\n", *blen1);
  221:         *blen1 = 0;
  222:     }
  223: 
  224:     if (!p2 && *blen2) {
  225:         dolog ("warning: !p2 && blen2=%d\n", *blen2);
  226:         *blen2 = 0;
  227:     }
  228: 
  229:     return 0;
  230: }
  231: 
  232: static int fmod_run_out (HWVoiceOut *hw)
  233: {
  234:     FMODVoiceOut *fmd = (FMODVoiceOut *) hw;
  235:     int live, decr;
  236:     void *p1 = 0, *p2 = 0;
  237:     unsigned int blen1 = 0, blen2 = 0;
  238:     unsigned int len1 = 0, len2 = 0;
  239:     int nb_live;
  240: 
  241:     live = audio_pcm_hw_get_live_out2 (hw, &nb_live);
  242:     if (!live) {
  243:         return 0;
  244:     }
  245: 
  246:     if (!hw->pending_disable
  247:         && nb_live
  248:         && (conf.threshold && live <= conf.threshold)) {
  249:         ldebug ("live=%d nb_live=%d\n", live, nb_live);
  250:         return 0;
  251:     }
  252: 
  253:     decr = live;
  254: 
  255:     if (fmd->channel >= 0) {
  256:         int len = decr;
  257:         int old_pos = fmd->old_pos;
  258:         int ppos = FSOUND_GetCurrentPosition (fmd->channel);
  259: 
  260:         if (ppos == old_pos || !ppos) {
  261:             return 0;
  262:         }
  263: 
  264:         if ((old_pos < ppos) && ((old_pos + len) > ppos)) {
  265:             len = ppos - old_pos;
  266:         }
  267:         else {
  268:             if ((old_pos > ppos) && ((old_pos + len) > (ppos + hw->samples))) {
  269:                 len = hw->samples - old_pos + ppos;
  270:             }
  271:         }
  272:         decr = len;
  273: 
  274:         if (audio_bug (AUDIO_FUNC, decr < 0)) {
  275:             dolog ("decr=%d live=%d ppos=%d old_pos=%d len=%d\n",
  276:                    decr, live, ppos, old_pos, len);
  277:             return 0;
  278:         }
  279:     }
  280: 
  281: 
  282:     if (!decr) {
  283:         return 0;
  284:     }
  285: 
  286:     if (fmod_lock_sample (fmd->fmod_sample, &fmd->hw.info,
  287:                           fmd->old_pos, decr,
  288:                           &p1, &p2,
  289:                           &blen1, &blen2)) {
  290:         return 0;
  291:     }
  292: 
  293:     len1 = blen1 >> hw->info.shift;
  294:     len2 = blen2 >> hw->info.shift;
  295:     ldebug ("%p %p %d %d %d %d\n", p1, p2, len1, len2, blen1, blen2);
  296:     decr = len1 + len2;
  297: 
  298:     if (p1 && len1) {
  299:         fmod_write_sample (hw, p1, len1);
  300:     }
  301: 
  302:     if (p2 && len2) {
  303:         fmod_write_sample (hw, p2, len2);
  304:     }
  305: 
  306:     fmod_unlock_sample (fmd->fmod_sample, p1, p2, blen1, blen2);
  307: 
  308:     fmd->old_pos = (fmd->old_pos + decr) % hw->samples;
  309:     return decr;
  310: }
  311: 
  312: static int aud_to_fmodfmt (audfmt_e fmt, int stereo)
  313: {
  314:     int mode = FSOUND_LOOP_NORMAL;
  315: 
  316:     switch (fmt) {
  317:     case AUD_FMT_S8:
  318:         mode |= FSOUND_SIGNED | FSOUND_8BITS;
  319:         break;
  320: 
  321:     case AUD_FMT_U8:
  322:         mode |= FSOUND_UNSIGNED | FSOUND_8BITS;
  323:         break;
  324: 
  325:     case AUD_FMT_S16:
  326:         mode |= FSOUND_SIGNED | FSOUND_16BITS;
  327:         break;
  328: 
  329:     case AUD_FMT_U16:
  330:         mode |= FSOUND_UNSIGNED | FSOUND_16BITS;
  331:         break;
  332: 
  333:     default:
  334:         dolog ("Internal logic error: Bad audio format %d\n", fmt);
  335: #ifdef DEBUG_FMOD
  336:         abort ();
  337: #endif
  338:         mode |= FSOUND_8BITS;
  339:     }
  340:     mode |= stereo ? FSOUND_STEREO : FSOUND_MONO;
  341:     return mode;
  342: }
  343: 
  344: static void fmod_fini_out (HWVoiceOut *hw)
  345: {
  346:     FMODVoiceOut *fmd = (FMODVoiceOut *) hw;
  347: 
  348:     if (fmd->fmod_sample) {
  349:         FSOUND_Sample_Free (fmd->fmod_sample);
  350:         fmd->fmod_sample = 0;
  351: 
  352:         if (fmd->channel >= 0) {
  353:             FSOUND_StopSound (fmd->channel);
  354:         }
  355:     }
  356: }
  357: 
  358: static int fmod_init_out (HWVoiceOut *hw, audsettings_t *as)
  359: {
  360:     int bits16, mode, channel;
  361:     FMODVoiceOut *fmd = (FMODVoiceOut *) hw;
  362:     audsettings_t obt_as = *as;
  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:     obt_as.endianness = 0;
  390:     audio_pcm_init_info (&hw->info, &obt_as);
  391:     bits16 = (mode & FSOUND_16BITS) != 0;
  392:     hw->samples = conf.nb_samples;
  393:     return 0;
  394: }
  395: 
  396: static int fmod_ctl_out (HWVoiceOut *hw, int cmd, ...)
  397: {
  398:     int status;
  399:     FMODVoiceOut *fmd = (FMODVoiceOut *) hw;
  400: 
  401:     switch (cmd) {
  402:     case VOICE_ENABLE:
  403:         fmod_clear_sample (fmd);
  404:         status = FSOUND_SetPaused (fmd->channel, 0);
  405:         if (!status) {
  406:             fmod_logerr ("Failed to resume channel %d\n", fmd->channel);
  407:         }
  408:         break;
  409: 
  410:     case VOICE_DISABLE:
  411:         status = FSOUND_SetPaused (fmd->channel, 1);
  412:         if (!status) {
  413:             fmod_logerr ("Failed to pause channel %d\n", fmd->channel);
  414:         }
  415:         break;
  416:     }
  417:     return 0;
  418: }
  419: 
  420: static int fmod_init_in (HWVoiceIn *hw, audsettings_t *as)
  421: {
  422:     int bits16, mode;
  423:     FMODVoiceIn *fmd = (FMODVoiceIn *) hw;
  424:     audsettings_t obt_as = *as;
  425: 
  426:     if (conf.broken_adc) {
  427:         return -1;
  428:     }
  429: 
  430:     mode = aud_to_fmodfmt (as->fmt, as->nchannels == 2 ? 1 : 0);
  431:     fmd->fmod_sample = FSOUND_Sample_Alloc (
  432:         FSOUND_FREE,            /* index */
  433:         conf.nb_samples,        /* length */
  434:         mode,                   /* mode */
  435:         as->freq,               /* freq */
  436:         255,                    /* volume */
  437:         128,                    /* pan */
  438:         255                     /* priority */
  439:         );
  440: 
  441:     if (!fmd->fmod_sample) {
  442:         fmod_logerr2 ("ADC", "Failed to allocate FMOD sample\n");
  443:         return -1;
  444:     }
  445: 
  446:     /* FMOD always operates on little endian frames? */
  447:     obt_as.endianness = 0;
  448:     audio_pcm_init_info (&hw->info, &obt_as);
  449:     bits16 = (mode & FSOUND_16BITS) != 0;
  450:     hw->samples = conf.nb_samples;
  451:     return 0;
  452: }
  453: 
  454: static void fmod_fini_in (HWVoiceIn *hw)
  455: {
  456:     FMODVoiceIn *fmd = (FMODVoiceIn *) hw;
  457: 
  458:     if (fmd->fmod_sample) {
  459:         FSOUND_Record_Stop ();
  460:         FSOUND_Sample_Free (fmd->fmod_sample);
  461:         fmd->fmod_sample = 0;
  462:     }
  463: }
  464: 
  465: static int fmod_run_in (HWVoiceIn *hw)
  466: {
  467:     FMODVoiceIn *fmd = (FMODVoiceIn *) hw;
  468:     int hwshift = hw->info.shift;
  469:     int live, dead, new_pos, len;
  470:     unsigned int blen1 = 0, blen2 = 0;
  471:     unsigned int len1, len2;
  472:     unsigned int decr;
  473:     void *p1, *p2;
  474: 
  475:     live = audio_pcm_hw_get_live_in (hw);
  476:     dead = hw->samples - live;
  477:     if (!dead) {
  478:         return 0;
  479:     }
  480: 
  481:     new_pos = FSOUND_Record_GetPosition ();
  482:     if (new_pos < 0) {
  483:         fmod_logerr ("Could not get recording position\n");
  484:         return 0;
  485:     }
  486: 
  487:     len = audio_ring_dist (new_pos,  hw->wpos, hw->samples);
  488:     if (!len) {
  489:         return 0;
  490:     }
  491:     len = audio_MIN (len, dead);
  492: 
  493:     if (fmod_lock_sample (fmd->fmod_sample, &fmd->hw.info,
  494:                           hw->wpos, len,
  495:                           &p1, &p2,
  496:                           &blen1, &blen2)) {
  497:         return 0;
  498:     }
  499: 
  500:     len1 = blen1 >> hwshift;
  501:     len2 = blen2 >> hwshift;
  502:     decr = len1 + len2;
  503: 
  504:     if (p1 && blen1) {
  505:         hw->conv (hw->conv_buf + hw->wpos, p1, len1, &nominal_volume);
  506:     }
  507:     if (p2 && len2) {
  508:         hw->conv (hw->conv_buf, p2, len2, &nominal_volume);
  509:     }
  510: 
  511:     fmod_unlock_sample (fmd->fmod_sample, p1, p2, blen1, blen2);
  512:     hw->wpos = (hw->wpos + decr) % hw->samples;
  513:     return decr;
  514: }
  515: 
  516: static struct {
  517:     const char *name;
  518:     int type;
  519: } drvtab[] = {
  520:     {"none", FSOUND_OUTPUT_NOSOUND},
  521: #ifdef _WIN32
  522:     {"winmm", FSOUND_OUTPUT_WINMM},
  523:     {"dsound", FSOUND_OUTPUT_DSOUND},
  524:     {"a3d", FSOUND_OUTPUT_A3D},
  525:     {"asio", FSOUND_OUTPUT_ASIO},
  526: #endif
  527: #ifdef __linux__
  528:     {"oss", FSOUND_OUTPUT_OSS},
  529:     {"alsa", FSOUND_OUTPUT_ALSA},
  530:     {"esd", FSOUND_OUTPUT_ESD},
  531: #endif
  532: #ifdef __APPLE__
  533:     {"mac", FSOUND_OUTPUT_MAC},
  534: #endif
  535: #if 0
  536:     {"xbox", FSOUND_OUTPUT_XBOX},
  537:     {"ps2", FSOUND_OUTPUT_PS2},
  538:     {"gcube", FSOUND_OUTPUT_GC},
  539: #endif
  540:     {"none-realtime", FSOUND_OUTPUT_NOSOUND_NONREALTIME}
  541: };
  542: 
  543: static void *fmod_audio_init (void)
  544: {
  545:     size_t i;
  546:     double ver;
  547:     int status;
  548:     int output_type = -1;
  549:     const char *drv = conf.drvname;
  550: 
  551:     ver = FSOUND_GetVersion ();
  552:     if (ver < FMOD_VERSION) {
  553:         dolog ("Wrong FMOD version %f, need at least %f\n", ver, FMOD_VERSION);
  554:         return NULL;
  555:     }
  556: 
  557: #ifdef __linux__
  558:     if (ver < 3.75) {
  559:         dolog ("FMOD before 3.75 has bug preventing ADC from working\n"
  560:                "ADC will be disabled.\n");
  561:         conf.broken_adc = 1;
  562:     }
  563: #endif
  564: 
  565:     if (drv) {
  566:         int found = 0;
  567:         for (i = 0; i < sizeof (drvtab) / sizeof (drvtab[0]); i++) {
  568:             if (!strcmp (drv, drvtab[i].name)) {
  569:                 output_type = drvtab[i].type;
  570:                 found = 1;
  571:                 break;
  572:             }
  573:         }
  574:         if (!found) {
  575:             dolog ("Unknown FMOD driver `%s'\n", drv);
  576:             dolog ("Valid drivers:\n");
  577:             for (i = 0; i < sizeof (drvtab) / sizeof (drvtab[0]); i++) {
  578:                 dolog ("  %s\n", drvtab[i].name);
  579:             }
  580:         }
  581:     }
  582: 
  583:     if (output_type != -1) {
  584:         status = FSOUND_SetOutput (output_type);
  585:         if (!status) {
  586:             fmod_logerr ("FSOUND_SetOutput(%d) failed\n", output_type);
  587:             return NULL;
  588:         }
  589:     }
  590: 
  591:     if (conf.bufsize) {
  592: