Annotation of qemu/audio/fmodaudio.c, revision 1.1.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.