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