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