eas_pcm.c revision a8c89077d78769bf4840fa91609edc51fe2fa02d
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