C++程序  |  942行  |  27.03 KB

/*----------------------------------------------------------------------------
 *
 * File: 
 * eas_tonecontrol.c
 *
 * Contents and purpose:
 * MMAPI ToneControl parser
 *
 * Copyright Sonic Network Inc. 2006

 * 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: 795 $
 *   $Date: 2007-08-01 00:14:45 -0700 (Wed, 01 Aug 2007) $
 *----------------------------------------------------------------------------
*/

#include "eas_data.h"
#include "eas_miditypes.h"
#include "eas_parser.h"
#include "eas_report.h"
#include "eas_host.h"
#include "eas_midi.h"
#include "eas_config.h"
#include "eas_vm_protos.h"
#include "eas_tcdata.h"


/* default channel and program for TC playback */
#define TC_CHANNEL				0
#define TC_PROGRAM				80
#define TC_VELOCITY				127

#define TC_FIELD_SILENCE		-1
#define TC_FIELD_VERSION		-2
#define TC_FIELD_TEMPO			-3
#define TC_FIELD_RESOLUTION		-4
#define TC_FIELD_BLOCK_START	-5
#define TC_FIELD_BLOCK_END		-6
#define TC_FIELD_PLAY_BLOCK		-7
#define TC_FIELD_SET_VOLUME		-8
#define TC_FIELD_REPEAT			-9
#define TC_FIELD_INVALID		-10

/* convert 0-100 volume to 0-127 velocity using fixed point */
#define TC_VOLUME_CONV			21307064
#define TC_VOLUME_SHIFT			24


/* local prototypes */
static EAS_RESULT TC_CheckFileType (S_EAS_DATA *pEASData, EAS_FILE_HANDLE fileHandle, EAS_VOID_PTR *ppHandle, EAS_I32 offset);
static EAS_RESULT TC_Prepare (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData);
static EAS_RESULT TC_Time (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_U32 *pTime);
static EAS_RESULT TC_Event (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_INT parserMode);
static EAS_RESULT TC_State (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_STATE *pState);
static EAS_RESULT TC_Close (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData);
static EAS_RESULT TC_Reset (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData);
static EAS_RESULT TC_Pause (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData);
static EAS_RESULT TC_Resume (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData);
static EAS_RESULT TC_SetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 value);
static EAS_RESULT TC_GetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 *pValue);
static EAS_RESULT TC_ParseHeader (S_EAS_DATA *pEASData, S_TC_DATA* pData);
static EAS_RESULT TC_StartNote (S_EAS_DATA *pEASData, S_TC_DATA* pData, EAS_INT parserMode, EAS_I8 note);
static EAS_RESULT TC_GetRepeat (S_EAS_DATA *pEASData, S_TC_DATA* pData, EAS_INT parserMode);
static EAS_RESULT TC_PlayBlock (S_EAS_DATA *pEASData, S_TC_DATA* pData);
static EAS_RESULT TC_BlockEnd (S_EAS_DATA *pEASData, S_TC_DATA* pData);
static EAS_RESULT TC_GetVolume (S_EAS_DATA *pEASData, S_TC_DATA* pData);
static EAS_RESULT TC_GetTempo (S_EAS_DATA *pEASData, S_TC_DATA* pData);
static EAS_RESULT TC_GetResolution (S_EAS_DATA *pEASData, S_TC_DATA* pData);
static EAS_RESULT TC_GetNextChar (EAS_HW_DATA_HANDLE hwInstData, S_TC_DATA *pData, EAS_I8 *pValue);
static void TC_PutBackChar (S_TC_DATA *pData, EAS_I8 value);

/* calculate a new tick time based on resolution & tempo */
EAS_INLINE void TC_CalcTimeBase (S_TC_DATA *pData)
{

	/* ticks in 256ths of a millisecond */
	pData->tick = ((60 * 1000) << 8) / (pData->tempo * pData->resolution);
}

/*----------------------------------------------------------------------------
 *
 * EAS_TC_Parser
 *
 * This structure contains the functional interface for the iMelody parser 
 *----------------------------------------------------------------------------
*/
const S_FILE_PARSER_INTERFACE EAS_TC_Parser =
{
	TC_CheckFileType,
	TC_Prepare,
	TC_Time,
	TC_Event,
	TC_State,
	TC_Close,
	TC_Reset,
	TC_Pause,
	TC_Resume,
	NULL,
	TC_SetData,
	TC_GetData,
	NULL
};

