C++程序  |  1130行  |  36.09 KB

/*----------------------------------------------------------------------------
 *
 * File: 
 * jet.c
 *
 * Contents and purpose:
 * Implementation for JET sound engine
 *
 * Copyright (c) 2006 Sonic Network Inc.

 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *----------------------------------------------------------------------------
 * Revision Control:
 *   $Revision: 563 $
 *   $Date: 2007-02-13 20:26:23 -0800 (Tue, 13 Feb 2007) $
 *----------------------------------------------------------------------------
*/

//#define LOG_NDEBUG 0
#define LOG_TAG "JET_C"

//#define DEBUG_JET

#include "eas_data.h"
#include "eas_smf.h"
#include "jet_data.h"
#include "eas_host.h"
#include "eas_report.h"


/* default configuration */
static const S_JET_CONFIG jetDefaultConfig = 
{
	JET_EVENT_APP_LOW,
	JET_EVENT_APP_HIGH
};

/* function prototypes */
extern EAS_RESULT EAS_IntSetStrmParam (S_EAS_DATA *pEASData, EAS_HANDLE pStream, EAS_INT param, EAS_I32 value);
extern EAS_RESULT EAS_OpenJETStream (EAS_DATA_HANDLE pEASData, EAS_FILE_HANDLE fileHandle, EAS_I32 offset, EAS_HANDLE *ppStream);
extern EAS_RESULT DLSParser (EAS_HW_DATA_HANDLE hwInstData, EAS_FILE_HANDLE fileHandle, EAS_I32 offset, EAS_DLSLIB_HANDLE *ppDLS);

/*----------------------------------------------------------------------------
 * JET_ParseEvent()
 *----------------------------------------------------------------------------
 * Returns current status
 *----------------------------------------------------------------------------
*/
EAS_PUBLIC void JET_ParseEvent (EAS_U32 event, S_JET_EVENT *pEvent)
{
	pEvent->segment = (EAS_U8) ((event & JET_EVENT_SEG_MASK) >> JET_EVENT_SEG_SHIFT);
	pEvent->track = (EAS_U8) ((event & JET_EVENT_TRACK_MASK) >> JET_EVENT_TRACK_SHIFT);
	pEvent->channel = (EAS_U8) ((event & JET_EVENT_CHAN_MASK) >> JET_EVENT_CHAN_SHIFT);
	pEvent->controller = (EAS_U8) ((event & JET_EVENT_CTRL_MASK) >> JET_EVENT_CTRL_SHIFT);
	pEvent->value = (EAS_U8) (event & JET_EVENT_VAL_MASK);
}

#ifdef DEBUG_JET
/*----------------------------------------------------------------------------
 * JET_DumpEvent
 *----------------------------------------------------------------------------
 * Advances queue read/write index
 *----------------------------------------------------------------------------
*/
static void JET_DumpEvent (const char *procName, EAS_U32 event)
{
	S_JET_EVENT sEvent;
	JET_ParseEvent(event, &sEvent);
	{ /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "%s: SegID=%d, TrkID=%d, channel=%d, ctrl=%d, val=%d\n",
		procName, sEvent.segment, sEvent.track, sEvent.channel, sEvent.controller, sEvent.value); */ }
}
#endif

/*----------------------------------------------------------------------------
 * JET_IncQueueIndex
 *----------------------------------------------------------------------------
 * Advances queue read/write index
 *----------------------------------------------------------------------------
*/
EAS_INLINE EAS_U8 JET_IncQueueIndex (EAS_U8 index, EAS_U8 queueSize)
{
	if (++index == queueSize)
		index = 0;
	return index;
}

/*----------------------------------------------------------------------------
 * JET_WriteQueue
 *----------------------------------------------------------------------------
 * Save event to queue
 *----------------------------------------------------------------------------
*/
EAS_INLINE void JET_WriteQueue (EAS_U32 *pEventQueue, EAS_U8 *pWriteIndex, EAS_U8 readIndex, EAS_U8 queueSize, EAS_U32 event)
{
	EAS_U8 temp;

	/* check for queue overflow */
	temp = JET_IncQueueIndex(*pWriteIndex, queueSize);
	if (temp == readIndex)
	{
		{ /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "JET_Event: Event queue overflow --- event ignored!\n"); */ }
		return;
	}

	/* save in queue and advance write index */
	pEventQueue[*pWriteIndex] = event;
	*pWriteIndex = temp;
}

/*----------------------------------------------------------------------------
 * JET_ReadQueue
 *----------------------------------------------------------------------------
 * Read event to queue
 *----------------------------------------------------------------------------
*/
EAS_INLINE EAS_BOOL JET_ReadQueue (EAS_U32 *pEventQueue, EAS_U8 *pReadIndex, EAS_U8 writeIndex, EAS_U8 queueSize, EAS_U32 *pEvent)
{

	/* check for empty queue */
	if (*pReadIndex == writeIndex)
		return EAS_FALSE;

	/* save in queue and advance write index */
	*pEvent = pEventQueue[*pReadIndex];
	*pReadIndex = JET_IncQueueIndex(*pReadIndex, queueSize);
	return EAS_TRUE;
}

