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