1/*---------------------------------------------------------------------------- 2 * 3 * File: 4 * eas_imelody.c 5 * 6 * Contents and purpose: 7 * iMelody parser 8 * 9 * Copyright Sonic Network Inc. 2005 10 11 * Licensed under the Apache License, Version 2.0 (the "License"); 12 * you may not use this file except in compliance with the License. 13 * You may obtain a copy of the License at 14 * 15 * http://www.apache.org/licenses/LICENSE-2.0 16 * 17 * Unless required by applicable law or agreed to in writing, software 18 * distributed under the License is distributed on an "AS IS" BASIS, 19 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 20 * See the License for the specific language governing permissions and 21 * limitations under the License. 22 * 23 *---------------------------------------------------------------------------- 24 * Revision Control: 25 * $Revision: 797 $ 26 * $Date: 2007-08-01 00:15:56 -0700 (Wed, 01 Aug 2007) $ 27 *---------------------------------------------------------------------------- 28*/ 29 30/* lint doesn't like the way some string.h files look */ 31#ifdef _lint 32#include "lint_stdlib.h" 33#else 34#include <string.h> 35#endif 36 37#include "eas_data.h" 38#include "eas_miditypes.h" 39#include "eas_parser.h" 40#include "eas_report.h" 41#include "eas_host.h" 42#include "eas_midi.h" 43#include "eas_config.h" 44#include "eas_vm_protos.h" 45#include "eas_imelodydata.h" 46#include "eas_ctype.h" 47 48// #define _DEBUG_IMELODY 49 50/* increase gain for mono ringtones */ 51#define IMELODY_GAIN_OFFSET 8 52 53/* length of 32nd note in 1/256ths of a msec for 120 BPM tempo */ 54#define DEFAULT_TICK_CONV 16000 55#define TICK_CONVERT 1920000 56 57/* default channel and program for iMelody playback */ 58#define IMELODY_CHANNEL 0 59#define IMELODY_PROGRAM 80 60#define IMELODY_VEL_MUL 4 61#define IMELODY_VEL_OFS 67 62 63/* multiplier for fixed point triplet conversion */ 64#define TRIPLET_MULTIPLIER 683 65#define TRIPLET_SHIFT 10 66 67static const char* const tokens[] = 68{ 69 "BEGIN:IMELODY", 70 "VERSION:", 71 "FORMAT:CLASS", 72 "NAME:", 73 "COMPOSER:", 74 "BEAT:", 75 "STYLE:", 76 "VOLUME:", 77 "MELODY:", 78 "END:IMELODY" 79}; 80 81/* ledon or ledoff */ 82static const char ledStr[] = "edo"; 83 84/* vibeon or vibeoff */ 85static const char vibeStr[] = "ibeo"; 86 87/* backon or backoff */ 88static const char backStr[] = "cko"; 89 90typedef enum 91{ 92 TOKEN_BEGIN, 93 TOKEN_VERSION, 94 TOKEN_FORMAT, 95 TOKEN_NAME, 96 TOKEN_COMPOSER, 97 TOKEN_BEAT, 98 TOKEN_STYLE, 99 TOKEN_VOLUME, 100 TOKEN_MELODY, 101 TOKEN_END, 102 TOKEN_INVALID 103} ENUM_IMELODY_TOKENS; 104 105/* lookup table for note values */ 106static const EAS_I8 noteTable[] = { 9, 11, 0, 2, 4, 5, 7 }; 107 108/* inline functions */ 109#ifdef _DEBUG_IMELODY 110static void PutBackChar (S_IMELODY_DATA *pData) 111{ 112 if (pData->index) 113 pData->index--; 114 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "PutBackChar '%c'\n", pData->buffer[pData->index]); */ } 115} 116#else 117EAS_INLINE void PutBackChar (S_IMELODY_DATA *pData) { if (pData->index) pData->index--; } 118#endif 119 120 121/* local prototypes */ 122static EAS_RESULT IMY_CheckFileType (S_EAS_DATA *pEASData, EAS_FILE_HANDLE fileHandle, EAS_VOID_PTR *ppHandle, EAS_I32 offset); 123static EAS_RESULT IMY_Prepare (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData); 124static EAS_RESULT IMY_Time (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_U32 *pTime); 125static EAS_RESULT IMY_Event (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_INT parserMode); 126static EAS_RESULT IMY_State (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_STATE *pState); 127static EAS_RESULT IMY_Close (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData); 128static EAS_RESULT IMY_Reset (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData); 129static EAS_RESULT IMY_Pause (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData); 130static EAS_RESULT IMY_Resume (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData); 131static EAS_RESULT IMY_SetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 value); 132static EAS_RESULT IMY_GetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 *pValue); 133static EAS_BOOL IMY_PlayNote (S_EAS_DATA *pEASData, S_IMELODY_DATA *pData, EAS_I8 note, EAS_INT parserMode); 134static EAS_BOOL IMY_PlayRest (S_EAS_DATA *pEASData, S_IMELODY_DATA *pData); 135static EAS_BOOL IMY_GetDuration (EAS_HW_DATA_HANDLE hwInstData, S_IMELODY_DATA *pData, EAS_I32 *pDuration); 136static EAS_BOOL IMY_GetLEDState (S_EAS_DATA *pEASData, S_IMELODY_DATA *pData); 137static EAS_BOOL IMY_GetVibeState (S_EAS_DATA *pEASData, S_IMELODY_DATA *pData); 138static EAS_BOOL IMY_GetBackState (S_EAS_DATA *pEASData, S_IMELODY_DATA *pData); 139static EAS_BOOL IMY_GetVolume (EAS_HW_DATA_HANDLE hwInstData, S_IMELODY_DATA *pData, EAS_BOOL inHeader); 140static EAS_BOOL IMY_GetNumber (EAS_HW_DATA_HANDLE hwInstData, S_IMELODY_DATA *pData, EAS_INT *temp, EAS_BOOL inHeader); 141static EAS_RESULT IMY_ParseHeader (S_EAS_DATA *pEASData, S_IMELODY_DATA* pData); 142static EAS_I8 IMY_GetNextChar (EAS_HW_DATA_HANDLE hwInstData, S_IMELODY_DATA *pData, EAS_BOOL inHeader); 143static EAS_RESULT IMY_ReadLine (EAS_HW_DATA_HANDLE hwInstData, EAS_FILE_HANDLE fileHandle, EAS_I8 *buffer, EAS_I32 *pStartLine); 144static EAS_INT IMY_ParseLine (EAS_I8 *buffer, EAS_U8 *pIndex); 145 146 147/*---------------------------------------------------------------------------- 148 * 149 * EAS_iMelody_Parser 150 * 151 * This structure contains the functional interface for the iMelody parser 152 *---------------------------------------------------------------------------- 153*/ 154const S_FILE_PARSER_INTERFACE EAS_iMelody_Parser = 155{ 156 IMY_CheckFileType, 157 IMY_Prepare, 158 IMY_Time, 159 IMY_Event, 160 IMY_State, 161 IMY_Close, 162 IMY_Reset, 163 IMY_Pause, 164 IMY_Resume, 165 NULL, 166 IMY_SetData, 167 IMY_GetData, 168 NULL 169}; 170 171/*---------------------------------------------------------------------------- 172 * IMY_CheckFileType() 173 *---------------------------------------------------------------------------- 174 * Purpose: 175 * Check the file type to see if we can parse it 176 * 177 * Inputs: 178 * pEASData - pointer to overall EAS data structure 179 * handle - pointer to file handle 180 * 181 * Outputs: 182 * 183 * 184 * Side Effects: 185 * 186 *---------------------------------------------------------------------------- 187*/ 188static EAS_RESULT IMY_CheckFileType (S_EAS_DATA *pEASData, EAS_FILE_HANDLE fileHandle, EAS_VOID_PTR *ppHandle, EAS_I32 offset) 189{ 190 S_IMELODY_DATA* pData; 191 EAS_I8 buffer[MAX_LINE_SIZE+1]; 192 EAS_U8 index; 193 194#ifdef _DEBUG_IMELODY 195 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "Enter IMY_CheckFileType\n"); */ } 196#endif 197 198 /* read the first line of the file */ 199 *ppHandle = NULL; 200 if (IMY_ReadLine(pEASData->hwInstData, fileHandle, buffer, NULL) != EAS_SUCCESS) 201 return EAS_SUCCESS; 202 203 /* check for header string */ 204 if (IMY_ParseLine(buffer, &index) == TOKEN_BEGIN) 205 { 206 207 /* check for static memory allocation */ 208 if (pEASData->staticMemoryModel) 209 pData = EAS_CMEnumData(EAS_CM_IMELODY_DATA); 210 else 211 pData = EAS_HWMalloc(pEASData->hwInstData, sizeof(S_IMELODY_DATA)); 212 if (!pData) 213 return EAS_ERROR_MALLOC_FAILED; 214 EAS_HWMemSet(pData, 0, sizeof(S_IMELODY_DATA)); 215 216 /* initialize */ 217 pData->fileHandle = fileHandle; 218 pData->fileOffset = offset; 219 pData->state = EAS_STATE_ERROR; 220 pData->state = EAS_STATE_OPEN; 221 222 /* return a pointer to the instance data */ 223 *ppHandle = pData; 224 } 225 226 return EAS_SUCCESS; 227} 228 229/*---------------------------------------------------------------------------- 230 * IMY_Prepare() 231 *---------------------------------------------------------------------------- 232 * Purpose: 233 * Prepare to parse the file. Allocates instance data (or uses static allocation for 234 * static memory model). 235 * 236 * Inputs: 237 * pEASData - pointer to overall EAS data structure 238 * handle - pointer to file handle 239 * 240 * Outputs: 241 * 242 * 243 * Side Effects: 244 * 245 *---------------------------------------------------------------------------- 246*/ 247static EAS_RESULT IMY_Prepare (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData) 248{ 249 S_IMELODY_DATA* pData; 250 EAS_RESULT result; 251 252#ifdef _DEBUG_IMELODY 253 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "Enter IMY_Prepare\n"); */ } 254#endif 255 256 /* check for valid state */ 257 pData = (S_IMELODY_DATA*) pInstData; 258 if (pData->state != EAS_STATE_OPEN) 259 return EAS_ERROR_NOT_VALID_IN_THIS_STATE; 260 261 /* instantiate a synthesizer */ 262 if ((result = VMInitMIDI(pEASData, &pData->pSynth)) != EAS_SUCCESS) 263 { 264 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "VMInitMIDI returned %d\n", result); */ } 265 return result; 266 } 267 268 /* parse the header */ 269 if ((result = IMY_ParseHeader(pEASData, pData)) != EAS_SUCCESS) 270 return result; 271 272#ifdef _DEBUG_IMELODY 273 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_Prepare: state set to EAS_STATE_READY\n"); */ } 274#endif 275 276 pData ->state = EAS_STATE_READY; 277 return EAS_SUCCESS; 278} 279 280/*---------------------------------------------------------------------------- 281 * IMY_Time() 282 *---------------------------------------------------------------------------- 283 * Purpose: 284 * Returns the time of the next event in msecs 285 * 286 * Inputs: 287 * pEASData - pointer to overall EAS data structure 288 * handle - pointer to file handle 289 * pTime - pointer to variable to hold time of next event (in msecs) 290 * 291 * Outputs: 292 * 293 * 294 * Side Effects: 295 * 296 *---------------------------------------------------------------------------- 297*/ 298/*lint -esym(715, pEASData) common decoder interface - pEASData not used */ 299static EAS_RESULT IMY_Time (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_U32 *pTime) 300{ 301 S_IMELODY_DATA *pData; 302 303 pData = (S_IMELODY_DATA*) pInstData; 304 305 /* return time in milliseconds */ 306 /*lint -e{704} use shift instead of division */ 307 *pTime = pData->time >> 8; 308 return EAS_SUCCESS; 309} 310 311/*---------------------------------------------------------------------------- 312 * IMY_Event() 313 *---------------------------------------------------------------------------- 314 * Purpose: 315 * Parse the next event in the file 316 * 317 * Inputs: 318 * pEASData - pointer to overall EAS data structure 319 * handle - pointer to file handle 320 * 321 * Outputs: 322 * 323 * 324 * Side Effects: 325 * 326 *---------------------------------------------------------------------------- 327*/ 328static EAS_RESULT IMY_Event (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_INT parserMode) 329{ 330 S_IMELODY_DATA* pData; 331 EAS_RESULT result; 332 EAS_I8 c; 333 EAS_BOOL eof; 334 EAS_INT temp; 335 336 pData = (S_IMELODY_DATA*) pInstData; 337 if (pData->state >= EAS_STATE_OPEN) 338 return EAS_SUCCESS; 339 340 if (pData->state == EAS_STATE_READY) { 341 pData->state = EAS_STATE_PLAY; 342 } 343 344 /* initialize MIDI channel when the track starts playing */ 345 if (pData->time == 0) 346 { 347 348#ifdef _DEBUG_IMELODY 349 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_Event: Reset\n"); */ } 350#endif 351 /* set program to square lead */ 352 VMProgramChange(pEASData->pVoiceMgr, pData->pSynth, IMELODY_CHANNEL, IMELODY_PROGRAM); 353 354 /* set channel volume to max */ 355 VMControlChange(pEASData->pVoiceMgr, pData->pSynth, IMELODY_CHANNEL, 7, 127); 356 } 357 358 /* check for end of note */ 359 if (pData->note) 360 { 361 362#ifdef _DEBUG_IMELODY 363 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "Stopping note %d\n", pData->note); */ } 364#endif 365 /* stop the note */ 366 VMStopNote(pEASData->pVoiceMgr, pData->pSynth, IMELODY_CHANNEL, pData->note, 0); 367 pData->note = 0; 368 369 /* check for rest between notes */ 370 if (pData->restTicks) 371 { 372 pData->time += pData->restTicks; 373 pData->restTicks = 0; 374 return EAS_SUCCESS; 375 } 376 } 377 378 /* parse the next event */ 379 eof = EAS_FALSE; 380 while (!eof) 381 { 382 383 /* get next character */ 384 c = IMY_GetNextChar(pEASData->hwInstData, pData, EAS_FALSE); 385 386 switch (c) 387 { 388 /* start repeat */ 389 case '(': 390 391#ifdef _DEBUG_IMELODY 392 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "Enter repeat section\n", c); */ } 393#endif 394 395 if (pData->repeatOffset < 0) 396 { 397 pData->repeatOffset = pData->startLine + (EAS_I32) pData->index; 398 399 /* save current time and check it later to make sure the loop isn't zero length */ 400 pData->repeatTime = pData->time; 401 402#ifdef _DEBUG_IMELODY 403 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "Repeat offset = %d\n", pData->repeatOffset); */ } 404#endif 405 } 406 else 407 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "Ignoring nested repeat section\n"); */ } 408 break; 409 410 /* end repeat */ 411 case ')': 412 413#ifdef _DEBUG_IMELODY 414 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "End repeat section, repeat offset = %d\n", pData->repeatOffset); */ } 415#endif 416 /* ignore zero-length loops */ 417 if (pData->repeatTime == pData->time) { 418 pData->repeatCount = -1; 419 pData->repeatOffset = -1; 420 } else if (pData->repeatCount >= 0) { 421 422 /* decrement repeat count (repeatCount == 0 means infinite loop) */ 423 if (pData->repeatCount > 0) 424 { 425 if (--pData->repeatCount == 0) 426 { 427 pData->repeatCount = -1; 428#ifdef _DEBUG_IMELODY 429 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "Repeat loop complete\n"); */ } 430#endif 431 } 432 } 433 434//2 TEMPORARY FIX: If locating, don't do infinite loops. 435//3 We need a different mode for metadata parsing where we don't loop at all 436 if ((parserMode == eParserModePlay) || (pData->repeatCount != 0)) 437 { 438 439#ifdef _DEBUG_IMELODY 440 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "Rewinding file for repeat\n"); */ } 441#endif 442 /* rewind to start of loop */ 443 if ((result = EAS_HWFileSeek(pEASData->hwInstData, pData->fileHandle, pData->repeatOffset)) != EAS_SUCCESS) 444 return result; 445 IMY_ReadLine(pEASData->hwInstData, pData->fileHandle, pData->buffer, &pData->startLine); 446 pData->index = 0; 447 448 /* if last loop, prevent future loops */ 449 if (pData->repeatCount == -1) 450 pData->repeatOffset = -1; 451 } 452 } 453 break; 454 455 /* repeat count */ 456 case '@': 457 if (!IMY_GetNumber(pEASData->hwInstData, pData, &temp, EAS_FALSE)) 458 eof = EAS_TRUE; 459 else if (pData->repeatOffset > 0) 460 { 461 462#ifdef _DEBUG_IMELODY 463 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "Repeat count = %dt", pData->repeatCount); */ } 464#endif 465 if (pData->repeatCount < 0) 466 pData->repeatCount = (EAS_I16) temp; 467 } 468 break; 469 470 /* volume */ 471 case 'V': 472 if (!IMY_GetVolume(pEASData->hwInstData, pData, EAS_FALSE)) 473 eof = EAS_TRUE; 474 break; 475 476 /* flat */ 477 case '&': 478 pData->noteModifier = -1; 479 break; 480 481 /* sharp */ 482 case '#': 483 pData->noteModifier = +1; 484 break; 485 486 /* octave */ 487 case '*': 488 c = IMY_GetNextChar(pEASData->hwInstData, pData, EAS_FALSE); 489 if (IsDigit(c)) 490 pData->octave = (EAS_U8) ((c - '0' + 1) * 12); 491 else if (!c) 492 eof = EAS_TRUE; 493 break; 494 495 /* ledon or ledoff */ 496 case 'l': 497 if (!IMY_GetLEDState(pEASData, pData)) 498 eof = EAS_TRUE; 499 break; 500 501 /* vibeon or vibeoff */ 502 case 'v': 503 if (!IMY_GetVibeState(pEASData, pData)) 504 eof = EAS_TRUE; 505 break; 506 507 /* either a B note or backon or backoff */ 508 case 'b': 509 if (IMY_GetNextChar(pEASData->hwInstData, pData, EAS_FALSE) == 'a') 510 { 511 if (!IMY_GetBackState(pEASData, pData)) 512 eof = EAS_TRUE; 513 } 514 else 515 { 516 PutBackChar(pData); 517 if (IMY_PlayNote(pEASData, pData, c, parserMode)) 518 return EAS_SUCCESS; 519 eof = EAS_TRUE; 520 } 521 break; 522 523 /* rest */ 524 case 'r': 525 case 'R': 526 if (IMY_PlayRest(pEASData, pData)) 527 return EAS_SUCCESS; 528 eof = EAS_TRUE; 529 break; 530 531 /* EOF */ 532 case 0: 533#ifdef _DEBUG_IMELODY 534 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_Event: end of iMelody file detected\n"); */ } 535#endif 536 eof = EAS_TRUE; 537 break; 538 539 /* must be a note */ 540 default: 541 c = ToLower(c); 542 if ((c >= 'a') && (c <= 'g')) 543 { 544 if (IMY_PlayNote(pEASData, pData, c, parserMode)) 545 return EAS_SUCCESS; 546 eof = EAS_TRUE; 547 } 548 else 549 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "Ignoring unexpected character '%c' [0x%02x]\n", c, c); */ } 550 break; 551 } 552 } 553 554 /* handle EOF */ 555#ifdef _DEBUG_IMELODY 556 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_Event: state set to EAS_STATE_STOPPING\n"); */ } 557#endif 558 pData->state = EAS_STATE_STOPPING; 559 VMReleaseAllVoices(pEASData->pVoiceMgr, pData->pSynth); 560 return EAS_SUCCESS; 561} 562 563/*---------------------------------------------------------------------------- 564 * IMY_State() 565 *---------------------------------------------------------------------------- 566 * Purpose: 567 * Returns the current state of the stream 568 * 569 * Inputs: 570 * pEASData - pointer to overall EAS data structure 571 * handle - pointer to file handle 572 * pState - pointer to variable to store state 573 * 574 * Outputs: 575 * 576 * 577 * Side Effects: 578 * 579 *---------------------------------------------------------------------------- 580*/ 581/*lint -esym(715, pEASData) common decoder interface - pEASData not used */ 582static EAS_RESULT IMY_State (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 *pState) 583{ 584 S_IMELODY_DATA* pData; 585 586 /* establish pointer to instance data */ 587 pData = (S_IMELODY_DATA*) pInstData; 588 589 /* if stopping, check to see if synth voices are active */ 590 if (pData->state == EAS_STATE_STOPPING) 591 { 592 if (VMActiveVoices(pData->pSynth) == 0) 593 { 594 pData->state = EAS_STATE_STOPPED; 595#ifdef _DEBUG_IMELODY 596 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_State: state set to EAS_STATE_STOPPED\n"); */ } 597#endif 598 } 599 } 600 601 if (pData->state == EAS_STATE_PAUSING) 602 { 603 if (VMActiveVoices(pData->pSynth) == 0) 604 { 605#ifdef _DEBUG_IMELODY 606 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_State: state set to EAS_STATE_PAUSED\n"); */ } 607#endif 608 pData->state = EAS_STATE_PAUSED; 609 } 610 } 611 612 /* return current state */ 613 *pState = pData->state; 614 return EAS_SUCCESS; 615} 616 617/*---------------------------------------------------------------------------- 618 * IMY_Close() 619 *---------------------------------------------------------------------------- 620 * Purpose: 621 * Close the file and clean up 622 * 623 * Inputs: 624 * pEASData - pointer to overall EAS data structure 625 * handle - pointer to file handle 626 * 627 * Outputs: 628 * 629 * 630 * Side Effects: 631 * 632 *---------------------------------------------------------------------------- 633*/ 634static EAS_RESULT IMY_Close (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData) 635{ 636 S_IMELODY_DATA* pData; 637 EAS_RESULT result; 638 639#ifdef _DEBUG_IMELODY 640 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_Close: close file\n"); */ } 641#endif 642 643 pData = (S_IMELODY_DATA*) pInstData; 644 645 /* close the file */ 646 if ((result = EAS_HWCloseFile(pEASData->hwInstData, pData->fileHandle)) != EAS_SUCCESS) 647 return result; 648 649 /* free the synth */ 650 if (pData->pSynth != NULL) 651 VMMIDIShutdown(pEASData, pData->pSynth); 652 653 /* if using dynamic memory, free it */ 654 if (!pEASData->staticMemoryModel) 655 EAS_HWFree(pEASData->hwInstData, pData); 656 657 return EAS_SUCCESS; 658} 659 660/*---------------------------------------------------------------------------- 661 * IMY_Reset() 662 *---------------------------------------------------------------------------- 663 * Purpose: 664 * Reset the sequencer. Used for locating backwards in the file. 665 * 666 * Inputs: 667 * pEASData - pointer to overall EAS data structure 668 * handle - pointer to file handle 669 * 670 * Outputs: 671 * 672 * 673 * Side Effects: 674 * 675 *---------------------------------------------------------------------------- 676*/ 677static EAS_RESULT IMY_Reset (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData) 678{ 679 S_IMELODY_DATA* pData; 680 EAS_RESULT result; 681 682#ifdef _DEBUG_IMELODY 683 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_Reset: reset file\n"); */ } 684#endif 685 pData = (S_IMELODY_DATA*) pInstData; 686 687 /* reset the synth */ 688 VMReset(pEASData->pVoiceMgr, pData->pSynth, EAS_TRUE); 689 690 /* reset time to zero */ 691 pData->time = 0; 692 pData->note = 0; 693 694 /* reset file position and re-parse header */ 695 pData->state = EAS_STATE_ERROR; 696 if ((result = EAS_HWFileSeek(pEASData->hwInstData, pData->fileHandle, pData->fileOffset)) != EAS_SUCCESS) 697 return result; 698 if ((result = IMY_ParseHeader (pEASData, pData)) != EAS_SUCCESS) 699 return result; 700 701#ifdef _DEBUG_IMELODY 702 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_Reset: state set to EAS_STATE_ERROR\n"); */ } 703#endif 704 705 pData->state = EAS_STATE_READY; 706 return EAS_SUCCESS; 707} 708 709/*---------------------------------------------------------------------------- 710 * IMY_Pause() 711 *---------------------------------------------------------------------------- 712 * Purpose: 713 * Pauses the sequencer. Mutes all voices and sets state to pause. 714 * 715 * Inputs: 716 * pEASData - pointer to overall EAS data structure 717 * handle - pointer to file handle 718 * 719 * Outputs: 720 * 721 * 722 * Side Effects: 723 * 724 *---------------------------------------------------------------------------- 725*/ 726static EAS_RESULT IMY_Pause (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData) 727{ 728 S_IMELODY_DATA *pData; 729 730#ifdef _DEBUG_IMELODY 731 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_Pause: pause file\n"); */ } 732#endif 733 734 /* can't pause a stopped stream */ 735 pData = (S_IMELODY_DATA*) pInstData; 736 if (pData->state == EAS_STATE_STOPPED) 737 return EAS_ERROR_ALREADY_STOPPED; 738 739 /* mute the synthesizer */ 740 VMMuteAllVoices(pEASData->pVoiceMgr, pData->pSynth); 741 pData->state = EAS_STATE_PAUSING; 742 return EAS_SUCCESS; 743} 744 745/*---------------------------------------------------------------------------- 746 * IMY_Resume() 747 *---------------------------------------------------------------------------- 748 * Purpose: 749 * Resume playing after a pause, sets state back to playing. 750 * 751 * Inputs: 752 * pEASData - pointer to overall EAS data structure 753 * handle - pointer to file handle 754 * 755 * Outputs: 756 * 757 * 758 * Side Effects: 759 * 760 *---------------------------------------------------------------------------- 761*/ 762/*lint -esym(715, pEASData) common decoder interface - pEASData not used */ 763static EAS_RESULT IMY_Resume (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData) 764{ 765 S_IMELODY_DATA *pData; 766 767#ifdef _DEBUG_IMELODY 768 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_Resume: resume file\n"); */ } 769#endif 770 771 /* can't resume a stopped stream */ 772 pData = (S_IMELODY_DATA*) pInstData; 773 if (pData->state == EAS_STATE_STOPPED) 774 return EAS_ERROR_ALREADY_STOPPED; 775 776 /* nothing to do but resume playback */ 777 pData->state = EAS_STATE_PLAY; 778 return EAS_SUCCESS; 779} 780 781/*---------------------------------------------------------------------------- 782 * IMY_SetData() 783 *---------------------------------------------------------------------------- 784 * Purpose: 785 * Adjust tempo relative to song tempo 786 * 787 * Inputs: 788 * pEASData - pointer to overall EAS data structure 789 * pInstData - pointer to iMelody instance data 790 * rate - rate (28-bit fractional amount) 791 * 792 * Outputs: 793 * 794 * 795 * Side Effects: 796 * 797 *---------------------------------------------------------------------------- 798*/ 799/*lint -esym(715, pEASData) common decoder interface - pEASData not used */ 800static EAS_RESULT IMY_SetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 value) 801{ 802 S_IMELODY_DATA *pData; 803 804 pData = (S_IMELODY_DATA*) pInstData; 805 switch (param) 806 { 807 808 /* set metadata callback */ 809 case PARSER_DATA_METADATA_CB: 810 EAS_HWMemCpy(&pData->metadata, (void*) value, sizeof(S_METADATA_CB)); 811 break; 812 813 default: 814 return EAS_ERROR_INVALID_PARAMETER; 815 } 816 817 return EAS_SUCCESS; 818} 819 820/*---------------------------------------------------------------------------- 821 * IMY_GetData() 822 *---------------------------------------------------------------------------- 823 * Purpose: 824 * Return the file type 825 * 826 * Inputs: 827 * pEASData - pointer to overall EAS data structure 828 * pInstData - pointer to iMelody instance data 829 * 830 * Outputs: 831 * 832 * 833 * Side Effects: 834 * 835 *---------------------------------------------------------------------------- 836*/ 837/*lint -esym(715, pEASData) common decoder interface - pEASData not used */ 838static EAS_RESULT IMY_GetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 *pValue) 839{ 840 S_IMELODY_DATA *pData; 841 842 pData = (S_IMELODY_DATA*) pInstData; 843 844 switch (param) 845 { 846 /* return file type as iMelody */ 847 case PARSER_DATA_FILE_TYPE: 848 *pValue = EAS_FILE_IMELODY; 849 break; 850 851 case PARSER_DATA_SYNTH_HANDLE: 852 *pValue = (EAS_I32) pData->pSynth; 853 break; 854 855 case PARSER_DATA_GAIN_OFFSET: 856 *pValue = IMELODY_GAIN_OFFSET; 857 break; 858 859 default: 860 return EAS_ERROR_INVALID_PARAMETER; 861 } 862 863 return EAS_SUCCESS; 864} 865 866/*---------------------------------------------------------------------------- 867 * IMY_PlayNote() 868 *---------------------------------------------------------------------------- 869 * Purpose: 870 * 871 * 872 * Inputs: 873 * 874 * 875 * Outputs: 876 * 877 * 878 * Side Effects: 879 * 880 *---------------------------------------------------------------------------- 881*/ 882static EAS_BOOL IMY_PlayNote (S_EAS_DATA *pEASData, S_IMELODY_DATA *pData, EAS_I8 note, EAS_INT parserMode) 883{ 884 EAS_I32 duration; 885 EAS_U8 velocity; 886 887 888#ifdef _DEBUG_IMELODY 889 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_PlayNote: start note %d\n", note); */ } 890#endif 891 892 /* get the duration */ 893 if (!IMY_GetDuration(pEASData->hwInstData, pData, &duration)) 894 return EAS_FALSE; 895 896 /* save note value */ 897 pData->note = (EAS_U8) (pData->octave + noteTable[note - 'a'] + pData->noteModifier); 898 velocity = (EAS_U8) (pData->volume ? pData->volume * IMELODY_VEL_MUL + IMELODY_VEL_OFS : 0); 899 900 /* start note only if in play mode */ 901 if (parserMode == eParserModePlay) 902 VMStartNote(pEASData->pVoiceMgr, pData->pSynth, IMELODY_CHANNEL, pData->note, velocity); 903 904#ifdef _DEBUG_IMELODY 905 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_PlayNote: Start note %d, duration %d\n", pData->note, duration); */ } 906#endif 907 908 /* determine note length */ 909 switch (pData->style) 910 { 911 case 0: 912 /*lint -e{704} shift for performance */ 913 pData->restTicks = duration >> 4; 914 break; 915 case 1: 916 pData->restTicks = 0; 917 break; 918 case 2: 919 /*lint -e{704} shift for performance */ 920 pData->restTicks = duration >> 1; 921 break; 922 default: 923 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "IMY_PlayNote: Note style out of range: %d\n", pData->style); */ } 924 /*lint -e{704} shift for performance */ 925 pData->restTicks = duration >> 4; 926 break; 927 } 928 929 /* next event is at end of this note */ 930 pData->time += duration - pData->restTicks; 931 932 /* reset the flat/sharp modifier */ 933 pData->noteModifier = 0; 934 935 return EAS_TRUE; 936} 937 938/*---------------------------------------------------------------------------- 939 * IMY_PlayRest() 940 *---------------------------------------------------------------------------- 941 * Purpose: 942 * 943 * 944 * Inputs: 945 * 946 * 947 * Outputs: 948 * 949 * 950 * Side Effects: 951 * 952 *---------------------------------------------------------------------------- 953*/ 954static EAS_BOOL IMY_PlayRest (S_EAS_DATA *pEASData, S_IMELODY_DATA *pData) 955{ 956 EAS_I32 duration; 957 958#ifdef _DEBUG_IMELODY 959 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "Enter IMY_PlayRest]n"); */ } 960#endif 961 962 /* get the duration */ 963 if (!IMY_GetDuration(pEASData->hwInstData, pData, &duration)) 964 return EAS_FALSE; 965 966#ifdef _DEBUG_IMELODY 967 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_PlayRest: note duration %d\n", duration); */ } 968#endif 969 970 /* next event is at end of this note */ 971 pData->time += duration; 972 return EAS_TRUE; 973} 974 975/*---------------------------------------------------------------------------- 976 * IMY_GetDuration() 977 *---------------------------------------------------------------------------- 978 * Purpose: 979 * 980 * 981 * Inputs: 982 * 983 * 984 * Outputs: 985 * 986 * 987 * Side Effects: 988 * 989 *---------------------------------------------------------------------------- 990*/ 991 992static EAS_BOOL IMY_GetDuration (EAS_HW_DATA_HANDLE hwInstData, S_IMELODY_DATA *pData, EAS_I32 *pDuration) 993{ 994 EAS_I32 duration; 995 EAS_I8 c; 996 997 /* get the duration */ 998 *pDuration = 0; 999 c = IMY_GetNextChar(hwInstData, pData, EAS_FALSE); 1000 if (!c) 1001 return EAS_FALSE; 1002 if ((c < '0') || (c > '5')) 1003 { 1004#ifdef _DEBUG_IMELODY 1005 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_GetDuration: error in duration '%c'\n", c); */ } 1006#endif 1007 return EAS_FALSE; 1008 } 1009 1010 /* calculate total length of note */ 1011 duration = pData->tick * (1 << ('5' - c)); 1012 1013 /* check for duration modifier */ 1014 c = IMY_GetNextChar(hwInstData, pData, EAS_FALSE); 1015 if (c) 1016 { 1017 if (c == '.') 1018 /*lint -e{704} shift for performance */ 1019 duration += duration >> 1; 1020 else if (c == ':') 1021 /*lint -e{704} shift for performance */ 1022 duration += (duration >> 1) + (duration >> 2); 1023 else if (c == ';') 1024 /*lint -e{704} shift for performance */ 1025 duration = (duration * TRIPLET_MULTIPLIER) >> TRIPLET_SHIFT; 1026 else 1027 PutBackChar(pData); 1028 } 1029 1030 *pDuration = duration; 1031 return EAS_TRUE; 1032} 1033 1034/*---------------------------------------------------------------------------- 1035 * IMY_GetLEDState() 1036 *---------------------------------------------------------------------------- 1037 * Purpose: 1038 * 1039 * 1040 * Inputs: 1041 * 1042 * 1043 * Outputs: 1044 * 1045 * 1046 * Side Effects: 1047 * 1048 *---------------------------------------------------------------------------- 1049*/ 1050static EAS_BOOL IMY_GetLEDState (S_EAS_DATA *pEASData, S_IMELODY_DATA *pData) 1051{ 1052 EAS_I8 c; 1053 EAS_INT i; 1054 1055#ifdef _DEBUG_IMELODY 1056 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "Enter IMY_GetLEDState\n"); */ } 1057#endif 1058 1059 for (i = 0; i < 5; i++) 1060 { 1061 c = IMY_GetNextChar(pEASData->hwInstData, pData, EAS_FALSE); 1062 if (!c) 1063 return EAS_FALSE; 1064 switch (i) 1065 { 1066 case 3: 1067 if (c == 'n') 1068 { 1069#ifdef _DEBUG_IMELODY 1070 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_GetLEDState: LED on\n"); */ } 1071#endif 1072 EAS_HWLED(pEASData->hwInstData, EAS_TRUE); 1073 return EAS_TRUE; 1074 } 1075 else if (c != 'f') 1076 return EAS_FALSE; 1077 break; 1078 1079 case 4: 1080 if (c == 'f') 1081 { 1082#ifdef _DEBUG_IMELODY 1083 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_GetLEDState: LED off\n"); */ } 1084#endif 1085 EAS_HWLED(pEASData->hwInstData, EAS_FALSE); 1086 return EAS_TRUE; 1087 } 1088 return EAS_FALSE; 1089 1090 default: 1091 if (c != ledStr[i]) 1092 return EAS_FALSE; 1093 break; 1094 } 1095 } 1096 return EAS_FALSE; 1097} 1098 1099/*---------------------------------------------------------------------------- 1100 * IMY_GetVibeState() 1101 *---------------------------------------------------------------------------- 1102 * Purpose: 1103 * 1104 * 1105 * Inputs: 1106 * 1107 * 1108 * Outputs: 1109 * 1110 * 1111 * Side Effects: 1112 * 1113 *---------------------------------------------------------------------------- 1114*/ 1115static EAS_BOOL IMY_GetVibeState (S_EAS_DATA *pEASData, S_IMELODY_DATA *pData) 1116{ 1117 EAS_I8 c; 1118 EAS_INT i; 1119 1120#ifdef _DEBUG_IMELODY 1121 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "Enter IMY_GetVibeState\n"); */ } 1122#endif 1123 1124 for (i = 0; i < 6; i++) 1125 { 1126 c = IMY_GetNextChar(pEASData->hwInstData, pData, EAS_FALSE); 1127 if (!c) 1128 return EAS_FALSE; 1129 switch (i) 1130 { 1131 case 4: 1132 if (c == 'n') 1133 { 1134#ifdef _DEBUG_IMELODY 1135 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_GetVibeState: vibrate on\n"); */ } 1136#endif 1137 EAS_HWVibrate(pEASData->hwInstData, EAS_TRUE); 1138 return EAS_TRUE; 1139 } 1140 else if (c != 'f') 1141 return EAS_FALSE; 1142 break; 1143 1144 case 5: 1145 if (c == 'f') 1146 { 1147#ifdef _DEBUG_IMELODY 1148 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_GetVibeState: vibrate off\n"); */ } 1149#endif 1150 EAS_HWVibrate(pEASData->hwInstData, EAS_FALSE); 1151 return EAS_TRUE; 1152 } 1153 return EAS_FALSE; 1154 1155 default: 1156 if (c != vibeStr[i]) 1157 return EAS_FALSE; 1158 break; 1159 } 1160 } 1161 return EAS_FALSE; 1162} 1163 1164/*---------------------------------------------------------------------------- 1165 * IMY_GetBackState() 1166 *---------------------------------------------------------------------------- 1167 * Purpose: 1168 * 1169 * 1170 * Inputs: 1171 * 1172 * 1173 * Outputs: 1174 * 1175 * 1176 * Side Effects: 1177 * 1178 *---------------------------------------------------------------------------- 1179*/ 1180static EAS_BOOL IMY_GetBackState (S_EAS_DATA *pEASData, S_IMELODY_DATA *pData) 1181{ 1182 EAS_I8 c; 1183 EAS_INT i; 1184 1185#ifdef _DEBUG_IMELODY 1186 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "Enter IMY_GetBackState\n"); */ } 1187#endif 1188 1189 for (i = 0; i < 5; i++) 1190 { 1191 c = IMY_GetNextChar(pEASData->hwInstData, pData, EAS_FALSE); 1192 if (!c) 1193 return EAS_FALSE; 1194 switch (i) 1195 { 1196 case 3: 1197 if (c == 'n') 1198 { 1199#ifdef _DEBUG_IMELODY 1200 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_GetBackState: backlight on\n"); */ } 1201#endif 1202 EAS_HWBackLight(pEASData->hwInstData, EAS_TRUE); 1203 return EAS_TRUE; 1204 } 1205 else if (c != 'f') 1206 return EAS_FALSE; 1207 break; 1208 1209 case 4: 1210 if (c == 'f') 1211 { 1212#ifdef _DEBUG_IMELODY 1213 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_GetBackState: backlight off\n"); */ } 1214#endif 1215 EAS_HWBackLight(pEASData->hwInstData, EAS_FALSE); 1216 return EAS_TRUE; 1217 } 1218 return EAS_FALSE; 1219 1220 default: 1221 if (c != backStr[i]) 1222 return EAS_FALSE; 1223 break; 1224 } 1225 } 1226 return EAS_FALSE; 1227} 1228 1229/*---------------------------------------------------------------------------- 1230 * IMY_GetVolume() 1231 *---------------------------------------------------------------------------- 1232 * Purpose: 1233 * 1234 * 1235 * Inputs: 1236 * 1237 * 1238 * Outputs: 1239 * 1240 * 1241 * Side Effects: 1242 * 1243 *---------------------------------------------------------------------------- 1244*/ 1245static EAS_BOOL IMY_GetVolume (EAS_HW_DATA_HANDLE hwInstData, S_IMELODY_DATA *pData, EAS_BOOL inHeader) 1246{ 1247 EAS_INT temp; 1248 EAS_I8 c; 1249 1250#ifdef _DEBUG_IMELODY 1251 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "Enter IMY_GetVolume\n"); */ } 1252#endif 1253 1254 c = IMY_GetNextChar(hwInstData, pData, inHeader); 1255 if (c == '+') 1256 { 1257 if (pData->volume < 15) 1258 pData->volume++; 1259 return EAS_TRUE; 1260 } 1261 else if (c == '-') 1262 { 1263 if (pData->volume > 0) 1264 pData->volume--; 1265 return EAS_TRUE; 1266 } 1267 else if (IsDigit(c)) 1268 temp = c - '0'; 1269 else 1270 return EAS_FALSE; 1271 1272 c = IMY_GetNextChar(hwInstData, pData, inHeader); 1273 if (IsDigit(c)) 1274 temp = temp * 10 + c - '0'; 1275 else if (c) 1276 PutBackChar(pData); 1277 if ((temp >= 0) && (temp <= 15)) 1278 { 1279 if (inHeader && (temp == 0)) 1280 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "Ignoring V0 encountered in header\n"); */ } 1281 else 1282 pData->volume = (EAS_U8) temp; 1283 } 1284 return EAS_TRUE; 1285} 1286 1287/*---------------------------------------------------------------------------- 1288 * IMY_GetNumber() 1289 *---------------------------------------------------------------------------- 1290 * Purpose: 1291 * 1292 * 1293 * Inputs: 1294 * 1295 * 1296 * Outputs: 1297 * 1298 * 1299 * Side Effects: 1300 * 1301 *---------------------------------------------------------------------------- 1302*/ 1303static EAS_BOOL IMY_GetNumber (EAS_HW_DATA_HANDLE hwInstData, S_IMELODY_DATA *pData, EAS_INT *temp, EAS_BOOL inHeader) 1304{ 1305 EAS_BOOL ok; 1306 EAS_I8 c; 1307 1308#ifdef _DEBUG_IMELODY 1309 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "Enter IMY_GetNumber\n"); */ } 1310#endif 1311 1312 *temp = 0; 1313 ok = EAS_FALSE; 1314 for (;;) 1315 { 1316 c = IMY_GetNextChar(hwInstData, pData, inHeader); 1317 if (IsDigit(c)) 1318 { 1319 *temp = *temp * 10 + c - '0'; 1320 ok = EAS_TRUE; 1321 } 1322 else 1323 { 1324 if (c) 1325 PutBackChar(pData); 1326 1327#ifdef _DEBUG_IMELODY 1328 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_GetNumber: value %d\n", *temp); */ } 1329#endif 1330 1331 return ok; 1332 } 1333 } 1334} 1335 1336/*---------------------------------------------------------------------------- 1337 * IMY_GetVersion() 1338 *---------------------------------------------------------------------------- 1339 * Purpose: 1340 * 1341 * 1342 * Inputs: 1343 * 1344 * 1345 * Outputs: 1346 * 1347 * 1348 * Side Effects: 1349 * 1350 *---------------------------------------------------------------------------- 1351*/ 1352static EAS_BOOL IMY_GetVersion (S_IMELODY_DATA *pData, EAS_INT *pVersion) 1353{ 1354 EAS_I8 c; 1355 EAS_INT temp; 1356 EAS_INT version; 1357 1358 version = temp = 0; 1359 for (;;) 1360 { 1361 c = pData->buffer[pData->index++]; 1362 if ((c == 0) || (c == '.')) 1363 { 1364 /*lint -e{701} use shift for performance */ 1365 version = (version << 8) + temp; 1366 if (c == 0) 1367 { 1368 1369#ifdef _DEBUG_IMELODY 1370 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_GetVersion: version 0x%04x\n", version); */ } 1371#endif 1372 1373 *pVersion = version; 1374 return EAS_TRUE; 1375 } 1376 temp = 0; 1377 } 1378 else if (IsDigit(c)) 1379 temp = (temp * 10) + c - '0'; 1380 } 1381} 1382 1383/*---------------------------------------------------------------------------- 1384 * IMY_MetaData() 1385 *---------------------------------------------------------------------------- 1386 * Purpose: 1387 * Prepare to parse the file. Allocates instance data (or uses static allocation for 1388 * static memory model). 1389 * 1390 * Inputs: 1391 * pEASData - pointer to overall EAS data structure 1392 * handle - pointer to file handle 1393 * 1394 * Outputs: 1395 * 1396 * 1397 * Side Effects: 1398 * 1399 *---------------------------------------------------------------------------- 1400*/ 1401static void IMY_MetaData (S_IMELODY_DATA *pData, E_EAS_METADATA_TYPE metaType, EAS_I8 *buffer) 1402{ 1403 EAS_I32 len; 1404 1405 /* check for callback */ 1406 if (!pData->metadata.callback) 1407 return; 1408 1409 /* copy data to host buffer */ 1410 len = (EAS_I32) strlen((char*) buffer); 1411 if (len >pData->metadata.bufferSize) 1412 len = pData->metadata.bufferSize; 1413 strncpy((char*) pData->metadata.buffer, (char*) buffer, (size_t) len); 1414 pData->metadata.buffer[len] = 0; 1415 1416 /* callback to host */ 1417 pData->metadata.callback(metaType, pData->metadata.buffer, pData->metadata.pUserData); 1418} 1419 1420/*---------------------------------------------------------------------------- 1421 * IMY_ParseHeader() 1422 *---------------------------------------------------------------------------- 1423 * Purpose: 1424 * Prepare to parse the file. Allocates instance data (or uses static allocation for 1425 * static memory model). 1426 * 1427 * Inputs: 1428 * pEASData - pointer to overall EAS data structure 1429 * handle - pointer to file handle 1430 * 1431 * Outputs: 1432 * 1433 * 1434 * Side Effects: 1435 * 1436 *---------------------------------------------------------------------------- 1437*/ 1438static EAS_RESULT IMY_ParseHeader (S_EAS_DATA *pEASData, S_IMELODY_DATA* pData) 1439{ 1440 EAS_RESULT result; 1441 EAS_INT token; 1442 EAS_INT temp; 1443 EAS_I8 c; 1444 1445#ifdef _DEBUG_IMELODY 1446 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "Enter IMY_ParseHeader\n"); */ } 1447#endif 1448 1449 /* initialize some defaults */ 1450 pData->time = 0; 1451 pData->tick = DEFAULT_TICK_CONV; 1452 pData->note = 0; 1453 pData->noteModifier = 0; 1454 pData ->restTicks = 0; 1455 pData->volume = 7; 1456 pData->octave = 60; 1457 pData->repeatOffset = -1; 1458 pData->repeatCount = -1; 1459 pData->style = 0; 1460 1461 /* force the read of the first line */ 1462 pData->index = 1; 1463 1464 /* read data until we get to melody */ 1465 for (;;) 1466 { 1467 /* read a line from the file and parse the token */ 1468 if (pData->index != 0) 1469 { 1470 if ((result = IMY_ReadLine(pEASData->hwInstData, pData->fileHandle, pData->buffer, &pData->startLine)) != EAS_SUCCESS) 1471 { 1472#ifdef _DEBUG_IMELODY 1473 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_ParseHeader: IMY_ReadLine returned %d\n", result); */ } 1474#endif 1475 return result; 1476 } 1477 } 1478 token = IMY_ParseLine(pData->buffer, &pData->index); 1479 1480 switch (token) 1481 { 1482 /* ignore these valid tokens */ 1483 case TOKEN_BEGIN: 1484 break; 1485 1486 case TOKEN_FORMAT: 1487 if (!IMY_GetVersion(pData, &temp)) 1488 { 1489 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "Invalid FORMAT field '%s'\n", pData->buffer); */ } 1490 return EAS_ERROR_FILE_FORMAT; 1491 } 1492 if ((temp != 0x0100) && (temp != 0x0200)) 1493 { 1494 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "Unsupported FORMAT %02x\n", temp); */ } 1495 return EAS_ERROR_UNRECOGNIZED_FORMAT; 1496 } 1497 break; 1498 1499 case TOKEN_VERSION: 1500 if (!IMY_GetVersion(pData, &temp)) 1501 { 1502 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "Invalid VERSION field '%s'\n", pData->buffer); */ } 1503 return EAS_ERROR_FILE_FORMAT; 1504 } 1505 if ((temp != 0x0100) && (temp != 0x0102)) 1506 { 1507 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "Unsupported VERSION %02x\n", temp); */ } 1508 return EAS_ERROR_UNRECOGNIZED_FORMAT; 1509 } 1510 break; 1511 1512 case TOKEN_NAME: 1513 IMY_MetaData(pData, EAS_METADATA_TITLE, pData->buffer + pData->index); 1514 break; 1515 1516 case TOKEN_COMPOSER: 1517 IMY_MetaData(pData, EAS_METADATA_AUTHOR, pData->buffer + pData->index); 1518 break; 1519 1520 /* handle beat */ 1521 case TOKEN_BEAT: 1522 IMY_GetNumber(pEASData->hwInstData, pData, &temp, EAS_TRUE); 1523 if ((temp >= 25) && (temp <= 900)) 1524 pData->tick = TICK_CONVERT / temp; 1525 break; 1526 1527 /* handle style */ 1528 case TOKEN_STYLE: 1529 c = IMY_GetNextChar(pEASData->hwInstData, pData, EAS_TRUE); 1530 if (c == 'S') 1531 c = IMY_GetNextChar(pEASData->hwInstData, pData, EAS_TRUE); 1532 if ((c >= '0') && (c <= '2')) 1533 pData->style = (EAS_U8) (c - '0'); 1534 else 1535 { 1536 PutBackChar(pData); 1537 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "Error in style command: %s\n", pData->buffer); */ } 1538 } 1539 break; 1540 1541 /* handle volume */ 1542 case TOKEN_VOLUME: 1543 c = IMY_GetNextChar(pEASData->hwInstData, pData, EAS_TRUE); 1544 if (c != 'V') 1545 { 1546 PutBackChar(pData); 1547 if (!IsDigit(c)) 1548 { 1549 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "Error in volume command: %s\n", pData->buffer); */ } 1550 break; 1551 } 1552 } 1553 IMY_GetVolume(pEASData->hwInstData, pData, EAS_TRUE); 1554 break; 1555 1556 case TOKEN_MELODY: 1557#ifdef _DEBUG_IMELODY 1558 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "Header successfully parsed\n"); */ } 1559#endif 1560 return EAS_SUCCESS; 1561 1562 case TOKEN_END: 1563 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "Unexpected END:IMELODY encountered\n"); */ } 1564 return EAS_ERROR_FILE_FORMAT; 1565 1566 default: 1567 /* force a read of the next line */ 1568 pData->index = 1; 1569 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "Ignoring unrecognized token in iMelody file: %s\n", pData->buffer); */ } 1570 break; 1571 } 1572 } 1573} 1574 1575/*---------------------------------------------------------------------------- 1576 * IMY_GetNextChar() 1577 *---------------------------------------------------------------------------- 1578 * Purpose: 1579 * 1580 * 1581 * Inputs: 1582 * 1583 * 1584 * Outputs: 1585 * 1586 * 1587 * Side Effects: 1588 * 1589 *---------------------------------------------------------------------------- 1590*/ 1591static EAS_I8 IMY_GetNextChar (EAS_HW_DATA_HANDLE hwInstData, S_IMELODY_DATA *pData, EAS_BOOL inHeader) 1592{ 1593 EAS_I8 c; 1594 EAS_U8 index; 1595 1596 for (;;) 1597 { 1598 /* get next character */ 1599 c = pData->buffer[pData->index++]; 1600 1601 /* buffer empty, read more */ 1602 if (!c) 1603 { 1604 /* don't read the next line in the header */ 1605 if (inHeader) 1606 return 0; 1607 1608 pData->index = 0; 1609 pData->buffer[0] = 0; 1610 if (IMY_ReadLine(hwInstData, pData->fileHandle, pData->buffer, &pData->startLine) != EAS_SUCCESS) 1611 { 1612#ifdef _DEBUG_IMELODY 1613 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_GetNextChar: EOF\n"); */ } 1614#endif 1615 return 0; 1616 } 1617 1618 /* check for END:IMELODY token */ 1619 if (IMY_ParseLine(pData->buffer, &index) == TOKEN_END) 1620 { 1621#ifdef _DEBUG_IMELODY 1622 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_GetNextChar: found END:IMELODY\n"); */ } 1623#endif 1624 pData->buffer[0] = 0; 1625 return 0; 1626 } 1627 continue; 1628 } 1629 1630 /* ignore white space */ 1631 if (!IsSpace(c)) 1632 { 1633 1634#ifdef _DEBUG_IMELODY 1635 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_GetNextChar returned '%c'\n", c); */ } 1636#endif 1637 return c; 1638 } 1639 } 1640} 1641 1642/*---------------------------------------------------------------------------- 1643 * IMY_ReadLine() 1644 *---------------------------------------------------------------------------- 1645 * Purpose: 1646 * Reads a line of input from the file, discarding the CR/LF 1647 * 1648 * Inputs: 1649 * 1650 * 1651 * Outputs: 1652 * 1653 * 1654 * Side Effects: 1655 * 1656 *---------------------------------------------------------------------------- 1657*/ 1658static EAS_RESULT IMY_ReadLine (EAS_HW_DATA_HANDLE hwInstData, EAS_FILE_HANDLE fileHandle, EAS_I8 *buffer, EAS_I32 *pStartLine) 1659{ 1660 EAS_RESULT result; 1661 EAS_INT i; 1662 EAS_I8 c; 1663 1664 /* fetch current file position and save it */ 1665 if (pStartLine != NULL) 1666 { 1667 if ((result = EAS_HWFilePos(hwInstData, fileHandle, pStartLine)) != EAS_SUCCESS) 1668 { 1669#ifdef _DEBUG_IMELODY 1670 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_ParseHeader: EAS_HWFilePos returned %d\n", result); */ } 1671#endif 1672 return result; 1673 } 1674 } 1675 1676 buffer[0] = 0; 1677 for (i = 0; i < MAX_LINE_SIZE; ) 1678 { 1679 if ((result = EAS_HWGetByte(hwInstData, fileHandle, &c)) != EAS_SUCCESS) 1680 { 1681 if ((result == EAS_EOF) && (i > 0)) 1682 break; 1683 return result; 1684 } 1685 1686 /* return on LF or end of data */ 1687 if (c == '\n') 1688 break; 1689 1690 /* store characters in buffer */ 1691 if (c != '\r') 1692 buffer[i++] = c; 1693 } 1694 buffer[i] = 0; 1695 1696#ifdef _DEBUG_IMELODY 1697 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_ReadLine read %s\n", buffer); */ } 1698#endif 1699 1700 return EAS_SUCCESS; 1701} 1702 1703/*---------------------------------------------------------------------------- 1704 * IMY_ParseLine() 1705 *---------------------------------------------------------------------------- 1706 * Purpose: 1707 * 1708 * 1709 * Inputs: 1710 * 1711 * 1712 * Outputs: 1713 * 1714 * 1715 * Side Effects: 1716 * 1717 *---------------------------------------------------------------------------- 1718*/ 1719static EAS_INT IMY_ParseLine (EAS_I8 *buffer, EAS_U8 *pIndex) 1720{ 1721 EAS_INT i; 1722 EAS_INT j; 1723 1724 /* there's no strnicmp() in stdlib, so we have to roll our own */ 1725 for (i = 0; i < TOKEN_INVALID; i++) 1726 { 1727 for (j = 0; ; j++) 1728 { 1729 /* end of token, must be a match */ 1730 if (tokens[i][j] == 0) 1731 { 1732#ifdef _DEBUG_IMELODY 1733 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_ParseLine found token %d\n", i); */ } 1734#endif 1735 *pIndex = (EAS_U8) j; 1736 return i; 1737 } 1738 if (tokens[i][j] != ToUpper(buffer[j])) 1739 break; 1740 } 1741 } 1742#ifdef _DEBUG_IMELODY 1743 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_ParseLine: no token found\n"); */ } 1744#endif 1745 return TOKEN_INVALID; 1746} 1747 1748