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