/*----------------------------------------------------------------------------
 * TC_CheckFileType()
 *----------------------------------------------------------------------------
 * Purpose: 
 * Check the file type to see if we can parse it
 *
 * Inputs:
 * pEASData			- pointer to overall EAS data structure
 * handle			- pointer to file handle
 *		
 * Outputs:
 * 
 *
 * Side Effects:
 *
 *----------------------------------------------------------------------------
*/
static EAS_RESULT TC_CheckFileType (S_EAS_DATA *pEASData, EAS_FILE_HANDLE fileHandle, EAS_VOID_PTR *ppHandle, EAS_I32 offset)
{
	S_TC_DATA data;
	S_TC_DATA *pData;

	/* init data */
	EAS_HWMemSet(&data, 0, sizeof(S_TC_DATA));
	data.fileHandle = fileHandle;
	data.fileOffset = offset;
	*ppHandle= NULL;
	
	/* see if we can parse the header */
	if (TC_ParseHeader(pEASData, &data) == EAS_SUCCESS)
	{

		/* check for static memory allocation */
		if (pEASData->staticMemoryModel)
			pData = EAS_CMEnumOptData(EAS_MODULE_MMAPI_TONE_CONTROL);
		else
			pData = EAS_HWMalloc(pEASData->hwInstData, sizeof(S_TC_DATA));
		if (!pData)
			return EAS_ERROR_MALLOC_FAILED;

		/* copy data to persistent storage */
		EAS_HWMemCpy(pData, &data, sizeof(S_TC_DATA));
		
		/* return a pointer to the instance data */
		pData->state = EAS_STATE_OPEN;
		*ppHandle = pData;
	}

	return EAS_SUCCESS;
}

/*----------------------------------------------------------------------------
 * TC_Prepare()
 *----------------------------------------------------------------------------
 * Purpose: 
 * Prepare to parse the file. Allocates instance data (or uses static allocation for
 * static memory model). 
 *
 * Inputs:
 * pEASData			- pointer to overall EAS data structure
 * handle			- pointer to file handle
 *		
 * Outputs:
 * 
 *
 * Side Effects:
 *
 *----------------------------------------------------------------------------
*/
static EAS_RESULT TC_Prepare (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
{
	S_TC_DATA* pData;
	EAS_RESULT result;

	/* check for valid state */
	pData = (S_TC_DATA*) pInstData;
	if (pData->state != EAS_STATE_OPEN)
		return EAS_ERROR_NOT_VALID_IN_THIS_STATE;

	/* instantiate a synthesizer */
	if ((result = VMInitMIDI(pEASData, &pData->pSynth)) != EAS_SUCCESS)
	{
		{ /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "VMInitMIDI returned %d\n", result); */ }
		return result;
	}

	/* set to ready state */
	pData->state = EAS_STATE_READY;
	return EAS_SUCCESS;
}

/*----------------------------------------------------------------------------
 * TC_Time()
 *----------------------------------------------------------------------------
 * Purpose: 
 * Returns the time of the next event in msecs
 *
 * Inputs:
 * pEASData			- pointer to overall EAS data structure
 * handle			- pointer to file handle
 * pTime			- pointer to variable to hold time of next event (in msecs)
 *		
 * Outputs:
 * 
 *
 * Side Effects:
 *
 *----------------------------------------------------------------------------
*/
/*lint -esym(715, pEASData) reserved for future use */
static EAS_RESULT TC_Time (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_U32 *pTime)
{
	S_TC_DATA *pData;
	
	pData = (S_TC_DATA*) pInstData;

	/* return time in milliseconds */
	/*lint -e{704} use shift instead of division */
	*pTime = pData->time >> 8;
	return EAS_SUCCESS;
}

