Annotation of qemu/audio/fmodaudio.c, revision 1.1

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

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.