/*----------------------------------------------------------------------------
 * JET_NextSegment
 *----------------------------------------------------------------------------
 * Advances segment number
 *----------------------------------------------------------------------------
*/
EAS_INLINE EAS_INT JET_NextSegment (EAS_INT seg_num)
{
	if (++seg_num == SEG_QUEUE_DEPTH)
		seg_num = 0;
	return seg_num;
}

/*----------------------------------------------------------------------------
 * JET_PrepareSegment()
 *----------------------------------------------------------------------------
 * Prepare a segment for playback
 *----------------------------------------------------------------------------
*/
static EAS_RESULT JET_PrepareSegment (EAS_DATA_HANDLE easHandle, EAS_I32 queueNum)
{
	EAS_RESULT result;
	S_JET_SEGMENT *p;

	{ /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "JET_PrepareSegment: %d\n", queueNum); */ }

	p = &easHandle->jetHandle->segQueue[queueNum];
	result = EAS_Prepare(easHandle, p->streamHandle);
	if (result != EAS_SUCCESS)
		return result;

	/* pause segment - must be triggered by play or end of previous segment */
	result = EAS_Pause(easHandle, p->streamHandle);
	if (result != EAS_SUCCESS)
		return result;
	p->state = JET_STATE_READY;

	/* set calback data */
	result = EAS_IntSetStrmParam(easHandle, p->streamHandle, PARSER_DATA_JET_CB, queueNum);
	if (result != EAS_SUCCESS)
		return result;
	
	/* set DLS collection */
	if (p->libNum >= 0)
	{
		result = EAS_IntSetStrmParam(easHandle, p->streamHandle, 
			PARSER_DATA_DLS_COLLECTION, (EAS_I32) easHandle->jetHandle->libHandles[p->libNum]);
		if (result != EAS_SUCCESS)
			return result;
	}

	/* set transposition */
	if (p->transpose)
	{
		result = EAS_SetTransposition(easHandle, p->streamHandle, p->transpose);
		if (result != EAS_SUCCESS)
			return result;
	}

	return result;
}

/*----------------------------------------------------------------------------
 * JET_StartPlayback()
 *----------------------------------------------------------------------------
 * Start segment playback
 *----------------------------------------------------------------------------
*/
static EAS_RESULT JET_StartPlayback (EAS_DATA_HANDLE easHandle, EAS_I32 queueNum)
{
	EAS_RESULT result = EAS_SUCCESS;
	S_JET_SEGMENT *pSeg;
	
	{ /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "JET_StartPlayback %d\n", queueNum); */ }

	/* if next segment is queued, start playback */					
	pSeg = &easHandle->jetHandle->segQueue[queueNum];
	if (pSeg->streamHandle != NULL)
	{
		result = EAS_Resume(easHandle, pSeg->streamHandle);
		easHandle->jetHandle->segQueue[queueNum].state = JET_STATE_PLAYING;

		/* set mute flags */
		if ((result == EAS_SUCCESS) && (pSeg->muteFlags != 0))
			result = EAS_IntSetStrmParam(easHandle, pSeg->streamHandle, PARSER_DATA_MUTE_FLAGS, (EAS_I32) pSeg->muteFlags);
	}
	return result;
}

/*----------------------------------------------------------------------------
 * JET_CloseSegment
 *----------------------------------------------------------------------------
 * Closes stream associated with a segment
 *----------------------------------------------------------------------------
*/
EAS_INLINE EAS_INT JET_CloseSegment (EAS_DATA_HANDLE easHandle, EAS_INT queueNum)
{
	EAS_RESULT result;

	{ /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "JET_CloseSegment %d\n", queueNum); */ }
	
	/* close the segment */
	result = EAS_CloseFile(easHandle, easHandle->jetHandle->segQueue[queueNum].streamHandle);
	if (result != EAS_SUCCESS)
		return result;

	easHandle->jetHandle->segQueue[queueNum].streamHandle = NULL;
	easHandle->jetHandle->segQueue[queueNum].state = JET_STATE_CLOSED;
	easHandle->jetHandle->numQueuedSegments--;
	return result;
}

/*----------------------------------------------------------------------------
 * JetParseInfoChunk()
 *----------------------------------------------------------------------------
 * Parses the JET info chunk
 *----------------------------------------------------------------------------
*/
static EAS_RESULT JetParseInfoChunk (EAS_DATA_HANDLE easHandle, EAS_I32 pos, EAS_I32 chunkSize)
{
	EAS_RESULT result;
	EAS_U32 infoType;
	EAS_U32 temp;

	/* offset to data */
	result = EAS_HWFileSeek(easHandle->hwInstData, easHandle->jetHandle->jetFileHandle, pos);
	if (result != EAS_SUCCESS)
		return result;

	/* read the entire chunk */
	result = EAS_SUCCESS;
	while ((result == EAS_SUCCESS) && (chunkSize > 0))
	{
		
		/* get info infoType */
		result = EAS_HWGetDWord(easHandle->hwInstData, easHandle->jetHandle->jetFileHandle, &infoType, EAS_TRUE);
		if (result != EAS_SUCCESS)
			break;

		/* get info field */
		result = EAS_HWGetDWord(easHandle->hwInstData, easHandle->jetHandle->jetFileHandle, &temp, EAS_FALSE);
		if (result == EAS_SUCCESS)

		switch (infoType)
		{
			case INFO_NUM_SMF_CHUNKS:
				easHandle->jetHandle->numSegments = (EAS_U8) temp;
				break;
				
			case INFO_NUM_DLS_CHUNKS:
				easHandle->jetHandle->numLibraries = (EAS_U8) temp;
				break;

			case INFO_JET_VERSION:
				/* check major version number */
				if ((temp & 0xff000000) != (JET_VERSION & 0xff000000))
					return EAS_ERROR_INCOMPATIBLE_VERSION;
				break;

			default:
				{ /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "Ignoring unrecognized JET info type 0x%08x", infoType); */ }
				break;
		}

		chunkSize -= 8;
	}

	/* allocate pointers for chunks to follow */
	
	return result;
}

