Home | History | Annotate | Download | only in lib_src
      1 /*----------------------------------------------------------------------------
      2  *
      3  * File:
      4  * eas_tonecontrol.c
      5  *
      6  * Contents and purpose:
      7  * MMAPI ToneControl parser
      8  *
      9  * Copyright Sonic Network Inc. 2006
     10 
     11  * Licensed under the Apache License, Version 2.0 (the "License");
     12  * you may not use this file except in compliance with the License.
     13  * You may obtain a copy of the License at
     14  *
     15  *      http://www.apache.org/licenses/LICENSE-2.0
     16  *
     17  * Unless required by applicable law or agreed to in writing, software
     18  * distributed under the License is distributed on an "AS IS" BASIS,
     19  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     20  * See the License for the specific language governing permissions and
     21  * limitations under the License.
     22  *
     23  *----------------------------------------------------------------------------
     24  * Revision Control:
     25  *   $Revision: 795 $
     26  *   $Date: 2007-08-01 00:14:45 -0700 (Wed, 01 Aug 2007) $
     27  *----------------------------------------------------------------------------
     28 */
     29 
     30 #include "eas_data.h"
     31 #include "eas_miditypes.h"
     32 #include "eas_parser.h"
     33 #include "eas_report.h"
     34 #include "eas_host.h"
     35 #include "eas_midi.h"
     36 #include "eas_config.h"
     37 #include "eas_vm_protos.h"
     38 #include "eas_tcdata.h"
     39 
     40 
     41 /* default channel and program for TC playback */
     42 #define TC_CHANNEL              0
     43 #define TC_PROGRAM              80
     44 #define TC_VELOCITY             127
     45 
     46 #define TC_FIELD_SILENCE        -1
     47 #define TC_FIELD_VERSION        -2
     48 #define TC_FIELD_TEMPO          -3
     49 #define TC_FIELD_RESOLUTION     -4
     50 #define TC_FIELD_BLOCK_START    -5
     51 #define TC_FIELD_BLOCK_END      -6
     52 #define TC_FIELD_PLAY_BLOCK     -7
     53 #define TC_FIELD_SET_VOLUME     -8
     54 #define TC_FIELD_REPEAT         -9
     55 #define TC_FIELD_INVALID        -10
     56 
     57 /* convert 0-100 volume to 0-127 velocity using fixed point */
     58 #define TC_VOLUME_CONV          21307064
     59 #define TC_VOLUME_SHIFT         24
     60 
     61 
     62 /* local prototypes */
     63 static EAS_RESULT TC_CheckFileType (S_EAS_DATA *pEASData, EAS_FILE_HANDLE fileHandle, EAS_VOID_PTR *ppHandle, EAS_I32 offset);
     64 static EAS_RESULT TC_Prepare (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData);
     65 static EAS_RESULT TC_Time (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_U32 *pTime);
     66 static EAS_RESULT TC_Event (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_INT parserMode);
     67 static EAS_RESULT TC_State (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_STATE *pState);
     68 static EAS_RESULT TC_Close (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData);
     69 static EAS_RESULT TC_Reset (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData);
     70 static EAS_RESULT TC_Pause (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData);
     71 static EAS_RESULT TC_Resume (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData);
     72 static EAS_RESULT TC_SetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 value);
     73 static EAS_RESULT TC_GetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 *pValue);
     74 static EAS_RESULT TC_ParseHeader (S_EAS_DATA *pEASData, S_TC_DATA* pData);
     75 static EAS_RESULT TC_StartNote (S_EAS_DATA *pEASData, S_TC_DATA* pData, EAS_INT parserMode, EAS_I8 note);
     76 static EAS_RESULT TC_GetRepeat (S_EAS_DATA *pEASData, S_TC_DATA* pData, EAS_INT parserMode);
     77 static EAS_RESULT TC_PlayBlock (S_EAS_DATA *pEASData, S_TC_DATA* pData);
     78 static EAS_RESULT TC_BlockEnd (S_EAS_DATA *pEASData, S_TC_DATA* pData);
     79 static EAS_RESULT TC_GetVolume (S_EAS_DATA *pEASData, S_TC_DATA* pData);
     80 static EAS_RESULT TC_GetTempo (S_EAS_DATA *pEASData, S_TC_DATA* pData);
     81 static EAS_RESULT TC_GetResolution (S_EAS_DATA *pEASData, S_TC_DATA* pData);
     82 static EAS_RESULT TC_GetNextChar (EAS_HW_DATA_HANDLE hwInstData, S_TC_DATA *pData, EAS_I8 *pValue);
     83 static void TC_PutBackChar (S_TC_DATA *pData, EAS_I8 value);
     84 
     85 /* calculate a new tick time based on resolution & tempo */
     86 EAS_INLINE void TC_CalcTimeBase (S_TC_DATA *pData)
     87 {
     88 
     89     /* ticks in 256ths of a millisecond */
     90     pData->tick = ((60 * 1000) << 8) / (pData->tempo * pData->resolution);
     91 }
     92 
     93 /*----------------------------------------------------------------------------
     94  *
     95  * EAS_TC_Parser
     96  *
     97  * This structure contains the functional interface for the iMelody parser
     98  *----------------------------------------------------------------------------
     99 */
    100 const S_FILE_PARSER_INTERFACE EAS_TC_Parser =
    101 {
    102     TC_CheckFileType,
    103     TC_Prepare,
    104     TC_Time,
    105     TC_Event,
    106     TC_State,
    107     TC_Close,
    108     TC_Reset,
    109     TC_Pause,
    110     TC_Resume,
    111     NULL,
    112     TC_SetData,
    113     TC_GetData,
    114     NULL
    115 };
    116 
    117 /*----------------------------------------------------------------------------
    118  * TC_CheckFileType()
    119  *----------------------------------------------------------------------------
    120  * Purpose:
    121  * Check the file type to see if we can parse it
    122  *
    123  * Inputs:
    124  * pEASData         - pointer to overall EAS data structure
    125  * handle           - pointer to file handle
    126  *
    127  * Outputs:
    128  *
    129  *
    130  * Side Effects:
    131  *
    132  *----------------------------------------------------------------------------
    133 */
    134 static EAS_RESULT TC_CheckFileType (S_EAS_DATA *pEASData, EAS_FILE_HANDLE fileHandle, EAS_VOID_PTR *ppHandle, EAS_I32 offset)
    135 {
    136     S_TC_DATA data;
    137     S_TC_DATA *pData;
    138 
    139     /* init data */
    140     EAS_HWMemSet(&data, 0, sizeof(S_TC_DATA));
    141     data.fileHandle = fileHandle;
    142     data.fileOffset = offset;
    143     *ppHandle= NULL;
    144 
    145     /* see if we can parse the header */
    146     if (TC_ParseHeader(pEASData, &data) == EAS_SUCCESS)
    147     {
    148 
    149         /* check for static memory allocation */
    150         if (pEASData->staticMemoryModel)
    151             pData = EAS_CMEnumOptData(EAS_MODULE_MMAPI_TONE_CONTROL);
    152         else
    153             pData = EAS_HWMalloc(pEASData->hwInstData, sizeof(S_TC_DATA));
    154         if (!pData)
    155             return EAS_ERROR_MALLOC_FAILED;
    156 
    157         /* copy data to persistent storage */
    158         EAS_HWMemCpy(pData, &data, sizeof(S_TC_DATA));
    159 
    160         /* return a pointer to the instance data */
    161         pData->state = EAS_STATE_OPEN;
    162         *ppHandle = pData;
    163     }
    164 
    165     return EAS_SUCCESS;
    166 }
    167 
    168 /*----------------------------------------------------------------------------
    169  * TC_Prepare()
    170  *----------------------------------------------------------------------------
    171  * Purpose:
    172  * Prepare to parse the file. Allocates instance data (or uses static allocation for
    173  * static memory model).
    174  *
    175  * Inputs:
    176  * pEASData         - pointer to overall EAS data structure
    177  * handle           - pointer to file handle
    178  *
    179  * Outputs:
    180  *
    181  *
    182  * Side Effects:
    183  *
    184  *----------------------------------------------------------------------------
    185 */
    186 static EAS_RESULT TC_Prepare (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
    187 {
    188     S_TC_DATA* pData;
    189     EAS_RESULT result;
    190 
    191     /* check for valid state */
    192     pData = (S_TC_DATA*) pInstData;
    193     if (pData->state != EAS_STATE_OPEN)
    194         return EAS_ERROR_NOT_VALID_IN_THIS_STATE;
    195 
    196     /* instantiate a synthesizer */
    197     if ((result = VMInitMIDI(pEASData, &pData->pSynth)) != EAS_SUCCESS)
    198     {
    199         { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "VMInitMIDI returned %d\n", result); */ }
    200         return result;
    201     }
    202 
    203     /* set to ready state */
    204     pData->state = EAS_STATE_READY;
    205     return EAS_SUCCESS;
    206 }
    207 
    208 /*----------------------------------------------------------------------------
    209  * TC_Time()
    210  *----------------------------------------------------------------------------
    211  * Purpose:
    212  * Returns the time of the next event in msecs
    213  *
    214  * Inputs:
    215  * pEASData         - pointer to overall EAS data structure
    216  * handle           - pointer to file handle
    217  * pTime            - pointer to variable to hold time of next event (in msecs)
    218  *
    219  * Outputs:
    220  *
    221  *
    222  * Side Effects:
    223  *
    224  *----------------------------------------------------------------------------
    225 */
    226 /*lint -esym(715, pEASData) reserved for future use */
    227 static EAS_RESULT TC_Time (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_U32 *pTime)
    228 {
    229     S_TC_DATA *pData;
    230 
    231     pData = (S_TC_DATA*) pInstData;
    232 
    233     /* return time in milliseconds */
    234     /*lint -e{704} use shift instead of division */
    235     *pTime = pData->time >> 8;
    236     return EAS_SUCCESS;
    237 }
    238 
    239 /*----------------------------------------------------------------------------
    240  * TC_Event()
    241  *----------------------------------------------------------------------------
    242  * Purpose:
    243  * Parse the next event in the file
    244  *
    245  * Inputs:
    246  * pEASData         - pointer to overall EAS data structure
    247  * handle           - pointer to file handle
    248  *
    249  * Outputs:
    250  *
    251  *
    252  * Side Effects:
    253  *
    254  *----------------------------------------------------------------------------
    255 */
    256 static EAS_RESULT TC_Event (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_INT parserMode)
    257 {
    258     S_TC_DATA* pData;
    259     EAS_RESULT result;
    260     EAS_I8 temp;
    261 
    262     pData = (S_TC_DATA*) pInstData;
    263     if (pData->state >= EAS_STATE_OPEN)
    264         return EAS_SUCCESS;
    265 
    266     /* initialize MIDI channel when the track starts playing */
    267     if (pData->time == 0)
    268     {
    269         /* set program to square lead */
    270         VMProgramChange(pEASData->pVoiceMgr, pData->pSynth, TC_CHANNEL, TC_PROGRAM);
    271 
    272         /* set channel volume to max */
    273         VMControlChange(pEASData->pVoiceMgr, pData->pSynth, TC_CHANNEL, 7, 127);
    274     }
    275 
    276     /* check for end of note */
    277     if (pData->note >= 0)
    278     {
    279         /* stop the note */
    280         VMStopNote(pEASData->pVoiceMgr, pData->pSynth, TC_CHANNEL, (EAS_U8) pData->note, 0);
    281 
    282         /* check for repeat note */
    283         if (pData->repeatCount)
    284         {
    285             pData->repeatCount--;
    286             pData->time += pData->length;
    287             if ((pData->note >= 0) && (parserMode == eParserModePlay))
    288                 VMStartNote(pEASData->pVoiceMgr, pData->pSynth, TC_CHANNEL, (EAS_U8) pData->note, pData->volume);
    289             return EAS_SUCCESS;
    290         }
    291 
    292         pData->note = TC_FIELD_SILENCE;
    293     }
    294 
    295     /* parse stream until we get a note or rest */
    296     for (;;)
    297     {
    298 
    299         /* get next byte from stream */
    300         if ((result = TC_GetNextChar(pEASData->hwInstData, pData, &temp)) != EAS_SUCCESS)
    301         {
    302             if (result == EAS_EOF)
    303             {
    304                 pData->state = EAS_STATE_STOPPING;
    305                 return EAS_SUCCESS;
    306             }
    307             break;
    308         }
    309 
    310         /* check for musical events */
    311         if (temp >= TC_FIELD_SILENCE)
    312         {
    313             result = TC_StartNote(pEASData, pData, parserMode, temp);
    314             break;
    315         }
    316 
    317         /* must be a control field */
    318         switch (temp)
    319         {
    320             case TC_FIELD_TEMPO:
    321                 result = TC_GetTempo(pEASData, pData);
    322                 break;
    323 
    324             case TC_FIELD_RESOLUTION:
    325                 result = TC_GetResolution(pEASData, pData);
    326                 break;
    327 
    328             case TC_FIELD_SET_VOLUME:
    329                 result = TC_GetVolume(pEASData, pData);
    330                 break;
    331 
    332             case TC_FIELD_REPEAT:
    333                 result = TC_GetRepeat(pEASData, pData, parserMode);
    334                 break;
    335 
    336             case TC_FIELD_PLAY_BLOCK:
    337                 result = TC_PlayBlock(pEASData, pData);
    338                 break;
    339 
    340             case TC_FIELD_BLOCK_START:
    341                 result = TC_GetNextChar(pEASData->hwInstData, pData, &temp);
    342                 break;
    343 
    344             case TC_FIELD_BLOCK_END:
    345                 result = TC_BlockEnd(pEASData, pData);
    346                 break;
    347 
    348             default:
    349                 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "Unexpected byte 0x%02x in ToneControl stream\n", temp); */ }
    350                 result = EAS_ERROR_FILE_FORMAT;
    351         }
    352 
    353         /* check for error */
    354         if (result != EAS_SUCCESS)
    355             break;
    356     }
    357 
    358     /* check for error */
    359     if (result != EAS_SUCCESS)
    360     {
    361         if (result == EAS_EOF)
    362             result = EAS_ERROR_FILE_FORMAT;
    363         pData->state = EAS_STATE_ERROR;
    364     }
    365     else
    366         pData->state = EAS_STATE_PLAY;
    367     return result;
    368 }
    369 
    370 /*----------------------------------------------------------------------------
    371  * TC_State()
    372  *----------------------------------------------------------------------------
    373  * Purpose:
    374  * Returns the current state of the stream
    375  *
    376  * Inputs:
    377  * pEASData         - pointer to overall EAS data structure
    378  * handle           - pointer to file handle
    379  * pState           - pointer to variable to store state
    380  *
    381  * Outputs:
    382  *
    383  *
    384  * Side Effects:
    385  *
    386  *----------------------------------------------------------------------------
    387 */
    388 /*lint -esym(715, pEASData) reserved for future use */
    389 static EAS_RESULT TC_State (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 *pState)
    390 {
    391     S_TC_DATA* pData;
    392 
    393     /* establish pointer to instance data */
    394     pData = (S_TC_DATA*) pInstData;
    395 
    396     /* if stopping, check to see if synth voices are active */
    397     if (pData->state == EAS_STATE_STOPPING)
    398     {
    399         if (VMActiveVoices(pData->pSynth) == 0)
    400             pData->state = EAS_STATE_STOPPED;
    401     }
    402 
    403     if (pData->state == EAS_STATE_PAUSING)
    404     {
    405         if (VMActiveVoices(pData->pSynth) == 0)
    406             pData->state = EAS_STATE_PAUSED;
    407     }
    408 
    409     /* return current state */
    410     *pState = pData->state;
    411     return EAS_SUCCESS;
    412 }
    413 
    414 /*----------------------------------------------------------------------------
    415  * TC_Close()
    416  *----------------------------------------------------------------------------
    417  * Purpose:
    418  * Close the file and clean up
    419  *
    420  * Inputs:
    421  * pEASData         - pointer to overall EAS data structure
    422  * handle           - pointer to file handle
    423  *
    424  * Outputs:
    425  *
    426  *
    427  * Side Effects:
    428  *
    429  *----------------------------------------------------------------------------
    430 */
    431 static EAS_RESULT TC_Close (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
    432 {
    433     S_TC_DATA* pData;
    434     EAS_RESULT result;
    435 
    436     pData = (S_TC_DATA*) pInstData;
    437 
    438     /* close the file */
    439     if ((result = EAS_HWCloseFile(pEASData->hwInstData, pData->fileHandle)) != EAS_SUCCESS)
    440             return result;
    441 
    442     /* free the synth */
    443     if (pData->pSynth != NULL)
    444         VMMIDIShutdown(pEASData, pData->pSynth);
    445 
    446     /* if using dynamic memory, free it */
    447     if (!pEASData->staticMemoryModel)
    448         EAS_HWFree(pEASData->hwInstData, pData);
    449 
    450     return EAS_SUCCESS;
    451 }
    452 
    453 /*----------------------------------------------------------------------------
    454  * TC_Reset()
    455  *----------------------------------------------------------------------------
    456  * Purpose:
    457  * Reset the sequencer. Used for locating backwards in the file.
    458  *
    459  * Inputs:
    460  * pEASData         - pointer to overall EAS data structure
    461  * handle           - pointer to file handle
    462  *
    463  * Outputs:
    464  *
    465  *
    466  * Side Effects:
    467  *
    468  *----------------------------------------------------------------------------
    469 */
    470 static EAS_RESULT TC_Reset (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
    471 {
    472     S_TC_DATA* pData;
    473     EAS_RESULT result;
    474 
    475     pData = (S_TC_DATA*) pInstData;
    476 
    477     /* reset the synth */
    478     VMReset(pEASData->pVoiceMgr, pData->pSynth, EAS_TRUE);
    479 
    480     /* reset time to zero */
    481     pData->time = 0;
    482 
    483     /* reset file position and re-parse header */
    484     pData->state = EAS_STATE_ERROR;
    485     if ((result = EAS_HWFileSeek(pEASData->hwInstData, pData->fileHandle, pData->fileOffset)) != EAS_SUCCESS)
    486         return result;
    487     if ((result = TC_ParseHeader (pEASData,  pData)) != EAS_SUCCESS)
    488         return result;
    489 
    490     pData->state = EAS_STATE_READY;
    491     return EAS_SUCCESS;
    492 }
    493 
    494 /*----------------------------------------------------------------------------
    495  * TC_Pause()
    496  *----------------------------------------------------------------------------
    497  * Purpose:
    498  * Pauses the sequencer. Mutes all voices and sets state to pause.
    499  *
    500  * Inputs:
    501  * pEASData         - pointer to overall EAS data structure
    502  * handle           - pointer to file handle
    503  *
    504  * Outputs:
    505  *
    506  *
    507  * Side Effects:
    508  *
    509  *----------------------------------------------------------------------------
    510 */
    511 static EAS_RESULT TC_Pause (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
    512 {
    513     S_TC_DATA *pData;
    514 
    515     /* can't pause a stopped stream */
    516     pData = (S_TC_DATA*) pInstData;
    517     if (pData->state == EAS_STATE_STOPPED)
    518         return EAS_ERROR_ALREADY_STOPPED;
    519 
    520     /* mute the synthesizer */
    521     VMMuteAllVoices(pEASData->pVoiceMgr, pData->pSynth);
    522     pData->state = EAS_STATE_PAUSING;
    523     return EAS_SUCCESS;
    524 }
    525 
    526 /*----------------------------------------------------------------------------
    527  * TC_Resume()
    528  *----------------------------------------------------------------------------
    529  * Purpose:
    530  * Resume playing after a pause, sets state back to playing.
    531  *
    532  * Inputs:
    533  * pEASData         - pointer to overall EAS data structure
    534  * handle           - pointer to file handle
    535  *
    536  * Outputs:
    537  *
    538  *
    539  * Side Effects:
    540  *
    541  *----------------------------------------------------------------------------
    542 */
    543 /*lint -esym(715, pEASData) reserved for future use */
    544 static EAS_RESULT TC_Resume (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
    545 {
    546     S_TC_DATA *pData;
    547 
    548     /* can't resume a stopped stream */
    549     pData = (S_TC_DATA*) pInstData;
    550     if (pData->state == EAS_STATE_STOPPED)
    551         return EAS_ERROR_ALREADY_STOPPED;
    552 
    553     /* nothing to do but resume playback */
    554     pData->state = EAS_STATE_PLAY;
    555     return EAS_SUCCESS;
    556 }
    557 
    558 /*----------------------------------------------------------------------------
    559  * TC_SetData()
    560  *----------------------------------------------------------------------------
    561  * Purpose:
    562  * Return file type
    563  *
    564  * Inputs:
    565  * pEASData         - pointer to overall EAS data structure
    566  * handle           - pointer to file handle
    567  *
    568  * Outputs:
    569  *
    570  *
    571  * Side Effects:
    572  *
    573  *----------------------------------------------------------------------------
    574 */
    575 /*lint -esym(715, pEASData, pInstData, value) reserved for future use */
    576 static EAS_RESULT TC_SetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 value)
    577 {
    578     /* we don't parse any metadata, but we need to return success here */
    579     if (param == PARSER_DATA_METADATA_CB)
    580         return EAS_SUCCESS;
    581 
    582     return EAS_ERROR_INVALID_PARAMETER;
    583 }
    584 
    585 /*----------------------------------------------------------------------------
    586  * TC_GetData()
    587  *----------------------------------------------------------------------------
    588  * Purpose:
    589  * Return file type
    590  *
    591  * Inputs:
    592  * pEASData         - pointer to overall EAS data structure
    593  * handle           - pointer to file handle
    594  *
    595  * Outputs:
    596  *
    597  *
    598  * Side Effects:
    599  *
    600  *----------------------------------------------------------------------------
    601 */
    602 /*lint -e{715} common with other parsers */
    603 static EAS_RESULT TC_GetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 *pValue)
    604 {
    605     S_TC_DATA *pData;
    606 
    607     pData = (S_TC_DATA *) pInstData;
    608     switch (param)
    609     {
    610         /* return file type as TC */
    611         case PARSER_DATA_FILE_TYPE:
    612             *pValue = EAS_FILE_MMAPI_TONE_CONTROL;
    613             break;
    614 
    615         case PARSER_DATA_SYNTH_HANDLE:
    616             *pValue = (EAS_I32) pData->pSynth;
    617             break;
    618 
    619     default:
    620             return EAS_ERROR_INVALID_PARAMETER;
    621     }
    622     return EAS_SUCCESS;
    623 }
    624 
    625 /*----------------------------------------------------------------------------
    626  * TC_ParseHeader()
    627  *----------------------------------------------------------------------------
    628  * Purpose:
    629  * Prepare to parse the file. Allocates instance data (or uses static allocation for
    630  * static memory model).
    631  *
    632  * Inputs:
    633  * pEASData         - pointer to overall EAS data structure
    634  * handle           - pointer to file handle
    635  *
    636  * Outputs:
    637  *
    638  *
    639  * Side Effects:
    640  *
    641  *----------------------------------------------------------------------------
    642 */
    643 static EAS_RESULT TC_ParseHeader (S_EAS_DATA *pEASData, S_TC_DATA* pData)
    644 {
    645     EAS_RESULT result;
    646     EAS_I8 temp;
    647 
    648     /* initialize some defaults */
    649     pData->time = 0;
    650     pData->tempo = 120;
    651     pData->resolution = 64;
    652     pData->volume = 127;
    653     pData->repeatCount = 0;
    654     pData->note = TC_FIELD_SILENCE;
    655     pData->byteAvail = EAS_FALSE;
    656 
    657     /* set default timebase */
    658     TC_CalcTimeBase(pData);
    659 
    660     /* seek to start of data */
    661     if ((result = EAS_HWFileSeek(pEASData->hwInstData, pData->fileHandle, pData->fileOffset)) != EAS_SUCCESS)
    662         return result;
    663 
    664     /* get version */
    665     if ((result = TC_GetNextChar(pEASData->hwInstData, pData, &temp)) != EAS_SUCCESS)
    666         return result;
    667 
    668     /* check for version number */
    669     if (temp == TC_FIELD_VERSION)
    670     {
    671         TC_GetNextChar(pEASData->hwInstData, pData, &temp);
    672 //      { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "ToneControl sequence version %d\n", temp); */ }
    673     }
    674     else
    675         return EAS_ERROR_FILE_FORMAT;
    676 
    677     /* parse the header data until we find the first note or block */
    678     for (;;)
    679     {
    680 
    681         /* get next byte from stream */
    682         if ((result = TC_GetNextChar(pEASData->hwInstData, pData, &temp)) != EAS_SUCCESS)
    683             return result;
    684 
    685         /* check for tempo */
    686         if (temp == TC_FIELD_TEMPO)
    687         {
    688             if ((result = TC_GetTempo(pEASData, pData)) != EAS_SUCCESS)
    689                 return result;
    690         }
    691 
    692         /* or resolution */
    693         else if (temp == TC_FIELD_TEMPO)
    694         {
    695             if ((result = TC_GetResolution(pEASData, pData)) != EAS_SUCCESS)
    696                 return result;
    697         }
    698 
    699         /* must be music data */
    700         else if (temp > TC_FIELD_INVALID)
    701         {
    702             TC_PutBackChar(pData, temp);
    703             return EAS_SUCCESS;
    704         }
    705 
    706         /* unknown codes */
    707         else
    708         {
    709             { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "Unexpected byte 0x%02x in ToneControl stream\n", temp); */ }
    710             return EAS_ERROR_FILE_FORMAT;
    711         }
    712     }
    713 }
    714 
    715 /*----------------------------------------------------------------------------
    716  * TC_StartNote()
    717  *----------------------------------------------------------------------------
    718  * Process a note or silence event
    719  *----------------------------------------------------------------------------
    720 */
    721 static EAS_RESULT TC_StartNote (S_EAS_DATA *pEASData, S_TC_DATA* pData, EAS_INT parserMode, EAS_I8 note)
    722 {
    723     EAS_I8 duration;
    724 
    725     /* get the duration */
    726     if (TC_GetNextChar(pEASData->hwInstData, pData, &duration) != EAS_SUCCESS)
    727         return EAS_ERROR_FILE_FORMAT;
    728 
    729     /* calculate time of next event */
    730     pData->length = (EAS_I32) duration * pData->tick;
    731     pData->time += pData->length;
    732 
    733     /* start the note */
    734     if ((note >= 0) && (parserMode == eParserModePlay))
    735     {
    736         VMStartNote(pEASData->pVoiceMgr, pData->pSynth, TC_CHANNEL, (EAS_U8) note, pData->volume);
    737         pData->note = note;
    738     }
    739 
    740     return EAS_SUCCESS;
    741 }
    742 
    743 /*----------------------------------------------------------------------------
    744  * TC_GetRepeat()
    745  *----------------------------------------------------------------------------
    746  * Process a repeat code
    747  *----------------------------------------------------------------------------
    748 */
    749 static EAS_RESULT TC_GetRepeat (S_EAS_DATA *pEASData, S_TC_DATA* pData, EAS_INT parserMode)
    750 {
    751     EAS_I8 count;
    752 
    753     /* get the repeat count */
    754     if (TC_GetNextChar(pEASData->hwInstData, pData, &count) != EAS_SUCCESS)
    755         return EAS_ERROR_FILE_FORMAT;
    756 
    757     /* validiate it */
    758     if (count < 2)
    759         return EAS_ERROR_FILE_FORMAT;
    760 
    761     /* calculate time of next event */
    762     pData->time += pData->length;
    763     pData->repeatCount = count - 2;
    764 
    765     /* start the note */
    766     if ((pData->note >= 0) && (parserMode == eParserModePlay))
    767         VMStartNote(pEASData->pVoiceMgr, pData->pSynth, TC_CHANNEL, (EAS_U8) pData->note, pData->volume);
    768 
    769     return EAS_SUCCESS;
    770 }
    771 
    772 /*----------------------------------------------------------------------------
    773  * TC_PlayBlock()
    774  *----------------------------------------------------------------------------
    775  * Play a block of notes
    776  *----------------------------------------------------------------------------
    777 */
    778 static EAS_RESULT TC_PlayBlock (S_EAS_DATA *pEASData, S_TC_DATA* pData)
    779 {
    780     EAS_RESULT result;
    781     EAS_I8 blockNum;
    782     EAS_I8 temp;
    783     EAS_I8 temp2;
    784 
    785     /* get the block number */
    786     if (TC_GetNextChar(pEASData->hwInstData, pData, &blockNum) != EAS_SUCCESS)
    787         return EAS_ERROR_FILE_FORMAT;
    788 
    789     /* validiate it */
    790     if (blockNum < 0)
    791         return EAS_ERROR_FILE_FORMAT;
    792 
    793     /* save the current position */
    794     if ((result = EAS_HWFilePos(pEASData->hwInstData, pData->fileHandle, &pData->restorePos)) != EAS_SUCCESS)
    795         return result;
    796 
    797     /* return to start of file */
    798     pData->byteAvail = EAS_FALSE;
    799     if ((result = EAS_HWFileSeek(pEASData->hwInstData, pData->fileHandle, pData->fileOffset)) != EAS_SUCCESS)
    800         return result;
    801 
    802     /* find the block */
    803     for (;;)
    804     {
    805         if (TC_GetNextChar(pEASData->hwInstData, pData, &temp) != EAS_SUCCESS)
    806             return EAS_ERROR_FILE_FORMAT;
    807 
    808         if (TC_GetNextChar(pEASData->hwInstData, pData, &temp2) != EAS_SUCCESS)
    809             return EAS_ERROR_FILE_FORMAT;
    810 
    811         if ((temp == TC_FIELD_BLOCK_START) && (temp2 == blockNum))
    812             return EAS_SUCCESS;
    813     }
    814 }
    815 
    816 /*----------------------------------------------------------------------------
    817  * TC_BlockEnd()
    818  *----------------------------------------------------------------------------
    819  * Handle end of block
    820  *----------------------------------------------------------------------------
    821 */
    822 static EAS_RESULT TC_BlockEnd (S_EAS_DATA *pEASData, S_TC_DATA* pData)
    823 {
    824     EAS_I8 blockNum;
    825 
    826     /* get the block number */
    827     if (TC_GetNextChar(pEASData->hwInstData, pData, &blockNum) != EAS_SUCCESS)
    828         return EAS_ERROR_FILE_FORMAT;
    829 
    830     /* validiate it */
    831     if (blockNum < 0)
    832         return EAS_ERROR_FILE_FORMAT;
    833 
    834     /* if we were playing this block, restore to previous position */
    835     pData->byteAvail = EAS_FALSE;
    836     return EAS_HWFileSeek(pEASData->hwInstData, pData->fileHandle, pData->restorePos);
    837 }
    838 
    839 /*----------------------------------------------------------------------------
    840  * TC_GetVolume()
    841  *----------------------------------------------------------------------------
    842  * Get the volume field and process it
    843  *----------------------------------------------------------------------------
    844 */
    845 static EAS_RESULT TC_GetVolume (S_EAS_DATA *pEASData, S_TC_DATA* pData)
    846 {
    847     EAS_I8 volume;
    848 
    849     /* get volume */
    850     if (TC_GetNextChar(pEASData->hwInstData, pData, &volume) != EAS_SUCCESS)
    851         return EAS_ERROR_FILE_FORMAT;
    852     if ((volume < 0) || (volume > 100))
    853         return EAS_ERROR_FILE_FORMAT;
    854 
    855     /* save volume */
    856     pData->volume = (EAS_U8) ((EAS_I32) (volume * TC_VOLUME_CONV + 1) >> TC_VOLUME_SHIFT);
    857     return EAS_SUCCESS;
    858 }
    859 
    860 /*----------------------------------------------------------------------------
    861  * TC_GetTempo()
    862  *----------------------------------------------------------------------------
    863  * Get the tempo field and process it
    864  *----------------------------------------------------------------------------
    865 */
    866 static EAS_RESULT TC_GetTempo (S_EAS_DATA *pEASData, S_TC_DATA* pData)
    867 {
    868     EAS_I8 tempo;
    869 
    870     /* get tempo */
    871     if (TC_GetNextChar(pEASData->hwInstData, pData, &tempo) != EAS_SUCCESS)
    872         return EAS_ERROR_FILE_FORMAT;
    873     if (tempo < 5)
    874         return EAS_ERROR_FILE_FORMAT;
    875 
    876     /* save tempo */
    877     pData->tempo = tempo;
    878 
    879     /* calculate new timebase */
    880     TC_CalcTimeBase(pData);
    881     return EAS_SUCCESS;
    882 }
    883 
    884 /*----------------------------------------------------------------------------
    885  * TC_GetResolution()
    886  *----------------------------------------------------------------------------
    887  * Get the resolution field and process it
    888  *----------------------------------------------------------------------------
    889 */
    890 static EAS_RESULT TC_GetResolution (S_EAS_DATA *pEASData, S_TC_DATA* pData)
    891 {
    892     EAS_I8 resolution;
    893 
    894     /* get resolution */
    895     if (TC_GetNextChar(pEASData->hwInstData, pData, &resolution) != EAS_SUCCESS)
    896         return EAS_ERROR_FILE_FORMAT;
    897     if (resolution < 0)
    898         return EAS_ERROR_FILE_FORMAT;
    899 
    900     /* save tempo */
    901     pData->resolution = resolution;
    902 
    903     /* calculate new timebase */
    904     TC_CalcTimeBase(pData);
    905     return EAS_SUCCESS;
    906 }
    907 
    908 /*----------------------------------------------------------------------------
    909  * TC_GetNextChar()
    910  *----------------------------------------------------------------------------
    911  * Fetch the next character from the stream
    912  *----------------------------------------------------------------------------
    913 */
    914 static EAS_RESULT TC_GetNextChar (EAS_HW_DATA_HANDLE hwInstData, S_TC_DATA *pData, EAS_I8 *pValue)
    915 {
    916 
    917     /* get character from "put back" buffer */
    918     if (pData->byteAvail)
    919     {
    920         pData->byteAvail = EAS_FALSE;
    921         *pValue = pData->dataByte;
    922         return EAS_SUCCESS;
    923     }
    924 
    925     /* get character from file */
    926     return EAS_HWGetByte(hwInstData, pData->fileHandle, pValue);
    927 }
    928 
    929 /*----------------------------------------------------------------------------
    930  * TC_PutBackChar()
    931  *----------------------------------------------------------------------------
    932  * Put back the character
    933  *----------------------------------------------------------------------------
    934 */
    935 static void TC_PutBackChar (S_TC_DATA *pData, EAS_I8 value)
    936 {
    937 
    938     pData->dataByte = value;
    939     pData->byteAvail = EAS_TRUE;
    940 }
    941 
    942