/*----------------------------------------------------------------------------
 * TC_Event()
 *----------------------------------------------------------------------------
 * Purpose: 
 * Parse the next event in the file
 *
 * Inputs:
 * pEASData			- pointer to overall EAS data structure
 * handle			- pointer to file handle
 *		
 * Outputs:
 * 
 *
 * Side Effects:
 *
 *----------------------------------------------------------------------------
*/
static EAS_RESULT TC_Event (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_INT parserMode)
{
	S_TC_DATA* pData;
	EAS_RESULT result;
	EAS_I8 temp;

	pData = (S_TC_DATA*) pInstData;
	if (pData->state >= EAS_STATE_OPEN)
		return EAS_SUCCESS;
	
	/* initialize MIDI channel when the track starts playing */
	if (pData->time == 0)
	{
		/* set program to square lead */
		VMProgramChange(pEASData->pVoiceMgr, pData->pSynth, TC_CHANNEL, TC_PROGRAM);

		/* set channel volume to max */
		VMControlChange(pEASData->pVoiceMgr, pData->pSynth, TC_CHANNEL, 7, 127);
	}

	/* check for end of note */
	if (pData->note >= 0)
	{
		/* stop the note */
		VMStopNote(pEASData->pVoiceMgr, pData->pSynth, TC_CHANNEL, (EAS_U8) pData->note, 0);

		/* check for repeat note */
		if (pData->repeatCount)
		{
			pData->repeatCount--;
			pData->time += pData->length;
			if ((pData->note >= 0) && (parserMode == eParserModePlay))
				VMStartNote(pEASData->pVoiceMgr, pData->pSynth, TC_CHANNEL, (EAS_U8) pData->note, pData->volume);
			return EAS_SUCCESS;
		}
		
		pData->note = TC_FIELD_SILENCE;
	}

	/* parse stream until we get a note or rest */
	for (;;)
	{

		/* get next byte from stream */
		if ((result = TC_GetNextChar(pEASData->hwInstData, pData, &temp)) != EAS_SUCCESS)
		{
			if (result == EAS_EOF)
			{
				pData->state = EAS_STATE_STOPPING;
				return EAS_SUCCESS;
			}
			break;
		}

		/* check for musical events */
		if (temp >= TC_FIELD_SILENCE)
		{
			result = TC_StartNote(pEASData, pData, parserMode, temp);
			break;
		}

		/* must be a control field */
		switch (temp)
		{
			case TC_FIELD_TEMPO:
				result = TC_GetTempo(pEASData, pData);
				break;

			case TC_FIELD_RESOLUTION:
				result = TC_GetResolution(pEASData, pData);
				break;

			case TC_FIELD_SET_VOLUME:
				result = TC_GetVolume(pEASData, pData);
				break;

			case TC_FIELD_REPEAT:
				result = TC_GetRepeat(pEASData, pData, parserMode);
				break;

			case TC_FIELD_PLAY_BLOCK:
				result = TC_PlayBlock(pEASData, pData);
				break;
				
			case TC_FIELD_BLOCK_START:
				result = TC_GetNextChar(pEASData->hwInstData, pData, &temp);
				break;
				
			case TC_FIELD_BLOCK_END:
				result = TC_BlockEnd(pEASData, pData);
				break;
				
			default:
				{ /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "Unexpected byte 0x%02x in ToneControl stream\n", temp); */ }
				result = EAS_ERROR_FILE_FORMAT;
		}

		/* check for error */
		if (result != EAS_SUCCESS)
			break;
	}

	/* check for error */
	if (result != EAS_SUCCESS)
	{
		if (result == EAS_EOF)
			result = EAS_ERROR_FILE_FORMAT;
		pData->state = EAS_STATE_ERROR;
	}
	else
		pData->state = EAS_STATE_PLAY;
	return result;
}

/*----------------------------------------------------------------------------
 * TC_State()
 *----------------------------------------------------------------------------
 * Purpose: 
 * Returns the current state of the stream
 *
 * Inputs:
 * pEASData			- pointer to overall EAS data structure
 * handle			- pointer to file handle
 * pState			- pointer to variable to store state
 *		
 * Outputs:
 * 
 *
 * Side Effects:
 *
 *----------------------------------------------------------------------------
*/
/*lint -esym(715, pEASData) reserved for future use */
static EAS_RESULT TC_State (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 *pState)
{
	S_TC_DATA* pData;

	/* establish pointer to instance data */
	pData = (S_TC_DATA*) pInstData;

	/* if stopping, check to see if synth voices are active */
	if (pData->state == EAS_STATE_STOPPING)
	{
		if (VMActiveVoices(pData->pSynth) == 0)
			pData->state = EAS_STATE_STOPPED;
	}
	
	if (pData->state == EAS_STATE_PAUSING)
	{
		if (VMActiveVoices(pData->pSynth) == 0)
			pData->state = EAS_STATE_PAUSED;
	}
	
	/* return current state */
	*pState = pData->state;
	return EAS_SUCCESS;
}