/*----------------------------------------------------------------------------
 * JET_OpenFile()
 *----------------------------------------------------------------------------
 * Opens a JET content file for playback
 *----------------------------------------------------------------------------
*/
EAS_PUBLIC EAS_RESULT JET_OpenFile (EAS_DATA_HANDLE easHandle, EAS_FILE_LOCATOR locator)
{
	EAS_RESULT result;
	EAS_U32 chunkType;
	EAS_I32 pos;
	EAS_I32 chunkSize;
	EAS_INT smfChunkNum;
	EAS_INT dlsChunkNum;
	EAS_I32 dataSize = 0; /* make lint happy */

	/* make sure that we don't have an open file */
	if (easHandle->jetHandle->jetFileHandle != NULL)
		return EAS_ERROR_FILE_ALREADY_OPEN;

	/* open the media file */
	result = EAS_HWOpenFile(easHandle->hwInstData, locator, &easHandle->jetHandle->jetFileHandle, EAS_FILE_READ);
	if (result != EAS_SUCCESS)
		return result;

	/* check header */
	result = EAS_HWGetDWord(easHandle->hwInstData, easHandle->jetHandle->jetFileHandle, &chunkType, EAS_TRUE);
	if (result == EAS_SUCCESS)
	{
		if (chunkType != JET_HEADER_TAG)
		{
			{ /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "File is not JET format\n"); */ }
			result = EAS_ERROR_UNRECOGNIZED_FORMAT;
		}
	}
	/* get the file data size */
	if (result == EAS_SUCCESS)
		result = EAS_HWGetDWord(easHandle->hwInstData, easHandle->jetHandle->jetFileHandle, &dataSize, EAS_FALSE);

	/* parse through the file to find contents */
	smfChunkNum = dlsChunkNum = 0;
	pos = chunkSize = 8;
	while ((result == EAS_SUCCESS) && (pos < dataSize))
	{

		/* offset to chunk data */
		result = EAS_HWFileSeek(easHandle->hwInstData, easHandle->jetHandle->jetFileHandle, pos);
		if (result != EAS_SUCCESS)
			break;
			
		/* get chunk size and type */
		result = EAS_HWGetDWord(easHandle->hwInstData, easHandle->jetHandle->jetFileHandle, &chunkType, EAS_TRUE);
		if (result != EAS_SUCCESS)
			break;

		result = EAS_HWGetDWord(easHandle->hwInstData, easHandle->jetHandle->jetFileHandle, &chunkSize, EAS_FALSE);
		if (result != EAS_SUCCESS)
			break;
		pos += 8;

		switch (chunkType)
		{
			case JET_INFO_CHUNK:
				result = JetParseInfoChunk(easHandle, pos, chunkSize);
				break;
				
			case JET_SMF_CHUNK:
				if (smfChunkNum < easHandle->jetHandle->numSegments)
					easHandle->jetHandle->segmentOffsets[smfChunkNum++] = pos;
				else
					{ /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "Ignoring extraneous SMF chunk"); */ }
				break;

			case JET_DLS_CHUNK:
				if (dlsChunkNum < easHandle->jetHandle->numLibraries)
					result = DLSParser(easHandle->hwInstData, easHandle->jetHandle->jetFileHandle, pos, &easHandle->jetHandle->libHandles[dlsChunkNum++]);
				else
					{ /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "Ignoring extraneous DLS chunk"); */ }
				break;

			case JET_APP_DATA_CHUNK:
				easHandle->jetHandle->appDataOffset = pos;
				easHandle->jetHandle->appDataSize = chunkSize;
				break;

			case INFO_JET_COPYRIGHT:
				break;

			default:
				{ /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "Ignoring unrecognized JET chunk type 0x%08x", chunkType); */ }
				break;
		}

		/* offset to next chunk */
		pos += chunkSize;
	}

	/* close file if something went wrong */
	if (result != EAS_SUCCESS)
		EAS_HWCloseFile(easHandle->hwInstData, easHandle->jetHandle->jetFileHandle);

	return result;
}

/*----------------------------------------------------------------------------
 * JET_GetAppData()
 *----------------------------------------------------------------------------
 * Returns location and size of application data in the JET file
 *----------------------------------------------------------------------------
*/
EAS_RESULT JET_GetAppData (EAS_DATA_HANDLE easHandle, EAS_I32 *pAppDataOffset, EAS_I32 *pAppDataSize)
{

	/* check for app chunk */
	if (easHandle->jetHandle->appDataSize == 0)
	{
		*pAppDataOffset = *pAppDataSize = 0;
		return EAS_FAILURE;
	}

	/* return app data */
	*pAppDataOffset = easHandle->jetHandle->appDataOffset;
	*pAppDataSize = easHandle->jetHandle->appDataSize;
	return EAS_SUCCESS;
}

