C++程序  |  1078行  |  30.11 KB

/*----------------------------------------------------------------------------
 *
 * File: 
 * eas_ota.c
 *
 * Contents and purpose:
 * OTA parser
 *
 * Copyright Sonic Network Inc. 2005

 * 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_otadata.h"

/* increase gain for mono ringtones */
#define OTA_GAIN_OFFSET				8

/* file definitions */
#define OTA_RINGTONE 				0x25
#define OTA_SOUND 					0x1d
#define OTA_UNICODE					0x22

/* song type definitions */
#define OTA_BASIC_SONG_TYPE			0x01
#define OTA_TEMPORARY_SONG_TYPE		0x02

/* instruction ID coding */
#define OTA_PATTERN_HEADER_ID		0x00
#define OTA_NOTE_INST_ID			0x01
#define OTA_SCALE_INST_ID			0x02
#define OTA_STYLE_INST_ID			0x03
#define OTA_TEMPO_INST_ID			0x04
#define OTA_VOLUME_INST_ID			0x05

/* note durations */
#define OTA_NORMAL_DURATION			0x00
#define OTA_DOTTED_NOTE				0x01
#define OTA_DOUBLE_DOTTED_NOTE		0x02
#define OTA_TRIPLET_NOTE			0x03

/* loop count value for infinite loop */
#define OTA_INFINITE_LOOP			0x0f

/* length of 32nd note in 1/256ths of a msec for 63 BPM tempo */
#define DEFAULT_TICK_CONV			30476

/* default channel and program for OTA playback */
#define OTA_CHANNEL					0
#define OTA_PROGRAM					80
#define OTA_VEL_MUL					4
#define OTA_VEL_OFS					67
#define OTA_VEL_DEFAULT				95

/* multiplier for fixed point triplet conversion */
#define TRIPLET_MULTIPLIER			683
#define TRIPLET_SHIFT				10

/* local prototypes */
static EAS_RESULT OTA_CheckFileType (S_EAS_DATA *pEASData, EAS_FILE_HANDLE fileHandle, EAS_VOID_PTR *ppHandle, EAS_I32 offset);
static EAS_RESULT OTA_Prepare (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData);
static EAS_RESULT OTA_Time (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_U32 *pTime);
static EAS_RESULT OTA_Event (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_INT parserMode);
static EAS_RESULT OTA_State (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_STATE *pState);
static EAS_RESULT OTA_Close (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData);
static EAS_RESULT OTA_Reset (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData);
static EAS_RESULT OTA_Pause (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData);
static EAS_RESULT OTA_Resume (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData);
static EAS_RESULT OTA_SetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 value);
static EAS_RESULT OTA_GetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 *pValue);
static EAS_RESULT OTA_ParseHeader (S_EAS_DATA *pEASData, S_OTA_DATA* pData);
static EAS_RESULT OTA_FetchBitField (EAS_HW_DATA_HANDLE hwInstData, S_OTA_DATA *pData, EAS_I32 numBits, EAS_U8 *pValue);
static EAS_RESULT OTA_SavePosition (EAS_HW_DATA_HANDLE hwInstData, S_OTA_DATA *pData, S_OTA_LOC *pLoc);
static EAS_RESULT OTA_RestorePosition (EAS_HW_DATA_HANDLE hwInstData, S_OTA_DATA *pData, S_OTA_LOC *pLoc);


/*----------------------------------------------------------------------------
 *
 * EAS_OTA_Parser
 *
 * This structure contains the functional interface for the OTA parser 
 *----------------------------------------------------------------------------
*/
const S_FILE_PARSER_INTERFACE EAS_OTA_Parser =
{
	OTA_CheckFileType,
	OTA_Prepare,
	OTA_Time,
	OTA_Event,
	OTA_State,
	OTA_Close,
	OTA_Reset,
	OTA_Pause,
	OTA_Resume,
	NULL,
	OTA_SetData,
	OTA_GetData,
	NULL
};

