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