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