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 /* get current ticks */ 285 ticks = pSMFData->nextStream->ticks; 286 287 /* assume that an error occurred */ 288 pSMFData->state = EAS_STATE_ERROR; 289 290#ifdef JET_INTERFACE 291 /* if JET has track muted, set parser mode to mute */ 292 if (pSMFData->nextStream->midiStream.jetData & MIDI_FLAGS_JET_MUTE) 293 parserMode = eParserModeMute; 294#endif 295 296 /* parse the next event from all the streams */ 297 if ((result = SMF_ParseEvent(pEASData, pSMFData, pSMFData->nextStream, parserMode)) != EAS_SUCCESS) 298 { 299 /* check for unexpected end-of-file */ 300 if (result != EAS_EOF) 301 return result; 302 303 /* indicate end of track for this stream */ 304 pSMFData->nextStream->ticks = SMF_END_OF_TRACK; 305 } 306 307 /* get next delta time, unless already at end of track */ 308 else if (pSMFData->nextStream->ticks != SMF_END_OF_TRACK) 309 { 310 if ((result = SMF_GetDeltaTime(pEASData->hwInstData, pSMFData->nextStream)) != EAS_SUCCESS) 311 { 312 /* check for unexpected end-of-file */ 313 if (result != EAS_EOF) 314 return result; 315 316 /* indicate end of track for this stream */ 317 pSMFData->nextStream->ticks = SMF_END_OF_TRACK; 318 } 319 320 /* if zero delta to next event, stay with this stream */ 321 else if (pSMFData->nextStream->ticks == ticks) 322 { 323 pSMFData->state = EAS_STATE_PLAY; 324 return EAS_SUCCESS; 325 } 326 } 327 328 /* find next event in all streams */ 329 temp = 0x7ffffff; 330 pSMFData->nextStream = NULL; 331 for (i = 0; i < pSMFData->numStreams; i++) 332 { 333 if (pSMFData->streams[i].ticks < temp) 334 { 335 temp = pSMFData->streams[i].ticks; 336 pSMFData->nextStream = &pSMFData->streams[i]; 337 } 338 } 339 340 /* are there any more events to parse? */ 341 if (pSMFData->nextStream) 342 { 343 pSMFData->state = EAS_STATE_PLAY; 344 345 /* update the time of the next event */ 346 SMF_UpdateTime(pSMFData, pSMFData->nextStream->ticks - ticks); 347 } 348 else 349 { 350 pSMFData->state = EAS_STATE_STOPPING; 351 VMReleaseAllVoices(pEASData->pVoiceMgr, pSMFData->pSynth); 352 } 353 354 return EAS_SUCCESS; 355} 356 357/*---------------------------------------------------------------------------- 358 * SMF_State() 359 *---------------------------------------------------------------------------- 360 * Purpose: 361 * Returns the current state of the stream 362 * 363 * Inputs: 364 * pEASData - pointer to overall EAS data structure 365 * handle - pointer to file handle 366 * pState - pointer to variable to store state 367 * 368 * Outputs: 369 * 370 * 371 * Side Effects: 372 * 373 *---------------------------------------------------------------------------- 374*/ 375/*lint -esym(715, pEASData) reserved for future use */ 376EAS_RESULT SMF_State (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 *pState) 377{ 378 S_SMF_DATA* pSMFData; 379 380 /* establish pointer to instance data */ 381 pSMFData = (S_SMF_DATA*) pInstData; 382 383 /* if stopping, check to see if synth voices are active */ 384 if (pSMFData->state == EAS_STATE_STOPPING) 385 { 386 if (VMActiveVoices(pSMFData->pSynth) == 0) 387 pSMFData->state = EAS_STATE_STOPPED; 388 } 389 390 if (pSMFData->state == EAS_STATE_PAUSING) 391 { 392 if (VMActiveVoices(pSMFData->pSynth) == 0) 393 pSMFData->state = EAS_STATE_PAUSED; 394 } 395 396 /* return current state */ 397 *pState = pSMFData->state; 398 return EAS_SUCCESS; 399} 400 401/*---------------------------------------------------------------------------- 402 * SMF_Close() 403 *---------------------------------------------------------------------------- 404 * Purpose: 405 * Close the file and clean up 406 * 407 * Inputs: 408 * pEASData - pointer to overall EAS data structure 409 * handle - pointer to file handle 410 * 411 * Outputs: 412 * 413 * 414 * Side Effects: 415 * 416 *---------------------------------------------------------------------------- 417*/ 418EAS_RESULT SMF_Close (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData) 419{ 420 S_SMF_DATA* pSMFData; 421 EAS_I32 i; 422 EAS_RESULT result; 423 424 pSMFData = (S_SMF_DATA*) pInstData; 425 426 /* close all the streams */ 427 for (i = 0; i < pSMFData->numStreams; i++) 428 { 429 if (pSMFData->streams[i].fileHandle != NULL) 430 { 431 if ((result = EAS_HWCloseFile(pEASData->hwInstData, pSMFData->streams[i].fileHandle)) != EAS_SUCCESS) 432 return result; 433 } 434 } 435 if (pSMFData->fileHandle != NULL) 436 if ((result = EAS_HWCloseFile(pEASData->hwInstData, pSMFData->fileHandle)) != EAS_SUCCESS) 437 return result; 438 439 /* free the synth */ 440 if (pSMFData->pSynth != NULL) 441 VMMIDIShutdown(pEASData, pSMFData->pSynth); 442 443 /* if using dynamic memory, free it */ 444 if (!pEASData->staticMemoryModel) 445 { 446 if (pSMFData->streams) 447 EAS_HWFree(pEASData->hwInstData, pSMFData->streams); 448 449 /* free the instance data */ 450 EAS_HWFree(pEASData->hwInstData, pSMFData); 451 } 452 453 return EAS_SUCCESS; 454} 455 456/*---------------------------------------------------------------------------- 457 * SMF_Reset() 458 *---------------------------------------------------------------------------- 459 * Purpose: 460 * Reset the sequencer. Used for locating backwards in the file. 461 * 462 * Inputs: 463 * pEASData - pointer to overall EAS data structure 464 * handle - pointer to file handle 465 * 466 * Outputs: 467 * 468 * 469 * Side Effects: 470 * 471 *---------------------------------------------------------------------------- 472*/ 473EAS_RESULT SMF_Reset (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData) 474{ 475 S_SMF_DATA* pSMFData; 476 EAS_I32 i; 477 EAS_RESULT result; 478 EAS_U32 ticks; 479 480 pSMFData = (S_SMF_DATA*) pInstData; 481 482 /* reset time to zero */ 483 pSMFData->time = 0; 484 485 /* reset the synth */ 486 VMReset(pEASData->pVoiceMgr, pSMFData->pSynth, EAS_TRUE); 487 488 /* find the start of each track */ 489 ticks = 0x7fffffffL; 490 pSMFData->nextStream = NULL; 491 for (i = 0; i < pSMFData->numStreams; i++) 492 { 493 494 /* reset file position to first byte of data in track */ 495 if ((result = EAS_HWFileSeek(pEASData->hwInstData, pSMFData->streams[i].fileHandle, pSMFData->streams[i].startFilePos)) != EAS_SUCCESS) 496 return result; 497 498 /* initalize some data */ 499 pSMFData->streams[i].ticks = 0; 500 501 /* initalize the MIDI parser data */ 502 EAS_InitMIDIStream(&pSMFData->streams[i].midiStream); 503 504 /* parse the first delta time in each stream */ 505 if ((result = SMF_GetDeltaTime(pEASData->hwInstData,&pSMFData->streams[i])) != EAS_SUCCESS) 506 return result; 507 if (pSMFData->streams[i].ticks < ticks) 508 { 509 ticks = pSMFData->streams[i].ticks; 510 pSMFData->nextStream = &pSMFData->streams[i]; 511 } 512 } 513 514 515 pSMFData->state = EAS_STATE_READY; 516 return EAS_SUCCESS; 517} 518 519/*---------------------------------------------------------------------------- 520 * SMF_Pause() 521 *---------------------------------------------------------------------------- 522 * Purpose: 523 * Pauses the sequencer. Mutes all voices and sets state to pause. 524 * 525 * Inputs: 526 * pEASData - pointer to overall EAS data structure 527 * handle - pointer to file handle 528 * 529 * Outputs: 530 * 531 * 532 * Side Effects: 533 * 534 *---------------------------------------------------------------------------- 535*/ 536EAS_RESULT SMF_Pause (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData) 537{ 538 S_SMF_DATA *pSMFData; 539 540 /* can't pause a stopped stream */ 541 pSMFData = (S_SMF_DATA*) pInstData; 542 if (pSMFData->state == EAS_STATE_STOPPED) 543 return EAS_ERROR_ALREADY_STOPPED; 544 545 /* mute the synthesizer */ 546 VMMuteAllVoices(pEASData->pVoiceMgr, pSMFData->pSynth); 547 pSMFData->state = EAS_STATE_PAUSING; 548 return EAS_SUCCESS; 549} 550 551/*---------------------------------------------------------------------------- 552 * SMF_Resume() 553 *---------------------------------------------------------------------------- 554 * Purpose: 555 * Resume playing after a pause, sets state back to playing. 556 * 557 * Inputs: 558 * pEASData - pointer to overall EAS data structure 559 * handle - pointer to file handle 560 * 561 * Outputs: 562 * 563 * 564 * Side Effects: 565 * 566 *---------------------------------------------------------------------------- 567*/ 568/*lint -esym(715, pEASData) reserved for future use */ 569EAS_RESULT SMF_Resume (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData) 570{ 571 S_SMF_DATA *pSMFData; 572 573 /* can't resume a stopped stream */ 574 pSMFData = (S_SMF_DATA*) pInstData; 575 if (pSMFData->state == EAS_STATE_STOPPED) 576 return EAS_ERROR_ALREADY_STOPPED; 577 578 /* nothing to do but resume playback */ 579 pSMFData->state = EAS_STATE_PLAY; 580 return EAS_SUCCESS; 581} 582 583/*---------------------------------------------------------------------------- 584 * SMF_SetData() 585 *---------------------------------------------------------------------------- 586 * Purpose: 587 * Sets parser parameters 588 * 589 * Inputs: 590 * pEASData - pointer to overall EAS data structure 591 * handle - pointer to file handle 592 * 593 * Outputs: 594 * 595 * 596 * Side Effects: 597 * 598 *---------------------------------------------------------------------------- 599*/ 600/*lint -esym(715, pEASData) reserved for future use */ 601EAS_RESULT SMF_SetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 value) 602{ 603 S_SMF_DATA *pSMFData; 604 605 pSMFData = (S_SMF_DATA*) pInstData; 606 switch (param) 607 { 608 609 /* set metadata callback */ 610 case PARSER_DATA_METADATA_CB: 611 EAS_HWMemCpy(&pSMFData->metadata, (void*) value, sizeof(S_METADATA_CB)); 612 break; 613 614#ifdef JET_INTERFACE 615 /* set jet segment and track ID of all tracks for callback function */ 616 case PARSER_DATA_JET_CB: 617 { 618 EAS_U32 i; 619 EAS_U32 bit = (EAS_U32) value; 620 bit = (bit << JET_EVENT_SEG_SHIFT) & JET_EVENT_SEG_MASK; 621 for (i = 0; i < pSMFData->numStreams; i++) 622 pSMFData->streams[i].midiStream.jetData = 623 (pSMFData->streams[i].midiStream.jetData & 624 ~(JET_EVENT_TRACK_MASK | JET_EVENT_SEG_MASK)) | 625 i << JET_EVENT_TRACK_SHIFT | bit | MIDI_FLAGS_JET_CB; 626 pSMFData->flags |= SMF_FLAGS_JET_STREAM; 627 } 628 break; 629 630 /* set state of all mute flags at once */ 631 case PARSER_DATA_MUTE_FLAGS: 632 { 633 EAS_INT i; 634 EAS_U32 bit = (EAS_U32) value; 635 for (i = 0; i < pSMFData->numStreams; i++) 636 { 637 if (bit & 1) 638 pSMFData->streams[i].midiStream.jetData |= MIDI_FLAGS_JET_MUTE; 639 else 640 pSMFData->streams[i].midiStream.jetData &= ~MIDI_FLAGS_JET_MUTE; 641 bit >>= 1; 642 } 643 } 644 break; 645 646 /* set track mute */ 647 case PARSER_DATA_SET_MUTE: 648 if (value < pSMFData->numStreams) 649 pSMFData->streams[value].midiStream.jetData |= MIDI_FLAGS_JET_MUTE; 650 else 651 return EAS_ERROR_PARAMETER_RANGE; 652 break; 653 654 /* clear track mute */ 655 case PARSER_DATA_CLEAR_MUTE: 656 if (value < pSMFData->numStreams) 657 pSMFData->streams[value].midiStream.jetData &= ~MIDI_FLAGS_JET_MUTE; 658 else 659 return EAS_ERROR_PARAMETER_RANGE; 660 break; 661#endif 662 663 default: 664 return EAS_ERROR_INVALID_PARAMETER; 665 } 666 667 return EAS_SUCCESS; 668} 669 670/*---------------------------------------------------------------------------- 671 * SMF_GetData() 672 *---------------------------------------------------------------------------- 673 * Purpose: 674 * Retrieves parser parameters 675 * 676 * Inputs: 677 * pEASData - pointer to overall EAS data structure 678 * handle - pointer to file handle 679 * 680 * Outputs: 681 * 682 * 683 * Side Effects: 684 * 685 *---------------------------------------------------------------------------- 686*/ 687/*lint -esym(715, pEASData) reserved for future use */ 688EAS_RESULT SMF_GetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 *pValue) 689{ 690 S_SMF_DATA *pSMFData; 691 692 pSMFData = (S_SMF_DATA*) pInstData; 693 switch (param) 694 { 695 /* return file type */ 696 case PARSER_DATA_FILE_TYPE: 697 if (pSMFData->numStreams == 1) 698 *pValue = EAS_FILE_SMF0; 699 else 700 *pValue = EAS_FILE_SMF1; 701 break; 702 703/* now handled in eas_public.c */ 704#if 0 705 case PARSER_DATA_POLYPHONY: 706 if (pSMFData->pSynth) 707 VMGetPolyphony(pEASData->pVoiceMgr, pSMFData->pSynth, pValue); 708 else 709 return EAS_ERROR_NOT_VALID_IN_THIS_STATE; 710 break; 711 712 case PARSER_DATA_PRIORITY: 713 if (pSMFData->pSynth) 714 VMGetPriority(pEASData->pVoiceMgr, pSMFData->pSynth, pValue); 715 break; 716 717 /* set transposition */ 718 case PARSER_DATA_TRANSPOSITION: 719 *pValue = pSMFData->transposition; 720 break; 721#endif 722 723 case PARSER_DATA_SYNTH_HANDLE: 724 *pValue = (EAS_I32) pSMFData->pSynth; 725 break; 726 727 default: 728 return EAS_ERROR_INVALID_PARAMETER; 729 } 730 731 return EAS_SUCCESS; 732} 733 734/*---------------------------------------------------------------------------- 735 * SMF_GetVarLenData() 736 *---------------------------------------------------------------------------- 737 * Purpose: 738 * Reads a varible length quantity from an SMF file 739 * 740 * Inputs: 741 * 742 * 743 * Outputs: 744 * 745 * 746 * Side Effects: 747 * 748 *---------------------------------------------------------------------------- 749*/ 750static EAS_RESULT SMF_GetVarLenData (EAS_HW_DATA_HANDLE hwInstData, EAS_FILE_HANDLE fileHandle, EAS_U32 *pData) 751{ 752 EAS_RESULT result; 753 EAS_U32 data; 754 EAS_U8 c; 755 756 /* read until bit 7 is zero */ 757 data = 0; 758 do 759 { 760 if ((result = EAS_HWGetByte(hwInstData, fileHandle,&c)) != EAS_SUCCESS) 761 return result; 762 data = (data << 7) | (c & 0x7f); 763 } while (c & 0x80); 764 *pData = data; 765 return EAS_SUCCESS; 766} 767 768/*---------------------------------------------------------------------------- 769 * SMF_GetDeltaTime() 770 *---------------------------------------------------------------------------- 771 * Purpose: 772 * Reads a varible length quantity from an SMF file 773 * 774 * Inputs: 775 * 776 * 777 * Outputs: 778 * 779 * 780 * Side Effects: 781 * 782 *---------------------------------------------------------------------------- 783*/ 784static EAS_RESULT SMF_GetDeltaTime (EAS_HW_DATA_HANDLE hwInstData, S_SMF_STREAM *pSMFStream) 785{ 786 EAS_RESULT result; 787 EAS_U32 ticks; 788 789 if ((result = SMF_GetVarLenData(hwInstData, pSMFStream->fileHandle, &ticks)) != EAS_SUCCESS) 790 return result; 791 792 pSMFStream->ticks += ticks; 793 return EAS_SUCCESS; 794} 795 796/*---------------------------------------------------------------------------- 797 * SMF_ParseMetaEvent() 798 *---------------------------------------------------------------------------- 799 * Purpose: 800 * Reads a varible length quantity from an SMF file 801 * 802 * Inputs: 803 * 804 * 805 * Outputs: 806 * 807 * 808 * Side Effects: 809 * 810 *---------------------------------------------------------------------------- 811*/ 812static EAS_RESULT SMF_ParseMetaEvent (S_EAS_DATA *pEASData, S_SMF_DATA *pSMFData, S_SMF_STREAM *pSMFStream) 813{ 814 EAS_RESULT result; 815 EAS_U32 len; 816 EAS_I32 pos; 817 EAS_U32 temp; 818 EAS_U8 c; 819 820 /* get the meta-event type */ 821 if ((result = EAS_HWGetByte(pEASData->hwInstData, pSMFStream->fileHandle, &c)) != EAS_SUCCESS) 822 return result; 823 824 /* get the length */ 825 if ((result = SMF_GetVarLenData(pEASData->hwInstData, pSMFStream->fileHandle, &len)) != EAS_SUCCESS) 826 return result; 827 828 /* get the current file position so we can skip the event */ 829 if ((result = EAS_HWFilePos(pEASData->hwInstData, pSMFStream->fileHandle, &pos)) != EAS_SUCCESS) 830 return result; 831 pos += (EAS_I32) len; 832 833 /* end of track? */ 834 if (c == SMF_META_END_OF_TRACK) 835 { 836 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "Meta-event: end of track\n", c, len); */ } 837 pSMFStream->ticks = SMF_END_OF_TRACK; 838 } 839 840 /* tempo event? */ 841 else if (c == SMF_META_TEMPO) 842 { 843 /* read the 3-byte timebase value */ 844 temp = 0; 845 while (len--) 846 { 847 if ((result = EAS_HWGetByte(pEASData->hwInstData, pSMFStream->fileHandle, &c)) != EAS_SUCCESS) 848 return result; 849 temp = (temp << 8) | c; 850 } 851 852 pSMFData->tickConv = (EAS_U16) (((temp * 1024) / pSMFData->ppqn + 500) / 1000); 853 pSMFData->flags |= SMF_FLAGS_HAS_TEMPO; 854 } 855 856 /* check for time signature - see iMelody spec V1.4 section 4.1.2.2.3.6 */ 857 else if (c == SMF_META_TIME_SIGNATURE) 858 { 859 pSMFData->flags |= SMF_FLAGS_HAS_TIME_SIG; 860 } 861 862 /* if the host has registered a metadata callback return the metadata */ 863 else if (pSMFData->metadata.callback) 864 { 865 EAS_I32 readLen; 866 E_EAS_METADATA_TYPE metaType; 867 868 metaType = EAS_METADATA_UNKNOWN; 869 870 /* only process title on the first track */ 871 if (c == SMF_META_SEQTRK_NAME) 872 metaType = EAS_METADATA_TITLE; 873 else if (c == SMF_META_TEXT) 874 metaType = EAS_METADATA_TEXT; 875 else if (c == SMF_META_COPYRIGHT) 876 metaType = EAS_METADATA_COPYRIGHT; 877 else if (c == SMF_META_LYRIC) 878 metaType = EAS_METADATA_LYRIC; 879 880 if (metaType != EAS_METADATA_UNKNOWN) 881 { 882 readLen = pSMFData->metadata.bufferSize - 1; 883 if ((EAS_I32) len < readLen) 884 readLen = (EAS_I32) len; 885 if ((result = EAS_HWReadFile(pEASData->hwInstData, pSMFStream->fileHandle, pSMFData->metadata.buffer, readLen, &readLen)) != EAS_SUCCESS) 886 return result; 887 pSMFData->metadata.buffer[readLen] = 0; 888 pSMFData->metadata.callback(metaType, pSMFData->metadata.buffer, pSMFData->metadata.pUserData); 889 } 890 } 891 892 /* position file to next event - in case we ignored all or part of the meta-event */ 893 if ((result = EAS_HWFileSeek(pEASData->hwInstData, pSMFStream->fileHandle, pos)) != EAS_SUCCESS) 894 return result; 895 896 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "Meta-event: type=%02x, len=%d\n", c, len); */ } 897 return EAS_SUCCESS; 898} 899 900/*---------------------------------------------------------------------------- 901 * SMF_ParseSysEx() 902 *---------------------------------------------------------------------------- 903 * Purpose: 904 * Reads a varible length quantity from an SMF file 905 * 906 * Inputs: 907 * 908 * 909 * Outputs: 910 * 911 * 912 * Side Effects: 913 * 914 *---------------------------------------------------------------------------- 915*/ 916static EAS_RESULT SMF_ParseSysEx (S_EAS_DATA *pEASData, S_SMF_DATA *pSMFData, S_SMF_STREAM *pSMFStream, EAS_U8 f0, EAS_INT parserMode) 917{ 918 EAS_RESULT result; 919 EAS_U32 len; 920 EAS_U8 c; 921 922 /* get the length */ 923 if ((result = SMF_GetVarLenData(pEASData->hwInstData, pSMFStream->fileHandle, &len)) != EAS_SUCCESS) 924 return result; 925 926 /* start of SysEx message? */ 927 if (f0 == 0xf0) 928 { 929 if ((result = EAS_ParseMIDIStream(pEASData, pSMFData->pSynth, &pSMFStream->midiStream, f0, parserMode)) != EAS_SUCCESS) 930 return result; 931 } 932 933 /* feed the SysEx to the stream parser */ 934 while (len--) 935 { 936 if ((result = EAS_HWGetByte(pEASData->hwInstData, pSMFStream->fileHandle, &c)) != EAS_SUCCESS) 937 return result; 938 if ((result = EAS_ParseMIDIStream(pEASData, pSMFData->pSynth, &pSMFStream->midiStream, c, parserMode)) != EAS_SUCCESS) 939 return result; 940 941 /* check for GM system ON */ 942 if (pSMFStream->midiStream.flags & MIDI_FLAG_GM_ON) 943 pSMFData->flags |= SMF_FLAGS_HAS_GM_ON; 944 } 945 946 return EAS_SUCCESS; 947} 948 949/*---------------------------------------------------------------------------- 950 * SMF_ParseEvent() 951 *---------------------------------------------------------------------------- 952 * Purpose: 953 * Reads a varible length quantity from an SMF file 954 * 955 * Inputs: 956 * 957 * 958 * Outputs: 959 * 960 * 961 * Side Effects: 962 * 963 *---------------------------------------------------------------------------- 964*/ 965static EAS_RESULT SMF_ParseEvent (S_EAS_DATA *pEASData, S_SMF_DATA *pSMFData, S_SMF_STREAM *pSMFStream, EAS_INT parserMode) 966{ 967 EAS_RESULT result; 968 EAS_U8 c; 969 970 /* get the event type */ 971 if ((result = EAS_HWGetByte(pEASData->hwInstData, pSMFStream->fileHandle, &c)) != EAS_SUCCESS) 972 return result; 973 974 /* parse meta-event */ 975 if (c == 0xff) 976 { 977 if ((result = SMF_ParseMetaEvent(pEASData, pSMFData, pSMFStream)) != EAS_SUCCESS) 978 return result; 979 } 980 981 /* parse SysEx */ 982 else if ((c == 0xf0) || (c == 0xf7)) 983 { 984 if ((result = SMF_ParseSysEx(pEASData, pSMFData, pSMFStream, c, parserMode)) != EAS_SUCCESS) 985 return result; 986 } 987 988 /* parse MIDI message */ 989 else 990 { 991 if ((result = EAS_ParseMIDIStream(pEASData, pSMFData->pSynth, &pSMFStream->midiStream, c, parserMode)) != EAS_SUCCESS) 992 return result; 993 994 /* keep streaming data to the MIDI parser until the message is complete */ 995 while (pSMFStream->midiStream.pending) 996 { 997 if ((result = EAS_HWGetByte(pEASData->hwInstData, pSMFStream->fileHandle, &c)) != EAS_SUCCESS) 998 return result; 999 if ((result = EAS_ParseMIDIStream(pEASData, pSMFData->pSynth, &pSMFStream->midiStream, c, parserMode)) != EAS_SUCCESS) 1000 return result; 1001 } 1002 1003 } 1004 1005 /* chase mode logic */ 1006 if (pSMFData->time == 0) 1007 { 1008 if (pSMFData->flags & SMF_FLAGS_CHASE_MODE) 1009 { 1010 if (pSMFStream->midiStream.flags & MIDI_FLAG_FIRST_NOTE) 1011 pSMFData->flags &= ~SMF_FLAGS_CHASE_MODE; 1012 } 1013 else if ((pSMFData->flags & SMF_FLAGS_SETUP_BAR) == SMF_FLAGS_SETUP_BAR) 1014 pSMFData->flags = (pSMFData->flags & ~SMF_FLAGS_SETUP_BAR) | SMF_FLAGS_CHASE_MODE; 1015 } 1016 1017 return EAS_SUCCESS; 1018} 1019 1020/*---------------------------------------------------------------------------- 1021 * SMF_ParseHeader() 1022 *---------------------------------------------------------------------------- 1023 * Purpose: 1024 * Parses the header of an SMF file, allocates memory the stream parsers and initializes the 1025 * stream parsers. 1026 * 1027 * Inputs: 1028 * pEASData - pointer to overall EAS data structure 1029 * pSMFData - pointer to parser instance data 1030 * fileHandle - file handle 1031 * fileOffset - offset in the file where the header data starts, usually 0 1032 * 1033 * 1034 * Outputs: 1035 * 1036 * 1037 * Side Effects: 1038 * 1039 *---------------------------------------------------------------------------- 1040*/ 1041/*lint -e{801} we know that 'goto' is deprecated - but it's cleaner in this case */ 1042EAS_RESULT SMF_ParseHeader (EAS_HW_DATA_HANDLE hwInstData, S_SMF_DATA *pSMFData) 1043{ 1044 EAS_RESULT result; 1045 EAS_I32 i; 1046 EAS_U16 division; 1047 EAS_U32 chunkSize; 1048 EAS_U32 chunkStart; 1049 EAS_U32 temp; 1050 EAS_U32 ticks; 1051 1052 /* rewind the file and find the end of the header chunk */ 1053 if ((result = EAS_HWFileSeek(hwInstData, pSMFData->fileHandle, pSMFData->fileOffset + SMF_OFS_HEADER_SIZE)) != EAS_SUCCESS) 1054 goto ReadError; 1055 if ((result = EAS_HWGetDWord(hwInstData, pSMFData->fileHandle, &chunkSize, EAS_TRUE)) != EAS_SUCCESS) 1056 goto ReadError; 1057 1058 /* determine the number of tracks */ 1059 if ((result = EAS_HWFileSeek(hwInstData, pSMFData->fileHandle, pSMFData->fileOffset + SMF_OFS_NUM_TRACKS)) != EAS_SUCCESS) 1060 goto ReadError; 1061 if ((result = EAS_HWGetWord(hwInstData, pSMFData->fileHandle, &pSMFData->numStreams, EAS_TRUE)) != EAS_SUCCESS) 1062 goto ReadError; 1063 1064 /* limit the number of tracks */ 1065 if (pSMFData->numStreams > MAX_SMF_STREAMS) 1066 { 1067 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "SMF file contains %u tracks, playing %d tracks\n", pSMFData->numStreams, MAX_SMF_STREAMS); */ } 1068 pSMFData->numStreams = MAX_SMF_STREAMS; 1069 } 1070 1071 /* get the time division */ 1072 if ((result = EAS_HWGetWord(hwInstData, pSMFData->fileHandle, &division, EAS_TRUE)) != EAS_SUCCESS) 1073 goto ReadError; 1074 1075 /* setup default timebase for 120 bpm */ 1076 pSMFData->ppqn = 192; 1077 if (division & 0x8000) 1078 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING,"No support for SMPTE code timebase\n"); */ } 1079 else 1080 pSMFData->ppqn = (division & 0x7fff); 1081 pSMFData->tickConv = (EAS_U16) (((SMF_DEFAULT_TIMEBASE * 1024) / pSMFData->ppqn + 500) / 1000); 1082 1083 /* dynamic memory allocation, allocate memory for streams */ 1084 if (pSMFData->streams == NULL) 1085 { 1086 pSMFData->streams = EAS_HWMalloc(hwInstData,sizeof(S_SMF_STREAM) * pSMFData->numStreams); 1087 if (pSMFData->streams == NULL) 1088 return EAS_ERROR_MALLOC_FAILED; 1089 1090 /* zero the memory to insure complete initialization */ 1091 EAS_HWMemSet((void *)(pSMFData->streams), 0, sizeof(S_SMF_STREAM) * pSMFData->numStreams); 1092 } 1093 1094 /* find the start of each track */ 1095 chunkStart = (EAS_U32) pSMFData->fileOffset; 1096 ticks = 0x7fffffffL; 1097 pSMFData->nextStream = NULL; 1098 for (i = 0; i < pSMFData->numStreams; i++) 1099 { 1100 1101 for (;;) 1102 { 1103 1104 /* calculate start of next chunk - checking for errors */ 1105 temp = chunkStart + SMF_CHUNK_INFO_SIZE + chunkSize; 1106 if (temp <= chunkStart) 1107 { 1108 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING,"Error in chunk size at offset %d\n", chunkStart); */ } 1109 return EAS_ERROR_FILE_FORMAT; 1110 } 1111 chunkStart = temp; 1112 1113 /* seek to the start of the next chunk */ 1114 if ((result = EAS_HWFileSeek(hwInstData, pSMFData->fileHandle, (EAS_I32) chunkStart)) != EAS_SUCCESS) 1115 goto ReadError; 1116 1117 /* read the chunk identifier */ 1118 if ((result = EAS_HWGetDWord(hwInstData, pSMFData->fileHandle, &temp, EAS_TRUE)) != EAS_SUCCESS) 1119 goto ReadError; 1120 1121 /* read the chunk size */ 1122 if ((result = EAS_HWGetDWord(hwInstData, pSMFData->fileHandle, &chunkSize, EAS_TRUE)) != EAS_SUCCESS) 1123 goto ReadError; 1124 1125 /* make sure this is an 'MTrk' chunk */ 1126 if (temp == SMF_CHUNK_TYPE_TRACK) 1127 break; 1128 1129 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING,"Unexpected chunk type: 0x%08x\n", temp); */ } 1130 } 1131 1132 /* initalize some data */ 1133 pSMFData->streams[i].ticks = 0; 1134 pSMFData->streams[i].fileHandle = pSMFData->fileHandle; 1135 1136 /* NULL the file handle so we don't try to close it twice */ 1137 pSMFData->fileHandle = NULL; 1138 1139 /* save this file position as the start of the track */ 1140 pSMFData->streams[i].startFilePos = (EAS_I32) chunkStart + SMF_CHUNK_INFO_SIZE; 1141 1142 /* initalize the MIDI parser data */ 1143 EAS_InitMIDIStream(&pSMFData->streams[i].midiStream); 1144 1145 /* parse the first delta time in each stream */ 1146 if ((result = SMF_GetDeltaTime(hwInstData, &pSMFData->streams[i])) != EAS_SUCCESS) 1147 goto ReadError; 1148 1149 if (pSMFData->streams[i].ticks < ticks) 1150 { 1151 ticks = pSMFData->streams[i].ticks; 1152 pSMFData->nextStream = &pSMFData->streams[i]; 1153 } 1154 1155 /* more tracks to do, create a duplicate file handle */ 1156 if (i < (pSMFData->numStreams - 1)) 1157 { 1158 if ((result = EAS_HWDupHandle(hwInstData, pSMFData->streams[i].fileHandle, &pSMFData->fileHandle)) != EAS_SUCCESS) 1159 goto ReadError; 1160 } 1161 } 1162 1163 /* update the time of the next event */ 1164 if (pSMFData->nextStream) 1165 SMF_UpdateTime(pSMFData, pSMFData->nextStream->ticks); 1166 1167 return EAS_SUCCESS; 1168 1169 /* ugly goto: but simpler than structured */ 1170 ReadError: 1171 if (result == EAS_EOF) 1172 return EAS_ERROR_FILE_FORMAT; 1173 return result; 1174} 1175 1176/*---------------------------------------------------------------------------- 1177 * SMF_UpdateTime() 1178 *---------------------------------------------------------------------------- 1179 * Purpose: 1180 * Update the millisecond time base by converting the ticks into millieconds 1181 * 1182 * Inputs: 1183 * 1184 * 1185 * Outputs: 1186 * 1187 * 1188 * Side Effects: 1189 * 1190 *---------------------------------------------------------------------------- 1191*/ 1192static void SMF_UpdateTime (S_SMF_DATA *pSMFData, EAS_U32 ticks) 1193{ 1194 EAS_U32 temp1, temp2; 1195 1196 if (pSMFData->flags & SMF_FLAGS_CHASE_MODE) 1197 return; 1198 1199 temp1 = (ticks >> 10) * pSMFData->tickConv; 1200 temp2 = (ticks & 0x3ff) * pSMFData->tickConv; 1201 pSMFData->time += (EAS_I32)((temp1 << 8) + (temp2 >> 2)); 1202} 1203 1204