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