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