Home | History | Annotate | Download | only in lib_src
      1 /*----------------------------------------------------------------------------
      2  *
      3  * File:
      4  * eas_rtttl.c
      5  *
      6  * Contents and purpose:
      7  * RTTTL parser
      8  *
      9  * Copyright Sonic Network Inc. 2005
     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_rtttldata.h"
     39 #include "eas_ctype.h"
     40 
     41 /* increase gain for mono ringtones */
     42 #define RTTTL_GAIN_OFFSET       8
     43 
     44 /* maximum title length including colon separator */
     45 #define RTTTL_MAX_TITLE_LEN     32
     46 #define RTTTL_INFINITE_LOOP     15
     47 
     48 /* length of 32nd note in 1/256ths of a msec for 63 BPM tempo */
     49 #define DEFAULT_TICK_CONV       30476
     50 #define TICK_CONVERT            1920000
     51 
     52 /* default channel and program for RTTTL playback */
     53 #define RTTTL_CHANNEL           0
     54 #define RTTTL_PROGRAM           80
     55 #define RTTTL_VELOCITY          127
     56 
     57 /* note used for rest */
     58 #define RTTTL_REST              1
     59 
     60 /* multiplier for fixed point triplet conversion */
     61 #define TRIPLET_MULTIPLIER      683
     62 #define TRIPLET_SHIFT           10
     63 
     64 /* local prototypes */
     65 static EAS_RESULT RTTTL_CheckFileType (S_EAS_DATA *pEASData, EAS_FILE_HANDLE fileHandle, EAS_VOID_PTR *ppHandle, EAS_I32 offset);
     66 static EAS_RESULT RTTTL_Prepare (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData);
     67 static EAS_RESULT RTTTL_Time (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_U32 *pTime);
     68 static EAS_RESULT RTTTL_Event (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_INT parserMode);
     69 static EAS_RESULT RTTTL_State (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_STATE *pState);
     70 static EAS_RESULT RTTTL_Close (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData);
     71 static EAS_RESULT RTTTL_Reset (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData);
     72 static EAS_RESULT RTTTL_Pause (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData);
     73 static EAS_RESULT RTTTL_Resume (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData);
     74 static EAS_RESULT RTTTL_SetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 value);
     75 static EAS_RESULT RTTTL_GetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 *pValue);
     76 static EAS_RESULT RTTTL_GetStyle (EAS_HW_DATA_HANDLE hwInstData, S_RTTTL_DATA *pData);
     77 static EAS_RESULT RTTTL_GetDuration (EAS_HW_DATA_HANDLE hwInstData, S_RTTTL_DATA *pData, EAS_I8 *pDuration);
     78 static EAS_RESULT RTTTL_GetOctave (EAS_HW_DATA_HANDLE hwInstData, S_RTTTL_DATA *pData, EAS_U8 *pOctave);
     79 static EAS_RESULT RTTTL_GetTempo (EAS_HW_DATA_HANDLE hwInstData, S_RTTTL_DATA *pData);
     80 static EAS_RESULT RTTTL_GetNumber (EAS_HW_DATA_HANDLE hwInstData, S_RTTTL_DATA *pData, EAS_I32 *pValue);
     81 static EAS_RESULT RTTTL_ParseHeader (S_EAS_DATA *pEASData, S_RTTTL_DATA* pData, EAS_BOOL metaData);
     82 static EAS_RESULT RTTTL_GetNextChar (EAS_HW_DATA_HANDLE hwInstData, S_RTTTL_DATA *pData, EAS_I8 *pValue);
     83 static EAS_RESULT RTTTL_PeekNextChar (EAS_HW_DATA_HANDLE hwInstData, S_RTTTL_DATA *pData, EAS_I8 *pValue);
     84 
     85 /* inline functions */
     86 EAS_INLINE void RTTTL_PutBackChar (S_RTTTL_DATA *pData, EAS_I8 value) { pData->dataByte = value; }
     87 
     88 
     89 /* lookup table for note values */
     90 static const EAS_U8 noteTable[] = { 21, 23, 12, 14, 16, 17, 19, 23 };
     91 
     92 /*----------------------------------------------------------------------------
     93  *
     94  * EAS_RTTTL_Parser
     95  *
     96  * This structure contains the functional interface for the iMelody parser
     97  *----------------------------------------------------------------------------
     98 */
     99 const S_FILE_PARSER_INTERFACE EAS_RTTTL_Parser =
    100 {
    101     RTTTL_CheckFileType,
    102     RTTTL_Prepare,
    103     RTTTL_Time,
    104     RTTTL_Event,
    105     RTTTL_State,
    106     RTTTL_Close,
    107     RTTTL_Reset,
    108     RTTTL_Pause,
    109     RTTTL_Resume,
    110     NULL,
    111     RTTTL_SetData,
    112     RTTTL_GetData,
    113     NULL
    114 };
    115 
    116 /*----------------------------------------------------------------------------
    117  * RTTTL_CheckFileType()
    118  *----------------------------------------------------------------------------
    119  * Purpose:
    120  * Check the file type to see if we can parse it
    121  *
    122  * Inputs:
    123  * pEASData         - pointer to overall EAS data structure
    124  * handle           - pointer to file handle
    125  *
    126  * Outputs:
    127  *
    128  *
    129  * Side Effects:
    130  *
    131  *----------------------------------------------------------------------------
    132 */
    133 static EAS_RESULT RTTTL_CheckFileType (S_EAS_DATA *pEASData, EAS_FILE_HANDLE fileHandle, EAS_VOID_PTR *ppHandle, EAS_I32 offset)
    134 {
    135     S_RTTTL_DATA data;
    136     S_RTTTL_DATA *pData;
    137 
    138     /* see if we can parse the header */
    139     data.fileHandle = fileHandle;
    140     data.fileOffset = offset;
    141     *ppHandle= NULL;
    142     if (RTTTL_ParseHeader (pEASData, &data, EAS_FALSE) == EAS_SUCCESS)
    143     {
    144 
    145         /* check for static memory allocation */
    146         if (pEASData->staticMemoryModel)
    147             pData = EAS_CMEnumData(EAS_CM_RTTTL_DATA);
    148         else
    149             pData = EAS_HWMalloc(pEASData->hwInstData, sizeof(S_RTTTL_DATA));
    150         if (!pData)
    151             return EAS_ERROR_MALLOC_FAILED;
    152         EAS_HWMemSet(pData, 0, sizeof(S_RTTTL_DATA));
    153 
    154         /* return a pointer to the instance data */
    155         pData->fileHandle = fileHandle;
    156         pData->fileOffset = offset;
    157         pData->state = EAS_STATE_OPEN;
    158         *ppHandle = pData;
    159     }
    160 
    161     return EAS_SUCCESS;
    162 }
    163 
    164 /*----------------------------------------------------------------------------
    165  * RTTTL_Prepare()
    166  *----------------------------------------------------------------------------
    167  * Purpose:
    168  * Prepare to parse the file. Allocates instance data (or uses static allocation for
    169  * static memory model).
    170  *
    171  * Inputs:
    172  * pEASData         - pointer to overall EAS data structure
    173  * handle           - pointer to file handle
    174  *
    175  * Outputs:
    176  *
    177  *
    178  * Side Effects:
    179  *
    180  *----------------------------------------------------------------------------
    181 */
    182 static EAS_RESULT RTTTL_Prepare (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
    183 {
    184     S_RTTTL_DATA* pData;
    185     EAS_RESULT result;
    186 
    187     /* check for valid state */
    188     pData = (S_RTTTL_DATA*) pInstData;
    189     if (pData->state != EAS_STATE_OPEN)
    190         return EAS_ERROR_NOT_VALID_IN_THIS_STATE;
    191 
    192     /* instantiate a synthesizer */
    193     if ((result = VMInitMIDI(pEASData, &pData->pSynth)) != EAS_SUCCESS)
    194     {
    195         { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "VMInitMIDI returned %d\n", result); */ }
    196         return result;
    197     }
    198 
    199     pData->state = EAS_STATE_ERROR;
    200     if ((result = RTTTL_ParseHeader (pEASData,  pData, (EAS_BOOL) (pData->metadata.callback != NULL))) != EAS_SUCCESS)
    201     {
    202         /* if using dynamic memory, free it */
    203         if (!pEASData->staticMemoryModel)
    204             EAS_HWFree(pEASData->hwInstData, pData);
    205         return result;
    206     }
    207 
    208     pData->state = EAS_STATE_READY;
    209     return EAS_SUCCESS;
    210 }
    211 
    212 /*----------------------------------------------------------------------------
    213  * RTTTL_Time()
    214  *----------------------------------------------------------------------------
    215  * Purpose:
    216  * Returns the time of the next event in msecs
    217  *
    218  * Inputs:
    219  * pEASData         - pointer to overall EAS data structure
    220  * handle           - pointer to file handle
    221  * pTime            - pointer to variable to hold time of next event (in msecs)
    222  *
    223  * Outputs:
    224  *
    225  *
    226  * Side Effects:
    227  *
    228  *----------------------------------------------------------------------------
    229 */
    230 /*lint -esym(715, pEASData) reserved for future use */
    231 static EAS_RESULT RTTTL_Time (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_U32 *pTime)
    232 {
    233     S_RTTTL_DATA *pData;
    234 
    235     pData = (S_RTTTL_DATA*) pInstData;
    236 
    237     /* return time in milliseconds */
    238     /*lint -e{704} use shift instead of division */
    239     *pTime = pData->time >> 8;
    240     return EAS_SUCCESS;
    241 }
    242 
    243 /*----------------------------------------------------------------------------
    244  * RTTTL_Event()
    245  *----------------------------------------------------------------------------
    246  * Purpose:
    247  * Parse the next event in the file
    248  *
    249  * Inputs:
    250  * pEASData         - pointer to overall EAS data structure
    251  * handle           - pointer to file handle
    252  *
    253  * Outputs:
    254  *
    255  *
    256  * Side Effects:
    257  *
    258  *----------------------------------------------------------------------------
    259 */
    260 static EAS_RESULT RTTTL_Event (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_INT parserMode)
    261 {
    262     S_RTTTL_DATA* pData;
    263     EAS_RESULT result;
    264     EAS_I32 ticks;
    265     EAS_I32 temp;
    266     EAS_I8 c;
    267     EAS_U8 note;
    268     EAS_U8 octave;
    269 
    270     pData = (S_RTTTL_DATA*) pInstData;
    271     if (pData->state >= EAS_STATE_OPEN)
    272         return EAS_SUCCESS;
    273 
    274     /* initialize MIDI channel when the track starts playing */
    275     if (pData->time == 0)
    276     {
    277         /* set program to square lead */
    278         VMProgramChange(pEASData->pVoiceMgr, pData->pSynth, RTTTL_CHANNEL, RTTTL_PROGRAM);
    279 
    280         /* set channel volume to max */
    281         VMControlChange(pEASData->pVoiceMgr, pData->pSynth, RTTTL_CHANNEL, 7, 127);
    282     }
    283 
    284     /* check for end of note */
    285     if (pData->note)
    286     {
    287         /* stop the note */
    288         VMStopNote(pEASData->pVoiceMgr, pData->pSynth, RTTTL_CHANNEL, pData->note, 0);
    289         pData->note = 0;
    290 
    291         /* check for rest between notes */
    292         if (pData->restTicks)
    293         {
    294             pData->time += pData->restTicks;
    295             pData->restTicks = 0;
    296             return EAS_SUCCESS;
    297         }
    298     }
    299 
    300     /* parse the next event */
    301     octave = pData->octave;
    302     note = 0;
    303     ticks = pData->duration * pData->tick;
    304     for (;;)
    305     {
    306 
    307         /* get next character */
    308         if ((result = RTTTL_GetNextChar(pEASData->hwInstData, pData, &c)) != EAS_SUCCESS)
    309         {
    310             if (result != EAS_EOF)
    311                 return result;
    312 
    313             /* end of file, if no notes to process, check for looping */
    314             if (!note)
    315             {
    316                 /* if no loop set state to stopping */
    317                 if (pData->repeatCount == 0)
    318                 {
    319                     pData->state = EAS_STATE_STOPPING;
    320                     VMReleaseAllVoices(pEASData->pVoiceMgr, pData->pSynth);
    321                     return EAS_SUCCESS;
    322                 }
    323 
    324                 /* decrement loop count */
    325                 if (pData->repeatCount != RTTTL_INFINITE_LOOP)
    326                     pData->repeatCount--;
    327 
    328                 /* if locating, ignore infinite loops */
    329                 else if (parserMode != eParserModePlay)
    330                 {
    331                     pData->state = EAS_STATE_STOPPING;
    332                     VMReleaseAllVoices(pEASData->pVoiceMgr, pData->pSynth);
    333                     return EAS_SUCCESS;
    334                 }
    335 
    336                 /* loop back to start of notes */
    337                 if ((result = EAS_HWFileSeek(pEASData->hwInstData, pData->fileHandle, pData->repeatOffset)) != EAS_SUCCESS)
    338                     return result;
    339                 continue;
    340             }
    341 
    342             /* still have a note to process */
    343             else
    344                 c = ',';
    345         }
    346 
    347         /* bpm */
    348         if (c == 'b')
    349         {
    350             /* peek at next character */
    351             if ((result = RTTTL_PeekNextChar(pEASData->hwInstData, pData, &c)) != EAS_SUCCESS)
    352                 return result;
    353 
    354             /* if a number, must be octave or tempo */
    355             if (IsDigit(c))
    356             {
    357                 if ((result = RTTTL_GetNumber(pEASData->hwInstData, pData, &temp)) != EAS_SUCCESS)
    358                     return result;
    359 
    360                 /* check for octave first */
    361                 if ((temp >= 4) && (temp <= 7))
    362                 {
    363                     octave = (EAS_U8) temp;
    364                 }
    365 
    366                 /* check for tempo */
    367                 else if ((temp >= 25) && (temp <= 900))
    368                 {
    369                     pData->tick = TICK_CONVERT / (EAS_U32) temp;
    370                 }
    371 
    372                 /* don't know what it was */
    373                 else
    374                     return EAS_ERROR_FILE_FORMAT;
    375             }
    376 
    377             /* must be a note */
    378             else
    379             {
    380                 note = noteTable[1];
    381             }
    382         }
    383 
    384         /* octave */
    385         else if (c == 'o')
    386         {
    387             if ((result = RTTTL_GetOctave(pEASData->hwInstData, pData, &pData->octave)) != EAS_SUCCESS)
    388                 return result;
    389         }
    390 
    391         /* style */
    392         else if (c == 's')
    393         {
    394             if ((result = RTTTL_GetStyle(pEASData->hwInstData, pData)) != EAS_SUCCESS)
    395                 return result;
    396         }
    397 
    398         /* duration or octave */
    399         else if (IsDigit(c))
    400         {
    401             RTTTL_PutBackChar(pData, c);
    402 
    403             /* duration comes before note */
    404             if (!note)
    405             {
    406                 if ((result = RTTTL_GetDuration(pEASData->hwInstData, pData, &c)) != EAS_SUCCESS)
    407                     return result;
    408                 ticks = c * pData->tick;
    409             }
    410 
    411             /* octave comes after note */
    412             else
    413             {
    414                 if ((result = RTTTL_GetOctave(pEASData->hwInstData, pData, &octave)) != EAS_SUCCESS)
    415                     return result;
    416             }
    417         }
    418 
    419         /* note or rest */
    420         else if ((c >= 'a') && (c <= 'h'))
    421         {
    422             note = noteTable[c - 'a'];
    423         }
    424 
    425         else if (c == 'p')
    426         {
    427             note = RTTTL_REST;
    428         }
    429 
    430         /* dotted note */
    431         else if (c == '.')
    432         {
    433             /*lint -e{704} shift for performance */
    434             ticks += ticks >> 1;
    435         }
    436 
    437         /* accidental */
    438         else if (c == '#')
    439         {
    440             if (note)
    441                 note++;
    442         }
    443 
    444         /* end of event */
    445         else if ((c == ',') && note)
    446         {
    447 
    448             /* handle note events */
    449             if (note != RTTTL_REST)
    450             {
    451 
    452                 /* save note and start it */
    453                 pData->note = note + octave;
    454                 if (parserMode == eParserModePlay)
    455                     VMStartNote(pEASData->pVoiceMgr, pData->pSynth, RTTTL_CHANNEL, pData->note, RTTTL_VELOCITY);
    456 
    457                 /* determine note length */
    458                 switch (pData->style)
    459                 {
    460                     /* natural */
    461                     case 'n':
    462                         /*lint -e{704} shift for performance */
    463                         pData->restTicks = ticks >> 4;
    464                         break;
    465                     /* continuous */
    466 
    467                     case 'c':
    468                         pData->restTicks = 0;
    469                         break;
    470 
    471                     /* staccato */
    472                     case 's':
    473                         /*lint -e{704} shift for performance */
    474                         pData->restTicks = ticks >> 1;
    475                         break;
    476 
    477                     default:
    478                         { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "RTTTL_Event: Unexpected style type %c\n", pData->style); */ }
    479                         break;
    480                 }
    481 
    482                 /* next event is at end of this note */
    483                 pData->time += ticks - pData->restTicks;
    484             }
    485 
    486             /* rest */
    487             else
    488                 pData->time += ticks;
    489 
    490             /* event found, return to caller */
    491             break;
    492         }
    493     }
    494 
    495     pData->state = EAS_STATE_PLAY;
    496     return EAS_SUCCESS;
    497 }
    498 
    499 /*----------------------------------------------------------------------------
    500  * RTTTL_State()
    501  *----------------------------------------------------------------------------
    502  * Purpose:
    503  * Returns the current state of the stream
    504  *
    505  * Inputs:
    506  * pEASData         - pointer to overall EAS data structure
    507  * handle           - pointer to file handle
    508  * pState           - pointer to variable to store state
    509  *
    510  * Outputs:
    511  *
    512  *
    513  * Side Effects:
    514  *
    515  *----------------------------------------------------------------------------
    516 */
    517 /*lint -esym(715, pEASData) reserved for future use */
    518 static EAS_RESULT RTTTL_State (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 *pState)
    519 {
    520     S_RTTTL_DATA* pData;
    521 
    522     /* establish pointer to instance data */
    523     pData = (S_RTTTL_DATA*) pInstData;
    524 
    525     /* if stopping, check to see if synth voices are active */
    526     if (pData->state == EAS_STATE_STOPPING)
    527     {
    528         if (VMActiveVoices(pData->pSynth) == 0)
    529             pData->state = EAS_STATE_STOPPED;
    530     }
    531 
    532     if (pData->state == EAS_STATE_PAUSING)
    533     {
    534         if (VMActiveVoices(pData->pSynth) == 0)
    535             pData->state = EAS_STATE_PAUSED;
    536     }
    537 
    538     /* return current state */
    539     *pState = pData->state;
    540     return EAS_SUCCESS;
    541 }
    542 
    543 /*----------------------------------------------------------------------------
    544  * RTTTL_Close()
    545  *----------------------------------------------------------------------------
    546  * Purpose:
    547  * Close the file and clean up
    548  *
    549  * Inputs:
    550  * pEASData         - pointer to overall EAS data structure
    551  * handle           - pointer to file handle
    552  *
    553  * Outputs:
    554  *
    555  *
    556  * Side Effects:
    557  *
    558  *----------------------------------------------------------------------------
    559 */
    560 static EAS_RESULT RTTTL_Close (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
    561 {
    562     S_RTTTL_DATA* pData;
    563     EAS_RESULT result;
    564 
    565     pData = (S_RTTTL_DATA*) pInstData;
    566 
    567     /* close the file */
    568     if ((result = EAS_HWCloseFile(pEASData->hwInstData, pData->fileHandle)) != EAS_SUCCESS)
    569             return result;
    570 
    571     /* free the synth */
    572     if (pData->pSynth != NULL)
    573         VMMIDIShutdown(pEASData, pData->pSynth);
    574 
    575     /* if using dynamic memory, free it */
    576     if (!pEASData->staticMemoryModel)
    577         EAS_HWFree(pEASData->hwInstData, pData);
    578 
    579     return EAS_SUCCESS;
    580 }
    581 
    582 /*----------------------------------------------------------------------------
    583  * RTTTL_Reset()
    584  *----------------------------------------------------------------------------
    585  * Purpose:
    586  * Reset the sequencer. Used for locating backwards in the file.
    587  *
    588  * Inputs:
    589  * pEASData         - pointer to overall EAS data structure
    590  * handle           - pointer to file handle
    591  *
    592  * Outputs:
    593  *
    594  *
    595  * Side Effects:
    596  *
    597  *----------------------------------------------------------------------------
    598 */
    599 static EAS_RESULT RTTTL_Reset (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
    600 {
    601     S_RTTTL_DATA* pData;
    602     EAS_RESULT result;
    603 
    604     pData = (S_RTTTL_DATA*) pInstData;
    605 
    606     /* reset the synth */
    607     VMReset(pEASData->pVoiceMgr, pData->pSynth, EAS_TRUE);
    608 
    609     /* reset time to zero */
    610     pData->time = 0;
    611     pData->note = 0;
    612 
    613     /* reset file position and re-parse header */
    614     pData->state = EAS_STATE_ERROR;
    615     if ((result = EAS_HWFileSeek(pEASData->hwInstData, pData->fileHandle, pData->fileOffset)) != EAS_SUCCESS)
    616         return result;
    617     if ((result = RTTTL_ParseHeader (pEASData,  pData, EAS_TRUE)) != EAS_SUCCESS)
    618         return result;
    619 
    620     pData->state = EAS_STATE_READY;
    621     return EAS_SUCCESS;
    622 }
    623 
    624 /*----------------------------------------------------------------------------
    625  * RTTTL_Pause()
    626  *----------------------------------------------------------------------------
    627  * Purpose:
    628  * Pauses the sequencer. Mutes all voices and sets state to pause.
    629  *
    630  * Inputs:
    631  * pEASData         - pointer to overall EAS data structure
    632  * handle           - pointer to file handle
    633  *
    634  * Outputs:
    635  *
    636  *
    637  * Side Effects:
    638  *
    639  *----------------------------------------------------------------------------
    640 */
    641 static EAS_RESULT RTTTL_Pause (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
    642 {
    643     S_RTTTL_DATA *pData;
    644 
    645     /* can't pause a stopped stream */
    646     pData = (S_RTTTL_DATA*) pInstData;
    647     if (pData->state == EAS_STATE_STOPPED)
    648         return EAS_ERROR_ALREADY_STOPPED;
    649 
    650     /* mute the synthesizer */
    651     VMMuteAllVoices(pEASData->pVoiceMgr, pData->pSynth);
    652     pData->state = EAS_STATE_PAUSING;
    653     return EAS_SUCCESS;
    654 }
    655 
    656 /*----------------------------------------------------------------------------
    657  * RTTTL_Resume()
    658  *----------------------------------------------------------------------------
    659  * Purpose:
    660  * Resume playing after a pause, sets state back to playing.
    661  *
    662  * Inputs:
    663  * pEASData         - pointer to overall EAS data structure
    664  * handle           - pointer to file handle
    665  *
    666  * Outputs:
    667  *
    668  *
    669  * Side Effects:
    670  *
    671  *----------------------------------------------------------------------------
    672 */
    673 /*lint -esym(715, pEASData) reserved for future use */
    674 static EAS_RESULT RTTTL_Resume (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
    675 {
    676     S_RTTTL_DATA *pData;
    677 
    678     /* can't resume a stopped stream */
    679     pData = (S_RTTTL_DATA*) pInstData;
    680     if (pData->state == EAS_STATE_STOPPED)
    681         return EAS_ERROR_ALREADY_STOPPED;
    682 
    683     /* nothing to do but resume playback */
    684     pData->state = EAS_STATE_PLAY;
    685     return EAS_SUCCESS;
    686 }
    687 
    688 /*----------------------------------------------------------------------------
    689  * RTTTL_SetData()
    690  *----------------------------------------------------------------------------
    691  * Purpose:
    692  * Return file type
    693  *
    694  * Inputs:
    695  * pEASData         - pointer to overall EAS data structure
    696  * handle           - pointer to file handle
    697  *
    698  * Outputs:
    699  *
    700  *
    701  * Side Effects:
    702  *
    703  *----------------------------------------------------------------------------
    704 */
    705 /*lint -esym(715, pEASData) reserved for future use */
    706 static EAS_RESULT RTTTL_SetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 value)
    707 {
    708     S_RTTTL_DATA *pData;
    709 
    710     pData = (S_RTTTL_DATA *) pInstData;
    711     switch (param)
    712     {
    713 
    714         /* set metadata callback */
    715         case PARSER_DATA_METADATA_CB:
    716             EAS_HWMemCpy(&pData->metadata, (void*) value, sizeof(S_METADATA_CB));
    717             break;
    718 
    719         default:
    720             return EAS_ERROR_INVALID_PARAMETER;
    721     }
    722 
    723     return EAS_SUCCESS;
    724 }
    725 
    726 /*----------------------------------------------------------------------------
    727  * RTTTL_GetData()
    728  *----------------------------------------------------------------------------
    729  * Purpose:
    730  * Return file type
    731  *
    732  * Inputs:
    733  * pEASData         - pointer to overall EAS data structure
    734  * handle           - pointer to file handle
    735  *
    736  * Outputs:
    737  *
    738  *
    739  * Side Effects:
    740  *
    741  *----------------------------------------------------------------------------
    742 */
    743 /*lint -esym(715, pEASData) reserved for future use */
    744 static EAS_RESULT RTTTL_GetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 *pValue)
    745 {
    746     S_RTTTL_DATA *pData;
    747 
    748     pData = (S_RTTTL_DATA *) pInstData;
    749     switch (param)
    750     {
    751         /* return file type as RTTTL */
    752         case PARSER_DATA_FILE_TYPE:
    753             *pValue = EAS_FILE_RTTTL;
    754             break;
    755 
    756 #if 0
    757         /* set transposition */
    758         case PARSER_DATA_TRANSPOSITION:
    759             *pValue = pData->transposition;
    760             break;
    761 #endif
    762 
    763         case PARSER_DATA_SYNTH_HANDLE:
    764             *pValue = (EAS_I32) pData->pSynth;
    765             break;
    766 
    767         case PARSER_DATA_GAIN_OFFSET:
    768             *pValue = RTTTL_GAIN_OFFSET;
    769             break;
    770 
    771     default:
    772             return EAS_ERROR_INVALID_PARAMETER;
    773     }
    774     return EAS_SUCCESS;
    775 }
    776 
    777 /*----------------------------------------------------------------------------
    778  * RTTTL_GetStyle()
    779  *----------------------------------------------------------------------------
    780  * Purpose:
    781  *
    782  *
    783  * Inputs:
    784  *
    785  *
    786  * Outputs:
    787  *
    788  *
    789  * Side Effects:
    790  *
    791  *----------------------------------------------------------------------------
    792 */
    793 static EAS_RESULT RTTTL_GetStyle (EAS_HW_DATA_HANDLE hwInstData, S_RTTTL_DATA *pData)
    794 {
    795     EAS_RESULT result;
    796     EAS_I8 style;
    797 
    798     /* get style */
    799     if ((result = RTTTL_GetNextChar(hwInstData, pData, &style)) != EAS_SUCCESS)
    800         return result;
    801 
    802     if ((style != 's')  && (style != 'n') && (style != 'c'))
    803         return EAS_ERROR_FILE_FORMAT;
    804 
    805     pData->style = style;
    806     return EAS_SUCCESS;
    807 }
    808 
    809 /*----------------------------------------------------------------------------
    810  * RTTTL_GetDuration()
    811  *----------------------------------------------------------------------------
    812  * Purpose:
    813  *
    814  *
    815  * Inputs:
    816  *
    817  *
    818  * Outputs:
    819  *
    820  *
    821  * Side Effects:
    822  *
    823  *----------------------------------------------------------------------------
    824 */
    825 static EAS_RESULT RTTTL_GetDuration (EAS_HW_DATA_HANDLE hwInstData, S_RTTTL_DATA *pData, EAS_I8 *pDuration)
    826 {
    827     EAS_RESULT result;
    828     EAS_I32 duration;
    829     EAS_I8 temp;
    830 
    831     /* get the duration */
    832     if ((result = RTTTL_GetNumber(hwInstData, pData, &duration)) != EAS_SUCCESS)
    833         return result;
    834 
    835     if ((duration != 1) && (duration != 2) && (duration != 4) && (duration != 8) && (duration != 16) && (duration != 32))
    836         return EAS_ERROR_FILE_FORMAT;
    837 
    838     temp = 64;
    839     while (duration)
    840     {
    841         /*lint -e{704} shift for performance */
    842         duration = duration >> 1;
    843         /*lint -e{702} use shift for performance */
    844         temp = temp >> 1;
    845     }
    846 
    847     *pDuration = temp;
    848     return EAS_SUCCESS;
    849 }
    850 
    851 /*----------------------------------------------------------------------------
    852  * RTTTL_GetOctave()
    853  *----------------------------------------------------------------------------
    854  * Purpose:
    855  *
    856  *
    857  * Inputs:
    858  *
    859  *
    860  * Outputs:
    861  *
    862  *
    863  * Side Effects:
    864  *
    865  *----------------------------------------------------------------------------
    866 */
    867 static EAS_RESULT RTTTL_GetOctave (EAS_HW_DATA_HANDLE hwInstData, S_RTTTL_DATA *pData, EAS_U8 *pOctave)
    868 {
    869     EAS_RESULT result;
    870     EAS_I32 octave;
    871 
    872     /* get the tempo */
    873     if ((result = RTTTL_GetNumber(hwInstData, pData, &octave)) != EAS_SUCCESS)
    874         return result;
    875 
    876     if ((octave < 4) || (octave > 7))
    877         return EAS_ERROR_FILE_FORMAT;
    878 
    879     *pOctave = (EAS_U8) (octave * 12);
    880     return EAS_SUCCESS;
    881 }
    882 
    883 /*----------------------------------------------------------------------------
    884  * RTTTL_GetTempo()
    885  *----------------------------------------------------------------------------
    886  * Purpose:
    887  *
    888  *
    889  * Inputs:
    890  *
    891  *
    892  * Outputs:
    893  *
    894  *
    895  * Side Effects:
    896  *
    897  *----------------------------------------------------------------------------
    898 */
    899 static EAS_RESULT RTTTL_GetTempo (EAS_HW_DATA_HANDLE hwInstData, S_RTTTL_DATA *pData)
    900 {
    901     EAS_RESULT result;
    902     EAS_I32 tempo;
    903 
    904     /* get the tempo */
    905     if ((result = RTTTL_GetNumber(hwInstData, pData, &tempo)) != EAS_SUCCESS)
    906         return result;
    907 
    908     if ((tempo < 25) || (tempo > 900))
    909         return EAS_ERROR_FILE_FORMAT;
    910 
    911     pData->tick = TICK_CONVERT / (EAS_U32) tempo;
    912     return EAS_SUCCESS;
    913 }
    914 
    915 /*----------------------------------------------------------------------------
    916  * RTTTL_GetNumber()
    917  *----------------------------------------------------------------------------
    918  * Purpose:
    919  *
    920  *
    921  * Inputs:
    922  *
    923  *
    924  * Outputs:
    925  *
    926  *
    927  * Side Effects:
    928  *
    929  *----------------------------------------------------------------------------
    930 */
    931 static EAS_RESULT RTTTL_GetNumber (EAS_HW_DATA_HANDLE hwInstData, S_RTTTL_DATA *pData, EAS_I32 *pValue)
    932 {
    933     EAS_RESULT result;
    934     EAS_INT temp;
    935     EAS_I8 c;
    936 
    937     *pValue = -1;
    938     temp = 0;
    939     for (;;)
    940     {
    941         if ((result = RTTTL_PeekNextChar(hwInstData, pData, &c)) != EAS_SUCCESS)
    942         {
    943             if ((result == EAS_EOF) && (*pValue != -1))
    944                 return EAS_SUCCESS;
    945             return result;
    946         }
    947 
    948         if (IsDigit(c))
    949         {
    950             pData->dataByte = 0;
    951             temp = temp * 10 + c - '0';
    952             *pValue = temp;
    953         }
    954         else
    955             return EAS_SUCCESS;
    956     }
    957 }
    958 
    959 /*----------------------------------------------------------------------------
    960  * RTTTL_ParseHeader()
    961  *----------------------------------------------------------------------------
    962  * Purpose:
    963  * Prepare to parse the file. Allocates instance data (or uses static allocation for
    964  * static memory model).
    965  *
    966  * Inputs:
    967  * pEASData         - pointer to overall EAS data structure
    968  * handle           - pointer to file handle
    969  *
    970  * Outputs:
    971  *
    972  *
    973  * Side Effects:
    974  *
    975  *----------------------------------------------------------------------------
    976 */
    977 static EAS_RESULT RTTTL_ParseHeader (S_EAS_DATA *pEASData, S_RTTTL_DATA* pData, EAS_BOOL metaData)
    978 {
    979     EAS_RESULT result;
    980     EAS_I32 i;
    981     EAS_I8 temp;
    982     EAS_I8 control;
    983 
    984     /* initialize some defaults */
    985     pData->time = 0;
    986     pData->tick = DEFAULT_TICK_CONV;
    987     pData->note = 0;
    988     pData->duration = 4;
    989     pData ->restTicks = 0;
    990     pData->octave = 60;
    991     pData->repeatOffset = -1;
    992     pData->repeatCount = 0;
    993     pData->style = 'n';
    994     pData->dataByte = 0;
    995 
    996     metaData = metaData && (pData->metadata.buffer != NULL) && (pData->metadata.callback != NULL);
    997 
    998     /* seek to start of data */
    999     if ((result = EAS_HWFileSeek(pEASData->hwInstData, pData->fileHandle, pData->fileOffset)) != EAS_SUCCESS)
   1000         return result;
   1001 
   1002     /* zero the metadata buffer */
   1003     if (metaData)
   1004         EAS_HWMemSet(pData->metadata.buffer, 0, pData->metadata.bufferSize);
   1005 
   1006     /* read the title */
   1007     for (i = 0; i < RTTTL_MAX_TITLE_LEN; i++)
   1008     {
   1009         if ((result = EAS_HWGetByte(pEASData->hwInstData, pData->fileHandle, &temp)) != EAS_SUCCESS)
   1010             return result;
   1011 
   1012         if (temp == ':')
   1013             break;
   1014 
   1015         /* pass along metadata */
   1016         if (metaData)
   1017         {
   1018             if (i < (pData->metadata.bufferSize- 1))
   1019                 pData->metadata.buffer[i] = (char) temp;
   1020         }
   1021     }
   1022 
   1023     /* check for error in title */
   1024     if (i == RTTTL_MAX_TITLE_LEN)
   1025         return EAS_ERROR_FILE_FORMAT;
   1026 
   1027     /* pass along metadata */
   1028     if (metaData)
   1029         (*pData->metadata.callback)(EAS_METADATA_TITLE, pData->metadata.buffer, pData->metadata.pUserData);
   1030 
   1031     /* control fields */
   1032     for (;;)
   1033     {
   1034 
   1035         /* get control type */
   1036         if ((result = RTTTL_GetNextChar(pEASData->hwInstData, pData, &control)) != EAS_SUCCESS)
   1037             return result;
   1038 
   1039         /* next char should be equal sign */
   1040         if ((result = RTTTL_GetNextChar(pEASData->hwInstData, pData, &temp)) != EAS_SUCCESS)
   1041             return result;
   1042         if (temp != '=')
   1043             return EAS_ERROR_FILE_FORMAT;
   1044 
   1045         /* get the control value */
   1046         switch (control)
   1047         {
   1048 
   1049             /* bpm */
   1050             case 'b':
   1051                 if ((result = RTTTL_GetTempo(pEASData->hwInstData, pData)) != EAS_SUCCESS)
   1052                     return result;
   1053                 break;
   1054 
   1055             /* duration */
   1056             case 'd':
   1057                 if ((result = RTTTL_GetDuration(pEASData->hwInstData, pData, &temp)) != EAS_SUCCESS)
   1058                     return result;
   1059                 pData->duration = temp;
   1060                 break;
   1061 
   1062             /* loop */
   1063             case 'l':
   1064                 if ((result = RTTTL_GetNumber(pEASData->hwInstData, pData, &i)) != EAS_SUCCESS)
   1065                     return result;
   1066                 if ((i < 0) || (i > 15))
   1067                     return EAS_ERROR_FILE_FORMAT;
   1068                 pData->repeatCount = (EAS_U8) i;
   1069                 break;
   1070 
   1071             /* octave */
   1072             case 'o':
   1073                 if ((result = RTTTL_GetOctave(pEASData->hwInstData, pData, &pData->octave)) != EAS_SUCCESS)
   1074                     return result;
   1075                 break;
   1076 
   1077             /* get style */
   1078             case 's':
   1079                 if ((result = RTTTL_GetStyle(pEASData->hwInstData, pData)) != EAS_SUCCESS)
   1080                     return result;
   1081                 break;
   1082 
   1083             /* unrecognized control */
   1084             default:
   1085                 return EAS_ERROR_FILE_FORMAT;
   1086         }
   1087 
   1088         /* next character should be comma or colon */
   1089         if ((result = RTTTL_GetNextChar(pEASData->hwInstData, pData, &temp)) != EAS_SUCCESS)
   1090             return result;
   1091 
   1092         /* check for end of control field */
   1093         if (temp == ':')
   1094             break;
   1095 
   1096         /* must be a comma */
   1097         if (temp != ',')
   1098             return EAS_ERROR_FILE_FORMAT;
   1099     }
   1100 
   1101     /* should be at the start of the music block */
   1102     if ((result = EAS_HWFilePos(pEASData->hwInstData, pData->fileHandle, &pData->repeatOffset)) != EAS_SUCCESS)
   1103         return result;
   1104 
   1105     return EAS_SUCCESS;
   1106 }
   1107 
   1108 /*----------------------------------------------------------------------------
   1109  * RTTTL_GetNextChar()
   1110  *----------------------------------------------------------------------------
   1111  * Purpose:
   1112  *
   1113  *
   1114  * Inputs:
   1115  *
   1116  *
   1117  * Outputs:
   1118  *
   1119  *
   1120  * Side Effects:
   1121  *
   1122  *----------------------------------------------------------------------------
   1123 */
   1124 static EAS_RESULT RTTTL_GetNextChar (EAS_HW_DATA_HANDLE hwInstData, S_RTTTL_DATA *pData, EAS_I8 *pValue)
   1125 {
   1126     EAS_RESULT result;
   1127     EAS_I8 temp;
   1128 
   1129     *pValue = 0;
   1130     for(;;)
   1131     {
   1132 
   1133         /* check for character that has been put back */
   1134         if (pData->dataByte)
   1135         {
   1136             temp = pData->dataByte;
   1137             pData->dataByte = 0;
   1138         }
   1139         else
   1140         {
   1141             if ((result = EAS_HWGetByte(hwInstData, pData->fileHandle, &temp)) != EAS_SUCCESS)
   1142                 return result;
   1143         }
   1144 
   1145         /* ignore white space */
   1146         if (!IsSpace(temp))
   1147         {
   1148             *pValue = ToLower(temp);
   1149             return EAS_SUCCESS;
   1150         }
   1151     }
   1152 }
   1153 
   1154 /*----------------------------------------------------------------------------
   1155  * RTTTL_PeekNextChar()
   1156  *----------------------------------------------------------------------------
   1157  * Purpose:
   1158  *
   1159  *
   1160  * Inputs:
   1161  *
   1162  *
   1163  * Outputs:
   1164  *
   1165  *
   1166  * Side Effects:
   1167  *
   1168  *----------------------------------------------------------------------------
   1169 */
   1170 static EAS_RESULT RTTTL_PeekNextChar (EAS_HW_DATA_HANDLE hwInstData, S_RTTTL_DATA *pData, EAS_I8 *pValue)
   1171 {
   1172     EAS_RESULT result;
   1173     EAS_I8 temp;
   1174 
   1175     *pValue = 0;
   1176     for(;;)
   1177     {
   1178 
   1179         /* read a character from the file, if necessary */
   1180         if (!pData->dataByte)
   1181         {
   1182             if ((result = EAS_HWGetByte(hwInstData, pData->fileHandle, &pData->dataByte)) != EAS_SUCCESS)
   1183                 return result;
   1184 
   1185         }
   1186         temp = pData->dataByte;
   1187 
   1188         /* ignore white space */
   1189         if (!IsSpace(temp))
   1190         {
   1191             *pValue = ToLower(temp);
   1192             return EAS_SUCCESS;
   1193         }
   1194         pData->dataByte = 0;
   1195     }
   1196 }
   1197 
   1198