1/*---------------------------------------------------------------------------- 2 * 3 * File: 4 * eas_smf.c 5 * 6 * Contents and purpose: 7 * SMF Type 0 and 1 File Parser 8 * 9 * For SMF timebase analysis, see "MIDI Sequencer Analysis.xls". 10 * 11 * Copyright Sonic Network Inc. 2005 12 13 * Licensed under the Apache License, Version 2.0 (the "License"); 14 * you may not use this file except in compliance with the License. 15 * You may obtain a copy of the License at 16 * 17 * http://www.apache.org/licenses/LICENSE-2.0 18 * 19 * Unless required by applicable law or agreed to in writing, software 20 * distributed under the License is distributed on an "AS IS" BASIS, 21 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 22 * See the License for the specific language governing permissions and 23 * limitations under the License. 24 * 25 *---------------------------------------------------------------------------- 26 * Revision Control: 27 * $Revision: 803 $ 28 * $Date: 2007-08-01 09:57:00 -0700 (Wed, 01 Aug 2007) $ 29 *---------------------------------------------------------------------------- 30*/ 31 32#include "eas_data.h" 33#include "eas_miditypes.h" 34#include "eas_parser.h" 35#include "eas_report.h" 36#include "eas_host.h" 37#include "eas_midi.h" 38#include "eas_config.h" 39#include "eas_vm_protos.h" 40#include "eas_smfdata.h" 41#include "eas_smf.h" 42 43#ifdef JET_INTERFACE 44#include "jet_data.h" 45#endif 46 47//3 dls: The timebase for this module is adequate to keep MIDI and 48//3 digital audio synchronized for only a few minutes. It should be 49//3 sufficient for most mobile applications. If better accuracy is 50//3 required, more fractional bits should be added to the timebase. 51 52static const EAS_U8 smfHeader[] = { 'M', 'T', 'h', 'd' }; 53 54/* local prototypes */ 55static EAS_RESULT SMF_GetVarLenData (EAS_HW_DATA_HANDLE hwInstData, EAS_FILE_HANDLE fileHandle, EAS_U32 *pData); 56static EAS_RESULT SMF_ParseMetaEvent (S_EAS_DATA *pEASData, S_SMF_DATA *pSMFData, S_SMF_STREAM *pSMFStream); 57static EAS_RESULT SMF_ParseSysEx (S_EAS_DATA *pEASData, S_SMF_DATA *pSMFData, S_SMF_STREAM *pSMFStream, EAS_U8 f0, EAS_INT parserMode); 58static EAS_RESULT SMF_ParseEvent (S_EAS_DATA *pEASData, S_SMF_DATA *pSMFData, S_SMF_STREAM *pSMFStream, EAS_INT parserMode); 59static EAS_RESULT SMF_GetDeltaTime (EAS_HW_DATA_HANDLE hwInstData, S_SMF_STREAM *pSMFStream); 60static void SMF_UpdateTime (S_SMF_DATA *pSMFData, EAS_U32 ticks); 61 62 63/*---------------------------------------------------------------------------- 64 * 65 * SMF_Parser 66 * 67 * This structure contains the functional interface for the SMF parser 68 *---------------------------------------------------------------------------- 69*/ 70const S_FILE_PARSER_INTERFACE EAS_SMF_Parser = 71{ 72 SMF_CheckFileType, 73 SMF_Prepare, 74 SMF_Time, 75 SMF_Event, 76 SMF_State, 77 SMF_Close, 78 SMF_Reset, 79 SMF_Pause, 80 SMF_Resume, 81 NULL, 82 SMF_SetData, 83 SMF_GetData, 84 NULL 85}; 86 87/*---------------------------------------------------------------------------- 88 * SMF_CheckFileType() 89 *---------------------------------------------------------------------------- 90 * Purpose: 91 * Check the file type to see if we can parse it 92 * 93 * Inputs: 94 * pEASData - pointer to overall EAS data structure 95 * handle - pointer to file handle 96 * 97 * Outputs: 98 * 99 * 100 * Side Effects: 101 * 102 *---------------------------------------------------------------------------- 103*/ 104EAS_RESULT SMF_CheckFileType (S_EAS_DATA *pEASData, EAS_FILE_HANDLE fileHandle, EAS_VOID_PTR *ppHandle, EAS_I32 offset) 105{ 106 S_SMF_DATA* pSMFData; 107 EAS_RESULT result; 108 109 /* seek to starting offset - usually 0 */ 110 *ppHandle = NULL; 111 if ((result = EAS_HWFileSeek(pEASData->hwInstData, fileHandle, offset)) != EAS_SUCCESS) 112 return result; 113 114 /* search through file for header - slow method */ 115 if (pEASData->searchHeaderFlag) 116 { 117 result = EAS_SearchFile(pEASData, fileHandle, smfHeader, sizeof(smfHeader), &offset); 118 if (result != EAS_SUCCESS) 119 return (result == EAS_EOF) ? EAS_SUCCESS : result; 120 } 121 122 /* read the first 4 bytes of the file - quick method */ 123 else { 124 EAS_U8 header[4]; 125 EAS_I32 count; 126 if ((result = EAS_HWReadFile(pEASData->hwInstData, fileHandle, header, sizeof(header), &count)) != EAS_SUCCESS) 127 return result; 128 129 /* check for 'MTrk' - return if no match */ 130 if ((header[0] != 'M') || (header[1] != 'T') || (header[2] != 'h') || (header[3] != 'd')) 131 return EAS_SUCCESS; 132 } 133 134 /* check for static memory allocation */ 135 if (pEASData->staticMemoryModel) 136 pSMFData = EAS_CMEnumData(EAS_CM_SMF_DATA); 137 else 138 { 139 pSMFData = EAS_HWMalloc(pEASData->hwInstData, sizeof(S_SMF_DATA)); 140 EAS_HWMemSet((void *)pSMFData,0, sizeof(S_SMF_DATA)); 141 } 142 if (!pSMFData) 143 return EAS_ERROR_MALLOC_FAILED; 144 145 /* initialize some critical data */ 146 pSMFData->fileHandle = fileHandle; 147 pSMFData->fileOffset = offset; 148 pSMFData->pSynth = NULL; 149 pSMFData->time = 0; 150 pSMFData->state = EAS_STATE_OPEN; 151 *ppHandle = pSMFData; 152 153 return EAS_SUCCESS; 154} 155 156/*---------------------------------------------------------------------------- 157 * SMF_Prepare() 158 *---------------------------------------------------------------------------- 159 * Purpose: 160 * Prepare to parse the file. Allocates instance data (or uses static allocation for 161 * static memory model). 162 * 163 * Inputs: 164 * pEASData - pointer to overall EAS data structure 165 * handle - pointer to file handle 166 * 167 * Outputs: 168 * 169 * 170 * Side Effects: 171 * 172 *---------------------------------------------------------------------------- 173*/ 174EAS_RESULT SMF_Prepare (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData) 175{ 176 S_SMF_DATA* pSMFData; 177 EAS_RESULT result; 178 179 /* check for valid state */ 180 pSMFData = (S_SMF_DATA *) pInstData; 181 if (pSMFData->state != EAS_STATE_OPEN) 182 return EAS_ERROR_NOT_VALID_IN_THIS_STATE; 183 184 /* instantiate a synthesizer */ 185 if ((result = VMInitMIDI(pEASData, &pSMFData->pSynth)) != EAS_SUCCESS) 186 { 187 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "VMInitMIDI returned %d\n", result); */ } 188 return result; 189 } 190 191 /* parse the file header and setup the individual stream parsers */ 192 if ((result = SMF_ParseHeader(pEASData->hwInstData, pSMFData)) != EAS_SUCCESS) 193 return result; 194 195 /* ready to play */ 196 pSMFData->state = EAS_STATE_READY; 197 return EAS_SUCCESS; 198} 199 200/*---------------------------------------------------------------------------- 201 * SMF_Time() 202 *---------------------------------------------------------------------------- 203 * Purpose: 204 * Returns the time of the next event in msecs 205 * 206 * Inputs: 207 * pEASData - pointer to overall EAS data structure 208 * handle - pointer to file handle 209 * pTime - pointer to variable to hold time of next event (in msecs) 210 * 211 * Outputs: 212 * 213 * 214 * Side Effects: 215 * 216 *---------------------------------------------------------------------------- 217*/ 218/*lint -esym(715, pEASData) reserved for future use */ 219EAS_RESULT SMF_Time (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_U32 *pTime) 220{ 221 S_SMF_DATA *pSMFData; 222 223 pSMFData = (S_SMF_DATA*) pInstData; 224 225 /* sanity check */ 226#ifdef _CHECKED_BUILD 227 if (pSMFData->state == EAS_STATE_STOPPED) 228 { 229 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "Can't ask for time on a stopped stream\n"); */ } 230 } 231 232 if (pSMFData->nextStream == NULL) 233 { 234 { /* dpp: EAS_ReportEx( _EAS_SEVERITY_ERROR, "no is NULL\n"); */ } 235 } 236#endif 237 238#if 0 239 /* return time in milliseconds */ 240 /* if chase mode, lie about time */ 241 if (pSMFData->flags & SMF_FLAGS_CHASE_MODE) 242 *pTime = 0; 243 244 else 245#endif 246 247 /*lint -e{704} use shift instead of division */ 248 *pTime = pSMFData->time >> 8; 249 250 *pTime = pSMFData->time >> 8; 251 return EAS_SUCCESS; 252} 253 254/*---------------------------------------------------------------------------- 255 * SMF_Event() 256 *---------------------------------------------------------------------------- 257 * Purpose: 258 * Parse the next event in the file 259 * 260 * Inputs: 261 * pEASData - pointer to overall EAS data structure 262 * handle - pointer to file handle 263 * 264 * Outputs: 265 * 266 * 267 * Side Effects: 268 * 269 *---------------------------------------------------------------------------- 270*/ 271EAS_RESULT SMF_Event (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_INT parserMode) 272{ 273 S_SMF_DATA* pSMFData; 274 EAS_RESULT result; 275 EAS_I32 i; 276 EAS_U32 ticks; 277 EAS_U32 temp; 278 279 /* establish pointer to instance data */ 280 pSMFData = (S_SMF_DATA*) pInstData; 281 if (pSMFData->state >= EAS_STATE_OPEN) 282 return EAS_SUCCESS; 283 284 if (!pSMFData->nextStream) { 285 return EAS_ERROR_FILE_FORMAT; 286 } 287 288 289 /* get current ticks */ 290 ticks = pSMFData->nextStream->ticks; 291 292 /* assume that an error occurred */ 293 pSMFData->state = EAS_STATE_ERROR; 294 295#ifdef JET_INTERFACE 296 /* if JET has track muted, set parser mode to mute */ 297 if (pSMFData->nextStream->midiStream.jetData & MIDI_FLAGS_JET_MUTE) 298 parserMode = eParserModeMute; 299#endif 300 301 /* parse the next event from all the streams */ 302 if ((result = SMF_ParseEvent(pEASData, pSMFData, pSMFData->nextStream, parserMode)) != EAS_SUCCESS) 303 { 304 /* check for unexpected end-of-file */ 305 if (result != EAS_EOF) 306 return result; 307 308 /* indicate end of track for this stream */ 309 pSMFData->nextStream->ticks = SMF_END_OF_TRACK; 310 } 311 312 /* get next delta time, unless already at end of track */ 313 else if (pSMFData->nextStream->ticks != SMF_END_OF_TRACK) 314 { 315 if ((result = SMF_GetDeltaTime(pEASData->hwInstData, pSMFData->nextStream)) != EAS_SUCCESS) 316 { 317 /* check for unexpected end-of-file */ 318 if (result != EAS_EOF) 319 return result; 320 321 /* indicate end of track for this stream */ 322 pSMFData->nextStream->ticks = SMF_END_OF_TRACK; 323 } 324 325 /* if zero delta to next event, stay with this stream */ 326 else if (pSMFData->nextStream->ticks == ticks) 327 { 328 pSMFData->state = EAS_STATE_PLAY; 329 return EAS_SUCCESS; 330 } 331 } 332 333 /* find next event in all streams */ 334 temp = 0x7ffffff; 335 pSMFData->nextStream = NULL; 336 for (i = 0; i < pSMFData->numStreams; i++) 337 { 338 if (pSMFData->streams[i].ticks < temp) 339 { 340 temp = pSMFData->streams[i].ticks; 341 pSMFData->nextStream = &pSMFData->streams[i]; 342 } 343 } 344 345 /* are there any more events to parse? */ 346 if (pSMFData->nextStream) 347 { 348 pSMFData->state = EAS_STATE_PLAY; 349 350 /* update the time of the next event */ 351 SMF_UpdateTime(pSMFData, pSMFData->nextStream->ticks - ticks); 352 } 353 else 354 { 355 pSMFData->state = EAS_STATE_STOPPING; 356 VMReleaseAllVoices(pEASData->pVoiceMgr, pSMFData->pSynth); 357 } 358 359 return EAS_SUCCESS; 360} 361 362/*---------------------------------------------------------------------------- 363 * SMF_State() 364 *---------------------------------------------------------------------------- 365 * Purpose: 366 * Returns the current state of the stream 367 * 368 * Inputs: 369 * pEASData - pointer to overall EAS data structure 370 * handle - pointer to file handle 371 * pState - pointer to variable to store state 372 * 373 * Outputs: 374 * 375 * 376 * Side Effects: 377 * 378 *---------------------------------------------------------------------------- 379*/ 380/*lint -esym(715, pEASData) reserved for future use */ 381EAS_RESULT SMF_State (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 *pState) 382{ 383 S_SMF_DATA* pSMFData; 384 385 /* establish pointer to instance data */ 386 pSMFData = (S_SMF_DATA*) pInstData; 387 388 /* if stopping, check to see if synth voices are active */ 389 if (pSMFData->state == EAS_STATE_STOPPING) 390 { 391 if (VMActiveVoices(pSMFData->pSynth) == 0) 392 pSMFData->state = EAS_STATE_STOPPED; 393 } 394 395 if (pSMFData->state == EAS_STATE_PAUSING) 396 { 397 if (VMActiveVoices(pSMFData->pSynth) == 0) 398 pSMFData->state = EAS_STATE_PAUSED; 399 } 400 401 /* return current state */ 402 *pState = pSMFData->state; 403 return EAS_SUCCESS; 404} 405 406/*---------------------------------------------------------------------------- 407 * SMF_Close() 408 *---------------------------------------------------------------------------- 409 * Purpose: 410 * Close the file and clean up 411 * 412 * Inputs: 413 * pEASData - pointer to overall EAS data structure 414 * handle - pointer to file handle 415 * 416 * Outputs: 417 * 418 * 419 * Side Effects: 420 * 421 *---------------------------------------------------------------------------- 422*/ 423EAS_RESULT SMF_Close (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData) 424{ 425 S_SMF_DATA* pSMFData; 426 EAS_I32 i; 427 EAS_RESULT result; 428 429 pSMFData = (S_SMF_DATA*) pInstData; 430 431 /* close all the streams */ 432 for (i = 0; i < pSMFData->numStreams; i++) 433 { 434 if (pSMFData->streams[i].fileHandle != NULL) 435 { 436 if ((result = EAS_HWCloseFile(pEASData->hwInstData, pSMFData->streams[i].fileHandle)) != EAS_SUCCESS) 437 return result; 438 } 439 } 440 if (pSMFData->fileHandle != NULL) 441 if ((result = EAS_HWCloseFile(pEASData->hwInstData, pSMFData->fileHandle)) != EAS_SUCCESS) 442 return result; 443 444 /* free the synth */ 445 if (pSMFData->pSynth != NULL) 446 VMMIDIShutdown(pEASData, pSMFData->pSynth); 447 448 /* if using dynamic memory, free it */ 449 if (!pEASData->staticMemoryModel) 450 { 451 if (pSMFData->streams) 452 EAS_HWFree(pEASData->hwInstData, pSMFData->streams); 453 454 /* free the instance data */ 455 EAS_HWFree(pEASData->hwInstData, pSMFData); 456 } 457 458 return EAS_SUCCESS; 459} 460 461/*---------------------------------------------------------------------------- 462 * SMF_Reset() 463 *---------------------------------------------------------------------------- 464 * Purpose: 465 * Reset the sequencer. Used for locating backwards in the file. 466 * 467 * Inputs: 468 * pEASData - pointer to overall EAS data structure 469 * handle - pointer to file handle 470 * 471 * Outputs: 472 * 473 * 474 * Side Effects: 475 * 476 *---------------------------------------------------------------------------- 477*/ 478EAS_RESULT SMF_Reset (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData) 479{ 480 S_SMF_DATA* pSMFData; 481 EAS_I32 i; 482 EAS_RESULT result; 483 EAS_U32 ticks; 484 485 pSMFData = (S_SMF_DATA*) pInstData; 486 487 /* reset time to zero */ 488 pSMFData->time = 0; 489 490 /* reset the synth */ 491 VMReset(pEASData->pVoiceMgr, pSMFData->pSynth, EAS_TRUE); 492 493 /* find the start of each track */ 494 ticks = 0x7fffffffL; 495 pSMFData->nextStream = NULL; 496 for (i = 0; i < pSMFData->numStreams; i++) 497 { 498 499 /* reset file position to first byte of data in track */ 500 if ((result = EAS_HWFileSeek(pEASData->hwInstData, pSMFData->streams[i].fileHandle, pSMFData->streams[i].startFilePos)) != EAS_SUCCESS) 501 return result; 502 503 /* initalize some data */ 504 pSMFData->streams[i].ticks = 0; 505 506 /* initalize the MIDI parser data */ 507 EAS_InitMIDIStream(&pSMFData->streams[i].midiStream); 508 509 /* parse the first delta time in each stream */ 510 if ((result = SMF_GetDeltaTime(pEASData->hwInstData,&pSMFData->streams[i])) != EAS_SUCCESS) 511 return result; 512 if (pSMFData->streams[i].ticks < ticks) 513 { 514 ticks = pSMFData->streams[i].ticks; 515 pSMFData->nextStream = &pSMFData->streams[i]; 516 } 517 } 518 519 520 pSMFData->state = EAS_STATE_READY; 521 return EAS_SUCCESS; 522} 523 524/*---------------------------------------------------------------------------- 525 * SMF_Pause() 526 *---------------------------------------------------------------------------- 527 * Purpose: 528 * Pauses the sequencer. Mutes all voices and sets state to pause. 529 * 530 * Inputs: 531 * pEASData - pointer to overall EAS data structure 532 * handle - pointer to file handle 533 * 534 * Outputs: 535 * 536 * 537 * Side Effects: 538 * 539 *---------------------------------------------------------------------------- 540*/ 541EAS_RESULT SMF_Pause (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData) 542{ 543 S_SMF_DATA *pSMFData; 544 545 /* can't pause a stopped stream */ 546 pSMFData = (S_SMF_DATA*) pInstData; 547 if (pSMFData->state == EAS_STATE_STOPPED) 548 return EAS_ERROR_ALREADY_STOPPED; 549 550 /* mute the synthesizer */ 551 VMMuteAllVoices(pEASData->pVoiceMgr, pSMFData->pSynth); 552 pSMFData->state = EAS_STATE_PAUSING; 553 return EAS_SUCCESS; 554} 555 556/*---------------------------------------------------------------------------- 557 * SMF_Resume() 558 *---------------------------------------------------------------------------- 559 * Purpose: 560 * Resume playing after a pause, sets state back to playing. 561 * 562 * Inputs: 563 * pEASData - pointer to overall EAS data structure 564 * handle - pointer to file handle 565 * 566 * Outputs: 567 * 568 * 569 * Side Effects: 570 * 571 *---------------------------------------------------------------------------- 572*/ 573/*lint -esym(715, pEASData) reserved for future use */ 574EAS_RESULT SMF_Resume (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData) 575{ 576 S_SMF_DATA *pSMFData; 577 578 /* can't resume a stopped stream */ 579 pSMFData = (S_SMF_DATA*) pInstData; 580 if (pSMFData->state == EAS_STATE_STOPPED) 581 return EAS_ERROR_ALREADY_STOPPED; 582 583 /* nothing to do but resume playback */ 584 pSMFData->state = EAS_STATE_PLAY; 585 return EAS_SUCCESS; 586} 587 588/*---------------------------------------------------------------------------- 589 * SMF_SetData() 590 *---------------------------------------------------------------------------- 591 * Purpose: 592 * Sets parser parameters 593 * 594 * Inputs: 595 * pEASData - pointer to overall EAS data structure 596 * handle - pointer to file handle 597 * 598 * Outputs: 599 * 600 * 601 * Side Effects: 602 * 603 *---------------------------------------------------------------------------- 604*/ 605/*lint -esym(715, pEASData) reserved for future use */ 606EAS_RESULT SMF_SetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 value) 607{ 608 S_SMF_DATA *pSMFData; 609 610 pSMFData = (S_SMF_DATA*) pInstData; 611 switch (param) 612 { 613 614 /* set metadata callback */ 615 case PARSER_DATA_METADATA_CB: 616 EAS_HWMemCpy(&pSMFData->metadata, (void*) value, sizeof(S_METADATA_CB)); 617 break; 618 619#ifdef JET_INTERFACE 620 /* set jet segment and track ID of all tracks for callback function */ 621 case PARSER_DATA_JET_CB: 622 { 623 EAS_U32 i; 624 EAS_U32 bit = (EAS_U32) value; 625 bit = (bit << JET_EVENT_SEG_SHIFT) & JET_EVENT_SEG_MASK; 626 for (i = 0; i < pSMFData->numStreams; i++) 627 pSMFData->streams[i].midiStream.jetData = 628 (pSMFData->streams[i].midiStream.jetData & 629 ~(JET_EVENT_TRACK_MASK | JET_EVENT_SEG_MASK)) | 630 i << JET_EVENT_TRACK_SHIFT | bit | MIDI_FLAGS_JET_CB; 631 pSMFData->flags |= SMF_FLAGS_JET_STREAM; 632 } 633 break; 634 635 /* set state of all mute flags at once */ 636 case PARSER_DATA_MUTE_FLAGS: 637 { 638 EAS_INT i; 639 EAS_U32 bit = (EAS_U32) value; 640 for (i = 0; i < pSMFData->numStreams; i++) 641 { 642 if (bit & 1) 643 pSMFData->streams[i].midiStream.jetData |= MIDI_FLAGS_JET_MUTE; 644 else 645 pSMFData->streams[i].midiStream.jetData &= ~MIDI_FLAGS_JET_MUTE; 646 bit >>= 1; 647 } 648 } 649 break; 650 651 /* set track mute */ 652 case PARSER_DATA_SET_MUTE: 653 if (value < pSMFData->numStreams) 654 pSMFData->streams[value].midiStream.jetData |= MIDI_FLAGS_JET_MUTE; 655 else 656 return EAS_ERROR_PARAMETER_RANGE; 657 break; 658 659 /* clear track mute */ 660 case PARSER_DATA_CLEAR_MUTE: 661 if (value < pSMFData->numStreams) 662 pSMFData->streams[value].midiStream.jetData &= ~MIDI_FLAGS_JET_MUTE; 663 else 664 return EAS_ERROR_PARAMETER_RANGE; 665 break; 666#endif 667 668 default: 669 return EAS_ERROR_INVALID_PARAMETER; 670 } 671 672 return EAS_SUCCESS; 673} 674 675/*---------------------------------------------------------------------------- 676 * SMF_GetData() 677 *---------------------------------------------------------------------------- 678 * Purpose: 679 * Retrieves parser parameters 680 * 681 * Inputs: 682 * pEASData - pointer to overall EAS data structure 683 * handle - pointer to file handle 684 * 685 * Outputs: 686 * 687 * 688 * Side Effects: 689 * 690 *---------------------------------------------------------------------------- 691*/ 692/*lint -esym(715, pEASData) reserved for future use */ 693EAS_RESULT SMF_GetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 *pValue) 694{ 695 S_SMF_DATA *pSMFData; 696 697 pSMFData = (S_SMF_DATA*) pInstData; 698 switch (param) 699 { 700 /* return file type */ 701 case PARSER_DATA_FILE_TYPE: 702 if (pSMFData->numStreams == 1) 703 *pValue = EAS_FILE_SMF0; 704 else 705 *pValue = EAS_FILE_SMF1; 706 break; 707 708/* now handled in eas_public.c */ 709#if 0 710 case PARSER_DATA_POLYPHONY: 711 if (pSMFData->pSynth) 712 VMGetPolyphony(pEASData->pVoiceMgr, pSMFData->pSynth, pValue); 713 else 714 return EAS_ERROR_NOT_VALID_IN_THIS_STATE; 715 break; 716 717 case PARSER_DATA_PRIORITY: 718 if (pSMFData->pSynth) 719 VMGetPriority(pEASData->pVoiceMgr, pSMFData->pSynth, pValue); 720 break; 721 722 /* set transposition */ 723 case PARSER_DATA_TRANSPOSITION: 724 *pValue = pSMFData->transposition; 725 break; 726#endif 727 728 case PARSER_DATA_SYNTH_HANDLE: 729 *pValue = (EAS_I32) pSMFData->pSynth; 730 break; 731 732 default: 733 return EAS_ERROR_INVALID_PARAMETER; 734 } 735 736 return EAS_SUCCESS; 737} 738 739/*---------------------------------------------------------------------------- 740 * SMF_GetVarLenData() 741 *---------------------------------------------------------------------------- 742 * Purpose: 743 * Reads a varible length quantity from an SMF file 744 * 745 * Inputs: 746 * 747 * 748 * Outputs: 749 * 750 * 751 * Side Effects: 752 * 753 *---------------------------------------------------------------------------- 754*/ 755static EAS_RESULT SMF_GetVarLenData (EAS_HW_DATA_HANDLE hwInstData, EAS_FILE_HANDLE fileHandle, EAS_U32 *pData) 756{ 757 EAS_RESULT result; 758 EAS_U32 data; 759 EAS_U8 c; 760 761 /* read until bit 7 is zero */ 762 data = 0; 763 do 764 { 765 if ((result = EAS_HWGetByte(hwInstData, fileHandle,&c)) != EAS_SUCCESS) 766 return result; 767 data = (data << 7) | (c & 0x7f); 768 } while (c & 0x80); 769 *pData = data; 770 return EAS_SUCCESS; 771} 772 773/*---------------------------------------------------------------------------- 774 * SMF_GetDeltaTime() 775 *---------------------------------------------------------------------------- 776 * Purpose: 777 * Reads a varible length quantity from an SMF file 778 * 779 * Inputs: 780 * 781 * 782 * Outputs: 783 * 784 * 785 * Side Effects: 786 * 787 *---------------------------------------------------------------------------- 788*/ 789static EAS_RESULT SMF_GetDeltaTime (EAS_HW_DATA_HANDLE hwInstData, S_SMF_STREAM *pSMFStream) 790{ 791 EAS_RESULT result; 792 EAS_U32 ticks; 793 794 if ((result = SMF_GetVarLenData(hwInstData, pSMFStream->fileHandle, &ticks)) != EAS_SUCCESS) 795 return result; 796 797 pSMFStream->ticks += ticks; 798 return EAS_SUCCESS; 799} 800 801/*---------------------------------------------------------------------------- 802 * SMF_ParseMetaEvent() 803 *---------------------------------------------------------------------------- 804 * Purpose: 805 * Reads a varible length quantity from an SMF file 806 * 807 * Inputs: 808 * 809 * 810 * Outputs: 811 * 812 * 813 * Side Effects: 814 * 815 *---------------------------------------------------------------------------- 816*/ 817static EAS_RESULT SMF_ParseMetaEvent (S_EAS_DATA *pEASData, S_SMF_DATA *pSMFData, S_SMF_STREAM *pSMFStream) 818{ 819 EAS_RESULT result; 820 EAS_U32 len; 821 EAS_I32 pos; 822 EAS_U32 temp; 823 EAS_U8 c; 824 825 /* get the meta-event type */ 826 if ((result = EAS_HWGetByte(pEASData->hwInstData, pSMFStream->fileHandle, &c)) != EAS_SUCCESS) 827 return result; 828 829 /* get the length */ 830 if ((result = SMF_GetVarLenData(pEASData->hwInstData, pSMFStream->fileHandle, &len)) != EAS_SUCCESS) 831 return result; 832 833 /* get the current file position so we can skip the event */ 834 if ((result = EAS_HWFilePos(pEASData->hwInstData, pSMFStream->fileHandle, &pos)) != EAS_SUCCESS) 835 return result; 836 pos += (EAS_I32) len; 837 838 /* end of track? */ 839 if (c == SMF_META_END_OF_TRACK) 840 { 841 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "Meta-event: end of track\n", c, len); */ } 842 pSMFStream->ticks = SMF_END_OF_TRACK; 843 } 844 845 /* tempo event? */ 846 else if (c == SMF_META_TEMPO) 847 { 848 /* read the 3-byte timebase value */ 849 temp = 0; 850 while (len--) 851 { 852 if ((result = EAS_HWGetByte(pEASData->hwInstData, pSMFStream->fileHandle, &c)) != EAS_SUCCESS) 853 return result; 854 temp = (temp << 8) | c; 855 } 856 857 pSMFData->tickConv = (EAS_U16) (((temp * 1024) / pSMFData->ppqn + 500) / 1000); 858 pSMFData->flags |= SMF_FLAGS_HAS_TEMPO; 859 } 860 861 /* check for time signature - see iMelody spec V1.4 section 4.1.2.2.3.6 */ 862 else if (c == SMF_META_TIME_SIGNATURE) 863 { 864 pSMFData->flags |= SMF_FLAGS_HAS_TIME_SIG; 865 } 866 867 /* if the host has registered a metadata callback return the metadata */ 868 else if (pSMFData->metadata.callback) 869 { 870 EAS_I32 readLen; 871 E_EAS_METADATA_TYPE metaType; 872 873 metaType = EAS_METADATA_UNKNOWN; 874 875 /* only process title on the first track */ 876 if (c == SMF_META_SEQTRK_NAME) 877 metaType = EAS_METADATA_TITLE; 878 else if (c == SMF_META_TEXT) 879 metaType = EAS_METADATA_TEXT; 880 else if (c == SMF_META_COPYRIGHT) 881 metaType = EAS_METADATA_COPYRIGHT; 882 else if (c == SMF_META_LYRIC) 883 metaType = EAS_METADATA_LYRIC; 884 885 if (metaType != EAS_METADATA_UNKNOWN) 886 { 887 readLen = pSMFData->metadata.bufferSize - 1; 888 if ((EAS_I32) len < readLen) 889 readLen = (EAS_I32) len; 890 if ((result = EAS_HWReadFile(pEASData->hwInstData, pSMFStream->fileHandle, pSMFData->metadata.buffer, readLen, &readLen)) != EAS_SUCCESS) 891 return result; 892 pSMFData->metadata.buffer[readLen] = 0; 893 pSMFData->metadata.callback(metaType, pSMFData->metadata.buffer, pSMFData->metadata.pUserData); 894 } 895 } 896 897 /* position file to next event - in case we ignored all or part of the meta-event */ 898 if ((result = EAS_HWFileSeek(pEASData->hwInstData, pSMFStream->fileHandle, pos)) != EAS_SUCCESS) 899 return result; 900 901 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "Meta-event: type=%02x, len=%d\n", c, len); */ } 902 return EAS_SUCCESS; 903} 904 905/*---------------------------------------------------------------------------- 906 * SMF_ParseSysEx() 907 *---------------------------------------------------------------------------- 908 * Purpose: 909 * Reads a varible length quantity from an SMF file 910 * 911 * Inputs: 912 * 913 * 914 * Outputs: 915 * 916 * 917 * Side Effects: 918 * 919 *---------------------------------------------------------------------------- 920*/ 921static EAS_RESULT SMF_ParseSysEx (S_EAS_DATA *pEASData, S_SMF_DATA *pSMFData, S_SMF_STREAM *pSMFStream, EAS_U8 f0, EAS_INT parserMode) 922{ 923 EAS_RESULT result; 924 EAS_U32 len; 925 EAS_U8 c; 926 927 /* get the length */ 928 if ((result = SMF_GetVarLenData(pEASData->hwInstData, pSMFStream->fileHandle, &len)) != EAS_SUCCESS) 929 return result; 930 931 /* start of SysEx message? */ 932 if (f0 == 0xf0) 933 { 934 if ((result = EAS_ParseMIDIStream(pEASData, pSMFData->pSynth, &pSMFStream->midiStream, f0, parserMode)) != EAS_SUCCESS) 935 return result; 936 } 937 938 /* feed the SysEx to the stream parser */ 939 while (len--) 940 { 941 if ((result = EAS_HWGetByte(pEASData->hwInstData, pSMFStream->fileHandle, &c)) != EAS_SUCCESS) 942 return result; 943 if ((result = EAS_ParseMIDIStream(pEASData, pSMFData->pSynth, &pSMFStream->midiStream, c, parserMode)) != EAS_SUCCESS) 944 return result; 945 946 /* check for GM system ON */ 947 if (pSMFStream->midiStream.flags & MIDI_FLAG_GM_ON) 948 pSMFData->flags |= SMF_FLAGS_HAS_GM_ON; 949 } 950 951 return EAS_SUCCESS; 952} 953 954/*---------------------------------------------------------------------------- 955 * SMF_ParseEvent() 956 *---------------------------------------------------------------------------- 957 * Purpose: 958 * Reads a varible length quantity from an SMF file 959 * 960 * Inputs: 961 * 962 * 963 * Outputs: 964 * 965 * 966 * Side Effects: 967 * 968 *---------------------------------------------------------------------------- 969*/ 970static EAS_RESULT SMF_ParseEvent (S_EAS_DATA *pEASData, S_SMF_DATA *pSMFData, S_SMF_STREAM *pSMFStream, EAS_INT parserMode) 971{ 972 EAS_RESULT result; 973 EAS_U8 c; 974 975 /* get the event type */ 976 if ((result = EAS_HWGetByte(pEASData->hwInstData, pSMFStream->fileHandle, &c)) != EAS_SUCCESS) 977 return result; 978 979 /* parse meta-event */ 980 if (c == 0xff) 981 { 982 if ((result = SMF_ParseMetaEvent(pEASData, pSMFData, pSMFStream)) != EAS_SUCCESS) 983 return result; 984 } 985 986 /* parse SysEx */ 987 else if ((c == 0xf0) || (c == 0xf7)) 988 { 989 if ((result = SMF_ParseSysEx(pEASData, pSMFData, pSMFStream, c, parserMode)) != EAS_SUCCESS) 990 return result; 991 } 992 993 /* parse MIDI message */ 994 else 995 { 996 if ((result = EAS_ParseMIDIStream(pEASData, pSMFData->pSynth, &pSMFStream->midiStream, c, parserMode)) != EAS_SUCCESS) 997 return result; 998 999 /* keep streaming data to the MIDI parser until the message is complete */ 1000 while (pSMFStream->midiStream.pending) 1001 { 1002 if ((result = EAS_HWGetByte(pEASData->hwInstData, pSMFStream->fileHandle, &c)) != EAS_SUCCESS) 1003 return result; 1004 if ((result = EAS_ParseMIDIStream(pEASData, pSMFData->pSynth, &pSMFStream->midiStream, c, parserMode)) != EAS_SUCCESS) 1005 return result; 1006 } 1007 1008 } 1009 1010 /* chase mode logic */ 1011 if (pSMFData->time == 0) 1012 { 1013 if (pSMFData->flags & SMF_FLAGS_CHASE_MODE) 1014 { 1015 if (pSMFStream->midiStream.flags & MIDI_FLAG_FIRST_NOTE) 1016 pSMFData->flags &= ~SMF_FLAGS_CHASE_MODE; 1017 } 1018 else if ((pSMFData->flags & SMF_FLAGS_SETUP_BAR) == SMF_FLAGS_SETUP_BAR) 1019 pSMFData->flags = (pSMFData->flags & ~SMF_FLAGS_SETUP_BAR) | SMF_FLAGS_CHASE_MODE; 1020 } 1021 1022 return EAS_SUCCESS; 1023} 1024 1025/*---------------------------------------------------------------------------- 1026 * SMF_ParseHeader() 1027 *---------------------------------------------------------------------------- 1028 * Purpose: 1029 * Parses the header of an SMF file, allocates memory the stream parsers and initializes the 1030 * stream parsers. 1031 * 1032 * Inputs: 1033 * pEASData - pointer to overall EAS data structure 1034 * pSMFData - pointer to parser instance data 1035 * fileHandle - file handle 1036 * fileOffset - offset in the file where the header data starts, usually 0 1037 * 1038 * 1039 * Outputs: 1040 * 1041 * 1042 * Side Effects: 1043 * 1044 *---------------------------------------------------------------------------- 1045*/ 1046/*lint -e{801} we know that 'goto' is deprecated - but it's cleaner in this case */ 1047EAS_RESULT SMF_ParseHeader (EAS_HW_DATA_HANDLE hwInstData, S_SMF_DATA *pSMFData) 1048{ 1049 EAS_RESULT result; 1050 EAS_I32 i; 1051 EAS_U16 division; 1052 EAS_U32 chunkSize; 1053 EAS_U32 chunkStart; 1054 EAS_U32 temp; 1055 EAS_U32 ticks; 1056 1057 /* rewind the file and find the end of the header chunk */ 1058 if ((result = EAS_HWFileSeek(hwInstData, pSMFData->fileHandle, pSMFData->fileOffset + SMF_OFS_HEADER_SIZE)) != EAS_SUCCESS) 1059 goto ReadError; 1060 if ((result = EAS_HWGetDWord(hwInstData, pSMFData->fileHandle, &chunkSize, EAS_TRUE)) != EAS_SUCCESS) 1061 goto ReadError; 1062 1063 /* determine the number of tracks */ 1064 if ((result = EAS_HWFileSeek(hwInstData, pSMFData->fileHandle, pSMFData->fileOffset + SMF_OFS_NUM_TRACKS)) != EAS_SUCCESS) 1065 goto ReadError; 1066 if ((result = EAS_HWGetWord(hwInstData, pSMFData->fileHandle, &pSMFData->numStreams, EAS_TRUE)) != EAS_SUCCESS) 1067 goto ReadError; 1068 1069 /* limit the number of tracks */ 1070 if (pSMFData->numStreams > MAX_SMF_STREAMS) 1071 { 1072 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "SMF file contains %u tracks, playing %d tracks\n", pSMFData->numStreams, MAX_SMF_STREAMS); */ } 1073 pSMFData->numStreams = MAX_SMF_STREAMS; 1074 } else if (pSMFData->numStreams == 0) 1075 { 1076 /* avoid 0 sized allocation */ 1077 return EAS_ERROR_PARAMETER_RANGE; 1078 } 1079 1080 /* get the time division */ 1081 if ((result = EAS_HWGetWord(hwInstData, pSMFData->fileHandle, &division, EAS_TRUE)) != EAS_SUCCESS) 1082 goto ReadError; 1083 1084 /* setup default timebase for 120 bpm */ 1085 pSMFData->ppqn = 192; 1086 if (!division || division & 0x8000) 1087 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING,"No support for SMPTE code timebase\n"); */ } 1088 else 1089 pSMFData->ppqn = (division & 0x7fff); 1090 pSMFData->tickConv = (EAS_U16) (((SMF_DEFAULT_TIMEBASE * 1024) / pSMFData->ppqn + 500) / 1000); 1091 1092 /* dynamic memory allocation, allocate memory for streams */ 1093 if (pSMFData->streams == NULL) 1094 { 1095 pSMFData->streams = EAS_HWMalloc(hwInstData,sizeof(S_SMF_STREAM) * pSMFData->numStreams); 1096 if (pSMFData->streams == NULL) 1097 return EAS_ERROR_MALLOC_FAILED; 1098 1099 /* zero the memory to insure complete initialization */ 1100 EAS_HWMemSet((void *)(pSMFData->streams), 0, sizeof(S_SMF_STREAM) * pSMFData->numStreams); 1101 } 1102 1103 /* find the start of each track */ 1104 chunkStart = (EAS_U32) pSMFData->fileOffset; 1105 ticks = 0x7fffffffL; 1106 pSMFData->nextStream = NULL; 1107 for (i = 0; i < pSMFData->numStreams; i++) 1108 { 1109 1110 for (;;) 1111 { 1112 1113 /* calculate start of next chunk - checking for errors */ 1114 temp = chunkStart + SMF_CHUNK_INFO_SIZE + chunkSize; 1115 if (temp <= chunkStart) 1116 { 1117 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING,"Error in chunk size at offset %d\n", chunkStart); */ } 1118 return EAS_ERROR_FILE_FORMAT; 1119 } 1120 chunkStart = temp; 1121 1122 /* seek to the start of the next chunk */ 1123 if ((result = EAS_HWFileSeek(hwInstData, pSMFData->fileHandle, (EAS_I32) chunkStart)) != EAS_SUCCESS) 1124 goto ReadError; 1125 1126 /* read the chunk identifier */ 1127 if ((result = EAS_HWGetDWord(hwInstData, pSMFData->fileHandle, &temp, EAS_TRUE)) != EAS_SUCCESS) 1128 goto ReadError; 1129 1130 /* read the chunk size */ 1131 if ((result = EAS_HWGetDWord(hwInstData, pSMFData->fileHandle, &chunkSize, EAS_TRUE)) != EAS_SUCCESS) 1132 goto ReadError; 1133 1134 /* make sure this is an 'MTrk' chunk */ 1135 if (temp == SMF_CHUNK_TYPE_TRACK) 1136 break; 1137 1138 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING,"Unexpected chunk type: 0x%08x\n", temp); */ } 1139 } 1140 1141 /* initalize some data */ 1142 pSMFData->streams[i].ticks = 0; 1143 pSMFData->streams[i].fileHandle = pSMFData->fileHandle; 1144 1145 /* NULL the file handle so we don't try to close it twice */ 1146 pSMFData->fileHandle = NULL; 1147 1148 /* save this file position as the start of the track */ 1149 pSMFData->streams[i].startFilePos = (EAS_I32) chunkStart + SMF_CHUNK_INFO_SIZE; 1150 1151 /* initalize the MIDI parser data */ 1152 EAS_InitMIDIStream(&pSMFData->streams[i].midiStream); 1153 1154 /* parse the first delta time in each stream */ 1155 if ((result = SMF_GetDeltaTime(hwInstData, &pSMFData->streams[i])) != EAS_SUCCESS) 1156 goto ReadError; 1157 1158 if (pSMFData->streams[i].ticks < ticks) 1159 { 1160 ticks = pSMFData->streams[i].ticks; 1161 pSMFData->nextStream = &pSMFData->streams[i]; 1162 } 1163 1164 /* more tracks to do, create a duplicate file handle */ 1165 if (i < (pSMFData->numStreams - 1)) 1166 { 1167 if ((result = EAS_HWDupHandle(hwInstData, pSMFData->streams[i].fileHandle, &pSMFData->fileHandle)) != EAS_SUCCESS) 1168 goto ReadError; 1169 } 1170 } 1171 1172 /* update the time of the next event */ 1173 if (pSMFData->nextStream) 1174 SMF_UpdateTime(pSMFData, pSMFData->nextStream->ticks); 1175 1176 return EAS_SUCCESS; 1177 1178 /* ugly goto: but simpler than structured */ 1179 ReadError: 1180 if (result == EAS_EOF) 1181 return EAS_ERROR_FILE_FORMAT; 1182 return result; 1183} 1184 1185/*---------------------------------------------------------------------------- 1186 * SMF_UpdateTime() 1187 *---------------------------------------------------------------------------- 1188 * Purpose: 1189 * Update the millisecond time base by converting the ticks into millieconds 1190 * 1191 * Inputs: 1192 * 1193 * 1194 * Outputs: 1195 * 1196 * 1197 * Side Effects: 1198 * 1199 *---------------------------------------------------------------------------- 1200*/ 1201static void SMF_UpdateTime (S_SMF_DATA *pSMFData, EAS_U32 ticks) 1202{ 1203 EAS_U32 temp1, temp2; 1204 1205 if (pSMFData->flags & SMF_FLAGS_CHASE_MODE) 1206 return; 1207 1208 temp1 = (ticks >> 10) * pSMFData->tickConv; 1209 temp2 = (ticks & 0x3ff) * pSMFData->tickConv; 1210 pSMFData->time += (EAS_I32)((temp1 << 8) + (temp2 >> 2)); 1211} 1212 1213