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