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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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