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 /* initialize MIDI channel when the track starts playing */ 341 if (pData->time == 0) 342 { 343 344#ifdef _DEBUG_IMELODY 345 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_Event: Reset\n"); */ } 346#endif 347 /* set program to square lead */ 348 VMProgramChange(pEASData->pVoiceMgr, pData->pSynth, IMELODY_CHANNEL, IMELODY_PROGRAM); 349 350 /* set channel volume to max */ 351 VMControlChange(pEASData->pVoiceMgr, pData->pSynth, IMELODY_CHANNEL, 7, 127); 352 } 353 354 /* check for end of note */ 355 if (pData->note) 356 { 357 358#ifdef _DEBUG_IMELODY 359 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "Stopping note %d\n", pData->note); */ } 360#endif 361 /* stop the note */ 362 VMStopNote(pEASData->pVoiceMgr, pData->pSynth, IMELODY_CHANNEL, pData->note, 0); 363 pData->note = 0; 364 365 /* check for rest between notes */ 366 if (pData->restTicks) 367 { 368 pData->time += pData->restTicks; 369 pData->restTicks = 0; 370 return EAS_SUCCESS; 371 } 372 } 373 374 /* parse the next event */ 375 eof = EAS_FALSE; 376 while (!eof) 377 { 378 379 /* get next character */ 380 c = IMY_GetNextChar(pEASData->hwInstData, pData, EAS_FALSE); 381 382 switch (c) 383 { 384 /* start repeat */ 385 case '(': 386 387#ifdef _DEBUG_IMELODY 388 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "Enter repeat section\n", c); */ } 389#endif 390 391 if (pData->repeatOffset < 0) 392 { 393 pData->repeatOffset = pData->startLine + (EAS_I32) pData->index; 394 395#ifdef _DEBUG_IMELODY 396 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "Repeat offset = %d\n", pData->repeatOffset); */ } 397#endif 398 } 399 else 400 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "Ignoring nested repeat section\n"); */ } 401 break; 402 403 /* end repeat */ 404 case ')': 405 406#ifdef _DEBUG_IMELODY 407 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "End repeat section, repeat offset = %d\n", pData->repeatOffset); */ } 408#endif 409 /* ignore invalid repeats */ 410 if (pData->repeatCount >= 0) 411 { 412 413 /* decrement repeat count (repeatCount == 0 means infinite loop) */ 414 if (pData->repeatCount > 0) 415 { 416 if (--pData->repeatCount == 0) 417 { 418 pData->repeatCount = -1; 419#ifdef _DEBUG_IMELODY 420 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "Repeat loop complete\n"); */ } 421#endif 422 } 423 } 424 425//2 TEMPORARY FIX: If locating, don't do infinite loops. 426//3 We need a different mode for metadata parsing where we don't loop at all 427 if ((parserMode == eParserModePlay) || (pData->repeatCount != 0)) 428 { 429 430#ifdef _DEBUG_IMELODY 431 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "Rewinding file for repeat\n"); */ } 432#endif 433 /* rewind to start of loop */ 434 if ((result = EAS_HWFileSeek(pEASData->hwInstData, pData->fileHandle, pData->repeatOffset)) != EAS_SUCCESS) 435 return result; 436 IMY_ReadLine(pEASData->hwInstData, pData->fileHandle, pData->buffer, &pData->startLine); 437 pData->index = 0; 438 439 /* if last loop, prevent future loops */ 440 if (pData->repeatCount == -1) 441 pData->repeatOffset = -1; 442 } 443 } 444 break; 445 446 /* repeat count */ 447 case '@': 448 if (!IMY_GetNumber(pEASData->hwInstData, pData, &temp, EAS_FALSE)) 449 eof = EAS_TRUE; 450 else if (pData->repeatOffset > 0) 451 { 452 453#ifdef _DEBUG_IMELODY 454 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "Repeat count = %dt", pData->repeatCount); */ } 455#endif 456 if (pData->repeatCount < 0) 457 pData->repeatCount = (EAS_I16) temp; 458 } 459 break; 460 461 /* volume */ 462 case 'V': 463 if (!IMY_GetVolume(pEASData->hwInstData, pData, EAS_FALSE)) 464 eof = EAS_TRUE; 465 break; 466 467 /* flat */ 468 case '&': 469 pData->noteModifier = -1; 470 break; 471 472 /* sharp */ 473 case '#': 474 pData->noteModifier = +1; 475 break; 476 477 /* octave */ 478 case '*': 479 c = IMY_GetNextChar(pEASData->hwInstData, pData, EAS_FALSE); 480 if (IsDigit(c)) 481 pData->octave = (EAS_U8) ((c - '0' + 1) * 12); 482 else if (!c) 483 eof = EAS_TRUE; 484 break; 485 486 /* ledon or ledoff */ 487 case 'l': 488 if (!IMY_GetLEDState(pEASData, pData)) 489 eof = EAS_TRUE; 490 break; 491 492 /* vibeon or vibeoff */ 493 case 'v': 494 if (!IMY_GetVibeState(pEASData, pData)) 495 eof = EAS_TRUE; 496 break; 497 498 /* either a B note or backon or backoff */ 499 case 'b': 500 if (IMY_GetNextChar(pEASData->hwInstData, pData, EAS_FALSE) == 'a') 501 { 502 if (!IMY_GetBackState(pEASData, pData)) 503 eof = EAS_TRUE; 504 } 505 else 506 { 507 PutBackChar(pData); 508 if (IMY_PlayNote(pEASData, pData, c, parserMode)) 509 return EAS_SUCCESS; 510 eof = EAS_TRUE; 511 } 512 break; 513 514 /* rest */ 515 case 'r': 516 case 'R': 517 if (IMY_PlayRest(pEASData, pData)) 518 return EAS_SUCCESS; 519 eof = EAS_TRUE; 520 break; 521 522 /* EOF */ 523 case 0: 524#ifdef _DEBUG_IMELODY 525 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_Event: end of iMelody file detected\n"); */ } 526#endif 527 eof = EAS_TRUE; 528 break; 529 530 /* must be a note */ 531 default: 532 c = ToLower(c); 533 if ((c >= 'a') && (c <= 'g')) 534 { 535 if (IMY_PlayNote(pEASData, pData, c, parserMode)) 536 return EAS_SUCCESS; 537 eof = EAS_TRUE; 538 } 539 else 540 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "Ignoring unexpected character '%c' [0x%02x]\n", c, c); */ } 541 break; 542 } 543 } 544 545 /* handle EOF */ 546#ifdef _DEBUG_IMELODY 547 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_Event: state set to EAS_STATE_STOPPING\n"); */ } 548#endif 549 pData->state = EAS_STATE_STOPPING; 550 VMReleaseAllVoices(pEASData->pVoiceMgr, pData->pSynth); 551 return EAS_SUCCESS; 552} 553 554/*---------------------------------------------------------------------------- 555 * IMY_State() 556 *---------------------------------------------------------------------------- 557 * Purpose: 558 * Returns the current state of the stream 559 * 560 * Inputs: 561 * pEASData - pointer to overall EAS data structure 562 * handle - pointer to file handle 563 * pState - pointer to variable to store state 564 * 565 * Outputs: 566 * 567 * 568 * Side Effects: 569 * 570 *---------------------------------------------------------------------------- 571*/ 572/*lint -esym(715, pEASData) common decoder interface - pEASData not used */ 573static EAS_RESULT IMY_State (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 *pState) 574{ 575 S_IMELODY_DATA* pData; 576 577 /* establish pointer to instance data */ 578 pData = (S_IMELODY_DATA*) pInstData; 579 580 /* if stopping, check to see if synth voices are active */ 581 if (pData->state == EAS_STATE_STOPPING) 582 { 583 if (VMActiveVoices(pData->pSynth) == 0) 584 { 585 pData->state = EAS_STATE_STOPPED; 586#ifdef _DEBUG_IMELODY 587 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_State: state set to EAS_STATE_STOPPED\n"); */ } 588#endif 589 } 590 } 591 592 if (pData->state == EAS_STATE_PAUSING) 593 { 594 if (VMActiveVoices(pData->pSynth) == 0) 595 { 596#ifdef _DEBUG_IMELODY 597 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_State: state set to EAS_STATE_PAUSED\n"); */ } 598#endif 599 pData->state = EAS_STATE_PAUSED; 600 } 601 } 602 603 /* return current state */ 604 *pState = pData->state; 605 return EAS_SUCCESS; 606} 607 608/*---------------------------------------------------------------------------- 609 * IMY_Close() 610 *---------------------------------------------------------------------------- 611 * Purpose: 612 * Close the file and clean up 613 * 614 * Inputs: 615 * pEASData - pointer to overall EAS data structure 616 * handle - pointer to file handle 617 * 618 * Outputs: 619 * 620 * 621 * Side Effects: 622 * 623 *---------------------------------------------------------------------------- 624*/ 625static EAS_RESULT IMY_Close (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData) 626{ 627 S_IMELODY_DATA* pData; 628 EAS_RESULT result; 629 630#ifdef _DEBUG_IMELODY 631 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_Close: close file\n"); */ } 632#endif 633 634 pData = (S_IMELODY_DATA*) pInstData; 635 636 /* close the file */ 637 if ((result = EAS_HWCloseFile(pEASData->hwInstData, pData->fileHandle)) != EAS_SUCCESS) 638 return result; 639 640 /* free the synth */ 641 if (pData->pSynth != NULL) 642 VMMIDIShutdown(pEASData, pData->pSynth); 643 644 /* if using dynamic memory, free it */ 645 if (!pEASData->staticMemoryModel) 646 EAS_HWFree(pEASData->hwInstData, pData); 647 648 return EAS_SUCCESS; 649} 650 651/*---------------------------------------------------------------------------- 652 * IMY_Reset() 653 *---------------------------------------------------------------------------- 654 * Purpose: 655 * Reset the sequencer. Used for locating backwards in the file. 656 * 657 * Inputs: 658 * pEASData - pointer to overall EAS data structure 659 * handle - pointer to file handle 660 * 661 * Outputs: 662 * 663 * 664 * Side Effects: 665 * 666 *---------------------------------------------------------------------------- 667*/ 668static EAS_RESULT IMY_Reset (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData) 669{ 670 S_IMELODY_DATA* pData; 671 EAS_RESULT result; 672 673#ifdef _DEBUG_IMELODY 674 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_Reset: reset file\n"); */ } 675#endif 676 pData = (S_IMELODY_DATA*) pInstData; 677 678 /* reset the synth */ 679 VMReset(pEASData->pVoiceMgr, pData->pSynth, EAS_TRUE); 680 681 /* reset time to zero */ 682 pData->time = 0; 683 pData->note = 0; 684 685 /* reset file position and re-parse header */ 686 pData->state = EAS_STATE_ERROR; 687 if ((result = EAS_HWFileSeek(pEASData->hwInstData, pData->fileHandle, pData->fileOffset)) != EAS_SUCCESS) 688 return result; 689 if ((result = IMY_ParseHeader (pEASData, pData)) != EAS_SUCCESS) 690 return result; 691 692#ifdef _DEBUG_IMELODY 693 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_Reset: state set to EAS_STATE_ERROR\n"); */ } 694#endif 695 696 pData->state = EAS_STATE_READY; 697 return EAS_SUCCESS; 698} 699 700/*---------------------------------------------------------------------------- 701 * IMY_Pause() 702 *---------------------------------------------------------------------------- 703 * Purpose: 704 * Pauses the sequencer. Mutes all voices and sets state to pause. 705 * 706 * Inputs: 707 * pEASData - pointer to overall EAS data structure 708 * handle - pointer to file handle 709 * 710 * Outputs: 711 * 712 * 713 * Side Effects: 714 * 715 *---------------------------------------------------------------------------- 716*/ 717static EAS_RESULT IMY_Pause (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData) 718{ 719 S_IMELODY_DATA *pData; 720 721#ifdef _DEBUG_IMELODY 722 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_Pause: pause file\n"); */ } 723#endif 724 725 /* can't pause a stopped stream */ 726 pData = (S_IMELODY_DATA*) pInstData; 727 if (pData->state == EAS_STATE_STOPPED) 728 return EAS_ERROR_ALREADY_STOPPED; 729 730 /* mute the synthesizer */ 731 VMMuteAllVoices(pEASData->pVoiceMgr, pData->pSynth); 732 pData->state = EAS_STATE_PAUSING; 733 return EAS_SUCCESS; 734} 735 736/*---------------------------------------------------------------------------- 737 * IMY_Resume() 738 *---------------------------------------------------------------------------- 739 * Purpose: 740 * Resume playing after a pause, sets state back to playing. 741 * 742 * Inputs: 743 * pEASData - pointer to overall EAS data structure 744 * handle - pointer to file handle 745 * 746 * Outputs: 747 * 748 * 749 * Side Effects: 750 * 751 *---------------------------------------------------------------------------- 752*/ 753/*lint -esym(715, pEASData) common decoder interface - pEASData not used */ 754static EAS_RESULT IMY_Resume (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData) 755{ 756 S_IMELODY_DATA *pData; 757 758#ifdef _DEBUG_IMELODY 759 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_Resume: resume file\n"); */ } 760#endif 761 762 /* can't resume a stopped stream */ 763 pData = (S_IMELODY_DATA*) pInstData; 764 if (pData->state == EAS_STATE_STOPPED) 765 return EAS_ERROR_ALREADY_STOPPED; 766 767 /* nothing to do but resume playback */ 768 pData->state = EAS_STATE_PLAY; 769 return EAS_SUCCESS; 770} 771 772/*---------------------------------------------------------------------------- 773 * IMY_SetData() 774 *---------------------------------------------------------------------------- 775 * Purpose: 776 * Adjust tempo relative to song tempo 777 * 778 * Inputs: 779 * pEASData - pointer to overall EAS data structure 780 * pInstData - pointer to iMelody instance data 781 * rate - rate (28-bit fractional amount) 782 * 783 * Outputs: 784 * 785 * 786 * Side Effects: 787 * 788 *---------------------------------------------------------------------------- 789*/ 790/*lint -esym(715, pEASData) common decoder interface - pEASData not used */ 791static EAS_RESULT IMY_SetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 value) 792{ 793 S_IMELODY_DATA *pData; 794 795 pData = (S_IMELODY_DATA*) pInstData; 796 switch (param) 797 { 798 799 /* set metadata callback */ 800 case PARSER_DATA_METADATA_CB: 801 EAS_HWMemCpy(&pData->metadata, (void*) value, sizeof(S_METADATA_CB)); 802 break; 803 804 default: 805 return EAS_ERROR_INVALID_PARAMETER; 806 } 807 808 return EAS_SUCCESS; 809} 810 811/*---------------------------------------------------------------------------- 812 * IMY_GetData() 813 *---------------------------------------------------------------------------- 814 * Purpose: 815 * Return the file type 816 * 817 * Inputs: 818 * pEASData - pointer to overall EAS data structure 819 * pInstData - pointer to iMelody instance data 820 * 821 * Outputs: 822 * 823 * 824 * Side Effects: 825 * 826 *---------------------------------------------------------------------------- 827*/ 828/*lint -esym(715, pEASData) common decoder interface - pEASData not used */ 829static EAS_RESULT IMY_GetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 *pValue) 830{ 831 S_IMELODY_DATA *pData; 832 833 pData = (S_IMELODY_DATA*) pInstData; 834 835 switch (param) 836 { 837 /* return file type as iMelody */ 838 case PARSER_DATA_FILE_TYPE: 839 *pValue = EAS_FILE_IMELODY; 840 break; 841 842 case PARSER_DATA_SYNTH_HANDLE: 843 *pValue = (EAS_I32) pData->pSynth; 844 break; 845 846 case PARSER_DATA_GAIN_OFFSET: 847 *pValue = IMELODY_GAIN_OFFSET; 848 break; 849 850 default: 851 return EAS_ERROR_INVALID_PARAMETER; 852 } 853 854 return EAS_SUCCESS; 855} 856 857/*---------------------------------------------------------------------------- 858 * IMY_PlayNote() 859 *---------------------------------------------------------------------------- 860 * Purpose: 861 * 862 * 863 * Inputs: 864 * 865 * 866 * Outputs: 867 * 868 * 869 * Side Effects: 870 * 871 *---------------------------------------------------------------------------- 872*/ 873static EAS_BOOL IMY_PlayNote (S_EAS_DATA *pEASData, S_IMELODY_DATA *pData, EAS_I8 note, EAS_INT parserMode) 874{ 875 EAS_I32 duration; 876 EAS_U8 velocity; 877 878 879#ifdef _DEBUG_IMELODY 880 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_PlayNote: start note %d\n", note); */ } 881#endif 882 883 /* get the duration */ 884 if (!IMY_GetDuration(pEASData->hwInstData, pData, &duration)) 885 return EAS_FALSE; 886 887 /* save note value */ 888 pData->note = (EAS_U8) (pData->octave + noteTable[note - 'a'] + pData->noteModifier); 889 velocity = (EAS_U8) (pData->volume ? pData->volume * IMELODY_VEL_MUL + IMELODY_VEL_OFS : 0); 890 891 /* start note only if in play mode */ 892 if (parserMode == eParserModePlay) 893 VMStartNote(pEASData->pVoiceMgr, pData->pSynth, IMELODY_CHANNEL, pData->note, velocity); 894 895#ifdef _DEBUG_IMELODY 896 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_PlayNote: Start note %d, duration %d\n", pData->note, duration); */ } 897#endif 898 899 /* determine note length */ 900 switch (pData->style) 901 { 902 case 0: 903 /*lint -e{704} shift for performance */ 904 pData->restTicks = duration >> 4; 905 break; 906 case 1: 907 pData->restTicks = 0; 908 break; 909 case 2: 910 /*lint -e{704} shift for performance */ 911 pData->restTicks = duration >> 1; 912 break; 913 default: 914 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "IMY_PlayNote: Note style out of range: %d\n", pData->style); */ } 915 /*lint -e{704} shift for performance */ 916 pData->restTicks = duration >> 4; 917 break; 918 } 919 920 /* next event is at end of this note */ 921 pData->time += duration - pData->restTicks; 922 923 /* reset the flat/sharp modifier */ 924 pData->noteModifier = 0; 925 926 return EAS_TRUE; 927} 928 929/*---------------------------------------------------------------------------- 930 * IMY_PlayRest() 931 *---------------------------------------------------------------------------- 932 * Purpose: 933 * 934 * 935 * Inputs: 936 * 937 * 938 * Outputs: 939 * 940 * 941 * Side Effects: 942 * 943 *---------------------------------------------------------------------------- 944*/ 945static EAS_BOOL IMY_PlayRest (S_EAS_DATA *pEASData, S_IMELODY_DATA *pData) 946{ 947 EAS_I32 duration; 948 949#ifdef _DEBUG_IMELODY 950 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "Enter IMY_PlayRest]n"); */ } 951#endif 952 953 /* get the duration */ 954 if (!IMY_GetDuration(pEASData->hwInstData, pData, &duration)) 955 return EAS_FALSE; 956 957#ifdef _DEBUG_IMELODY 958 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_PlayRest: note duration %d\n", duration); */ } 959#endif 960 961 /* next event is at end of this note */ 962 pData->time += duration; 963 return EAS_TRUE; 964} 965 966/*---------------------------------------------------------------------------- 967 * IMY_GetDuration() 968 *---------------------------------------------------------------------------- 969 * Purpose: 970 * 971 * 972 * Inputs: 973 * 974 * 975 * Outputs: 976 * 977 * 978 * Side Effects: 979 * 980 *---------------------------------------------------------------------------- 981*/ 982 983static EAS_BOOL IMY_GetDuration (EAS_HW_DATA_HANDLE hwInstData, S_IMELODY_DATA *pData, EAS_I32 *pDuration) 984{ 985 EAS_I32 duration; 986 EAS_I8 c; 987 988 /* get the duration */ 989 *pDuration = 0; 990 c = IMY_GetNextChar(hwInstData, pData, EAS_FALSE); 991 if (!c) 992 return EAS_FALSE; 993 if ((c < '0') || (c > '5')) 994 { 995#ifdef _DEBUG_IMELODY 996 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_GetDuration: error in duration '%c'\n", c); */ } 997#endif 998 return EAS_FALSE; 999 } 1000 1001 /* calculate total length of note */ 1002 duration = pData->tick * (1 << ('5' - c)); 1003 1004 /* check for duration modifier */ 1005 c = IMY_GetNextChar(hwInstData, pData, EAS_FALSE); 1006 if (c) 1007 { 1008 if (c == '.') 1009 /*lint -e{704} shift for performance */ 1010 duration += duration >> 1; 1011 else if (c == ':') 1012 /*lint -e{704} shift for performance */ 1013 duration += (duration >> 1) + (duration >> 2); 1014 else if (c == ';') 1015 /*lint -e{704} shift for performance */ 1016 duration = (duration * TRIPLET_MULTIPLIER) >> TRIPLET_SHIFT; 1017 else 1018 PutBackChar(pData); 1019 } 1020 1021 *pDuration = duration; 1022 return EAS_TRUE; 1023} 1024 1025/*---------------------------------------------------------------------------- 1026 * IMY_GetLEDState() 1027 *---------------------------------------------------------------------------- 1028 * Purpose: 1029 * 1030 * 1031 * Inputs: 1032 * 1033 * 1034 * Outputs: 1035 * 1036 * 1037 * Side Effects: 1038 * 1039 *---------------------------------------------------------------------------- 1040*/ 1041static EAS_BOOL IMY_GetLEDState (S_EAS_DATA *pEASData, S_IMELODY_DATA *pData) 1042{ 1043 EAS_I8 c; 1044 EAS_INT i; 1045 1046#ifdef _DEBUG_IMELODY 1047 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "Enter IMY_GetLEDState\n"); */ } 1048#endif 1049 1050 for (i = 0; i < 5; i++) 1051 { 1052 c = IMY_GetNextChar(pEASData->hwInstData, pData, EAS_FALSE); 1053 if (!c) 1054 return EAS_FALSE; 1055 switch (i) 1056 { 1057 case 3: 1058 if (c == 'n') 1059 { 1060#ifdef _DEBUG_IMELODY 1061 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_GetLEDState: LED on\n"); */ } 1062#endif 1063 EAS_HWLED(pEASData->hwInstData, EAS_TRUE); 1064 return EAS_TRUE; 1065 } 1066 else if (c != 'f') 1067 return EAS_FALSE; 1068 break; 1069 1070 case 4: 1071 if (c == 'f') 1072 { 1073#ifdef _DEBUG_IMELODY 1074 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_GetLEDState: LED off\n"); */ } 1075#endif 1076 EAS_HWLED(pEASData->hwInstData, EAS_FALSE); 1077 return EAS_TRUE; 1078 } 1079 return EAS_FALSE; 1080 1081 default: 1082 if (c != ledStr[i]) 1083 return EAS_FALSE; 1084 break; 1085 } 1086 } 1087 return EAS_FALSE; 1088} 1089 1090/*---------------------------------------------------------------------------- 1091 * IMY_GetVibeState() 1092 *---------------------------------------------------------------------------- 1093 * Purpose: 1094 * 1095 * 1096 * Inputs: 1097 * 1098 * 1099 * Outputs: 1100 * 1101 * 1102 * Side Effects: 1103 * 1104 *---------------------------------------------------------------------------- 1105*/ 1106static EAS_BOOL IMY_GetVibeState (S_EAS_DATA *pEASData, S_IMELODY_DATA *pData) 1107{ 1108 EAS_I8 c; 1109 EAS_INT i; 1110 1111#ifdef _DEBUG_IMELODY 1112 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "Enter IMY_GetVibeState\n"); */ } 1113#endif 1114 1115 for (i = 0; i < 6; i++) 1116 { 1117 c = IMY_GetNextChar(pEASData->hwInstData, pData, EAS_FALSE); 1118 if (!c) 1119 return EAS_FALSE; 1120 switch (i) 1121 { 1122 case 4: 1123 if (c == 'n') 1124 { 1125#ifdef _DEBUG_IMELODY 1126 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_GetVibeState: vibrate on\n"); */ } 1127#endif 1128 EAS_HWVibrate(pEASData->hwInstData, EAS_TRUE); 1129 return EAS_TRUE; 1130 } 1131 else if (c != 'f') 1132 return EAS_FALSE; 1133 break; 1134 1135 case 5: 1136 if (c == 'f') 1137 { 1138#ifdef _DEBUG_IMELODY 1139 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_GetVibeState: vibrate off\n"); */ } 1140#endif 1141 EAS_HWVibrate(pEASData->hwInstData, EAS_FALSE); 1142 return EAS_TRUE; 1143 } 1144 return EAS_FALSE; 1145 1146 default: 1147 if (c != vibeStr[i]) 1148 return EAS_FALSE; 1149 break; 1150 } 1151 } 1152 return EAS_FALSE; 1153} 1154 1155/*---------------------------------------------------------------------------- 1156 * IMY_GetBackState() 1157 *---------------------------------------------------------------------------- 1158 * Purpose: 1159 * 1160 * 1161 * Inputs: 1162 * 1163 * 1164 * Outputs: 1165 * 1166 * 1167 * Side Effects: 1168 * 1169 *---------------------------------------------------------------------------- 1170*/ 1171static EAS_BOOL IMY_GetBackState (S_EAS_DATA *pEASData, S_IMELODY_DATA *pData) 1172{ 1173 EAS_I8 c; 1174 EAS_INT i; 1175 1176#ifdef _DEBUG_IMELODY 1177 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "Enter IMY_GetBackState\n"); */ } 1178#endif 1179 1180 for (i = 0; i < 5; i++) 1181 { 1182 c = IMY_GetNextChar(pEASData->hwInstData, pData, EAS_FALSE); 1183 if (!c) 1184 return EAS_FALSE; 1185 switch (i) 1186 { 1187 case 3: 1188 if (c == 'n') 1189 { 1190#ifdef _DEBUG_IMELODY 1191 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_GetBackState: backlight on\n"); */ } 1192#endif 1193 EAS_HWBackLight(pEASData->hwInstData, EAS_TRUE); 1194 return EAS_TRUE; 1195 } 1196 else if (c != 'f') 1197 return EAS_FALSE; 1198 break; 1199 1200 case 4: 1201 if (c == 'f') 1202 { 1203#ifdef _DEBUG_IMELODY 1204 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_GetBackState: backlight off\n"); */ } 1205#endif 1206 EAS_HWBackLight(pEASData->hwInstData, EAS_FALSE); 1207 return EAS_TRUE; 1208 } 1209 return EAS_FALSE; 1210 1211 default: 1212 if (c != backStr[i]) 1213 return EAS_FALSE; 1214 break; 1215 } 1216 } 1217 return EAS_FALSE; 1218} 1219 1220/*---------------------------------------------------------------------------- 1221 * IMY_GetVolume() 1222 *---------------------------------------------------------------------------- 1223 * Purpose: 1224 * 1225 * 1226 * Inputs: 1227 * 1228 * 1229 * Outputs: 1230 * 1231 * 1232 * Side Effects: 1233 * 1234 *---------------------------------------------------------------------------- 1235*/ 1236static EAS_BOOL IMY_GetVolume (EAS_HW_DATA_HANDLE hwInstData, S_IMELODY_DATA *pData, EAS_BOOL inHeader) 1237{ 1238 EAS_INT temp; 1239 EAS_I8 c; 1240 1241#ifdef _DEBUG_IMELODY 1242 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "Enter IMY_GetVolume\n"); */ } 1243#endif 1244 1245 c = IMY_GetNextChar(hwInstData, pData, inHeader); 1246 if (c == '+') 1247 { 1248 if (pData->volume < 15) 1249 pData->volume++; 1250 return EAS_TRUE; 1251 } 1252 else if (c == '-') 1253 { 1254 if (pData->volume > 0) 1255 pData->volume--; 1256 return EAS_TRUE; 1257 } 1258 else if (IsDigit(c)) 1259 temp = c - '0'; 1260 else 1261 return EAS_FALSE; 1262 1263 c = IMY_GetNextChar(hwInstData, pData, inHeader); 1264 if (IsDigit(c)) 1265 temp = temp * 10 + c - '0'; 1266 else if (c) 1267 PutBackChar(pData); 1268 if ((temp >= 0) && (temp <= 15)) 1269 { 1270 if (inHeader && (temp == 0)) 1271 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "Ignoring V0 encountered in header\n"); */ } 1272 else 1273 pData->volume = (EAS_U8) temp; 1274 } 1275 return EAS_TRUE; 1276} 1277 1278/*---------------------------------------------------------------------------- 1279 * IMY_GetNumber() 1280 *---------------------------------------------------------------------------- 1281 * Purpose: 1282 * 1283 * 1284 * Inputs: 1285 * 1286 * 1287 * Outputs: 1288 * 1289 * 1290 * Side Effects: 1291 * 1292 *---------------------------------------------------------------------------- 1293*/ 1294static EAS_BOOL IMY_GetNumber (EAS_HW_DATA_HANDLE hwInstData, S_IMELODY_DATA *pData, EAS_INT *temp, EAS_BOOL inHeader) 1295{ 1296 EAS_BOOL ok; 1297 EAS_I8 c; 1298 1299#ifdef _DEBUG_IMELODY 1300 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "Enter IMY_GetNumber\n"); */ } 1301#endif 1302 1303 *temp = 0; 1304 ok = EAS_FALSE; 1305 for (;;) 1306 { 1307 c = IMY_GetNextChar(hwInstData, pData, inHeader); 1308 if (IsDigit(c)) 1309 { 1310 *temp = *temp * 10 + c - '0'; 1311 ok = EAS_TRUE; 1312 } 1313 else 1314 { 1315 if (c) 1316 PutBackChar(pData); 1317 1318#ifdef _DEBUG_IMELODY 1319 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_GetNumber: value %d\n", *temp); */ } 1320#endif 1321 1322 return ok; 1323 } 1324 } 1325} 1326 1327/*---------------------------------------------------------------------------- 1328 * IMY_GetVersion() 1329 *---------------------------------------------------------------------------- 1330 * Purpose: 1331 * 1332 * 1333 * Inputs: 1334 * 1335 * 1336 * Outputs: 1337 * 1338 * 1339 * Side Effects: 1340 * 1341 *---------------------------------------------------------------------------- 1342*/ 1343static EAS_BOOL IMY_GetVersion (S_IMELODY_DATA *pData, EAS_INT *pVersion) 1344{ 1345 EAS_I8 c; 1346 EAS_INT temp; 1347 EAS_INT version; 1348 1349 version = temp = 0; 1350 for (;;) 1351 { 1352 c = pData->buffer[pData->index++]; 1353 if ((c == 0) || (c == '.')) 1354 { 1355 /*lint -e{701} use shift for performance */ 1356 version = (version << 8) + temp; 1357 if (c == 0) 1358 { 1359 1360#ifdef _DEBUG_IMELODY 1361 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_GetVersion: version 0x%04x\n", version); */ } 1362#endif 1363 1364 *pVersion = version; 1365 return EAS_TRUE; 1366 } 1367 temp = 0; 1368 } 1369 else if (IsDigit(c)) 1370 temp = (temp * 10) + c - '0'; 1371 } 1372} 1373 1374/*---------------------------------------------------------------------------- 1375 * IMY_MetaData() 1376 *---------------------------------------------------------------------------- 1377 * Purpose: 1378 * Prepare to parse the file. Allocates instance data (or uses static allocation for 1379 * static memory model). 1380 * 1381 * Inputs: 1382 * pEASData - pointer to overall EAS data structure 1383 * handle - pointer to file handle 1384 * 1385 * Outputs: 1386 * 1387 * 1388 * Side Effects: 1389 * 1390 *---------------------------------------------------------------------------- 1391*/ 1392static void IMY_MetaData (S_IMELODY_DATA *pData, E_EAS_METADATA_TYPE metaType, EAS_I8 *buffer) 1393{ 1394 EAS_I32 len; 1395 1396 /* check for callback */ 1397 if (!pData->metadata.callback) 1398 return; 1399 1400 /* copy data to host buffer */ 1401 len = (EAS_I32) strlen((char*) buffer); 1402 if (len >pData->metadata.bufferSize) 1403 len = pData->metadata.bufferSize; 1404 strncpy((char*) pData->metadata.buffer, (char*) buffer, (size_t) len); 1405 pData->metadata.buffer[len] = 0; 1406 1407 /* callback to host */ 1408 pData->metadata.callback(metaType, pData->metadata.buffer, pData->metadata.pUserData); 1409} 1410 1411/*---------------------------------------------------------------------------- 1412 * IMY_ParseHeader() 1413 *---------------------------------------------------------------------------- 1414 * Purpose: 1415 * Prepare to parse the file. Allocates instance data (or uses static allocation for 1416 * static memory model). 1417 * 1418 * Inputs: 1419 * pEASData - pointer to overall EAS data structure 1420 * handle - pointer to file handle 1421 * 1422 * Outputs: 1423 * 1424 * 1425 * Side Effects: 1426 * 1427 *---------------------------------------------------------------------------- 1428*/ 1429static EAS_RESULT IMY_ParseHeader (S_EAS_DATA *pEASData, S_IMELODY_DATA* pData) 1430{ 1431 EAS_RESULT result; 1432 EAS_INT token; 1433 EAS_INT temp; 1434 EAS_I8 c; 1435 1436#ifdef _DEBUG_IMELODY 1437 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "Enter IMY_ParseHeader\n"); */ } 1438#endif 1439 1440 /* initialize some defaults */ 1441 pData->time = 0; 1442 pData->tick = DEFAULT_TICK_CONV; 1443 pData->note = 0; 1444 pData->noteModifier = 0; 1445 pData ->restTicks = 0; 1446 pData->volume = 7; 1447 pData->octave = 60; 1448 pData->repeatOffset = -1; 1449 pData->repeatCount = -1; 1450 pData->style = 0; 1451 1452 /* force the read of the first line */ 1453 pData->index = 1; 1454 1455 /* read data until we get to melody */ 1456 for (;;) 1457 { 1458 /* read a line from the file and parse the token */ 1459 if (pData->index != 0) 1460 { 1461 if ((result = IMY_ReadLine(pEASData->hwInstData, pData->fileHandle, pData->buffer, &pData->startLine)) != EAS_SUCCESS) 1462 { 1463#ifdef _DEBUG_IMELODY 1464 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_ParseHeader: IMY_ReadLine returned %d\n", result); */ } 1465#endif 1466 return result; 1467 } 1468 } 1469 token = IMY_ParseLine(pData->buffer, &pData->index); 1470 1471 switch (token) 1472 { 1473 /* ignore these valid tokens */ 1474 case TOKEN_BEGIN: 1475 break; 1476 1477 case TOKEN_FORMAT: 1478 if (!IMY_GetVersion(pData, &temp)) 1479 { 1480 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "Invalid FORMAT field '%s'\n", pData->buffer); */ } 1481 return EAS_ERROR_FILE_FORMAT; 1482 } 1483 if ((temp != 0x0100) && (temp != 0x0200)) 1484 { 1485 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "Unsupported FORMAT %02x\n", temp); */ } 1486 return EAS_ERROR_UNRECOGNIZED_FORMAT; 1487 } 1488 break; 1489 1490 case TOKEN_VERSION: 1491 if (!IMY_GetVersion(pData, &temp)) 1492 { 1493 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "Invalid VERSION field '%s'\n", pData->buffer); */ } 1494 return EAS_ERROR_FILE_FORMAT; 1495 } 1496 if ((temp != 0x0100) && (temp != 0x0102)) 1497 { 1498 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "Unsupported VERSION %02x\n", temp); */ } 1499 return EAS_ERROR_UNRECOGNIZED_FORMAT; 1500 } 1501 break; 1502 1503 case TOKEN_NAME: 1504 IMY_MetaData(pData, EAS_METADATA_TITLE, pData->buffer + pData->index); 1505 break; 1506 1507 case TOKEN_COMPOSER: 1508 IMY_MetaData(pData, EAS_METADATA_AUTHOR, pData->buffer + pData->index); 1509 break; 1510 1511 /* handle beat */ 1512 case TOKEN_BEAT: 1513 IMY_GetNumber(pEASData->hwInstData, pData, &temp, EAS_TRUE); 1514 if ((temp >= 25) && (temp <= 900)) 1515 pData->tick = TICK_CONVERT / temp; 1516 break; 1517 1518 /* handle style */ 1519 case TOKEN_STYLE: 1520 c = IMY_GetNextChar(pEASData->hwInstData, pData, EAS_TRUE); 1521 if (c == 'S') 1522 c = IMY_GetNextChar(pEASData->hwInstData, pData, EAS_TRUE); 1523 if ((c >= '0') && (c <= '2')) 1524 pData->style = (EAS_U8) (c - '0'); 1525 else 1526 { 1527 PutBackChar(pData); 1528 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "Error in style command: %s\n", pData->buffer); */ } 1529 } 1530 break; 1531 1532 /* handle volume */ 1533 case TOKEN_VOLUME: 1534 c = IMY_GetNextChar(pEASData->hwInstData, pData, EAS_TRUE); 1535 if (c != 'V') 1536 { 1537 PutBackChar(pData); 1538 if (!IsDigit(c)) 1539 { 1540 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "Error in volume command: %s\n", pData->buffer); */ } 1541 break; 1542 } 1543 } 1544 IMY_GetVolume(pEASData->hwInstData, pData, EAS_TRUE); 1545 break; 1546 1547 case TOKEN_MELODY: 1548#ifdef _DEBUG_IMELODY 1549 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "Header successfully parsed\n"); */ } 1550#endif 1551 return EAS_SUCCESS; 1552 1553 case TOKEN_END: 1554 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "Unexpected END:IMELODY encountered\n"); */ } 1555 return EAS_ERROR_FILE_FORMAT; 1556 1557 default: 1558 /* force a read of the next line */ 1559 pData->index = 1; 1560 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "Ignoring unrecognized token in iMelody file: %s\n", pData->buffer); */ } 1561 break; 1562 } 1563 } 1564} 1565 1566/*---------------------------------------------------------------------------- 1567 * IMY_GetNextChar() 1568 *---------------------------------------------------------------------------- 1569 * Purpose: 1570 * 1571 * 1572 * Inputs: 1573 * 1574 * 1575 * Outputs: 1576 * 1577 * 1578 * Side Effects: 1579 * 1580 *---------------------------------------------------------------------------- 1581*/ 1582static EAS_I8 IMY_GetNextChar (EAS_HW_DATA_HANDLE hwInstData, S_IMELODY_DATA *pData, EAS_BOOL inHeader) 1583{ 1584 EAS_I8 c; 1585 EAS_U8 index; 1586 1587 for (;;) 1588 { 1589 /* get next character */ 1590 c = pData->buffer[pData->index++]; 1591 1592 /* buffer empty, read more */ 1593 if (!c) 1594 { 1595 /* don't read the next line in the header */ 1596 if (inHeader) 1597 return 0; 1598 1599 pData->index = 0; 1600 pData->buffer[0] = 0; 1601 if (IMY_ReadLine(hwInstData, pData->fileHandle, pData->buffer, &pData->startLine) != EAS_SUCCESS) 1602 { 1603#ifdef _DEBUG_IMELODY 1604 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_GetNextChar: EOF\n"); */ } 1605#endif 1606 return 0; 1607 } 1608 1609 /* check for END:IMELODY token */ 1610 if (IMY_ParseLine(pData->buffer, &index) == TOKEN_END) 1611 { 1612#ifdef _DEBUG_IMELODY 1613 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_GetNextChar: found END:IMELODY\n"); */ } 1614#endif 1615 pData->buffer[0] = 0; 1616 return 0; 1617 } 1618 continue; 1619 } 1620 1621 /* ignore white space */ 1622 if (!IsSpace(c)) 1623 { 1624 1625#ifdef _DEBUG_IMELODY 1626 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_GetNextChar returned '%c'\n", c); */ } 1627#endif 1628 return c; 1629 } 1630 } 1631} 1632 1633/*---------------------------------------------------------------------------- 1634 * IMY_ReadLine() 1635 *---------------------------------------------------------------------------- 1636 * Purpose: 1637 * Reads a line of input from the file, discarding the CR/LF 1638 * 1639 * Inputs: 1640 * 1641 * 1642 * Outputs: 1643 * 1644 * 1645 * Side Effects: 1646 * 1647 *---------------------------------------------------------------------------- 1648*/ 1649static EAS_RESULT IMY_ReadLine (EAS_HW_DATA_HANDLE hwInstData, EAS_FILE_HANDLE fileHandle, EAS_I8 *buffer, EAS_I32 *pStartLine) 1650{ 1651 EAS_RESULT result; 1652 EAS_INT i; 1653 EAS_I8 c; 1654 1655 /* fetch current file position and save it */ 1656 if (pStartLine != NULL) 1657 { 1658 if ((result = EAS_HWFilePos(hwInstData, fileHandle, pStartLine)) != EAS_SUCCESS) 1659 { 1660#ifdef _DEBUG_IMELODY 1661 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_ParseHeader: EAS_HWFilePos returned %d\n", result); */ } 1662#endif 1663 return result; 1664 } 1665 } 1666 1667 buffer[0] = 0; 1668 for (i = 0; i < MAX_LINE_SIZE; ) 1669 { 1670 if ((result = EAS_HWGetByte(hwInstData, fileHandle, &c)) != EAS_SUCCESS) 1671 { 1672 if ((result == EAS_EOF) && (i > 0)) 1673 break; 1674 return result; 1675 } 1676 1677 /* return on LF or end of data */ 1678 if (c == '\n') 1679 break; 1680 1681 /* store characters in buffer */ 1682 if (c != '\r') 1683 buffer[i++] = c; 1684 } 1685 buffer[i] = 0; 1686 1687#ifdef _DEBUG_IMELODY 1688 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_ReadLine read %s\n", buffer); */ } 1689#endif 1690 1691 return EAS_SUCCESS; 1692} 1693 1694/*---------------------------------------------------------------------------- 1695 * IMY_ParseLine() 1696 *---------------------------------------------------------------------------- 1697 * Purpose: 1698 * 1699 * 1700 * Inputs: 1701 * 1702 * 1703 * Outputs: 1704 * 1705 * 1706 * Side Effects: 1707 * 1708 *---------------------------------------------------------------------------- 1709*/ 1710static EAS_INT IMY_ParseLine (EAS_I8 *buffer, EAS_U8 *pIndex) 1711{ 1712 EAS_INT i; 1713 EAS_INT j; 1714 1715 /* there's no strnicmp() in stdlib, so we have to roll our own */ 1716 for (i = 0; i < TOKEN_INVALID; i++) 1717 { 1718 for (j = 0; ; j++) 1719 { 1720 /* end of token, must be a match */ 1721 if (tokens[i][j] == 0) 1722 { 1723#ifdef _DEBUG_IMELODY 1724 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_ParseLine found token %d\n", i); */ } 1725#endif 1726 *pIndex = (EAS_U8) j; 1727 return i; 1728 } 1729 if (tokens[i][j] != ToUpper(buffer[j])) 1730 break; 1731 } 1732 } 1733#ifdef _DEBUG_IMELODY 1734 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMY_ParseLine: no token found\n"); */ } 1735#endif 1736 return TOKEN_INVALID; 1737} 1738 1739