/*----------------------------------------------------------------------------
 * JET_CloseFile()
 *----------------------------------------------------------------------------
 * Closes a JET content file and releases associated resources
 *----------------------------------------------------------------------------
*/
EAS_PUBLIC EAS_RESULT JET_CloseFile (EAS_DATA_HANDLE easHandle)
{
	EAS_INT index;
	EAS_RESULT result = EAS_SUCCESS;

	/* close open streams */
	for (index = 0; index < SEG_QUEUE_DEPTH; index++)
	{
		if (easHandle->jetHandle->segQueue[index].streamHandle != NULL)
		{
			result = JET_CloseSegment(easHandle, index);
			if (result != EAS_SUCCESS)
				break;
		}
	}

	/* close the main file handle */
	if ((result == EAS_SUCCESS) && (easHandle->jetHandle->jetFileHandle != NULL))
	{
		result = EAS_HWCloseFile(easHandle->hwInstData, easHandle->jetHandle->jetFileHandle);
		if (result == EAS_SUCCESS)
			easHandle->jetHandle->jetFileHandle = NULL;
	}
	return result;
}
	
/*----------------------------------------------------------------------------
 * JET_Init()
 *----------------------------------------------------------------------------
 * Initializes the JET library, allocates memory, etc. Call
 * JET_Shutdown to de-allocate memory.
 *----------------------------------------------------------------------------
*/
EAS_PUBLIC EAS_RESULT JET_Init (EAS_DATA_HANDLE easHandle, const S_JET_CONFIG *pConfig, EAS_INT configSize)
{
	S_JET_DATA *pJet;
	EAS_U8 flags = 0;

	/* sanity check */
	if (easHandle == NULL)
		return EAS_ERROR_HANDLE_INTEGRITY;
	if (easHandle->jetHandle != NULL)
		return EAS_ERROR_FEATURE_ALREADY_ACTIVE;
	if (pConfig == NULL)
		pConfig = &jetDefaultConfig;

	/* allocate the JET data object */
	pJet = EAS_HWMalloc(easHandle->hwInstData, sizeof(S_JET_DATA));
	if (pJet == NULL)
		return EAS_ERROR_MALLOC_FAILED;

	/* initialize JET data structure */
	EAS_HWMemSet(pJet, 0, sizeof(S_JET_DATA));
	easHandle->jetHandle = pJet;
	pJet->flags = flags;

	/* copy config data */
	if (configSize > (EAS_INT) sizeof(S_JET_CONFIG))
		configSize = sizeof(S_JET_CONFIG);
	EAS_HWMemCpy(&pJet->config, pConfig, configSize);
	return EAS_SUCCESS;
}

/*----------------------------------------------------------------------------
 * JET_Shutdown()
 *----------------------------------------------------------------------------
 * Frees any memory used by the JET library
 *----------------------------------------------------------------------------
*/
EAS_PUBLIC EAS_RESULT JET_Shutdown (EAS_DATA_HANDLE easHandle)
{
	EAS_RESULT result;

	/* close any open files */
	result = JET_CloseFile(easHandle);

	/* free allocated data */
	EAS_HWFree(easHandle->hwInstData, easHandle->jetHandle);
	easHandle->jetHandle = NULL;
	return result;
}

/*----------------------------------------------------------------------------
 * JET_Status()
 *----------------------------------------------------------------------------
 * Returns current status
 *----------------------------------------------------------------------------
*/
EAS_PUBLIC EAS_RESULT JET_Status (EAS_DATA_HANDLE easHandle, S_JET_STATUS *pStatus)
{
	S_JET_SEGMENT *pSeg;

	pSeg = &easHandle->jetHandle->segQueue[easHandle->jetHandle->playSegment];
	if (pSeg->streamHandle != NULL)
	{
		pStatus->currentUserID = pSeg->userID;
		pStatus->segmentRepeatCount = pSeg->repeatCount;
	}
	else
	{
		pStatus->currentUserID = -1;
		pStatus->segmentRepeatCount = 0;
	}

	pStatus->paused = !(easHandle->jetHandle->flags & JET_FLAGS_PLAYING);
	pStatus->numQueuedSegments = easHandle->jetHandle->numQueuedSegments;
	pStatus->currentPlayingSegment = easHandle->jetHandle->playSegment;
	pStatus->currentQueuedSegment = easHandle->jetHandle->queueSegment;
	if (pSeg->streamHandle != NULL)
	{
		EAS_RESULT result;
		EAS_I32 location ;
		if ((result = EAS_GetLocation(easHandle, pSeg->streamHandle, &location)) == EAS_SUCCESS)
			if(location != 0)
			{
				pStatus->location = location;
			}
	}
	return EAS_SUCCESS;
}

