/*---------------------------------------------------------------------------- | |
* | |
* 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; | |
} | |