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