Home | History | Annotate | Download | only in lib_src
      1 /*----------------------------------------------------------------------------
      2  *
      3  * File:
      4  * eas_smf.c
      5  *
      6  * Contents and purpose:
      7  * SMF Type 0 and 1 File Parser
      8  *
      9  * For SMF timebase analysis, see "MIDI Sequencer Analysis.xls".
     10  *
     11  * Copyright Sonic Network Inc. 2005
     12 
     13  * Licensed under the Apache License, Version 2.0 (the "License");
     14  * you may not use this file except in compliance with the License.
     15  * You may obtain a copy of the License at
     16  *
     17  *      http://www.apache.org/licenses/LICENSE-2.0
     18  *
     19  * Unless required by applicable law or agreed to in writing, software
     20  * distributed under the License is distributed on an "AS IS" BASIS,
     21  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     22  * See the License for the specific language governing permissions and
     23  * limitations under the License.
     24  *
     25  *----------------------------------------------------------------------------
     26  * Revision Control:
     27  *   $Revision: 803 $
     28  *   $Date: 2007-08-01 09:57:00 -0700 (Wed, 01 Aug 2007) $
     29  *----------------------------------------------------------------------------
     30 */
     31 
     32 #define LOG_TAG "Sonivox"
     33 #include "log/log.h"
     34 
     35 #include "eas_data.h"
     36 #include "eas_miditypes.h"
     37 #include "eas_parser.h"
     38 #include "eas_report.h"
     39 #include "eas_host.h"
     40 #include "eas_midi.h"
     41 #include "eas_config.h"
     42 #include "eas_vm_protos.h"
     43 #include "eas_smfdata.h"
     44 #include "eas_smf.h"
     45 
     46 #ifdef JET_INTERFACE
     47 #include "jet_data.h"
     48 #endif
     49 
     50 //3 dls: The timebase for this module is adequate to keep MIDI and
     51 //3 digital audio synchronized for only a few minutes. It should be
     52 //3 sufficient for most mobile applications. If better accuracy is
     53 //3 required, more fractional bits should be added to the timebase.
     54 
     55 static const EAS_U8 smfHeader[] = { 'M', 'T', 'h', 'd' };
     56 
     57 /* local prototypes */
     58 static EAS_RESULT SMF_GetVarLenData (EAS_HW_DATA_HANDLE hwInstData, EAS_FILE_HANDLE fileHandle, EAS_U32 *pData);
     59 static EAS_RESULT SMF_ParseMetaEvent (S_EAS_DATA *pEASData, S_SMF_DATA *pSMFData, S_SMF_STREAM *pSMFStream);
     60 static EAS_RESULT SMF_ParseSysEx (S_EAS_DATA *pEASData, S_SMF_DATA *pSMFData, S_SMF_STREAM *pSMFStream, EAS_U8 f0, EAS_INT parserMode);
     61 static EAS_RESULT SMF_ParseEvent (S_EAS_DATA *pEASData, S_SMF_DATA *pSMFData, S_SMF_STREAM *pSMFStream, EAS_INT parserMode);
     62 static EAS_RESULT SMF_GetDeltaTime (EAS_HW_DATA_HANDLE hwInstData, S_SMF_STREAM *pSMFStream);
     63 static void SMF_UpdateTime (S_SMF_DATA *pSMFData, EAS_U32 ticks);
     64 
     65 
     66 /*----------------------------------------------------------------------------
     67  *
     68  * SMF_Parser
     69  *
     70  * This structure contains the functional interface for the SMF parser
     71  *----------------------------------------------------------------------------
     72 */
     73 const S_FILE_PARSER_INTERFACE EAS_SMF_Parser =
     74 {
     75     SMF_CheckFileType,
     76     SMF_Prepare,
     77     SMF_Time,
     78     SMF_Event,
     79     SMF_State,
     80     SMF_Close,
     81     SMF_Reset,
     82     SMF_Pause,
     83     SMF_Resume,
     84     NULL,
     85     SMF_SetData,
     86     SMF_GetData,
     87     NULL
     88 };
     89 
     90 /*----------------------------------------------------------------------------
     91  * SMF_CheckFileType()
     92  *----------------------------------------------------------------------------
     93  * Purpose:
     94  * Check the file type to see if we can parse it
     95  *
     96  * Inputs:
     97  * pEASData         - pointer to overall EAS data structure
     98  * handle           - pointer to file handle
     99  *
    100  * Outputs:
    101  *
    102  *
    103  * Side Effects:
    104  *
    105  *----------------------------------------------------------------------------
    106 */
    107 EAS_RESULT SMF_CheckFileType (S_EAS_DATA *pEASData, EAS_FILE_HANDLE fileHandle, EAS_VOID_PTR *ppHandle, EAS_I32 offset)
    108 {
    109     S_SMF_DATA* pSMFData;
    110     EAS_RESULT result;
    111 
    112     /* seek to starting offset - usually 0 */
    113     *ppHandle = NULL;
    114     if ((result = EAS_HWFileSeek(pEASData->hwInstData, fileHandle, offset)) != EAS_SUCCESS)
    115         return result;
    116 
    117     /* search through file for header - slow method */
    118     if (pEASData->searchHeaderFlag)
    119     {
    120         result = EAS_SearchFile(pEASData, fileHandle, smfHeader, sizeof(smfHeader), &offset);
    121         if (result != EAS_SUCCESS)
    122             return (result == EAS_EOF) ? EAS_SUCCESS : result;
    123     }
    124 
    125     /* read the first 4 bytes of the file - quick method */
    126     else {
    127         EAS_U8 header[4];
    128         EAS_I32 count;
    129         if ((result = EAS_HWReadFile(pEASData->hwInstData, fileHandle, header, sizeof(header), &count)) != EAS_SUCCESS)
    130             return result;
    131 
    132         /* check for 'MThd' - If no match then return SUCCESS with NULL handle
    133          * to indicate not an SMF file. */
    134         if ((header[0] != 'M') || (header[1] != 'T') || (header[2] != 'h') || (header[3] != 'd'))
    135             return EAS_SUCCESS;
    136     }
    137 
    138     /* check for static memory allocation */
    139     if (pEASData->staticMemoryModel)
    140         pSMFData = EAS_CMEnumData(EAS_CM_SMF_DATA);
    141     else
    142     {
    143         pSMFData = EAS_HWMalloc(pEASData->hwInstData, sizeof(S_SMF_DATA));
    144         EAS_HWMemSet((void *)pSMFData,0, sizeof(S_SMF_DATA));
    145     }
    146     if (!pSMFData)
    147         return EAS_ERROR_MALLOC_FAILED;
    148 
    149     /* initialize some critical data */
    150     pSMFData->fileHandle = fileHandle;
    151     pSMFData->fileOffset = offset;
    152     pSMFData->pSynth = NULL;
    153     pSMFData->time = 0;
    154     pSMFData->state = EAS_STATE_OPEN;
    155     *ppHandle = pSMFData;
    156 
    157     return EAS_SUCCESS;
    158 }
    159 
    160 /*----------------------------------------------------------------------------
    161  * SMF_Prepare()
    162  *----------------------------------------------------------------------------
    163  * Purpose:
    164  * Prepare to parse the file. Allocates instance data (or uses static allocation for
    165  * static memory model).
    166  *
    167  * Inputs:
    168  * pEASData         - pointer to overall EAS data structure
    169  * handle           - pointer to file handle
    170  *
    171  * Outputs:
    172  *
    173  *
    174  * Side Effects:
    175  *
    176  *----------------------------------------------------------------------------
    177 */
    178 EAS_RESULT SMF_Prepare (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
    179 {
    180     S_SMF_DATA* pSMFData;
    181     EAS_RESULT result;
    182 
    183     /* check for valid state */
    184     pSMFData = (S_SMF_DATA *) pInstData;
    185     if (pSMFData->state != EAS_STATE_OPEN)
    186         return EAS_ERROR_NOT_VALID_IN_THIS_STATE;
    187 
    188     /* instantiate a synthesizer */
    189     if ((result = VMInitMIDI(pEASData, &pSMFData->pSynth)) != EAS_SUCCESS)
    190     {
    191         { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "VMInitMIDI returned %d\n", result); */ }
    192         return result;
    193     }
    194 
    195     /* parse the file header and setup the individual stream parsers */
    196     if ((result = SMF_ParseHeader(pEASData->hwInstData, pSMFData)) != EAS_SUCCESS)
    197         return result;
    198 
    199     /* ready to play */
    200     pSMFData->state = EAS_STATE_READY;
    201     return EAS_SUCCESS;
    202 }
    203 
    204 /*----------------------------------------------------------------------------
    205  * SMF_Time()
    206  *----------------------------------------------------------------------------
    207  * Purpose:
    208  * Returns the time of the next event in msecs
    209  *
    210  * Inputs:
    211  * pEASData         - pointer to overall EAS data structure
    212  * handle           - pointer to file handle
    213  * pTime            - pointer to variable to hold time of next event (in msecs)
    214  *
    215  * Outputs:
    216  *
    217  *
    218  * Side Effects:
    219  *
    220  *----------------------------------------------------------------------------
    221 */
    222 /*lint -esym(715, pEASData) reserved for future use */
    223 EAS_RESULT SMF_Time (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_U32 *pTime)
    224 {
    225     S_SMF_DATA *pSMFData;
    226 
    227     pSMFData = (S_SMF_DATA*) pInstData;
    228 
    229     /* sanity check */
    230 #ifdef _CHECKED_BUILD
    231     if (pSMFData->state == EAS_STATE_STOPPED)
    232     {
    233         { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "Can't ask for time on a stopped stream\n"); */ }
    234     }
    235 
    236     if (pSMFData->nextStream == NULL)
    237     {
    238         { /* dpp: EAS_ReportEx( _EAS_SEVERITY_ERROR, "no is NULL\n"); */ }
    239     }
    240 #endif
    241 
    242 #if 0
    243     /* return time in milliseconds */
    244     /* if chase mode, lie about time */
    245     if (pSMFData->flags & SMF_FLAGS_CHASE_MODE)
    246         *pTime = 0;
    247 
    248     else
    249 #endif
    250 
    251         /*lint -e{704} use shift instead of division */
    252         *pTime = pSMFData->time >> 8;
    253 
    254     *pTime = pSMFData->time >> 8;
    255     return EAS_SUCCESS;
    256 }
    257 
    258 /*----------------------------------------------------------------------------
    259  * SMF_Event()
    260  *----------------------------------------------------------------------------
    261  * Purpose:
    262  * Parse the next event in the file
    263  *
    264  * Inputs:
    265  * pEASData         - pointer to overall EAS data structure
    266  * handle           - pointer to file handle
    267  *
    268  * Outputs:
    269  *
    270  *
    271  * Side Effects:
    272  *
    273  *----------------------------------------------------------------------------
    274 */
    275 EAS_RESULT SMF_Event (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_INT parserMode)
    276 {
    277     S_SMF_DATA* pSMFData;
    278     EAS_RESULT result;
    279     EAS_I32 i;
    280     EAS_U32 ticks;
    281     EAS_U32 temp;
    282 
    283     /* establish pointer to instance data */
    284     pSMFData = (S_SMF_DATA*) pInstData;
    285     if (pSMFData->state >= EAS_STATE_OPEN)
    286         return EAS_SUCCESS;
    287 
    288     if (!pSMFData->nextStream) {
    289         return EAS_ERROR_FILE_FORMAT;
    290     }
    291 
    292 
    293     /* get current ticks */
    294     ticks = pSMFData->nextStream->ticks;
    295 
    296     /* assume that an error occurred */
    297     pSMFData->state = EAS_STATE_ERROR;
    298 
    299 #ifdef JET_INTERFACE
    300     /* if JET has track muted, set parser mode to mute */
    301     if (pSMFData->nextStream->midiStream.jetData & MIDI_FLAGS_JET_MUTE)
    302         parserMode = eParserModeMute;
    303 #endif
    304 
    305     /* parse the next event from all the streams */
    306     if ((result = SMF_ParseEvent(pEASData, pSMFData, pSMFData->nextStream, parserMode)) != EAS_SUCCESS)
    307     {
    308         /* check for unexpected end-of-file */
    309         if (result != EAS_EOF)
    310             return result;
    311 
    312         /* indicate end of track for this stream */
    313         pSMFData->nextStream->ticks = SMF_END_OF_TRACK;
    314     }
    315 
    316     /* get next delta time, unless already at end of track */
    317     else if (pSMFData->nextStream->ticks != SMF_END_OF_TRACK)
    318     {
    319         if ((result = SMF_GetDeltaTime(pEASData->hwInstData, pSMFData->nextStream)) != EAS_SUCCESS)
    320         {
    321             /* check for unexpected end-of-file */
    322             if (result != EAS_EOF)
    323                 return result;
    324 
    325             /* indicate end of track for this stream */
    326             pSMFData->nextStream->ticks = SMF_END_OF_TRACK;
    327         }
    328 
    329         /* if zero delta to next event, stay with this stream */
    330         else if (pSMFData->nextStream->ticks == ticks)
    331         {
    332             pSMFData->state = EAS_STATE_PLAY;
    333             return EAS_SUCCESS;
    334         }
    335     }
    336 
    337     /* find next event in all streams */
    338     temp = 0x7ffffff;
    339     pSMFData->nextStream = NULL;
    340     for (i = 0; i < pSMFData->numStreams; i++)
    341     {
    342         if (pSMFData->streams[i].ticks < temp)
    343         {
    344             temp = pSMFData->streams[i].ticks;
    345             pSMFData->nextStream = &pSMFData->streams[i];
    346         }
    347     }
    348 
    349     /* are there any more events to parse? */
    350     if (pSMFData->nextStream)
    351     {
    352         pSMFData->state = EAS_STATE_PLAY;
    353 
    354         /* update the time of the next event */
    355         SMF_UpdateTime(pSMFData, pSMFData->nextStream->ticks - ticks);
    356     }
    357     else
    358     {
    359         pSMFData->state = EAS_STATE_STOPPING;
    360         VMReleaseAllVoices(pEASData->pVoiceMgr, pSMFData->pSynth);
    361     }
    362 
    363     return EAS_SUCCESS;
    364 }
    365 
    366 /*----------------------------------------------------------------------------
    367  * SMF_State()
    368  *----------------------------------------------------------------------------
    369  * Purpose:
    370  * Returns the current state of the stream
    371  *
    372  * Inputs:
    373  * pEASData         - pointer to overall EAS data structure
    374  * handle           - pointer to file handle
    375  * pState           - pointer to variable to store state
    376  *
    377  * Outputs:
    378  *
    379  *
    380  * Side Effects:
    381  *
    382  *----------------------------------------------------------------------------
    383 */
    384 /*lint -esym(715, pEASData) reserved for future use */
    385 EAS_RESULT SMF_State (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 *pState)
    386 {
    387     S_SMF_DATA* pSMFData;
    388 
    389     /* establish pointer to instance data */
    390     pSMFData = (S_SMF_DATA*) pInstData;
    391 
    392     /* if stopping, check to see if synth voices are active */
    393     if (pSMFData->state == EAS_STATE_STOPPING)
    394     {
    395         if (VMActiveVoices(pSMFData->pSynth) == 0)
    396             pSMFData->state = EAS_STATE_STOPPED;
    397     }
    398 
    399     if (pSMFData->state == EAS_STATE_PAUSING)
    400     {
    401         if (VMActiveVoices(pSMFData->pSynth) == 0)
    402             pSMFData->state = EAS_STATE_PAUSED;
    403     }
    404 
    405     /* return current state */
    406     *pState = pSMFData->state;
    407     return EAS_SUCCESS;
    408 }
    409 
    410 /*----------------------------------------------------------------------------
    411  * SMF_Close()
    412  *----------------------------------------------------------------------------
    413  * Purpose:
    414  * Close the file and clean up
    415  *
    416  * Inputs:
    417  * pEASData         - pointer to overall EAS data structure
    418  * handle           - pointer to file handle
    419  *
    420  * Outputs:
    421  *
    422  *
    423  * Side Effects:
    424  *
    425  *----------------------------------------------------------------------------
    426 */
    427 EAS_RESULT SMF_Close (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
    428 {
    429     S_SMF_DATA* pSMFData;
    430     EAS_I32 i;
    431     EAS_RESULT result;
    432 
    433     pSMFData = (S_SMF_DATA*) pInstData;
    434 
    435     /* close all the streams */
    436     for (i = 0; i < pSMFData->numStreams; i++)
    437     {
    438         if (pSMFData->streams[i].fileHandle != NULL)
    439         {
    440             if ((result = EAS_HWCloseFile(pEASData->hwInstData, pSMFData->streams[i].fileHandle)) != EAS_SUCCESS)
    441                 return result;
    442         }
    443     }
    444     if (pSMFData->fileHandle != NULL)
    445         if ((result = EAS_HWCloseFile(pEASData->hwInstData, pSMFData->fileHandle)) != EAS_SUCCESS)
    446             return result;
    447 
    448     /* free the synth */
    449     if (pSMFData->pSynth != NULL)
    450         VMMIDIShutdown(pEASData, pSMFData->pSynth);
    451 
    452     /* if using dynamic memory, free it */
    453     if (!pEASData->staticMemoryModel)
    454     {
    455         if (pSMFData->streams)
    456             EAS_HWFree(pEASData->hwInstData, pSMFData->streams);
    457 
    458         /* free the instance data */
    459         EAS_HWFree(pEASData->hwInstData, pSMFData);
    460     }
    461 
    462     return EAS_SUCCESS;
    463 }
    464 
    465 /*----------------------------------------------------------------------------
    466  * SMF_Reset()
    467  *----------------------------------------------------------------------------
    468  * Purpose:
    469  * Reset the sequencer. Used for locating backwards in the file.
    470  *
    471  * Inputs:
    472  * pEASData         - pointer to overall EAS data structure
    473  * handle           - pointer to file handle
    474  *
    475  * Outputs:
    476  *
    477  *
    478  * Side Effects:
    479  *
    480  *----------------------------------------------------------------------------
    481 */
    482 EAS_RESULT SMF_Reset (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
    483 {
    484     S_SMF_DATA* pSMFData;
    485     EAS_I32 i;
    486     EAS_RESULT result;
    487     EAS_U32 ticks;
    488 
    489     pSMFData = (S_SMF_DATA*) pInstData;
    490 
    491     /* reset time to zero */
    492     pSMFData->time = 0;
    493 
    494     /* reset the synth */
    495     VMReset(pEASData->pVoiceMgr, pSMFData->pSynth, EAS_TRUE);
    496 
    497     /* find the start of each track */
    498     ticks = 0x7fffffffL;
    499     pSMFData->nextStream = NULL;
    500     for (i = 0; i < pSMFData->numStreams; i++)
    501     {
    502 
    503         /* reset file position to first byte of data in track */
    504         if ((result = EAS_HWFileSeek(pEASData->hwInstData, pSMFData->streams[i].fileHandle, pSMFData->streams[i].startFilePos)) != EAS_SUCCESS)
    505             return result;
    506 
    507         /* initalize some data */
    508         pSMFData->streams[i].ticks = 0;
    509 
    510         /* initalize the MIDI parser data */
    511         EAS_InitMIDIStream(&pSMFData->streams[i].midiStream);
    512 
    513         /* parse the first delta time in each stream */
    514         if ((result = SMF_GetDeltaTime(pEASData->hwInstData,&pSMFData->streams[i])) != EAS_SUCCESS)
    515             return result;
    516         if (pSMFData->streams[i].ticks < ticks)
    517         {
    518             ticks = pSMFData->streams[i].ticks;
    519             pSMFData->nextStream = &pSMFData->streams[i];
    520         }
    521     }
    522 
    523 
    524     pSMFData->state = EAS_STATE_READY;
    525     return EAS_SUCCESS;
    526 }
    527 
    528 /*----------------------------------------------------------------------------
    529  * SMF_Pause()
    530  *----------------------------------------------------------------------------
    531  * Purpose:
    532  * Pauses the sequencer. Mutes all voices and sets state to pause.
    533  *
    534  * Inputs:
    535  * pEASData         - pointer to overall EAS data structure
    536  * handle           - pointer to file handle
    537  *
    538  * Outputs:
    539  *
    540  *
    541  * Side Effects:
    542  *
    543  *----------------------------------------------------------------------------
    544 */
    545 EAS_RESULT SMF_Pause (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
    546 {
    547     S_SMF_DATA *pSMFData;
    548 
    549     /* can't pause a stopped stream */
    550     pSMFData = (S_SMF_DATA*) pInstData;
    551     if (pSMFData->state == EAS_STATE_STOPPED)
    552         return EAS_ERROR_ALREADY_STOPPED;
    553 
    554     /* mute the synthesizer */
    555     VMMuteAllVoices(pEASData->pVoiceMgr, pSMFData->pSynth);
    556     pSMFData->state = EAS_STATE_PAUSING;
    557     return EAS_SUCCESS;
    558 }
    559 
    560 /*----------------------------------------------------------------------------
    561  * SMF_Resume()
    562  *----------------------------------------------------------------------------
    563  * Purpose:
    564  * Resume playing after a pause, sets state back to playing.
    565  *
    566  * Inputs:
    567  * pEASData         - pointer to overall EAS data structure
    568  * handle           - pointer to file handle
    569  *
    570  * Outputs:
    571  *
    572  *
    573  * Side Effects:
    574  *
    575  *----------------------------------------------------------------------------
    576 */
    577 /*lint -esym(715, pEASData) reserved for future use */
    578 EAS_RESULT SMF_Resume (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
    579 {
    580     S_SMF_DATA *pSMFData;
    581 
    582     /* can't resume a stopped stream */
    583     pSMFData = (S_SMF_DATA*) pInstData;
    584     if (pSMFData->state == EAS_STATE_STOPPED)
    585         return EAS_ERROR_ALREADY_STOPPED;
    586 
    587     /* nothing to do but resume playback */
    588     pSMFData->state = EAS_STATE_PLAY;
    589     return EAS_SUCCESS;
    590 }
    591 
    592 /*----------------------------------------------------------------------------
    593  * SMF_SetData()
    594  *----------------------------------------------------------------------------
    595  * Purpose:
    596  * Sets parser parameters
    597  *
    598  * Inputs:
    599  * pEASData         - pointer to overall EAS data structure
    600  * handle           - pointer to file handle
    601  *
    602  * Outputs:
    603  *
    604  *
    605  * Side Effects:
    606  *
    607  *----------------------------------------------------------------------------
    608 */
    609 /*lint -esym(715, pEASData) reserved for future use */
    610 EAS_RESULT SMF_SetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 value)
    611 {
    612     S_SMF_DATA *pSMFData;
    613 
    614     pSMFData = (S_SMF_DATA*) pInstData;
    615     switch (param)
    616     {
    617 
    618         /* set metadata callback */
    619         case PARSER_DATA_METADATA_CB:
    620             EAS_HWMemCpy(&pSMFData->metadata, (void*) value, sizeof(S_METADATA_CB));
    621             break;
    622 
    623 #ifdef JET_INTERFACE
    624         /* set jet segment and track ID of all tracks for callback function */
    625         case PARSER_DATA_JET_CB:
    626             {
    627                 EAS_U32 i;
    628                 EAS_U32 bit = (EAS_U32) value;
    629                 bit = (bit << JET_EVENT_SEG_SHIFT) & JET_EVENT_SEG_MASK;
    630                 for (i = 0; i < pSMFData->numStreams; i++)
    631                     pSMFData->streams[i].midiStream.jetData =
    632                         (pSMFData->streams[i].midiStream.jetData &
    633                         ~(JET_EVENT_TRACK_MASK | JET_EVENT_SEG_MASK)) |
    634                         i << JET_EVENT_TRACK_SHIFT | bit | MIDI_FLAGS_JET_CB;
    635                 pSMFData->flags |= SMF_FLAGS_JET_STREAM;
    636             }
    637             break;
    638 
    639         /* set state of all mute flags at once */
    640         case PARSER_DATA_MUTE_FLAGS:
    641             {
    642                 EAS_INT i;
    643                 EAS_U32 bit = (EAS_U32) value;
    644                 for (i = 0; i < pSMFData->numStreams; i++)
    645                 {
    646                     if (bit & 1)
    647                         pSMFData->streams[i].midiStream.jetData |= MIDI_FLAGS_JET_MUTE;
    648                     else
    649                         pSMFData->streams[i].midiStream.jetData &= ~MIDI_FLAGS_JET_MUTE;
    650                     bit >>= 1;
    651                 }
    652             }
    653             break;
    654 
    655         /* set track mute */
    656         case PARSER_DATA_SET_MUTE:
    657             if (value < pSMFData->numStreams)
    658                 pSMFData->streams[value].midiStream.jetData |= MIDI_FLAGS_JET_MUTE;
    659             else
    660                 return EAS_ERROR_PARAMETER_RANGE;
    661             break;
    662 
    663         /* clear track mute */
    664         case PARSER_DATA_CLEAR_MUTE:
    665             if (value < pSMFData->numStreams)
    666                 pSMFData->streams[value].midiStream.jetData &= ~MIDI_FLAGS_JET_MUTE;
    667             else
    668                 return EAS_ERROR_PARAMETER_RANGE;
    669             break;
    670 #endif
    671 
    672         default:
    673             return EAS_ERROR_INVALID_PARAMETER;
    674     }
    675 
    676     return EAS_SUCCESS;
    677 }
    678 
    679 /*----------------------------------------------------------------------------
    680  * SMF_GetData()
    681  *----------------------------------------------------------------------------
    682  * Purpose:
    683  * Retrieves parser parameters
    684  *
    685  * Inputs:
    686  * pEASData         - pointer to overall EAS data structure
    687  * handle           - pointer to file handle
    688  *
    689  * Outputs:
    690  *
    691  *
    692  * Side Effects:
    693  *
    694  *----------------------------------------------------------------------------
    695 */
    696 /*lint -esym(715, pEASData) reserved for future use */
    697 EAS_RESULT SMF_GetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 *pValue)
    698 {
    699     S_SMF_DATA *pSMFData;
    700 
    701     pSMFData = (S_SMF_DATA*) pInstData;
    702     switch (param)
    703     {
    704         /* return file type */
    705         case PARSER_DATA_FILE_TYPE:
    706             if (pSMFData->numStreams == 1)
    707                 *pValue = EAS_FILE_SMF0;
    708             else
    709                 *pValue = EAS_FILE_SMF1;
    710             break;
    711 
    712 /* now handled in eas_public.c */
    713 #if 0
    714         case PARSER_DATA_POLYPHONY:
    715             if (pSMFData->pSynth)
    716                 VMGetPolyphony(pEASData->pVoiceMgr, pSMFData->pSynth, pValue);
    717             else
    718                 return EAS_ERROR_NOT_VALID_IN_THIS_STATE;
    719             break;
    720 
    721         case PARSER_DATA_PRIORITY:
    722             if (pSMFData->pSynth)
    723                 VMGetPriority(pEASData->pVoiceMgr, pSMFData->pSynth, pValue);
    724             break;
    725 
    726         /* set transposition */
    727         case PARSER_DATA_TRANSPOSITION:
    728             *pValue = pSMFData->transposition;
    729             break;
    730 #endif
    731 
    732         case PARSER_DATA_SYNTH_HANDLE:
    733             *pValue = (EAS_I32) pSMFData->pSynth;
    734             break;
    735 
    736         default:
    737             return EAS_ERROR_INVALID_PARAMETER;
    738     }
    739 
    740     return EAS_SUCCESS;
    741 }
    742 
    743 /*----------------------------------------------------------------------------
    744  * SMF_GetVarLenData()
    745  *----------------------------------------------------------------------------
    746  * Purpose:
    747  * Reads a varible length quantity from an SMF file
    748  *
    749  * Inputs:
    750  *
    751  *
    752  * Outputs:
    753  *
    754  *
    755  * Side Effects:
    756  *
    757  *----------------------------------------------------------------------------
    758 */
    759 static EAS_RESULT SMF_GetVarLenData (EAS_HW_DATA_HANDLE hwInstData, EAS_FILE_HANDLE fileHandle, EAS_U32 *pData)
    760 {
    761     EAS_RESULT result;
    762     EAS_U32 data;
    763     EAS_U8 c;
    764 
    765     /* read until bit 7 is zero */
    766     data = 0;
    767     do
    768     {
    769         if ((result = EAS_HWGetByte(hwInstData, fileHandle,&c)) != EAS_SUCCESS)
    770             return result;
    771         data = (data << 7) | (c & 0x7f);
    772     } while (c & 0x80);
    773     *pData = data;
    774     return EAS_SUCCESS;
    775 }
    776 
    777 /*----------------------------------------------------------------------------
    778  * SMF_GetDeltaTime()
    779  *----------------------------------------------------------------------------
    780  * Purpose:
    781  * Reads a varible length quantity from an SMF file
    782  *
    783  * Inputs:
    784  *
    785  *
    786  * Outputs:
    787  *
    788  *
    789  * Side Effects:
    790  *
    791  *----------------------------------------------------------------------------
    792 */
    793 static EAS_RESULT SMF_GetDeltaTime (EAS_HW_DATA_HANDLE hwInstData, S_SMF_STREAM *pSMFStream)
    794 {
    795     EAS_RESULT result;
    796     EAS_U32 ticks;
    797 
    798     if ((result = SMF_GetVarLenData(hwInstData, pSMFStream->fileHandle, &ticks)) != EAS_SUCCESS)
    799         return result;
    800 
    801     pSMFStream->ticks += ticks;
    802     return EAS_SUCCESS;
    803 }
    804 
    805 /*----------------------------------------------------------------------------
    806  * SMF_ParseMetaEvent()
    807  *----------------------------------------------------------------------------
    808  * Purpose:
    809  * Reads a varible length quantity from an SMF file
    810  *
    811  * Inputs:
    812  *
    813  *
    814  * Outputs:
    815  *
    816  *
    817  * Side Effects:
    818  *
    819  *----------------------------------------------------------------------------
    820 */
    821 static EAS_RESULT SMF_ParseMetaEvent (S_EAS_DATA *pEASData, S_SMF_DATA *pSMFData, S_SMF_STREAM *pSMFStream)
    822 {
    823     EAS_RESULT result;
    824     EAS_U32 len;
    825     EAS_I32 pos;
    826     EAS_U32 temp;
    827     EAS_U8 c;
    828 
    829     /* get the meta-event type */
    830     if ((result = EAS_HWGetByte(pEASData->hwInstData, pSMFStream->fileHandle, &c)) != EAS_SUCCESS)
    831         return result;
    832 
    833     /* get the length */
    834     if ((result = SMF_GetVarLenData(pEASData->hwInstData, pSMFStream->fileHandle, &len)) != EAS_SUCCESS)
    835         return result;
    836 
    837     /* get the current file position so we can skip the event */
    838     if ((result = EAS_HWFilePos(pEASData->hwInstData, pSMFStream->fileHandle, &pos)) != EAS_SUCCESS)
    839         return result;
    840 
    841     /* prevent a large unsigned length from being treated as a negative length */
    842     if ((EAS_I32) len < 0) {
    843         /* note that EAS_I32 is a long, which can be 64-bits on some computers */
    844         ALOGE("%s() negative len = %ld", __func__, (long) len);
    845         android_errorWriteLog(0x534e4554, "68953854");
    846         return EAS_ERROR_FILE_FORMAT;
    847     }
    848     /* prevent numeric overflow caused by a very large len, assume pos > 0 */
    849     const EAS_I32 EAS_I32_MAX = 0x7FFFFFFF;
    850     if ((EAS_I32) len > (EAS_I32_MAX - pos)) {
    851         ALOGE("%s() too large len = %ld", __func__, (long) len);
    852         android_errorWriteLog(0x534e4554, "68953854");
    853         return EAS_ERROR_FILE_FORMAT;
    854     }
    855 
    856     pos += (EAS_I32) len;
    857 
    858     /* end of track? */
    859     if (c == SMF_META_END_OF_TRACK)
    860     {
    861         { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "Meta-event: end of track\n", c, len); */ }
    862         pSMFStream->ticks = SMF_END_OF_TRACK;
    863     }
    864 
    865     /* tempo event? */
    866     else if (c == SMF_META_TEMPO)
    867     {
    868         /* read the 3-byte timebase value */
    869         temp = 0;
    870         while (len--)
    871         {
    872             if ((result = EAS_HWGetByte(pEASData->hwInstData, pSMFStream->fileHandle, &c)) != EAS_SUCCESS)
    873                 return result;
    874             temp = (temp << 8) | c;
    875         }
    876 
    877         pSMFData->tickConv = (EAS_U16) (((temp * 1024) / pSMFData->ppqn + 500) / 1000);
    878         pSMFData->flags |= SMF_FLAGS_HAS_TEMPO;
    879     }
    880 
    881     /* check for time signature - see iMelody spec V1.4 section 4.1.2.2.3.6 */
    882     else if (c == SMF_META_TIME_SIGNATURE)
    883     {
    884         pSMFData->flags |= SMF_FLAGS_HAS_TIME_SIG;
    885     }
    886 
    887     /* if the host has registered a metadata callback return the metadata */
    888     else if (pSMFData->metadata.callback)
    889     {
    890         EAS_I32 readLen;
    891         E_EAS_METADATA_TYPE metaType;
    892 
    893         metaType = EAS_METADATA_UNKNOWN;
    894 
    895         /* only process title on the first track */
    896         if (c == SMF_META_SEQTRK_NAME)
    897             metaType = EAS_METADATA_TITLE;
    898         else if (c == SMF_META_TEXT)
    899             metaType = EAS_METADATA_TEXT;
    900         else if (c == SMF_META_COPYRIGHT)
    901             metaType = EAS_METADATA_COPYRIGHT;
    902         else if (c == SMF_META_LYRIC)
    903             metaType = EAS_METADATA_LYRIC;
    904 
    905         if (metaType != EAS_METADATA_UNKNOWN)
    906         {
    907             readLen = pSMFData->metadata.bufferSize - 1;
    908             if ((EAS_I32) len < readLen)
    909                 readLen = (EAS_I32) len;
    910             if ((result = EAS_HWReadFile(pEASData->hwInstData, pSMFStream->fileHandle, pSMFData->metadata.buffer, readLen, &readLen)) != EAS_SUCCESS)
    911                 return result;
    912             pSMFData->metadata.buffer[readLen] = 0;
    913             pSMFData->metadata.callback(metaType, pSMFData->metadata.buffer, pSMFData->metadata.pUserData);
    914         }
    915     }
    916 
    917     /* position file to next event - in case we ignored all or part of the meta-event */
    918     if ((result = EAS_HWFileSeek(pEASData->hwInstData, pSMFStream->fileHandle, pos)) != EAS_SUCCESS)
    919         return result;
    920 
    921     { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "Meta-event: type=%02x, len=%d\n", c, len); */ }
    922     return EAS_SUCCESS;
    923 }
    924 
    925 /*----------------------------------------------------------------------------
    926  * SMF_ParseSysEx()
    927  *----------------------------------------------------------------------------
    928  * Purpose:
    929  * Reads a varible length quantity from an SMF file
    930  *
    931  * Inputs:
    932  *
    933  *
    934  * Outputs:
    935  *
    936  *
    937  * Side Effects:
    938  *
    939  *----------------------------------------------------------------------------
    940 */
    941 static EAS_RESULT SMF_ParseSysEx (S_EAS_DATA *pEASData, S_SMF_DATA *pSMFData, S_SMF_STREAM *pSMFStream, EAS_U8 f0, EAS_INT parserMode)
    942 {
    943     EAS_RESULT result;
    944     EAS_U32 len;
    945     EAS_U8 c;
    946 
    947     /* get the length */
    948     if ((result = SMF_GetVarLenData(pEASData->hwInstData, pSMFStream->fileHandle, &len)) != EAS_SUCCESS)
    949         return result;
    950 
    951     /* start of SysEx message? */
    952     if (f0 == 0xf0)
    953     {
    954         if ((result = EAS_ParseMIDIStream(pEASData, pSMFData->pSynth, &pSMFStream->midiStream, f0, parserMode)) != EAS_SUCCESS)
    955             return result;
    956     }
    957 
    958     /* feed the SysEx to the stream parser */
    959     while (len--)
    960     {
    961         if ((result = EAS_HWGetByte(pEASData->hwInstData, pSMFStream->fileHandle, &c)) != EAS_SUCCESS)
    962             return result;
    963         if ((result = EAS_ParseMIDIStream(pEASData, pSMFData->pSynth, &pSMFStream->midiStream, c, parserMode)) != EAS_SUCCESS)
    964             return result;
    965 
    966         /* check for GM system ON */
    967         if (pSMFStream->midiStream.flags & MIDI_FLAG_GM_ON)
    968             pSMFData->flags |= SMF_FLAGS_HAS_GM_ON;
    969     }
    970 
    971     return EAS_SUCCESS;
    972 }
    973 
    974 /*----------------------------------------------------------------------------
    975  * SMF_ParseEvent()
    976  *----------------------------------------------------------------------------
    977  * Purpose:
    978  * Reads a varible length quantity from an SMF file
    979  *
    980  * Inputs:
    981  *
    982  *
    983  * Outputs:
    984  *
    985  *
    986  * Side Effects:
    987  *
    988  *----------------------------------------------------------------------------
    989 */
    990 static EAS_RESULT SMF_ParseEvent (S_EAS_DATA *pEASData, S_SMF_DATA *pSMFData, S_SMF_STREAM *pSMFStream, EAS_INT parserMode)
    991 {
    992     EAS_RESULT result;
    993     EAS_U8 c;
    994 
    995     /* get the event type */
    996     if ((result = EAS_HWGetByte(pEASData->hwInstData, pSMFStream->fileHandle, &c)) != EAS_SUCCESS)
    997         return result;
    998 
    999     /* parse meta-event */
   1000     if (c == 0xff)
   1001     {
   1002         if ((result = SMF_ParseMetaEvent(pEASData, pSMFData, pSMFStream)) != EAS_SUCCESS)
   1003             return result;
   1004     }
   1005 
   1006     /* parse SysEx */
   1007     else if ((c == 0xf0) || (c == 0xf7))
   1008     {
   1009         if ((result = SMF_ParseSysEx(pEASData, pSMFData, pSMFStream, c, parserMode)) != EAS_SUCCESS)
   1010             return result;
   1011     }
   1012 
   1013     /* parse MIDI message */
   1014     else
   1015     {
   1016         if ((result = EAS_ParseMIDIStream(pEASData, pSMFData->pSynth, &pSMFStream->midiStream, c, parserMode)) != EAS_SUCCESS)
   1017             return result;
   1018 
   1019         /* keep streaming data to the MIDI parser until the message is complete */
   1020         while (pSMFStream->midiStream.pending)
   1021         {
   1022             if ((result = EAS_HWGetByte(pEASData->hwInstData, pSMFStream->fileHandle, &c)) != EAS_SUCCESS)
   1023                 return result;
   1024             if ((result = EAS_ParseMIDIStream(pEASData, pSMFData->pSynth, &pSMFStream->midiStream, c, parserMode)) != EAS_SUCCESS)
   1025                 return result;
   1026         }
   1027 
   1028     }
   1029 
   1030     /* chase mode logic */
   1031     if (pSMFData->time == 0)
   1032     {
   1033         if (pSMFData->flags & SMF_FLAGS_CHASE_MODE)
   1034         {
   1035             if (pSMFStream->midiStream.flags & MIDI_FLAG_FIRST_NOTE)
   1036                 pSMFData->flags &= ~SMF_FLAGS_CHASE_MODE;
   1037         }
   1038         else if ((pSMFData->flags & SMF_FLAGS_SETUP_BAR) == SMF_FLAGS_SETUP_BAR)
   1039             pSMFData->flags = (pSMFData->flags & ~SMF_FLAGS_SETUP_BAR) | SMF_FLAGS_CHASE_MODE;
   1040     }
   1041 
   1042     return EAS_SUCCESS;
   1043 }
   1044 
   1045 /*----------------------------------------------------------------------------
   1046  * SMF_ParseHeader()
   1047  *----------------------------------------------------------------------------
   1048  * Purpose:
   1049  * Parses the header of an SMF file, allocates memory the stream parsers and initializes the
   1050  * stream parsers.
   1051  *
   1052  * Inputs:
   1053  * pEASData         - pointer to overall EAS data structure
   1054  * pSMFData         - pointer to parser instance data
   1055  * fileHandle       - file handle
   1056  * fileOffset       - offset in the file where the header data starts, usually 0
   1057  *
   1058  *
   1059  * Outputs:
   1060  *
   1061  *
   1062  * Side Effects:
   1063  *
   1064  *----------------------------------------------------------------------------
   1065 */
   1066 /*lint -e{801} we know that 'goto' is deprecated - but it's cleaner in this case */
   1067 EAS_RESULT SMF_ParseHeader (EAS_HW_DATA_HANDLE hwInstData, S_SMF_DATA *pSMFData)
   1068 {
   1069     EAS_RESULT result;
   1070     EAS_I32 i;
   1071     EAS_U16 division;
   1072     EAS_U16 numStreams;
   1073     EAS_U32 chunkSize;
   1074     EAS_U32 chunkStart;
   1075     EAS_U32 temp;
   1076     EAS_U32 ticks;
   1077 
   1078     /* explicitly set numStreams to 0. It will later be used by SMF_Close to
   1079      * determine whether we have valid streams or not. */
   1080     pSMFData->numStreams = 0;
   1081 
   1082     /* rewind the file and find the end of the header chunk */
   1083     if ((result = EAS_HWFileSeek(hwInstData, pSMFData->fileHandle, pSMFData->fileOffset + SMF_OFS_HEADER_SIZE)) != EAS_SUCCESS)
   1084         goto ReadError;
   1085     if ((result = EAS_HWGetDWord(hwInstData, pSMFData->fileHandle, &chunkSize, EAS_TRUE)) != EAS_SUCCESS)
   1086         goto ReadError;
   1087 
   1088     /* determine the number of tracks */
   1089     if ((result = EAS_HWFileSeek(hwInstData, pSMFData->fileHandle, pSMFData->fileOffset + SMF_OFS_NUM_TRACKS)) != EAS_SUCCESS)
   1090         goto ReadError;
   1091     if ((result = EAS_HWGetWord(hwInstData, pSMFData->fileHandle, &numStreams, EAS_TRUE)) != EAS_SUCCESS)
   1092         goto ReadError;
   1093 
   1094     /* limit the number of tracks */
   1095     if (numStreams > MAX_SMF_STREAMS)
   1096     {
   1097         { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "SMF file contains %u tracks, playing %d tracks\n", numStreams, MAX_SMF_STREAMS); */ }
   1098         numStreams = MAX_SMF_STREAMS;
   1099     } else if (numStreams == 0)
   1100     {
   1101         /* avoid 0 sized allocation */
   1102         return EAS_ERROR_PARAMETER_RANGE;
   1103     }
   1104 
   1105     /* get the time division */
   1106     if ((result = EAS_HWGetWord(hwInstData, pSMFData->fileHandle, &division, EAS_TRUE)) != EAS_SUCCESS)
   1107         goto ReadError;
   1108 
   1109     /* setup default timebase for 120 bpm */
   1110     pSMFData->ppqn = 192;
   1111     if (!division || division & 0x8000)
   1112         { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING,"No support for SMPTE code timebase\n"); */ }
   1113     else
   1114         pSMFData->ppqn = (division & 0x7fff);
   1115     pSMFData->tickConv = (EAS_U16) (((SMF_DEFAULT_TIMEBASE * 1024) / pSMFData->ppqn + 500) / 1000);
   1116 
   1117     /* dynamic memory allocation, allocate memory for streams */
   1118     if (pSMFData->streams == NULL)
   1119     {
   1120         pSMFData->streams = EAS_HWMalloc(hwInstData,sizeof(S_SMF_STREAM) * numStreams);
   1121         if (pSMFData->streams == NULL)
   1122             return EAS_ERROR_MALLOC_FAILED;
   1123 
   1124         /* zero the memory to insure complete initialization */
   1125         EAS_HWMemSet((void *)(pSMFData->streams), 0, sizeof(S_SMF_STREAM) * numStreams);
   1126     }
   1127     pSMFData->numStreams = numStreams;
   1128 
   1129     /* find the start of each track */
   1130     chunkStart = (EAS_U32) pSMFData->fileOffset;
   1131     ticks = 0x7fffffffL;
   1132     pSMFData->nextStream = NULL;
   1133     for (i = 0; i < pSMFData->numStreams; i++)
   1134     {
   1135 
   1136         for (;;)
   1137         {
   1138 
   1139             /* calculate start of next chunk - checking for errors */
   1140             temp = chunkStart + SMF_CHUNK_INFO_SIZE + chunkSize;
   1141             if (temp <= chunkStart)
   1142             {
   1143                 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING,"Error in chunk size at offset %d\n", chunkStart); */ }
   1144                 return EAS_ERROR_FILE_FORMAT;
   1145             }
   1146             chunkStart = temp;
   1147 
   1148             /* seek to the start of the next chunk */
   1149             if ((result = EAS_HWFileSeek(hwInstData, pSMFData->fileHandle, (EAS_I32) chunkStart)) != EAS_SUCCESS)
   1150                 goto ReadError;
   1151 
   1152             /* read the chunk identifier */
   1153             if ((result = EAS_HWGetDWord(hwInstData, pSMFData->fileHandle, &temp, EAS_TRUE)) != EAS_SUCCESS)
   1154                 goto ReadError;
   1155 
   1156             /* read the chunk size */
   1157             if ((result = EAS_HWGetDWord(hwInstData, pSMFData->fileHandle, &chunkSize, EAS_TRUE)) != EAS_SUCCESS)
   1158                 goto ReadError;
   1159 
   1160             /* make sure this is an 'MTrk' chunk */
   1161             if (temp == SMF_CHUNK_TYPE_TRACK)
   1162                 break;
   1163 
   1164             { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING,"Unexpected chunk type: 0x%08x\n", temp); */ }
   1165         }
   1166 
   1167         /* initalize some data */
   1168         pSMFData->streams[i].ticks = 0;
   1169         pSMFData->streams[i].fileHandle = pSMFData->fileHandle;
   1170 
   1171         /* NULL the file handle so we don't try to close it twice */
   1172         pSMFData->fileHandle = NULL;
   1173 
   1174         /* save this file position as the start of the track */
   1175         pSMFData->streams[i].startFilePos = (EAS_I32) chunkStart + SMF_CHUNK_INFO_SIZE;
   1176 
   1177         /* initalize the MIDI parser data */
   1178         EAS_InitMIDIStream(&pSMFData->streams[i].midiStream);
   1179 
   1180         /* parse the first delta time in each stream */
   1181         if ((result = SMF_GetDeltaTime(hwInstData, &pSMFData->streams[i])) != EAS_SUCCESS)
   1182                 goto ReadError;
   1183 
   1184         if (pSMFData->streams[i].ticks < ticks)
   1185         {
   1186             ticks = pSMFData->streams[i].ticks;
   1187             pSMFData->nextStream = &pSMFData->streams[i];
   1188         }
   1189 
   1190         /* more tracks to do, create a duplicate file handle */
   1191         if (i < (pSMFData->numStreams - 1))
   1192         {
   1193             if ((result = EAS_HWDupHandle(hwInstData, pSMFData->streams[i].fileHandle, &pSMFData->fileHandle)) != EAS_SUCCESS)
   1194                 goto ReadError;
   1195         }
   1196     }
   1197 
   1198     /* update the time of the next event */
   1199     if (pSMFData->nextStream)
   1200         SMF_UpdateTime(pSMFData, pSMFData->nextStream->ticks);
   1201 
   1202     return EAS_SUCCESS;
   1203 
   1204     /* ugly goto: but simpler than structured */
   1205     ReadError:
   1206         if (result == EAS_EOF)
   1207             return EAS_ERROR_FILE_FORMAT;
   1208         return result;
   1209 }
   1210 
   1211 /*----------------------------------------------------------------------------
   1212  * SMF_UpdateTime()
   1213  *----------------------------------------------------------------------------
   1214  * Purpose:
   1215  * Update the millisecond time base by converting the ticks into millieconds
   1216  *
   1217  * Inputs:
   1218  *
   1219  *
   1220  * Outputs:
   1221  *
   1222  *
   1223  * Side Effects:
   1224  *
   1225  *----------------------------------------------------------------------------
   1226 */
   1227 static void SMF_UpdateTime (S_SMF_DATA *pSMFData, EAS_U32 ticks)
   1228 {
   1229     EAS_U32 temp1, temp2;
   1230 
   1231     if (pSMFData->flags & SMF_FLAGS_CHASE_MODE)
   1232         return;
   1233 
   1234     temp1 = (ticks >> 10) * pSMFData->tickConv;
   1235     temp2 = (ticks & 0x3ff) * pSMFData->tickConv;
   1236     pSMFData->time += (EAS_I32)((temp1 << 8) + (temp2 >> 2));
   1237 }
   1238 
   1239