/*----------------------------------------------------------------------------
 * JET_GetEvent()
 *----------------------------------------------------------------------------
 * Checks for application events
 *----------------------------------------------------------------------------
*/
EAS_PUBLIC EAS_BOOL JET_GetEvent (EAS_DATA_HANDLE easHandle, EAS_U32 *pEventRaw, S_JET_EVENT *pEvent)
{
	EAS_U32 jetEvent;
	EAS_BOOL gotEvent;
	
	/* process event queue */
	gotEvent = JET_ReadQueue(easHandle->jetHandle->appEventQueue,
		&easHandle->jetHandle->appEventQueueRead, 
		easHandle->jetHandle->appEventQueueWrite,
		APP_EVENT_QUEUE_SIZE, &jetEvent);

	if (gotEvent)
	{
		if (pEventRaw != NULL)
			*pEventRaw = jetEvent;
		
		if (pEvent != NULL)
			JET_ParseEvent(jetEvent, pEvent);
	}

	return gotEvent;
}

/*----------------------------------------------------------------------------
 * JET_QueueSegment()
 *----------------------------------------------------------------------------
 * Queue a segment for playback
 *----------------------------------------------------------------------------
*/
EAS_PUBLIC EAS_RESULT JET_QueueSegment (EAS_DATA_HANDLE easHandle, EAS_INT segmentNum, EAS_INT libNum, EAS_INT repeatCount, EAS_INT transpose, EAS_U32 muteFlags, EAS_U8 userID)
{
	EAS_FILE_HANDLE fileHandle;
	EAS_RESULT result;
	S_JET_SEGMENT *p;

	{ /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "JET_QueueSegment segNum=%d, queue=%d\n", segmentNum, easHandle->jetHandle->queueSegment); */ }

	/* make sure it's a valid segment */
	if (segmentNum >= easHandle->jetHandle->numSegments)
		return EAS_ERROR_PARAMETER_RANGE;

	/* make sure it's a valid DLS */
	if (libNum >= easHandle->jetHandle->numLibraries)
		return EAS_ERROR_PARAMETER_RANGE;

	/* check to see if queue is full */
	p = &easHandle->jetHandle->segQueue[easHandle->jetHandle->queueSegment];
	if (p->streamHandle != NULL)
		return EAS_ERROR_QUEUE_IS_FULL;		

	/* initialize data */
	p->userID = userID;
	p->repeatCount = (EAS_I16) repeatCount;
	p->transpose = (EAS_I8) transpose;
	p->libNum = (EAS_I8) libNum;
	p->muteFlags = muteFlags;
	p->state = JET_STATE_CLOSED;

	/* open the file */
	result = EAS_OpenJETStream(easHandle, easHandle->jetHandle->jetFileHandle, easHandle->jetHandle->segmentOffsets[segmentNum], &p->streamHandle);
	if (result != EAS_SUCCESS)
		return result;
	p->state = JET_STATE_OPEN;

	/* if less than SEG_QUEUE_DEPTH segments queued up, prepare file for playback */
	if (++easHandle->jetHandle->numQueuedSegments < SEG_QUEUE_DEPTH)
	{
		result = JET_PrepareSegment(easHandle, easHandle->jetHandle->queueSegment);
		if (result != EAS_SUCCESS)
			return result;
	}
	
	/* create duplicate file handle */
	result = EAS_HWDupHandle(easHandle->hwInstData, easHandle->jetHandle->jetFileHandle, &fileHandle);
	if (result != EAS_SUCCESS)
		return result;

	easHandle->jetHandle->jetFileHandle = fileHandle;
	easHandle->jetHandle->queueSegment = (EAS_U8) JET_NextSegment(easHandle->jetHandle->queueSegment);
	return result;
}

/*----------------------------------------------------------------------------
 * JET_Play()
 *----------------------------------------------------------------------------
 * Starts playback of the file
 *----------------------------------------------------------------------------
*/
EAS_PUBLIC EAS_RESULT JET_Play (EAS_DATA_HANDLE easHandle)
{
	EAS_RESULT result;
	EAS_INT index;
	EAS_INT count = 0;

	{ /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "JET_Play\n"); */ }

	/* sanity check */
	if (easHandle->jetHandle->flags & JET_FLAGS_PLAYING)
		return EAS_ERROR_NOT_VALID_IN_THIS_STATE;

	/* resume all paused streams */
	for (index = 0; index < SEG_QUEUE_DEPTH; index++)
	{
		if (((index == easHandle->jetHandle->playSegment) && (easHandle->jetHandle->segQueue[index].state == JET_STATE_READY)) ||
			(easHandle->jetHandle->segQueue[index].state == JET_STATE_PAUSED))
		{
			result = JET_StartPlayback(easHandle, index);
			if (result != EAS_SUCCESS)
				return result;
			count++;
		}
	}

	/* if no streams are playing, return error */
	if (!count)
		return EAS_ERROR_QUEUE_IS_EMPTY;

	easHandle->jetHandle->flags |= JET_FLAGS_PLAYING;
	return EAS_SUCCESS;
}

