1 /*---------------------------------------------------------------------------- 2 * 3 * File: 4 * eas_wavefile.c 5 * 6 * Contents and purpose: 7 * This file implements the wave file 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: 852 $ 26 * $Date: 2007-09-04 11:43:49 -0700 (Tue, 04 Sep 2007) $ 27 *---------------------------------------------------------------------------- 28 */ 29 30 #include "eas_data.h" 31 #include "eas_report.h" 32 #include "eas_host.h" 33 #include "eas_config.h" 34 #include "eas_parser.h" 35 #include "eas_pcm.h" 36 #include "eas_wavefile.h" 37 38 /* lint is choking on the ARM math.h file, so we declare the log10 function here */ 39 extern double log10(double x); 40 41 /* increase gain to compensate for loss in mixer */ 42 #define WAVE_GAIN_OFFSET 6 43 44 /* constant for 1200 / log10(2.0) */ 45 #define PITCH_CENTS_CONVERSION 3986.313714 46 47 /*---------------------------------------------------------------------------- 48 * WAVE file defines 49 *---------------------------------------------------------------------------- 50 */ 51 /* RIFF chunks */ 52 #define CHUNK_TYPE(a,b,c,d) ( \ 53 ( ((EAS_U32)(a) & 0xFF) << 24 ) \ 54 + ( ((EAS_U32)(b) & 0xFF) << 16 ) \ 55 + ( ((EAS_U32)(c) & 0xFF) << 8 ) \ 56 + ( ((EAS_U32)(d) & 0xFF) ) ) 57 58 #define CHUNK_RIFF CHUNK_TYPE('R','I','F','F') 59 #define CHUNK_WAVE CHUNK_TYPE('W','A','V','E') 60 #define CHUNK_FMT CHUNK_TYPE('f','m','t',' ') 61 #define CHUNK_DATA CHUNK_TYPE('d','a','t','a') 62 #define CHUNK_LIST CHUNK_TYPE('L','I','S','T') 63 #define CHUNK_INFO CHUNK_TYPE('I','N','F','O') 64 #define CHUNK_INAM CHUNK_TYPE('I','N','A','M') 65 #define CHUNK_ICOP CHUNK_TYPE('I','C','O','P') 66 #define CHUNK_IART CHUNK_TYPE('I','A','R','T') 67 68 /* wave file format identifiers */ 69 #define WAVE_FORMAT_PCM 0x0001 70 #define WAVE_FORMAT_IMA_ADPCM 0x0011 71 72 /* file size for streamed file */ 73 #define FILE_SIZE_STREAMING 0x80000000 74 75 /*---------------------------------------------------------------------------- 76 * prototypes 77 *---------------------------------------------------------------------------- 78 */ 79 static EAS_RESULT WaveCheckFileType (S_EAS_DATA *pEASData, EAS_FILE_HANDLE fileHandle, EAS_VOID_PTR *pHandle, EAS_I32 offset); 80 static EAS_RESULT WavePrepare (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData); 81 static EAS_RESULT WaveState (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_STATE *pState); 82 static EAS_RESULT WaveClose (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData); 83 static EAS_RESULT WaveReset (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData); 84 static EAS_RESULT WaveLocate (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 time, EAS_BOOL *pParserLocate); 85 static EAS_RESULT WavePause (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData); 86 static EAS_RESULT WaveResume (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData); 87 static EAS_RESULT WaveSetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 value); 88 static EAS_RESULT WaveGetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 *pValue); 89 static EAS_RESULT WaveParseHeader (S_EAS_DATA *pEASData, EAS_FILE_HANDLE fileHandle, S_WAVE_STATE *pWaveData); 90 static EAS_RESULT WaveGetMetaData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 *pMediaLength); 91 92 #ifdef MMAPI_SUPPORT 93 static EAS_RESULT SaveFmtChunk (S_EAS_DATA *pEASData, EAS_FILE_HANDLE fileHandle, S_WAVE_STATE *pWaveData, EAS_I32 size); 94 #endif 95 96 /*---------------------------------------------------------------------------- 97 * 98 * EAS_Wave_Parser 99 * 100 * This structure contains the functional interface for the Wave file parser 101 *---------------------------------------------------------------------------- 102 */ 103 const S_FILE_PARSER_INTERFACE EAS_Wave_Parser = 104 { 105 WaveCheckFileType, 106 WavePrepare, 107 NULL, 108 NULL, 109 WaveState, 110 WaveClose, 111 WaveReset, 112 WavePause, 113 WaveResume, 114 WaveLocate, 115 WaveSetData, 116 WaveGetData, 117 WaveGetMetaData 118 }; 119 120 /*---------------------------------------------------------------------------- 121 * WaveCheckFileType() 122 *---------------------------------------------------------------------------- 123 * Purpose: 124 * Check the file type to see if we can parse it 125 * 126 * Inputs: 127 * pEASData - pointer to overall EAS data structure 128 * handle - pointer to file handle 129 * 130 * Outputs: 131 * 132 * 133 * Side Effects: 134 * 135 *---------------------------------------------------------------------------- 136 */ 137 static EAS_RESULT WaveCheckFileType (S_EAS_DATA *pEASData, EAS_FILE_HANDLE fileHandle, EAS_VOID_PTR *pHandle, EAS_I32 offset) 138 { 139 S_WAVE_STATE *pWaveData; 140 141 /* zero the memory to insure complete initialization */ 142 *pHandle = NULL; 143 144 /* read the file header */ 145 if (WaveParseHeader(pEASData, fileHandle, NULL) == EAS_SUCCESS) 146 { 147 148 /* check for static memory allocation */ 149 if (pEASData->staticMemoryModel) 150 pWaveData = EAS_CMEnumData(EAS_CM_WAVE_DATA); 151 else 152 pWaveData = EAS_HWMalloc(pEASData->hwInstData, sizeof(S_WAVE_STATE)); 153 if (!pWaveData) 154 return EAS_ERROR_MALLOC_FAILED; 155 EAS_HWMemSet(pWaveData, 0, sizeof(S_WAVE_STATE)); 156 157 /* return a pointer to the instance data */ 158 pWaveData->fileHandle = fileHandle; 159 pWaveData->fileOffset = offset; 160 *pHandle = pWaveData; 161 } 162 163 return EAS_SUCCESS; 164 } 165 166 /*---------------------------------------------------------------------------- 167 * WavePrepare() 168 *---------------------------------------------------------------------------- 169 * Purpose: 170 * Prepare to parse the file. 171 * 172 * Inputs: 173 * pEASData - pointer to overall EAS data structure 174 * handle - pointer to file handle 175 * 176 * Outputs: 177 * 178 * 179 * Side Effects: 180 * 181 *---------------------------------------------------------------------------- 182 */ 183 static EAS_RESULT WavePrepare (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData) 184 { 185 S_WAVE_STATE *pWaveData; 186 EAS_RESULT result; 187 188 /* validate parser state */ 189 pWaveData = (S_WAVE_STATE*) pInstData; 190 if (pWaveData->streamHandle != NULL) 191 return EAS_ERROR_NOT_VALID_IN_THIS_STATE; 192 193 /* back to start of file */ 194 pWaveData->time = 0; 195 if ((result = EAS_HWFileSeek(pEASData->hwInstData, pWaveData->fileHandle, pWaveData->fileOffset)) != EAS_SUCCESS) 196 return result; 197 198 /* parse the file header */ 199 if ((result = WaveParseHeader(pEASData, pWaveData->fileHandle, pWaveData)) != EAS_SUCCESS) 200 return result; 201 202 return EAS_SUCCESS; 203 } 204 205 /*---------------------------------------------------------------------------- 206 * WaveState() 207 *---------------------------------------------------------------------------- 208 * Purpose: 209 * Returns the current state of the stream 210 * 211 * Inputs: 212 * pEASData - pointer to overall EAS data structure 213 * handle - pointer to file handle 214 * pState - pointer to variable to store state 215 * 216 * Outputs: 217 * 218 * 219 * Side Effects: 220 * 221 * Notes: 222 * This interface is also exposed in the internal library for use by the other modules. 223 *---------------------------------------------------------------------------- 224 */ 225 static EAS_RESULT WaveState (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_STATE *pState) 226 { 227 S_WAVE_STATE *pWaveData; 228 229 /* return current state */ 230 pWaveData = (S_WAVE_STATE*) pInstData; 231 if (pWaveData->streamHandle) 232 return EAS_PEState(pEASData, pWaveData->streamHandle, pState); 233 234 /* if no stream handle, and time is not zero, we are done */ 235 if (pWaveData->time > 0) 236 *pState = EAS_STATE_STOPPED; 237 else 238 *pState = EAS_STATE_OPEN; 239 return EAS_SUCCESS; 240 } 241 242 /*---------------------------------------------------------------------------- 243 * WaveClose() 244 *---------------------------------------------------------------------------- 245 * Purpose: 246 * Close the file and clean up 247 * 248 * Inputs: 249 * pEASData - pointer to overall EAS data structure 250 * handle - pointer to file handle 251 * 252 * Outputs: 253 * 254 * 255 * Side Effects: 256 * 257 *---------------------------------------------------------------------------- 258 */ 259 static EAS_RESULT WaveClose (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData) 260 { 261 S_WAVE_STATE *pWaveData; 262 EAS_RESULT result; 263 264 pWaveData = (S_WAVE_STATE*) pInstData; 265 266 /* close the stream */ 267 if (pWaveData->streamHandle) 268 { 269 if ((result = EAS_PEClose(pEASData, pWaveData->streamHandle)) != EAS_SUCCESS) 270 return result; 271 pWaveData->streamHandle = NULL; 272 } 273 274 /* if using dynamic memory, free it */ 275 if (!pEASData->staticMemoryModel) 276 { 277 278 #ifdef MMAPI_SUPPORT 279 /* need to free the fmt chunk */ 280 if (pWaveData->fmtChunk != NULL) 281 EAS_HWFree(pEASData->hwInstData, pWaveData->fmtChunk); 282 #endif 283 284 /* free the instance data */ 285 EAS_HWFree(pEASData->hwInstData, pWaveData); 286 287 } 288 return EAS_SUCCESS; 289 } 290 291 /*---------------------------------------------------------------------------- 292 * WaveReset() 293 *---------------------------------------------------------------------------- 294 * Purpose: 295 * Reset the sequencer. Used for locating backwards in the file. 296 * 297 * Inputs: 298 * pEASData - pointer to overall EAS data structure 299 * handle - pointer to file handle 300 * 301 * Outputs: 302 * 303 * 304 * Side Effects: 305 * 306 *---------------------------------------------------------------------------- 307 */ 308 static EAS_RESULT WaveReset (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData) 309 { 310 EAS_PCM_HANDLE streamHandle; 311 312 /* reset to first byte of data in the stream */ 313 streamHandle = ((S_WAVE_STATE*)pInstData)->streamHandle; 314 if (streamHandle) 315 return EAS_PEReset(pEASData, streamHandle); 316 return EAS_ERROR_NOT_VALID_IN_THIS_STATE; 317 } 318 319 /*---------------------------------------------------------------------------- 320 * WaveLocate() 321 *---------------------------------------------------------------------------- 322 * Purpose: 323 * Rewind/fast-forward in file. 324 * 325 * Inputs: 326 * pEASData - pointer to overall EAS data structure 327 * handle - pointer to file handle 328 * time - time (in msecs) 329 * 330 * Outputs: 331 * 332 * 333 * Side Effects: 334 * 335 *---------------------------------------------------------------------------- 336 */ 337 /*lint -esym(715, pParserLocate) reserved for future use */ 338 static EAS_RESULT WaveLocate (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 time, EAS_BOOL *pParserLocate) 339 { 340 EAS_PCM_HANDLE streamHandle; 341 342 /* reset to first byte of data in the stream */ 343 streamHandle = ((S_WAVE_STATE*)pInstData)->streamHandle; 344 if (streamHandle) 345 return EAS_PELocate(pEASData, streamHandle, time); 346 return EAS_ERROR_NOT_VALID_IN_THIS_STATE; 347 } 348 349 /*---------------------------------------------------------------------------- 350 * WavePause() 351 *---------------------------------------------------------------------------- 352 * Purpose: 353 * Mute and stop rendering a PCM stream. Sets the gain target to zero and stops the playback 354 * at the end of the next audio frame. 355 * 356 * Inputs: 357 * pEASData - pointer to EAS library instance data 358 * handle - pointer to S_WAVE_STATE for this stream 359 * 360 * Outputs: 361 * 362 * 363 * Side Effects: 364 * 365 *---------------------------------------------------------------------------- 366 */ 367 /*lint -esym(715, pEASData) reserved for future use */ 368 static EAS_RESULT WavePause (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData) 369 { 370 EAS_PCM_HANDLE streamHandle; 371 372 /* pause the stream */ 373 streamHandle = ((S_WAVE_STATE*)pInstData)->streamHandle; 374 if (streamHandle) 375 return EAS_PEPause(pEASData, streamHandle); 376 return EAS_ERROR_NOT_VALID_IN_THIS_STATE; 377 } 378 379 /*---------------------------------------------------------------------------- 380 * WaveResume() 381 *---------------------------------------------------------------------------- 382 * Purpose: 383 * Resume rendering a PCM stream. Sets the gain target back to its 384 * previous setting and restarts playback at the end of the next audio 385 * frame. 386 * 387 * Inputs: 388 * pEASData - pointer to EAS library instance data 389 * handle - pointer to S_WAVE_STATE for this stream 390 * 391 * Outputs: 392 * 393 * 394 * Side Effects: 395 * 396 *---------------------------------------------------------------------------- 397 */ 398 /*lint -esym(715, pEASData) reserved for future use */ 399 static EAS_RESULT WaveResume (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData) 400 { 401 EAS_PCM_HANDLE streamHandle; 402 403 /* resume the stream */ 404 streamHandle = ((S_WAVE_STATE*)pInstData)->streamHandle; 405 if (streamHandle) 406 return EAS_PEResume(pEASData, streamHandle); 407 return EAS_ERROR_NOT_VALID_IN_THIS_STATE; 408 } 409 410 /*---------------------------------------------------------------------------- 411 * WaveSetData() 412 *---------------------------------------------------------------------------- 413 * Purpose: 414 * 415 * Inputs: 416 * pEASData - pointer to EAS library instance data 417 * handle - pointer to S_WAVE_STATE for this stream 418 * 419 * Outputs: 420 * 421 * 422 * Side Effects: 423 * 424 *---------------------------------------------------------------------------- 425 */ 426 static EAS_RESULT WaveSetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 value) 427 { 428 S_WAVE_STATE *pWaveData = (S_WAVE_STATE*) pInstData; 429 430 switch (param) 431 { 432 /* set metadata callback */ 433 case PARSER_DATA_METADATA_CB: 434 EAS_HWMemCpy(&pWaveData->metadata, (void*) value, sizeof(S_METADATA_CB)); 435 return EAS_SUCCESS; 436 437 case PARSER_DATA_PLAYBACK_RATE: 438 value = (EAS_I32) (PITCH_CENTS_CONVERSION * log10((double) value / (double) (1 << 28))); 439 return EAS_PEUpdatePitch(pEASData, pWaveData->streamHandle, (EAS_I16) value); 440 441 case PARSER_DATA_VOLUME: 442 return EAS_PEUpdateVolume(pEASData, pWaveData->streamHandle, (EAS_I16) value); 443 444 default: 445 return EAS_ERROR_INVALID_PARAMETER; 446 } 447 } 448 449 /*---------------------------------------------------------------------------- 450 * WaveGetData() 451 *---------------------------------------------------------------------------- 452 * Purpose: 453 * 454 * Inputs: 455 * pEASData - pointer to EAS library instance data 456 * handle - pointer to S_WAVE_STATE for this stream 457 * 458 * Outputs: 459 * 460 * 461 * Side Effects: 462 * 463 *---------------------------------------------------------------------------- 464 */ 465 /*lint -esym(715, pEASData) reserved for future use */ 466 static EAS_RESULT WaveGetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 *pValue) 467 { 468 S_WAVE_STATE *pWaveData; 469 470 pWaveData = (S_WAVE_STATE*) pInstData; 471 switch (param) 472 { 473 /* return file type as WAVE */ 474 case PARSER_DATA_FILE_TYPE: 475 *pValue = pWaveData->fileType; 476 break; 477 478 #ifdef MMAPI_SUPPORT 479 /* return pointer to 'fmt' chunk */ 480 case PARSER_DATA_FORMAT: 481 *pValue = (EAS_I32) pWaveData->fmtChunk; 482 break; 483 #endif 484 485 case PARSER_DATA_GAIN_OFFSET: 486 *pValue = WAVE_GAIN_OFFSET; 487 break; 488 489 default: 490 return EAS_ERROR_INVALID_PARAMETER; 491 } 492 493 return EAS_SUCCESS; 494 } 495 496 /*---------------------------------------------------------------------------- 497 * WaveParseHeader() 498 *---------------------------------------------------------------------------- 499 * Purpose: 500 * Parse the WAVE file header. 501 * 502 * Inputs: 503 * pEASData - pointer to EAS library instance data 504 * handle - pointer to S_WAVE_STATE for this stream 505 * 506 * Outputs: 507 * 508 * 509 * Side Effects: 510 * 511 *---------------------------------------------------------------------------- 512 */ 513 static EAS_RESULT WaveParseHeader (S_EAS_DATA *pEASData, EAS_FILE_HANDLE fileHandle, S_WAVE_STATE *pWaveData) 514 { 515 S_PCM_OPEN_PARAMS params; 516 EAS_RESULT result; 517 EAS_U32 tag; 518 EAS_U32 fileSize; 519 EAS_U32 size; 520 EAS_I32 pos; 521 EAS_I32 audioOffset; 522 EAS_U16 usTemp; 523 EAS_BOOL parseDone; 524 EAS_U32 avgBytesPerSec; 525 526 /* init some data (and keep lint happy) */ 527 params.sampleRate = 0; 528 params.size = 0; 529 audioOffset = 0; 530 params.decoder = 0; 531 params.blockSize = 0; 532 params.pCallbackFunc = NULL; 533 params.cbInstData = NULL; 534 params.loopSamples = 0; 535 params.fileHandle = fileHandle; 536 params.volume = 0x7fff; 537 params.envData = 0; 538 avgBytesPerSec = 8000; 539 540 /* check for 'RIFF' tag */ 541 if ((result = EAS_HWGetDWord(pEASData->hwInstData, fileHandle, &tag, EAS_TRUE)) != EAS_FALSE) 542 return result; 543 if (tag != CHUNK_RIFF) 544 return EAS_ERROR_UNRECOGNIZED_FORMAT; 545 546 /* get size */ 547 if ((result = EAS_HWGetDWord(pEASData->hwInstData, fileHandle, &fileSize, EAS_FALSE)) != EAS_FALSE) 548 return result; 549 550 /* check for 'WAVE' tag */ 551 if ((result = EAS_HWGetDWord(pEASData->hwInstData, fileHandle, &tag, EAS_TRUE)) != EAS_FALSE) 552 return result; 553 if (tag != CHUNK_WAVE) 554 return EAS_ERROR_UNRECOGNIZED_FORMAT; 555 556 /* this is enough to say we recognize the file */ 557 if (pWaveData == NULL) 558 return EAS_SUCCESS; 559 560 /* check for streaming mode */ 561 pWaveData->flags = 0; 562 pWaveData->mediaLength = -1; 563 pWaveData->infoChunkPos = -1; 564 pWaveData->infoChunkSize = -1; 565 if (fileSize== FILE_SIZE_STREAMING) 566 { 567 pWaveData->flags |= PCM_FLAGS_STREAMING; 568 fileSize = 0x7fffffff; 569 } 570 571 /* find out where we're at */ 572 if ((result = EAS_HWFilePos(pEASData->hwInstData, fileHandle, &pos)) != EAS_SUCCESS) 573 return result; 574 fileSize -= 4; 575 576 parseDone = EAS_FALSE; 577 for (;;) 578 { 579 /* get tag and size for next chunk */ 580 if ((result = EAS_HWGetDWord(pEASData->hwInstData, fileHandle, &tag, EAS_TRUE)) != EAS_FALSE) 581 return result; 582 if ((result = EAS_HWGetDWord(pEASData->hwInstData, fileHandle, &size, EAS_FALSE)) != EAS_FALSE) 583 return result; 584 585 /* process chunk */ 586 pos += 8; 587 switch (tag) 588 { 589 case CHUNK_FMT: 590 591 #ifdef MMAPI_SUPPORT 592 if ((result = SaveFmtChunk(pEASData, fileHandle, pWaveData, (EAS_I32) size)) != EAS_SUCCESS) 593 return result; 594 #endif 595 596 /* get audio format */ 597 if ((result = EAS_HWGetWord(pEASData->hwInstData, fileHandle, &usTemp, EAS_FALSE)) != EAS_FALSE) 598 return result; 599 if (usTemp == WAVE_FORMAT_PCM) 600 { 601 params.decoder = EAS_DECODER_PCM; 602 pWaveData->fileType = EAS_FILE_WAVE_PCM; 603 } 604 else if (usTemp == WAVE_FORMAT_IMA_ADPCM) 605 { 606 params.decoder = EAS_DECODER_IMA_ADPCM; 607 pWaveData->fileType = EAS_FILE_WAVE_IMA_ADPCM; 608 } 609 else 610 return EAS_ERROR_UNRECOGNIZED_FORMAT; 611 612 /* get number of channels */ 613 if ((result = EAS_HWGetWord(pEASData->hwInstData, fileHandle, &usTemp, EAS_FALSE)) != EAS_FALSE) 614 return result; 615 if (usTemp == 2) 616 pWaveData->flags |= PCM_FLAGS_STEREO; 617 else if (usTemp != 1) 618 return EAS_ERROR_UNRECOGNIZED_FORMAT; 619 620 /* get sample rate */ 621 if ((result = EAS_HWGetDWord(pEASData->hwInstData, fileHandle, ¶ms.sampleRate, EAS_FALSE)) != EAS_FALSE) 622 return result; 623 624 /* get stream rate */ 625 if ((result = EAS_HWGetDWord(pEASData->hwInstData, fileHandle, &avgBytesPerSec, EAS_FALSE)) != EAS_FALSE) 626 return result; 627 628 /* get block alignment */ 629 if ((result = EAS_HWGetWord(pEASData->hwInstData, fileHandle, &usTemp, EAS_FALSE)) != EAS_FALSE) 630 return result; 631 params.blockSize = usTemp; 632 633 /* get bits per sample */ 634 if ((result = EAS_HWGetWord(pEASData->hwInstData, fileHandle, &usTemp, EAS_FALSE)) != EAS_FALSE) 635 return result; 636 637 /* PCM, must be 8 or 16 bit samples */ 638 if (params.decoder == EAS_DECODER_PCM) 639 { 640 if (usTemp == 8) 641 pWaveData->flags |= PCM_FLAGS_8_BIT | PCM_FLAGS_UNSIGNED; 642 else if (usTemp != 16) 643 return EAS_ERROR_UNRECOGNIZED_FORMAT; 644 } 645 646 /* for IMA ADPCM, we only support mono 4-bit ADPCM */ 647 else 648 { 649 if ((usTemp != 4) || (pWaveData->flags & PCM_FLAGS_STEREO)) 650 return EAS_ERROR_UNRECOGNIZED_FORMAT; 651 } 652 653 break; 654 655 case CHUNK_DATA: 656 audioOffset = pos; 657 if (pWaveData->flags & PCM_FLAGS_STREAMING) 658 { 659 params.size = 0x7fffffff; 660 parseDone = EAS_TRUE; 661 } 662 else 663 { 664 params.size = (EAS_I32) size; 665 params.loopStart = size; 666 /* use more accurate method if possible */ 667 if (size <= (0x7fffffff / 1000)) 668 pWaveData->mediaLength = (EAS_I32) ((size * 1000) / avgBytesPerSec); 669 else 670 pWaveData->mediaLength = (EAS_I32) (size / (avgBytesPerSec / 1000)); 671 } 672 break; 673 674 case CHUNK_LIST: 675 /* get the list type */ 676 if ((result = EAS_HWGetDWord(pEASData->hwInstData, fileHandle, &tag, EAS_TRUE)) != EAS_FALSE) 677 return result; 678 if (tag == CHUNK_INFO) 679 { 680 pWaveData->infoChunkPos = pos + 4; 681 pWaveData->infoChunkSize = (EAS_I32) size - 4; 682 } 683 break; 684 685 default: 686 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "WaveParseHeader: %c%c%c%c chunk - %d byte(s) ignored\n", 687 (char) (tag >> 24), (char) (tag >> 16), (char) (tag >> 8), (char) tag, size); */ } 688 break; 689 } 690 691 if (parseDone) 692 break; 693 694 /* subtract header size */ 695 fileSize -= 8; 696 697 /* account for zero-padding on odd length chunks */ 698 if (size & 1) 699 size++; 700 701 /* this check works for files with odd length last chunk and no zero-pad */ 702 if (size >= fileSize) 703 { 704 if (size > fileSize) 705 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "WaveParseHeader: '%c%c%c%c' chunk size exceeds length of file or is not zero-padded\n", 706 (char) (tag >> 24), (char) (tag >> 16), (char) (tag >> 8), (char) tag, size); */ } 707 break; 708 } 709 710 /* subtract size of data chunk (including any zero-pad) */ 711 fileSize -= size; 712 713 /* seek to next chunk */ 714 pos += (EAS_I32) size; 715 if ((result = EAS_HWFileSeek(pEASData->hwInstData, fileHandle, pos)) != EAS_SUCCESS) 716 return result; 717 } 718 719 /* check for valid header */ 720 if ((params.sampleRate == 0) || (params.size == 0)) 721 return EAS_ERROR_UNRECOGNIZED_FORMAT; 722 723 /* save the pertinent information */ 724 pWaveData->audioOffset = audioOffset; 725 params.flags = pWaveData->flags; 726 727 /* seek to data */ 728 if ((result = EAS_HWFileSeek(pEASData->hwInstData, fileHandle, audioOffset)) != EAS_SUCCESS) 729 return result; 730 731 /* open a stream in the PCM engine */ 732 return EAS_PEOpenStream(pEASData, ¶ms, &pWaveData->streamHandle); 733 } 734 735 /*---------------------------------------------------------------------------- 736 * WaveGetMetaData() 737 *---------------------------------------------------------------------------- 738 * Purpose: 739 * Process the INFO chunk and return metadata to host 740 *---------------------------------------------------------------------------- 741 */ 742 static EAS_RESULT WaveGetMetaData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 *pMediaLength) 743 { 744 S_WAVE_STATE *pWaveData; 745 EAS_RESULT result; 746 EAS_I32 pos; 747 EAS_U32 size; 748 EAS_I32 infoSize; 749 EAS_U32 tag; 750 EAS_I32 restorePos; 751 E_EAS_METADATA_TYPE metaType; 752 EAS_I32 metaLen; 753 754 /* get current position so we can restore it */ 755 pWaveData = (S_WAVE_STATE*) pInstData; 756 757 /* return media length */ 758 *pMediaLength = pWaveData->mediaLength; 759 760 /* did we encounter an INFO chunk? */ 761 if (pWaveData->infoChunkPos < 0) 762 return EAS_SUCCESS; 763 764 if ((result = EAS_HWFilePos(pEASData->hwInstData, pWaveData->fileHandle, &restorePos)) != EAS_SUCCESS) 765 return result; 766 767 /* offset to start of first chunk in INFO chunk */ 768 pos = pWaveData->infoChunkPos; 769 infoSize = pWaveData->infoChunkSize; 770 771 /* read all the chunks in the INFO chunk */ 772 for (;;) 773 { 774 775 /* seek to next chunk */ 776 if ((result = EAS_HWFileSeek(pEASData->hwInstData, pWaveData->fileHandle, pos)) != EAS_SUCCESS) 777 return result; 778 779 /* get tag and size for next chunk */ 780 if ((result = EAS_HWGetDWord(pEASData->hwInstData, pWaveData->fileHandle, &tag, EAS_TRUE)) != EAS_FALSE) 781 return result; 782 if ((result = EAS_HWGetDWord(pEASData->hwInstData, pWaveData->fileHandle, &size, EAS_FALSE)) != EAS_FALSE) 783 return result; 784 785 /* process chunk */ 786 pos += 8; 787 metaType = EAS_METADATA_UNKNOWN; 788 switch (tag) 789 { 790 case CHUNK_INAM: 791 metaType = EAS_METADATA_TITLE; 792 break; 793 794 case CHUNK_IART: 795 metaType = EAS_METADATA_AUTHOR; 796 break; 797 798 case CHUNK_ICOP: 799 metaType = EAS_METADATA_COPYRIGHT; 800 break; 801 802 default: 803 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "WaveParseHeader: %c%c%c%c chunk - %d byte(s) ignored\n", 804 (char) (tag >> 24), (char) (tag >> 16), (char) (tag >> 8), (char) tag, size); */ } 805 break; 806 } 807 808 /* process known metadata */ 809 if (metaType != EAS_METADATA_UNKNOWN) 810 { 811 metaLen = pWaveData->metadata.bufferSize - 1; 812 if (metaLen > (EAS_I32) size) 813 metaLen = (EAS_I32) size; 814 if ((result = EAS_HWReadFile(pEASData->hwInstData, pWaveData->fileHandle, pWaveData->metadata.buffer, metaLen, &metaLen)) != EAS_SUCCESS) 815 return result; 816 pWaveData->metadata.buffer[metaLen] = 0; 817 pWaveData->metadata.callback(metaType, pWaveData->metadata.buffer, pWaveData->metadata.pUserData); 818 } 819 820 /* subtract this block */ 821 if (size & 1) 822 size++; 823 infoSize -= (EAS_I32) size + 8; 824 if (infoSize == 0) 825 break; 826 pos += (EAS_I32) size; 827 } 828 829 830 /* restore original position */ 831 return EAS_HWFileSeek(pEASData->hwInstData, pWaveData->fileHandle, restorePos); 832 } 833 834 #ifdef MMAPI_SUPPORT 835 /*---------------------------------------------------------------------------- 836 * SaveFmtChunk() 837 *---------------------------------------------------------------------------- 838 * Purpose: 839 * Save the fmt chunk for the MMAPI library 840 *---------------------------------------------------------------------------- 841 */ 842 static EAS_RESULT SaveFmtChunk (S_EAS_DATA *pEASData, EAS_FILE_HANDLE fileHandle, S_WAVE_STATE *pWaveData, EAS_I32 fmtSize) 843 { 844 EAS_RESULT result; 845 EAS_I32 pos; 846 EAS_I32 count; 847 848 /* save current file position */ 849 if ((result = EAS_HWFilePos(pEASData->hwInstData, fileHandle, &pos)) != EAS_SUCCESS) 850 return result; 851 852 /* allocate a chunk of memory */ 853 pWaveData->fmtChunk = EAS_HWMalloc(pEASData->hwInstData, fmtSize); 854 if (!pWaveData->fmtChunk) 855 return EAS_ERROR_MALLOC_FAILED; 856 857 /* read the fmt chunk into memory */ 858 if ((result = EAS_HWReadFile(pEASData->hwInstData, fileHandle, pWaveData->fmtChunk, fmtSize, &count)) != EAS_SUCCESS) 859 return result; 860 if (count != fmtSize) 861 return EAS_ERROR_FILE_READ_FAILED; 862 863 /* restore file position */ 864 return EAS_HWFileSeek(pEASData->hwInstData, fileHandle, pos); 865 } 866 #endif 867 868