/*----------------------------------------------------------------------------
 *
 * bpmTable
 *
 * BPM conversion table. Converts bpm values to 256ths of a millisecond for a 32nd note 
 *----------------------------------------------------------------------------
*/
static const EAS_U32 bpmTable[32] =
{
	76800, 68571, 61935, 54857, 
	48000, 42667, 38400, 34286,
	30476, 27429, 24000, 21333,
	19200, 17143, 15360, 13714,
	12000, 10667, 9600, 8533,
	7680, 6737, 6000, 5408,
	4800, 4267, 3840, 3398,
	3024, 2685, 2400, 2133
};

/*----------------------------------------------------------------------------
 * OTA_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 OTA_CheckFileType (S_EAS_DATA *pEASData, EAS_FILE_HANDLE fileHandle, EAS_VOID_PTR *ppHandle, EAS_I32 offset)
{
	S_OTA_DATA* pData;
	EAS_RESULT result;
	EAS_INT cmdLen;
	EAS_INT state;
	EAS_U8 temp;
	
	/* read the first byte, should be command length */
	*ppHandle = NULL;
	if ((result = EAS_HWGetByte(pEASData->hwInstData, fileHandle, &temp)) != EAS_SUCCESS)
		return result;

	/* read all the commands */
	cmdLen = temp;
	state = 0;
	while (cmdLen--)
	{
	
		/* read the command, upper 7 bits */	
		if ((result = EAS_HWGetByte(pEASData->hwInstData, fileHandle, &temp)) != EAS_SUCCESS)
			return result;
		temp = temp >> 1;

		if (state == 0)
		{
			if (temp != OTA_RINGTONE)
				break;
			state++;
		}
		else
		{
	
			if (temp == OTA_SOUND)
			{
				
				/* check for static memory allocation */
				if (pEASData->staticMemoryModel)
					pData = EAS_CMEnumData(EAS_CM_OTA_DATA);
				else
					pData = EAS_HWMalloc(pEASData->hwInstData, sizeof(S_OTA_DATA));
				if (!pData)
				{
					{ /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "Malloc failed in OTA_Prepare\n"); */ }
					return EAS_ERROR_MALLOC_FAILED;
				}
				EAS_HWMemSet(pData, 0, sizeof(S_OTA_DATA));

				/* return a pointer to the instance data */
				pData->fileHandle = fileHandle;
				pData->fileOffset = offset;
				pData->state = EAS_STATE_OPEN;
				*ppHandle = pData;
				break;
			}

			if (temp != OTA_UNICODE)
				break;
		}
	}

	/* not recognized */
	return EAS_SUCCESS;
}

/*----------------------------------------------------------------------------
 * OTA_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 OTA_Prepare (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
{
	S_OTA_DATA* pData;
	EAS_RESULT result;

	/* check for valid state */
	pData = (S_OTA_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;
	}
		
	pData->state = EAS_STATE_ERROR;
	if ((result = OTA_ParseHeader(pEASData, pData)) != EAS_SUCCESS)
		return result;

	pData->state = EAS_STATE_READY;
	return EAS_SUCCESS;
}

/*----------------------------------------------------------------------------
 * OTA_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) common decoder interface - pEASData not used */
