1 /*---------------------------------------------------------------------------- 2 * 3 * File: 4 * eas_tonecontrol.c 5 * 6 * Contents and purpose: 7 * MMAPI ToneControl parser 8 * 9 * Copyright Sonic Network Inc. 2006 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_tcdata.h" 39 40 41 /* default channel and program for TC playback */ 42 #define TC_CHANNEL 0 43 #define TC_PROGRAM 80 44 #define TC_VELOCITY 127 45 46 #define TC_FIELD_SILENCE -1 47 #define TC_FIELD_VERSION -2 48 #define TC_FIELD_TEMPO -3 49 #define TC_FIELD_RESOLUTION -4 50 #define TC_FIELD_BLOCK_START -5 51 #define TC_FIELD_BLOCK_END -6 52 #define TC_FIELD_PLAY_BLOCK -7 53 #define TC_FIELD_SET_VOLUME -8 54 #define TC_FIELD_REPEAT -9 55 #define TC_FIELD_INVALID -10 56 57 /* convert 0-100 volume to 0-127 velocity using fixed point */ 58 #define TC_VOLUME_CONV 21307064 59 #define TC_VOLUME_SHIFT 24 60 61 62 /* local prototypes */ 63 static EAS_RESULT TC_CheckFileType (S_EAS_DATA *pEASData, EAS_FILE_HANDLE fileHandle, EAS_VOID_PTR *ppHandle, EAS_I32 offset); 64 static EAS_RESULT TC_Prepare (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData); 65 static EAS_RESULT TC_Time (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_U32 *pTime); 66 static EAS_RESULT TC_Event (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_INT parserMode); 67 static EAS_RESULT TC_State (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_STATE *pState); 68 static EAS_RESULT TC_Close (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData); 69 static EAS_RESULT TC_Reset (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData); 70 static EAS_RESULT TC_Pause (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData); 71 static EAS_RESULT TC_Resume (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData); 72 static EAS_RESULT TC_SetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 value); 73 static EAS_RESULT TC_GetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 *pValue); 74 static EAS_RESULT TC_ParseHeader (S_EAS_DATA *pEASData, S_TC_DATA* pData); 75 static EAS_RESULT TC_StartNote (S_EAS_DATA *pEASData, S_TC_DATA* pData, EAS_INT parserMode, EAS_I8 note); 76 static EAS_RESULT TC_GetRepeat (S_EAS_DATA *pEASData, S_TC_DATA* pData, EAS_INT parserMode); 77 static EAS_RESULT TC_PlayBlock (S_EAS_DATA *pEASData, S_TC_DATA* pData); 78 static EAS_RESULT TC_BlockEnd (S_EAS_DATA *pEASData, S_TC_DATA* pData); 79 static EAS_RESULT TC_GetVolume (S_EAS_DATA *pEASData, S_TC_DATA* pData); 80 static EAS_RESULT TC_GetTempo (S_EAS_DATA *pEASData, S_TC_DATA* pData); 81 static EAS_RESULT TC_GetResolution (S_EAS_DATA *pEASData, S_TC_DATA* pData); 82 static EAS_RESULT TC_GetNextChar (EAS_HW_DATA_HANDLE hwInstData, S_TC_DATA *pData, EAS_I8 *pValue); 83 static void TC_PutBackChar (S_TC_DATA *pData, EAS_I8 value); 84 85 /* calculate a new tick time based on resolution & tempo */ 86 EAS_INLINE void TC_CalcTimeBase (S_TC_DATA *pData) 87 { 88 89 /* ticks in 256ths of a millisecond */ 90 pData->tick = ((60 * 1000) << 8) / (pData->tempo * pData->resolution); 91 } 92 93 /*---------------------------------------------------------------------------- 94 * 95 * EAS_TC_Parser 96 * 97 * This structure contains the functional interface for the iMelody parser 98 *---------------------------------------------------------------------------- 99 */ 100 const S_FILE_PARSER_INTERFACE EAS_TC_Parser = 101 { 102 TC_CheckFileType, 103 TC_Prepare, 104 TC_Time, 105 TC_Event, 106 TC_State, 107 TC_Close, 108 TC_Reset, 109 TC_Pause, 110 TC_Resume, 111 NULL, 112 TC_SetData, 113 TC_GetData, 114 NULL 115 }; 116 117 /*---------------------------------------------------------------------------- 118 * TC_CheckFileType() 119 *---------------------------------------------------------------------------- 120 * Purpose: 121 * Check the file type to see if we can parse it 122 * 123 * Inputs: 124 * pEASData - pointer to overall EAS data structure 125 * handle - pointer to file handle 126 * 127 * Outputs: 128 * 129 * 130 * Side Effects: 131 * 132 *---------------------------------------------------------------------------- 133 */ 134 static EAS_RESULT TC_CheckFileType (S_EAS_DATA *pEASData, EAS_FILE_HANDLE fileHandle, EAS_VOID_PTR *ppHandle, EAS_I32 offset) 135 { 136 S_TC_DATA data; 137 S_TC_DATA *pData; 138 139 /* init data */ 140 EAS_HWMemSet(&data, 0, sizeof(S_TC_DATA)); 141 data.fileHandle = fileHandle; 142 data.fileOffset = offset; 143 *ppHandle= NULL; 144 145 /* see if we can parse the header */ 146 if (TC_ParseHeader(pEASData, &data) == EAS_SUCCESS) 147 { 148 149 /* check for static memory allocation */ 150 if (pEASData->staticMemoryModel) 151 pData = EAS_CMEnumOptData(EAS_MODULE_MMAPI_TONE_CONTROL); 152 else 153 pData = EAS_HWMalloc(pEASData->hwInstData, sizeof(S_TC_DATA)); 154 if (!pData) 155 return EAS_ERROR_MALLOC_FAILED; 156 157 /* copy data to persistent storage */ 158 EAS_HWMemCpy(pData, &data, sizeof(S_TC_DATA)); 159 160 /* return a pointer to the instance data */ 161 pData->state = EAS_STATE_OPEN; 162 *ppHandle = pData; 163 } 164 165 return EAS_SUCCESS; 166 } 167 168 /*---------------------------------------------------------------------------- 169 * TC_Prepare() 170 *---------------------------------------------------------------------------- 171 * Purpose: 172 * Prepare to parse the file. Allocates instance data (or uses static allocation for 173 * static memory model). 174 * 175 * Inputs: 176 * pEASData - pointer to overall EAS data structure 177 * handle - pointer to file handle 178 * 179 * Outputs: 180 * 181 * 182 * Side Effects: 183 * 184 *---------------------------------------------------------------------------- 185 */ 186 static EAS_RESULT TC_Prepare (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData) 187 { 188 S_TC_DATA* pData; 189 EAS_RESULT result; 190 191 /* check for valid state */ 192 pData = (S_TC_DATA*) pInstData; 193 if (pData->state != EAS_STATE_OPEN) 194 return EAS_ERROR_NOT_VALID_IN_THIS_STATE; 195 196 /* instantiate a synthesizer */ 197 if ((result = VMInitMIDI(pEASData, &pData->pSynth)) != EAS_SUCCESS) 198 { 199 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "VMInitMIDI returned %d\n", result); */ } 200 return result; 201 } 202 203 /* set to ready state */ 204 pData->state = EAS_STATE_READY; 205 return EAS_SUCCESS; 206 } 207 208 /*---------------------------------------------------------------------------- 209 * TC_Time() 210 *---------------------------------------------------------------------------- 211 * Purpose: 212 * Returns the time of the next event in msecs 213 * 214 * Inputs: 215 * pEASData - pointer to overall EAS data structure 216 * handle - pointer to file handle 217 * pTime - pointer to variable to hold time of next event (in msecs) 218 * 219 * Outputs: 220 * 221 * 222 * Side Effects: 223 * 224 *---------------------------------------------------------------------------- 225 */ 226 /*lint -esym(715, pEASData) reserved for future use */ 227 static EAS_RESULT TC_Time (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_U32 *pTime) 228 { 229 S_TC_DATA *pData; 230 231 pData = (S_TC_DATA*) pInstData; 232 233 /* return time in milliseconds */ 234 /*lint -e{704} use shift instead of division */ 235 *pTime = pData->time >> 8; 236 return EAS_SUCCESS; 237 } 238 239 /*---------------------------------------------------------------------------- 240 * TC_Event() 241 *---------------------------------------------------------------------------- 242 * Purpose: 243 * Parse the next event in the file 244 * 245 * Inputs: 246 * pEASData - pointer to overall EAS data structure 247 * handle - pointer to file handle 248 * 249 * Outputs: 250 * 251 * 252 * Side Effects: 253 * 254 *---------------------------------------------------------------------------- 255 */ 256 static EAS_RESULT TC_Event (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_INT parserMode) 257 { 258 S_TC_DATA* pData; 259 EAS_RESULT result; 260 EAS_I8 temp; 261 262 pData = (S_TC_DATA*) pInstData; 263 if (pData->state >= EAS_STATE_OPEN) 264 return EAS_SUCCESS; 265 266 /* initialize MIDI channel when the track starts playing */ 267 if (pData->time == 0) 268 { 269 /* set program to square lead */ 270 VMProgramChange(pEASData->pVoiceMgr, pData->pSynth, TC_CHANNEL, TC_PROGRAM); 271 272 /* set channel volume to max */ 273 VMControlChange(pEASData->pVoiceMgr, pData->pSynth, TC_CHANNEL, 7, 127); 274 } 275 276 /* check for end of note */ 277 if (pData->note >= 0) 278 { 279 /* stop the note */ 280 VMStopNote(pEASData->pVoiceMgr, pData->pSynth, TC_CHANNEL, (EAS_U8) pData->note, 0); 281 282 /* check for repeat note */ 283 if (pData->repeatCount) 284 { 285 pData->repeatCount--; 286 pData->time += pData->length; 287 if ((pData->note >= 0) && (parserMode == eParserModePlay)) 288 VMStartNote(pEASData->pVoiceMgr, pData->pSynth, TC_CHANNEL, (EAS_U8) pData->note, pData->volume); 289 return EAS_SUCCESS; 290 } 291 292 pData->note = TC_FIELD_SILENCE; 293 } 294 295 /* parse stream until we get a note or rest */ 296 for (;;) 297 { 298 299 /* get next byte from stream */ 300 if ((result = TC_GetNextChar(pEASData->hwInstData, pData, &temp)) != EAS_SUCCESS) 301 { 302 if (result == EAS_EOF) 303 { 304 pData->state = EAS_STATE_STOPPING; 305 return EAS_SUCCESS; 306 } 307 break; 308 } 309 310 /* check for musical events */ 311 if (temp >= TC_FIELD_SILENCE) 312 { 313 result = TC_StartNote(pEASData, pData, parserMode, temp); 314 break; 315 } 316 317 /* must be a control field */ 318 switch (temp) 319 { 320 case TC_FIELD_TEMPO: 321 result = TC_GetTempo(pEASData, pData); 322 break; 323 324 case TC_FIELD_RESOLUTION: 325 result = TC_GetResolution(pEASData, pData); 326 break; 327 328 case TC_FIELD_SET_VOLUME: 329 result = TC_GetVolume(pEASData, pData); 330 break; 331 332 case TC_FIELD_REPEAT: 333 result = TC_GetRepeat(pEASData, pData, parserMode); 334 break; 335 336 case TC_FIELD_PLAY_BLOCK: 337 result = TC_PlayBlock(pEASData, pData); 338 break; 339 340 case TC_FIELD_BLOCK_START: 341 result = TC_GetNextChar(pEASData->hwInstData, pData, &temp); 342 break; 343 344 case TC_FIELD_BLOCK_END: 345 result = TC_BlockEnd(pEASData, pData); 346 break; 347 348 default: 349 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "Unexpected byte 0x%02x in ToneControl stream\n", temp); */ } 350 result = EAS_ERROR_FILE_FORMAT; 351 } 352 353 /* check for error */ 354 if (result != EAS_SUCCESS) 355 break; 356 } 357 358 /* check for error */ 359 if (result != EAS_SUCCESS) 360 { 361 if (result == EAS_EOF) 362 result = EAS_ERROR_FILE_FORMAT; 363 pData->state = EAS_STATE_ERROR; 364 } 365 else 366 pData->state = EAS_STATE_PLAY; 367 return result; 368 } 369 370 /*---------------------------------------------------------------------------- 371 * TC_State() 372 *---------------------------------------------------------------------------- 373 * Purpose: 374 * Returns the current state of the stream 375 * 376 * Inputs: 377 * pEASData - pointer to overall EAS data structure 378 * handle - pointer to file handle 379 * pState - pointer to variable to store state 380 * 381 * Outputs: 382 * 383 * 384 * Side Effects: 385 * 386 *---------------------------------------------------------------------------- 387 */ 388 /*lint -esym(715, pEASData) reserved for future use */ 389 static EAS_RESULT TC_State (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 *pState) 390 { 391 S_TC_DATA* pData; 392 393 /* establish pointer to instance data */ 394 pData = (S_TC_DATA*) pInstData; 395 396 /* if stopping, check to see if synth voices are active */ 397 if (pData->state == EAS_STATE_STOPPING) 398 { 399 if (VMActiveVoices(pData->pSynth) == 0) 400 pData->state = EAS_STATE_STOPPED; 401 } 402 403 if (pData->state == EAS_STATE_PAUSING) 404 { 405 if (VMActiveVoices(pData->pSynth) == 0) 406 pData->state = EAS_STATE_PAUSED; 407 } 408 409 /* return current state */ 410 *pState = pData->state; 411 return EAS_SUCCESS; 412 } 413 414 /*---------------------------------------------------------------------------- 415 * TC_Close() 416 *---------------------------------------------------------------------------- 417 * Purpose: 418 * Close the file and clean up 419 * 420 * Inputs: 421 * pEASData - pointer to overall EAS data structure 422 * handle - pointer to file handle 423 * 424 * Outputs: 425 * 426 * 427 * Side Effects: 428 * 429 *---------------------------------------------------------------------------- 430 */ 431 static EAS_RESULT TC_Close (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData) 432 { 433 S_TC_DATA* pData; 434 EAS_RESULT result; 435 436 pData = (S_TC_DATA*) pInstData; 437 438 /* close the file */ 439 if ((result = EAS_HWCloseFile(pEASData->hwInstData, pData->fileHandle)) != EAS_SUCCESS) 440 return result; 441 442 /* free the synth */ 443 if (pData->pSynth != NULL) 444 VMMIDIShutdown(pEASData, pData->pSynth); 445 446 /* if using dynamic memory, free it */ 447 if (!pEASData->staticMemoryModel) 448 EAS_HWFree(pEASData->hwInstData, pData); 449 450 return EAS_SUCCESS; 451 } 452 453 /*---------------------------------------------------------------------------- 454 * TC_Reset() 455 *---------------------------------------------------------------------------- 456 * Purpose: 457 * Reset the sequencer. Used for locating backwards in the file. 458 * 459 * Inputs: 460 * pEASData - pointer to overall EAS data structure 461 * handle - pointer to file handle 462 * 463 * Outputs: 464 * 465 * 466 * Side Effects: 467 * 468 *---------------------------------------------------------------------------- 469 */ 470 static EAS_RESULT TC_Reset (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData) 471 { 472 S_TC_DATA* pData; 473 EAS_RESULT result; 474 475 pData = (S_TC_DATA*) pInstData; 476 477 /* reset the synth */ 478 VMReset(pEASData->pVoiceMgr, pData->pSynth, EAS_TRUE); 479 480 /* reset time to zero */ 481 pData->time = 0; 482 483 /* reset file position and re-parse header */ 484 pData->state = EAS_STATE_ERROR; 485 if ((result = EAS_HWFileSeek(pEASData->hwInstData, pData->fileHandle, pData->fileOffset)) != EAS_SUCCESS) 486 return result; 487 if ((result = TC_ParseHeader (pEASData, pData)) != EAS_SUCCESS) 488 return result; 489 490 pData->state = EAS_STATE_READY; 491 return EAS_SUCCESS; 492 } 493 494 /*---------------------------------------------------------------------------- 495 * TC_Pause() 496 *---------------------------------------------------------------------------- 497 * Purpose: 498 * Pauses the sequencer. Mutes all voices and sets state to pause. 499 * 500 * Inputs: 501 * pEASData - pointer to overall EAS data structure 502 * handle - pointer to file handle 503 * 504 * Outputs: 505 * 506 * 507 * Side Effects: 508 * 509 *---------------------------------------------------------------------------- 510 */ 511 static EAS_RESULT TC_Pause (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData) 512 { 513 S_TC_DATA *pData; 514 515 /* can't pause a stopped stream */ 516 pData = (S_TC_DATA*) pInstData; 517 if (pData->state == EAS_STATE_STOPPED) 518 return EAS_ERROR_ALREADY_STOPPED; 519 520 /* mute the synthesizer */ 521 VMMuteAllVoices(pEASData->pVoiceMgr, pData->pSynth); 522 pData->state = EAS_STATE_PAUSING; 523 return EAS_SUCCESS; 524 } 525 526 /*---------------------------------------------------------------------------- 527 * TC_Resume() 528 *---------------------------------------------------------------------------- 529 * Purpose: 530 * Resume playing after a pause, sets state back to playing. 531 * 532 * Inputs: 533 * pEASData - pointer to overall EAS data structure 534 * handle - pointer to file handle 535 * 536 * Outputs: 537 * 538 * 539 * Side Effects: 540 * 541 *---------------------------------------------------------------------------- 542 */ 543 /*lint -esym(715, pEASData) reserved for future use */ 544 static EAS_RESULT TC_Resume (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData) 545 { 546 S_TC_DATA *pData; 547 548 /* can't resume a stopped stream */ 549 pData = (S_TC_DATA*) pInstData; 550 if (pData->state == EAS_STATE_STOPPED) 551 return EAS_ERROR_ALREADY_STOPPED; 552 553 /* nothing to do but resume playback */ 554 pData->state = EAS_STATE_PLAY; 555 return EAS_SUCCESS; 556 } 557 558 /*---------------------------------------------------------------------------- 559 * TC_SetData() 560 *---------------------------------------------------------------------------- 561 * Purpose: 562 * Return file type 563 * 564 * Inputs: 565 * pEASData - pointer to overall EAS data structure 566 * handle - pointer to file handle 567 * 568 * Outputs: 569 * 570 * 571 * Side Effects: 572 * 573 *---------------------------------------------------------------------------- 574 */ 575 /*lint -esym(715, pEASData, pInstData, value) reserved for future use */ 576 static EAS_RESULT TC_SetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 value) 577 { 578 /* we don't parse any metadata, but we need to return success here */ 579 if (param == PARSER_DATA_METADATA_CB) 580 return EAS_SUCCESS; 581 582 return EAS_ERROR_INVALID_PARAMETER; 583 } 584 585 /*---------------------------------------------------------------------------- 586 * TC_GetData() 587 *---------------------------------------------------------------------------- 588 * Purpose: 589 * Return file type 590 * 591 * Inputs: 592 * pEASData - pointer to overall EAS data structure 593 * handle - pointer to file handle 594 * 595 * Outputs: 596 * 597 * 598 * Side Effects: 599 * 600 *---------------------------------------------------------------------------- 601 */ 602 /*lint -e{715} common with other parsers */ 603 static EAS_RESULT TC_GetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 *pValue) 604 { 605 S_TC_DATA *pData; 606 607 pData = (S_TC_DATA *) pInstData; 608 switch (param) 609 { 610 /* return file type as TC */ 611 case PARSER_DATA_FILE_TYPE: 612 *pValue = EAS_FILE_MMAPI_TONE_CONTROL; 613 break; 614 615 case PARSER_DATA_SYNTH_HANDLE: 616 *pValue = (EAS_I32) pData->pSynth; 617 break; 618 619 default: 620 return EAS_ERROR_INVALID_PARAMETER; 621 } 622 return EAS_SUCCESS; 623 } 624 625 /*---------------------------------------------------------------------------- 626 * TC_ParseHeader() 627 *---------------------------------------------------------------------------- 628 * Purpose: 629 * Prepare to parse the file. Allocates instance data (or uses static allocation for 630 * static memory model). 631 * 632 * Inputs: 633 * pEASData - pointer to overall EAS data structure 634 * handle - pointer to file handle 635 * 636 * Outputs: 637 * 638 * 639 * Side Effects: 640 * 641 *---------------------------------------------------------------------------- 642 */ 643 static EAS_RESULT TC_ParseHeader (S_EAS_DATA *pEASData, S_TC_DATA* pData) 644 { 645 EAS_RESULT result; 646 EAS_I8 temp; 647 648 /* initialize some defaults */ 649 pData->time = 0; 650 pData->tempo = 120; 651 pData->resolution = 64; 652 pData->volume = 127; 653 pData->repeatCount = 0; 654 pData->note = TC_FIELD_SILENCE; 655 pData->byteAvail = EAS_FALSE; 656 657 /* set default timebase */ 658 TC_CalcTimeBase(pData); 659 660 /* seek to start of data */ 661 if ((result = EAS_HWFileSeek(pEASData->hwInstData, pData->fileHandle, pData->fileOffset)) != EAS_SUCCESS) 662 return result; 663 664 /* get version */ 665 if ((result = TC_GetNextChar(pEASData->hwInstData, pData, &temp)) != EAS_SUCCESS) 666 return result; 667 668 /* check for version number */ 669 if (temp == TC_FIELD_VERSION) 670 { 671 TC_GetNextChar(pEASData->hwInstData, pData, &temp); 672 // { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "ToneControl sequence version %d\n", temp); */ } 673 } 674 else 675 return EAS_ERROR_FILE_FORMAT; 676 677 /* parse the header data until we find the first note or block */ 678 for (;;) 679 { 680 681 /* get next byte from stream */ 682 if ((result = TC_GetNextChar(pEASData->hwInstData, pData, &temp)) != EAS_SUCCESS) 683 return result; 684 685 /* check for tempo */ 686 if (temp == TC_FIELD_TEMPO) 687 { 688 if ((result = TC_GetTempo(pEASData, pData)) != EAS_SUCCESS) 689 return result; 690 } 691 692 /* or resolution */ 693 else if (temp == TC_FIELD_TEMPO) 694 { 695 if ((result = TC_GetResolution(pEASData, pData)) != EAS_SUCCESS) 696 return result; 697 } 698 699 /* must be music data */ 700 else if (temp > TC_FIELD_INVALID) 701 { 702 TC_PutBackChar(pData, temp); 703 return EAS_SUCCESS; 704 } 705 706 /* unknown codes */ 707 else 708 { 709 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "Unexpected byte 0x%02x in ToneControl stream\n", temp); */ } 710 return EAS_ERROR_FILE_FORMAT; 711 } 712 } 713 } 714 715 /*---------------------------------------------------------------------------- 716 * TC_StartNote() 717 *---------------------------------------------------------------------------- 718 * Process a note or silence event 719 *---------------------------------------------------------------------------- 720 */ 721 static EAS_RESULT TC_StartNote (S_EAS_DATA *pEASData, S_TC_DATA* pData, EAS_INT parserMode, EAS_I8 note) 722 { 723 EAS_I8 duration; 724 725 /* get the duration */ 726 if (TC_GetNextChar(pEASData->hwInstData, pData, &duration) != EAS_SUCCESS) 727 return EAS_ERROR_FILE_FORMAT; 728 729 /* calculate time of next event */ 730 pData->length = (EAS_I32) duration * pData->tick; 731 pData->time += pData->length; 732 733 /* start the note */ 734 if ((note >= 0) && (parserMode == eParserModePlay)) 735 { 736 VMStartNote(pEASData->pVoiceMgr, pData->pSynth, TC_CHANNEL, (EAS_U8) note, pData->volume); 737 pData->note = note; 738 } 739 740 return EAS_SUCCESS; 741 } 742 743 /*---------------------------------------------------------------------------- 744 * TC_GetRepeat() 745 *---------------------------------------------------------------------------- 746 * Process a repeat code 747 *---------------------------------------------------------------------------- 748 */ 749 static EAS_RESULT TC_GetRepeat (S_EAS_DATA *pEASData, S_TC_DATA* pData, EAS_INT parserMode) 750 { 751 EAS_I8 count; 752 753 /* get the repeat count */ 754 if (TC_GetNextChar(pEASData->hwInstData, pData, &count) != EAS_SUCCESS) 755 return EAS_ERROR_FILE_FORMAT; 756 757 /* validiate it */ 758 if (count < 2) 759 return EAS_ERROR_FILE_FORMAT; 760 761 /* calculate time of next event */ 762 pData->time += pData->length; 763 pData->repeatCount = count - 2; 764 765 /* start the note */ 766 if ((pData->note >= 0) && (parserMode == eParserModePlay)) 767 VMStartNote(pEASData->pVoiceMgr, pData->pSynth, TC_CHANNEL, (EAS_U8) pData->note, pData->volume); 768 769 return EAS_SUCCESS; 770 } 771 772 /*---------------------------------------------------------------------------- 773 * TC_PlayBlock() 774 *---------------------------------------------------------------------------- 775 * Play a block of notes 776 *---------------------------------------------------------------------------- 777 */ 778 static EAS_RESULT TC_PlayBlock (S_EAS_DATA *pEASData, S_TC_DATA* pData) 779 { 780 EAS_RESULT result; 781 EAS_I8 blockNum; 782 EAS_I8 temp; 783 EAS_I8 temp2; 784 785 /* get the block number */ 786 if (TC_GetNextChar(pEASData->hwInstData, pData, &blockNum) != EAS_SUCCESS) 787 return EAS_ERROR_FILE_FORMAT; 788 789 /* validiate it */ 790 if (blockNum < 0) 791 return EAS_ERROR_FILE_FORMAT; 792 793 /* save the current position */ 794 if ((result = EAS_HWFilePos(pEASData->hwInstData, pData->fileHandle, &pData->restorePos)) != EAS_SUCCESS) 795 return result; 796 797 /* return to start of file */ 798 pData->byteAvail = EAS_FALSE; 799 if ((result = EAS_HWFileSeek(pEASData->hwInstData, pData->fileHandle, pData->fileOffset)) != EAS_SUCCESS) 800 return result; 801 802 /* find the block */ 803 for (;;) 804 { 805 if (TC_GetNextChar(pEASData->hwInstData, pData, &temp) != EAS_SUCCESS) 806 return EAS_ERROR_FILE_FORMAT; 807 808 if (TC_GetNextChar(pEASData->hwInstData, pData, &temp2) != EAS_SUCCESS) 809 return EAS_ERROR_FILE_FORMAT; 810 811 if ((temp == TC_FIELD_BLOCK_START) && (temp2 == blockNum)) 812 return EAS_SUCCESS; 813 } 814 } 815 816 /*---------------------------------------------------------------------------- 817 * TC_BlockEnd() 818 *---------------------------------------------------------------------------- 819 * Handle end of block 820 *---------------------------------------------------------------------------- 821 */ 822 static EAS_RESULT TC_BlockEnd (S_EAS_DATA *pEASData, S_TC_DATA* pData) 823 { 824 EAS_I8 blockNum; 825 826 /* get the block number */ 827 if (TC_GetNextChar(pEASData->hwInstData, pData, &blockNum) != EAS_SUCCESS) 828 return EAS_ERROR_FILE_FORMAT; 829 830 /* validiate it */ 831 if (blockNum < 0) 832 return EAS_ERROR_FILE_FORMAT; 833 834 /* if we were playing this block, restore to previous position */ 835 pData->byteAvail = EAS_FALSE; 836 return EAS_HWFileSeek(pEASData->hwInstData, pData->fileHandle, pData->restorePos); 837 } 838 839 /*---------------------------------------------------------------------------- 840 * TC_GetVolume() 841 *---------------------------------------------------------------------------- 842 * Get the volume field and process it 843 *---------------------------------------------------------------------------- 844 */ 845 static EAS_RESULT TC_GetVolume (S_EAS_DATA *pEASData, S_TC_DATA* pData) 846 { 847 EAS_I8 volume; 848 849 /* get volume */ 850 if (TC_GetNextChar(pEASData->hwInstData, pData, &volume) != EAS_SUCCESS) 851 return EAS_ERROR_FILE_FORMAT; 852 if ((volume < 0) || (volume > 100)) 853 return EAS_ERROR_FILE_FORMAT; 854 855 /* save volume */ 856 pData->volume = (EAS_U8) ((EAS_I32) (volume * TC_VOLUME_CONV + 1) >> TC_VOLUME_SHIFT); 857 return EAS_SUCCESS; 858 } 859 860 /*---------------------------------------------------------------------------- 861 * TC_GetTempo() 862 *---------------------------------------------------------------------------- 863 * Get the tempo field and process it 864 *---------------------------------------------------------------------------- 865 */ 866 static EAS_RESULT TC_GetTempo (S_EAS_DATA *pEASData, S_TC_DATA* pData) 867 { 868 EAS_I8 tempo; 869 870 /* get tempo */ 871 if (TC_GetNextChar(pEASData->hwInstData, pData, &tempo) != EAS_SUCCESS) 872 return EAS_ERROR_FILE_FORMAT; 873 if (tempo < 5) 874 return EAS_ERROR_FILE_FORMAT; 875 876 /* save tempo */ 877 pData->tempo = tempo; 878 879 /* calculate new timebase */ 880 TC_CalcTimeBase(pData); 881 return EAS_SUCCESS; 882 } 883 884 /*---------------------------------------------------------------------------- 885 * TC_GetResolution() 886 *---------------------------------------------------------------------------- 887 * Get the resolution field and process it 888 *---------------------------------------------------------------------------- 889 */ 890 static EAS_RESULT TC_GetResolution (S_EAS_DATA *pEASData, S_TC_DATA* pData) 891 { 892 EAS_I8 resolution; 893 894 /* get resolution */ 895 if (TC_GetNextChar(pEASData->hwInstData, pData, &resolution) != EAS_SUCCESS) 896 return EAS_ERROR_FILE_FORMAT; 897 if (resolution < 0) 898 return EAS_ERROR_FILE_FORMAT; 899 900 /* save tempo */ 901 pData->resolution = resolution; 902 903 /* calculate new timebase */ 904 TC_CalcTimeBase(pData); 905 return EAS_SUCCESS; 906 } 907 908 /*---------------------------------------------------------------------------- 909 * TC_GetNextChar() 910 *---------------------------------------------------------------------------- 911 * Fetch the next character from the stream 912 *---------------------------------------------------------------------------- 913 */ 914 static EAS_RESULT TC_GetNextChar (EAS_HW_DATA_HANDLE hwInstData, S_TC_DATA *pData, EAS_I8 *pValue) 915 { 916 917 /* get character from "put back" buffer */ 918 if (pData->byteAvail) 919 { 920 pData->byteAvail = EAS_FALSE; 921 *pValue = pData->dataByte; 922 return EAS_SUCCESS; 923 } 924 925 /* get character from file */ 926 return EAS_HWGetByte(hwInstData, pData->fileHandle, pValue); 927 } 928 929 /*---------------------------------------------------------------------------- 930 * TC_PutBackChar() 931 *---------------------------------------------------------------------------- 932 * Put back the character 933 *---------------------------------------------------------------------------- 934 */ 935 static void TC_PutBackChar (S_TC_DATA *pData, EAS_I8 value) 936 { 937 938 pData->dataByte = value; 939 pData->byteAvail = EAS_TRUE; 940 } 941 942