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