eas_rtttl.c revision 7df30109963092559d3760c0661a020f9daf1030
1/*----------------------------------------------------------------------------
2 *
3 * File:
4 * eas_rtttl.c
5 *
6 * Contents and purpose:
7 * RTTTL 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: 795 $
26 *   $Date: 2007-08-01 00:14:45 -0700 (Wed, 01 Aug 2007) $
27 *----------------------------------------------------------------------------
28*/
29
30#include "eas_data.h"
31#include "eas_miditypes.h"
32#include "eas_parser.h"
33#include "eas_report.h"
34#include "eas_host.h"
35#include "eas_midi.h"
36#include "eas_config.h"
37#include "eas_vm_protos.h"
38#include "eas_rtttldata.h"
39#include "eas_ctype.h"
40
41/* increase gain for mono ringtones */
42#define RTTTL_GAIN_OFFSET		8
43
44/* maximum title length including colon separator */
45#define RTTTL_MAX_TITLE_LEN		32
46#define RTTTL_INFINITE_LOOP		15
47
48/* length of 32nd note in 1/256ths of a msec for 63 BPM tempo */
49#define DEFAULT_TICK_CONV		30476
50#define TICK_CONVERT			1920000
51
52/* default channel and program for RTTTL playback */
53#define RTTTL_CHANNEL			0
54#define RTTTL_PROGRAM			80
55#define RTTTL_VELOCITY			127
56
57/* note used for rest */
58#define RTTTL_REST				1
59
60/* multiplier for fixed point triplet conversion */
61#define TRIPLET_MULTIPLIER		683
62#define TRIPLET_SHIFT			10
63
64/* local prototypes */
65static EAS_RESULT RTTTL_CheckFileType (S_EAS_DATA *pEASData, EAS_FILE_HANDLE fileHandle, EAS_VOID_PTR *ppHandle, EAS_I32 offset);
66static EAS_RESULT RTTTL_Prepare (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData);
67static EAS_RESULT RTTTL_Time (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_U32 *pTime);
68static EAS_RESULT RTTTL_Event (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_INT parserMode);
69static EAS_RESULT RTTTL_State (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_STATE *pState);
70static EAS_RESULT RTTTL_Close (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData);
71static EAS_RESULT RTTTL_Reset (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData);
72static EAS_RESULT RTTTL_Pause (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData);
73static EAS_RESULT RTTTL_Resume (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData);
74static EAS_RESULT RTTTL_SetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 value);
75static EAS_RESULT RTTTL_GetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 *pValue);
76static EAS_RESULT RTTTL_GetStyle (EAS_HW_DATA_HANDLE hwInstData, S_RTTTL_DATA *pData);
77static EAS_RESULT RTTTL_GetDuration (EAS_HW_DATA_HANDLE hwInstData, S_RTTTL_DATA *pData, EAS_I8 *pDuration);
78static EAS_RESULT RTTTL_GetOctave (EAS_HW_DATA_HANDLE hwInstData, S_RTTTL_DATA *pData, EAS_U8 *pOctave);
79static EAS_RESULT RTTTL_GetTempo (EAS_HW_DATA_HANDLE hwInstData, S_RTTTL_DATA *pData);
80static EAS_RESULT RTTTL_GetNumber (EAS_HW_DATA_HANDLE hwInstData, S_RTTTL_DATA *pData, EAS_I32 *pValue);
81static EAS_RESULT RTTTL_ParseHeader (S_EAS_DATA *pEASData, S_RTTTL_DATA* pData, EAS_BOOL metaData);
82static EAS_RESULT RTTTL_GetNextChar (EAS_HW_DATA_HANDLE hwInstData, S_RTTTL_DATA *pData, EAS_I8 *pValue);
83static EAS_RESULT RTTTL_PeekNextChar (EAS_HW_DATA_HANDLE hwInstData, S_RTTTL_DATA *pData, EAS_I8 *pValue);
84
85/* inline functions */
86EAS_INLINE void RTTTL_PutBackChar (S_RTTTL_DATA *pData, EAS_I8 value) { pData->dataByte = value; }
87
88
89/* lookup table for note values */
90static const EAS_U8 noteTable[] = { 21, 23, 12, 14, 16, 17, 19, 23 };
91
92/*----------------------------------------------------------------------------
93 *
94 * EAS_RTTTL_Parser
95 *
96 * This structure contains the functional interface for the iMelody parser
97 *----------------------------------------------------------------------------
98*/
99const S_FILE_PARSER_INTERFACE EAS_RTTTL_Parser =
100{
101	RTTTL_CheckFileType,
102	RTTTL_Prepare,
103	RTTTL_Time,
104	RTTTL_Event,
105	RTTTL_State,
106	RTTTL_Close,
107	RTTTL_Reset,
108	RTTTL_Pause,
109	RTTTL_Resume,
110	NULL,
111	RTTTL_SetData,
112	RTTTL_GetData,
113	NULL
114};
115
116/*----------------------------------------------------------------------------
117 * RTTTL_CheckFileType()
118 *----------------------------------------------------------------------------
119 * Purpose:
120 * Check the file type to see if we can parse it
121 *
122 * Inputs:
123 * pEASData			- pointer to overall EAS data structure
124 * handle			- pointer to file handle
125 *
126 * Outputs:
127 *
128 *
129 * Side Effects:
130 *
131 *----------------------------------------------------------------------------
132*/
133static EAS_RESULT RTTTL_CheckFileType (S_EAS_DATA *pEASData, EAS_FILE_HANDLE fileHandle, EAS_VOID_PTR *ppHandle, EAS_I32 offset)
134{
135	S_RTTTL_DATA data;
136	S_RTTTL_DATA *pData;
137
138	/* see if we can parse the header */
139	data.fileHandle = fileHandle;
140	data.fileOffset = offset;
141	*ppHandle= NULL;
142	if (RTTTL_ParseHeader (pEASData, &data, EAS_FALSE) == EAS_SUCCESS)
143	{
144
145		/* check for static memory allocation */
146		if (pEASData->staticMemoryModel)
147			pData = EAS_CMEnumData(EAS_CM_RTTTL_DATA);
148		else
149			pData = EAS_HWMalloc(pEASData->hwInstData, sizeof(S_RTTTL_DATA));
150		if (!pData)
151			return EAS_ERROR_MALLOC_FAILED;
152		EAS_HWMemSet(pData, 0, sizeof(S_RTTTL_DATA));
153
154		/* return a pointer to the instance data */
155		pData->fileHandle = fileHandle;
156		pData->fileOffset = offset;
157		pData->state = EAS_STATE_OPEN;
158		*ppHandle = pData;
159	}
160
161	return EAS_SUCCESS;
162}
163
164/*----------------------------------------------------------------------------
165 * RTTTL_Prepare()
166 *----------------------------------------------------------------------------
167 * Purpose:
168 * Prepare to parse the file. Allocates instance data (or uses static allocation for
169 * static memory model).
170 *
171 * Inputs:
172 * pEASData			- pointer to overall EAS data structure
173 * handle			- pointer to file handle
174 *
175 * Outputs:
176 *
177 *
178 * Side Effects:
179 *
180 *----------------------------------------------------------------------------
181*/
182static EAS_RESULT RTTTL_Prepare (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
183{
184	S_RTTTL_DATA* pData;
185	EAS_RESULT result;
186
187	/* check for valid state */
188	pData = (S_RTTTL_DATA*) pInstData;
189	if (pData->state != EAS_STATE_OPEN)
190		return EAS_ERROR_NOT_VALID_IN_THIS_STATE;
191
192	/* instantiate a synthesizer */
193	if ((result = VMInitMIDI(pEASData, &pData->pSynth)) != EAS_SUCCESS)
194	{
195		{ /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "VMInitMIDI returned %d\n", result); */ }
196		return result;
197	}
198
199	pData->state = EAS_STATE_ERROR;
200	if ((result = RTTTL_ParseHeader (pEASData,  pData, (EAS_BOOL) (pData->metadata.callback != NULL))) != EAS_SUCCESS)
201	{
202		/* if using dynamic memory, free it */
203		if (!pEASData->staticMemoryModel)
204			EAS_HWFree(pEASData->hwInstData, pData);
205		return result;
206	}
207
208	pData->state = EAS_STATE_READY;
209	return EAS_SUCCESS;
210}
211
212/*----------------------------------------------------------------------------
213 * RTTTL_Time()
214 *----------------------------------------------------------------------------
215 * Purpose:
216 * Returns the time of the next event in msecs
217 *
218 * Inputs:
219 * pEASData			- pointer to overall EAS data structure
220 * handle			- pointer to file handle
221 * pTime			- pointer to variable to hold time of next event (in msecs)
222 *
223 * Outputs:
224 *
225 *
226 * Side Effects:
227 *
228 *----------------------------------------------------------------------------
229*/
230/*lint -esym(715, pEASData) reserved for future use */
231static EAS_RESULT RTTTL_Time (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_U32 *pTime)
232{
233	S_RTTTL_DATA *pData;
234
235	pData = (S_RTTTL_DATA*) pInstData;
236
237	/* return time in milliseconds */
238	/*lint -e{704} use shift instead of division */
239	*pTime = pData->time >> 8;
240	return EAS_SUCCESS;
241}
242
243/*----------------------------------------------------------------------------
244 * RTTTL_Event()
245 *----------------------------------------------------------------------------
246 * Purpose:
247 * Parse the next event in the file
248 *
249 * Inputs:
250 * pEASData			- pointer to overall EAS data structure
251 * handle			- pointer to file handle
252 *
253 * Outputs:
254 *
255 *
256 * Side Effects:
257 *
258 *----------------------------------------------------------------------------
259*/
260static EAS_RESULT RTTTL_Event (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_INT parserMode)
261{
262	S_RTTTL_DATA* pData;
263	EAS_RESULT result;
264	EAS_I32 ticks;
265	EAS_I32 temp;
266	EAS_I8 c;
267	EAS_U8 note;
268	EAS_U8 octave;
269
270	pData = (S_RTTTL_DATA*) pInstData;
271	if (pData->state >= EAS_STATE_OPEN)
272		return EAS_SUCCESS;
273
274	/* initialize MIDI channel when the track starts playing */
275	if (pData->time == 0)
276	{
277		/* set program to square lead */
278		VMProgramChange(pEASData->pVoiceMgr, pData->pSynth, RTTTL_CHANNEL, RTTTL_PROGRAM);
279
280		/* set channel volume to max */
281		VMControlChange(pEASData->pVoiceMgr, pData->pSynth, RTTTL_CHANNEL, 7, 127);
282	}
283
284	/* check for end of note */
285	if (pData->note)
286	{
287		/* stop the note */
288		VMStopNote(pEASData->pVoiceMgr, pData->pSynth, RTTTL_CHANNEL, pData->note, 0);
289		pData->note = 0;
290
291		/* check for rest between notes */
292		if (pData->restTicks)
293		{
294			pData->time += pData->restTicks;
295			pData->restTicks = 0;
296			return EAS_SUCCESS;
297		}
298	}
299
300	/* parse the next event */
301	octave = pData->octave;
302	note = 0;
303	ticks = pData->duration * pData->tick;
304	for (;;)
305	{
306
307		/* get next character */
308		if ((result = RTTTL_GetNextChar(pEASData->hwInstData, pData, &c)) != EAS_SUCCESS)
309		{
310			if (result != EAS_EOF)
311				return result;
312
313			/* end of file, if no notes to process, check for looping */
314			if (!note)
315			{
316				/* if no loop set state to stopping */
317				if (pData->repeatCount == 0)
318				{
319					pData->state = EAS_STATE_STOPPING;
320					VMReleaseAllVoices(pEASData->pVoiceMgr, pData->pSynth);
321					return EAS_SUCCESS;
322				}
323
324				/* decrement loop count */
325				if (pData->repeatCount != RTTTL_INFINITE_LOOP)
326					pData->repeatCount--;
327
328				/* if locating, ignore infinite loops */
329				else if (parserMode != eParserModePlay)
330				{
331					pData->state = EAS_STATE_STOPPING;
332					VMReleaseAllVoices(pEASData->pVoiceMgr, pData->pSynth);
333					return EAS_SUCCESS;
334				}
335
336				/* loop back to start of notes */
337				if ((result = EAS_HWFileSeek(pEASData->hwInstData, pData->fileHandle, pData->repeatOffset)) != EAS_SUCCESS)
338					return result;
339				continue;
340			}
341
342			/* still have a note to process */
343			else
344				c = ',';
345		}
346
347		/* bpm */
348		if (c == 'b')
349		{
350			/* peek at next character */
351			if ((result = RTTTL_PeekNextChar(pEASData->hwInstData, pData, &c)) != EAS_SUCCESS)
352				return result;
353
354			/* if a number, must be octave or tempo */
355			if (IsDigit(c))
356			{
357				if ((result = RTTTL_GetNumber(pEASData->hwInstData, pData, &temp)) != EAS_SUCCESS)
358					return result;
359
360				/* check for octave first */
361				if ((temp >= 4) && (temp <= 7))
362				{
363					octave = (EAS_U8) temp;
364				}
365
366				/* check for tempo */
367				else if ((temp >= 25) && (temp <= 900))
368				{
369					pData->tick = TICK_CONVERT / (EAS_U32) temp;
370				}
371
372				/* don't know what it was */
373				else
374					return EAS_ERROR_FILE_FORMAT;
375			}
376
377			/* must be a note */
378			else
379			{
380				note = noteTable[1];
381			}
382		}
383
384		/* octave */
385		else if (c == 'o')
386		{
387			if ((result = RTTTL_GetOctave(pEASData->hwInstData, pData, &pData->octave)) != EAS_SUCCESS)
388				return result;
389		}
390
391		/* style */
392		else if (c == 's')
393		{
394			if ((result = RTTTL_GetStyle(pEASData->hwInstData, pData)) != EAS_SUCCESS)
395				return result;
396		}
397
398		/* duration or octave */
399		else if (IsDigit(c))
400		{
401			RTTTL_PutBackChar(pData, c);
402
403			/* duration comes before note */
404			if (!note)
405			{
406				if ((result = RTTTL_GetDuration(pEASData->hwInstData, pData, &c)) != EAS_SUCCESS)
407					return result;
408				ticks = c * pData->tick;
409			}
410
411			/* octave comes after note */
412			else
413			{
414				if ((result = RTTTL_GetOctave(pEASData->hwInstData, pData, &octave)) != EAS_SUCCESS)
415					return result;
416			}
417		}
418
419		/* note or rest */
420		else if ((c >= 'a') && (c <= 'h'))
421		{
422			note = noteTable[c - 'a'];
423		}
424
425		else if (c == 'p')
426		{
427			note = RTTTL_REST;
428		}
429
430		/* dotted note */
431		else if (c == '.')
432		{
433			/*lint -e{704} shift for performance */
434			ticks += ticks >> 1;
435		}
436
437		/* accidental */
438		else if (c == '#')
439		{
440			if (note)
441				note++;
442		}
443
444		/* end of event */
445		else if ((c == ',') && note)
446		{
447
448			/* handle note events */
449			if (note != RTTTL_REST)
450			{
451
452				/* save note and start it */
453				pData->note = note + octave;
454				if (parserMode == eParserModePlay)
455					VMStartNote(pEASData->pVoiceMgr, pData->pSynth, RTTTL_CHANNEL, pData->note, RTTTL_VELOCITY);
456
457				/* determine note length */
458				switch (pData->style)
459				{
460					/* natural */
461					case 'n':
462						/*lint -e{704} shift for performance */
463						pData->restTicks = ticks >> 4;
464						break;
465					/* continuous */
466
467					case 'c':
468						pData->restTicks = 0;
469						break;
470
471					/* staccato */
472					case 's':
473						/*lint -e{704} shift for performance */
474						pData->restTicks = ticks >> 1;
475						break;
476
477					default:
478						{ /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "RTTTL_Event: Unexpected style type %c\n", pData->style); */ }
479						break;
480				}
481
482				/* next event is at end of this note */
483				pData->time += ticks - pData->restTicks;
484			}
485
486			/* rest */
487			else
488				pData->time += ticks;
489
490			/* event found, return to caller */
491			break;
492		}
493	}
494
495	pData->state = EAS_STATE_PLAY;
496	return EAS_SUCCESS;
497}
498
499/*----------------------------------------------------------------------------
500 * RTTTL_State()
501 *----------------------------------------------------------------------------
502 * Purpose:
503 * Returns the current state of the stream
504 *
505 * Inputs:
506 * pEASData			- pointer to overall EAS data structure
507 * handle			- pointer to file handle
508 * pState			- pointer to variable to store state
509 *
510 * Outputs:
511 *
512 *
513 * Side Effects:
514 *
515 *----------------------------------------------------------------------------
516*/
517/*lint -esym(715, pEASData) reserved for future use */
518static EAS_RESULT RTTTL_State (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 *pState)
519{
520	S_RTTTL_DATA* pData;
521
522	/* establish pointer to instance data */
523	pData = (S_RTTTL_DATA*) pInstData;
524
525	/* if stopping, check to see if synth voices are active */
526	if (pData->state == EAS_STATE_STOPPING)
527	{
528		if (VMActiveVoices(pData->pSynth) == 0)
529			pData->state = EAS_STATE_STOPPED;
530	}
531
532	if (pData->state == EAS_STATE_PAUSING)
533	{
534		if (VMActiveVoices(pData->pSynth) == 0)
535			pData->state = EAS_STATE_PAUSED;
536	}
537
538	/* return current state */
539	*pState = pData->state;
540	return EAS_SUCCESS;
541}
542
543/*----------------------------------------------------------------------------
544 * RTTTL_Close()
545 *----------------------------------------------------------------------------
546 * Purpose:
547 * Close the file and clean up
548 *
549 * Inputs:
550 * pEASData			- pointer to overall EAS data structure
551 * handle			- pointer to file handle
552 *
553 * Outputs:
554 *
555 *
556 * Side Effects:
557 *
558 *----------------------------------------------------------------------------
559*/
560static EAS_RESULT RTTTL_Close (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
561{
562	S_RTTTL_DATA* pData;
563	EAS_RESULT result;
564
565	pData = (S_RTTTL_DATA*) pInstData;
566
567	/* close the file */
568	if ((result = EAS_HWCloseFile(pEASData->hwInstData, pData->fileHandle)) != EAS_SUCCESS)
569			return result;
570
571	/* free the synth */
572	if (pData->pSynth != NULL)
573		VMMIDIShutdown(pEASData, pData->pSynth);
574
575	/* if using dynamic memory, free it */
576	if (!pEASData->staticMemoryModel)
577		EAS_HWFree(pEASData->hwInstData, pData);
578
579	return EAS_SUCCESS;
580}
581
582/*----------------------------------------------------------------------------
583 * RTTTL_Reset()
584 *----------------------------------------------------------------------------
585 * Purpose:
586 * Reset the sequencer. Used for locating backwards in the file.
587 *
588 * Inputs:
589 * pEASData			- pointer to overall EAS data structure
590 * handle			- pointer to file handle
591 *
592 * Outputs:
593 *
594 *
595 * Side Effects:
596 *
597 *----------------------------------------------------------------------------
598*/
599static EAS_RESULT RTTTL_Reset (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
600{
601	S_RTTTL_DATA* pData;
602	EAS_RESULT result;
603
604	pData = (S_RTTTL_DATA*) pInstData;
605
606	/* reset the synth */
607	VMReset(pEASData->pVoiceMgr, pData->pSynth, EAS_TRUE);
608
609	/* reset time to zero */
610	pData->time = 0;
611	pData->note = 0;
612
613	/* reset file position and re-parse header */
614	pData->state = EAS_STATE_ERROR;
615	if ((result = EAS_HWFileSeek(pEASData->hwInstData, pData->fileHandle, pData->fileOffset)) != EAS_SUCCESS)
616		return result;
617	if ((result = RTTTL_ParseHeader (pEASData,  pData, EAS_TRUE)) != EAS_SUCCESS)
618		return result;
619
620	pData->state = EAS_STATE_READY;
621	return EAS_SUCCESS;
622}
623
624/*----------------------------------------------------------------------------
625 * RTTTL_Pause()
626 *----------------------------------------------------------------------------
627 * Purpose:
628 * Pauses the sequencer. Mutes all voices and sets state to pause.
629 *
630 * Inputs:
631 * pEASData			- pointer to overall EAS data structure
632 * handle			- pointer to file handle
633 *
634 * Outputs:
635 *
636 *
637 * Side Effects:
638 *
639 *----------------------------------------------------------------------------
640*/
641static EAS_RESULT RTTTL_Pause (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
642{
643	S_RTTTL_DATA *pData;
644
645	/* can't pause a stopped stream */
646	pData = (S_RTTTL_DATA*) pInstData;
647	if (pData->state == EAS_STATE_STOPPED)
648		return EAS_ERROR_ALREADY_STOPPED;
649
650	/* mute the synthesizer */
651	VMMuteAllVoices(pEASData->pVoiceMgr, pData->pSynth);
652	pData->state = EAS_STATE_PAUSING;
653	return EAS_SUCCESS;
654}
655
656/*----------------------------------------------------------------------------
657 * RTTTL_Resume()
658 *----------------------------------------------------------------------------
659 * Purpose:
660 * Resume playing after a pause, sets state back to playing.
661 *
662 * Inputs:
663 * pEASData			- pointer to overall EAS data structure
664 * handle			- pointer to file handle
665 *
666 * Outputs:
667 *
668 *
669 * Side Effects:
670 *
671 *----------------------------------------------------------------------------
672*/
673/*lint -esym(715, pEASData) reserved for future use */
674static EAS_RESULT RTTTL_Resume (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
675{
676	S_RTTTL_DATA *pData;
677
678	/* can't resume a stopped stream */
679	pData = (S_RTTTL_DATA*) pInstData;
680	if (pData->state == EAS_STATE_STOPPED)
681		return EAS_ERROR_ALREADY_STOPPED;
682
683	/* nothing to do but resume playback */
684	pData->state = EAS_STATE_PLAY;
685	return EAS_SUCCESS;
686}
687
688/*----------------------------------------------------------------------------
689 * RTTTL_SetData()
690 *----------------------------------------------------------------------------
691 * Purpose:
692 * Return file type
693 *
694 * Inputs:
695 * pEASData			- pointer to overall EAS data structure
696 * handle			- pointer to file handle
697 *
698 * Outputs:
699 *
700 *
701 * Side Effects:
702 *
703 *----------------------------------------------------------------------------
704*/
705/*lint -esym(715, pEASData) reserved for future use */
706static EAS_RESULT RTTTL_SetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 value)
707{
708	S_RTTTL_DATA *pData;
709
710	pData = (S_RTTTL_DATA *) pInstData;
711	switch (param)
712	{
713
714		/* set metadata callback */
715		case PARSER_DATA_METADATA_CB:
716			EAS_HWMemCpy(&pData->metadata, (void*) value, sizeof(S_METADATA_CB));
717			break;
718
719		default:
720			return EAS_ERROR_INVALID_PARAMETER;
721	}
722
723	return EAS_SUCCESS;
724}
725
726/*----------------------------------------------------------------------------
727 * RTTTL_GetData()
728 *----------------------------------------------------------------------------
729 * Purpose:
730 * Return file type
731 *
732 * Inputs:
733 * pEASData			- pointer to overall EAS data structure
734 * handle			- pointer to file handle
735 *
736 * Outputs:
737 *
738 *
739 * Side Effects:
740 *
741 *----------------------------------------------------------------------------
742*/
743/*lint -esym(715, pEASData) reserved for future use */
744static EAS_RESULT RTTTL_GetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 *pValue)
745{
746	S_RTTTL_DATA *pData;
747
748	pData = (S_RTTTL_DATA *) pInstData;
749	switch (param)
750	{
751		/* return file type as RTTTL */
752		case PARSER_DATA_FILE_TYPE:
753			*pValue = EAS_FILE_RTTTL;
754			break;
755
756#if 0
757		/* set transposition */
758		case PARSER_DATA_TRANSPOSITION:
759			*pValue = pData->transposition;
760			break;
761#endif
762
763		case PARSER_DATA_SYNTH_HANDLE:
764			*pValue = (EAS_I32) pData->pSynth;
765			break;
766
767		case PARSER_DATA_GAIN_OFFSET:
768			*pValue = RTTTL_GAIN_OFFSET;
769			break;
770
771	default:
772			return EAS_ERROR_INVALID_PARAMETER;
773	}
774	return EAS_SUCCESS;
775}
776
777/*----------------------------------------------------------------------------
778 * RTTTL_GetStyle()
779 *----------------------------------------------------------------------------
780 * Purpose:
781 *
782 *
783 * Inputs:
784 *
785 *
786 * Outputs:
787 *
788 *
789 * Side Effects:
790 *
791 *----------------------------------------------------------------------------
792*/
793static EAS_RESULT RTTTL_GetStyle (EAS_HW_DATA_HANDLE hwInstData, S_RTTTL_DATA *pData)
794{
795	EAS_RESULT result;
796	EAS_I8 style;
797
798	/* get style */
799	if ((result = RTTTL_GetNextChar(hwInstData, pData, &style)) != EAS_SUCCESS)
800		return result;
801
802	if ((style != 's')  && (style != 'n') && (style != 'c'))
803		return EAS_ERROR_FILE_FORMAT;
804
805	pData->style = style;
806	return EAS_SUCCESS;
807}
808
809/*----------------------------------------------------------------------------
810 * RTTTL_GetDuration()
811 *----------------------------------------------------------------------------
812 * Purpose:
813 *
814 *
815 * Inputs:
816 *
817 *
818 * Outputs:
819 *
820 *
821 * Side Effects:
822 *
823 *----------------------------------------------------------------------------
824*/
825static EAS_RESULT RTTTL_GetDuration (EAS_HW_DATA_HANDLE hwInstData, S_RTTTL_DATA *pData, EAS_I8 *pDuration)
826{
827	EAS_RESULT result;
828	EAS_I32 duration;
829	EAS_I8 temp;
830
831	/* get the duration */
832	if ((result = RTTTL_GetNumber(hwInstData, pData, &duration)) != EAS_SUCCESS)
833		return result;
834
835	if ((duration != 1) && (duration != 2) && (duration != 4) && (duration != 8) && (duration != 16) && (duration != 32))
836		return EAS_ERROR_FILE_FORMAT;
837
838	temp = 64;
839	while (duration)
840	{
841		/*lint -e{704} shift for performance */
842		duration = duration >> 1;
843		/*lint -e{702} use shift for performance */
844		temp = temp >> 1;
845	}
846
847	*pDuration = temp;
848	return EAS_SUCCESS;
849}
850
851/*----------------------------------------------------------------------------
852 * RTTTL_GetOctave()
853 *----------------------------------------------------------------------------
854 * Purpose:
855 *
856 *
857 * Inputs:
858 *
859 *
860 * Outputs:
861 *
862 *
863 * Side Effects:
864 *
865 *----------------------------------------------------------------------------
866*/
867static EAS_RESULT RTTTL_GetOctave (EAS_HW_DATA_HANDLE hwInstData, S_RTTTL_DATA *pData, EAS_U8 *pOctave)
868{
869	EAS_RESULT result;
870	EAS_I32 octave;
871
872	/* get the tempo */
873	if ((result = RTTTL_GetNumber(hwInstData, pData, &octave)) != EAS_SUCCESS)
874		return result;
875
876	if ((octave < 4) || (octave > 7))
877		return EAS_ERROR_FILE_FORMAT;
878
879	*pOctave = (EAS_U8) (octave * 12);
880	return EAS_SUCCESS;
881}
882
883/*----------------------------------------------------------------------------
884 * RTTTL_GetTempo()
885 *----------------------------------------------------------------------------
886 * Purpose:
887 *
888 *
889 * Inputs:
890 *
891 *
892 * Outputs:
893 *
894 *
895 * Side Effects:
896 *
897 *----------------------------------------------------------------------------
898*/
899static EAS_RESULT RTTTL_GetTempo (EAS_HW_DATA_HANDLE hwInstData, S_RTTTL_DATA *pData)
900{
901	EAS_RESULT result;
902	EAS_I32 tempo;
903
904	/* get the tempo */
905	if ((result = RTTTL_GetNumber(hwInstData, pData, &tempo)) != EAS_SUCCESS)
906		return result;
907
908	if ((tempo < 25) || (tempo > 900))
909		return EAS_ERROR_FILE_FORMAT;
910
911	pData->tick = TICK_CONVERT / (EAS_U32) tempo;
912	return EAS_SUCCESS;
913}
914
915/*----------------------------------------------------------------------------
916 * RTTTL_GetNumber()
917 *----------------------------------------------------------------------------
918 * Purpose:
919 *
920 *
921 * Inputs:
922 *
923 *
924 * Outputs:
925 *
926 *
927 * Side Effects:
928 *
929 *----------------------------------------------------------------------------
930*/
931static EAS_RESULT RTTTL_GetNumber (EAS_HW_DATA_HANDLE hwInstData, S_RTTTL_DATA *pData, EAS_I32 *pValue)
932{
933	EAS_RESULT result;
934	EAS_INT temp;
935	EAS_I8 c;
936
937	*pValue = -1;
938	temp = 0;
939	for (;;)
940	{
941		if ((result = RTTTL_PeekNextChar(hwInstData, pData, &c)) != EAS_SUCCESS)
942		{
943			if ((result == EAS_EOF) && (*pValue != -1))
944				return EAS_SUCCESS;
945			return result;
946		}
947
948		if (IsDigit(c))
949		{
950			pData->dataByte = 0;
951			temp = temp * 10 + c - '0';
952			*pValue = temp;
953		}
954		else
955			return EAS_SUCCESS;
956	}
957}
958
959/*----------------------------------------------------------------------------
960 * RTTTL_ParseHeader()
961 *----------------------------------------------------------------------------
962 * Purpose:
963 * Prepare to parse the file. Allocates instance data (or uses static allocation for
964 * static memory model).
965 *
966 * Inputs:
967 * pEASData			- pointer to overall EAS data structure
968 * handle			- pointer to file handle
969 *
970 * Outputs:
971 *
972 *
973 * Side Effects:
974 *
975 *----------------------------------------------------------------------------
976*/
977static EAS_RESULT RTTTL_ParseHeader (S_EAS_DATA *pEASData, S_RTTTL_DATA* pData, EAS_BOOL metaData)
978{
979	EAS_RESULT result;
980	EAS_I32 i;
981	EAS_I8 temp;
982	EAS_I8 control;
983
984	/* initialize some defaults */
985	pData->time = 0;
986	pData->tick = DEFAULT_TICK_CONV;
987	pData->note = 0;
988	pData->duration = 4;
989	pData ->restTicks = 0;
990	pData->octave = 60;
991	pData->repeatOffset = -1;
992	pData->repeatCount = 0;
993	pData->style = 'n';
994	pData->dataByte = 0;
995
996	metaData = metaData && (pData->metadata.buffer != NULL) && (pData->metadata.callback != NULL);
997
998	/* seek to start of data */
999	if ((result = EAS_HWFileSeek(pEASData->hwInstData, pData->fileHandle, pData->fileOffset)) != EAS_SUCCESS)
1000		return result;
1001
1002	/* zero the metadata buffer */
1003	if (metaData)
1004		EAS_HWMemSet(pData->metadata.buffer, 0, pData->metadata.bufferSize);
1005
1006	/* read the title */
1007	for (i = 0; i < RTTTL_MAX_TITLE_LEN; i++)
1008	{
1009		if ((result = EAS_HWGetByte(pEASData->hwInstData, pData->fileHandle, &temp)) != EAS_SUCCESS)
1010			return result;
1011
1012		if (temp == ':')
1013			break;
1014
1015		/* pass along metadata */
1016		if (metaData)
1017		{
1018			if (i < (pData->metadata.bufferSize- 1))
1019				pData->metadata.buffer[i] = (char) temp;
1020		}
1021	}
1022
1023	/* check for error in title */
1024	if (i == RTTTL_MAX_TITLE_LEN)
1025		return EAS_ERROR_FILE_FORMAT;
1026
1027	/* pass along metadata */
1028	if (metaData)
1029		(*pData->metadata.callback)(EAS_METADATA_TITLE, pData->metadata.buffer, pData->metadata.pUserData);
1030
1031	/* control fields */
1032	for (;;)
1033	{
1034
1035		/* get control type */
1036		if ((result = RTTTL_GetNextChar(pEASData->hwInstData, pData, &control)) != EAS_SUCCESS)
1037			return result;
1038
1039		/* next char should be equal sign */
1040		if ((result = RTTTL_GetNextChar(pEASData->hwInstData, pData, &temp)) != EAS_SUCCESS)
1041			return result;
1042		if (temp != '=')
1043			return EAS_ERROR_FILE_FORMAT;
1044
1045		/* get the control value */
1046		switch (control)
1047		{
1048
1049			/* bpm */
1050			case 'b':
1051				if ((result = RTTTL_GetTempo(pEASData->hwInstData, pData)) != EAS_SUCCESS)
1052					return result;
1053				break;
1054
1055			/* duration */
1056			case 'd':
1057				if ((result = RTTTL_GetDuration(pEASData->hwInstData, pData, &temp)) != EAS_SUCCESS)
1058					return result;
1059				pData->duration = temp;
1060				break;
1061
1062			/* loop */
1063			case 'l':
1064				if ((result = RTTTL_GetNumber(pEASData->hwInstData, pData, &i)) != EAS_SUCCESS)
1065					return result;
1066				if ((i < 0) || (i > 15))
1067					return EAS_ERROR_FILE_FORMAT;
1068				pData->repeatCount = (EAS_U8) i;
1069				break;
1070
1071			/* octave */
1072			case 'o':
1073				if ((result = RTTTL_GetOctave(pEASData->hwInstData, pData, &pData->octave)) != EAS_SUCCESS)
1074					return result;
1075				break;
1076
1077			/* get style */
1078			case 's':
1079				if ((result = RTTTL_GetStyle(pEASData->hwInstData, pData)) != EAS_SUCCESS)
1080					return result;
1081				break;
1082
1083			/* unrecognized control */
1084			default:
1085				return EAS_ERROR_FILE_FORMAT;
1086		}
1087
1088		/* next character should be comma or colon */
1089		if ((result = RTTTL_GetNextChar(pEASData->hwInstData, pData, &temp)) != EAS_SUCCESS)
1090			return result;
1091
1092		/* check for end of control field */
1093		if (temp == ':')
1094			break;
1095
1096		/* must be a comma */
1097		if (temp != ',')
1098			return EAS_ERROR_FILE_FORMAT;
1099	}
1100
1101	/* should be at the start of the music block */
1102	if ((result = EAS_HWFilePos(pEASData->hwInstData, pData->fileHandle, &pData->repeatOffset)) != EAS_SUCCESS)
1103		return result;
1104
1105	return EAS_SUCCESS;
1106}
1107
1108/*----------------------------------------------------------------------------
1109 * RTTTL_GetNextChar()
1110 *----------------------------------------------------------------------------
1111 * Purpose:
1112 *
1113 *
1114 * Inputs:
1115 *
1116 *
1117 * Outputs:
1118 *
1119 *
1120 * Side Effects:
1121 *
1122 *----------------------------------------------------------------------------
1123*/
1124static EAS_RESULT RTTTL_GetNextChar (EAS_HW_DATA_HANDLE hwInstData, S_RTTTL_DATA *pData, EAS_I8 *pValue)
1125{
1126	EAS_RESULT result;
1127	EAS_I8 temp;
1128
1129	*pValue = 0;
1130	for(;;)
1131	{
1132
1133		/* check for character that has been put back */
1134		if (pData->dataByte)
1135		{
1136			temp = pData->dataByte;
1137			pData->dataByte = 0;
1138		}
1139		else
1140		{
1141			if ((result = EAS_HWGetByte(hwInstData, pData->fileHandle, &temp)) != EAS_SUCCESS)
1142				return result;
1143		}
1144
1145		/* ignore white space */
1146		if (!IsSpace(temp))
1147		{
1148			*pValue = ToLower(temp);
1149			return EAS_SUCCESS;
1150		}
1151	}
1152}
1153
1154/*----------------------------------------------------------------------------
1155 * RTTTL_PeekNextChar()
1156 *----------------------------------------------------------------------------
1157 * Purpose:
1158 *
1159 *
1160 * Inputs:
1161 *
1162 *
1163 * Outputs:
1164 *
1165 *
1166 * Side Effects:
1167 *
1168 *----------------------------------------------------------------------------
1169*/
1170static EAS_RESULT RTTTL_PeekNextChar (EAS_HW_DATA_HANDLE hwInstData, S_RTTTL_DATA *pData, EAS_I8 *pValue)
1171{
1172	EAS_RESULT result;
1173	EAS_I8 temp;
1174
1175	*pValue = 0;
1176	for(;;)
1177	{
1178
1179		/* read a character from the file, if necessary */
1180		if (!pData->dataByte)
1181		{
1182			if ((result = EAS_HWGetByte(hwInstData, pData->fileHandle, &pData->dataByte)) != EAS_SUCCESS)
1183				return result;
1184
1185		}
1186		temp = pData->dataByte;
1187
1188		/* ignore white space */
1189		if (!IsSpace(temp))
1190		{
1191			*pValue = ToLower(temp);
1192			return EAS_SUCCESS;
1193		}
1194		pData->dataByte = 0;
1195	}
1196}
1197
1198