static EAS_RESULT OTA_Time (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_U32 *pTime)
{
	S_OTA_DATA *pData;
	
	pData = (S_OTA_DATA*) pInstData;

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

/*----------------------------------------------------------------------------
 * OTA_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 OTA_Event (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_INT parserMode)
{
	S_OTA_DATA* pData;
	EAS_RESULT result;
	EAS_U32 duration;
	EAS_U8 temp;

	pData = (S_OTA_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 */
		if (parserMode != eParserModeMetaData)
			VMProgramChange(pEASData->pVoiceMgr, pData->pSynth, OTA_CHANNEL, OTA_PROGRAM);

		/* set channel volume to max */
		if (parserMode != eParserModeMetaData)
			VMControlChange(pEASData->pVoiceMgr, pData->pSynth, OTA_CHANNEL, 7, 127);
	}

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

		/* check for rest between notes */
		if (pData->restTicks)
		{
			pData->time += (EAS_I32) pData->restTicks;
			pData->restTicks = 0;
			return EAS_SUCCESS;
		}
	}

	/* if not in a pattern, read the pattern header */
	while (pData->current.patternLen == 0)
	{

		/* check for loop - don't do infinite loops when locating */
		if (pData->loopCount && ((parserMode == eParserModePlay) || (pData->loopCount != OTA_INFINITE_LOOP)))
		{
			/* if not infinite loop, decrement loop count */
			if (pData->loopCount != OTA_INFINITE_LOOP)
				pData->loopCount--;
			
			/* back to start of pattern*/
			if ((result = OTA_RestorePosition(pEASData->hwInstData, pData, &pData->patterns[pData->currentPattern])) != EAS_SUCCESS)
				return result;
		}

		/* if no previous position to restore, continue forward */
		else if (pData->restore.fileOffset < 0)
		{
		
			/* check for end of song */
			if (pData->numPatterns == 0)
			{
				pData->state = EAS_STATE_STOPPING;
				VMReleaseAllVoices(pEASData->pVoiceMgr, pData->pSynth);
				return EAS_SUCCESS;
			}

			/* read the next pattern header */
			if ((result = OTA_FetchBitField(pEASData->hwInstData, pData, 3, &temp)) != EAS_SUCCESS)
				return result;
			if (temp != OTA_PATTERN_HEADER_ID)
			{
				{ /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "Expected OTA pattern header\n"); */ }
				return EAS_ERROR_FILE_FORMAT;
			}

			/* get the pattern ID */
			if ((result = OTA_FetchBitField(pEASData->hwInstData, pData, 2, &pData->currentPattern)) != EAS_SUCCESS)
				return result;

			/* get the loop count */
			if ((result = OTA_FetchBitField(pEASData->hwInstData, pData, 4, &pData->loopCount)) != EAS_SUCCESS)
				return result;

			/* get the pattern length */
			if ((result = OTA_FetchBitField(pEASData->hwInstData, pData, 8, &pData->current.patternLen)) != EAS_SUCCESS)
				return result;

			/* if pattern definition, save the current position */
			if (pData->current.patternLen)
			{
				if ((result = OTA_SavePosition(pEASData->hwInstData, pData, &pData->patterns[pData->currentPattern])) != EAS_SUCCESS)
					return result;
			}
				
			/* if pattern length is zero, repeat a previous pattern */
			else
			{
				/* make sure it's a valid pattern */
				if (pData->patterns[pData->currentPattern].fileOffset < 0)
				{
					{ /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "OTA pattern error, invalid pattern specified\n"); */ }
					return EAS_ERROR_FILE_FORMAT;
				}

				/* save current position and data */
				if ((result = OTA_SavePosition(pEASData->hwInstData, pData, &pData->restore)) != EAS_SUCCESS)
					return result;

				/* seek to the pattern in the file */
				if ((result = OTA_RestorePosition(pEASData->hwInstData, pData, &pData->patterns[pData->currentPattern])) != EAS_SUCCESS)
					return result;
			}

			/* decrement pattern count */
			pData->numPatterns--;
		}

		/* restore previous position */
		else
		{
			if ((result = OTA_RestorePosition(pEASData->hwInstData, pData, &pData->restore)) != EAS_SUCCESS)
				return result;
		}
	}

	/* get the next event */
	if ((result = OTA_FetchBitField(pEASData->hwInstData, pData, 3, &temp)) != EAS_SUCCESS)
		return result;

	switch (temp)
	{
		case OTA_NOTE_INST_ID:
			/* fetch note value */
			if ((result = OTA_FetchBitField(pEASData->hwInstData, pData, 4, &pData->note)) != EAS_SUCCESS)
				return result;
			
			/* fetch note duration */
			if ((result = OTA_FetchBitField(pEASData->hwInstData, pData, 3, &temp)) != EAS_SUCCESS)
				return result;
			duration = pData->tick * (0x20 >> temp); 
			
			/* fetch note duration modifier */
			if ((result = OTA_FetchBitField(pEASData->hwInstData, pData, 2, &temp)) != EAS_SUCCESS)
				return result;
			switch (temp)
			{
				case OTA_NORMAL_DURATION:
					break;
					
				case OTA_DOTTED_NOTE:
					duration += duration >> 1;
					break;

				case OTA_DOUBLE_DOTTED_NOTE:
					duration += (duration >> 1) + (duration >> 2);
					break;

				case OTA_TRIPLET_NOTE:
					duration = (duration * TRIPLET_MULTIPLIER) >> TRIPLET_SHIFT;
					break;

				default:
					{ /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "Unrecognized note duration ignored\n"); */ }
					break;
			}

			/* check for note */
			if (pData->note)
			{
				
				/* determine note length based on style */
				switch (pData->style)
				{
					case 0:
						pData->restTicks = duration >> 4;
						break;
					case 1:
						pData->restTicks = 0;
						break;
					case 2:
						pData->restTicks = duration >> 1;
						break;
					default:
						{ /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "Unrecognized note style ignored\n"); */ }
				}

				/* add octave */
				pData->note += pData->octave;
				if (parserMode == eParserModePlay)
					VMStartNote(pEASData->pVoiceMgr, pData->pSynth, OTA_CHANNEL, pData->note, pData->velocity);
				pData->time += (EAS_I32) duration - (EAS_I32) pData->restTicks;
			}
			
			/* this is a rest */
			else
				pData->time += (EAS_I32) duration;
			break;
			
		case OTA_SCALE_INST_ID:
			/* fetch octave */
			if ((result = OTA_FetchBitField(pEASData->hwInstData, pData, 2, &temp)) != EAS_SUCCESS)
				return result;
			pData->octave = (EAS_U8) (temp * 12 + 59);
			break;

		case OTA_STYLE_INST_ID:
			/* fetch note style */
			if ((result = OTA_FetchBitField(pEASData->hwInstData, pData, 2, &pData->style)) != EAS_SUCCESS)
				return result;
			break;

		case OTA_TEMPO_INST_ID:
			/* fetch tempo */
			if ((result = OTA_FetchBitField(pEASData->hwInstData, pData, 5, &temp)) != EAS_SUCCESS)
				return result;
			pData->tick = bpmTable[temp];
			break;

		case OTA_VOLUME_INST_ID:
			/* fetch volume */
			if ((result = OTA_FetchBitField(pEASData->hwInstData, pData, 4, &temp)) != EAS_SUCCESS)
				return result;
			pData->velocity = temp ? (EAS_U8) (temp * OTA_VEL_MUL + OTA_VEL_OFS) : 0;
			break;

		default:
			{ /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "Unexpected instruction ID in OTA stream\n"); */ }
			return EAS_ERROR_FILE_FORMAT;
	}

	/* decrement pattern length */
	pData->current.patternLen--;
	return EAS_SUCCESS;
}
		
