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