Home | History | Annotate | Download | only in lib_src
      1 /*----------------------------------------------------------------------------
      2  *
      3  * File:
      4  * eas_imelody.c
      5  *
      6  * Contents and purpose:
      7  * iMelody 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: 797 $
     26  *   $Date: 2007-08-01 00:15:56 -0700 (Wed, 01 Aug 2007) $
     27  *----------------------------------------------------------------------------
     28 */
     29 
     30 /* lint doesn't like the way some string.h files look */
     31 #ifdef _lint
     32 #include "lint_stdlib.h"
     33 #else
     34 #include <string.h>
     35 #endif
     36 
     37 #include "eas_data.h"
     38 #include "eas_miditypes.h"
     39 #include "eas_parser.h"
     40 #include "eas_report.h"
     41 #include "eas_host.h"
     42 #include "eas_midi.h"
     43 #include "eas_config.h"
     44 #include "eas_vm_protos.h"
     45 #include "eas_imelodydata.h"
     46 #include "eas_ctype.h"
     47 
     48 // #define _DEBUG_IMELODY
     49 
     50 /* increase gain for mono ringtones */
     51 #define IMELODY_GAIN_OFFSET     8
     52 
     53 /* length of 32nd note in 1/256ths of a msec for 120 BPM tempo */
     54 #define DEFAULT_TICK_CONV       16000
     55 #define TICK_CONVERT            1920000
     56 
     57 /* default channel and program for iMelody playback */
     58 #define IMELODY_CHANNEL         0
     59 #define IMELODY_PROGRAM         80
     60 #define IMELODY_VEL_MUL         4
     61 #define IMELODY_VEL_OFS         67
     62 
     63 /* multiplier for fixed point triplet conversion */
     64 #define TRIPLET_MULTIPLIER      683
     65 #define TRIPLET_SHIFT           10
     66 
     67 static const char* const tokens[] =
     68 {
     69     "BEGIN:IMELODY",
     70     "VERSION:",
     71     "FORMAT:CLASS",
     72     "NAME:",
     73     "COMPOSER:",
     74     "BEAT:",
     75     "STYLE:",
     76     "VOLUME:",
     77     "MELODY:",
     78     "END:IMELODY"
     79 };
     80 
     81 /* ledon or ledoff */
     82 static const char ledStr[] = "edo";
     83 
     84 /* vibeon or vibeoff */
     85 static const char vibeStr[] = "ibeo";
     86 
     87 /* backon or backoff */
     88 static const char backStr[] = "cko";
     89 
     90 typedef enum
     91 {
     92     TOKEN_BEGIN,
     93     TOKEN_VERSION,
     94     TOKEN_FORMAT,
     95     TOKEN_NAME,
     96     TOKEN_COMPOSER,
     97     TOKEN_BEAT,
     98     TOKEN_STYLE,
     99     TOKEN_VOLUME,
    100     TOKEN_MELODY,
    101     TOKEN_END,
    102     TOKEN_INVALID
    103 } ENUM_IMELODY_TOKENS;
    104 
    105 /* lookup table for note values */
    106 static const EAS_I8 noteTable[] = { 9, 11, 0, 2, 4, 5, 7 };
    107 
    108 /* inline functions */
    109 #ifdef _DEBUG_IMELODY
    110 static void PutBackChar (S_IMELODY_DATA *pData)
    111 {
    112     if (pData->index)
    113         pData->index--;
    114     { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "PutBackChar '%c'\n", pData->buffer[pData->index]); */ }
    115 }
    116 #else
    117 EAS_INLINE void PutBackChar (S_IMELODY_DATA *pData) { if (pData->index) pData->index--; }
    118 #endif
    119 
    120 
    121 /* local prototypes */
    122 static EAS_RESULT IMY_CheckFileType (S_EAS_DATA *pEASData, EAS_FILE_HANDLE fileHandle, EAS_VOID_PTR *ppHandle, EAS_I32 offset);
    123 static EAS_RESULT IMY_Prepare (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData);
    124 static EAS_RESULT IMY_Time (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_U32 *pTime);
    125 static EAS_RESULT IMY_Event (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_INT parserMode);
    126 static EAS_RESULT IMY_State (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_STATE *pState);
    127 static EAS_RESULT IMY_Close (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData);
    128 static EAS_RESULT IMY_Reset (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData);
    129 static EAS_RESULT IMY_Pause (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData);
    130 static EAS_RESULT IMY_Resume (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData);
    131 static EAS_RESULT IMY_SetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 value);
    132 static EAS_RESULT IMY_GetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 *pValue);
    133 static EAS_BOOL IMY_PlayNote (S_EAS_DATA *pEASData, S_IMELODY_DATA *pData, EAS_I8 note, EAS_INT parserMode);
    134 static EAS_BOOL IMY_PlayRest (S_EAS_DATA *pEASData, S_IMELODY_DATA *pData);
    135 static EAS_BOOL IMY_GetDuration (EAS_HW_DATA_HANDLE hwInstData, S_IMELODY_DATA *pData, EAS_I32 *pDuration);
    136 static EAS_BOOL IMY_GetLEDState (S_EAS_DATA *pEASData, S_IMELODY_DATA *pData);
    137 static EAS_BOOL IMY_GetVibeState (S_EAS_DATA *pEASData, S_IMELODY_DATA *pData);
    138 static EAS_BOOL IMY_GetBackState (S_EAS_DATA *pEASData, S_IMELODY_DATA *pData);
    139 static EAS_BOOL IMY_GetVolume (EAS_HW_DATA_HANDLE hwInstData, S_IMELODY_DATA *pData, EAS_BOOL inHeader);
    140 static EAS_BOOL IMY_GetNumber (EAS_HW_DATA_HANDLE hwInstData, S_IMELODY_DATA *pData, EAS_INT *temp, EAS_BOOL inHeader);
    141 static EAS_RESULT IMY_ParseHeader (S_EAS_DATA *pEASData, S_IMELODY_DATA* pData);
    142 static EAS_I8 IMY_GetNextChar (EAS_HW_DATA_HANDLE hwInstData, S_IMELODY_DATA *pData, EAS_BOOL inHeader);
    143 static EAS_RESULT IMY_ReadLine (EAS_HW_DATA_HANDLE hwInstData, EAS_FILE_HANDLE fileHandle, EAS_I8 *buffer, EAS_I32 *pStartLine);
    144 static EAS_INT IMY_ParseLine (EAS_I8 *buffer, EAS_U8 *pIndex);
    145 
    146 
    147 /*----------------------------------------------------------------------------
    148  *
    149  * EAS_iMelody_Parser
    150  *
    151  * This structure contains the functional interface for the iMelody parser
    152  *----------------------------------------------------------------------------
    153 */
    154 const S_FILE_PARSER_INTERFACE EAS_iMelody_Parser =
    155 {
    156     IMY_CheckFileType,
    157     IMY_Prepare,
    158     IMY_Time,
    159     IMY_Event,
    160     IMY_State,
    161     IMY_Close,
    162     IMY_Reset,
    163     IMY_Pause,
    164     IMY_Resume,
    165     NULL,
    166     IMY_SetData,
    167     IMY_GetData,
    168     NULL
    169 };
    170 
    171 /*----------------------------------------------------------------------------
    172  * IMY_CheckFileType()
    173  *----------------------------------------------------------------------------
    174  * Purpose:
    175  * Check the file type to see if we can parse it
    176  *
    177  * Inputs:
    178  * pEASData         - pointer to overall EAS data structure
    179  * handle           - pointer to file handle
    180  *
    181  * Outputs:
    182  *
    183  *
    184  * Side Effects:
    185  *
    186  *----------------------------------------------------------------------------
    187 */
    188 static EAS_RESULT IMY_CheckFileType (S_EAS_DATA *pEASData, EAS_FILE_HANDLE fileHandle, EAS_VOID_PTR *ppHandle, EAS_I32 offset)
    189 {
    190     S_IMELODY_DATA* pData;
    191     EAS_I8 buffer[MAX_LINE_SIZE+1];
    192     EAS_U8 index;
    193 
    194 #ifdef _DEBUG_IMELODY
    195     { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "Enter IMY_CheckFileType\n"); */ }
    196 #endif
    197 
    198     /* read the first line of the file */
    199     *ppHandle = NULL;
    200     if (IMY_ReadLine(pEASData->hwInstData, fileHandle, buffer, NULL) != EAS_SUCCESS)
    201         return EAS_SUCCESS;
    202 
    203     /* check for header string */
    204     if (IMY_ParseLine(buffer, &index) == TOKEN_BEGIN)
    205     {
    206 
    207         /* check for static memory allocation */
    208         if (pEASData->staticMemoryModel)
    209             pData = EAS_CMEnumData(EAS_CM_IMELODY_DATA);
    210         else
    211             pData = EAS_HWMalloc(pEASData->hwInstData, sizeof(S_IMELODY_DATA));
    212         if (!pData)
    213             return EAS_ERROR_MALLOC_FAILED;
    214         EAS_HWMemSet(pData, 0, sizeof(S_IMELODY_DATA));
    215 
    216         /* initialize */
    217         pData->fileHandle = fileHandle;
    218         pData->fileOffset = offset;
    219         pData->state = EAS_STATE_ERROR;
    220         pData->state = EAS_STATE_OPEN;
    221 
    222         /* return a pointer to the instance data */
    223         *ppHandle = pData;
    224     }
    225 
    226     return EAS_SUCCESS;
    227 }
    228 
    229 /*----------------------------------------------------------------------------
    230  * IMY_Prepare()
    231  *----------------------------------------------------------------------------
    232  * Purpose:
    233  * Prepare to parse the file. Allocates instance data (or uses static allocation for
    234  * static memory model).
    235  *
    236  * Inputs:
    237  * pEASData         - pointer to overall EAS data structure
    238  * handle           - pointer to file handle
    239  *
    240  * Outputs:
    241  *
    242  *
    243  * Side Effects:
    244  *
    245  *----------------------------------------------------------------------------
    246 */
    247 static EAS_RESULT IMY_Prepare (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
    248 {
    249     S_IMELODY_DATA* pData;
    250     EAS_RESULT result;
    251 
    252 #ifdef _DEBUG_IMELODY
    253     { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "Enter IMY_Prepare\n"); */ }
    254 #endif
    255 
    256     /* check for valid state */
    257     pData = (S_IMELODY_DATA*) pInstData;
    258     if (pData->state != EAS_STATE_OPEN)
    259         return EAS_ERROR_NOT_VALID_IN_THIS_STATE;
    260 
    261     /* instantiate a synthesizer */
    262     if ((result = VMInitMIDI(pEASData, &pData->pSynth)) != EAS_SUCCESS)
    263     {
    264         { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "VMInitMIDI returned %d\n", result); */ }
    265         return result;
    266     }
    267 
    268     /* parse the header */
    269     if ((result = IMY_ParseHeader(pEASData,  pData)) != EAS_SUCCESS)
    270         return result;
    271 
    272 #ifdef _DEBUG_IMELODY
    273     { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_Prepare: state set to EAS_STATE_READY\n"); */ }
    274 #endif
    275 
    276     pData ->state = EAS_STATE_READY;
    277     return EAS_SUCCESS;
    278 }
    279 
    280 /*----------------------------------------------------------------------------
    281  * IMY_Time()
    282  *----------------------------------------------------------------------------
    283  * Purpose:
    284  * Returns the time of the next event in msecs
    285  *
    286  * Inputs:
    287  * pEASData         - pointer to overall EAS data structure
    288  * handle           - pointer to file handle
    289  * pTime            - pointer to variable to hold time of next event (in msecs)
    290  *
    291  * Outputs:
    292  *
    293  *
    294  * Side Effects:
    295  *
    296  *----------------------------------------------------------------------------
    297 */
    298 /*lint -esym(715, pEASData) common decoder interface - pEASData not used */
    299 static EAS_RESULT IMY_Time (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_U32 *pTime)
    300 {
    301     S_IMELODY_DATA *pData;
    302 
    303     pData = (S_IMELODY_DATA*) pInstData;
    304 
    305     /* return time in milliseconds */
    306     /*lint -e{704} use shift instead of division */
    307     *pTime = pData->time >> 8;
    308     return EAS_SUCCESS;
    309 }
    310 
    311 /*----------------------------------------------------------------------------
    312  * IMY_Event()
    313  *----------------------------------------------------------------------------
    314  * Purpose:
    315  * Parse the next event in the file
    316  *
    317  * Inputs:
    318  * pEASData         - pointer to overall EAS data structure
    319  * handle           - pointer to file handle
    320  *
    321  * Outputs:
    322  *
    323  *
    324  * Side Effects:
    325  *
    326  *----------------------------------------------------------------------------
    327 */
    328 static EAS_RESULT IMY_Event (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_INT parserMode)
    329 {
    330     S_IMELODY_DATA* pData;
    331     EAS_RESULT result;
    332     EAS_I8 c;
    333     EAS_BOOL eof;
    334     EAS_INT temp;
    335 
    336     pData = (S_IMELODY_DATA*) pInstData;
    337     if (pData->state >= EAS_STATE_OPEN)
    338         return EAS_SUCCESS;
    339 
    340     if (pData->state == EAS_STATE_READY) {
    341         pData->state = EAS_STATE_PLAY;
    342     }
    343 
    344     /* initialize MIDI channel when the track starts playing */
    345     if (pData->time == 0)
    346     {
    347 
    348 #ifdef _DEBUG_IMELODY
    349     { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_Event: Reset\n"); */ }
    350 #endif
    351         /* set program to square lead */
    352         VMProgramChange(pEASData->pVoiceMgr, pData->pSynth, IMELODY_CHANNEL, IMELODY_PROGRAM);
    353 
    354         /* set channel volume to max */
    355         VMControlChange(pEASData->pVoiceMgr, pData->pSynth, IMELODY_CHANNEL, 7, 127);
    356     }
    357 
    358     /* check for end of note */
    359     if (pData->note)
    360     {
    361 
    362 #ifdef _DEBUG_IMELODY
    363     { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "Stopping note %d\n", pData->note); */ }
    364 #endif
    365         /* stop the note */
    366         VMStopNote(pEASData->pVoiceMgr, pData->pSynth, IMELODY_CHANNEL, pData->note, 0);
    367         pData->note = 0;
    368 
    369         /* check for rest between notes */
    370         if (pData->restTicks)
    371         {
    372             pData->time += pData->restTicks;
    373             pData->restTicks = 0;
    374             return EAS_SUCCESS;
    375         }
    376     }
    377 
    378     /* parse the next event */
    379     eof = EAS_FALSE;
    380     while (!eof)
    381     {
    382 
    383         /* get next character */
    384         c = IMY_GetNextChar(pEASData->hwInstData, pData, EAS_FALSE);
    385 
    386         switch (c)
    387         {
    388             /* start repeat */
    389             case '(':
    390 
    391 #ifdef _DEBUG_IMELODY
    392                 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "Enter repeat section\n", c); */ }
    393 #endif
    394 
    395                 if (pData->repeatOffset < 0)
    396                 {
    397                     pData->repeatOffset = pData->startLine + (EAS_I32) pData->index;
    398 
    399                     /* save current time and check it later to make sure the loop isn't zero length */
    400                     pData->repeatTime = pData->time;
    401 
    402 #ifdef _DEBUG_IMELODY
    403                     { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "Repeat offset = %d\n", pData->repeatOffset); */ }
    404 #endif
    405                 }
    406                 else
    407                     { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "Ignoring nested repeat section\n"); */ }
    408                 break;
    409 
    410             /* end repeat */
    411             case ')':
    412 
    413 #ifdef _DEBUG_IMELODY
    414                 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "End repeat section, repeat offset = %d\n", pData->repeatOffset); */ }
    415 #endif
    416                 /* ignore zero-length loops */
    417                 if (pData->repeatTime == pData->time) {
    418                     pData->repeatCount = -1;
    419                     pData->repeatOffset = -1;
    420                 } else if (pData->repeatCount >= 0) {
    421 
    422                     /* decrement repeat count (repeatCount == 0 means infinite loop) */
    423                     if (pData->repeatCount > 0)
    424                     {
    425                         if (--pData->repeatCount == 0)
    426                         {
    427                             pData->repeatCount = -1;
    428 #ifdef _DEBUG_IMELODY
    429                             { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "Repeat loop complete\n"); */ }
    430 #endif
    431                         }
    432                     }
    433 
    434 //2 TEMPORARY FIX: If locating, don't do infinite loops.
    435 //3 We need a different mode for metadata parsing where we don't loop at all
    436                     if ((parserMode == eParserModePlay) || (pData->repeatCount != 0))
    437                     {
    438 
    439 #ifdef _DEBUG_IMELODY
    440                         { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "Rewinding file for repeat\n"); */ }
    441 #endif
    442                         /* rewind to start of loop */
    443                         if ((result = EAS_HWFileSeek(pEASData->hwInstData, pData->fileHandle, pData->repeatOffset)) != EAS_SUCCESS)
    444                             return result;
    445                         IMY_ReadLine(pEASData->hwInstData, pData->fileHandle, pData->buffer, &pData->startLine);
    446                         pData->index = 0;
    447 
    448                         /* if last loop, prevent future loops */
    449                         if (pData->repeatCount == -1)
    450                             pData->repeatOffset = -1;
    451                     }
    452                 }
    453                 break;
    454 
    455             /* repeat count */
    456             case '@':
    457                 if (!IMY_GetNumber(pEASData->hwInstData, pData, &temp, EAS_FALSE))
    458                     eof = EAS_TRUE;
    459                 else if (pData->repeatOffset > 0)
    460                 {
    461 
    462 #ifdef _DEBUG_IMELODY
    463                     { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "Repeat count = %dt", pData->repeatCount); */ }
    464 #endif
    465                     if (pData->repeatCount < 0)
    466                         pData->repeatCount = (EAS_I16) temp;
    467                 }
    468                 break;
    469 
    470             /* volume */
    471             case 'V':
    472                 if (!IMY_GetVolume(pEASData->hwInstData, pData, EAS_FALSE))
    473                     eof = EAS_TRUE;
    474                 break;
    475 
    476             /* flat */
    477             case '&':
    478                 pData->noteModifier = -1;
    479                 break;
    480 
    481             /* sharp */
    482             case '#':
    483                 pData->noteModifier = +1;
    484                 break;
    485 
    486             /* octave */
    487             case '*':
    488                 c = IMY_GetNextChar(pEASData->hwInstData, pData, EAS_FALSE);
    489                 if (IsDigit(c))
    490                     pData->octave = (EAS_U8) ((c - '0' + 1) * 12);
    491                 else if (!c)
    492                     eof = EAS_TRUE;
    493                 break;
    494 
    495             /* ledon or ledoff */
    496             case 'l':
    497                 if (!IMY_GetLEDState(pEASData, pData))
    498                     eof = EAS_TRUE;
    499                 break;
    500 
    501             /* vibeon or vibeoff */
    502             case 'v':
    503                 if (!IMY_GetVibeState(pEASData, pData))
    504                     eof = EAS_TRUE;
    505                 break;
    506 
    507             /* either a B note or backon or backoff */
    508             case 'b':
    509                 if (IMY_GetNextChar(pEASData->hwInstData, pData, EAS_FALSE) == 'a')
    510                 {
    511                     if (!IMY_GetBackState(pEASData, pData))
    512                         eof = EAS_TRUE;
    513                 }
    514                 else
    515                 {
    516                     PutBackChar(pData);
    517                     if (IMY_PlayNote(pEASData, pData, c, parserMode))
    518                         return EAS_SUCCESS;
    519                     eof = EAS_TRUE;
    520                 }
    521                 break;
    522 
    523             /* rest */
    524             case 'r':
    525             case 'R':
    526                 if (IMY_PlayRest(pEASData, pData))
    527                     return EAS_SUCCESS;
    528                 eof = EAS_TRUE;
    529                 break;
    530 
    531             /* EOF */
    532             case 0:
    533 #ifdef _DEBUG_IMELODY
    534                 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_Event: end of iMelody file detected\n"); */ }
    535 #endif
    536                 eof = EAS_TRUE;
    537             break;
    538 
    539             /* must be a note */
    540             default:
    541                 c = ToLower(c);
    542                 if ((c >= 'a') && (c <= 'g'))
    543                 {
    544                     if (IMY_PlayNote(pEASData, pData, c, parserMode))
    545                         return EAS_SUCCESS;
    546                     eof = EAS_TRUE;
    547                 }
    548                 else
    549                     { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "Ignoring unexpected character '%c' [0x%02x]\n", c, c); */ }
    550                 break;
    551         }
    552     }
    553 
    554     /* handle EOF */
    555 #ifdef _DEBUG_IMELODY
    556     { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_Event: state set to EAS_STATE_STOPPING\n"); */ }
    557 #endif
    558     pData->state = EAS_STATE_STOPPING;
    559     VMReleaseAllVoices(pEASData->pVoiceMgr, pData->pSynth);
    560     return EAS_SUCCESS;
    561 }
    562 
    563 /*----------------------------------------------------------------------------
    564  * IMY_State()
    565  *----------------------------------------------------------------------------
    566  * Purpose:
    567  * Returns the current state of the stream
    568  *
    569  * Inputs:
    570  * pEASData         - pointer to overall EAS data structure
    571  * handle           - pointer to file handle
    572  * pState           - pointer to variable to store state
    573  *
    574  * Outputs:
    575  *
    576  *
    577  * Side Effects:
    578  *
    579  *----------------------------------------------------------------------------
    580 */
    581 /*lint -esym(715, pEASData) common decoder interface - pEASData not used */
    582 static EAS_RESULT IMY_State (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 *pState)
    583 {
    584     S_IMELODY_DATA* pData;
    585 
    586     /* establish pointer to instance data */
    587     pData = (S_IMELODY_DATA*) pInstData;
    588 
    589     /* if stopping, check to see if synth voices are active */
    590     if (pData->state == EAS_STATE_STOPPING)
    591     {
    592         if (VMActiveVoices(pData->pSynth) == 0)
    593         {
    594             pData->state = EAS_STATE_STOPPED;
    595 #ifdef _DEBUG_IMELODY
    596             { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_State: state set to EAS_STATE_STOPPED\n"); */ }
    597 #endif
    598         }
    599     }
    600 
    601     if (pData->state == EAS_STATE_PAUSING)
    602     {
    603         if (VMActiveVoices(pData->pSynth) == 0)
    604         {
    605 #ifdef _DEBUG_IMELODY
    606             { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_State: state set to EAS_STATE_PAUSED\n"); */ }
    607 #endif
    608             pData->state = EAS_STATE_PAUSED;
    609         }
    610     }
    611 
    612     /* return current state */
    613     *pState = pData->state;
    614     return EAS_SUCCESS;
    615 }
    616 
    617 /*----------------------------------------------------------------------------
    618  * IMY_Close()
    619  *----------------------------------------------------------------------------
    620  * Purpose:
    621  * Close the file and clean up
    622  *
    623  * Inputs:
    624  * pEASData         - pointer to overall EAS data structure
    625  * handle           - pointer to file handle
    626  *
    627  * Outputs:
    628  *
    629  *
    630  * Side Effects:
    631  *
    632  *----------------------------------------------------------------------------
    633 */
    634 static EAS_RESULT IMY_Close (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
    635 {
    636     S_IMELODY_DATA* pData;
    637     EAS_RESULT result;
    638 
    639 #ifdef _DEBUG_IMELODY
    640     { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_Close: close file\n"); */ }
    641 #endif
    642 
    643     pData = (S_IMELODY_DATA*) pInstData;
    644 
    645     /* close the file */
    646     if ((result = EAS_HWCloseFile(pEASData->hwInstData, pData->fileHandle)) != EAS_SUCCESS)
    647             return result;
    648 
    649     /* free the synth */
    650     if (pData->pSynth != NULL)
    651         VMMIDIShutdown(pEASData, pData->pSynth);
    652 
    653     /* if using dynamic memory, free it */
    654     if (!pEASData->staticMemoryModel)
    655         EAS_HWFree(pEASData->hwInstData, pData);
    656 
    657     return EAS_SUCCESS;
    658 }
    659 
    660 /*----------------------------------------------------------------------------
    661  * IMY_Reset()
    662  *----------------------------------------------------------------------------
    663  * Purpose:
    664  * Reset the sequencer. Used for locating backwards in the file.
    665  *
    666  * Inputs:
    667  * pEASData         - pointer to overall EAS data structure
    668  * handle           - pointer to file handle
    669  *
    670  * Outputs:
    671  *
    672  *
    673  * Side Effects:
    674  *
    675  *----------------------------------------------------------------------------
    676 */
    677 static EAS_RESULT IMY_Reset (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
    678 {
    679     S_IMELODY_DATA* pData;
    680     EAS_RESULT result;
    681 
    682 #ifdef _DEBUG_IMELODY
    683     { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_Reset: reset file\n"); */ }
    684 #endif
    685     pData = (S_IMELODY_DATA*) pInstData;
    686 
    687     /* reset the synth */
    688     VMReset(pEASData->pVoiceMgr, pData->pSynth, EAS_TRUE);
    689 
    690     /* reset time to zero */
    691     pData->time = 0;
    692     pData->note = 0;
    693 
    694     /* reset file position and re-parse header */
    695     pData->state = EAS_STATE_ERROR;
    696     if ((result = EAS_HWFileSeek(pEASData->hwInstData, pData->fileHandle, pData->fileOffset)) != EAS_SUCCESS)
    697         return result;
    698     if ((result = IMY_ParseHeader (pEASData,  pData)) != EAS_SUCCESS)
    699         return result;
    700 
    701 #ifdef _DEBUG_IMELODY
    702     { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_Reset: state set to EAS_STATE_ERROR\n"); */ }
    703 #endif
    704 
    705     pData->state = EAS_STATE_READY;
    706     return EAS_SUCCESS;
    707 }
    708 
    709 /*----------------------------------------------------------------------------
    710  * IMY_Pause()
    711  *----------------------------------------------------------------------------
    712  * Purpose:
    713  * Pauses the sequencer. Mutes all voices and sets state to pause.
    714  *
    715  * Inputs:
    716  * pEASData         - pointer to overall EAS data structure
    717  * handle           - pointer to file handle
    718  *
    719  * Outputs:
    720  *
    721  *
    722  * Side Effects:
    723  *
    724  *----------------------------------------------------------------------------
    725 */
    726 static EAS_RESULT IMY_Pause (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
    727 {
    728     S_IMELODY_DATA *pData;
    729 
    730 #ifdef _DEBUG_IMELODY
    731     { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_Pause: pause file\n"); */ }
    732 #endif
    733 
    734     /* can't pause a stopped stream */
    735     pData = (S_IMELODY_DATA*) pInstData;
    736     if (pData->state == EAS_STATE_STOPPED)
    737         return EAS_ERROR_ALREADY_STOPPED;
    738 
    739     /* mute the synthesizer */
    740     VMMuteAllVoices(pEASData->pVoiceMgr, pData->pSynth);
    741     pData->state = EAS_STATE_PAUSING;
    742     return EAS_SUCCESS;
    743 }
    744 
    745 /*----------------------------------------------------------------------------
    746  * IMY_Resume()
    747  *----------------------------------------------------------------------------
    748  * Purpose:
    749  * Resume playing after a pause, sets state back to playing.
    750  *
    751  * Inputs:
    752  * pEASData         - pointer to overall EAS data structure
    753  * handle           - pointer to file handle
    754  *
    755  * Outputs:
    756  *
    757  *
    758  * Side Effects:
    759  *
    760  *----------------------------------------------------------------------------
    761 */
    762 /*lint -esym(715, pEASData) common decoder interface - pEASData not used */
    763 static EAS_RESULT IMY_Resume (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
    764 {
    765     S_IMELODY_DATA *pData;
    766 
    767 #ifdef _DEBUG_IMELODY
    768     { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_Resume: resume file\n"); */ }
    769 #endif
    770 
    771     /* can't resume a stopped stream */
    772     pData = (S_IMELODY_DATA*) pInstData;
    773     if (pData->state == EAS_STATE_STOPPED)
    774         return EAS_ERROR_ALREADY_STOPPED;
    775 
    776     /* nothing to do but resume playback */
    777     pData->state = EAS_STATE_PLAY;
    778     return EAS_SUCCESS;
    779 }
    780 
    781 /*----------------------------------------------------------------------------
    782  * IMY_SetData()
    783  *----------------------------------------------------------------------------
    784  * Purpose:
    785  * Adjust tempo relative to song tempo
    786  *
    787  * Inputs:
    788  * pEASData         - pointer to overall EAS data structure
    789  * pInstData        - pointer to iMelody instance data
    790  * rate             - rate (28-bit fractional amount)
    791  *
    792  * Outputs:
    793  *
    794  *
    795  * Side Effects:
    796  *
    797  *----------------------------------------------------------------------------
    798 */
    799 /*lint -esym(715, pEASData) common decoder interface - pEASData not used */
    800 static EAS_RESULT IMY_SetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 value)
    801 {
    802     S_IMELODY_DATA *pData;
    803 
    804     pData = (S_IMELODY_DATA*) pInstData;
    805     switch (param)
    806     {
    807 
    808         /* set metadata callback */
    809         case PARSER_DATA_METADATA_CB:
    810             EAS_HWMemCpy(&pData->metadata, (void*) value, sizeof(S_METADATA_CB));
    811             break;
    812 
    813         default:
    814             return EAS_ERROR_INVALID_PARAMETER;
    815     }
    816 
    817     return EAS_SUCCESS;
    818 }
    819 
    820 /*----------------------------------------------------------------------------
    821  * IMY_GetData()
    822  *----------------------------------------------------------------------------
    823  * Purpose:
    824  * Return the file type
    825  *
    826  * Inputs:
    827  * pEASData         - pointer to overall EAS data structure
    828  * pInstData        - pointer to iMelody instance data
    829  *
    830  * Outputs:
    831  *
    832  *
    833  * Side Effects:
    834  *
    835  *----------------------------------------------------------------------------
    836 */
    837 /*lint -esym(715, pEASData) common decoder interface - pEASData not used */
    838 static EAS_RESULT IMY_GetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 *pValue)
    839 {
    840     S_IMELODY_DATA *pData;
    841 
    842     pData = (S_IMELODY_DATA*) pInstData;
    843 
    844     switch (param)
    845     {
    846         /* return file type as iMelody */
    847         case PARSER_DATA_FILE_TYPE:
    848             *pValue = EAS_FILE_IMELODY;
    849             break;
    850 
    851         case PARSER_DATA_SYNTH_HANDLE:
    852             *pValue = (EAS_I32) pData->pSynth;
    853             break;
    854 
    855         case PARSER_DATA_GAIN_OFFSET:
    856             *pValue = IMELODY_GAIN_OFFSET;
    857             break;
    858 
    859         default:
    860             return EAS_ERROR_INVALID_PARAMETER;
    861     }
    862 
    863     return EAS_SUCCESS;
    864 }
    865 
    866 /*----------------------------------------------------------------------------
    867  * IMY_PlayNote()
    868  *----------------------------------------------------------------------------
    869  * Purpose:
    870  *
    871  *
    872  * Inputs:
    873  *
    874  *
    875  * Outputs:
    876  *
    877  *
    878  * Side Effects:
    879  *
    880  *----------------------------------------------------------------------------
    881 */
    882 static EAS_BOOL IMY_PlayNote (S_EAS_DATA *pEASData, S_IMELODY_DATA *pData, EAS_I8 note, EAS_INT parserMode)
    883 {
    884     EAS_I32 duration;
    885     EAS_U8 velocity;
    886 
    887 
    888 #ifdef _DEBUG_IMELODY
    889     { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_PlayNote: start note %d\n", note); */ }
    890 #endif
    891 
    892     /* get the duration */
    893     if (!IMY_GetDuration(pEASData->hwInstData, pData, &duration))
    894         return EAS_FALSE;
    895 
    896     /* save note value */
    897     pData->note = (EAS_U8) (pData->octave + noteTable[note - 'a'] + pData->noteModifier);
    898     velocity = (EAS_U8) (pData->volume ? pData->volume * IMELODY_VEL_MUL + IMELODY_VEL_OFS : 0);
    899 
    900     /* start note only if in play mode */
    901     if (parserMode == eParserModePlay)
    902         VMStartNote(pEASData->pVoiceMgr, pData->pSynth, IMELODY_CHANNEL, pData->note, velocity);
    903 
    904 #ifdef _DEBUG_IMELODY
    905     { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_PlayNote: Start note %d, duration %d\n", pData->note, duration); */ }
    906 #endif
    907 
    908     /* determine note length */
    909     switch (pData->style)
    910     {
    911         case 0:
    912             /*lint -e{704} shift for performance */
    913             pData->restTicks = duration >> 4;
    914             break;
    915         case 1:
    916             pData->restTicks = 0;
    917             break;
    918         case 2:
    919             /*lint -e{704} shift for performance */
    920             pData->restTicks = duration >> 1;
    921             break;
    922         default:
    923             { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "IMY_PlayNote: Note style out of range: %d\n", pData->style); */ }
    924             /*lint -e{704} shift for performance */
    925             pData->restTicks = duration >> 4;
    926             break;
    927     }
    928 
    929     /* next event is at end of this note */
    930     pData->time += duration - pData->restTicks;
    931 
    932     /* reset the flat/sharp modifier */
    933     pData->noteModifier = 0;
    934 
    935     return EAS_TRUE;
    936 }
    937 
    938 /*----------------------------------------------------------------------------
    939  * IMY_PlayRest()
    940  *----------------------------------------------------------------------------
    941  * Purpose:
    942  *
    943  *
    944  * Inputs:
    945  *
    946  *
    947  * Outputs:
    948  *
    949  *
    950  * Side Effects:
    951  *
    952  *----------------------------------------------------------------------------
    953 */
    954 static EAS_BOOL IMY_PlayRest (S_EAS_DATA *pEASData, S_IMELODY_DATA *pData)
    955 {
    956     EAS_I32 duration;
    957 
    958 #ifdef _DEBUG_IMELODY
    959     { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "Enter IMY_PlayRest]n"); */ }
    960 #endif
    961 
    962     /* get the duration */
    963     if (!IMY_GetDuration(pEASData->hwInstData, pData, &duration))
    964         return EAS_FALSE;
    965 
    966 #ifdef _DEBUG_IMELODY
    967     { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_PlayRest: note duration %d\n", duration); */ }
    968 #endif
    969 
    970     /* next event is at end of this note */
    971     pData->time += duration;
    972     return EAS_TRUE;
    973 }
    974 
    975 /*----------------------------------------------------------------------------
    976  * IMY_GetDuration()
    977  *----------------------------------------------------------------------------
    978  * Purpose:
    979  *
    980  *
    981  * Inputs:
    982  *
    983  *
    984  * Outputs:
    985  *
    986  *
    987  * Side Effects:
    988  *
    989  *----------------------------------------------------------------------------
    990 */
    991 
    992 static EAS_BOOL IMY_GetDuration (EAS_HW_DATA_HANDLE hwInstData, S_IMELODY_DATA *pData, EAS_I32 *pDuration)
    993 {
    994     EAS_I32 duration;
    995     EAS_I8 c;
    996 
    997     /* get the duration */
    998     *pDuration = 0;
    999     c = IMY_GetNextChar(hwInstData, pData, EAS_FALSE);
   1000     if (!c)
   1001         return EAS_FALSE;
   1002     if ((c < '0') || (c > '5'))
   1003     {
   1004 #ifdef _DEBUG_IMELODY
   1005         { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_GetDuration: error in duration '%c'\n", c); */ }
   1006 #endif
   1007         return EAS_FALSE;
   1008     }
   1009 
   1010     /* calculate total length of note */
   1011     duration = pData->tick * (1 << ('5' - c));
   1012 
   1013     /* check for duration modifier */
   1014     c = IMY_GetNextChar(hwInstData, pData, EAS_FALSE);
   1015     if (c)
   1016     {
   1017         if (c == '.')
   1018             /*lint -e{704} shift for performance */
   1019             duration += duration >> 1;
   1020         else if (c == ':')
   1021             /*lint -e{704} shift for performance */
   1022             duration += (duration >> 1) + (duration >> 2);
   1023         else if (c == ';')
   1024             /*lint -e{704} shift for performance */
   1025             duration = (duration * TRIPLET_MULTIPLIER) >> TRIPLET_SHIFT;
   1026         else
   1027             PutBackChar(pData);
   1028     }
   1029 
   1030     *pDuration = duration;
   1031     return EAS_TRUE;
   1032 }
   1033 
   1034 /*----------------------------------------------------------------------------
   1035  * IMY_GetLEDState()
   1036  *----------------------------------------------------------------------------
   1037  * Purpose:
   1038  *
   1039  *
   1040  * Inputs:
   1041  *
   1042  *
   1043  * Outputs:
   1044  *
   1045  *
   1046  * Side Effects:
   1047  *
   1048  *----------------------------------------------------------------------------
   1049 */
   1050 static EAS_BOOL IMY_GetLEDState (S_EAS_DATA *pEASData, S_IMELODY_DATA *pData)
   1051 {
   1052     EAS_I8 c;
   1053     EAS_INT i;
   1054 
   1055 #ifdef _DEBUG_IMELODY
   1056     { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "Enter IMY_GetLEDState\n"); */ }
   1057 #endif
   1058 
   1059     for (i = 0; i < 5; i++)
   1060     {
   1061         c = IMY_GetNextChar(pEASData->hwInstData, pData, EAS_FALSE);
   1062         if (!c)
   1063             return EAS_FALSE;
   1064         switch (i)
   1065         {
   1066             case 3:
   1067                 if (c == 'n')
   1068                 {
   1069 #ifdef _DEBUG_IMELODY
   1070                     { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_GetLEDState: LED on\n"); */ }
   1071 #endif
   1072                     EAS_HWLED(pEASData->hwInstData, EAS_TRUE);
   1073                     return EAS_TRUE;
   1074                 }
   1075                 else if (c != 'f')
   1076                     return EAS_FALSE;
   1077                 break;
   1078 
   1079             case 4:
   1080                 if (c == 'f')
   1081                 {
   1082 #ifdef _DEBUG_IMELODY
   1083                     { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_GetLEDState: LED off\n"); */ }
   1084 #endif
   1085                     EAS_HWLED(pEASData->hwInstData, EAS_FALSE);
   1086                     return EAS_TRUE;
   1087                 }
   1088                 return EAS_FALSE;
   1089 
   1090             default:
   1091                 if (c != ledStr[i])
   1092                     return EAS_FALSE;
   1093                 break;
   1094         }
   1095     }
   1096     return EAS_FALSE;
   1097 }
   1098 
   1099 /*----------------------------------------------------------------------------
   1100  * IMY_GetVibeState()
   1101  *----------------------------------------------------------------------------
   1102  * Purpose:
   1103  *
   1104  *
   1105  * Inputs:
   1106  *
   1107  *
   1108  * Outputs:
   1109  *
   1110  *
   1111  * Side Effects:
   1112  *
   1113  *----------------------------------------------------------------------------
   1114 */
   1115 static EAS_BOOL IMY_GetVibeState (S_EAS_DATA *pEASData, S_IMELODY_DATA *pData)
   1116 {
   1117     EAS_I8 c;
   1118     EAS_INT i;
   1119 
   1120 #ifdef _DEBUG_IMELODY
   1121     { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "Enter IMY_GetVibeState\n"); */ }
   1122 #endif
   1123 
   1124     for (i = 0; i < 6; i++)
   1125     {
   1126         c = IMY_GetNextChar(pEASData->hwInstData, pData, EAS_FALSE);
   1127         if (!c)
   1128             return EAS_FALSE;
   1129         switch (i)
   1130         {
   1131             case 4:
   1132                 if (c == 'n')
   1133                 {
   1134 #ifdef _DEBUG_IMELODY
   1135                     { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_GetVibeState: vibrate on\n"); */ }
   1136 #endif
   1137                     EAS_HWVibrate(pEASData->hwInstData, EAS_TRUE);
   1138                     return EAS_TRUE;
   1139                 }
   1140                 else if (c != 'f')
   1141                     return EAS_FALSE;
   1142                 break;
   1143 
   1144             case 5:
   1145                 if (c == 'f')
   1146                 {
   1147 #ifdef _DEBUG_IMELODY
   1148                     { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_GetVibeState: vibrate off\n"); */ }
   1149 #endif
   1150                     EAS_HWVibrate(pEASData->hwInstData, EAS_FALSE);
   1151                     return EAS_TRUE;
   1152                 }
   1153                 return EAS_FALSE;
   1154 
   1155             default:
   1156                 if (c != vibeStr[i])
   1157                     return EAS_FALSE;
   1158                 break;
   1159         }
   1160     }
   1161     return EAS_FALSE;
   1162 }
   1163 
   1164 /*----------------------------------------------------------------------------
   1165  * IMY_GetBackState()
   1166  *----------------------------------------------------------------------------
   1167  * Purpose:
   1168  *
   1169  *
   1170  * Inputs:
   1171  *
   1172  *
   1173  * Outputs:
   1174  *
   1175  *
   1176  * Side Effects:
   1177  *
   1178  *----------------------------------------------------------------------------
   1179 */
   1180 static EAS_BOOL IMY_GetBackState (S_EAS_DATA *pEASData, S_IMELODY_DATA *pData)
   1181 {
   1182     EAS_I8 c;
   1183     EAS_INT i;
   1184 
   1185 #ifdef _DEBUG_IMELODY
   1186     { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "Enter IMY_GetBackState\n"); */ }
   1187 #endif
   1188 
   1189     for (i = 0; i < 5; i++)
   1190     {
   1191         c = IMY_GetNextChar(pEASData->hwInstData, pData, EAS_FALSE);
   1192         if (!c)
   1193             return EAS_FALSE;
   1194         switch (i)
   1195         {
   1196             case 3:
   1197                 if (c == 'n')
   1198                 {
   1199 #ifdef _DEBUG_IMELODY
   1200                     { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_GetBackState: backlight on\n"); */ }
   1201 #endif
   1202                     EAS_HWBackLight(pEASData->hwInstData, EAS_TRUE);
   1203                     return EAS_TRUE;
   1204                 }
   1205                 else if (c != 'f')
   1206                     return EAS_FALSE;
   1207                 break;
   1208 
   1209             case 4:
   1210                 if (c == 'f')
   1211                 {
   1212 #ifdef _DEBUG_IMELODY
   1213                     { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_GetBackState: backlight off\n"); */ }
   1214 #endif
   1215                     EAS_HWBackLight(pEASData->hwInstData, EAS_FALSE);
   1216                     return EAS_TRUE;
   1217                 }
   1218                 return EAS_FALSE;
   1219 
   1220             default:
   1221                 if (c != backStr[i])
   1222                     return EAS_FALSE;
   1223                 break;
   1224         }
   1225     }
   1226     return EAS_FALSE;
   1227 }
   1228 
   1229 /*----------------------------------------------------------------------------
   1230  * IMY_GetVolume()
   1231  *----------------------------------------------------------------------------
   1232  * Purpose:
   1233  *
   1234  *
   1235  * Inputs:
   1236  *
   1237  *
   1238  * Outputs:
   1239  *
   1240  *
   1241  * Side Effects:
   1242  *
   1243  *----------------------------------------------------------------------------
   1244 */
   1245 static EAS_BOOL IMY_GetVolume (EAS_HW_DATA_HANDLE hwInstData, S_IMELODY_DATA *pData, EAS_BOOL inHeader)
   1246 {
   1247     EAS_INT temp;
   1248     EAS_I8 c;
   1249 
   1250 #ifdef _DEBUG_IMELODY
   1251     { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "Enter IMY_GetVolume\n"); */ }
   1252 #endif
   1253 
   1254     c = IMY_GetNextChar(hwInstData, pData, inHeader);
   1255     if (c == '+')
   1256     {
   1257         if (pData->volume < 15)
   1258             pData->volume++;
   1259         return EAS_TRUE;
   1260     }
   1261     else if (c == '-')
   1262     {
   1263         if (pData->volume > 0)
   1264             pData->volume--;
   1265         return EAS_TRUE;
   1266     }
   1267     else if (IsDigit(c))
   1268         temp = c - '0';
   1269     else
   1270         return EAS_FALSE;
   1271 
   1272     c = IMY_GetNextChar(hwInstData, pData, inHeader);
   1273     if (IsDigit(c))
   1274         temp = temp * 10 + c - '0';
   1275     else if (c)
   1276         PutBackChar(pData);
   1277     if ((temp >= 0) && (temp <= 15))
   1278     {
   1279         if (inHeader && (temp == 0))
   1280             { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "Ignoring V0 encountered in header\n"); */ }
   1281         else
   1282             pData->volume = (EAS_U8) temp;
   1283     }
   1284     return EAS_TRUE;
   1285 }
   1286 
   1287 /*----------------------------------------------------------------------------
   1288  * IMY_GetNumber()
   1289  *----------------------------------------------------------------------------
   1290  * Purpose:
   1291  *
   1292  *
   1293  * Inputs:
   1294  *
   1295  *
   1296  * Outputs:
   1297  *
   1298  *
   1299  * Side Effects:
   1300  *
   1301  *----------------------------------------------------------------------------
   1302 */
   1303 static EAS_BOOL IMY_GetNumber (EAS_HW_DATA_HANDLE hwInstData, S_IMELODY_DATA *pData, EAS_INT *temp, EAS_BOOL inHeader)
   1304 {
   1305     EAS_BOOL ok;
   1306     EAS_I8 c;
   1307 
   1308 #ifdef _DEBUG_IMELODY
   1309     { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "Enter IMY_GetNumber\n"); */ }
   1310 #endif
   1311 
   1312     *temp = 0;
   1313     ok = EAS_FALSE;
   1314     for (;;)
   1315     {
   1316         c = IMY_GetNextChar(hwInstData, pData, inHeader);
   1317         if (IsDigit(c))
   1318         {
   1319             *temp = *temp * 10 + c - '0';
   1320             ok = EAS_TRUE;
   1321         }
   1322         else
   1323         {
   1324             if (c)
   1325                 PutBackChar(pData);
   1326 
   1327 #ifdef _DEBUG_IMELODY
   1328             { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_GetNumber: value %d\n", *temp); */ }
   1329 #endif
   1330 
   1331             return ok;
   1332         }
   1333     }
   1334 }
   1335 
   1336 /*----------------------------------------------------------------------------
   1337  * IMY_GetVersion()
   1338  *----------------------------------------------------------------------------
   1339  * Purpose:
   1340  *
   1341  *
   1342  * Inputs:
   1343  *
   1344  *
   1345  * Outputs:
   1346  *
   1347  *
   1348  * Side Effects:
   1349  *
   1350  *----------------------------------------------------------------------------
   1351 */
   1352 static EAS_BOOL IMY_GetVersion (S_IMELODY_DATA *pData, EAS_INT *pVersion)
   1353 {
   1354     EAS_I8 c;
   1355     EAS_INT temp;
   1356     EAS_INT version;
   1357 
   1358     version = temp = 0;
   1359     for (;;)
   1360     {
   1361         c = pData->buffer[pData->index++];
   1362         if ((c == 0) || (c == '.'))
   1363         {
   1364             /*lint -e{701} use shift for performance */
   1365             version = (version << 8) + temp;
   1366             if (c == 0)
   1367             {
   1368 
   1369 #ifdef _DEBUG_IMELODY
   1370                 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_GetVersion: version 0x%04x\n", version); */ }
   1371 #endif
   1372 
   1373                 *pVersion = version;
   1374                 return EAS_TRUE;
   1375             }
   1376             temp = 0;
   1377         }
   1378         else if (IsDigit(c))
   1379             temp = (temp * 10) + c - '0';
   1380     }
   1381 }
   1382 
   1383 /*----------------------------------------------------------------------------
   1384  * IMY_MetaData()
   1385  *----------------------------------------------------------------------------
   1386  * Purpose:
   1387  * Prepare to parse the file. Allocates instance data (or uses static allocation for
   1388  * static memory model).
   1389  *
   1390  * Inputs:
   1391  * pEASData         - pointer to overall EAS data structure
   1392  * handle           - pointer to file handle
   1393  *
   1394  * Outputs:
   1395  *
   1396  *
   1397  * Side Effects:
   1398  *
   1399  *----------------------------------------------------------------------------
   1400 */
   1401 static void IMY_MetaData (S_IMELODY_DATA *pData, E_EAS_METADATA_TYPE metaType, EAS_I8 *buffer)
   1402 {
   1403     EAS_I32 len;
   1404 
   1405     /* check for callback */
   1406     if (!pData->metadata.callback)
   1407         return;
   1408 
   1409     /* copy data to host buffer */
   1410     len = (EAS_I32) strlen((char*) buffer);
   1411     if (len >pData->metadata.bufferSize)
   1412         len = pData->metadata.bufferSize;
   1413     strncpy((char*) pData->metadata.buffer, (char*) buffer, (size_t) len);
   1414     pData->metadata.buffer[len] = 0;
   1415 
   1416     /* callback to host */
   1417     pData->metadata.callback(metaType, pData->metadata.buffer, pData->metadata.pUserData);
   1418 }
   1419 
   1420 /*----------------------------------------------------------------------------
   1421  * IMY_ParseHeader()
   1422  *----------------------------------------------------------------------------
   1423  * Purpose:
   1424  * Prepare to parse the file. Allocates instance data (or uses static allocation for
   1425  * static memory model).
   1426  *
   1427  * Inputs:
   1428  * pEASData         - pointer to overall EAS data structure
   1429  * handle           - pointer to file handle
   1430  *
   1431  * Outputs:
   1432  *
   1433  *
   1434  * Side Effects:
   1435  *
   1436  *----------------------------------------------------------------------------
   1437 */
   1438 static EAS_RESULT IMY_ParseHeader (S_EAS_DATA *pEASData, S_IMELODY_DATA* pData)
   1439 {
   1440     EAS_RESULT result;
   1441     EAS_INT token;
   1442     EAS_INT temp;
   1443     EAS_I8 c;
   1444 
   1445 #ifdef _DEBUG_IMELODY
   1446     { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "Enter IMY_ParseHeader\n"); */ }
   1447 #endif
   1448 
   1449     /* initialize some defaults */
   1450     pData->time = 0;
   1451     pData->tick = DEFAULT_TICK_CONV;
   1452     pData->note = 0;
   1453     pData->noteModifier = 0;
   1454     pData ->restTicks = 0;
   1455     pData->volume = 7;
   1456     pData->octave = 60;
   1457     pData->repeatOffset = -1;
   1458     pData->repeatCount = -1;
   1459     pData->style = 0;
   1460 
   1461     /* force the read of the first line */
   1462     pData->index = 1;
   1463 
   1464     /* read data until we get to melody */
   1465     for (;;)
   1466     {
   1467         /* read a line from the file and parse the token */
   1468         if (pData->index != 0)
   1469         {
   1470             if ((result = IMY_ReadLine(pEASData->hwInstData, pData->fileHandle, pData->buffer, &pData->startLine)) != EAS_SUCCESS)
   1471             {
   1472 #ifdef _DEBUG_IMELODY
   1473                 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_ParseHeader: IMY_ReadLine returned %d\n", result); */ }
   1474 #endif
   1475                 return result;
   1476             }
   1477         }
   1478         token = IMY_ParseLine(pData->buffer, &pData->index);
   1479 
   1480         switch (token)
   1481         {
   1482             /* ignore these valid tokens */
   1483             case TOKEN_BEGIN:
   1484                 break;
   1485 
   1486             case TOKEN_FORMAT:
   1487                 if (!IMY_GetVersion(pData, &temp))
   1488                 {
   1489                     { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "Invalid FORMAT field '%s'\n", pData->buffer); */ }
   1490                     return EAS_ERROR_FILE_FORMAT;
   1491                 }
   1492                 if ((temp != 0x0100) && (temp != 0x0200))
   1493                 {
   1494                     { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "Unsupported FORMAT %02x\n", temp); */ }
   1495                     return EAS_ERROR_UNRECOGNIZED_FORMAT;
   1496                 }
   1497                 break;
   1498 
   1499             case TOKEN_VERSION:
   1500                 if (!IMY_GetVersion(pData, &temp))
   1501                 {
   1502                     { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "Invalid VERSION field '%s'\n", pData->buffer); */ }
   1503                     return EAS_ERROR_FILE_FORMAT;
   1504                 }
   1505                 if ((temp != 0x0100) && (temp != 0x0102))
   1506                 {
   1507                     { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "Unsupported VERSION %02x\n", temp); */ }
   1508                     return EAS_ERROR_UNRECOGNIZED_FORMAT;
   1509                 }
   1510                 break;
   1511 
   1512             case TOKEN_NAME:
   1513                 IMY_MetaData(pData, EAS_METADATA_TITLE, pData->buffer + pData->index);
   1514                 break;
   1515 
   1516             case TOKEN_COMPOSER:
   1517                 IMY_MetaData(pData, EAS_METADATA_AUTHOR, pData->buffer + pData->index);
   1518                 break;
   1519 
   1520             /* handle beat */
   1521             case TOKEN_BEAT:
   1522                 IMY_GetNumber(pEASData->hwInstData, pData, &temp, EAS_TRUE);
   1523                 if ((temp >= 25) && (temp <= 900))
   1524                     pData->tick = TICK_CONVERT / temp;
   1525                 break;
   1526 
   1527             /* handle style */
   1528             case TOKEN_STYLE:
   1529                 c = IMY_GetNextChar(pEASData->hwInstData, pData, EAS_TRUE);
   1530                 if (c == 'S')
   1531                     c = IMY_GetNextChar(pEASData->hwInstData, pData, EAS_TRUE);
   1532                 if ((c >= '0') && (c <= '2'))
   1533                     pData->style = (EAS_U8) (c - '0');
   1534                 else
   1535                 {
   1536                     PutBackChar(pData);
   1537                     { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "Error in style command: %s\n", pData->buffer); */ }
   1538                 }
   1539                 break;
   1540 
   1541             /* handle volume */
   1542             case TOKEN_VOLUME:
   1543                 c = IMY_GetNextChar(pEASData->hwInstData, pData, EAS_TRUE);
   1544                 if (c != 'V')
   1545                 {
   1546                     PutBackChar(pData);
   1547                     if (!IsDigit(c))
   1548                     {
   1549                         { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "Error in volume command: %s\n", pData->buffer); */ }
   1550                         break;
   1551                     }
   1552                 }
   1553                 IMY_GetVolume(pEASData->hwInstData, pData, EAS_TRUE);
   1554                 break;
   1555 
   1556             case TOKEN_MELODY:
   1557 #ifdef _DEBUG_IMELODY
   1558                 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "Header successfully parsed\n"); */ }
   1559 #endif
   1560                 return EAS_SUCCESS;
   1561 
   1562             case TOKEN_END:
   1563                 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "Unexpected END:IMELODY encountered\n"); */ }
   1564                 return EAS_ERROR_FILE_FORMAT;
   1565 
   1566             default:
   1567                 /* force a read of the next line */
   1568                 pData->index = 1;
   1569                 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "Ignoring unrecognized token in iMelody file: %s\n", pData->buffer); */ }
   1570                 break;
   1571         }
   1572     }
   1573 }
   1574 
   1575 /*----------------------------------------------------------------------------
   1576  * IMY_GetNextChar()
   1577  *----------------------------------------------------------------------------
   1578  * Purpose:
   1579  *
   1580  *
   1581  * Inputs:
   1582  *
   1583  *
   1584  * Outputs:
   1585  *
   1586  *
   1587  * Side Effects:
   1588  *
   1589  *----------------------------------------------------------------------------
   1590 */
   1591 static EAS_I8 IMY_GetNextChar (EAS_HW_DATA_HANDLE hwInstData, S_IMELODY_DATA *pData, EAS_BOOL inHeader)
   1592 {
   1593     EAS_I8 c;
   1594     EAS_U8 index;
   1595 
   1596     for (;;)
   1597     {
   1598         /* get next character */
   1599         c = pData->buffer[pData->index++];
   1600 
   1601         /* buffer empty, read more */
   1602         if (!c)
   1603         {
   1604             /* don't read the next line in the header */
   1605             if (inHeader)
   1606                 return 0;
   1607 
   1608             pData->index = 0;
   1609             pData->buffer[0] = 0;
   1610             if (IMY_ReadLine(hwInstData, pData->fileHandle, pData->buffer, &pData->startLine) != EAS_SUCCESS)
   1611             {
   1612 #ifdef _DEBUG_IMELODY
   1613                 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_GetNextChar: EOF\n"); */ }
   1614 #endif
   1615                 return 0;
   1616             }
   1617 
   1618             /* check for END:IMELODY token */
   1619             if (IMY_ParseLine(pData->buffer, &index) == TOKEN_END)
   1620             {
   1621 #ifdef _DEBUG_IMELODY
   1622                 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_GetNextChar: found END:IMELODY\n"); */ }
   1623 #endif
   1624                 pData->buffer[0] = 0;
   1625                 return 0;
   1626             }
   1627             continue;
   1628         }
   1629 
   1630         /* ignore white space */
   1631         if (!IsSpace(c))
   1632         {
   1633 
   1634 #ifdef _DEBUG_IMELODY
   1635     { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_GetNextChar returned '%c'\n", c); */ }
   1636 #endif
   1637             return c;
   1638         }
   1639     }
   1640 }
   1641 
   1642 /*----------------------------------------------------------------------------
   1643  * IMY_ReadLine()
   1644  *----------------------------------------------------------------------------
   1645  * Purpose:
   1646  * Reads a line of input from the file, discarding the CR/LF
   1647  *
   1648  * Inputs:
   1649  *
   1650  *
   1651  * Outputs:
   1652  *
   1653  *
   1654  * Side Effects:
   1655  *
   1656  *----------------------------------------------------------------------------
   1657 */
   1658 static EAS_RESULT IMY_ReadLine (EAS_HW_DATA_HANDLE hwInstData, EAS_FILE_HANDLE fileHandle, EAS_I8 *buffer, EAS_I32 *pStartLine)
   1659 {
   1660     EAS_RESULT result;
   1661     EAS_INT i;
   1662     EAS_I8 c;
   1663 
   1664     /* fetch current file position and save it */
   1665     if (pStartLine != NULL)
   1666     {
   1667         if ((result = EAS_HWFilePos(hwInstData, fileHandle, pStartLine)) != EAS_SUCCESS)
   1668         {
   1669 #ifdef _DEBUG_IMELODY
   1670             { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_ParseHeader: EAS_HWFilePos returned %d\n", result); */ }
   1671 #endif
   1672             return result;
   1673         }
   1674     }
   1675 
   1676     buffer[0] = 0;
   1677     for (i = 0; i < MAX_LINE_SIZE; )
   1678     {
   1679         if ((result = EAS_HWGetByte(hwInstData, fileHandle, &c)) != EAS_SUCCESS)
   1680         {
   1681             if ((result == EAS_EOF) && (i > 0))
   1682                 break;
   1683             return result;
   1684         }
   1685 
   1686         /* return on LF or end of data */
   1687         if (c == '\n')
   1688             break;
   1689 
   1690         /* store characters in buffer */
   1691         if (c != '\r')
   1692             buffer[i++] = c;
   1693     }
   1694     buffer[i] = 0;
   1695 
   1696 #ifdef _DEBUG_IMELODY
   1697     { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_ReadLine read %s\n", buffer); */ }
   1698 #endif
   1699 
   1700     return EAS_SUCCESS;
   1701 }
   1702 
   1703 /*----------------------------------------------------------------------------
   1704  * IMY_ParseLine()
   1705  *----------------------------------------------------------------------------
   1706  * Purpose:
   1707  *
   1708  *
   1709  * Inputs:
   1710  *
   1711  *
   1712  * Outputs:
   1713  *
   1714  *
   1715  * Side Effects:
   1716  *
   1717  *----------------------------------------------------------------------------
   1718 */
   1719 static EAS_INT IMY_ParseLine (EAS_I8 *buffer, EAS_U8 *pIndex)
   1720 {
   1721     EAS_INT i;
   1722     EAS_INT j;
   1723 
   1724     /* there's no strnicmp() in stdlib, so we have to roll our own */
   1725     for (i = 0; i < TOKEN_INVALID; i++)
   1726     {
   1727         for (j = 0; ; j++)
   1728         {
   1729             /* end of token, must be a match */
   1730             if (tokens[i][j] == 0)
   1731             {
   1732 #ifdef _DEBUG_IMELODY
   1733                 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_ParseLine found token %d\n", i); */ }
   1734 #endif
   1735                 *pIndex = (EAS_U8) j;
   1736                 return i;
   1737             }
   1738             if (tokens[i][j] != ToUpper(buffer[j]))
   1739                 break;
   1740         }
   1741     }
   1742 #ifdef _DEBUG_IMELODY
   1743     { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_ParseLine: no token found\n"); */ }
   1744 #endif
   1745     return TOKEN_INVALID;
   1746 }
   1747 
   1748