/*----------------------------------------------------------------------------
 * OTA_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) common decoder interface - pEASData not used */
static EAS_RESULT OTA_State (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 *pState)
{
	S_OTA_DATA* pData;

	/* establish pointer to instance data */
	pData = (S_OTA_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;
}

/*----------------------------------------------------------------------------
 * OTA_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 OTA_Close (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
{
	S_OTA_DATA* pData;
	EAS_RESULT result;

	pData = (S_OTA_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;
}

/*----------------------------------------------------------------------------
 * OTA_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 OTA_Reset (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
{
	S_OTA_DATA* pData;
	EAS_RESULT result;

	pData = (S_OTA_DATA*) pInstData;

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

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

	pData->state = EAS_STATE_READY;
	return EAS_SUCCESS;
}

/*----------------------------------------------------------------------------
 * OTA_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 OTA_Pause (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
{
	S_OTA_DATA *pData;

	/* can't pause a stopped stream */
	pData = (S_OTA_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;
}

/*----------------------------------------------------------------------------
 * OTA_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) common decoder interface - pEASData not used */
static EAS_RESULT OTA_Resume (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
{
	S_OTA_DATA *pData;

	/* can't resume a stopped stream */
	pData = (S_OTA_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;
}

/*----------------------------------------------------------------------------
 * OTA_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) common decoder interface - pEASData not used */
static EAS_RESULT OTA_SetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 value)
{
	S_OTA_DATA *pData;

	pData = (S_OTA_DATA *) pInstData;
	switch (param)
	{

		/* set metadata callback */
		case PARSER_DATA_METADATA_CB:
			EAS_HWMemCpy(&pData->metadata, (void*) value, sizeof(S_METADATA_CB));
			break;

		default:
			return EAS_ERROR_INVALID_PARAMETER;
	}

	return EAS_SUCCESS;
}

/*----------------------------------------------------------------------------
 * OTA_GetData()
 *----------------------------------------------------------------------------
 * Purpose: 
 * Return file type
 *
 * Inputs:
 * pEASData			- pointer to overall EAS data structure
 * handle			- pointer to file handle
 *		
 * Outputs:
 * 
 *
 * Side Effects:
 *
 *----------------------------------------------------------------------------
*/
/*lint -esym(715, pEASData) common decoder interface - pEASData not used */
static EAS_RESULT OTA_GetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 *pValue)
{
	S_OTA_DATA *pData;

	pData = (S_OTA_DATA*) pInstData;
	switch (param)
	{
		/* return file type as OTA */
		case PARSER_DATA_FILE_TYPE:
			*pValue = EAS_FILE_OTA;
			break;

#if 0			
		/* set transposition */
		case PARSER_DATA_TRANSPOSITION:
			*pValue = pData->transposition;
			break;
#endif

		case PARSER_DATA_SYNTH_HANDLE:
			*pValue = (EAS_I32) pData->pSynth;
			break;

		case PARSER_DATA_GAIN_OFFSET:
			*pValue = OTA_GAIN_OFFSET;
			break;
			
		default:
			return EAS_ERROR_INVALID_PARAMETER;
	}			
	return EAS_SUCCESS;
}

/*----------------------------------------------------------------------------
 * OTA_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 OTA_ParseHeader (S_EAS_DATA *pEASData, S_OTA_DATA* pData)
{
	EAS_RESULT result;
	EAS_INT i;
	EAS_INT state;
	EAS_U8 temp;
	EAS_U8 titleLen;

	/* initialize some data */
	pData->flags = 0;
	pData->time = 0;
	pData->tick = DEFAULT_TICK_CONV;
	pData->patterns[0].fileOffset = pData->patterns[1].fileOffset =
		pData->patterns[2].fileOffset = pData->patterns[3].fileOffset = -1;
	pData->current.bitCount = 0;
	pData->current.patternLen = 0;
	pData->loopCount = 0;
	pData->restore.fileOffset = -1;
	pData->note = 0;
	pData->restTicks = 0;
	pData->velocity = OTA_VEL_DEFAULT;
	pData->style = 0;
	pData->octave = 59;

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

	/* read the first byte, should be command length */
	if ((result = EAS_HWGetByte(pEASData->hwInstData, pData->fileHandle, &temp)) != EAS_SUCCESS)
		return result;

	/* read all the commands */
	i = temp;
	state = 0;
	while (i--)
	{

		/* fetch command, always starts on byte boundary */
		pData->current.bitCount = 0;
		if ((result = OTA_FetchBitField(pEASData->hwInstData, pData, 7, &temp)) != EAS_SUCCESS)
			return result;

		if (state == 0)
		{
			if (temp != OTA_RINGTONE)
			{
				{ /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "Expected OTA Ring Tone Programming type\n"); */ }
				return EAS_ERROR_FILE_FORMAT;
			}
			state++;
		}
		else
		{
	
			if (temp == OTA_SOUND)
				break;

			if (temp == OTA_UNICODE)
				pData->flags |= OTA_FLAGS_UNICODE;
			else
			{
				{ /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "Expected OTA Sound or Unicode type\n"); */ }
				return EAS_ERROR_FILE_FORMAT;
			}
		}
	}

	/* get song type */
	if ((result = OTA_FetchBitField(pEASData->hwInstData, pData, 3, &temp)) != EAS_SUCCESS)
		return result;

	/* check for basic song type */
	if (temp == OTA_BASIC_SONG_TYPE)
	{
		/* fetch title length */
		if ((result = OTA_FetchBitField(pEASData->hwInstData, pData, 4, &titleLen)) != EAS_SUCCESS)
			return result;

		/* if unicode, double the length */
		if (pData->flags & OTA_FLAGS_UNICODE)
			titleLen = (EAS_U8) (titleLen << 1);

		/* zero the metadata buffer */
		if (pData->metadata.buffer)
			EAS_HWMemSet(pData->metadata.buffer, 0, pData->metadata.bufferSize);
	
		/* read the song title */
		for (i = 0; i < titleLen; i++)
		{
			/* fetch character */
			if ((result = OTA_FetchBitField(pEASData->hwInstData, pData, 8, &temp)) != EAS_SUCCESS)
				return result;
			
			/* check for metadata callback */
			if (pData->metadata.callback)
			{
				if (i < (pData->metadata.bufferSize - 1))
					pData->metadata.buffer[i] = (char) temp;
			}
		}

		/* if host has registered callback, call it now */
		if (pData->metadata.callback)
			(*pData->metadata.callback)(EAS_METADATA_TITLE, pData->metadata.buffer, pData->metadata.pUserData);
	}

	/* must be temporary song */
	else if (temp != OTA_TEMPORARY_SONG_TYPE)
	{
		{ /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "Expected OTA basic or temporary song type\n"); */ }
		return EAS_ERROR_FILE_FORMAT;
	}

	/* get the song length */
	if ((result = OTA_FetchBitField(pEASData->hwInstData, pData, 8, &pData->numPatterns)) != EAS_SUCCESS)
		return result;

	/* sanity check */
	if (pData->numPatterns == 0)
	{
		{ /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "OTA number of patterns is zero\n"); */ }
		return EAS_ERROR_FILE_FORMAT;
	}

	/* at start of first pattern */
	return EAS_SUCCESS;
}

