1/*---------------------------------------------------------------------------- 2 * 3 * File: 4 * eas_pcm.c 5 * 6 * Contents and purpose: 7 * Implements the PCM engine including ADPCM decode for SMAF and CMX audio playback. 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: 849 $ 26 * $Date: 2007-08-28 08:59:11 -0700 (Tue, 28 Aug 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_math.h" 37#include "eas_mixer.h" 38 39#define PCM_MIXER_GUARD_BITS (NUM_MIXER_GUARD_BITS + 1) 40 41/*---------------------------------------------------------------------------- 42 * Decoder interfaces 43 *---------------------------------------------------------------------------- 44*/ 45 46static EAS_RESULT LinearPCMDecode (EAS_DATA_HANDLE pEASData, S_PCM_STATE *pState); 47static EAS_RESULT LinearPCMLocate (EAS_DATA_HANDLE pEASData, S_PCM_STATE *pState, EAS_I32 time); 48 49static const S_DECODER_INTERFACE PCMDecoder = 50{ 51 NULL, 52 LinearPCMDecode, 53 LinearPCMLocate, 54}; 55 56/* SMAF ADPCM decoder */ 57#ifdef _SMAF_PARSER 58extern S_DECODER_INTERFACE SmafDecoder; 59#define SMAF_DECODER &SmafDecoder 60extern S_DECODER_INTERFACE Smaf7BitDecoder; 61#define SMAF_7BIT_DECODER &Smaf7BitDecoder 62#else 63#define SMAF_DECODER NULL 64#define SMAF_7BIT_DECODER NULL 65#endif 66 67/* IMA ADPCM decoder */ 68#ifdef _IMA_DECODER 69extern S_DECODER_INTERFACE IMADecoder; 70#define IMA_DECODER &IMADecoder 71#else 72#define IMA_DECODER NULL 73#endif 74 75static const S_DECODER_INTERFACE * const decoders[] = 76{ 77 &PCMDecoder, 78 SMAF_DECODER, 79 IMA_DECODER, 80 SMAF_7BIT_DECODER 81}; 82 83/*---------------------------------------------------------------------------- 84 * Sample rate conversion 85 *---------------------------------------------------------------------------- 86*/ 87 88#define SRC_RATE_MULTIPLER (0x40000000 / _OUTPUT_SAMPLE_RATE) 89 90#ifdef _LOOKUP_SAMPLE_RATE 91static const EAS_U32 srcConvRate[][2] = 92{ 93 4000L, (4000L << 15) / _OUTPUT_SAMPLE_RATE, 94 8000L, (8000L << 15) / _OUTPUT_SAMPLE_RATE, 95 11025L, (11025L << 15) / _OUTPUT_SAMPLE_RATE, 96 12000L, (12000L << 15) / _OUTPUT_SAMPLE_RATE, 97 16000L, (16000L << 15) / _OUTPUT_SAMPLE_RATE, 98 22050L, (22050L << 15) / _OUTPUT_SAMPLE_RATE, 99 24000L, (24000L << 15) / _OUTPUT_SAMPLE_RATE, 100 32000L, (32000L << 15) / _OUTPUT_SAMPLE_RATE 101}; 102static EAS_U32 CalcBaseFreq (EAS_U32 sampleRate); 103#define SRC_CONV_RATE_ENTRIES (sizeof(srcConvRate)/sizeof(EAS_U32)/2) 104#endif 105 106 107/* interface prototypes */ 108static EAS_RESULT RenderPCMStream (S_EAS_DATA *pEASData, S_PCM_STATE *pState, EAS_I32 numSamples); 109 110 111/* local prototypes */ 112static S_PCM_STATE *FindSlot (S_EAS_DATA *pEASData, EAS_FILE_HANDLE fileHandle, EAS_PCM_CALLBACK pCallbackFunc, EAS_VOID_PTR cbInstData); 113static EAS_RESULT InitPCMStream (S_EAS_DATA *pEASData, S_PCM_STATE *pState); 114 115/*---------------------------------------------------------------------------- 116 * EAS_PEInit() 117 *---------------------------------------------------------------------------- 118 * Purpose: 119 * Initializes the PCM engine 120 * 121 * Inputs: 122 * 123 * 124 * Outputs: 125 * 126 * 127 * Side Effects: 128 * 129 *---------------------------------------------------------------------------- 130*/ 131EAS_RESULT EAS_PEInit (S_EAS_DATA *pEASData) 132{ 133 S_PCM_STATE *pState; 134 EAS_INT i; 135 136 /* check for static memory allocation */ 137 if (pEASData->staticMemoryModel) 138 pEASData->pPCMStreams = EAS_CMEnumData(EAS_CM_PCM_DATA); 139 /* allocate dynamic memory */ 140 else 141 pEASData->pPCMStreams = EAS_HWMalloc(pEASData->hwInstData, sizeof(S_PCM_STATE) * MAX_PCM_STREAMS); 142 143 if (!pEASData->pPCMStreams) 144 { 145 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_FATAL, "Failed to allocate memory for PCM streams\n"); */ } 146 return EAS_ERROR_MALLOC_FAILED; 147 } 148 149 //zero the memory to insure complete initialization 150 EAS_HWMemSet((void *)(pEASData->pPCMStreams),0, sizeof(S_PCM_STATE) * MAX_PCM_STREAMS); 151 152 /* initialize the state data */ 153 for (i = 0, pState = pEASData->pPCMStreams; i < MAX_PCM_STREAMS; i++, pState++) 154 pState->fileHandle = NULL; 155 156 return EAS_SUCCESS; 157} 158 159/*---------------------------------------------------------------------------- 160 * EAS_PEShutdown() 161 *---------------------------------------------------------------------------- 162 * Purpose: 163 * Shuts down the PCM engine 164 * 165 * Inputs: 166 * 167 * 168 * Outputs: 169 * 170 * 171 * Side Effects: 172 * 173 *---------------------------------------------------------------------------- 174*/ 175EAS_RESULT EAS_PEShutdown (S_EAS_DATA *pEASData) 176{ 177 178 /* free any dynamic memory */ 179 if (!pEASData->staticMemoryModel) 180 { 181 if (pEASData->pPCMStreams) 182 { 183 EAS_HWFree(pEASData->hwInstData, pEASData->pPCMStreams); 184 pEASData->pPCMStreams = NULL; 185 } 186 } 187 return EAS_SUCCESS; 188} 189 190/*---------------------------------------------------------------------------- 191 * EAS_PERender() 192 *---------------------------------------------------------------------------- 193 * Purpose: 194 * Render a buffer of PCM audio 195 * 196 * Inputs: 197 * 198 * 199 * Outputs: 200 * 201 * 202 * Side Effects: 203 * 204 *---------------------------------------------------------------------------- 205*/ 206EAS_RESULT EAS_PERender (S_EAS_DATA* pEASData, EAS_I32 numSamples) 207{ 208 S_PCM_STATE *pState; 209 EAS_RESULT result; 210 EAS_INT i; 211 212 /* render all the active streams */ 213 for (i = 0, pState = pEASData->pPCMStreams; i < MAX_PCM_STREAMS; i++, pState++) 214 { 215 if ((pState->fileHandle) && (pState->state != EAS_STATE_STOPPED) && (pState->state != EAS_STATE_PAUSED)) 216 if ((result = RenderPCMStream(pEASData, pState, numSamples)) != EAS_SUCCESS) 217 return result; 218 } 219 return EAS_SUCCESS; 220} 221 222 223/*---------------------------------------------------------------------------- 224 * EAS_PEState() 225 *---------------------------------------------------------------------------- 226 * Purpose: 227 * Returns the current state of the stream 228 * 229 * Inputs: 230 * pEASData - pointer to overall EAS data structure 231 * handle - pointer to file handle 232 * pState - pointer to variable to store state 233 * 234 * Outputs: 235 * 236 * 237 * Side Effects: 238 * 239 * Notes: 240 * This interface is also exposed in the internal library for use by the other modules. 241 *---------------------------------------------------------------------------- 242*/ 243/*lint -esym(715, pEASData) reserved for future use */ 244EAS_RESULT EAS_PEState (S_EAS_DATA *pEASData, EAS_PCM_HANDLE pInstData, EAS_STATE *pState) 245{ 246 /* return current state */ 247 *pState = pInstData->state; 248 return EAS_SUCCESS; 249} 250 251/*---------------------------------------------------------------------------- 252 * EAS_PEClose() 253 *---------------------------------------------------------------------------- 254 * Purpose: 255 * Close the file and clean up 256 * 257 * Inputs: 258 * pEASData - pointer to overall EAS data structure 259 * handle - pointer to file handle 260 * 261 * Outputs: 262 * 263 * 264 * Side Effects: 265 * 266 *---------------------------------------------------------------------------- 267*/ 268EAS_RESULT EAS_PEClose (S_EAS_DATA *pEASData, EAS_PCM_HANDLE pState) 269{ 270 EAS_RESULT result; 271 272 if ((result = EAS_HWCloseFile(pEASData->hwInstData, pState->fileHandle)) != EAS_SUCCESS) 273 return result; 274 275 pState->fileHandle = NULL; 276 return EAS_SUCCESS; 277} 278 279/*---------------------------------------------------------------------------- 280 * PCM_Reset() 281 *---------------------------------------------------------------------------- 282 * Purpose: 283 * Reset the sequencer. Used for locating backwards in the file. 284 * 285 * Inputs: 286 * pEASData - pointer to overall EAS data structure 287 * handle - pointer to file handle 288 * 289 * Outputs: 290 * 291 * 292 * Side Effects: 293 * 294 *---------------------------------------------------------------------------- 295*/ 296EAS_RESULT EAS_PEReset (S_EAS_DATA *pEASData, EAS_PCM_HANDLE pState) 297{ 298 EAS_RESULT result; 299 300 /* reset file position to first byte of data in the stream */ 301 if ((result = EAS_HWFileSeek(pEASData->hwInstData, pState->fileHandle, pState->startPos)) != EAS_SUCCESS) 302 { 303 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "Error %d seeking to start of PCM file\n", result); */ } 304 return result; 305 } 306 307 /* re-initialize stream */ 308 return InitPCMStream(pEASData, pState); 309} 310 311/*---------------------------------------------------------------------------- 312 * EAS_PEOpenStream() 313 *---------------------------------------------------------------------------- 314 * Purpose: 315 * Starts up a PCM playback 316 * 317 * Inputs: 318 * 319 * 320 * Outputs: 321 * 322 * 323 * Side Effects: 324 * 325 *---------------------------------------------------------------------------- 326*/ 327EAS_RESULT EAS_PEOpenStream (S_EAS_DATA *pEASData, S_PCM_OPEN_PARAMS *pParams, EAS_PCM_HANDLE *pHandle) 328{ 329 EAS_RESULT result; 330 S_PCM_STATE *pState; 331 EAS_I32 filePos; 332 333 /* make sure we support this decoder */ 334 if (pParams->decoder >= NUM_DECODER_MODULES) 335 { 336 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "Decoder selector out of range\n"); */ } 337 return EAS_ERROR_PARAMETER_RANGE; 338 } 339 if (decoders[pParams->decoder] == NULL) 340 { 341 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "Decoder module not available\n"); */ } 342 return EAS_ERROR_FEATURE_NOT_AVAILABLE; 343 } 344 345 /* find a slot for the new stream */ 346 if ((pState = FindSlot(pEASData, pParams->fileHandle, pParams->pCallbackFunc, pParams->cbInstData)) == NULL) 347 { 348 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "Unable to open ADPCM stream, too many streams open\n"); */ } 349 return EAS_ERROR_MAX_PCM_STREAMS; 350 } 351 352 /* get the current file position */ 353 if ((result = EAS_HWFilePos(pEASData->hwInstData, pState->fileHandle, &filePos)) != EAS_SUCCESS) 354 { 355 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "EAS_HWFilePos returned %ld\n",result); */ } 356 pState->fileHandle = NULL; 357 return result; 358 } 359 360 pState->pDecoder = decoders[pParams->decoder]; 361 pState->startPos = filePos; 362 pState->bytesLeftLoop = pState->byteCount = pParams->size; 363 pState->loopStart = pParams->loopStart; 364 pState->samplesTilLoop = (EAS_I32) pState->loopStart; 365 pState->loopSamples = pParams->loopSamples; 366 pState->samplesInLoop = 0; 367 pState->blockSize = (EAS_U16) pParams->blockSize; 368 pState->flags = pParams->flags; 369 pState->envData = pParams->envData; 370 pState->volume = pParams->volume; 371 pState->sampleRate = (EAS_U16) pParams->sampleRate; 372 373 /* set the base frequency */ 374 pState->basefreq = (SRC_RATE_MULTIPLER * (EAS_U32) pParams->sampleRate) >> 15; 375 376 /* calculate shift for frequencies > 1.0 */ 377 pState->rateShift = 0; 378 while (pState->basefreq > 32767) 379 { 380 pState->basefreq = pState->basefreq >> 1; 381 pState->rateShift++; 382 } 383 384 /* initialize */ 385 if ((result = InitPCMStream(pEASData, pState)) != EAS_SUCCESS) 386 return result; 387 388 *pHandle = pState; 389 390 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "EAS_PEOpenStream: StartPos=%d, byteCount = %d, loopSamples=%d\n", 391 pState->startPos, pState->byteCount, pState->loopSamples); */ } 392 return EAS_SUCCESS; 393} 394 395/*---------------------------------------------------------------------------- 396 * EAS_PEContinueStream() 397 *---------------------------------------------------------------------------- 398 * Purpose: 399 * Continues a PCM stream 400 * 401 * Inputs: 402 * 403 * 404 * Outputs: 405 * 406 * 407 * Side Effects: 408 * 409 *---------------------------------------------------------------------------- 410*/ 411/*lint -e{715} reserved for future use */ 412EAS_RESULT EAS_PEContinueStream (S_EAS_DATA *pEASData, EAS_PCM_HANDLE pState, EAS_I32 size) 413{ 414 415 /* add new samples to count */ 416 pState->bytesLeft += size; 417 if (pState->bytesLeft > 0) 418 pState->flags &= ~PCM_FLAGS_EMPTY; 419 return EAS_SUCCESS; 420} 421 422/*---------------------------------------------------------------------------- 423 * EAS_PEGetFileHandle() 424 *---------------------------------------------------------------------------- 425 * Purpose: 426 * Returns the file handle of a stream 427 * 428 * Inputs: 429 * 430 * 431 * Outputs: 432 * 433 * 434 * Side Effects: 435 * 436 *---------------------------------------------------------------------------- 437*/ 438/*lint -esym(715, pEASData) reserved for future use */ 439EAS_RESULT EAS_PEGetFileHandle (S_EAS_DATA *pEASData, EAS_PCM_HANDLE pState, EAS_FILE_HANDLE *pFileHandle) 440{ 441 *pFileHandle = pState->fileHandle; 442 return EAS_SUCCESS; 443} 444 445/*---------------------------------------------------------------------------- 446 * EAS_PEUpdateParams() 447 *---------------------------------------------------------------------------- 448 * Purpose: 449 * Update the pitch and volume parameters for a PCM stream 450 * 451 * Inputs: 452 * pEASData - pointer to EAS library instance data 453 * handle - pointer to S_PCM_STATE for this stream 454 * gainLeft - linear gain multipler in 1.15 fraction format 455 * gainRight - linear gain multipler in 1.15 fraction format 456 * pitch - pitch shift in cents 457 * initial - initial settings, set current gain 458 * 459 * Outputs: 460 * 461 * 462 * Side Effects: 463 * 464 * Notes 465 * In mono mode, leftGain controls the output gain and rightGain is ignored 466 *---------------------------------------------------------------------------- 467*/ 468/*lint -esym(715, pEASData) reserved for future use */ 469/*lint -esym(715, gainRight) used only in 2-channel version */ 470EAS_RESULT EAS_PEUpdateParams (S_EAS_DATA* pEASData, EAS_PCM_HANDLE pState, EAS_I16 pitch, EAS_I16 gainLeft, EAS_I16 gainRight) 471{ 472 473 pState->gainLeft = gainLeft; 474 475#if (NUM_OUTPUT_CHANNELS == 2) 476 pState->gainRight = gainRight; 477#endif 478 479 pState->pitch = pitch; 480 return EAS_SUCCESS; 481} 482 483/*---------------------------------------------------------------------------- 484 * EAS_PELocate() 485 *---------------------------------------------------------------------------- 486 * Purpose: 487 * This function seeks to the requested place in the file. Accuracy 488 * is dependent on the sample rate and block size. 489 * 490 * Inputs: 491 * pEASData - pointer to overall EAS data structure 492 * pState - stream handle 493 * time - media time in milliseconds 494 *---------------------------------------------------------------------------- 495*/ 496EAS_RESULT EAS_PELocate (S_EAS_DATA *pEASData, EAS_PCM_HANDLE pState, EAS_I32 time) 497{ 498 if (pState->pDecoder->pfLocate == NULL) 499 return EAS_ERROR_FEATURE_NOT_AVAILABLE; 500 501 return pState->pDecoder->pfLocate(pEASData, pState, time); 502} 503 504/*---------------------------------------------------------------------------- 505 * EAS_PEUpdateVolume() 506 *---------------------------------------------------------------------------- 507 * Purpose: 508 * Update the volume parameters for a PCM stream 509 * 510 * Inputs: 511 * pEASData - pointer to EAS library instance data 512 * handle - pointer to S_PCM_STATE for this stream 513 * gainLeft - linear gain multipler in 1.15 fraction format 514 * gainRight - linear gain multipler in 1.15 fraction format 515 * initial - initial settings, set current gain 516 * 517 * Outputs: 518 * 519 * 520 * Side Effects: 521 * 522 * Notes 523 * In mono mode, leftGain controls the output gain and rightGain is ignored 524 *---------------------------------------------------------------------------- 525*/ 526/*lint -esym(715, pEASData) reserved for future use */ 527EAS_RESULT EAS_PEUpdateVolume (S_EAS_DATA* pEASData, EAS_PCM_HANDLE pState, EAS_I16 volume) 528{ 529 pState->volume = volume; 530 return EAS_SUCCESS; 531} 532 533/*---------------------------------------------------------------------------- 534 * EAS_PEUpdatePitch() 535 *---------------------------------------------------------------------------- 536 * Purpose: 537 * Update the pitch parameter for a PCM stream 538 * 539 * Inputs: 540 * pEASData - pointer to EAS library instance data 541 * pState - pointer to S_PCM_STATE for this stream 542 * pitch - new pitch value in pitch cents 543 *---------------------------------------------------------------------------- 544*/ 545/*lint -esym(715, pEASData) reserved for future use */ 546EAS_RESULT EAS_PEUpdatePitch (S_EAS_DATA* pEASData, EAS_PCM_HANDLE pState, EAS_I16 pitch) 547{ 548 pState->pitch = pitch; 549 return EAS_SUCCESS; 550} 551 552/*---------------------------------------------------------------------------- 553 * EAS_PEPause() 554 *---------------------------------------------------------------------------- 555 * Purpose: 556 * Mute and stop rendering a PCM stream. Sets the gain target to zero and stops the playback 557 * at the end of the next audio frame. 558 * 559 * Inputs: 560 * pEASData - pointer to EAS library instance data 561 * handle - pointer to S_PCM_STATE for this stream 562 * 563 * Outputs: 564 * 565 * 566 * Side Effects: 567 * 568 *---------------------------------------------------------------------------- 569*/ 570/*lint -esym(715, pEASData) reserved for future use */ 571EAS_RESULT EAS_PEPause (S_EAS_DATA *pEASData, EAS_PCM_HANDLE pState) 572{ 573 /* set state to stopping */ 574 pState->state = EAS_STATE_PAUSING; 575 return EAS_SUCCESS; 576} 577 578/*---------------------------------------------------------------------------- 579 * EAS_PEResume() 580 *---------------------------------------------------------------------------- 581 * Purpose: 582 * Resume rendering a PCM stream. Sets the gain target back to its 583 * previous setting and restarts playback at the end of the next audio 584 * frame. 585 * 586 * Inputs: 587 * pEASData - pointer to EAS library instance data 588 * handle - pointer to S_PCM_STATE for this stream 589 * 590 * Outputs: 591 * 592 * 593 * Side Effects: 594 * 595 *---------------------------------------------------------------------------- 596*/ 597/*lint -esym(715, pEASData) reserved for future use */ 598EAS_RESULT EAS_PEResume (S_EAS_DATA *pEASData, EAS_PCM_HANDLE pState) 599{ 600 /* set state to stopping */ 601 pState->state = EAS_STATE_PLAY; 602 return EAS_SUCCESS; 603} 604 605EAS_U32 getDecayScale(EAS_U32 index) 606{ 607 EAS_U32 utemp; 608 609 //envelope decay segment 610 switch (index) 611 { 612 case 0: //no decay 613 utemp = 512;//32768; 614 break; 615 case 1: //.0156 dB per update 616 utemp = 511;//32709; 617 break; 618 case 2: //.03125 619 utemp = 510;//32649; 620 break; 621 case 3: //.0625 622 utemp = 508;//32532; 623 break; 624 case 4: //.125 625 utemp = 505;//32298; 626 break; 627 case 5: //.25 628 utemp = 497;//31835; 629 break; 630 case 6: //.5 631 utemp = 483;//30929; 632 break; 633 case 7: //1.0 634 utemp = 456;//29193; 635 break; 636 case 8: //2.0 637 utemp = 406;//26008; 638 break; 639 case 9: //4.0 640 utemp = 323;//20642; 641 break; 642 case 10: //8.0 643 utemp = 203;//13004; 644 break; 645 case 11: //16.0 646 utemp = 81;//5160; 647 break; 648 case 12: //32.0 649 utemp = 13;//813; 650 break; 651 case 13: //64.0 652 utemp = 0;//20; 653 break; 654 case 14: //128.0 655 utemp = 0; 656 break; 657 case 15: //256.0 658 default: 659 utemp = 0; 660 break; 661 } 662 //printf("getdecayscale returned %d\n",utemp); 663 return utemp; 664} 665 666EAS_U32 getAttackIncrement(EAS_U32 index) 667{ 668 EAS_U32 utemp; 669 670 //envelope decay segment 671 switch (index) 672 { 673 case 0: 674 utemp = 32; 675 break; 676 case 1: 677 utemp = 64; 678 break; 679 case 2: 680 utemp = 128; 681 break; 682 case 3: 683 utemp = 256; 684 break; 685 case 4: 686 utemp = 512; 687 break; 688 case 5: 689 utemp = 1024; 690 break; 691 case 6: 692 utemp = 2048; 693 break; 694 case 7: 695 utemp = 4096; 696 break; 697 case 8: 698 utemp = 8192; 699 break; 700 case 9: 701 utemp = 16384; 702 break; 703 case 10: 704 utemp = 32768; 705 break; 706 case 11: 707 utemp = 65536; 708 break; 709 case 12: 710 utemp = 65536; 711 break; 712 case 13: 713 utemp = 65536; 714 break; 715 case 14: 716 utemp = 65535; 717 break; 718 case 15: 719 default: 720 utemp = 0; 721 break; 722 } 723 //printf("getattackincrement returned %d\n",utemp); 724 return utemp; 725} 726 727/*---------------------------------------------------------------------------- 728 * EAS_PERelease() 729 *---------------------------------------------------------------------------- 730 * Purpose: 731 * Put the PCM stream envelope into release. 732 * 733 * Inputs: 734 * pEASData - pointer to EAS library instance data 735 * handle - pointer to S_PCM_STATE for this stream 736 * 737 * Outputs: 738 * 739 * 740 * Side Effects: 741 * 742 *---------------------------------------------------------------------------- 743*/ 744/*lint -esym(715, pEASData) reserved for future use */ 745EAS_RESULT EAS_PERelease (S_EAS_DATA *pEASData, EAS_PCM_HANDLE pState) 746{ 747 EAS_U32 utemp; 748 749 //printf("handling note-off part of envelope\n"); 750 /*if the note is not ignore release or sustained*/ 751 if (((pState->envData >> 24) & 0x0F)==0) 752 { 753 /* set envelope state to release */ 754 pState->envState = PCM_ENV_RELEASE; 755 utemp = ((pState->envData >> 20) & 0x0F); 756 pState->envScale = getDecayScale(utemp); //getReleaseScale(utemp); 757 } 758 else 759 { 760 /*else change envelope state to sustain */ 761 pState->envState = PCM_ENV_SUSTAIN; 762 utemp = ((pState->envData >> 28) & 0x0F); 763 pState->envScale = getDecayScale(utemp); //getSustainScale(utemp); 764 } 765 //since we are in release, don't let anything hang around too long 766 //printf("checking env scale, val = %d\n",((S_PCM_STATE*) handle)->envScale); 767 if (pState->envScale > 505) 768 pState->envScale = 505; 769 return EAS_SUCCESS; 770} 771 772/*---------------------------------------------------------------------------- 773 * FindSlot() 774 *---------------------------------------------------------------------------- 775 * Purpose: 776 * Locates an empty stream slot and assigns the file handle 777 * 778 * Inputs: 779 * pEASData - pointer to EAS library instance data 780 * fileHandle - file handle 781 * pCallbackFunc - function to be called back upon EAS_STATE_STOPPED 782 * 783 * Outputs: 784 * returns handle to slot or NULL if all slots are used 785 * 786 * Side Effects: 787 * 788 *---------------------------------------------------------------------------- 789*/ 790static S_PCM_STATE *FindSlot (S_EAS_DATA *pEASData, EAS_FILE_HANDLE fileHandle, EAS_PCM_CALLBACK pCallbackFunc, EAS_VOID_PTR cbInstData) 791{ 792 EAS_INT i; 793 S_PCM_STATE *pState; 794 795#ifndef NO_PCM_STEAL 796 S_PCM_STATE *foundState = NULL; 797 EAS_INT count = 0; 798 EAS_U32 startOrder = 0xFFFFFFFF; 799 S_PCM_STATE *stealState = NULL; 800 EAS_U32 youngest = 0; 801 802 /* find an empty slot, count total in use, and find oldest in use (lowest start order) */ 803 for (i = 0, pState = pEASData->pPCMStreams; i < MAX_PCM_STREAMS; i++, pState++) 804 { 805 /* if this one is available */ 806 if (pState->fileHandle == NULL) 807 { 808 foundState = pState; 809 } 810 /* else this one is in use, so see if it is the oldest, and count total in use */ 811 /* also find youngest */ 812 else 813 { 814 /*one more voice in use*/ 815 count++; 816 /* is this the oldest? (lowest start order) */ 817 if ((pState->state != EAS_STATE_STOPPING) && (pState->startOrder < startOrder)) 818 { 819 /* remember this one */ 820 stealState = pState; 821 /* remember the oldest so far */ 822 startOrder = pState->startOrder; 823 } 824 /* is this the youngest? (highest start order) */ 825 if (pState->startOrder >= youngest) 826 { 827 youngest = pState->startOrder; 828 } 829 } 830 } 831 832 /* if there are too many voices active, stop the oldest one */ 833 if (count > PCM_STREAM_THRESHOLD) 834 { 835 //printf("stealing!!!\n"); 836 /* make sure we got one, although we should always have one at this point */ 837 if (stealState != NULL) 838 { 839 //flag this as stopping, so it will get shut off 840 stealState->state = EAS_STATE_STOPPING; 841 } 842 } 843 844 /* if there are no available open streams (we won't likely see this, due to stealing) */ 845 if (foundState == NULL) 846 return NULL; 847 848 /* save info */ 849 foundState->startOrder = youngest + 1; 850 foundState->fileHandle = fileHandle; 851 foundState->pCallback = pCallbackFunc; 852 foundState->cbInstData = cbInstData; 853 return foundState; 854#else 855 /* find an empty slot*/ 856 for (i = 0; i < MAX_PCM_STREAMS; i++) 857 { 858 pState = &pEASData->pPCMStreams[i]; 859 if (pState->fileHandle != NULL) 860 continue; 861 862 pState->fileHandle = fileHandle; 863 pState->pCallback = pCallbackFunc; 864 pState->cbInstData = cbInstData; 865 return pState; 866 } 867 return NULL; 868#endif 869} 870 871#ifdef _LOOKUP_SAMPLE_RATE 872/*---------------------------------------------------------------------------- 873 * CalcBaseFreq() 874 *---------------------------------------------------------------------------- 875 * Purpose: 876 * Calculates the fractional phase increment for the sample rate converter 877 * 878 * Inputs: 879 * sampleRate - sample rate in samples/sec 880 * 881 * Outputs: 882 * Returns fractional sample rate with a 15-bit fraction 883 * 884 * Side Effects: 885 * 886 *---------------------------------------------------------------------------- 887*/ 888static EAS_U32 CalcBaseFreq (EAS_U32 sampleRate) 889{ 890 EAS_INT i; 891 892 /* look up the conversion rate */ 893 for (i = 0; i < (EAS_INT)(SRC_CONV_RATE_ENTRIES); i ++) 894 { 895 if (srcConvRate[i][0] == sampleRate) 896 return srcConvRate[i][1]; 897 } 898 899 /* if not found in table, do it the long way */ 900 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "Sample rate %u not in table, calculating by division\n", sampleRate); */ } 901 902 return (SRC_RATE_MULTIPLER * (EAS_U32) sampleRate) >> 15; 903} 904#endif 905 906/*---------------------------------------------------------------------------- 907 * InitPCMStream() 908 *---------------------------------------------------------------------------- 909 * Purpose: 910 * Start an ADPCM stream playback. Decodes the header, preps the engine. 911 * 912 * Inputs: 913 * 914 * 915 * Outputs: 916 * 917 * 918 * Side Effects: 919 * 920 *---------------------------------------------------------------------------- 921*/ 922static EAS_RESULT InitPCMStream (S_EAS_DATA *pEASData, S_PCM_STATE *pState) 923{ 924 925 /* initialize the data structure */ 926 pState->bytesLeft = pState->byteCount; 927 pState->phase = 0; 928 pState->srcByte = 0; 929 pState->decoderL.acc = 0; 930 pState->decoderL.output = 0; 931 pState->decoderL.x0 = pState->decoderL.x1 = 0; 932 pState->decoderL.step = 0; 933 pState->decoderR.acc = 0; 934 pState->decoderR.output = 0; 935 pState->decoderR.x0 = pState->decoderR.x1 = 0; 936 pState->decoderR.step = 0; 937 pState->hiNibble = EAS_FALSE; 938 pState->pitch = 0; 939 pState->blockCount = 0; 940 pState->gainLeft = PCM_DEFAULT_GAIN_SETTING; 941// pState->currentGainLeft = PCM_DEFAULT_GAIN_SETTING; 942 pState->envValue = 0; 943 pState->envState = PCM_ENV_START; 944 945#if (NUM_OUTPUT_CHANNELS == 2) 946 pState->gainRight = PCM_DEFAULT_GAIN_SETTING; 947// pState->currentGainRight = PCM_DEFAULT_GAIN_SETTING; 948#endif 949 pState->state = EAS_STATE_READY; 950 951 /* initialize the decoder */ 952 if (pState->pDecoder->pfInit) 953 return (*pState->pDecoder->pfInit)(pEASData, pState); 954 return EAS_SUCCESS; 955} 956 957/*---------------------------------------------------------------------------- 958 * RenderPCMStream() 959 *---------------------------------------------------------------------------- 960 * Purpose: 961 * Decodes a buffer of ADPCM data. 962 * 963 * Inputs: 964 * 965 * 966 * Outputs: 967 * 968 * 969 * Side Effects: 970 * 971 *---------------------------------------------------------------------------- 972*/ 973static EAS_RESULT RenderPCMStream (S_EAS_DATA *pEASData, S_PCM_STATE *pState, EAS_I32 numSamples) 974{ 975 EAS_RESULT result; 976 EAS_U32 phaseInc; 977 EAS_I32 gainLeft, gainIncLeft; 978 EAS_I32 *pOut; 979 EAS_I32 temp; 980 EAS_U32 utemp; 981 982#if (NUM_OUTPUT_CHANNELS == 2) 983 EAS_I32 gainRight, gainIncRight; 984#endif 985 986#if 0 987 printf("env data: AR = %d, DR = %d, SL = %d, SR = %d, RR = %d\n", 988 ((pState->envData >> 12) & 0x0F), 989 ((pState->envData >> 16) & 0x0F), 990 ((pState->envData >> 8) & 0x0F), 991 ((pState->envData >> 28) & 0x0F), 992 ((pState->envData >> 20) & 0x0F)); 993#endif 994 995 if (pState->envState == PCM_ENV_START) 996 { 997 //printf("env start\n"); 998 utemp = ((pState->envData >> 12) & 0x0F); 999 //if fastest rate, attack is already completed 1000 //do the same for slowest rate, since that allows zero to be passed for default envelope 1001 if (utemp == 0x0F || utemp == 0x00) 1002 { 1003 //start envelope at full 1004 pState->envValue = (32768<<7); 1005 //jump right into decay 1006 utemp = ((pState->envData >> 16) & 0x0F); 1007 pState->envScale = getDecayScale(utemp); 1008 pState->envState = PCM_ENV_DECAY; 1009 pState->currentGainLeft = (EAS_I16) FMUL_15x15(pState->gainLeft, pState->volume); 1010 pState->currentGainRight = (EAS_I16) FMUL_15x15(pState->gainRight, pState->volume); 1011 } 1012 //else attack has a ramp 1013 else 1014 { 1015 //start the envelope very low 1016 pState->envValue = (2<<7); 1017 pState->currentGainLeft = 0; 1018 pState->currentGainRight = 0; 1019 //get envelope attack scaling value 1020 pState->envScale = getAttackIncrement(utemp); 1021 //go to attack state 1022 pState->envState = PCM_ENV_ATTACK; 1023 } 1024 } 1025 if (pState->envState == PCM_ENV_ATTACK) 1026 { 1027 //printf("env attack, env value = %d, env scale = %d\n",pState->envValue>>7,pState->envScale); 1028 //update envelope value 1029 pState->envValue = pState->envValue + (pState->envScale << 7); 1030 //check envelope level and update state if needed 1031 if (pState->envValue >= (32768<<7)) 1032 { 1033 pState->envValue = (32768<<7); 1034 utemp = ((pState->envData >> 16) & 0x0F); 1035 pState->envScale = getDecayScale(utemp); 1036 pState->envState = PCM_ENV_DECAY; 1037 } 1038 } 1039 else if (pState->envState == PCM_ENV_DECAY) 1040 { 1041 //printf("env decay, env value = %d, env scale = %d\n",pState->envValue>>7,pState->envScale); 1042 //update envelope value 1043 pState->envValue = (pState->envValue * pState->envScale)>>9; 1044 //check envelope level against sustain level and update state if needed 1045 utemp = ((pState->envData >> 8) & 0x0F); 1046 if (utemp == (EAS_U32)0x0F) 1047 utemp = (2<<7); 1048 else 1049 { 1050 utemp = ((32769<<7) >> (utemp>>1)); 1051 } 1052 if (pState->envValue <= utemp) 1053 { 1054 utemp = ((pState->envData >> 28) & 0x0F); 1055 pState->envScale = getDecayScale(utemp); //getSustainScale(utemp); 1056 pState->envState = PCM_ENV_SUSTAIN; 1057 } 1058 } 1059 else if (pState->envState == PCM_ENV_SUSTAIN) 1060 { 1061 //printf("env sustain, env value = %d, env scale = %d\n",pState->envValue>>7,pState->envScale); 1062 //update envelope value 1063 pState->envValue = (pState->envValue * pState->envScale)>>9; 1064 //check envelope level against bottom level and update state if needed 1065 if (pState->envValue <= (2<<7)) 1066 { 1067 //no more decay 1068 pState->envScale = 512; 1069 pState->envState = PCM_ENV_END; 1070 } 1071 } 1072 else if (pState->envState == PCM_ENV_RELEASE) 1073 { 1074 //printf("env release, env value = %d, env scale = %d\n",pState->envValue>>7,pState->envScale); 1075 //update envelope value 1076 pState->envValue = (pState->envValue * pState->envScale)>>9; 1077 //check envelope level against bottom level and update state if needed 1078 if (pState->envValue <= (2<<7)) 1079 { 1080 //no more decay 1081 pState->envScale = 512; 1082 pState->envState = PCM_ENV_END; 1083 } 1084 } 1085 else if (pState->envState == PCM_ENV_END) 1086 { 1087 //printf("env end\n"); 1088 /* set state to stopping, already ramped down */ 1089 pState->state = EAS_STATE_STOPPING; 1090 } 1091 1092 //pState->gainLeft = (EAS_U16)((pState->gainLeft * (pState->envValue>>7))>>15); 1093 //pState->gainRight = (EAS_U16)((pState->gainRight * (pState->envValue>>7))>>15); 1094 1095 /* gain to 32-bits to increase resolution on anti-zipper filter */ 1096 /*lint -e{703} use shift for performance */ 1097 gainLeft = (EAS_I32) pState->currentGainLeft << SYNTH_UPDATE_PERIOD_IN_BITS; 1098#if (NUM_OUTPUT_CHANNELS == 2) 1099 /*lint -e{703} use shift for performance */ 1100 gainRight = (EAS_I32) pState->currentGainRight << SYNTH_UPDATE_PERIOD_IN_BITS; 1101#endif 1102 1103 /* calculate a new gain increment, gain target is zero if pausing */ 1104 if ((pState->state == EAS_STATE_PAUSING) || (pState->state == EAS_STATE_PAUSED)) 1105 { 1106 gainIncLeft = -pState->currentGainLeft; 1107#if (NUM_OUTPUT_CHANNELS == 2) 1108 gainIncRight= -pState->currentGainRight; 1109#endif 1110 } 1111 else 1112 { 1113 EAS_I32 gain = FMUL_15x15(pState->envValue >> 7, pState->volume); 1114 gainIncLeft = FMUL_15x15(pState->gainLeft, gain) - pState->currentGainLeft; 1115#if (NUM_OUTPUT_CHANNELS == 2) 1116 gainIncRight = FMUL_15x15(pState->gainRight, gain) - pState->currentGainRight; 1117#endif 1118 } 1119 1120 /* calculate phase increment */ 1121 phaseInc = pState->basefreq; 1122 1123 /* convert pitch cents to linear multiplier */ 1124 if (pState->pitch) 1125 { 1126 temp = EAS_Calculate2toX(pState->pitch); 1127 phaseInc = FMUL_15x15(phaseInc, temp); 1128 } 1129 phaseInc = phaseInc << pState->rateShift; 1130 1131 /* pointer to mix buffer */ 1132 pOut = pEASData->pMixBuffer; 1133 1134 /* render a buffer of samples */ 1135 while (numSamples--) 1136 { 1137 1138 /* interpolate an output sample */ 1139 pState->decoderL.output = pState->decoderL.x0 + FMUL_15x15((pState->decoderL.x1 - pState->decoderL.x0), pState->phase & PHASE_FRAC_MASK); 1140 1141 /* stereo output */ 1142#if (NUM_OUTPUT_CHANNELS == 2) 1143 1144 /* stereo stream? */ 1145 if (pState->flags & PCM_FLAGS_STEREO) 1146 pState->decoderR.output = pState->decoderR.x0 + FMUL_15x15((pState->decoderR.x1 - pState->decoderR.x0), pState->phase & PHASE_FRAC_MASK); 1147 1148 /* gain scale and mix */ 1149 /*lint -e{704} use shift instead of division */ 1150 *pOut++ += (pState->decoderL.output * (gainLeft >> SYNTH_UPDATE_PERIOD_IN_BITS)) >> PCM_MIXER_GUARD_BITS; 1151 gainLeft += gainIncLeft; 1152 1153 /*lint -e{704} use shift instead of division */ 1154 if (pState->flags & PCM_FLAGS_STEREO) 1155 *pOut++ += (pState->decoderR.output * (gainRight >> SYNTH_UPDATE_PERIOD_IN_BITS)) >> PCM_MIXER_GUARD_BITS; 1156 else 1157 *pOut++ += (pState->decoderL.output * (gainRight >> SYNTH_UPDATE_PERIOD_IN_BITS)) >> PCM_MIXER_GUARD_BITS; 1158 1159 gainRight += gainIncRight; 1160 1161 /* mono output */ 1162#else 1163 /* if stereo stream, decode right channel and mix to mono */ 1164 if (pState->flags & PCM_FLAGS_STEREO) 1165 { 1166 pState->decoderR.output= pState->decoderR.x0 + FMUL_15x15((pState->decoderR.x1 - pState->decoderR.x0), pState->phase & PHASE_FRAC_MASK); 1167 1168 /* for mono, sum stereo ADPCM to mono */ 1169 /*lint -e{704} use shift instead of division */ 1170 *pOut++ += ((pState->decoderL.output + pState->decoderR.output) * (gainLeft >> SYNTH_UPDATE_PERIOD_IN_BITS)) >> PCM_MIXER_GUARD_BITS; 1171 } 1172 else 1173 /*lint -e{704} use shift instead of division */ 1174 *pOut++ += (pState->decoderL.output * (gainLeft >> SYNTH_UPDATE_PERIOD_IN_BITS)) >> PCM_MIXER_GUARD_BITS; 1175 1176 gainLeft += gainIncLeft; 1177#endif 1178 1179 /* advance phase accumulator */ 1180 pState->phase += phaseInc; 1181 1182 /* if integer part of phase accumulator is non-zero, advance to next sample */ 1183 while (pState->phase & ~PHASE_FRAC_MASK) 1184 { 1185 pState->decoderL.x0 = pState->decoderL.x1; 1186 pState->decoderR.x0 = pState->decoderR.x1; 1187 1188 /* give the source a chance to continue the stream */ 1189 if (!pState->bytesLeft && pState->pCallback && ((pState->flags & PCM_FLAGS_EMPTY) == 0)) 1190 { 1191 pState->flags |= PCM_FLAGS_EMPTY; 1192 (*pState->pCallback)(pEASData, pState->cbInstData, pState, EAS_STATE_EMPTY); 1193 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "RenderPCMStream: After empty callback, bytesLeft = %d\n", pState->bytesLeft); */ } 1194 } 1195 1196 /* decode the next sample */ 1197 if ((result = (*pState->pDecoder->pfDecodeSample)(pEASData, pState)) != EAS_SUCCESS) 1198 return result; 1199 1200 /* adjust phase by one sample */ 1201 pState->phase -= (1L << NUM_PHASE_FRAC_BITS); 1202 } 1203 1204 } 1205 1206 /* save new gain */ 1207 /*lint -e{704} use shift instead of division */ 1208 pState->currentGainLeft = (EAS_I16) (gainLeft >> SYNTH_UPDATE_PERIOD_IN_BITS); 1209 1210#if (NUM_OUTPUT_CHANNELS == 2) 1211 /*lint -e{704} use shift instead of division */ 1212 pState->currentGainRight = (EAS_I16) (gainRight >> SYNTH_UPDATE_PERIOD_IN_BITS); 1213#endif 1214 1215 /* if pausing, set new state and notify */ 1216 if (pState->state == EAS_STATE_PAUSING) 1217 { 1218 pState->state = EAS_STATE_PAUSED; 1219 if (pState->pCallback) 1220 (*pState->pCallback)(pEASData, pState->cbInstData, pState, pState->state); 1221 } 1222 1223 /* if out of data, set stopped state and notify */ 1224 if (pState->bytesLeft == 0 || pState->state == EAS_STATE_STOPPING) 1225 { 1226 pState->state = EAS_STATE_STOPPED; 1227 1228 /* do callback unless the file has already been closed */ 1229 if (pState->pCallback && pState->fileHandle) 1230 (*pState->pCallback)(pEASData, pState->cbInstData, pState, pState->state); 1231 } 1232 1233 if (pState->state == EAS_STATE_READY) 1234 pState->state = EAS_STATE_PLAY; 1235 1236 return EAS_SUCCESS; 1237} 1238 1239/*---------------------------------------------------------------------------- 1240 * LinearPCMDecode() 1241 *---------------------------------------------------------------------------- 1242 * Purpose: 1243 * Decodes a PCM sample 1244 * 1245 * Inputs: 1246 * 1247 * 1248 * Outputs: 1249 * 1250 * 1251 * Side Effects: 1252 * 1253 *---------------------------------------------------------------------------- 1254*/ 1255static EAS_RESULT LinearPCMDecode (EAS_DATA_HANDLE pEASData, S_PCM_STATE *pState) 1256{ 1257 EAS_RESULT result; 1258 EAS_HW_DATA_HANDLE hwInstData; 1259 1260 hwInstData = ((S_EAS_DATA*) pEASData)->hwInstData; 1261 1262 /* if out of data, check for loop */ 1263 if ((pState->bytesLeft == 0) && (pState->loopSamples != 0)) 1264 { 1265 if ((result = EAS_HWFileSeek(pEASData->hwInstData, pState->fileHandle, (EAS_I32) (pState->startPos + pState->loopLocation))) != EAS_SUCCESS) 1266 return result; 1267 pState->bytesLeft = pState->byteCount = (EAS_I32) pState->bytesLeftLoop; 1268 pState->flags &= ~PCM_FLAGS_EMPTY; 1269 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "LinearPCMDecode: Rewind file to %d, bytesLeft = %d\n", pState->startPos, pState->bytesLeft); */ } 1270 } 1271 1272 if (pState->bytesLeft) 1273 { 1274 1275 /* check format byte for 8-bit samples */ 1276 if (pState->flags & PCM_FLAGS_8_BIT) 1277 { 1278 /* fetch left or mono sample */ 1279 if ((result = EAS_HWGetByte(hwInstData, pState->fileHandle, &pState->srcByte)) != EAS_SUCCESS) 1280 return result; 1281 1282 /* if unsigned */ 1283 if (pState->flags & PCM_FLAGS_UNSIGNED) 1284 { 1285 /*lint -e{734} converting unsigned 8-bit to signed 16-bit */ 1286 pState->decoderL.x1 = (EAS_PCM)(((EAS_PCM) pState->srcByte << 8) ^ 0x8000); 1287 } 1288 else 1289 { 1290 /*lint -e{734} converting signed 8-bit to signed 16-bit */ 1291 pState->decoderL.x1 = (EAS_PCM)((EAS_PCM) pState->srcByte << 8); 1292 } 1293 pState->bytesLeft--; 1294 1295 /* fetch right sample */ 1296 if(pState->flags & PCM_FLAGS_STEREO) 1297 { 1298 if ((result = EAS_HWGetByte(hwInstData, pState->fileHandle, &pState->srcByte)) != EAS_SUCCESS) 1299 return result; 1300 1301 /* if unsigned */ 1302 if (pState->flags & PCM_FLAGS_UNSIGNED) 1303 { 1304 /*lint -e{734} converting unsigned 8-bit to signed 16-bit */ 1305 pState->decoderR.x1 = (EAS_PCM)(((EAS_PCM) pState->srcByte << 8) ^ 0x8000); 1306 } 1307 else 1308 { 1309 /*lint -e{734} converting signed 8-bit to signed 16-bit */ 1310 pState->decoderR.x1 = (EAS_PCM)((EAS_PCM) pState->srcByte << 8); 1311 } 1312 pState->bytesLeft--; 1313 } 1314 } 1315 1316 /* must be 16-bit samples */ 1317 else 1318 { 1319 //unsigned 16 bit currently not supported 1320 if (pState->flags & PCM_FLAGS_UNSIGNED) 1321 { 1322 return EAS_ERROR_INVALID_PCM_TYPE; 1323 } 1324 1325 /* fetch left or mono sample */ 1326 if ((result = EAS_HWGetWord(hwInstData, pState->fileHandle, &pState->decoderL.x1, EAS_FALSE)) != EAS_SUCCESS) 1327 return result; 1328 pState->bytesLeft -= 2; 1329 1330 /* fetch right sample */ 1331 if(pState->flags & PCM_FLAGS_STEREO) 1332 { 1333 if ((result = EAS_HWGetWord(hwInstData, pState->fileHandle, &pState->decoderR.x1, EAS_FALSE)) != EAS_SUCCESS) 1334 return result; 1335 pState->bytesLeft -= 2; 1336 } 1337 } 1338 } 1339 1340 /* no more data, force zero samples */ 1341 else 1342 pState->decoderL.x1 = pState->decoderR.x1 = 0; 1343 1344 return EAS_SUCCESS; 1345} 1346 1347/*---------------------------------------------------------------------------- 1348 * LinearPCMLocate() 1349 *---------------------------------------------------------------------------- 1350 * Purpose: 1351 * Locate in a linear PCM stream 1352 *---------------------------------------------------------------------------- 1353*/ 1354static EAS_RESULT LinearPCMLocate (EAS_DATA_HANDLE pEASData, S_PCM_STATE *pState, EAS_I32 time) 1355{ 1356 EAS_RESULT result; 1357 EAS_I32 temp; 1358 EAS_I32 secs, msecs; 1359 EAS_INT shift; 1360 1361 /* calculate size of sample frame */ 1362 if (pState->flags & PCM_FLAGS_8_BIT) 1363 shift = 0; 1364 else 1365 shift = 1; 1366 if (pState->flags & PCM_FLAGS_STEREO) 1367 shift++; 1368 1369 /* break down into secs and msecs */ 1370 secs = time / 1000; 1371 msecs = time - (secs * 1000); 1372 1373 /* calculate sample number fraction from msecs */ 1374 temp = (msecs * pState->sampleRate); 1375 temp = (temp >> 10) + ((temp * 49) >> 21); 1376 1377 /* add integer sample count */ 1378 temp += secs * pState->sampleRate; 1379 1380 /* calculate the position based on sample frame size */ 1381 /*lint -e{703} use shift for performance */ 1382 temp <<= shift; 1383 1384 /* past end of sample? */ 1385 if (temp > (EAS_I32) pState->loopStart) 1386 { 1387 /* if not looped, flag error */ 1388 if (pState->loopSamples == 0) 1389 { 1390 pState->bytesLeft = 0; 1391 pState->flags |= PCM_FLAGS_EMPTY; 1392 return EAS_ERROR_LOCATE_BEYOND_END; 1393 } 1394 1395 /* looped sample - calculate position in loop */ 1396 while (temp > (EAS_I32) pState->loopStart) 1397 temp -= (EAS_I32) pState->loopStart; 1398 } 1399 1400 /* seek to new position */ 1401 if ((result = EAS_PESeek(pEASData, pState, &temp)) != EAS_SUCCESS) 1402 return result; 1403 1404 /* reset state */ 1405 if ((pState->state != EAS_STATE_PAUSING) && (pState->state != EAS_STATE_PAUSED)) 1406 pState->state = EAS_STATE_READY; 1407 1408 return EAS_SUCCESS; 1409} 1410 1411/*---------------------------------------------------------------------------- 1412 * EAS_PESeek 1413 *---------------------------------------------------------------------------- 1414 * Purpose: 1415 * Locate to a particular byte in a PCM stream 1416 *---------------------------------------------------------------------------- 1417 * This bit is tricky because the chunks may not be contiguous, 1418 * so we have to rely on the parser to position in the file. We 1419 * do this by seeking to the end of each chunk and simulating an 1420 * empty buffer condition until we get to where we want to go. 1421 * 1422 * A better solution would be a parser API for re-positioning, 1423 * but there isn't time at the moment to re-factor all the 1424 * parsers to support a new API. 1425 *---------------------------------------------------------------------------- 1426*/ 1427EAS_RESULT EAS_PESeek (S_EAS_DATA *pEASData, S_PCM_STATE *pState, EAS_I32 *pLocation) 1428{ 1429 EAS_RESULT result; 1430 1431 /* seek to start of audio */ 1432 if ((result = EAS_HWFileSeek(pEASData->hwInstData, pState->fileHandle, pState->startPos)) != EAS_SUCCESS) 1433 { 1434 pState->state = EAS_STATE_ERROR; 1435 return result; 1436 } 1437 pState->bytesLeft = pState->bytesLeftLoop; 1438 1439 /* skip through chunks until we find the right chunk */ 1440 while (*pLocation > (EAS_I32) pState->bytesLeft) 1441 { 1442 /* seek to end of audio chunk */ 1443 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "EAS_PESeek: Seek to offset = %d\n", pState->bytesLeft); */ } 1444 if ((result = EAS_HWFileSeekOfs(pEASData->hwInstData, pState->fileHandle, pState->bytesLeft)) != EAS_SUCCESS) 1445 { 1446 pState->state = EAS_STATE_ERROR; 1447 return result; 1448 } 1449 *pLocation -= pState->bytesLeft; 1450 pState->bytesLeft = 0; 1451 pState->flags |= PCM_FLAGS_EMPTY; 1452 1453 /* retrieve more data */ 1454 if (pState->pCallback) 1455 (*pState->pCallback)(pEASData, pState->cbInstData, pState, EAS_STATE_EMPTY); 1456 1457 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "EAS_PESeek: bytesLeft=%d, byte location = %d\n", pState->bytesLeft, *pLocation); */ } 1458 1459 /* no more samples */ 1460 if (pState->bytesLeft == 0) 1461 return EAS_ERROR_LOCATE_BEYOND_END; 1462 } 1463 1464 /* seek to new offset in current chunk */ 1465 if (*pLocation > 0) 1466 { 1467 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "EAS_PESeek: Seek to offset = %d\n", *pLocation); */ } 1468 if ((result = EAS_HWFileSeekOfs(pEASData->hwInstData, pState->fileHandle, *pLocation)) != EAS_SUCCESS) 1469 { 1470 pState->state = EAS_STATE_ERROR; 1471 return result; 1472 } 1473 1474 /* if not streamed, calculate number of bytes left */ 1475 if (pState->flags & PCM_FLAGS_STREAMING) 1476 pState->bytesLeft = 0x7fffffff; 1477 else 1478 pState->bytesLeft -= *pLocation; 1479 } 1480 return EAS_SUCCESS; 1481} 1482 1483