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             /* Number of ticks must not be greater than 32-bits */
443             if ((ticks >> 1) > (INT32_MAX - ticks))
444             {
445                 return EAS_ERROR_FILE_FORMAT;
446             }
447 
448             /*lint -e{704} shift for performance */
449             ticks += ticks >> 1;
450         }
451 
452         /* accidental */
453         else if (c == '#')
454         {
455             if (note)
456                 note++;
457         }
458 
459         /* end of event */
460         else if ((c == ',') && note)
461         {
462             pData->notePlayedSinceRepeat = 1;
463 
464             /* handle note events */
465             if (note != RTTTL_REST)
466             {
467 
468                 /* save note and start it */
469                 pData->note = note + octave;
470                 if (parserMode == eParserModePlay)
471                     VMStartNote(pEASData->pVoiceMgr, pData->pSynth, RTTTL_CHANNEL, pData->note, RTTTL_VELOCITY);
472 
473                 /* determine note length */
474                 switch (pData->style)
475                 {
476                     /* natural */
477                     case 'n':
478                         /*lint -e{704} shift for performance */
479                         pData->restTicks = ticks >> 4;
480                         break;
481                     /* continuous */
482 
483                     case 'c':
484                         pData->restTicks = 0;
485                         break;
486 
487                     /* staccato */
488                     case 's':
489                         /*lint -e{704} shift for performance */
490                         pData->restTicks = ticks >> 1;
491                         break;
492 
493                     default:
494                         { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "RTTTL_Event: Unexpected style type %c\n", pData->style); */ }
495                         break;
496                 }
497 
498                 /* next event is at end of this note */
499                 if ((ticks - pData->restTicks) > (INT32_MAX - pData->time))
500                 {
501                     return EAS_ERROR_FILE_FORMAT;
502                 }
503                 pData->time += ticks - pData->restTicks;
504             }
505 
506             /* rest */
507             else
508             {
509                 if (ticks > (INT32_MAX - pData->time))
510                 {
511                     return EAS_ERROR_FILE_FORMAT;
512                 }
513                 pData->time += ticks;
514             }
515 
516             /* event found, return to caller */
517             break;
518         }
519     }
520 
521     pData->state = EAS_STATE_PLAY;
522     return EAS_SUCCESS;
523 }
524 
525 /*----------------------------------------------------------------------------
526  * RTTTL_State()
527  *----------------------------------------------------------------------------
528  * Purpose:
529  * Returns the current state of the stream
530  *
531  * Inputs:
532  * pEASData         - pointer to overall EAS data structure
533  * handle           - pointer to file handle
534  * pState           - pointer to variable to store state
535  *
536  * Outputs:
537  *
538  *
539  * Side Effects:
540  *
541  *----------------------------------------------------------------------------
542 */
543 /*lint -esym(715, pEASData) reserved for future use */
RTTTL_State(S_EAS_DATA * pEASData,EAS_VOID_PTR pInstData,EAS_I32 * pState)544 static EAS_RESULT RTTTL_State (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 *pState)
545 {
546     S_RTTTL_DATA* pData;
547 
548     /* establish pointer to instance data */
549     pData = (S_RTTTL_DATA*) pInstData;
550 
551     /* if stopping, check to see if synth voices are active */
552     if (pData->state == EAS_STATE_STOPPING)
553     {
554         if (VMActiveVoices(pData->pSynth) == 0)
555             pData->state = EAS_STATE_STOPPED;
556     }
557 
558     if (pData->state == EAS_STATE_PAUSING)
559     {
560         if (VMActiveVoices(pData->pSynth) == 0)
561             pData->state = EAS_STATE_PAUSED;
562     }
563 
564     /* return current state */
565     *pState = pData->state;
566     return EAS_SUCCESS;
567 }
568 
569 /*----------------------------------------------------------------------------
570  * RTTTL_Close()
571  *----------------------------------------------------------------------------
572  * Purpose:
573  * Close the file and clean up
574  *
575  * Inputs:
576  * pEASData         - pointer to overall EAS data structure
577  * handle           - pointer to file handle
578  *
579  * Outputs:
580  *
581  *
582  * Side Effects:
583  *
584  *----------------------------------------------------------------------------
585 */
RTTTL_Close(S_EAS_DATA * pEASData,EAS_VOID_PTR pInstData)586 static EAS_RESULT RTTTL_Close (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
587 {
588     S_RTTTL_DATA* pData;
589     EAS_RESULT result;
590 
591     pData = (S_RTTTL_DATA*) pInstData;
592 
593     /* close the file */
594     if ((result = EAS_HWCloseFile(pEASData->hwInstData, pData->fileHandle)) != EAS_SUCCESS)
595             return result;
596 
597     /* free the synth */
598     if (pData->pSynth != NULL)
599         VMMIDIShutdown(pEASData, pData->pSynth);
600 
601     /* if using dynamic memory, free it */
602     if (!pEASData->staticMemoryModel)
603         EAS_HWFree(pEASData->hwInstData, pData);
604 
605     return EAS_SUCCESS;
606 }
607 
608 /*----------------------------------------------------------------------------
609  * RTTTL_Reset()
610  *----------------------------------------------------------------------------
611  * Purpose:
612  * Reset the sequencer. Used for locating backwards in the file.
613  *
614  * Inputs:
615  * pEASData         - pointer to overall EAS data structure
616  * handle           - pointer to file handle
617  *
618  * Outputs:
619  *
620  *
621  * Side Effects:
622  *
623  *----------------------------------------------------------------------------
624 */
RTTTL_Reset(S_EAS_DATA * pEASData,EAS_VOID_PTR pInstData)625 static EAS_RESULT RTTTL_Reset (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
626 {
627     S_RTTTL_DATA* pData;
628     EAS_RESULT result;
629 
630     pData = (S_RTTTL_DATA*) pInstData;
631 
632     /* reset the synth */
633     VMReset(pEASData->pVoiceMgr, pData->pSynth, EAS_TRUE);
634 
635     /* reset time to zero */
636     pData->time = 0;
637     pData->note = 0;
638 
639     /* reset file position and re-parse header */
640     pData->state = EAS_STATE_ERROR;
641     if ((result = EAS_HWFileSeek(pEASData->hwInstData, pData->fileHandle, pData->fileOffset)) != EAS_SUCCESS)
642         return result;
643     if ((result = RTTTL_ParseHeader (pEASData,  pData, EAS_TRUE)) != EAS_SUCCESS)
644         return result;
645 
646     pData->state = EAS_STATE_READY;
647     return EAS_SUCCESS;
648 }
649 
650 #ifdef JET_INTERFACE
651 /*----------------------------------------------------------------------------
652  * RTTTL_Pause()
653  *----------------------------------------------------------------------------
654  * Purpose:
655  * Pauses the sequencer. Mutes all voices and sets state to pause.
656  *
657  * Inputs:
658  * pEASData         - pointer to overall EAS data structure
659  * handle           - pointer to file handle
660  *
661  * Outputs:
662  *
663  *
664  * Side Effects:
665  *
666  *----------------------------------------------------------------------------
667 */
RTTTL_Pause(S_EAS_DATA * pEASData,EAS_VOID_PTR pInstData)668 static EAS_RESULT RTTTL_Pause (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
669 {
670     S_RTTTL_DATA *pData;
671 
672     /* can't pause a stopped stream */
673     pData = (S_RTTTL_DATA*) pInstData;
674     if (pData->state == EAS_STATE_STOPPED)
675         return EAS_ERROR_ALREADY_STOPPED;
676 
677     /* mute the synthesizer */
678     VMMuteAllVoices(pEASData->pVoiceMgr, pData->pSynth);
679     pData->state = EAS_STATE_PAUSING;
680     return EAS_SUCCESS;
681 }
682 
683 /*----------------------------------------------------------------------------
684  * RTTTL_Resume()
685  *----------------------------------------------------------------------------
686  * Purpose:
687  * Resume playing after a pause, sets state back to playing.
688  *
689  * Inputs:
690  * pEASData         - pointer to overall EAS data structure
691  * handle           - pointer to file handle
692  *
693  * Outputs:
694  *
695  *
696  * Side Effects:
697  *
698  *----------------------------------------------------------------------------
699 */
700 /*lint -esym(715, pEASData) reserved for future use */
RTTTL_Resume(S_EAS_DATA * pEASData,EAS_VOID_PTR pInstData)701 static EAS_RESULT RTTTL_Resume (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
702 {
703     S_RTTTL_DATA *pData;
704 
705     /* can't resume a stopped stream */
706     pData = (S_RTTTL_DATA*) pInstData;
707     if (pData->state == EAS_STATE_STOPPED)
708         return EAS_ERROR_ALREADY_STOPPED;
709 
710     /* nothing to do but resume playback */
711     pData->state = EAS_STATE_PLAY;
712     return EAS_SUCCESS;
713 }
714 #endif
715 
716 /*----------------------------------------------------------------------------
717  * RTTTL_SetData()
718  *----------------------------------------------------------------------------
719  * Purpose:
720  * Return file type
721  *
722  * Inputs:
723  * pEASData         - pointer to overall EAS data structure
724  * handle           - pointer to file handle
725  *
726  * Outputs:
727  *
728  *
729  * Side Effects:
730  *
731  *----------------------------------------------------------------------------
732 */
733 /*lint -esym(715, pEASData) reserved for future use */
RTTTL_SetData(S_EAS_DATA * pEASData,EAS_VOID_PTR pInstData,EAS_I32 param,EAS_I32 value)734 static EAS_RESULT RTTTL_SetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 value)
735 {
736     S_RTTTL_DATA *pData;
737 
738     pData = (S_RTTTL_DATA *) pInstData;
739     switch (param)
740     {
741 
742         /* set metadata callback */
743         case PARSER_DATA_METADATA_CB:
744             EAS_HWMemCpy(&pData->metadata, (void*) value, sizeof(S_METADATA_CB));
745             break;
746 
747         default:
748             return EAS_ERROR_INVALID_PARAMETER;
749     }
750 
751     return EAS_SUCCESS;
752 }
753 
754 /*----------------------------------------------------------------------------
755  * RTTTL_GetData()
756  *----------------------------------------------------------------------------
757  * Purpose:
758  * Return file type
759  *
760  * Inputs:
761  * pEASData         - pointer to overall EAS data structure
762  * handle           - pointer to file handle
763  *
764  * Outputs:
765  *
766  *
767  * Side Effects:
768  *
769  *----------------------------------------------------------------------------
770 */
771 /*lint -esym(715, pEASData) reserved for future use */
RTTTL_GetData(S_EAS_DATA * pEASData,EAS_VOID_PTR pInstData,EAS_I32 param,EAS_I32 * pValue)772 static EAS_RESULT RTTTL_GetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 *pValue)
773 {
774     S_RTTTL_DATA *pData;
775 
776     pData = (S_RTTTL_DATA *) pInstData;
777     switch (param)
778     {
779         /* return file type as RTTTL */
780         case PARSER_DATA_FILE_TYPE:
781             *pValue = EAS_FILE_RTTTL;
782             break;
783 
784 #if 0
785         /* set transposition */
786         case PARSER_DATA_TRANSPOSITION:
787             *pValue = pData->transposition;
788             break;
789 #endif
790 
791         case PARSER_DATA_SYNTH_HANDLE:
792             *pValue = (EAS_I32) pData->pSynth;
793             break;
794 
795         case PARSER_DATA_GAIN_OFFSET:
796             *pValue = RTTTL_GAIN_OFFSET;
797             break;
798 
799     default:
800             return EAS_ERROR_INVALID_PARAMETER;
801     }
802     return EAS_SUCCESS;
803 }
804 
805 /*----------------------------------------------------------------------------
806  * RTTTL_GetStyle()
807  *----------------------------------------------------------------------------
808  * Purpose:
809  *
810  *
811  * Inputs:
812  *
813  *
814  * Outputs:
815  *
816  *
817  * Side Effects:
818  *
819  *----------------------------------------------------------------------------
820 */
RTTTL_GetStyle(EAS_HW_DATA_HANDLE hwInstData,S_RTTTL_DATA * pData)821 static EAS_RESULT RTTTL_GetStyle (EAS_HW_DATA_HANDLE hwInstData, S_RTTTL_DATA *pData)
822 {
823     EAS_RESULT result;
824     EAS_I8 style;
825 
826     /* get style */
827     if ((result = RTTTL_GetNextChar(hwInstData, pData, &style)) != EAS_SUCCESS)
828         return result;
829 
830     if ((style != 's')  && (style != 'n') && (style != 'c'))
831         return EAS_ERROR_FILE_FORMAT;
832 
833     pData->style = style;
834     return EAS_SUCCESS;
835 }
836 
837 /*----------------------------------------------------------------------------
838  * RTTTL_GetDuration()
839  *----------------------------------------------------------------------------
840  * Purpose:
841  *
842  *
843  * Inputs:
844  *
845  *
846  * Outputs:
847  *
848  *
849  * Side Effects:
850  *
851  *----------------------------------------------------------------------------
852 */
RTTTL_GetDuration(EAS_HW_DATA_HANDLE hwInstData,S_RTTTL_DATA * pData,EAS_I8 * pDuration)853 static EAS_RESULT RTTTL_GetDuration (EAS_HW_DATA_HANDLE hwInstData, S_RTTTL_DATA *pData, EAS_I8 *pDuration)
854 {
855     EAS_RESULT result;
856     EAS_I32 duration;
857     EAS_I8 temp;
858 
859     /* get the duration */
860     if ((result = RTTTL_GetNumber(hwInstData, pData, &duration)) != EAS_SUCCESS)
861         return result;
862 
863     if ((duration != 1) && (duration != 2) && (duration != 4) && (duration != 8) && (duration != 16) && (duration != 32))
864         return EAS_ERROR_FILE_FORMAT;
865 
866     temp = 64;
867     while (duration)
868     {
869         /*lint -e{704} shift for performance */
870         duration = duration >> 1;
871         /*lint -e{702} use shift for performance */
872         temp = temp >> 1;
873     }
874 
875     *pDuration = temp;
876     return EAS_SUCCESS;
877 }
878 
879 /*----------------------------------------------------------------------------
880  * RTTTL_GetOctave()
881  *----------------------------------------------------------------------------
882  * Purpose:
883  *
884  *
885  * Inputs:
886  *
887  *
888  * Outputs:
889  *
890  *
891  * Side Effects:
892  *
893  *----------------------------------------------------------------------------
894 */
RTTTL_GetOctave(EAS_HW_DATA_HANDLE hwInstData,S_RTTTL_DATA * pData,EAS_U8 * pOctave)895 static EAS_RESULT RTTTL_GetOctave (EAS_HW_DATA_HANDLE hwInstData, S_RTTTL_DATA *pData, EAS_U8 *pOctave)
896 {
897     EAS_RESULT result;
898     EAS_I32 octave;
899 
900     /* get the tempo */
901     if ((result = RTTTL_GetNumber(hwInstData, pData, &octave)) != EAS_SUCCESS)
902         return result;
903 
904     if ((octave < 4) || (octave > 7))
905         return EAS_ERROR_FILE_FORMAT;
906 
907     *pOctave = (EAS_U8) (octave * 12);
908     return EAS_SUCCESS;
909 }
910 
911 /*----------------------------------------------------------------------------
912  * RTTTL_GetTempo()
913  *----------------------------------------------------------------------------
914  * Purpose:
915  *
916  *
917  * Inputs:
918  *
919  *
920  * Outputs:
921  *
922  *
923  * Side Effects:
924  *
925  *----------------------------------------------------------------------------
926 */
RTTTL_GetTempo(EAS_HW_DATA_HANDLE hwInstData,S_RTTTL_DATA * pData)927 static EAS_RESULT RTTTL_GetTempo (EAS_HW_DATA_HANDLE hwInstData, S_RTTTL_DATA *pData)
928 {
929     EAS_RESULT result;
930     EAS_I32 tempo;
931 
932     /* get the tempo */
933     if ((result = RTTTL_GetNumber(hwInstData, pData, &tempo)) != EAS_SUCCESS)
934         return result;
935 
936     if ((tempo < 25) || (tempo > 900))
937         return EAS_ERROR_FILE_FORMAT;
938 
939     pData->tick = TICK_CONVERT / (EAS_U32) tempo;
940     return EAS_SUCCESS;
941 }
942 
943 /*----------------------------------------------------------------------------
944  * RTTTL_GetNumber()
945  *----------------------------------------------------------------------------
946  * Purpose:
947  *
948  *
949  * Inputs:
950  *
951  *
952  * Outputs:
953  *
954  *
955  * Side Effects:
956  *
957  *----------------------------------------------------------------------------
958 */
RTTTL_GetNumber(EAS_HW_DATA_HANDLE hwInstData,S_RTTTL_DATA * pData,EAS_I32 * pValue)959 static EAS_RESULT RTTTL_GetNumber (EAS_HW_DATA_HANDLE hwInstData, S_RTTTL_DATA *pData, EAS_I32 *pValue)
960 {
961     EAS_RESULT result;
962     EAS_INT temp;
963     EAS_I8 c;
964 
965     *pValue = -1;
966     temp = 0;
967     for (;;)
968     {
969         if ((result = RTTTL_PeekNextChar(hwInstData, pData, &c)) != EAS_SUCCESS)
970         {
971             if ((result == EAS_EOF) && (*pValue != -1))
972                 return EAS_SUCCESS;
973             return result;
974         }
975 
976         if (IsDigit(c))
977         {
978             pData->dataByte = 0;
979             if (temp > 100) {
980                 // This is just to prevent overflows in case of a really large number
981                 // in the file, but rather than allowing the number to grow up to INT_MAX,
982                 // we limit it to a much smaller number since the numbers in an RTTTL file
983                 // are supposed to be at most in the hundreds, not millions or billions.
984                 // There are more specific checks in the callers of this function to enforce
985                 // the various limits for notes, octaves, tempo, etc.
986                 return EAS_FAILURE;
987             }
988             temp = temp * 10 + c - '0';
989             *pValue = temp;
990         }
991         else
992             return EAS_SUCCESS;
993     }
994 }
995 
996 /*----------------------------------------------------------------------------
997  * RTTTL_ParseHeader()
998  *----------------------------------------------------------------------------
999  * Purpose:
1000  * Prepare to parse the file. Allocates instance data (or uses static allocation for
1001  * static memory model).
1002  *
1003  * Inputs:
1004  * pEASData         - pointer to overall EAS data structure
1005  * handle           - pointer to file handle
1006  *
1007  * Outputs:
1008  *
1009  *
1010  * Side Effects:
1011  *
1012  *----------------------------------------------------------------------------
1013 */
RTTTL_ParseHeader(S_EAS_DATA * pEASData,S_RTTTL_DATA * pData,EAS_BOOL metaData)1014 static EAS_RESULT RTTTL_ParseHeader (S_EAS_DATA *pEASData, S_RTTTL_DATA* pData, EAS_BOOL metaData)
1015 {
1016     EAS_RESULT result;
1017     EAS_I32 i;
1018     EAS_I8 temp;
1019     EAS_I8 control;
1020 
1021     /* initialize some defaults */
1022     pData->time = 0;
1023     pData->tick = DEFAULT_TICK_CONV;
1024     pData->note = 0;
1025     pData->duration = 4;
1026     pData ->restTicks = 0;
1027     pData->octave = 60;
1028     pData->repeatOffset = -1;
1029     pData->repeatCount = 0;
1030     pData->style = 'n';
1031     pData->dataByte = 0;
1032 
1033     metaData = metaData && (pData->metadata.buffer != NULL) && (pData->metadata.callback != NULL);
1034 
1035     /* seek to start of data */
1036     if ((result = EAS_HWFileSeek(pEASData->hwInstData, pData->fileHandle, pData->fileOffset)) != EAS_SUCCESS)
1037         return result;
1038 
1039     /* zero the metadata buffer */
1040     if (metaData)
1041         EAS_HWMemSet(pData->metadata.buffer, 0, pData->metadata.bufferSize);
1042 
1043     /* read the title */
1044     for (i = 0; i < RTTTL_MAX_TITLE_LEN; i++)
1045     {
1046         if ((result = EAS_HWGetByte(pEASData->hwInstData, pData->fileHandle, &temp)) != EAS_SUCCESS)
1047             return result;
1048 
1049         if (temp == ':')
1050             break;
1051 
1052         /* pass along metadata */
1053         if (metaData)
1054         {
1055             if (i < (pData->metadata.bufferSize- 1))
1056                 pData->metadata.buffer[i] = (char) temp;
1057         }
1058     }
1059 
1060     /* check for error in title */
1061     if (i == RTTTL_MAX_TITLE_LEN)
1062         return EAS_ERROR_FILE_FORMAT;
1063 
1064     /* pass along metadata */
1065     if (metaData)
1066         (*pData->metadata.callback)(EAS_METADATA_TITLE, pData->metadata.buffer, pData->metadata.pUserData);
1067 
1068     /* control fields */
1069     for (;;)
1070     {
1071 
1072         /* get control type */
1073         if ((result = RTTTL_GetNextChar(pEASData->hwInstData, pData, &control)) != EAS_SUCCESS)
1074             return result;
1075 
1076         /* next char should be equal sign */
1077         if ((result = RTTTL_GetNextChar(pEASData->hwInstData, pData, &temp)) != EAS_SUCCESS)
1078             return result;
1079         if (temp != '=')
1080             return EAS_ERROR_FILE_FORMAT;
1081 
1082         /* get the control value */
1083         switch (control)
1084         {
1085 
1086             /* bpm */
1087             case 'b':
1088                 if ((result = RTTTL_GetTempo(pEASData->hwInstData, pData)) != EAS_SUCCESS)
1089                     return result;
1090                 break;
1091 
1092             /* duration */
1093             case 'd':
1094                 if ((result = RTTTL_GetDuration(pEASData->hwInstData, pData, &temp)) != EAS_SUCCESS)
1095                     return result;
1096                 pData->duration = temp;
1097                 break;
1098 
1099             /* loop */
1100             case 'l':
1101                 if ((result = RTTTL_GetNumber(pEASData->hwInstData, pData, &i)) != EAS_SUCCESS)
1102                     return result;
1103                 if ((i < 0) || (i > 15))
1104                     return EAS_ERROR_FILE_FORMAT;
1105                 pData->repeatCount = (EAS_U8) i;
1106                 break;
1107 
1108             /* octave */
1109             case 'o':
1110                 if ((result = RTTTL_GetOctave(pEASData->hwInstData, pData, &pData->octave)) != EAS_SUCCESS)
1111                     return result;
1112                 break;
1113 
1114             /* get style */
1115             case 's':
1116                 if ((result = RTTTL_GetStyle(pEASData->hwInstData, pData)) != EAS_SUCCESS)
1117                     return result;
1118                 break;
1119 
1120             /* unrecognized control */
1121             default:
1122                 return EAS_ERROR_FILE_FORMAT;
1123         }
1124 
1125         /* next character should be comma or colon */
1126         if ((result = RTTTL_GetNextChar(pEASData->hwInstData, pData, &temp)) != EAS_SUCCESS)
1127             return result;
1128 
1129         /* check for end of control field */
1130         if (temp == ':')
1131             break;
1132 
1133         /* must be a comma */
1134         if (temp != ',')
1135             return EAS_ERROR_FILE_FORMAT;
1136     }
1137 
1138     /* should be at the start of the music block */
1139     if ((result = EAS_HWFilePos(pEASData->hwInstData, pData->fileHandle, &pData->repeatOffset)) != EAS_SUCCESS)
1140         return result;
1141 
1142     pData->notePlayedSinceRepeat = 0;
1143     return EAS_SUCCESS;
1144 }
1145 
1146 /*----------------------------------------------------------------------------
1147  * RTTTL_GetNextChar()
1148  *----------------------------------------------------------------------------
1149  * Purpose:
1150  *
1151  *
1152  * Inputs:
1153  *
1154  *
1155  * Outputs:
1156  *
1157  *
1158  * Side Effects:
1159  *
1160  *----------------------------------------------------------------------------
1161 */
RTTTL_GetNextChar(EAS_HW_DATA_HANDLE hwInstData,S_RTTTL_DATA * pData,EAS_I8 * pValue)1162 static EAS_RESULT RTTTL_GetNextChar (EAS_HW_DATA_HANDLE hwInstData, S_RTTTL_DATA *pData, EAS_I8 *pValue)
1163 {
1164     EAS_RESULT result;
1165     EAS_I8 temp;
1166 
1167     *pValue = 0;
1168     for(;;)
1169     {
1170 
1171         /* check for character that has been put back */
1172         if (pData->dataByte)
1173         {
1174             temp = pData->dataByte;
1175             pData->dataByte = 0;
1176         }
1177         else
1178         {
1179             if ((result = EAS_HWGetByte(hwInstData, pData->fileHandle, &temp)) != EAS_SUCCESS)
1180                 return result;
1181         }
1182 
1183         /* ignore white space */
1184         if (!IsSpace(temp))
1185         {
1186             *pValue = ToLower(temp);
1187             return EAS_SUCCESS;
1188         }
1189     }
1190 }
1191 
1192 /*----------------------------------------------------------------------------
1193  * RTTTL_PeekNextChar()
1194  *----------------------------------------------------------------------------
1195  * Purpose:
1196  *
1197  *
1198  * Inputs:
1199  *
1200  *
1201  * Outputs:
1202  *
1203  *
1204  * Side Effects:
1205  *
1206  *----------------------------------------------------------------------------
1207 */
RTTTL_PeekNextChar(EAS_HW_DATA_HANDLE hwInstData,S_RTTTL_DATA * pData,EAS_I8 * pValue)1208 static EAS_RESULT RTTTL_PeekNextChar (EAS_HW_DATA_HANDLE hwInstData, S_RTTTL_DATA *pData, EAS_I8 *pValue)
1209 {
1210     EAS_RESULT result;
1211     EAS_I8 temp;
1212 
1213     *pValue = 0;
1214     for(;;)
1215     {
1216 
1217         /* read a character from the file, if necessary */
1218         if (!pData->dataByte)
1219         {
1220             if ((result = EAS_HWGetByte(hwInstData, pData->fileHandle, &pData->dataByte)) != EAS_SUCCESS)
1221                 return result;
1222 
1223         }
1224         temp = pData->dataByte;
1225 
1226         /* ignore white space */
1227         if (!IsSpace(temp))
1228         {
1229             *pValue = ToLower(temp);
1230             return EAS_SUCCESS;
1231         }
1232         pData->dataByte = 0;
1233     }
1234 }
1235 
1236