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