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