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