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