/*----------------------------------------------------------------------------
 * JET_Pause()
 *----------------------------------------------------------------------------
 * Pauses playback of the file
 *----------------------------------------------------------------------------
*/
EAS_PUBLIC EAS_RESULT JET_Pause (EAS_DATA_HANDLE easHandle)
{
	EAS_RESULT result;
	EAS_INT index;
	EAS_INT count = 0;

	{ /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "JET_Pause\n"); */ }
	
	/* sanity check */
	if ((easHandle->jetHandle->flags & JET_FLAGS_PLAYING) == 0)
		return EAS_ERROR_NOT_VALID_IN_THIS_STATE;
	
	/* pause all playing streams */
	for (index = 0; index < SEG_QUEUE_DEPTH; index++)
	{
		if (easHandle->jetHandle->segQueue[index].state == JET_STATE_PLAYING)
		{
			result = EAS_Pause(easHandle, easHandle->jetHandle->segQueue[easHandle->jetHandle->playSegment].streamHandle);
			if (result != EAS_SUCCESS)
				return result;
			easHandle->jetHandle->segQueue[easHandle->jetHandle->playSegment].state = JET_STATE_PAUSED;
			count++;
		}
	}

	/* if no streams are paused, return error */
	if (!count)
		return EAS_ERROR_QUEUE_IS_EMPTY;

	easHandle->jetHandle->flags &= ~JET_FLAGS_PLAYING;
	return EAS_SUCCESS;
}

/*----------------------------------------------------------------------------
 * JET_SetMuteFlags()
 *----------------------------------------------------------------------------
 * Change the state of the mute flags
 *----------------------------------------------------------------------------
*/
EAS_PUBLIC EAS_RESULT JET_SetMuteFlags (EAS_DATA_HANDLE easHandle, EAS_U32 muteFlags, EAS_BOOL sync)
{
	S_JET_SEGMENT *pSeg;

	/* get pointer to current segment */
	pSeg = &easHandle->jetHandle->segQueue[easHandle->jetHandle->playSegment];

	/* unsynchronized mute, set flags and return */
	if (!sync)
	{
		if (pSeg->streamHandle == NULL)
			return EAS_ERROR_QUEUE_IS_EMPTY;
		pSeg->muteFlags = muteFlags;
		return EAS_IntSetStrmParam(easHandle, pSeg->streamHandle, PARSER_DATA_MUTE_FLAGS, (EAS_I32) muteFlags);
	}


	/* check for valid stream state */
	if (pSeg->state == JET_STATE_CLOSED)
		return EAS_ERROR_QUEUE_IS_EMPTY;

	/* save mute flags */
	pSeg->muteFlags = muteFlags;

	/* if repeating segment, set mute update flag */
	if (sync)
		pSeg->flags |= JET_SEG_FLAG_MUTE_UPDATE;
	return EAS_SUCCESS;
}

/*----------------------------------------------------------------------------
 * JET_SetMuteFlag()
 *----------------------------------------------------------------------------
 * Change the state of a single mute flag
 *----------------------------------------------------------------------------
*/
EAS_PUBLIC EAS_RESULT JET_SetMuteFlag (EAS_DATA_HANDLE easHandle, EAS_INT trackNum, EAS_BOOL muteFlag, EAS_BOOL sync)
{
	S_JET_SEGMENT *pSeg;
	EAS_U32 trackMuteFlag;


	/* setup flag */
	if ((trackNum < 0) || (trackNum > 31))
		return EAS_ERROR_PARAMETER_RANGE;
	trackMuteFlag = (1 << trackNum);

	/* get pointer to current segment */
	pSeg = &easHandle->jetHandle->segQueue[easHandle->jetHandle->playSegment];

	/* unsynchronized mute, set flags and return */
	if (!sync)
	{
		if (pSeg->streamHandle == NULL)
			return EAS_ERROR_QUEUE_IS_EMPTY;
		if (muteFlag)
			pSeg->muteFlags |= trackMuteFlag;
		else
			pSeg->muteFlags &= ~trackMuteFlag;
		return EAS_IntSetStrmParam(easHandle, pSeg->streamHandle, PARSER_DATA_MUTE_FLAGS, (EAS_I32) pSeg->muteFlags);
	}


	/* check for valid stream state */
	if (pSeg->state == JET_STATE_CLOSED)
		return EAS_ERROR_QUEUE_IS_EMPTY;

	/* save mute flags and set mute update flag */
	if (muteFlag)
		pSeg->muteFlags |= trackMuteFlag;
	else
		pSeg->muteFlags &= ~trackMuteFlag;
	pSeg->flags |= JET_SEG_FLAG_MUTE_UPDATE;
	return EAS_SUCCESS;
}

/*----------------------------------------------------------------------------
 * JET_TriggerClip()
 *----------------------------------------------------------------------------
 * Unmute a track and then mute it when it is complete. If a clip
 * is already playing, change mute event to a trigger event. The
 * JET_Event function will not mute the clip, but will allow it
 * to continue playing through the next clip.
 *
 * NOTE: We use bit 7 to indicate an entry in the queue. For a
 * small queue, it is cheaper in both memory and CPU cycles to
 * scan the entire queue for non-zero events than keep enqueue
 * and dequeue indices.
 *----------------------------------------------------------------------------
*/
EAS_PUBLIC EAS_RESULT JET_TriggerClip (EAS_DATA_HANDLE easHandle, EAS_INT clipID)
{
	EAS_INT i;
	EAS_INT index = -1;

	/* check for valid clipID */
	if ((clipID < 0) || (clipID > 63))
		return EAS_ERROR_PARAMETER_RANGE;

	/* set active flag */
	clipID |= JET_CLIP_ACTIVE_FLAG;

	/* Reverse the search so that we get the first empty element */
	for (i = JET_MUTE_QUEUE_SIZE-1; i >= 0 ; i--)
	{
		if (easHandle->jetHandle->muteQueue[i] == clipID)
		{
			index = i;
			break;
		}
		if (easHandle->jetHandle->muteQueue[i] == 0)
			index = i;
	}
	if (index < 0)
		return EAS_ERROR_QUEUE_IS_FULL;

	easHandle->jetHandle->muteQueue[index] = (EAS_U8) clipID | JET_CLIP_TRIGGER_FLAG;
	return EAS_SUCCESS;
}

