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
67static 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 */
82static const char ledStr[] = "edo";
83
84/* vibeon or vibeoff */
85static const char vibeStr[] = "ibeo";
86
87/* backon or backoff */
88static const char backStr[] = "cko";
89
90typedef 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 */
106static const EAS_I8 noteTable[] = { 9, 11, 0, 2, 4, 5, 7 };
107
108/* inline functions */
109#ifdef _DEBUG_IMELODY
110static 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
117EAS_INLINE void PutBackChar (S_IMELODY_DATA *pData) { if (pData->index) pData->index--; }
118#endif
119
120
121/* local prototypes */
122static EAS_RESULT IMY_CheckFileType (S_EAS_DATA *pEASData, EAS_FILE_HANDLE fileHandle, EAS_VOID_PTR *ppHandle, EAS_I32 offset);
123static EAS_RESULT IMY_Prepare (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData);
124static EAS_RESULT IMY_Time (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_U32 *pTime);
125static EAS_RESULT IMY_Event (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_INT parserMode);
126static EAS_RESULT IMY_State (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_STATE *pState);
127static EAS_RESULT IMY_Close (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData);
128static EAS_RESULT IMY_Reset (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData);
129static EAS_RESULT IMY_Pause (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData);
130static EAS_RESULT IMY_Resume (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData);
131static EAS_RESULT IMY_SetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 value);
132static EAS_RESULT IMY_GetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 *pValue);
133static EAS_BOOL IMY_PlayNote (S_EAS_DATA *pEASData, S_IMELODY_DATA *pData, EAS_I8 note, EAS_INT parserMode);
134static EAS_BOOL IMY_PlayRest (S_EAS_DATA *pEASData, S_IMELODY_DATA *pData);
135static EAS_BOOL IMY_GetDuration (EAS_HW_DATA_HANDLE hwInstData, S_IMELODY_DATA *pData, EAS_I32 *pDuration);
136static EAS_BOOL IMY_GetLEDState (S_EAS_DATA *pEASData, S_IMELODY_DATA *pData);
137static EAS_BOOL IMY_GetVibeState (S_EAS_DATA *pEASData, S_IMELODY_DATA *pData);
138static EAS_BOOL IMY_GetBackState (S_EAS_DATA *pEASData, S_IMELODY_DATA *pData);
139static EAS_BOOL IMY_GetVolume (EAS_HW_DATA_HANDLE hwInstData, S_IMELODY_DATA *pData, EAS_BOOL inHeader);
140static EAS_BOOL IMY_GetNumber (EAS_HW_DATA_HANDLE hwInstData, S_IMELODY_DATA *pData, EAS_INT *temp, EAS_BOOL inHeader);
141static EAS_RESULT IMY_ParseHeader (S_EAS_DATA *pEASData, S_IMELODY_DATA* pData);
142static EAS_I8 IMY_GetNextChar (EAS_HW_DATA_HANDLE hwInstData, S_IMELODY_DATA *pData, EAS_BOOL inHeader);
143static EAS_RESULT IMY_ReadLine (EAS_HW_DATA_HANDLE hwInstData, EAS_FILE_HANDLE fileHandle, EAS_I8 *buffer, EAS_I32 *pStartLine);
144static 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*/
154const 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*/
188static 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*/
247static 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 */
299static 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*/
328static 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 */
582static 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*/
634static 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*/
677static 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*/
726static 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 */
763static 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 */
800static 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 */
838static 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*/
882static 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*/
954static 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
992static 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*/
1050static 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*/
1115static 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*/
1180static 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*/
1245static 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*/
1303static 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*/
1352static 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*/
1401static 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*/
1438static 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*/
1591static 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*/
1658static 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*/
1719static 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