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     if (!pSMFData->nextStream) {
    285         return EAS_ERROR_FILE_FORMAT;
    286     }
    287 
    288 
    289     /* get current ticks */
    290     ticks = pSMFData->nextStream->ticks;
    291 
    292     /* assume that an error occurred */
    293     pSMFData->state = EAS_STATE_ERROR;
    294 
    295 #ifdef JET_INTERFACE
    296     /* if JET has track muted, set parser mode to mute */
    297     if (pSMFData->nextStream->midiStream.jetData & MIDI_FLAGS_JET_MUTE)
    298         parserMode = eParserModeMute;
    299 #endif
    300 
    301     /* parse the next event from all the streams */
    302     if ((result = SMF_ParseEvent(pEASData, pSMFData, pSMFData->nextStream, parserMode)) != EAS_SUCCESS)
    303     {
    304         /* check for unexpected end-of-file */
    305         if (result != EAS_EOF)
    306             return result;
    307 
    308         /* indicate end of track for this stream */
    309         pSMFData->nextStream->ticks = SMF_END_OF_TRACK;
    310     }
    311 
    312     /* get next delta time, unless already at end of track */
    313     else if (pSMFData->nextStream->ticks != SMF_END_OF_TRACK)
    314     {
    315         if ((result = SMF_GetDeltaTime(pEASData->hwInstData, pSMFData->nextStream)) != EAS_SUCCESS)
    316         {
    317             /* check for unexpected end-of-file */
    318             if (result != EAS_EOF)
    319                 return result;
    320 
    321             /* indicate end of track for this stream */
    322             pSMFData->nextStream->ticks = SMF_END_OF_TRACK;
    323         }
    324 
    325         /* if zero delta to next event, stay with this stream */
    326         else if (pSMFData->nextStream->ticks == ticks)
    327         {
    328             pSMFData->state = EAS_STATE_PLAY;
    329             return EAS_SUCCESS;
    330         }
    331     }
    332 
    333     /* find next event in all streams */
    334     temp = 0x7ffffff;
    335     pSMFData->nextStream = NULL;
    336     for (i = 0; i < pSMFData->numStreams; i++)
    337     {
    338         if (pSMFData->streams[i].ticks < temp)
    339         {
    340             temp = pSMFData->streams[i].ticks;
    341             pSMFData->nextStream = &pSMFData->streams[i];
    342         }
    343     }
    344 
    345     /* are there any more events to parse? */
    346     if (pSMFData->nextStream)
    347     {
    348         pSMFData->state = EAS_STATE_PLAY;
    349 
    350         /* update the time of the next event */
    351         SMF_UpdateTime(pSMFData, pSMFData->nextStream->ticks - ticks);
    352     }
    353     else
    354     {
    355         pSMFData->state = EAS_STATE_STOPPING;
    356         VMReleaseAllVoices(pEASData->pVoiceMgr, pSMFData->pSynth);
    357     }
    358 
    359     return EAS_SUCCESS;
    360 }
    361 
    362 /*----------------------------------------------------------------------------
    363  * SMF_State()
    364  *----------------------------------------------------------------------------
    365  * Purpose:
    366  * Returns the current state of the stream
    367  *
    368  * Inputs:
    369  * pEASData         - pointer to overall EAS data structure
    370  * handle           - pointer to file handle
    371  * pState           - pointer to variable to store state
    372  *
    373  * Outputs:
    374  *
    375  *
    376  * Side Effects:
    377  *
    378  *----------------------------------------------------------------------------
    379 */
    380 /*lint -esym(715, pEASData) reserved for future use */
    381 EAS_RESULT SMF_State (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 *pState)
    382 {
    383     S_SMF_DATA* pSMFData;
    384 
    385     /* establish pointer to instance data */
    386     pSMFData = (S_SMF_DATA*) pInstData;
    387 
    388     /* if stopping, check to see if synth voices are active */
    389     if (pSMFData->state == EAS_STATE_STOPPING)
    390     {
    391         if (VMActiveVoices(pSMFData->pSynth) == 0)
    392             pSMFData->state = EAS_STATE_STOPPED;
    393     }
    394 
    395     if (pSMFData->state == EAS_STATE_PAUSING)
    396     {
    397         if (VMActiveVoices(pSMFData->pSynth) == 0)
    398             pSMFData->state = EAS_STATE_PAUSED;
    399     }
    400 
    401     /* return current state */
    402     *pState = pSMFData->state;
    403     return EAS_SUCCESS;
    404 }
    405 
    406 /*----------------------------------------------------------------------------
    407  * SMF_Close()
    408  *----------------------------------------------------------------------------
    409  * Purpose:
    410  * Close the file and clean up
    411  *
    412  * Inputs:
    413  * pEASData         - pointer to overall EAS data structure
    414  * handle           - pointer to file handle
    415  *
    416  * Outputs:
    417  *
    418  *
    419  * Side Effects:
    420  *
    421  *----------------------------------------------------------------------------
    422 */
    423 EAS_RESULT SMF_Close (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
    424 {
    425     S_SMF_DATA* pSMFData;
    426     EAS_I32 i;
    427     EAS_RESULT result;
    428 
    429     pSMFData = (S_SMF_DATA*) pInstData;
    430 
    431     /* close all the streams */
    432     for (i = 0; i < pSMFData->numStreams; i++)
    433     {
    434         if (pSMFData->streams[i].fileHandle != NULL)
    435         {
    436             if ((result = EAS_HWCloseFile(pEASData->hwInstData, pSMFData->streams[i].fileHandle)) != EAS_SUCCESS)
    437                 return result;
    438         }
    439     }
    440     if (pSMFData->fileHandle != NULL)
    441         if ((result = EAS_HWCloseFile(pEASData->hwInstData, pSMFData->fileHandle)) != EAS_SUCCESS)
    442             return result;
    443 
    444     /* free the synth */
    445     if (pSMFData->pSynth != NULL)
    446         VMMIDIShutdown(pEASData, pSMFData->pSynth);
    447 
    448     /* if using dynamic memory, free it */
    449     if (!pEASData->staticMemoryModel)
    450     {
    451         if (pSMFData->streams)
    452             EAS_HWFree(pEASData->hwInstData, pSMFData->streams);
    453 
    454         /* free the instance data */
    455         EAS_HWFree(pEASData->hwInstData, pSMFData);
    456     }
    457 
    458     return EAS_SUCCESS;
    459 }
    460 
    461 /*----------------------------------------------------------------------------
    462  * SMF_Reset()
    463  *----------------------------------------------------------------------------
    464  * Purpose:
    465  * Reset the sequencer. Used for locating backwards in the file.
    466  *
    467  * Inputs:
    468  * pEASData         - pointer to overall EAS data structure
    469  * handle           - pointer to file handle
    470  *
    471  * Outputs:
    472  *
    473  *
    474  * Side Effects:
    475  *
    476  *----------------------------------------------------------------------------
    477 */
    478 EAS_RESULT SMF_Reset (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
    479 {
    480     S_SMF_DATA* pSMFData;
    481     EAS_I32 i;
    482     EAS_RESULT result;
    483     EAS_U32 ticks;
    484 
    485     pSMFData = (S_SMF_DATA*) pInstData;
    486 
    487     /* reset time to zero */
    488     pSMFData->time = 0;
    489 
    490     /* reset the synth */
    491     VMReset(pEASData->pVoiceMgr, pSMFData->pSynth, EAS_TRUE);
    492 
    493     /* find the start of each track */
    494     ticks = 0x7fffffffL;
    495     pSMFData->nextStream = NULL;
    496     for (i = 0; i < pSMFData->numStreams; i++)
    497     {
    498 
    499         /* reset file position to first byte of data in track */
    500         if ((result = EAS_HWFileSeek(pEASData->hwInstData, pSMFData->streams[i].fileHandle, pSMFData->streams[i].startFilePos)) != EAS_SUCCESS)
    501             return result;
    502 
    503         /* initalize some data */
    504         pSMFData->streams[i].ticks = 0;
    505 
    506         /* initalize the MIDI parser data */
    507         EAS_InitMIDIStream(&pSMFData->streams[i].midiStream);
    508 
    509         /* parse the first delta time in each stream */
    510         if ((result = SMF_GetDeltaTime(pEASData->hwInstData,&pSMFData->streams[i])) != EAS_SUCCESS)
    511             return result;
    512         if (pSMFData->streams[i].ticks < ticks)
    513         {
    514             ticks = pSMFData->streams[i].ticks;
    515             pSMFData->nextStream = &pSMFData->streams[i];
    516         }
    517     }
    518 
    519 
    520     pSMFData->state = EAS_STATE_READY;
    521     return EAS_SUCCESS;
    522 }
    523 
    524 /*----------------------------------------------------------------------------
    525  * SMF_Pause()
    526  *----------------------------------------------------------------------------
    527  * Purpose:
    528  * Pauses the sequencer. Mutes all voices and sets state to pause.
    529  *
    530  * Inputs:
    531  * pEASData         - pointer to overall EAS data structure
    532  * handle           - pointer to file handle
    533  *
    534  * Outputs:
    535  *
    536  *
    537  * Side Effects:
    538  *
    539  *----------------------------------------------------------------------------
    540 */
    541 EAS_RESULT SMF_Pause (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
    542 {
    543     S_SMF_DATA *pSMFData;
    544 
    545     /* can't pause a stopped stream */
    546     pSMFData = (S_SMF_DATA*) pInstData;
    547     if (pSMFData->state == EAS_STATE_STOPPED)
    548         return EAS_ERROR_ALREADY_STOPPED;
    549 
    550     /* mute the synthesizer */
    551     VMMuteAllVoices(pEASData->pVoiceMgr, pSMFData->pSynth);
    552     pSMFData->state = EAS_STATE_PAUSING;
    553     return EAS_SUCCESS;
    554 }
    555 
    556 /*----------------------------------------------------------------------------
    557  * SMF_Resume()
    558  *----------------------------------------------------------------------------
    559  * Purpose:
    560  * Resume playing after a pause, sets state back to playing.
    561  *
    562  * Inputs:
    563  * pEASData         - pointer to overall EAS data structure
    564  * handle           - pointer to file handle
    565  *
    566  * Outputs:
    567  *
    568  *
    569  * Side Effects:
    570  *
    571  *----------------------------------------------------------------------------
    572 */
    573 /*lint -esym(715, pEASData) reserved for future use */
    574 EAS_RESULT SMF_Resume (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
    575 {
    576     S_SMF_DATA *pSMFData;
    577 
    578     /* can't resume a stopped stream */
    579     pSMFData = (S_SMF_DATA*) pInstData;
    580     if (pSMFData->state == EAS_STATE_STOPPED)
    581         return EAS_ERROR_ALREADY_STOPPED;
    582 
    583     /* nothing to do but resume playback */
    584     pSMFData->state = EAS_STATE_PLAY;
    585     return EAS_SUCCESS;
    586 }
    587 
    588 /*----------------------------------------------------------------------------
    589  * SMF_SetData()
    590  *----------------------------------------------------------------------------
    591  * Purpose:
    592  * Sets parser parameters
    593  *
    594  * Inputs:
    595  * pEASData         - pointer to overall EAS data structure
    596  * handle           - pointer to file handle
    597  *
    598  * Outputs:
    599  *
    600  *
    601  * Side Effects:
    602  *
    603  *----------------------------------------------------------------------------
    604 */
    605 /*lint -esym(715, pEASData) reserved for future use */
    606 EAS_RESULT SMF_SetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 value)
    607 {
    608     S_SMF_DATA *pSMFData;
    609 
    610     pSMFData = (S_SMF_DATA*) pInstData;
    611     switch (param)
    612     {
    613 
    614         /* set metadata callback */
    615         case PARSER_DATA_METADATA_CB:
    616             EAS_HWMemCpy(&pSMFData->metadata, (void*) value, sizeof(S_METADATA_CB));
    617             break;
    618 
    619 #ifdef JET_INTERFACE
    620         /* set jet segment and track ID of all tracks for callback function */
    621         case PARSER_DATA_JET_CB:
    622             {
    623                 EAS_U32 i;
    624                 EAS_U32 bit = (EAS_U32) value;
    625                 bit = (bit << JET_EVENT_SEG_SHIFT) & JET_EVENT_SEG_MASK;
    626                 for (i = 0; i < pSMFData->numStreams; i++)
    627                     pSMFData->streams[i].midiStream.jetData =
    628                         (pSMFData->streams[i].midiStream.jetData &
    629                         ~(JET_EVENT_TRACK_MASK | JET_EVENT_SEG_MASK)) |
    630                         i << JET_EVENT_TRACK_SHIFT | bit | MIDI_FLAGS_JET_CB;
    631                 pSMFData->flags |= SMF_FLAGS_JET_STREAM;
    632             }
    633             break;
    634 
    635         /* set state of all mute flags at once */
    636         case PARSER_DATA_MUTE_FLAGS:
    637             {
    638                 EAS_INT i;
    639                 EAS_U32 bit = (EAS_U32) value;
    640                 for (i = 0; i < pSMFData->numStreams; i++)
    641                 {
    642                     if (bit & 1)
    643                         pSMFData->streams[i].midiStream.jetData |= MIDI_FLAGS_JET_MUTE;
    644                     else
    645                         pSMFData->streams[i].midiStream.jetData &= ~MIDI_FLAGS_JET_MUTE;
    646                     bit >>= 1;
    647                 }
    648             }
    649             break;
    650 
    651         /* set track mute */
    652         case PARSER_DATA_SET_MUTE:
    653             if (value < pSMFData->numStreams)
    654                 pSMFData->streams[value].midiStream.jetData |= MIDI_FLAGS_JET_MUTE;
    655             else
    656                 return EAS_ERROR_PARAMETER_RANGE;
    657             break;
    658 
    659         /* clear track mute */
    660         case PARSER_DATA_CLEAR_MUTE:
    661             if (value < pSMFData->numStreams)
    662                 pSMFData->streams[value].midiStream.jetData &= ~MIDI_FLAGS_JET_MUTE;
    663             else
    664                 return EAS_ERROR_PARAMETER_RANGE;
    665             break;
    666 #endif
    667 
    668         default:
    669             return EAS_ERROR_INVALID_PARAMETER;
    670     }
    671 
    672     return EAS_SUCCESS;
    673 }
    674 
    675 /*----------------------------------------------------------------------------
    676  * SMF_GetData()
    677  *----------------------------------------------------------------------------
    678  * Purpose:
    679  * Retrieves parser parameters
    680  *
    681  * Inputs:
    682  * pEASData         - pointer to overall EAS data structure
    683  * handle           - pointer to file handle
    684  *
    685  * Outputs:
    686  *
    687  *
    688  * Side Effects:
    689  *
    690  *----------------------------------------------------------------------------
    691 */
    692 /*lint -esym(715, pEASData) reserved for future use */
    693 EAS_RESULT SMF_GetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 *pValue)
    694 {
    695     S_SMF_DATA *pSMFData;
    696 
    697     pSMFData = (S_SMF_DATA*) pInstData;
    698     switch (param)
    699     {
    700         /* return file type */
    701         case PARSER_DATA_FILE_TYPE:
    702             if (pSMFData->numStreams == 1)
    703                 *pValue = EAS_FILE_SMF0;
    704             else
    705                 *pValue = EAS_FILE_SMF1;
    706             break;
    707 
    708 /* now handled in eas_public.c */
    709 #if 0
    710         case PARSER_DATA_POLYPHONY:
    711             if (pSMFData->pSynth)
    712                 VMGetPolyphony(pEASData->pVoiceMgr, pSMFData->pSynth, pValue);
    713             else
    714                 return EAS_ERROR_NOT_VALID_IN_THIS_STATE;
    715             break;
    716 
    717         case PARSER_DATA_PRIORITY:
    718             if (pSMFData->pSynth)
    719                 VMGetPriority(pEASData->pVoiceMgr, pSMFData->pSynth, pValue);
    720             break;
    721 
    722         /* set transposition */
    723         case PARSER_DATA_TRANSPOSITION:
    724             *pValue = pSMFData->transposition;
    725             break;
    726 #endif
    727 
    728         case PARSER_DATA_SYNTH_HANDLE:
    729             *pValue = (EAS_I32) pSMFData->pSynth;
    730             break;
    731 
    732         default:
    733             return EAS_ERROR_INVALID_PARAMETER;
    734     }
    735 
    736     return EAS_SUCCESS;
    737 }
    738 
    739 /*----------------------------------------------------------------------------
    740  * SMF_GetVarLenData()
    741  *----------------------------------------------------------------------------
    742  * Purpose:
    743  * Reads a varible length quantity from an SMF file
    744  *
    745  * Inputs:
    746  *
    747  *
    748  * Outputs:
    749  *
    750  *
    751  * Side Effects:
    752  *
    753  *----------------------------------------------------------------------------
    754 */
    755 static EAS_RESULT SMF_GetVarLenData (EAS_HW_DATA_HANDLE hwInstData, EAS_FILE_HANDLE fileHandle, EAS_U32 *pData)
    756 {
    757     EAS_RESULT result;
    758     EAS_U32 data;
    759     EAS_U8 c;
    760 
    761     /* read until bit 7 is zero */
    762     data = 0;
    763     do
    764     {
    765         if ((result = EAS_HWGetByte(hwInstData, fileHandle,&c)) != EAS_SUCCESS)
    766             return result;
    767         data = (data << 7) | (c & 0x7f);
    768     } while (c & 0x80);
    769     *pData = data;
    770     return EAS_SUCCESS;
    771 }
    772 
    773 /*----------------------------------------------------------------------------
    774  * SMF_GetDeltaTime()
    775  *----------------------------------------------------------------------------
    776  * Purpose:
    777  * Reads a varible length quantity from an SMF file
    778  *
    779  * Inputs:
    780  *
    781  *
    782  * Outputs:
    783  *
    784  *
    785  * Side Effects:
    786  *
    787  *----------------------------------------------------------------------------
    788 */
    789 static EAS_RESULT SMF_GetDeltaTime (EAS_HW_DATA_HANDLE hwInstData, S_SMF_STREAM *pSMFStream)
    790 {
    791     EAS_RESULT result;
    792     EAS_U32 ticks;
    793 
    794     if ((result = SMF_GetVarLenData(hwInstData, pSMFStream->fileHandle, &ticks)) != EAS_SUCCESS)
    795         return result;
    796 
    797     pSMFStream->ticks += ticks;
    798     return EAS_SUCCESS;
    799 }
    800 
    801 /*----------------------------------------------------------------------------
    802  * SMF_ParseMetaEvent()
    803  *----------------------------------------------------------------------------
    804  * Purpose:
    805  * Reads a varible length quantity from an SMF file
    806  *
    807  * Inputs:
    808  *
    809  *
    810  * Outputs:
    811  *
    812  *
    813  * Side Effects:
    814  *
    815  *----------------------------------------------------------------------------
    816 */
    817 static EAS_RESULT SMF_ParseMetaEvent (S_EAS_DATA *pEASData, S_SMF_DATA *pSMFData, S_SMF_STREAM *pSMFStream)
    818 {
    819     EAS_RESULT result;
    820     EAS_U32 len;
    821     EAS_I32 pos;
    822     EAS_U32 temp;
    823     EAS_U8 c;
    824 
    825     /* get the meta-event type */
    826     if ((result = EAS_HWGetByte(pEASData->hwInstData, pSMFStream->fileHandle, &c)) != EAS_SUCCESS)
    827         return result;
    828 
    829     /* get the length */
    830     if ((result = SMF_GetVarLenData(pEASData->hwInstData, pSMFStream->fileHandle, &len)) != EAS_SUCCESS)
    831         return result;
    832 
    833     /* get the current file position so we can skip the event */
    834     if ((result = EAS_HWFilePos(pEASData->hwInstData, pSMFStream->fileHandle, &pos)) != EAS_SUCCESS)
    835         return result;
    836     pos += (EAS_I32) len;
    837 
    838     /* end of track? */
    839     if (c == SMF_META_END_OF_TRACK)
    840     {
    841         { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "Meta-event: end of track\n", c, len); */ }
    842         pSMFStream->ticks = SMF_END_OF_TRACK;
    843     }
    844 
    845     /* tempo event? */
    846     else if (c == SMF_META_TEMPO)
    847     {
    848         /* read the 3-byte timebase value */
    849         temp = 0;
    850         while (len--)
    851         {
    852             if ((result = EAS_HWGetByte(pEASData->hwInstData, pSMFStream->fileHandle, &c)) != EAS_SUCCESS)
    853                 return result;
    854             temp = (temp << 8) | c;
    855         }
    856 
    857         pSMFData->tickConv = (EAS_U16) (((temp * 1024) / pSMFData->ppqn + 500) / 1000);
    858         pSMFData->flags |= SMF_FLAGS_HAS_TEMPO;
    859     }
    860 
    861     /* check for time signature - see iMelody spec V1.4 section 4.1.2.2.3.6 */
    862     else if (c == SMF_META_TIME_SIGNATURE)
    863     {
    864         pSMFData->flags |= SMF_FLAGS_HAS_TIME_SIG;
    865     }
    866 
    867     /* if the host has registered a metadata callback return the metadata */
    868     else if (pSMFData->metadata.callback)
    869     {
    870         EAS_I32 readLen;
    871         E_EAS_METADATA_TYPE metaType;
    872 
    873         metaType = EAS_METADATA_UNKNOWN;
    874 
    875         /* only process title on the first track */
    876         if (c == SMF_META_SEQTRK_NAME)
    877             metaType = EAS_METADATA_TITLE;
    878         else if (c == SMF_META_TEXT)
    879             metaType = EAS_METADATA_TEXT;
    880         else if (c == SMF_META_COPYRIGHT)
    881             metaType = EAS_METADATA_COPYRIGHT;
    882         else if (c == SMF_META_LYRIC)
    883             metaType = EAS_METADATA_LYRIC;
    884 
    885         if (metaType != EAS_METADATA_UNKNOWN)
    886         {
    887             readLen = pSMFData->metadata.bufferSize - 1;
    888             if ((EAS_I32) len < readLen)
    889                 readLen = (EAS_I32) len;
    890             if ((result = EAS_HWReadFile(pEASData->hwInstData, pSMFStream->fileHandle, pSMFData->metadata.buffer, readLen, &readLen)) != EAS_SUCCESS)
    891                 return result;
    892             pSMFData->metadata.buffer[readLen] = 0;
    893             pSMFData->metadata.callback(metaType, pSMFData->metadata.buffer, pSMFData->metadata.pUserData);
    894         }
    895     }
    896 
    897     /* position file to next event - in case we ignored all or part of the meta-event */
    898     if ((result = EAS_HWFileSeek(pEASData->hwInstData, pSMFStream->fileHandle, pos)) != EAS_SUCCESS)
    899         return result;
    900 
    901     { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "Meta-event: type=%02x, len=%d\n", c, len); */ }
    902     return EAS_SUCCESS;
    903 }
    904 
    905 /*----------------------------------------------------------------------------
    906  * SMF_ParseSysEx()
    907  *----------------------------------------------------------------------------
    908  * Purpose:
    909  * Reads a varible length quantity from an SMF file
    910  *
    911  * Inputs:
    912  *
    913  *
    914  * Outputs:
    915  *
    916  *
    917  * Side Effects:
    918  *
    919  *----------------------------------------------------------------------------
    920 */
    921 static EAS_RESULT SMF_ParseSysEx (S_EAS_DATA *pEASData, S_SMF_DATA *pSMFData, S_SMF_STREAM *pSMFStream, EAS_U8 f0, EAS_INT parserMode)
    922 {
    923     EAS_RESULT result;
    924     EAS_U32 len;
    925     EAS_U8 c;
    926 
    927     /* get the length */
    928     if ((result = SMF_GetVarLenData(pEASData->hwInstData, pSMFStream->fileHandle, &len)) != EAS_SUCCESS)
    929         return result;
    930 
    931     /* start of SysEx message? */
    932     if (f0 == 0xf0)
    933     {
    934         if ((result = EAS_ParseMIDIStream(pEASData, pSMFData->pSynth, &pSMFStream->midiStream, f0, parserMode)) != EAS_SUCCESS)
    935             return result;
    936     }
    937 
    938     /* feed the SysEx to the stream parser */
    939     while (len--)
    940     {
    941         if ((result = EAS_HWGetByte(pEASData->hwInstData, pSMFStream->fileHandle, &c)) != EAS_SUCCESS)
    942             return result;
    943         if ((result = EAS_ParseMIDIStream(pEASData, pSMFData->pSynth, &pSMFStream->midiStream, c, parserMode)) != EAS_SUCCESS)
    944             return result;
    945 
    946         /* check for GM system ON */
    947         if (pSMFStream->midiStream.flags & MIDI_FLAG_GM_ON)
    948             pSMFData->flags |= SMF_FLAGS_HAS_GM_ON;
    949     }
    950 
    951     return EAS_SUCCESS;
    952 }
    953 
    954 /*----------------------------------------------------------------------------
    955  * SMF_ParseEvent()
    956  *----------------------------------------------------------------------------
    957  * Purpose:
    958  * Reads a varible length quantity from an SMF file
    959  *
    960  * Inputs:
    961  *
    962  *
    963  * Outputs:
    964  *
    965  *
    966  * Side Effects:
    967  *
    968  *----------------------------------------------------------------------------
    969 */
    970 static EAS_RESULT SMF_ParseEvent (S_EAS_DATA *pEASData, S_SMF_DATA *pSMFData, S_SMF_STREAM *pSMFStream, EAS_INT parserMode)
    971 {
    972     EAS_RESULT result;
    973     EAS_U8 c;
    974 
    975     /* get the event type */
    976     if ((result = EAS_HWGetByte(pEASData->hwInstData, pSMFStream->fileHandle, &c)) != EAS_SUCCESS)
    977         return result;
    978 
    979     /* parse meta-event */
    980     if (c == 0xff)
    981     {
    982         if ((result = SMF_ParseMetaEvent(pEASData, pSMFData, pSMFStream)) != EAS_SUCCESS)
    983             return result;
    984     }
    985 
    986     /* parse SysEx */
    987     else if ((c == 0xf0) || (c == 0xf7))
    988     {
    989         if ((result = SMF_ParseSysEx(pEASData, pSMFData, pSMFStream, c, parserMode)) != EAS_SUCCESS)
    990             return result;
    991     }
    992 
    993     /* parse MIDI message */
    994     else
    995     {
    996         if ((result = EAS_ParseMIDIStream(pEASData, pSMFData->pSynth, &pSMFStream->midiStream, c, parserMode)) != EAS_SUCCESS)
    997             return result;
    998 
    999         /* keep streaming data to the MIDI parser until the message is complete */
   1000         while (pSMFStream->midiStream.pending)
   1001         {
   1002             if ((result = EAS_HWGetByte(pEASData->hwInstData, pSMFStream->fileHandle, &c)) != EAS_SUCCESS)
   1003                 return result;
   1004             if ((result = EAS_ParseMIDIStream(pEASData, pSMFData->pSynth, &pSMFStream->midiStream, c, parserMode)) != EAS_SUCCESS)
   1005                 return result;
   1006         }
   1007 
   1008     }
   1009 
   1010     /* chase mode logic */
   1011     if (pSMFData->time == 0)
   1012     {
   1013         if (pSMFData->flags & SMF_FLAGS_CHASE_MODE)
   1014         {
   1015             if (pSMFStream->midiStream.flags & MIDI_FLAG_FIRST_NOTE)
   1016                 pSMFData->flags &= ~SMF_FLAGS_CHASE_MODE;
   1017         }
   1018         else if ((pSMFData->flags & SMF_FLAGS_SETUP_BAR) == SMF_FLAGS_SETUP_BAR)
   1019             pSMFData->flags = (pSMFData->flags & ~SMF_FLAGS_SETUP_BAR) | SMF_FLAGS_CHASE_MODE;
   1020     }
   1021 
   1022     return EAS_SUCCESS;
   1023 }
   1024 
   1025 /*----------------------------------------------------------------------------
   1026  * SMF_ParseHeader()
   1027  *----------------------------------------------------------------------------
   1028  * Purpose:
   1029  * Parses the header of an SMF file, allocates memory the stream parsers and initializes the
   1030  * stream parsers.
   1031  *
   1032  * Inputs:
   1033  * pEASData         - pointer to overall EAS data structure
   1034  * pSMFData         - pointer to parser instance data
   1035  * fileHandle       - file handle
   1036  * fileOffset       - offset in the file where the header data starts, usually 0
   1037  *
   1038  *
   1039  * Outputs:
   1040  *
   1041  *
   1042  * Side Effects:
   1043  *
   1044  *----------------------------------------------------------------------------
   1045 */
   1046 /*lint -e{801} we know that 'goto' is deprecated - but it's cleaner in this case */
   1047 EAS_RESULT SMF_ParseHeader (EAS_HW_DATA_HANDLE hwInstData, S_SMF_DATA *pSMFData)
   1048 {
   1049     EAS_RESULT result;
   1050     EAS_I32 i;
   1051     EAS_U16 division;
   1052     EAS_U32 chunkSize;
   1053     EAS_U32 chunkStart;
   1054     EAS_U32 temp;
   1055     EAS_U32 ticks;
   1056 
   1057     /* rewind the file and find the end of the header chunk */
   1058     if ((result = EAS_HWFileSeek(hwInstData, pSMFData->fileHandle, pSMFData->fileOffset + SMF_OFS_HEADER_SIZE)) != EAS_SUCCESS)
   1059         goto ReadError;
   1060     if ((result = EAS_HWGetDWord(hwInstData, pSMFData->fileHandle, &chunkSize, EAS_TRUE)) != EAS_SUCCESS)
   1061         goto ReadError;
   1062 
   1063     /* determine the number of tracks */
   1064     if ((result = EAS_HWFileSeek(hwInstData, pSMFData->fileHandle, pSMFData->fileOffset + SMF_OFS_NUM_TRACKS)) != EAS_SUCCESS)
   1065         goto ReadError;
   1066     if ((result = EAS_HWGetWord(hwInstData, pSMFData->fileHandle, &pSMFData->numStreams, EAS_TRUE)) != EAS_SUCCESS)
   1067         goto ReadError;
   1068 
   1069     /* limit the number of tracks */
   1070     if (pSMFData->numStreams > MAX_SMF_STREAMS)
   1071     {
   1072         { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "SMF file contains %u tracks, playing %d tracks\n", pSMFData->numStreams, MAX_SMF_STREAMS); */ }
   1073         pSMFData->numStreams = MAX_SMF_STREAMS;
   1074     } else if (pSMFData->numStreams == 0)
   1075     {
   1076         /* avoid 0 sized allocation */
   1077         return EAS_ERROR_PARAMETER_RANGE;
   1078     }
   1079 
   1080     /* get the time division */
   1081     if ((result = EAS_HWGetWord(hwInstData, pSMFData->fileHandle, &division, EAS_TRUE)) != EAS_SUCCESS)
   1082         goto ReadError;
   1083 
   1084     /* setup default timebase for 120 bpm */
   1085     pSMFData->ppqn = 192;
   1086     if (!division || division & 0x8000)
   1087         { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING,"No support for SMPTE code timebase\n"); */ }
   1088     else
   1089         pSMFData->ppqn = (division & 0x7fff);
   1090     pSMFData->tickConv = (EAS_U16) (((SMF_DEFAULT_TIMEBASE * 1024) / pSMFData->ppqn + 500) / 1000);
   1091 
   1092     /* dynamic memory allocation, allocate memory for streams */
   1093     if (pSMFData->streams == NULL)
   1094     {
   1095         pSMFData->streams = EAS_HWMalloc(hwInstData,sizeof(S_SMF_STREAM) * pSMFData->numStreams);
   1096         if (pSMFData->streams == NULL)
   1097             return EAS_ERROR_MALLOC_FAILED;
   1098 
   1099         /* zero the memory to insure complete initialization */
   1100         EAS_HWMemSet((void *)(pSMFData->streams), 0, sizeof(S_SMF_STREAM) * pSMFData->numStreams);
   1101     }
   1102 
   1103     /* find the start of each track */
   1104     chunkStart = (EAS_U32) pSMFData->fileOffset;
   1105     ticks = 0x7fffffffL;
   1106     pSMFData->nextStream = NULL;
   1107     for (i = 0; i < pSMFData->numStreams; i++)
   1108     {
   1109 
   1110         for (;;)
   1111         {
   1112 
   1113             /* calculate start of next chunk - checking for errors */
   1114             temp = chunkStart + SMF_CHUNK_INFO_SIZE + chunkSize;
   1115             if (temp <= chunkStart)
   1116             {
   1117                 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING,"Error in chunk size at offset %d\n", chunkStart); */ }
   1118                 return EAS_ERROR_FILE_FORMAT;
   1119             }
   1120             chunkStart = temp;
   1121 
   1122             /* seek to the start of the next chunk */
   1123             if ((result = EAS_HWFileSeek(hwInstData, pSMFData->fileHandle, (EAS_I32) chunkStart)) != EAS_SUCCESS)
   1124                 goto ReadError;
   1125 
   1126             /* read the chunk identifier */
   1127             if ((result = EAS_HWGetDWord(hwInstData, pSMFData->fileHandle, &temp, EAS_TRUE)) != EAS_SUCCESS)
   1128                 goto ReadError;
   1129 
   1130             /* read the chunk size */
   1131             if ((result = EAS_HWGetDWord(hwInstData, pSMFData->fileHandle, &chunkSize, EAS_TRUE)) != EAS_SUCCESS)
   1132                 goto ReadError;
   1133 
   1134             /* make sure this is an 'MTrk' chunk */
   1135             if (temp == SMF_CHUNK_TYPE_TRACK)
   1136                 break;
   1137 
   1138             { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING,"Unexpected chunk type: 0x%08x\n", temp); */ }
   1139         }
   1140 
   1141         /* initalize some data */
   1142         pSMFData->streams[i].ticks = 0;
   1143         pSMFData->streams[i].fileHandle = pSMFData->fileHandle;
   1144 
   1145         /* NULL the file handle so we don't try to close it twice */
   1146         pSMFData->fileHandle = NULL;
   1147 
   1148         /* save this file position as the start of the track */
   1149         pSMFData->streams[i].startFilePos = (EAS_I32) chunkStart + SMF_CHUNK_INFO_SIZE;
   1150 
   1151         /* initalize the MIDI parser data */
   1152         EAS_InitMIDIStream(&pSMFData->streams[i].midiStream);
   1153 
   1154         /* parse the first delta time in each stream */
   1155         if ((result = SMF_GetDeltaTime(hwInstData, &pSMFData->streams[i])) != EAS_SUCCESS)
   1156                 goto ReadError;
   1157 
   1158         if (pSMFData->streams[i].ticks < ticks)
   1159         {
   1160             ticks = pSMFData->streams[i].ticks;
   1161             pSMFData->nextStream = &pSMFData->streams[i];
   1162         }
   1163 
   1164         /* more tracks to do, create a duplicate file handle */
   1165         if (i < (pSMFData->numStreams - 1))
   1166         {
   1167             if ((result = EAS_HWDupHandle(hwInstData, pSMFData->streams[i].fileHandle, &pSMFData->fileHandle)) != EAS_SUCCESS)
   1168                 goto ReadError;
   1169         }
   1170     }
   1171 
   1172     /* update the time of the next event */
   1173     if (pSMFData->nextStream)
   1174         SMF_UpdateTime(pSMFData, pSMFData->nextStream->ticks);
   1175 
   1176     return EAS_SUCCESS;
   1177 
   1178     /* ugly goto: but simpler than structured */
   1179     ReadError:
   1180         if (result == EAS_EOF)
   1181             return EAS_ERROR_FILE_FORMAT;
   1182         return result;
   1183 }
   1184 
   1185 /*----------------------------------------------------------------------------
   1186  * SMF_UpdateTime()
   1187  *----------------------------------------------------------------------------
   1188  * Purpose:
   1189  * Update the millisecond time base by converting the ticks into millieconds
   1190  *
   1191  * Inputs:
   1192  *
   1193  *
   1194  * Outputs:
   1195  *
   1196  *
   1197  * Side Effects:
   1198  *
   1199  *----------------------------------------------------------------------------
   1200 */
   1201 static void SMF_UpdateTime (S_SMF_DATA *pSMFData, EAS_U32 ticks)
   1202 {
   1203     EAS_U32 temp1, temp2;
   1204 
   1205     if (pSMFData->flags & SMF_FLAGS_CHASE_MODE)
   1206         return;
   1207 
   1208     temp1 = (ticks >> 10) * pSMFData->tickConv;
   1209     temp2 = (ticks & 0x3ff) * pSMFData->tickConv;
   1210     pSMFData->time += (EAS_I32)((temp1 << 8) + (temp2 >> 2));
   1211 }
   1212 
   1213