/*----------------------------------------------------------------------------
 * JET_Process()
 *----------------------------------------------------------------------------
 * Called during EAS_Render to process stream states
 *----------------------------------------------------------------------------
*/
EAS_PUBLIC EAS_RESULT JET_Process (EAS_DATA_HANDLE easHandle)
{
	S_JET_SEGMENT *pSeg;
	EAS_STATE state;
	EAS_INT index;
	EAS_INT playIndex;
	EAS_RESULT result = EAS_SUCCESS;
	EAS_BOOL endOfLoop = EAS_FALSE;
	EAS_BOOL startNextSegment = EAS_FALSE;
	EAS_BOOL prepareNextSegment = EAS_FALSE;
	EAS_U32 jetEvent;

	/* process event queue */
	while (JET_ReadQueue(easHandle->jetHandle->jetEventQueue,
		&easHandle->jetHandle->jetEventQueueRead, 
		easHandle->jetHandle->jetEventQueueWrite,
		JET_EVENT_QUEUE_SIZE, &jetEvent))
	{
		S_JET_EVENT event;
#ifdef DEBUG_JET		
		JET_DumpEvent("JET_Process", jetEvent);
#endif
		JET_ParseEvent(jetEvent, &event);

		/* check for end of loop */
		if ((event.controller == JET_EVENT_MARKER) &&
				(event.value == JET_MARKER_LOOP_END) && 
				(easHandle->jetHandle->segQueue[easHandle->jetHandle->playSegment].streamHandle != NULL))
			endOfLoop = EAS_TRUE;
	}

	/* check state of all streams */
	index = playIndex = easHandle->jetHandle->playSegment;
	for (;;)
	{
		pSeg = &easHandle->jetHandle->segQueue[index];
		if (pSeg->state != JET_STATE_CLOSED)
		{

			/* get playback state */		
			result = EAS_State(easHandle, pSeg->streamHandle, &state);
			if (result != EAS_SUCCESS)
				return result;

			/* process state */
			switch (pSeg->state)
			{
				/* take action if this segment is stopping */
				case JET_STATE_PLAYING:
					if (endOfLoop || (state == EAS_STATE_STOPPING) || (state == EAS_STATE_STOPPED))
					{
						/* handle repeats */
						if (pSeg->repeatCount != 0)
						{
							{ /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "JET_Render repeating segment %d\n", index); */ }
							result = EAS_Locate(easHandle, pSeg->streamHandle, 0, EAS_FALSE);
							if (result != EAS_SUCCESS)
								return result;
							if (pSeg->repeatCount > 0)
								pSeg->repeatCount--;

							/* update mute flags if necessary */
							if (pSeg->flags & JET_SEG_FLAG_MUTE_UPDATE)
							{
								result = EAS_IntSetStrmParam(easHandle, pSeg->streamHandle, PARSER_DATA_MUTE_FLAGS, (EAS_I32) pSeg->muteFlags);
								if (result != EAS_SUCCESS)
									return result;
								pSeg->flags &= ~JET_SEG_FLAG_MUTE_UPDATE;
							}
							
						}
						/* no repeat, start next segment */
						else
						{
							{ /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "JET_Render stopping queue %d\n", index); */ }
							startNextSegment = EAS_TRUE;
							pSeg->state = JET_STATE_STOPPING;
							easHandle->jetHandle->playSegment = (EAS_U8) JET_NextSegment(index);
						}
					}
					break;

				/* if playback has stopped, close the segment */
				case JET_STATE_STOPPING:
					if (state == EAS_STATE_STOPPED)
					{
						result = JET_CloseSegment(easHandle, index);
						if (result != EAS_SUCCESS)
							return result;
					}
					break;

				case JET_STATE_READY:
					if (startNextSegment)
					{
						result = JET_StartPlayback(easHandle, index);
						if (result != EAS_SUCCESS)
							return result;
						startNextSegment = EAS_FALSE;
						prepareNextSegment = EAS_TRUE;
					}
					break;

				case JET_STATE_OPEN:
					if (prepareNextSegment)
					{
						result = JET_PrepareSegment(easHandle, index);
						if (result != EAS_SUCCESS)
							return result;
						prepareNextSegment = EAS_FALSE;
					}
					break;

				case JET_STATE_PAUSED:
					break;
					
				default:
					{ /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "JET_Render: Unexpected segment state %d\n", pSeg->state); */ }
					break;
			}
		}
		
		/* increment index */
		index = JET_NextSegment(index);
		if (index == playIndex)
			break;
	}

	/* if out of segments, clear playing flag */
	if (easHandle->jetHandle->numQueuedSegments == 0)
		easHandle->jetHandle->flags &= ~JET_FLAGS_PLAYING;

	return result;
}

