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