/*----------------------------------------------------------------------------
 * OTA_FetchBitField()
 *----------------------------------------------------------------------------
 * Purpose: 
 * Fetch a specified number of bits from the input stream
 *
 * Inputs:
 * 
 *		
 * Outputs:
 * 
 *
 * Side Effects:
 *
 *----------------------------------------------------------------------------
*/
static EAS_RESULT OTA_FetchBitField (EAS_HW_DATA_HANDLE hwInstData, S_OTA_DATA *pData, EAS_I32 numBits, EAS_U8 *pValue)
{
	EAS_RESULT result;
	EAS_I32 bitsLeft;
	EAS_U8 value;

	value = 0;
	
	/* do we have enough bits? */
	bitsLeft = pData->current.bitCount - numBits;

	/* not enough bits, assemble them from 2 characters */
	if (bitsLeft < 0)
	{
		/* grab the remaining bits from the previous byte */
		if (pData->current.bitCount)
			/*lint -e{504,734} this is a legitimate shift operation */
			value = pData->current.dataByte << -bitsLeft;

		/* read the next byte */
		if ((result = EAS_HWGetByte(hwInstData, pData->fileHandle, &pData->current.dataByte)) != EAS_SUCCESS)
			return result;
		bitsLeft += 8;
	}

	/* more bits than needed? */
	if (bitsLeft > 0)
	{
		value |= pData->current.dataByte >> bitsLeft;
		pData->current.bitCount = (EAS_U8) bitsLeft;
		pData->current.dataByte = pData->current.dataByte & (0xff >> (8 - bitsLeft));
	}

	/* exactly the right number of bits */
	else
	{
		value |= pData->current.dataByte;
		pData->current.bitCount = 0;
	}

	*pValue = value;
	return EAS_SUCCESS;
}

