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 
46 static EAS_RESULT LinearPCMDecode (EAS_DATA_HANDLE pEASData, S_PCM_STATE *pState);
47 static EAS_RESULT LinearPCMLocate (EAS_DATA_HANDLE pEASData, S_PCM_STATE *pState, EAS_I32 time);
48 
49 static const S_DECODER_INTERFACE PCMDecoder =
50 {
51     NULL,
52     LinearPCMDecode,
53     LinearPCMLocate,
54 };
55 
56 /* SMAF ADPCM decoder */
57 #ifdef _SMAF_PARSER
58 extern S_DECODER_INTERFACE SmafDecoder;
59 #define SMAF_DECODER &SmafDecoder
60 extern 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
69 extern S_DECODER_INTERFACE IMADecoder;
70 #define IMA_DECODER &IMADecoder
71 #else
72 #define IMA_DECODER NULL
73 #endif
74 
75 static 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
91 static 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 };
102 static 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 */
108 static EAS_RESULT RenderPCMStream (S_EAS_DATA *pEASData, S_PCM_STATE *pState, EAS_I32 numSamples);
109 
110 
111 /* local prototypes */
112 static S_PCM_STATE *FindSlot (S_EAS_DATA *pEASData, EAS_FILE_HANDLE fileHandle, EAS_PCM_CALLBACK pCallbackFunc, EAS_VOID_PTR cbInstData);
113 static 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 */
EAS_PEInit(S_EAS_DATA * pEASData)131 EAS_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 */
EAS_PEShutdown(S_EAS_DATA * pEASData)175 EAS_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 */
EAS_PERender(S_EAS_DATA * pEASData,EAS_I32 numSamples)206 EAS_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 */
EAS_PEState(S_EAS_DATA * pEASData,EAS_PCM_HANDLE pInstData,EAS_STATE * pState)244 EAS_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 */
EAS_PEClose(S_EAS_DATA * pEASData,EAS_PCM_HANDLE pState)268 EAS_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 */
EAS_PEReset(S_EAS_DATA * pEASData,EAS_PCM_HANDLE pState)296 EAS_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 */
EAS_PEOpenStream(S_EAS_DATA * pEASData,S_PCM_OPEN_PARAMS * pParams,EAS_PCM_HANDLE * pHandle)327 EAS_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 */
EAS_PEContinueStream(S_EAS_DATA * pEASData,EAS_PCM_HANDLE pState,EAS_I32 size)412 EAS_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 */
EAS_PEGetFileHandle(S_EAS_DATA * pEASData,EAS_PCM_HANDLE pState,EAS_FILE_HANDLE * pFileHandle)439 EAS_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 */
EAS_PEUpdateParams(S_EAS_DATA * pEASData,EAS_PCM_HANDLE pState,EAS_I16 pitch,EAS_I16 gainLeft,EAS_I16 gainRight)470 EAS_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 */
EAS_PELocate(S_EAS_DATA * pEASData,EAS_PCM_HANDLE pState,EAS_I32 time)496 EAS_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 */
EAS_PEUpdateVolume(S_EAS_DATA * pEASData,EAS_PCM_HANDLE pState,EAS_I16 volume)527 EAS_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 */
EAS_PEUpdatePitch(S_EAS_DATA * pEASData,EAS_PCM_HANDLE pState,EAS_I16 pitch)546 EAS_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 */
EAS_PEPause(S_EAS_DATA * pEASData,EAS_PCM_HANDLE pState)571 EAS_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 */
EAS_PEResume(S_EAS_DATA * pEASData,EAS_PCM_HANDLE pState)598 EAS_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 
getDecayScale(EAS_U32 index)605 EAS_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 
getAttackIncrement(EAS_U32 index)666 EAS_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 */
EAS_PERelease(S_EAS_DATA * pEASData,EAS_PCM_HANDLE pState)745 EAS_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 */
FindSlot(S_EAS_DATA * pEASData,EAS_FILE_HANDLE fileHandle,EAS_PCM_CALLBACK pCallbackFunc,EAS_VOID_PTR cbInstData)790 static 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 */
CalcBaseFreq(EAS_U32 sampleRate)888 static 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 */
InitPCMStream(S_EAS_DATA * pEASData,S_PCM_STATE * pState)922 static 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 */
RenderPCMStream(S_EAS_DATA * pEASData,S_PCM_STATE * pState,EAS_I32 numSamples)973 static 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 */
LinearPCMDecode(EAS_DATA_HANDLE pEASData,S_PCM_STATE * pState)1255 static 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 */
LinearPCMLocate(EAS_DATA_HANDLE pEASData,S_PCM_STATE * pState,EAS_I32 time)1354 static 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 */
EAS_PESeek(S_EAS_DATA * pEASData,S_PCM_STATE * pState,EAS_I32 * pLocation)1427 EAS_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