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