/*----------------------------------------------------------------------------
 * OTA_SavePosition()
 *----------------------------------------------------------------------------
 * Purpose: 
 * 
 *
 * Inputs:
 * 
 *		
 * Outputs:
 * 
 *
 * Side Effects:
 *
 *----------------------------------------------------------------------------
*/
static EAS_RESULT OTA_SavePosition (EAS_HW_DATA_HANDLE hwInstData, S_OTA_DATA *pData, S_OTA_LOC *pLoc)
{
	EAS_HWMemCpy(pLoc, &pData->current, sizeof(S_OTA_LOC));
	return EAS_HWFilePos(hwInstData, pData->fileHandle, &pLoc->fileOffset);
}

/*----------------------------------------------------------------------------
 * OTA_RestorePosition()
 *----------------------------------------------------------------------------
 * Purpose: 
 * 
 *
 * Inputs:
 * 
 *		
 * Outputs:
 * 
 *
 * Side Effects:
 *
 *----------------------------------------------------------------------------
*/
static EAS_RESULT OTA_RestorePosition (EAS_HW_DATA_HANDLE hwInstData, S_OTA_DATA *pData, S_OTA_LOC *pLoc)
{
	EAS_HWMemCpy(&pData->current, pLoc, sizeof(S_OTA_LOC));
	pData->restore.fileOffset = -1;
	return EAS_HWFileSeek(hwInstData, pData->fileHandle, pLoc->fileOffset);
}