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