/*----------------------------------------------------------------------------
 * TC_Close()
 *----------------------------------------------------------------------------
 * Purpose: 
 * Close the file and clean up
 *
 * Inputs:
 * pEASData			- pointer to overall EAS data structure
 * handle			- pointer to file handle
 *		
 * Outputs:
 * 
 *
 * Side Effects:
 *
 *----------------------------------------------------------------------------
*/
static EAS_RESULT TC_Close (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
{
	S_TC_DATA* pData;
	EAS_RESULT result;

	pData = (S_TC_DATA*) pInstData;	

	/* close the file */
	if ((result = EAS_HWCloseFile(pEASData->hwInstData, pData->fileHandle)) != EAS_SUCCESS)
			return result;

	/* free the synth */
	if (pData->pSynth != NULL)
		VMMIDIShutdown(pEASData, pData->pSynth);

	/* if using dynamic memory, free it */
	if (!pEASData->staticMemoryModel)
		EAS_HWFree(pEASData->hwInstData, pData);

	return EAS_SUCCESS;
}

/*----------------------------------------------------------------------------
 * TC_Reset()
 *----------------------------------------------------------------------------
 * Purpose: 
 * Reset the sequencer. Used for locating backwards in the file.
 *
 * Inputs:
 * pEASData			- pointer to overall EAS data structure
 * handle			- pointer to file handle
 *		
 * Outputs:
 * 
 *
 * Side Effects:
 *
 *----------------------------------------------------------------------------
*/
static EAS_RESULT TC_Reset (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
{
	S_TC_DATA* pData;
	EAS_RESULT result;

	pData = (S_TC_DATA*) pInstData;

	/* reset the synth */
	VMReset(pEASData->pVoiceMgr, pData->pSynth, EAS_TRUE);

	/* reset time to zero */
	pData->time = 0;

	/* reset file position and re-parse header */
	pData->state = EAS_STATE_ERROR;
	if ((result = EAS_HWFileSeek(pEASData->hwInstData, pData->fileHandle, pData->fileOffset)) != EAS_SUCCESS)
		return result;
	if ((result = TC_ParseHeader (pEASData,  pData)) != EAS_SUCCESS)
		return result;

	pData->state = EAS_STATE_READY;
	return EAS_SUCCESS;
}

/*----------------------------------------------------------------------------
 * TC_Pause()
 *----------------------------------------------------------------------------
 * Purpose: 
 * Pauses the sequencer. Mutes all voices and sets state to pause.
 *
 * Inputs:
 * pEASData			- pointer to overall EAS data structure
 * handle			- pointer to file handle
 *		
 * Outputs:
 * 
 *
 * Side Effects:
 *
 *----------------------------------------------------------------------------
*/
static EAS_RESULT TC_Pause (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
{
	S_TC_DATA *pData;

	/* can't pause a stopped stream */
	pData = (S_TC_DATA*) pInstData;
	if (pData->state == EAS_STATE_STOPPED)
		return EAS_ERROR_ALREADY_STOPPED;

	/* mute the synthesizer */
	VMMuteAllVoices(pEASData->pVoiceMgr, pData->pSynth);
	pData->state = EAS_STATE_PAUSING;	
	return EAS_SUCCESS;
}

/*----------------------------------------------------------------------------
 * TC_Resume()
 *----------------------------------------------------------------------------
 * Purpose: 
 * Resume playing after a pause, sets state back to playing.
 *
 * Inputs:
 * pEASData			- pointer to overall EAS data structure
 * handle			- pointer to file handle
 *		
 * Outputs:
 * 
 *
 * Side Effects:
 *
 *----------------------------------------------------------------------------
*/
/*lint -esym(715, pEASData) reserved for future use */
static EAS_RESULT TC_Resume (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
{
	S_TC_DATA *pData;

	/* can't resume a stopped stream */
	pData = (S_TC_DATA*) pInstData;
	if (pData->state == EAS_STATE_STOPPED)
		return EAS_ERROR_ALREADY_STOPPED;

	/* nothing to do but resume playback */
	pData->state = EAS_STATE_PLAY;	
	return EAS_SUCCESS;
}

/*----------------------------------------------------------------------------
 * TC_SetData()
 *----------------------------------------------------------------------------
 * Purpose: 
 * Return file type
 *
 * Inputs:
 * pEASData			- pointer to overall EAS data structure
 * handle			- pointer to file handle
 *		
 * Outputs:
 * 
 *
 * Side Effects:
 *
 *----------------------------------------------------------------------------
*/
/*lint -esym(715, pEASData, pInstData, value) reserved for future use */
static EAS_RESULT TC_SetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 value)
{
	/* we don't parse any metadata, but we need to return success here */
	if (param == PARSER_DATA_METADATA_CB)
		return EAS_SUCCESS;
	
	return EAS_ERROR_INVALID_PARAMETER;
}

/*----------------------------------------------------------------------------
 * TC_GetData()
 *----------------------------------------------------------------------------
 * Purpose: 
 * Return file type
 *
 * Inputs:
 * pEASData			- pointer to overall EAS data structure
 * handle			- pointer to file handle
 *		
 * Outputs:
 * 
 *
 * Side Effects:
 *
 *----------------------------------------------------------------------------
*/
/*lint -e{715} common with other parsers */
static EAS_RESULT TC_GetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 *pValue)
{
	S_TC_DATA *pData;

	pData = (S_TC_DATA *) pInstData;
	switch (param)
	{
		/* return file type as TC */
		case PARSER_DATA_FILE_TYPE:
			*pValue = EAS_FILE_MMAPI_TONE_CONTROL;
			break;

		case PARSER_DATA_SYNTH_HANDLE:
			*pValue = (EAS_I32) pData->pSynth;
			break;
			
	default:
			return EAS_ERROR_INVALID_PARAMETER;
	}			
	return EAS_SUCCESS;
}

/*----------------------------------------------------------------------------
 * TC_ParseHeader()
 *----------------------------------------------------------------------------
 * Purpose: 
 * Prepare to parse the file. Allocates instance data (or uses static allocation for
 * static memory model). 
 *
 * Inputs:
 * pEASData			- pointer to overall EAS data structure
 * handle			- pointer to file handle
 *		
 * Outputs:
 * 
 *
 * Side Effects:
 *
 *----------------------------------------------------------------------------
*/
static EAS_RESULT TC_ParseHeader (S_EAS_DATA *pEASData, S_TC_DATA* pData)
{
	EAS_RESULT result;
	EAS_I8 temp;

	/* initialize some defaults */
	pData->time = 0;
	pData->tempo = 120;
	pData->resolution = 64;
	pData->volume = 127;
	pData->repeatCount = 0;
	pData->note = TC_FIELD_SILENCE;
	pData->byteAvail = EAS_FALSE;

	/* set default timebase */
	TC_CalcTimeBase(pData);

	/* seek to start of data */
	if ((result = EAS_HWFileSeek(pEASData->hwInstData, pData->fileHandle, pData->fileOffset)) != EAS_SUCCESS)
		return result;

	/* get version */
	if ((result = TC_GetNextChar(pEASData->hwInstData, pData, &temp)) != EAS_SUCCESS)
		return result;

	/* check for version number */
	if (temp == TC_FIELD_VERSION)
	{
		TC_GetNextChar(pEASData->hwInstData, pData, &temp);
//		{ /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "ToneControl sequence version %d\n", temp); */ }
	}
	else
		return EAS_ERROR_FILE_FORMAT;

	/* parse the header data until we find the first note or block */
	for (;;)
	{

		/* get next byte from stream */
		if ((result = TC_GetNextChar(pEASData->hwInstData, pData, &temp)) != EAS_SUCCESS)
			return result;

		/* check for tempo */
		if (temp == TC_FIELD_TEMPO)
		{
			if ((result = TC_GetTempo(pEASData, pData)) != EAS_SUCCESS)
				return result;
		}

		/* or resolution */
		else if (temp == TC_FIELD_TEMPO)
		{
			if ((result = TC_GetResolution(pEASData, pData)) != EAS_SUCCESS)
				return result;
		}

		/* must be music data */
		else if (temp > TC_FIELD_INVALID)
		{
			TC_PutBackChar(pData, temp);
			return EAS_SUCCESS;
		}

		/* unknown codes */
		else
		{
			{ /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "Unexpected byte 0x%02x in ToneControl stream\n", temp); */ }
			return EAS_ERROR_FILE_FORMAT;
		}
	}
}

/*----------------------------------------------------------------------------
 * TC_StartNote()
 *----------------------------------------------------------------------------
 * Process a note or silence event
 *----------------------------------------------------------------------------
*/
static EAS_RESULT TC_StartNote (S_EAS_DATA *pEASData, S_TC_DATA* pData, EAS_INT parserMode, EAS_I8 note)
{
	EAS_I8 duration;

	/* get the duration */
	if (TC_GetNextChar(pEASData->hwInstData, pData, &duration) != EAS_SUCCESS)
		return EAS_ERROR_FILE_FORMAT;

	/* calculate time of next event */
	pData->length = (EAS_I32) duration * pData->tick;
	pData->time += pData->length;

	/* start the note */
	if ((note >= 0) && (parserMode == eParserModePlay))
	{
		VMStartNote(pEASData->pVoiceMgr, pData->pSynth, TC_CHANNEL, (EAS_U8) note, pData->volume);
		pData->note = note;
	}
	
	return EAS_SUCCESS;
}

/*----------------------------------------------------------------------------
 * TC_GetRepeat()
 *----------------------------------------------------------------------------
 * Process a repeat code
 *----------------------------------------------------------------------------
*/
static EAS_RESULT TC_GetRepeat (S_EAS_DATA *pEASData, S_TC_DATA* pData, EAS_INT parserMode)
{
	EAS_I8 count;

	/* get the repeat count */
	if (TC_GetNextChar(pEASData->hwInstData, pData, &count) != EAS_SUCCESS)
		return EAS_ERROR_FILE_FORMAT;

	/* validiate it */
	if (count < 2)
		return EAS_ERROR_FILE_FORMAT;

	/* calculate time of next event */
	pData->time += pData->length;
	pData->repeatCount = count - 2;

	/* start the note */
	if ((pData->note >= 0) && (parserMode == eParserModePlay))
		VMStartNote(pEASData->pVoiceMgr, pData->pSynth, TC_CHANNEL, (EAS_U8) pData->note, pData->volume);
	
	return EAS_SUCCESS;
}

/*----------------------------------------------------------------------------
 * TC_PlayBlock()
 *----------------------------------------------------------------------------
 * Play a block of notes
 *----------------------------------------------------------------------------
*/
static EAS_RESULT TC_PlayBlock (S_EAS_DATA *pEASData, S_TC_DATA* pData)
{
	EAS_RESULT result;
	EAS_I8 blockNum;
	EAS_I8 temp;
	EAS_I8 temp2;

	/* get the block number */
	if (TC_GetNextChar(pEASData->hwInstData, pData, &blockNum) != EAS_SUCCESS)
		return EAS_ERROR_FILE_FORMAT;

	/* validiate it */
	if (blockNum < 0)
		return EAS_ERROR_FILE_FORMAT;

	/* save the current position */
	if ((result = EAS_HWFilePos(pEASData->hwInstData, pData->fileHandle, &pData->restorePos)) != EAS_SUCCESS)
		return result;

	/* return to start of file */
	pData->byteAvail = EAS_FALSE;
	if ((result = EAS_HWFileSeek(pEASData->hwInstData, pData->fileHandle, pData->fileOffset)) != EAS_SUCCESS)
		return result;

	/* find the block */
	for (;;)
	{
		if (TC_GetNextChar(pEASData->hwInstData, pData, &temp) != EAS_SUCCESS)
			return EAS_ERROR_FILE_FORMAT;

		if (TC_GetNextChar(pEASData->hwInstData, pData, &temp2) != EAS_SUCCESS)
			return EAS_ERROR_FILE_FORMAT;
		
		if ((temp == TC_FIELD_BLOCK_START) && (temp2 == blockNum))
			return EAS_SUCCESS;
	}
}

/*----------------------------------------------------------------------------
 * TC_BlockEnd()
 *----------------------------------------------------------------------------
 * Handle end of block
 *----------------------------------------------------------------------------
*/
static EAS_RESULT TC_BlockEnd (S_EAS_DATA *pEASData, S_TC_DATA* pData)
{
	EAS_I8 blockNum;

	/* get the block number */
	if (TC_GetNextChar(pEASData->hwInstData, pData, &blockNum) != EAS_SUCCESS)
		return EAS_ERROR_FILE_FORMAT;

	/* validiate it */
	if (blockNum < 0)
		return EAS_ERROR_FILE_FORMAT;

	/* if we were playing this block, restore to previous position */
	pData->byteAvail = EAS_FALSE;
	return EAS_HWFileSeek(pEASData->hwInstData, pData->fileHandle, pData->restorePos);
}

/*----------------------------------------------------------------------------
 * TC_GetVolume()
 *----------------------------------------------------------------------------
 * Get the volume field and process it
 *----------------------------------------------------------------------------
*/
static EAS_RESULT TC_GetVolume (S_EAS_DATA *pEASData, S_TC_DATA* pData)
{
	EAS_I8 volume;

	/* get volume */		
	if (TC_GetNextChar(pEASData->hwInstData, pData, &volume) != EAS_SUCCESS)
		return EAS_ERROR_FILE_FORMAT;
	if ((volume < 0) || (volume > 100))
		return EAS_ERROR_FILE_FORMAT;

	/* save volume */
	pData->volume = (EAS_U8) ((EAS_I32) (volume * TC_VOLUME_CONV + 1) >> TC_VOLUME_SHIFT);
	return EAS_SUCCESS;
}

/*----------------------------------------------------------------------------
 * TC_GetTempo()
 *----------------------------------------------------------------------------
 * Get the tempo field and process it
 *----------------------------------------------------------------------------
*/
static EAS_RESULT TC_GetTempo (S_EAS_DATA *pEASData, S_TC_DATA* pData)
{
	EAS_I8 tempo;

	/* get tempo */		
	if (TC_GetNextChar(pEASData->hwInstData, pData, &tempo) != EAS_SUCCESS)
		return EAS_ERROR_FILE_FORMAT;
	if (tempo < 5)
		return EAS_ERROR_FILE_FORMAT;

	/* save tempo */
	pData->tempo = tempo;
	
	/* calculate new timebase */
	TC_CalcTimeBase(pData);
	return EAS_SUCCESS;
}

/*----------------------------------------------------------------------------
 * TC_GetResolution()
 *----------------------------------------------------------------------------
 * Get the resolution field and process it
 *----------------------------------------------------------------------------
*/
static EAS_RESULT TC_GetResolution (S_EAS_DATA *pEASData, S_TC_DATA* pData)
{
	EAS_I8 resolution;

	/* get resolution */		
	if (TC_GetNextChar(pEASData->hwInstData, pData, &resolution) != EAS_SUCCESS)
		return EAS_ERROR_FILE_FORMAT;
	if (resolution < 0)
		return EAS_ERROR_FILE_FORMAT;

	/* save tempo */
	pData->resolution = resolution;
	
	/* calculate new timebase */
	TC_CalcTimeBase(pData);
	return EAS_SUCCESS;
}

/*----------------------------------------------------------------------------
 * TC_GetNextChar()
 *----------------------------------------------------------------------------
 * Fetch the next character from the stream
 *----------------------------------------------------------------------------
*/
static EAS_RESULT TC_GetNextChar (EAS_HW_DATA_HANDLE hwInstData, S_TC_DATA *pData, EAS_I8 *pValue)
{

	/* get character from "put back" buffer */
	if (pData->byteAvail)
	{
		pData->byteAvail = EAS_FALSE;
		*pValue = pData->dataByte;
		return EAS_SUCCESS;
	}

	/* get character from file */
	return EAS_HWGetByte(hwInstData, pData->fileHandle, pValue);
}

/*----------------------------------------------------------------------------
 * TC_PutBackChar()
 *----------------------------------------------------------------------------
 * Put back the character
 *----------------------------------------------------------------------------
*/
static void TC_PutBackChar (S_TC_DATA *pData, EAS_I8 value)
{

	pData->dataByte = value;
	pData->byteAvail = EAS_TRUE;
}