/*----------------------------------------------------------------------------
 * JET_Event()
 *----------------------------------------------------------------------------
 * Called from MIDI parser when data of interest is received
 *----------------------------------------------------------------------------
*/
void JET_Event (EAS_DATA_HANDLE easHandle, EAS_U32 segTrack, EAS_U8 channel, EAS_U8 controller, EAS_U8 value)
{
	EAS_U32 event;

	if (easHandle->jetHandle == NULL)
		return;

	/* handle triggers */
	if (controller == JET_EVENT_TRIGGER_CLIP)
	{
		S_JET_SEGMENT *pSeg;
		EAS_INT i;
		EAS_U32 muteFlag;

		for (i = 0; i < JET_MUTE_QUEUE_SIZE; i++)
		{
			/* search for event in queue */
			if ((easHandle->jetHandle->muteQueue[i] & JET_CLIP_ID_MASK) == (value & JET_CLIP_ID_MASK))
			{
				/* get segment pointer and mute flag */
				pSeg = &easHandle->jetHandle->segQueue[segTrack >> JET_EVENT_SEG_SHIFT];
				muteFlag = 1 << ((segTrack & JET_EVENT_TRACK_MASK) >> JET_EVENT_TRACK_SHIFT);
				
				/* un-mute the track */
				if ((easHandle->jetHandle->muteQueue[i] & JET_CLIP_TRIGGER_FLAG) && ((value & 0x40) > 0))
				{
					pSeg->muteFlags &= ~muteFlag;
					easHandle->jetHandle->muteQueue[i] &= ~JET_CLIP_TRIGGER_FLAG;
				}

				/* mute the track */
				else
				{
					EAS_U32 beforeMute ;
					beforeMute = pSeg->muteFlags ;
					pSeg->muteFlags |= muteFlag;
					if (beforeMute != pSeg->muteFlags)
						easHandle->jetHandle->muteQueue[i] = 0;
				}
				EAS_IntSetStrmParam(easHandle, pSeg->streamHandle, PARSER_DATA_MUTE_FLAGS, (EAS_I32) pSeg->muteFlags);
				return;
			}
		}
		return;
	}
		
	/* generic event stuff */
	event = (channel << JET_EVENT_CHAN_SHIFT) | (controller << JET_EVENT_CTRL_SHIFT) | value;

	/* write to app queue, translate queue index to segment number */
	if ((controller >= easHandle->jetHandle->config.appEventRangeLow) && (controller <= easHandle->jetHandle->config.appEventRangeHigh))
	{

		event |= easHandle->jetHandle->segQueue[(segTrack & JET_EVENT_SEG_MASK) >> JET_EVENT_SEG_SHIFT].userID << JET_EVENT_SEG_SHIFT;
#ifdef DEBUG_JET		
		JET_DumpEvent("JET_Event[app]", event);
#endif
		JET_WriteQueue(easHandle->jetHandle->appEventQueue,
			&easHandle->jetHandle->appEventQueueWrite, 
			easHandle->jetHandle->appEventQueueRead, 
			APP_EVENT_QUEUE_SIZE,
			event);
	}

	/* write to JET queue */
	else if ((controller >= JET_EVENT_LOW) && (controller <= JET_EVENT_HIGH))
	{
		event |= segTrack;
#ifdef DEBUG_JET		
		JET_DumpEvent("JET_Event[jet]", event);
#endif
		JET_WriteQueue(easHandle->jetHandle->jetEventQueue,
			&easHandle->jetHandle->jetEventQueueWrite, 
			easHandle->jetHandle->jetEventQueueRead, 
			JET_EVENT_QUEUE_SIZE,
			event);
	}
}

/*----------------------------------------------------------------------------
 * JET_Clear_Queue()
 *----------------------------------------------------------------------------
 * Clears the queue and stops play without a complete shutdown
 *----------------------------------------------------------------------------
*/
EAS_RESULT JET_Clear_Queue(EAS_DATA_HANDLE easHandle)
{	
	EAS_INT index;
	EAS_RESULT result = EAS_SUCCESS;

	{ /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "JET_Clear_Queue\n"); */ }
		
	/* pause all playing streams */
	for (index = 0; index < SEG_QUEUE_DEPTH; index++)
	{
		if (easHandle->jetHandle->segQueue[index].state == JET_STATE_PLAYING)
		{
			result = EAS_Pause(easHandle, easHandle->jetHandle->segQueue[index].streamHandle);
			if (result != EAS_SUCCESS)
				return result;
				
			easHandle->jetHandle->segQueue[index].state = JET_STATE_PAUSED;
		}		
	}
	
	/* close all streams */
	for (index = 0; index < SEG_QUEUE_DEPTH; index++)
	{
		if (easHandle->jetHandle->segQueue[index].streamHandle != NULL)
		{
			result = JET_CloseSegment(easHandle, index);
			if (result != EAS_SUCCESS)
				return result;
		}
	}
    
    /* clear all clips */
    for (index = 0; index < JET_MUTE_QUEUE_SIZE ; index++)
    {
        easHandle->jetHandle->muteQueue[index] = 0;
    }
	
	easHandle->jetHandle->flags &= ~JET_FLAGS_PLAYING;
	easHandle->jetHandle->playSegment = easHandle->jetHandle->queueSegment = 0;
	return result;
}