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    /* 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 */
573static 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*/
625static 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*/
668static 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*/
717static 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 */
754static 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 */
791static 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 */
829static 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*/
873static 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*/
945static 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
983static 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*/
1041static 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*/
1106static 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*/
1171static 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*/
1236static 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*/
1294static 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*/
1343static 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*/
1392static 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*/
1429static 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*/
1582static 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*/
1649static 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*/
1710static 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