Home | History | Annotate | Download | only in audio
      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 broken_adc;
     51 } conf = {
     52     .nb_samples  = 2048 * 2,
     53     .freq        = 44100,
     54     .nb_channels = 2,
     55 };
     56 
     57 static 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 
     69 static 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 
     87 static int fmod_write (SWVoiceOut *sw, void *buf, int len)
     88 {
     89     return audio_pcm_sw_write (sw, buf, len);
     90 }
     91 
     92 static 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 
    135 static 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 
    162 static 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 
    173 static 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 
    227 static 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 
    298 static 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 
    330 static 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 
    344 static 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 
    382 static 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 
    406 static 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 
    440 static 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 
    451 static 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, &nominal_volume);
    492     }
    493     if (p2 && len2) {
    494         hw->conv (hw->conv_buf, p2, len2, &nominal_volume);
    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 
    502 static 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 
    529 static 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 
    593 static int fmod_read (SWVoiceIn *sw, void *buf, int size)
    594 {
    595     return audio_pcm_sw_read (sw, buf, size);
    596 }
    597 
    598 static 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 
    621 static void fmod_audio_fini (void *opaque)
    622 {
    623     (void) opaque;
    624     FSOUND_Close ();
    625 }
    626 
    627 static 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 
    661 static 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 
    675 struct 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