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