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     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 */
RTTTL_CheckFileType(S_EAS_DATA * pEASData,EAS_FILE_HANDLE fileHandle,EAS_VOID_PTR * ppHandle,EAS_I32 offset)133 static 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 */
RTTTL_Prepare(S_EAS_DATA * pEASData,EAS_VOID_PTR pInstData)182 static 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 */
RTTTL_Time(S_EAS_DATA * pEASData,EAS_VOID_PTR pInstData,EAS_U32 * pTime)231 static 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 */
RTTTL_Event(S_EAS_DATA * pEASData,EAS_VOID_PTR pInstData,EAS_INT parserMode)260 static 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 */
RTTTL_State(S_EAS_DATA * pEASData,EAS_VOID_PTR pInstData,EAS_I32 * pState)518 static 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 */
RTTTL_Close(S_EAS_DATA * pEASData,EAS_VOID_PTR pInstData)560 static 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 */
RTTTL_Reset(S_EAS_DATA * pEASData,EAS_VOID_PTR pInstData)599 static 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 */
RTTTL_Pause(S_EAS_DATA * pEASData,EAS_VOID_PTR pInstData)641 static 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 */
RTTTL_Resume(S_EAS_DATA * pEASData,EAS_VOID_PTR pInstData)674 static 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 */
RTTTL_SetData(S_EAS_DATA * pEASData,EAS_VOID_PTR pInstData,EAS_I32 param,EAS_I32 value)706 static 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 */
RTTTL_GetData(S_EAS_DATA * pEASData,EAS_VOID_PTR pInstData,EAS_I32 param,EAS_I32 * pValue)744 static 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 */
RTTTL_GetStyle(EAS_HW_DATA_HANDLE hwInstData,S_RTTTL_DATA * pData)793 static 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 */
RTTTL_GetDuration(EAS_HW_DATA_HANDLE hwInstData,S_RTTTL_DATA * pData,EAS_I8 * pDuration)825 static 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 */
RTTTL_GetOctave(EAS_HW_DATA_HANDLE hwInstData,S_RTTTL_DATA * pData,EAS_U8 * pOctave)867 static 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 */
RTTTL_GetTempo(EAS_HW_DATA_HANDLE hwInstData,S_RTTTL_DATA * pData)899 static 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 */
RTTTL_GetNumber(EAS_HW_DATA_HANDLE hwInstData,S_RTTTL_DATA * pData,EAS_I32 * pValue)931 static 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 */
RTTTL_ParseHeader(S_EAS_DATA * pEASData,S_RTTTL_DATA * pData,EAS_BOOL metaData)977 static 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 */
RTTTL_GetNextChar(EAS_HW_DATA_HANDLE hwInstData,S_RTTTL_DATA * pData,EAS_I8 * pValue)1124 static 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 */
RTTTL_PeekNextChar(EAS_HW_DATA_HANDLE hwInstData,S_RTTTL_DATA * pData,EAS_I8 * pValue)1170 static 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