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