summaryrefslogtreecommitdiffstats
path: root/arm-fm-22k/lib_src/eas_smf.c
diff options
context:
space:
mode:
Diffstat (limited to 'arm-fm-22k/lib_src/eas_smf.c')
-rw-r--r--arm-fm-22k/lib_src/eas_smf.c2382
1 files changed, 1191 insertions, 1191 deletions
diff --git a/arm-fm-22k/lib_src/eas_smf.c b/arm-fm-22k/lib_src/eas_smf.c
index 7b56e97..e609583 100644
--- a/arm-fm-22k/lib_src/eas_smf.c
+++ b/arm-fm-22k/lib_src/eas_smf.c
@@ -1,14 +1,14 @@
-/*----------------------------------------------------------------------------
- *
- * File:
- * eas_smf.c
- *
- * Contents and purpose:
- * SMF Type 0 and 1 File Parser
- *
- * For SMF timebase analysis, see "MIDI Sequencer Analysis.xls".
- *
- * Copyright Sonic Network Inc. 2005
+/*----------------------------------------------------------------------------
+ *
+ * File:
+ * eas_smf.c
+ *
+ * Contents and purpose:
+ * SMF Type 0 and 1 File Parser
+ *
+ * For SMF timebase analysis, see "MIDI Sequencer Analysis.xls".
+ *
+ * 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.
@@ -21,1183 +21,1183 @@
* 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: 803 $
- * $Date: 2007-08-01 09:57:00 -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_smfdata.h"
-#include "eas_smf.h"
-
-#ifdef JET_INTERFACE
-#include "jet_data.h"
-#endif
-
-//3 dls: The timebase for this module is adequate to keep MIDI and
-//3 digital audio synchronized for only a few minutes. It should be
-//3 sufficient for most mobile applications. If better accuracy is
-//3 required, more fractional bits should be added to the timebase.
-
-static const EAS_U8 smfHeader[] = { 'M', 'T', 'h', 'd' };
-
-/* local prototypes */
-static EAS_RESULT SMF_GetVarLenData (EAS_HW_DATA_HANDLE hwInstData, EAS_FILE_HANDLE fileHandle, EAS_U32 *pData);
-static EAS_RESULT SMF_ParseMetaEvent (S_EAS_DATA *pEASData, S_SMF_DATA *pSMFData, S_SMF_STREAM *pSMFStream);
-static EAS_RESULT SMF_ParseSysEx (S_EAS_DATA *pEASData, S_SMF_DATA *pSMFData, S_SMF_STREAM *pSMFStream, EAS_U8 f0, EAS_INT parserMode);
-static EAS_RESULT SMF_ParseEvent (S_EAS_DATA *pEASData, S_SMF_DATA *pSMFData, S_SMF_STREAM *pSMFStream, EAS_INT parserMode);
-static EAS_RESULT SMF_GetDeltaTime (EAS_HW_DATA_HANDLE hwInstData, S_SMF_STREAM *pSMFStream);
-static void SMF_UpdateTime (S_SMF_DATA *pSMFData, EAS_U32 ticks);
-
-
-/*----------------------------------------------------------------------------
- *
- * SMF_Parser
- *
- * This structure contains the functional interface for the SMF parser
- *----------------------------------------------------------------------------
-*/
-const S_FILE_PARSER_INTERFACE EAS_SMF_Parser =
-{
- SMF_CheckFileType,
- SMF_Prepare,
- SMF_Time,
- SMF_Event,
- SMF_State,
- SMF_Close,
- SMF_Reset,
- SMF_Pause,
- SMF_Resume,
- NULL,
- SMF_SetData,
- SMF_GetData,
- NULL
-};
-
-/*----------------------------------------------------------------------------
- * SMF_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:
- *
- *----------------------------------------------------------------------------
-*/
-EAS_RESULT SMF_CheckFileType (S_EAS_DATA *pEASData, EAS_FILE_HANDLE fileHandle, EAS_VOID_PTR *ppHandle, EAS_I32 offset)
-{
- S_SMF_DATA* pSMFData;
- EAS_RESULT result;
-
- /* seek to starting offset - usually 0 */
- *ppHandle = NULL;
- if ((result = EAS_HWFileSeek(pEASData->hwInstData, fileHandle, offset)) != EAS_SUCCESS)
- return result;
-
- /* search through file for header - slow method */
- if (pEASData->searchHeaderFlag)
- {
- result = EAS_SearchFile(pEASData, fileHandle, smfHeader, sizeof(smfHeader), &offset);
- if (result != EAS_SUCCESS)
- return (result == EAS_EOF) ? EAS_SUCCESS : result;
- }
-
- /* read the first 4 bytes of the file - quick method */
- else {
- EAS_U8 header[4];
- EAS_I32 count;
- if ((result = EAS_HWReadFile(pEASData->hwInstData, fileHandle, header, sizeof(header), &count)) != EAS_SUCCESS)
- return result;
-
- /* check for 'MTrk' - return if no match */
- if ((header[0] != 'M') || (header[1] != 'T') || (header[2] != 'h') || (header[3] != 'd'))
- return EAS_SUCCESS;
- }
-
- /* check for static memory allocation */
- if (pEASData->staticMemoryModel)
- pSMFData = EAS_CMEnumData(EAS_CM_SMF_DATA);
- else
- {
- pSMFData = EAS_HWMalloc(pEASData->hwInstData, sizeof(S_SMF_DATA));
- EAS_HWMemSet((void *)pSMFData,0, sizeof(S_SMF_DATA));
- }
- if (!pSMFData)
- return EAS_ERROR_MALLOC_FAILED;
-
- /* initialize some critical data */
- pSMFData->fileHandle = fileHandle;
- pSMFData->fileOffset = offset;
- pSMFData->pSynth = NULL;
- pSMFData->time = 0;
- pSMFData->state = EAS_STATE_OPEN;
- *ppHandle = pSMFData;
-
- return EAS_SUCCESS;
-}
-
-/*----------------------------------------------------------------------------
- * SMF_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:
- *
- *----------------------------------------------------------------------------
-*/
-EAS_RESULT SMF_Prepare (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
-{
- S_SMF_DATA* pSMFData;
- EAS_RESULT result;
-
- /* check for valid state */
- pSMFData = (S_SMF_DATA *) pInstData;
- if (pSMFData->state != EAS_STATE_OPEN)
- return EAS_ERROR_NOT_VALID_IN_THIS_STATE;
-
- /* instantiate a synthesizer */
- if ((result = VMInitMIDI(pEASData, &pSMFData->pSynth)) != EAS_SUCCESS)
- {
- { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "VMInitMIDI returned %d\n", result); */ }
- return result;
- }
-
- /* parse the file header and setup the individual stream parsers */
- if ((result = SMF_ParseHeader(pEASData->hwInstData, pSMFData)) != EAS_SUCCESS)
- return result;
-
- /* ready to play */
- pSMFData->state = EAS_STATE_READY;
- return EAS_SUCCESS;
-}
-
-/*----------------------------------------------------------------------------
- * SMF_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 */
-EAS_RESULT SMF_Time (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_U32 *pTime)
-{
- S_SMF_DATA *pSMFData;
-
- pSMFData = (S_SMF_DATA*) pInstData;
-
- /* sanity check */
-#ifdef _CHECKED_BUILD
- if (pSMFData->state == EAS_STATE_STOPPED)
- {
- { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "Can't ask for time on a stopped stream\n"); */ }
- }
-
- if (pSMFData->nextStream == NULL)
- {
- { /* dpp: EAS_ReportEx( _EAS_SEVERITY_ERROR, "no is NULL\n"); */ }
- }
-#endif
-
-#if 0
- /* return time in milliseconds */
- /* if chase mode, lie about time */
- if (pSMFData->flags & SMF_FLAGS_CHASE_MODE)
- *pTime = 0;
-
- else
-#endif
-
- /*lint -e{704} use shift instead of division */
- *pTime = pSMFData->time >> 8;
-
- *pTime = pSMFData->time >> 8;
- return EAS_SUCCESS;
-}
-
-/*----------------------------------------------------------------------------
- * SMF_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:
- *
- *----------------------------------------------------------------------------
-*/
-EAS_RESULT SMF_Event (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_INT parserMode)
-{
- S_SMF_DATA* pSMFData;
- EAS_RESULT result;
- EAS_I32 i;
- EAS_U32 ticks;
- EAS_U32 temp;
-
- /* establish pointer to instance data */
- pSMFData = (S_SMF_DATA*) pInstData;
- if (pSMFData->state >= EAS_STATE_OPEN)
- return EAS_SUCCESS;
-
- /* get current ticks */
- ticks = pSMFData->nextStream->ticks;
-
- /* assume that an error occurred */
- pSMFData->state = EAS_STATE_ERROR;
-
-#ifdef JET_INTERFACE
- /* if JET has track muted, set parser mode to mute */
- if (pSMFData->nextStream->midiStream.jetData & MIDI_FLAGS_JET_MUTE)
- parserMode = eParserModeMute;
-#endif
-
- /* parse the next event from all the streams */
- if ((result = SMF_ParseEvent(pEASData, pSMFData, pSMFData->nextStream, parserMode)) != EAS_SUCCESS)
- {
- /* check for unexpected end-of-file */
- if (result != EAS_EOF)
- return result;
-
- /* indicate end of track for this stream */
- pSMFData->nextStream->ticks = SMF_END_OF_TRACK;
- }
-
- /* get next delta time, unless already at end of track */
- else if (pSMFData->nextStream->ticks != SMF_END_OF_TRACK)
- {
- if ((result = SMF_GetDeltaTime(pEASData->hwInstData, pSMFData->nextStream)) != EAS_SUCCESS)
- {
- /* check for unexpected end-of-file */
- if (result != EAS_EOF)
- return result;
-
- /* indicate end of track for this stream */
- pSMFData->nextStream->ticks = SMF_END_OF_TRACK;
- }
-
- /* if zero delta to next event, stay with this stream */
- else if (pSMFData->nextStream->ticks == ticks)
- {
- pSMFData->state = EAS_STATE_PLAY;
- return EAS_SUCCESS;
- }
- }
-
- /* find next event in all streams */
- temp = 0x7ffffff;
- pSMFData->nextStream = NULL;
- for (i = 0; i < pSMFData->numStreams; i++)
- {
- if (pSMFData->streams[i].ticks < temp)
- {
- temp = pSMFData->streams[i].ticks;
- pSMFData->nextStream = &pSMFData->streams[i];
- }
- }
-
- /* are there any more events to parse? */
- if (pSMFData->nextStream)
- {
- pSMFData->state = EAS_STATE_PLAY;
-
- /* update the time of the next event */
- SMF_UpdateTime(pSMFData, pSMFData->nextStream->ticks - ticks);
- }
- else
- {
- pSMFData->state = EAS_STATE_STOPPING;
- VMReleaseAllVoices(pEASData->pVoiceMgr, pSMFData->pSynth);
- }
-
- return EAS_SUCCESS;
-}
-
-/*----------------------------------------------------------------------------
- * SMF_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 */
-EAS_RESULT SMF_State (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 *pState)
-{
- S_SMF_DATA* pSMFData;
-
- /* establish pointer to instance data */
- pSMFData = (S_SMF_DATA*) pInstData;
-
- /* if stopping, check to see if synth voices are active */
- if (pSMFData->state == EAS_STATE_STOPPING)
- {
- if (VMActiveVoices(pSMFData->pSynth) == 0)
- pSMFData->state = EAS_STATE_STOPPED;
- }
-
- if (pSMFData->state == EAS_STATE_PAUSING)
- {
- if (VMActiveVoices(pSMFData->pSynth) == 0)
- pSMFData->state = EAS_STATE_PAUSED;
- }
-
- /* return current state */
- *pState = pSMFData->state;
- return EAS_SUCCESS;
-}
-
-/*----------------------------------------------------------------------------
- * SMF_Close()
- *----------------------------------------------------------------------------
- * Purpose:
- * Close the file and clean up
- *
- * Inputs:
- * pEASData - pointer to overall EAS data structure
- * handle - pointer to file handle
- *
- * Outputs:
- *
- *
- * Side Effects:
- *
- *----------------------------------------------------------------------------
-*/
-EAS_RESULT SMF_Close (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
-{
- S_SMF_DATA* pSMFData;
- EAS_I32 i;
- EAS_RESULT result;
-
- pSMFData = (S_SMF_DATA*) pInstData;
-
- /* close all the streams */
- for (i = 0; i < pSMFData->numStreams; i++)
- {
- if (pSMFData->streams[i].fileHandle != NULL)
- {
- if ((result = EAS_HWCloseFile(pEASData->hwInstData, pSMFData->streams[i].fileHandle)) != EAS_SUCCESS)
- return result;
- }
- }
- if (pSMFData->fileHandle != NULL)
- if ((result = EAS_HWCloseFile(pEASData->hwInstData, pSMFData->fileHandle)) != EAS_SUCCESS)
- return result;
-
- /* free the synth */
- if (pSMFData->pSynth != NULL)
- VMMIDIShutdown(pEASData, pSMFData->pSynth);
-
- /* if using dynamic memory, free it */
- if (!pEASData->staticMemoryModel)
- {
- if (pSMFData->streams)
- EAS_HWFree(pEASData->hwInstData, pSMFData->streams);
-
- /* free the instance data */
- EAS_HWFree(pEASData->hwInstData, pSMFData);
- }
-
- return EAS_SUCCESS;
-}
-
-/*----------------------------------------------------------------------------
- * SMF_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:
- *
- *----------------------------------------------------------------------------
-*/
-EAS_RESULT SMF_Reset (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
-{
- S_SMF_DATA* pSMFData;
- EAS_I32 i;
- EAS_RESULT result;
- EAS_U32 ticks;
-
- pSMFData = (S_SMF_DATA*) pInstData;
-
- /* reset time to zero */
- pSMFData->time = 0;
-
- /* reset the synth */
- VMReset(pEASData->pVoiceMgr, pSMFData->pSynth, EAS_TRUE);
-
- /* find the start of each track */
- ticks = 0x7fffffffL;
- pSMFData->nextStream = NULL;
- for (i = 0; i < pSMFData->numStreams; i++)
- {
-
- /* reset file position to first byte of data in track */
- if ((result = EAS_HWFileSeek(pEASData->hwInstData, pSMFData->streams[i].fileHandle, pSMFData->streams[i].startFilePos)) != EAS_SUCCESS)
- return result;
-
- /* initalize some data */
- pSMFData->streams[i].ticks = 0;
-
- /* initalize the MIDI parser data */
- EAS_InitMIDIStream(&pSMFData->streams[i].midiStream);
-
- /* parse the first delta time in each stream */
- if ((result = SMF_GetDeltaTime(pEASData->hwInstData,&pSMFData->streams[i])) != EAS_SUCCESS)
- return result;
- if (pSMFData->streams[i].ticks < ticks)
- {
- ticks = pSMFData->streams[i].ticks;
- pSMFData->nextStream = &pSMFData->streams[i];
- }
- }
-
-
- pSMFData->state = EAS_STATE_READY;
- return EAS_SUCCESS;
-}
-
-/*----------------------------------------------------------------------------
- * SMF_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:
- *
- *----------------------------------------------------------------------------
-*/
-EAS_RESULT SMF_Pause (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
-{
- S_SMF_DATA *pSMFData;
-
- /* can't pause a stopped stream */
- pSMFData = (S_SMF_DATA*) pInstData;
- if (pSMFData->state == EAS_STATE_STOPPED)
- return EAS_ERROR_ALREADY_STOPPED;
-
- /* mute the synthesizer */
- VMMuteAllVoices(pEASData->pVoiceMgr, pSMFData->pSynth);
- pSMFData->state = EAS_STATE_PAUSING;
- return EAS_SUCCESS;
-}
-
-/*----------------------------------------------------------------------------
- * SMF_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 */
-EAS_RESULT SMF_Resume (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
-{
- S_SMF_DATA *pSMFData;
-
- /* can't resume a stopped stream */
- pSMFData = (S_SMF_DATA*) pInstData;
- if (pSMFData->state == EAS_STATE_STOPPED)
- return EAS_ERROR_ALREADY_STOPPED;
-
- /* nothing to do but resume playback */
- pSMFData->state = EAS_STATE_PLAY;
- return EAS_SUCCESS;
-}
-
-/*----------------------------------------------------------------------------
- * SMF_SetData()
- *----------------------------------------------------------------------------
- * Purpose:
- * Sets parser parameters
- *
- * Inputs:
- * pEASData - pointer to overall EAS data structure
- * handle - pointer to file handle
- *
- * Outputs:
- *
- *
- * Side Effects:
- *
- *----------------------------------------------------------------------------
-*/
-/*lint -esym(715, pEASData) reserved for future use */
-EAS_RESULT SMF_SetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 value)
-{
- S_SMF_DATA *pSMFData;
-
- pSMFData = (S_SMF_DATA*) pInstData;
- switch (param)
- {
-
- /* set metadata callback */
- case PARSER_DATA_METADATA_CB:
- EAS_HWMemCpy(&pSMFData->metadata, (void*) value, sizeof(S_METADATA_CB));
- break;
-
-#ifdef JET_INTERFACE
- /* set jet segment and track ID of all tracks for callback function */
- case PARSER_DATA_JET_CB:
- {
- EAS_U32 i;
- EAS_U32 bit = (EAS_U32) value;
- bit = (bit << JET_EVENT_SEG_SHIFT) & JET_EVENT_SEG_MASK;
- for (i = 0; i < pSMFData->numStreams; i++)
- pSMFData->streams[i].midiStream.jetData =
- (pSMFData->streams[i].midiStream.jetData &
- ~(JET_EVENT_TRACK_MASK | JET_EVENT_SEG_MASK)) |
- i << JET_EVENT_TRACK_SHIFT | bit | MIDI_FLAGS_JET_CB;
- pSMFData->flags |= SMF_FLAGS_JET_STREAM;
- }
- break;
-
- /* set state of all mute flags at once */
- case PARSER_DATA_MUTE_FLAGS:
- {
- EAS_INT i;
- EAS_U32 bit = (EAS_U32) value;
- for (i = 0; i < pSMFData->numStreams; i++)
- {
- if (bit & 1)
- pSMFData->streams[i].midiStream.jetData |= MIDI_FLAGS_JET_MUTE;
- else
- pSMFData->streams[i].midiStream.jetData &= ~MIDI_FLAGS_JET_MUTE;
- bit >>= 1;
- }
- }
- break;
-
- /* set track mute */
- case PARSER_DATA_SET_MUTE:
- if (value < pSMFData->numStreams)
- pSMFData->streams[value].midiStream.jetData |= MIDI_FLAGS_JET_MUTE;
- else
- return EAS_ERROR_PARAMETER_RANGE;
- break;
-
- /* clear track mute */
- case PARSER_DATA_CLEAR_MUTE:
- if (value < pSMFData->numStreams)
- pSMFData->streams[value].midiStream.jetData &= ~MIDI_FLAGS_JET_MUTE;
- else
- return EAS_ERROR_PARAMETER_RANGE;
- break;
-#endif
-
- default:
- return EAS_ERROR_INVALID_PARAMETER;
- }
-
- return EAS_SUCCESS;
-}
-
-/*----------------------------------------------------------------------------
- * SMF_GetData()
- *----------------------------------------------------------------------------
- * Purpose:
- * Retrieves parser parameters
- *
- * Inputs:
- * pEASData - pointer to overall EAS data structure
- * handle - pointer to file handle
- *
- * Outputs:
- *
- *
- * Side Effects:
- *
- *----------------------------------------------------------------------------
-*/
-/*lint -esym(715, pEASData) reserved for future use */
-EAS_RESULT SMF_GetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 *pValue)
-{
- S_SMF_DATA *pSMFData;
-
- pSMFData = (S_SMF_DATA*) pInstData;
- switch (param)
- {
- /* return file type */
- case PARSER_DATA_FILE_TYPE:
- if (pSMFData->numStreams == 1)
- *pValue = EAS_FILE_SMF0;
- else
- *pValue = EAS_FILE_SMF1;
- break;
-
-/* now handled in eas_public.c */
-#if 0
- case PARSER_DATA_POLYPHONY:
- if (pSMFData->pSynth)
- VMGetPolyphony(pEASData->pVoiceMgr, pSMFData->pSynth, pValue);
- else
- return EAS_ERROR_NOT_VALID_IN_THIS_STATE;
- break;
-
- case PARSER_DATA_PRIORITY:
- if (pSMFData->pSynth)
- VMGetPriority(pEASData->pVoiceMgr, pSMFData->pSynth, pValue);
- break;
-
- /* set transposition */
- case PARSER_DATA_TRANSPOSITION:
- *pValue = pSMFData->transposition;
- break;
-#endif
-
- case PARSER_DATA_SYNTH_HANDLE:
- *pValue = (EAS_I32) pSMFData->pSynth;
- break;
-
- default:
- return EAS_ERROR_INVALID_PARAMETER;
- }
-
- return EAS_SUCCESS;
-}
-
-/*----------------------------------------------------------------------------
- * SMF_GetVarLenData()
- *----------------------------------------------------------------------------
- * Purpose:
- * Reads a varible length quantity from an SMF file
- *
- * Inputs:
- *
- *
- * Outputs:
- *
- *
- * Side Effects:
- *
- *----------------------------------------------------------------------------
-*/
-static EAS_RESULT SMF_GetVarLenData (EAS_HW_DATA_HANDLE hwInstData, EAS_FILE_HANDLE fileHandle, EAS_U32 *pData)
-{
- EAS_RESULT result;
- EAS_U32 data;
- EAS_U8 c;
-
- /* read until bit 7 is zero */
- data = 0;
- do
- {
- if ((result = EAS_HWGetByte(hwInstData, fileHandle,&c)) != EAS_SUCCESS)
- return result;
- data = (data << 7) | (c & 0x7f);
- } while (c & 0x80);
- *pData = data;
- return EAS_SUCCESS;
-}
-
-/*----------------------------------------------------------------------------
- * SMF_GetDeltaTime()
- *----------------------------------------------------------------------------
- * Purpose:
- * Reads a varible length quantity from an SMF file
- *
- * Inputs:
- *
- *
- * Outputs:
- *
- *
- * Side Effects:
- *
- *----------------------------------------------------------------------------
-*/
-static EAS_RESULT SMF_GetDeltaTime (EAS_HW_DATA_HANDLE hwInstData, S_SMF_STREAM *pSMFStream)
-{
- EAS_RESULT result;
- EAS_U32 ticks;
-
- if ((result = SMF_GetVarLenData(hwInstData, pSMFStream->fileHandle, &ticks)) != EAS_SUCCESS)
- return result;
-
- pSMFStream->ticks += ticks;
- return EAS_SUCCESS;
-}
-
-/*----------------------------------------------------------------------------
- * SMF_ParseMetaEvent()
- *----------------------------------------------------------------------------
- * Purpose:
- * Reads a varible length quantity from an SMF file
- *
- * Inputs:
- *
- *
- * Outputs:
- *
- *
- * Side Effects:
- *
- *----------------------------------------------------------------------------
-*/
-static EAS_RESULT SMF_ParseMetaEvent (S_EAS_DATA *pEASData, S_SMF_DATA *pSMFData, S_SMF_STREAM *pSMFStream)
-{
- EAS_RESULT result;
- EAS_U32 len;
- EAS_I32 pos;
- EAS_U32 temp;
- EAS_U8 c;
-
- /* get the meta-event type */
- if ((result = EAS_HWGetByte(pEASData->hwInstData, pSMFStream->fileHandle, &c)) != EAS_SUCCESS)
- return result;
-
- /* get the length */
- if ((result = SMF_GetVarLenData(pEASData->hwInstData, pSMFStream->fileHandle, &len)) != EAS_SUCCESS)
- return result;
-
- /* get the current file position so we can skip the event */
- if ((result = EAS_HWFilePos(pEASData->hwInstData, pSMFStream->fileHandle, &pos)) != EAS_SUCCESS)
- return result;
- pos += (EAS_I32) len;
-
- /* end of track? */
- if (c == SMF_META_END_OF_TRACK)
- {
- { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "Meta-event: end of track\n", c, len); */ }
- pSMFStream->ticks = SMF_END_OF_TRACK;
- }
-
- /* tempo event? */
- else if (c == SMF_META_TEMPO)
- {
- /* read the 3-byte timebase value */
- temp = 0;
- while (len--)
- {
- if ((result = EAS_HWGetByte(pEASData->hwInstData, pSMFStream->fileHandle, &c)) != EAS_SUCCESS)
- return result;
- temp = (temp << 8) | c;
- }
-
- pSMFData->tickConv = (EAS_U16) (((temp * 1024) / pSMFData->ppqn + 500) / 1000);
- pSMFData->flags |= SMF_FLAGS_HAS_TEMPO;
- }
-
- /* check for time signature - see iMelody spec V1.4 section 4.1.2.2.3.6 */
- else if (c == SMF_META_TIME_SIGNATURE)
- {
- pSMFData->flags |= SMF_FLAGS_HAS_TIME_SIG;
- }
-
- /* if the host has registered a metadata callback return the metadata */
- else if (pSMFData->metadata.callback)
- {
- EAS_I32 readLen;
- E_EAS_METADATA_TYPE metaType;
-
- metaType = EAS_METADATA_UNKNOWN;
-
- /* only process title on the first track */
- if (c == SMF_META_SEQTRK_NAME)
- metaType = EAS_METADATA_TITLE;
- else if (c == SMF_META_TEXT)
- metaType = EAS_METADATA_TEXT;
- else if (c == SMF_META_COPYRIGHT)
- metaType = EAS_METADATA_COPYRIGHT;
- else if (c == SMF_META_LYRIC)
- metaType = EAS_METADATA_LYRIC;
-
- if (metaType != EAS_METADATA_UNKNOWN)
- {
- readLen = pSMFData->metadata.bufferSize - 1;
- if ((EAS_I32) len < readLen)
- readLen = (EAS_I32) len;
- if ((result = EAS_HWReadFile(pEASData->hwInstData, pSMFStream->fileHandle, pSMFData->metadata.buffer, readLen, &readLen)) != EAS_SUCCESS)
- return result;
- pSMFData->metadata.buffer[readLen] = 0;
- pSMFData->metadata.callback(metaType, pSMFData->metadata.buffer, pSMFData->metadata.pUserData);
- }
- }
-
- /* position file to next event - in case we ignored all or part of the meta-event */
- if ((result = EAS_HWFileSeek(pEASData->hwInstData, pSMFStream->fileHandle, pos)) != EAS_SUCCESS)
- return result;
-
- { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "Meta-event: type=%02x, len=%d\n", c, len); */ }
- return EAS_SUCCESS;
-}
-
-/*----------------------------------------------------------------------------
- * SMF_ParseSysEx()
- *----------------------------------------------------------------------------
- * Purpose:
- * Reads a varible length quantity from an SMF file
- *
- * Inputs:
- *
- *
- * Outputs:
- *
- *
- * Side Effects:
- *
- *----------------------------------------------------------------------------
-*/
-static EAS_RESULT SMF_ParseSysEx (S_EAS_DATA *pEASData, S_SMF_DATA *pSMFData, S_SMF_STREAM *pSMFStream, EAS_U8 f0, EAS_INT parserMode)
-{
- EAS_RESULT result;
- EAS_U32 len;
- EAS_U8 c;
-
- /* get the length */
- if ((result = SMF_GetVarLenData(pEASData->hwInstData, pSMFStream->fileHandle, &len)) != EAS_SUCCESS)
- return result;
-
- /* start of SysEx message? */
- if (f0 == 0xf0)
- {
- if ((result = EAS_ParseMIDIStream(pEASData, pSMFData->pSynth, &pSMFStream->midiStream, f0, parserMode)) != EAS_SUCCESS)
- return result;
- }
-
- /* feed the SysEx to the stream parser */
- while (len--)
- {
- if ((result = EAS_HWGetByte(pEASData->hwInstData, pSMFStream->fileHandle, &c)) != EAS_SUCCESS)
- return result;
- if ((result = EAS_ParseMIDIStream(pEASData, pSMFData->pSynth, &pSMFStream->midiStream, c, parserMode)) != EAS_SUCCESS)
- return result;
-
- /* check for GM system ON */
- if (pSMFStream->midiStream.flags & MIDI_FLAG_GM_ON)
- pSMFData->flags |= SMF_FLAGS_HAS_GM_ON;
- }
-
- return EAS_SUCCESS;
-}
-
-/*----------------------------------------------------------------------------
- * SMF_ParseEvent()
- *----------------------------------------------------------------------------
- * Purpose:
- * Reads a varible length quantity from an SMF file
- *
- * Inputs:
- *
- *
- * Outputs:
- *
- *
- * Side Effects:
- *
- *----------------------------------------------------------------------------
-*/
-static EAS_RESULT SMF_ParseEvent (S_EAS_DATA *pEASData, S_SMF_DATA *pSMFData, S_SMF_STREAM *pSMFStream, EAS_INT parserMode)
-{
- EAS_RESULT result;
- EAS_U8 c;
-
- /* get the event type */
- if ((result = EAS_HWGetByte(pEASData->hwInstData, pSMFStream->fileHandle, &c)) != EAS_SUCCESS)
- return result;
-
- /* parse meta-event */
- if (c == 0xff)
- {
- if ((result = SMF_ParseMetaEvent(pEASData, pSMFData, pSMFStream)) != EAS_SUCCESS)
- return result;
- }
-
- /* parse SysEx */
- else if ((c == 0xf0) || (c == 0xf7))
- {
- if ((result = SMF_ParseSysEx(pEASData, pSMFData, pSMFStream, c, parserMode)) != EAS_SUCCESS)
- return result;
- }
-
- /* parse MIDI message */
- else
- {
- if ((result = EAS_ParseMIDIStream(pEASData, pSMFData->pSynth, &pSMFStream->midiStream, c, parserMode)) != EAS_SUCCESS)
- return result;
-
- /* keep streaming data to the MIDI parser until the message is complete */
- while (pSMFStream->midiStream.pending)
- {
- if ((result = EAS_HWGetByte(pEASData->hwInstData, pSMFStream->fileHandle, &c)) != EAS_SUCCESS)
- return result;
- if ((result = EAS_ParseMIDIStream(pEASData, pSMFData->pSynth, &pSMFStream->midiStream, c, parserMode)) != EAS_SUCCESS)
- return result;
- }
-
- }
-
- /* chase mode logic */
- if (pSMFData->time == 0)
- {
- if (pSMFData->flags & SMF_FLAGS_CHASE_MODE)
- {
- if (pSMFStream->midiStream.flags & MIDI_FLAG_FIRST_NOTE)
- pSMFData->flags &= ~SMF_FLAGS_CHASE_MODE;
- }
- else if ((pSMFData->flags & SMF_FLAGS_SETUP_BAR) == SMF_FLAGS_SETUP_BAR)
- pSMFData->flags = (pSMFData->flags & ~SMF_FLAGS_SETUP_BAR) | SMF_FLAGS_CHASE_MODE;
- }
-
- return EAS_SUCCESS;
-}
-
-/*----------------------------------------------------------------------------
- * SMF_ParseHeader()
- *----------------------------------------------------------------------------
- * Purpose:
- * Parses the header of an SMF file, allocates memory the stream parsers and initializes the
- * stream parsers.
- *
- * Inputs:
- * pEASData - pointer to overall EAS data structure
- * pSMFData - pointer to parser instance data
- * fileHandle - file handle
- * fileOffset - offset in the file where the header data starts, usually 0
- *
- *
- * Outputs:
- *
- *
- * Side Effects:
- *
- *----------------------------------------------------------------------------
-*/
-/*lint -e{801} we know that 'goto' is deprecated - but it's cleaner in this case */
-EAS_RESULT SMF_ParseHeader (EAS_HW_DATA_HANDLE hwInstData, S_SMF_DATA *pSMFData)
-{
- EAS_RESULT result;
- EAS_I32 i;
- EAS_U16 division;
- EAS_U32 chunkSize;
- EAS_U32 chunkStart;
- EAS_U32 temp;
- EAS_U32 ticks;
-
- /* rewind the file and find the end of the header chunk */
- if ((result = EAS_HWFileSeek(hwInstData, pSMFData->fileHandle, pSMFData->fileOffset + SMF_OFS_HEADER_SIZE)) != EAS_SUCCESS)
- goto ReadError;
- if ((result = EAS_HWGetDWord(hwInstData, pSMFData->fileHandle, &chunkSize, EAS_TRUE)) != EAS_SUCCESS)
- goto ReadError;
-
- /* determine the number of tracks */
- if ((result = EAS_HWFileSeek(hwInstData, pSMFData->fileHandle, pSMFData->fileOffset + SMF_OFS_NUM_TRACKS)) != EAS_SUCCESS)
- goto ReadError;
- if ((result = EAS_HWGetWord(hwInstData, pSMFData->fileHandle, &pSMFData->numStreams, EAS_TRUE)) != EAS_SUCCESS)
- goto ReadError;
-
- /* limit the number of tracks */
- if (pSMFData->numStreams > MAX_SMF_STREAMS)
- {
- { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "SMF file contains %u tracks, playing %d tracks\n", pSMFData->numStreams, MAX_SMF_STREAMS); */ }
- pSMFData->numStreams = MAX_SMF_STREAMS;
- }
-
- /* get the time division */
- if ((result = EAS_HWGetWord(hwInstData, pSMFData->fileHandle, &division, EAS_TRUE)) != EAS_SUCCESS)
- goto ReadError;
-
- /* setup default timebase for 120 bpm */
- pSMFData->ppqn = 192;
- if (division & 0x8000)
- { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING,"No support for SMPTE code timebase\n"); */ }
- else
- pSMFData->ppqn = (division & 0x7fff);
- pSMFData->tickConv = (EAS_U16) (((SMF_DEFAULT_TIMEBASE * 1024) / pSMFData->ppqn + 500) / 1000);
-
- /* dynamic memory allocation, allocate memory for streams */
- if (pSMFData->streams == NULL)
- {
- pSMFData->streams = EAS_HWMalloc(hwInstData,sizeof(S_SMF_STREAM) * pSMFData->numStreams);
- if (pSMFData->streams == NULL)
- return EAS_ERROR_MALLOC_FAILED;
-
- /* zero the memory to insure complete initialization */
- EAS_HWMemSet((void *)(pSMFData->streams), 0, sizeof(S_SMF_STREAM) * pSMFData->numStreams);
- }
-
- /* find the start of each track */
- chunkStart = (EAS_U32) pSMFData->fileOffset;
- ticks = 0x7fffffffL;
- pSMFData->nextStream = NULL;
- for (i = 0; i < pSMFData->numStreams; i++)
- {
-
- for (;;)
- {
-
- /* calculate start of next chunk - checking for errors */
- temp = chunkStart + SMF_CHUNK_INFO_SIZE + chunkSize;
- if (temp <= chunkStart)
- {
- { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING,"Error in chunk size at offset %d\n", chunkStart); */ }
- return EAS_ERROR_FILE_FORMAT;
- }
- chunkStart = temp;
-
- /* seek to the start of the next chunk */
- if ((result = EAS_HWFileSeek(hwInstData, pSMFData->fileHandle, (EAS_I32) chunkStart)) != EAS_SUCCESS)
- goto ReadError;
-
- /* read the chunk identifier */
- if ((result = EAS_HWGetDWord(hwInstData, pSMFData->fileHandle, &temp, EAS_TRUE)) != EAS_SUCCESS)
- goto ReadError;
-
- /* read the chunk size */
- if ((result = EAS_HWGetDWord(hwInstData, pSMFData->fileHandle, &chunkSize, EAS_TRUE)) != EAS_SUCCESS)
- goto ReadError;
-
- /* make sure this is an 'MTrk' chunk */
- if (temp == SMF_CHUNK_TYPE_TRACK)
- break;
-
- { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING,"Unexpected chunk type: 0x%08x\n", temp); */ }
- }
-
- /* initalize some data */
- pSMFData->streams[i].ticks = 0;
- pSMFData->streams[i].fileHandle = pSMFData->fileHandle;
-
- /* NULL the file handle so we don't try to close it twice */
- pSMFData->fileHandle = NULL;
-
- /* save this file position as the start of the track */
- pSMFData->streams[i].startFilePos = (EAS_I32) chunkStart + SMF_CHUNK_INFO_SIZE;
-
- /* initalize the MIDI parser data */
- EAS_InitMIDIStream(&pSMFData->streams[i].midiStream);
-
- /* parse the first delta time in each stream */
- if ((result = SMF_GetDeltaTime(hwInstData, &pSMFData->streams[i])) != EAS_SUCCESS)
- goto ReadError;
-
- if (pSMFData->streams[i].ticks < ticks)
- {
- ticks = pSMFData->streams[i].ticks;
- pSMFData->nextStream = &pSMFData->streams[i];
- }
-
- /* more tracks to do, create a duplicate file handle */
- if (i < (pSMFData->numStreams - 1))
- {
- if ((result = EAS_HWDupHandle(hwInstData, pSMFData->streams[i].fileHandle, &pSMFData->fileHandle)) != EAS_SUCCESS)
- goto ReadError;
- }
- }
-
- /* update the time of the next event */
- if (pSMFData->nextStream)
- SMF_UpdateTime(pSMFData, pSMFData->nextStream->ticks);
-
- return EAS_SUCCESS;
-
- /* ugly goto: but simpler than structured */
- ReadError:
- if (result == EAS_EOF)
- return EAS_ERROR_FILE_FORMAT;
- return result;
-}
-
-/*----------------------------------------------------------------------------
- * SMF_UpdateTime()
- *----------------------------------------------------------------------------
- * Purpose:
- * Update the millisecond time base by converting the ticks into millieconds
- *
- * Inputs:
- *
- *
- * Outputs:
- *
- *
- * Side Effects:
- *
- *----------------------------------------------------------------------------
-*/
-static void SMF_UpdateTime (S_SMF_DATA *pSMFData, EAS_U32 ticks)
-{
- EAS_U32 temp1, temp2;
-
- if (pSMFData->flags & SMF_FLAGS_CHASE_MODE)
- return;
-
- temp1 = (ticks >> 10) * pSMFData->tickConv;
- temp2 = (ticks & 0x3ff) * pSMFData->tickConv;
- pSMFData->time += (EAS_I32)((temp1 << 8) + (temp2 >> 2));
-}
-
+ *
+ *----------------------------------------------------------------------------
+ * Revision Control:
+ * $Revision: 803 $
+ * $Date: 2007-08-01 09:57:00 -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_smfdata.h"
+#include "eas_smf.h"
+
+#ifdef JET_INTERFACE
+#include "jet_data.h"
+#endif
+
+//3 dls: The timebase for this module is adequate to keep MIDI and
+//3 digital audio synchronized for only a few minutes. It should be
+//3 sufficient for most mobile applications. If better accuracy is
+//3 required, more fractional bits should be added to the timebase.
+
+static const EAS_U8 smfHeader[] = { 'M', 'T', 'h', 'd' };
+
+/* local prototypes */
+static EAS_RESULT SMF_GetVarLenData (EAS_HW_DATA_HANDLE hwInstData, EAS_FILE_HANDLE fileHandle, EAS_U32 *pData);
+static EAS_RESULT SMF_ParseMetaEvent (S_EAS_DATA *pEASData, S_SMF_DATA *pSMFData, S_SMF_STREAM *pSMFStream);
+static EAS_RESULT SMF_ParseSysEx (S_EAS_DATA *pEASData, S_SMF_DATA *pSMFData, S_SMF_STREAM *pSMFStream, EAS_U8 f0, EAS_INT parserMode);
+static EAS_RESULT SMF_ParseEvent (S_EAS_DATA *pEASData, S_SMF_DATA *pSMFData, S_SMF_STREAM *pSMFStream, EAS_INT parserMode);
+static EAS_RESULT SMF_GetDeltaTime (EAS_HW_DATA_HANDLE hwInstData, S_SMF_STREAM *pSMFStream);
+static void SMF_UpdateTime (S_SMF_DATA *pSMFData, EAS_U32 ticks);
+
+
+/*----------------------------------------------------------------------------
+ *
+ * SMF_Parser
+ *
+ * This structure contains the functional interface for the SMF parser
+ *----------------------------------------------------------------------------
+*/
+const S_FILE_PARSER_INTERFACE EAS_SMF_Parser =
+{
+ SMF_CheckFileType,
+ SMF_Prepare,
+ SMF_Time,
+ SMF_Event,
+ SMF_State,
+ SMF_Close,
+ SMF_Reset,
+ SMF_Pause,
+ SMF_Resume,
+ NULL,
+ SMF_SetData,
+ SMF_GetData,
+ NULL
+};
+
+/*----------------------------------------------------------------------------
+ * SMF_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:
+ *
+ *----------------------------------------------------------------------------
+*/
+EAS_RESULT SMF_CheckFileType (S_EAS_DATA *pEASData, EAS_FILE_HANDLE fileHandle, EAS_VOID_PTR *ppHandle, EAS_I32 offset)
+{
+ S_SMF_DATA* pSMFData;
+ EAS_RESULT result;
+
+ /* seek to starting offset - usually 0 */
+ *ppHandle = NULL;
+ if ((result = EAS_HWFileSeek(pEASData->hwInstData, fileHandle, offset)) != EAS_SUCCESS)
+ return result;
+
+ /* search through file for header - slow method */
+ if (pEASData->searchHeaderFlag)
+ {
+ result = EAS_SearchFile(pEASData, fileHandle, smfHeader, sizeof(smfHeader), &offset);
+ if (result != EAS_SUCCESS)
+ return (result == EAS_EOF) ? EAS_SUCCESS : result;
+ }
+
+ /* read the first 4 bytes of the file - quick method */
+ else {
+ EAS_U8 header[4];
+ EAS_I32 count;
+ if ((result = EAS_HWReadFile(pEASData->hwInstData, fileHandle, header, sizeof(header), &count)) != EAS_SUCCESS)
+ return result;
+
+ /* check for 'MTrk' - return if no match */
+ if ((header[0] != 'M') || (header[1] != 'T') || (header[2] != 'h') || (header[3] != 'd'))
+ return EAS_SUCCESS;
+ }
+
+ /* check for static memory allocation */
+ if (pEASData->staticMemoryModel)
+ pSMFData = EAS_CMEnumData(EAS_CM_SMF_DATA);
+ else
+ {
+ pSMFData = EAS_HWMalloc(pEASData->hwInstData, sizeof(S_SMF_DATA));
+ EAS_HWMemSet((void *)pSMFData,0, sizeof(S_SMF_DATA));
+ }
+ if (!pSMFData)
+ return EAS_ERROR_MALLOC_FAILED;
+
+ /* initialize some critical data */
+ pSMFData->fileHandle = fileHandle;
+ pSMFData->fileOffset = offset;
+ pSMFData->pSynth = NULL;
+ pSMFData->time = 0;
+ pSMFData->state = EAS_STATE_OPEN;
+ *ppHandle = pSMFData;
+
+ return EAS_SUCCESS;
+}
+
+/*----------------------------------------------------------------------------
+ * SMF_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:
+ *
+ *----------------------------------------------------------------------------
+*/
+EAS_RESULT SMF_Prepare (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
+{
+ S_SMF_DATA* pSMFData;
+ EAS_RESULT result;
+
+ /* check for valid state */
+ pSMFData = (S_SMF_DATA *) pInstData;
+ if (pSMFData->state != EAS_STATE_OPEN)
+ return EAS_ERROR_NOT_VALID_IN_THIS_STATE;
+
+ /* instantiate a synthesizer */
+ if ((result = VMInitMIDI(pEASData, &pSMFData->pSynth)) != EAS_SUCCESS)
+ {
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "VMInitMIDI returned %d\n", result); */ }
+ return result;
+ }
+
+ /* parse the file header and setup the individual stream parsers */
+ if ((result = SMF_ParseHeader(pEASData->hwInstData, pSMFData)) != EAS_SUCCESS)
+ return result;
+
+ /* ready to play */
+ pSMFData->state = EAS_STATE_READY;
+ return EAS_SUCCESS;
+}
+
+/*----------------------------------------------------------------------------
+ * SMF_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 */
+EAS_RESULT SMF_Time (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_U32 *pTime)
+{
+ S_SMF_DATA *pSMFData;
+
+ pSMFData = (S_SMF_DATA*) pInstData;
+
+ /* sanity check */
+#ifdef _CHECKED_BUILD
+ if (pSMFData->state == EAS_STATE_STOPPED)
+ {
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "Can't ask for time on a stopped stream\n"); */ }
+ }
+
+ if (pSMFData->nextStream == NULL)
+ {
+ { /* dpp: EAS_ReportEx( _EAS_SEVERITY_ERROR, "no is NULL\n"); */ }
+ }
+#endif
+
+#if 0
+ /* return time in milliseconds */
+ /* if chase mode, lie about time */
+ if (pSMFData->flags & SMF_FLAGS_CHASE_MODE)
+ *pTime = 0;
+
+ else
+#endif
+
+ /*lint -e{704} use shift instead of division */
+ *pTime = pSMFData->time >> 8;
+
+ *pTime = pSMFData->time >> 8;
+ return EAS_SUCCESS;
+}
+
+/*----------------------------------------------------------------------------
+ * SMF_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:
+ *
+ *----------------------------------------------------------------------------
+*/
+EAS_RESULT SMF_Event (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_INT parserMode)
+{
+ S_SMF_DATA* pSMFData;
+ EAS_RESULT result;
+ EAS_I32 i;
+ EAS_U32 ticks;
+ EAS_U32 temp;
+
+ /* establish pointer to instance data */
+ pSMFData = (S_SMF_DATA*) pInstData;
+ if (pSMFData->state >= EAS_STATE_OPEN)
+ return EAS_SUCCESS;
+
+ /* get current ticks */
+ ticks = pSMFData->nextStream->ticks;
+
+ /* assume that an error occurred */
+ pSMFData->state = EAS_STATE_ERROR;
+
+#ifdef JET_INTERFACE
+ /* if JET has track muted, set parser mode to mute */
+ if (pSMFData->nextStream->midiStream.jetData & MIDI_FLAGS_JET_MUTE)
+ parserMode = eParserModeMute;
+#endif
+
+ /* parse the next event from all the streams */
+ if ((result = SMF_ParseEvent(pEASData, pSMFData, pSMFData->nextStream, parserMode)) != EAS_SUCCESS)
+ {
+ /* check for unexpected end-of-file */
+ if (result != EAS_EOF)
+ return result;
+
+ /* indicate end of track for this stream */
+ pSMFData->nextStream->ticks = SMF_END_OF_TRACK;
+ }
+
+ /* get next delta time, unless already at end of track */
+ else if (pSMFData->nextStream->ticks != SMF_END_OF_TRACK)
+ {
+ if ((result = SMF_GetDeltaTime(pEASData->hwInstData, pSMFData->nextStream)) != EAS_SUCCESS)
+ {
+ /* check for unexpected end-of-file */
+ if (result != EAS_EOF)
+ return result;
+
+ /* indicate end of track for this stream */
+ pSMFData->nextStream->ticks = SMF_END_OF_TRACK;
+ }
+
+ /* if zero delta to next event, stay with this stream */
+ else if (pSMFData->nextStream->ticks == ticks)
+ {
+ pSMFData->state = EAS_STATE_PLAY;
+ return EAS_SUCCESS;
+ }
+ }
+
+ /* find next event in all streams */
+ temp = 0x7ffffff;
+ pSMFData->nextStream = NULL;
+ for (i = 0; i < pSMFData->numStreams; i++)
+ {
+ if (pSMFData->streams[i].ticks < temp)
+ {
+ temp = pSMFData->streams[i].ticks;
+ pSMFData->nextStream = &pSMFData->streams[i];
+ }
+ }
+
+ /* are there any more events to parse? */
+ if (pSMFData->nextStream)
+ {
+ pSMFData->state = EAS_STATE_PLAY;
+
+ /* update the time of the next event */
+ SMF_UpdateTime(pSMFData, pSMFData->nextStream->ticks - ticks);
+ }
+ else
+ {
+ pSMFData->state = EAS_STATE_STOPPING;
+ VMReleaseAllVoices(pEASData->pVoiceMgr, pSMFData->pSynth);
+ }
+
+ return EAS_SUCCESS;
+}
+
+/*----------------------------------------------------------------------------
+ * SMF_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 */
+EAS_RESULT SMF_State (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 *pState)
+{
+ S_SMF_DATA* pSMFData;
+
+ /* establish pointer to instance data */
+ pSMFData = (S_SMF_DATA*) pInstData;
+
+ /* if stopping, check to see if synth voices are active */
+ if (pSMFData->state == EAS_STATE_STOPPING)
+ {
+ if (VMActiveVoices(pSMFData->pSynth) == 0)
+ pSMFData->state = EAS_STATE_STOPPED;
+ }
+
+ if (pSMFData->state == EAS_STATE_PAUSING)
+ {
+ if (VMActiveVoices(pSMFData->pSynth) == 0)
+ pSMFData->state = EAS_STATE_PAUSED;
+ }
+
+ /* return current state */
+ *pState = pSMFData->state;
+ return EAS_SUCCESS;
+}
+
+/*----------------------------------------------------------------------------
+ * SMF_Close()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * Close the file and clean up
+ *
+ * Inputs:
+ * pEASData - pointer to overall EAS data structure
+ * handle - pointer to file handle
+ *
+ * Outputs:
+ *
+ *
+ * Side Effects:
+ *
+ *----------------------------------------------------------------------------
+*/
+EAS_RESULT SMF_Close (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
+{
+ S_SMF_DATA* pSMFData;
+ EAS_I32 i;
+ EAS_RESULT result;
+
+ pSMFData = (S_SMF_DATA*) pInstData;
+
+ /* close all the streams */
+ for (i = 0; i < pSMFData->numStreams; i++)
+ {
+ if (pSMFData->streams[i].fileHandle != NULL)
+ {
+ if ((result = EAS_HWCloseFile(pEASData->hwInstData, pSMFData->streams[i].fileHandle)) != EAS_SUCCESS)
+ return result;
+ }
+ }
+ if (pSMFData->fileHandle != NULL)
+ if ((result = EAS_HWCloseFile(pEASData->hwInstData, pSMFData->fileHandle)) != EAS_SUCCESS)
+ return result;
+
+ /* free the synth */
+ if (pSMFData->pSynth != NULL)
+ VMMIDIShutdown(pEASData, pSMFData->pSynth);
+
+ /* if using dynamic memory, free it */
+ if (!pEASData->staticMemoryModel)
+ {
+ if (pSMFData->streams)
+ EAS_HWFree(pEASData->hwInstData, pSMFData->streams);
+
+ /* free the instance data */
+ EAS_HWFree(pEASData->hwInstData, pSMFData);
+ }
+
+ return EAS_SUCCESS;
+}
+
+/*----------------------------------------------------------------------------
+ * SMF_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:
+ *
+ *----------------------------------------------------------------------------
+*/
+EAS_RESULT SMF_Reset (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
+{
+ S_SMF_DATA* pSMFData;
+ EAS_I32 i;
+ EAS_RESULT result;
+ EAS_U32 ticks;
+
+ pSMFData = (S_SMF_DATA*) pInstData;
+
+ /* reset time to zero */
+ pSMFData->time = 0;
+
+ /* reset the synth */
+ VMReset(pEASData->pVoiceMgr, pSMFData->pSynth, EAS_TRUE);
+
+ /* find the start of each track */
+ ticks = 0x7fffffffL;
+ pSMFData->nextStream = NULL;
+ for (i = 0; i < pSMFData->numStreams; i++)
+ {
+
+ /* reset file position to first byte of data in track */
+ if ((result = EAS_HWFileSeek(pEASData->hwInstData, pSMFData->streams[i].fileHandle, pSMFData->streams[i].startFilePos)) != EAS_SUCCESS)
+ return result;
+
+ /* initalize some data */
+ pSMFData->streams[i].ticks = 0;
+
+ /* initalize the MIDI parser data */
+ EAS_InitMIDIStream(&pSMFData->streams[i].midiStream);
+
+ /* parse the first delta time in each stream */
+ if ((result = SMF_GetDeltaTime(pEASData->hwInstData,&pSMFData->streams[i])) != EAS_SUCCESS)
+ return result;
+ if (pSMFData->streams[i].ticks < ticks)
+ {
+ ticks = pSMFData->streams[i].ticks;
+ pSMFData->nextStream = &pSMFData->streams[i];
+ }
+ }
+
+
+ pSMFData->state = EAS_STATE_READY;
+ return EAS_SUCCESS;
+}
+
+/*----------------------------------------------------------------------------
+ * SMF_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:
+ *
+ *----------------------------------------------------------------------------
+*/
+EAS_RESULT SMF_Pause (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
+{
+ S_SMF_DATA *pSMFData;
+
+ /* can't pause a stopped stream */
+ pSMFData = (S_SMF_DATA*) pInstData;
+ if (pSMFData->state == EAS_STATE_STOPPED)
+ return EAS_ERROR_ALREADY_STOPPED;
+
+ /* mute the synthesizer */
+ VMMuteAllVoices(pEASData->pVoiceMgr, pSMFData->pSynth);
+ pSMFData->state = EAS_STATE_PAUSING;
+ return EAS_SUCCESS;
+}
+
+/*----------------------------------------------------------------------------
+ * SMF_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 */
+EAS_RESULT SMF_Resume (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
+{
+ S_SMF_DATA *pSMFData;
+
+ /* can't resume a stopped stream */
+ pSMFData = (S_SMF_DATA*) pInstData;
+ if (pSMFData->state == EAS_STATE_STOPPED)
+ return EAS_ERROR_ALREADY_STOPPED;
+
+ /* nothing to do but resume playback */
+ pSMFData->state = EAS_STATE_PLAY;
+ return EAS_SUCCESS;
+}
+
+/*----------------------------------------------------------------------------
+ * SMF_SetData()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * Sets parser parameters
+ *
+ * Inputs:
+ * pEASData - pointer to overall EAS data structure
+ * handle - pointer to file handle
+ *
+ * Outputs:
+ *
+ *
+ * Side Effects:
+ *
+ *----------------------------------------------------------------------------
+*/
+/*lint -esym(715, pEASData) reserved for future use */
+EAS_RESULT SMF_SetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 value)
+{
+ S_SMF_DATA *pSMFData;
+
+ pSMFData = (S_SMF_DATA*) pInstData;
+ switch (param)
+ {
+
+ /* set metadata callback */
+ case PARSER_DATA_METADATA_CB:
+ EAS_HWMemCpy(&pSMFData->metadata, (void*) value, sizeof(S_METADATA_CB));
+ break;
+
+#ifdef JET_INTERFACE
+ /* set jet segment and track ID of all tracks for callback function */
+ case PARSER_DATA_JET_CB:
+ {
+ EAS_U32 i;
+ EAS_U32 bit = (EAS_U32) value;
+ bit = (bit << JET_EVENT_SEG_SHIFT) & JET_EVENT_SEG_MASK;
+ for (i = 0; i < pSMFData->numStreams; i++)
+ pSMFData->streams[i].midiStream.jetData =
+ (pSMFData->streams[i].midiStream.jetData &
+ ~(JET_EVENT_TRACK_MASK | JET_EVENT_SEG_MASK)) |
+ i << JET_EVENT_TRACK_SHIFT | bit | MIDI_FLAGS_JET_CB;
+ pSMFData->flags |= SMF_FLAGS_JET_STREAM;
+ }
+ break;
+
+ /* set state of all mute flags at once */
+ case PARSER_DATA_MUTE_FLAGS:
+ {
+ EAS_INT i;
+ EAS_U32 bit = (EAS_U32) value;
+ for (i = 0; i < pSMFData->numStreams; i++)
+ {
+ if (bit & 1)
+ pSMFData->streams[i].midiStream.jetData |= MIDI_FLAGS_JET_MUTE;
+ else
+ pSMFData->streams[i].midiStream.jetData &= ~MIDI_FLAGS_JET_MUTE;
+ bit >>= 1;
+ }
+ }
+ break;
+
+ /* set track mute */
+ case PARSER_DATA_SET_MUTE:
+ if (value < pSMFData->numStreams)
+ pSMFData->streams[value].midiStream.jetData |= MIDI_FLAGS_JET_MUTE;
+ else
+ return EAS_ERROR_PARAMETER_RANGE;
+ break;
+
+ /* clear track mute */
+ case PARSER_DATA_CLEAR_MUTE:
+ if (value < pSMFData->numStreams)
+ pSMFData->streams[value].midiStream.jetData &= ~MIDI_FLAGS_JET_MUTE;
+ else
+ return EAS_ERROR_PARAMETER_RANGE;
+ break;
+#endif
+
+ default:
+ return EAS_ERROR_INVALID_PARAMETER;
+ }
+
+ return EAS_SUCCESS;
+}
+
+/*----------------------------------------------------------------------------
+ * SMF_GetData()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * Retrieves parser parameters
+ *
+ * Inputs:
+ * pEASData - pointer to overall EAS data structure
+ * handle - pointer to file handle
+ *
+ * Outputs:
+ *
+ *
+ * Side Effects:
+ *
+ *----------------------------------------------------------------------------
+*/
+/*lint -esym(715, pEASData) reserved for future use */
+EAS_RESULT SMF_GetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 *pValue)
+{
+ S_SMF_DATA *pSMFData;
+
+ pSMFData = (S_SMF_DATA*) pInstData;
+ switch (param)
+ {
+ /* return file type */
+ case PARSER_DATA_FILE_TYPE:
+ if (pSMFData->numStreams == 1)
+ *pValue = EAS_FILE_SMF0;
+ else
+ *pValue = EAS_FILE_SMF1;
+ break;
+
+/* now handled in eas_public.c */
+#if 0
+ case PARSER_DATA_POLYPHONY:
+ if (pSMFData->pSynth)
+ VMGetPolyphony(pEASData->pVoiceMgr, pSMFData->pSynth, pValue);
+ else
+ return EAS_ERROR_NOT_VALID_IN_THIS_STATE;
+ break;
+
+ case PARSER_DATA_PRIORITY:
+ if (pSMFData->pSynth)
+ VMGetPriority(pEASData->pVoiceMgr, pSMFData->pSynth, pValue);
+ break;
+
+ /* set transposition */
+ case PARSER_DATA_TRANSPOSITION:
+ *pValue = pSMFData->transposition;
+ break;
+#endif
+
+ case PARSER_DATA_SYNTH_HANDLE:
+ *pValue = (EAS_I32) pSMFData->pSynth;
+ break;
+
+ default:
+ return EAS_ERROR_INVALID_PARAMETER;
+ }
+
+ return EAS_SUCCESS;
+}
+
+/*----------------------------------------------------------------------------
+ * SMF_GetVarLenData()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * Reads a varible length quantity from an SMF file
+ *
+ * Inputs:
+ *
+ *
+ * Outputs:
+ *
+ *
+ * Side Effects:
+ *
+ *----------------------------------------------------------------------------
+*/
+static EAS_RESULT SMF_GetVarLenData (EAS_HW_DATA_HANDLE hwInstData, EAS_FILE_HANDLE fileHandle, EAS_U32 *pData)
+{
+ EAS_RESULT result;
+ EAS_U32 data;
+ EAS_U8 c;
+
+ /* read until bit 7 is zero */
+ data = 0;
+ do
+ {
+ if ((result = EAS_HWGetByte(hwInstData, fileHandle,&c)) != EAS_SUCCESS)
+ return result;
+ data = (data << 7) | (c & 0x7f);
+ } while (c & 0x80);
+ *pData = data;
+ return EAS_SUCCESS;
+}
+
+/*----------------------------------------------------------------------------
+ * SMF_GetDeltaTime()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * Reads a varible length quantity from an SMF file
+ *
+ * Inputs:
+ *
+ *
+ * Outputs:
+ *
+ *
+ * Side Effects:
+ *
+ *----------------------------------------------------------------------------
+*/
+static EAS_RESULT SMF_GetDeltaTime (EAS_HW_DATA_HANDLE hwInstData, S_SMF_STREAM *pSMFStream)
+{
+ EAS_RESULT result;
+ EAS_U32 ticks;
+
+ if ((result = SMF_GetVarLenData(hwInstData, pSMFStream->fileHandle, &ticks)) != EAS_SUCCESS)
+ return result;
+
+ pSMFStream->ticks += ticks;
+ return EAS_SUCCESS;
+}
+
+/*----------------------------------------------------------------------------
+ * SMF_ParseMetaEvent()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * Reads a varible length quantity from an SMF file
+ *
+ * Inputs:
+ *
+ *
+ * Outputs:
+ *
+ *
+ * Side Effects:
+ *
+ *----------------------------------------------------------------------------
+*/
+static EAS_RESULT SMF_ParseMetaEvent (S_EAS_DATA *pEASData, S_SMF_DATA *pSMFData, S_SMF_STREAM *pSMFStream)
+{
+ EAS_RESULT result;
+ EAS_U32 len;
+ EAS_I32 pos;
+ EAS_U32 temp;
+ EAS_U8 c;
+
+ /* get the meta-event type */
+ if ((result = EAS_HWGetByte(pEASData->hwInstData, pSMFStream->fileHandle, &c)) != EAS_SUCCESS)
+ return result;
+
+ /* get the length */
+ if ((result = SMF_GetVarLenData(pEASData->hwInstData, pSMFStream->fileHandle, &len)) != EAS_SUCCESS)
+ return result;
+
+ /* get the current file position so we can skip the event */
+ if ((result = EAS_HWFilePos(pEASData->hwInstData, pSMFStream->fileHandle, &pos)) != EAS_SUCCESS)
+ return result;
+ pos += (EAS_I32) len;
+
+ /* end of track? */
+ if (c == SMF_META_END_OF_TRACK)
+ {
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "Meta-event: end of track\n", c, len); */ }
+ pSMFStream->ticks = SMF_END_OF_TRACK;
+ }
+
+ /* tempo event? */
+ else if (c == SMF_META_TEMPO)
+ {
+ /* read the 3-byte timebase value */
+ temp = 0;
+ while (len--)
+ {
+ if ((result = EAS_HWGetByte(pEASData->hwInstData, pSMFStream->fileHandle, &c)) != EAS_SUCCESS)
+ return result;
+ temp = (temp << 8) | c;
+ }
+
+ pSMFData->tickConv = (EAS_U16) (((temp * 1024) / pSMFData->ppqn + 500) / 1000);
+ pSMFData->flags |= SMF_FLAGS_HAS_TEMPO;
+ }
+
+ /* check for time signature - see iMelody spec V1.4 section 4.1.2.2.3.6 */
+ else if (c == SMF_META_TIME_SIGNATURE)
+ {
+ pSMFData->flags |= SMF_FLAGS_HAS_TIME_SIG;
+ }
+
+ /* if the host has registered a metadata callback return the metadata */
+ else if (pSMFData->metadata.callback)
+ {
+ EAS_I32 readLen;
+ E_EAS_METADATA_TYPE metaType;
+
+ metaType = EAS_METADATA_UNKNOWN;
+
+ /* only process title on the first track */
+ if (c == SMF_META_SEQTRK_NAME)
+ metaType = EAS_METADATA_TITLE;
+ else if (c == SMF_META_TEXT)
+ metaType = EAS_METADATA_TEXT;
+ else if (c == SMF_META_COPYRIGHT)
+ metaType = EAS_METADATA_COPYRIGHT;
+ else if (c == SMF_META_LYRIC)
+ metaType = EAS_METADATA_LYRIC;
+
+ if (metaType != EAS_METADATA_UNKNOWN)
+ {
+ readLen = pSMFData->metadata.bufferSize - 1;
+ if ((EAS_I32) len < readLen)
+ readLen = (EAS_I32) len;
+ if ((result = EAS_HWReadFile(pEASData->hwInstData, pSMFStream->fileHandle, pSMFData->metadata.buffer, readLen, &readLen)) != EAS_SUCCESS)
+ return result;
+ pSMFData->metadata.buffer[readLen] = 0;
+ pSMFData->metadata.callback(metaType, pSMFData->metadata.buffer, pSMFData->metadata.pUserData);
+ }
+ }
+
+ /* position file to next event - in case we ignored all or part of the meta-event */
+ if ((result = EAS_HWFileSeek(pEASData->hwInstData, pSMFStream->fileHandle, pos)) != EAS_SUCCESS)
+ return result;
+
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "Meta-event: type=%02x, len=%d\n", c, len); */ }
+ return EAS_SUCCESS;
+}
+
+/*----------------------------------------------------------------------------
+ * SMF_ParseSysEx()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * Reads a varible length quantity from an SMF file
+ *
+ * Inputs:
+ *
+ *
+ * Outputs:
+ *
+ *
+ * Side Effects:
+ *
+ *----------------------------------------------------------------------------
+*/
+static EAS_RESULT SMF_ParseSysEx (S_EAS_DATA *pEASData, S_SMF_DATA *pSMFData, S_SMF_STREAM *pSMFStream, EAS_U8 f0, EAS_INT parserMode)
+{
+ EAS_RESULT result;
+ EAS_U32 len;
+ EAS_U8 c;
+
+ /* get the length */
+ if ((result = SMF_GetVarLenData(pEASData->hwInstData, pSMFStream->fileHandle, &len)) != EAS_SUCCESS)
+ return result;
+
+ /* start of SysEx message? */
+ if (f0 == 0xf0)
+ {
+ if ((result = EAS_ParseMIDIStream(pEASData, pSMFData->pSynth, &pSMFStream->midiStream, f0, parserMode)) != EAS_SUCCESS)
+ return result;
+ }
+
+ /* feed the SysEx to the stream parser */
+ while (len--)
+ {
+ if ((result = EAS_HWGetByte(pEASData->hwInstData, pSMFStream->fileHandle, &c)) != EAS_SUCCESS)
+ return result;
+ if ((result = EAS_ParseMIDIStream(pEASData, pSMFData->pSynth, &pSMFStream->midiStream, c, parserMode)) != EAS_SUCCESS)
+ return result;
+
+ /* check for GM system ON */
+ if (pSMFStream->midiStream.flags & MIDI_FLAG_GM_ON)
+ pSMFData->flags |= SMF_FLAGS_HAS_GM_ON;
+ }
+
+ return EAS_SUCCESS;
+}
+
+/*----------------------------------------------------------------------------
+ * SMF_ParseEvent()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * Reads a varible length quantity from an SMF file
+ *
+ * Inputs:
+ *
+ *
+ * Outputs:
+ *
+ *
+ * Side Effects:
+ *
+ *----------------------------------------------------------------------------
+*/
+static EAS_RESULT SMF_ParseEvent (S_EAS_DATA *pEASData, S_SMF_DATA *pSMFData, S_SMF_STREAM *pSMFStream, EAS_INT parserMode)
+{
+ EAS_RESULT result;
+ EAS_U8 c;
+
+ /* get the event type */
+ if ((result = EAS_HWGetByte(pEASData->hwInstData, pSMFStream->fileHandle, &c)) != EAS_SUCCESS)
+ return result;
+
+ /* parse meta-event */
+ if (c == 0xff)
+ {
+ if ((result = SMF_ParseMetaEvent(pEASData, pSMFData, pSMFStream)) != EAS_SUCCESS)
+ return result;
+ }
+
+ /* parse SysEx */
+ else if ((c == 0xf0) || (c == 0xf7))
+ {
+ if ((result = SMF_ParseSysEx(pEASData, pSMFData, pSMFStream, c, parserMode)) != EAS_SUCCESS)
+ return result;
+ }
+
+ /* parse MIDI message */
+ else
+ {
+ if ((result = EAS_ParseMIDIStream(pEASData, pSMFData->pSynth, &pSMFStream->midiStream, c, parserMode)) != EAS_SUCCESS)
+ return result;
+
+ /* keep streaming data to the MIDI parser until the message is complete */
+ while (pSMFStream->midiStream.pending)
+ {
+ if ((result = EAS_HWGetByte(pEASData->hwInstData, pSMFStream->fileHandle, &c)) != EAS_SUCCESS)
+ return result;
+ if ((result = EAS_ParseMIDIStream(pEASData, pSMFData->pSynth, &pSMFStream->midiStream, c, parserMode)) != EAS_SUCCESS)
+ return result;
+ }
+
+ }
+
+ /* chase mode logic */
+ if (pSMFData->time == 0)
+ {
+ if (pSMFData->flags & SMF_FLAGS_CHASE_MODE)
+ {
+ if (pSMFStream->midiStream.flags & MIDI_FLAG_FIRST_NOTE)
+ pSMFData->flags &= ~SMF_FLAGS_CHASE_MODE;
+ }
+ else if ((pSMFData->flags & SMF_FLAGS_SETUP_BAR) == SMF_FLAGS_SETUP_BAR)
+ pSMFData->flags = (pSMFData->flags & ~SMF_FLAGS_SETUP_BAR) | SMF_FLAGS_CHASE_MODE;
+ }
+
+ return EAS_SUCCESS;
+}
+
+/*----------------------------------------------------------------------------
+ * SMF_ParseHeader()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * Parses the header of an SMF file, allocates memory the stream parsers and initializes the
+ * stream parsers.
+ *
+ * Inputs:
+ * pEASData - pointer to overall EAS data structure
+ * pSMFData - pointer to parser instance data
+ * fileHandle - file handle
+ * fileOffset - offset in the file where the header data starts, usually 0
+ *
+ *
+ * Outputs:
+ *
+ *
+ * Side Effects:
+ *
+ *----------------------------------------------------------------------------
+*/
+/*lint -e{801} we know that 'goto' is deprecated - but it's cleaner in this case */
+EAS_RESULT SMF_ParseHeader (EAS_HW_DATA_HANDLE hwInstData, S_SMF_DATA *pSMFData)
+{
+ EAS_RESULT result;
+ EAS_I32 i;
+ EAS_U16 division;
+ EAS_U32 chunkSize;
+ EAS_U32 chunkStart;
+ EAS_U32 temp;
+ EAS_U32 ticks;
+
+ /* rewind the file and find the end of the header chunk */
+ if ((result = EAS_HWFileSeek(hwInstData, pSMFData->fileHandle, pSMFData->fileOffset + SMF_OFS_HEADER_SIZE)) != EAS_SUCCESS)
+ goto ReadError;
+ if ((result = EAS_HWGetDWord(hwInstData, pSMFData->fileHandle, &chunkSize, EAS_TRUE)) != EAS_SUCCESS)
+ goto ReadError;
+
+ /* determine the number of tracks */
+ if ((result = EAS_HWFileSeek(hwInstData, pSMFData->fileHandle, pSMFData->fileOffset + SMF_OFS_NUM_TRACKS)) != EAS_SUCCESS)
+ goto ReadError;
+ if ((result = EAS_HWGetWord(hwInstData, pSMFData->fileHandle, &pSMFData->numStreams, EAS_TRUE)) != EAS_SUCCESS)
+ goto ReadError;
+
+ /* limit the number of tracks */
+ if (pSMFData->numStreams > MAX_SMF_STREAMS)
+ {
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "SMF file contains %u tracks, playing %d tracks\n", pSMFData->numStreams, MAX_SMF_STREAMS); */ }
+ pSMFData->numStreams = MAX_SMF_STREAMS;
+ }
+
+ /* get the time division */
+ if ((result = EAS_HWGetWord(hwInstData, pSMFData->fileHandle, &division, EAS_TRUE)) != EAS_SUCCESS)
+ goto ReadError;
+
+ /* setup default timebase for 120 bpm */
+ pSMFData->ppqn = 192;
+ if (division & 0x8000)
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING,"No support for SMPTE code timebase\n"); */ }
+ else
+ pSMFData->ppqn = (division & 0x7fff);
+ pSMFData->tickConv = (EAS_U16) (((SMF_DEFAULT_TIMEBASE * 1024) / pSMFData->ppqn + 500) / 1000);
+
+ /* dynamic memory allocation, allocate memory for streams */
+ if (pSMFData->streams == NULL)
+ {
+ pSMFData->streams = EAS_HWMalloc(hwInstData,sizeof(S_SMF_STREAM) * pSMFData->numStreams);
+ if (pSMFData->streams == NULL)
+ return EAS_ERROR_MALLOC_FAILED;
+
+ /* zero the memory to insure complete initialization */
+ EAS_HWMemSet((void *)(pSMFData->streams), 0, sizeof(S_SMF_STREAM) * pSMFData->numStreams);
+ }
+
+ /* find the start of each track */
+ chunkStart = (EAS_U32) pSMFData->fileOffset;
+ ticks = 0x7fffffffL;
+ pSMFData->nextStream = NULL;
+ for (i = 0; i < pSMFData->numStreams; i++)
+ {
+
+ for (;;)
+ {
+
+ /* calculate start of next chunk - checking for errors */
+ temp = chunkStart + SMF_CHUNK_INFO_SIZE + chunkSize;
+ if (temp <= chunkStart)
+ {
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING,"Error in chunk size at offset %d\n", chunkStart); */ }
+ return EAS_ERROR_FILE_FORMAT;
+ }
+ chunkStart = temp;
+
+ /* seek to the start of the next chunk */
+ if ((result = EAS_HWFileSeek(hwInstData, pSMFData->fileHandle, (EAS_I32) chunkStart)) != EAS_SUCCESS)
+ goto ReadError;
+
+ /* read the chunk identifier */
+ if ((result = EAS_HWGetDWord(hwInstData, pSMFData->fileHandle, &temp, EAS_TRUE)) != EAS_SUCCESS)
+ goto ReadError;
+
+ /* read the chunk size */
+ if ((result = EAS_HWGetDWord(hwInstData, pSMFData->fileHandle, &chunkSize, EAS_TRUE)) != EAS_SUCCESS)
+ goto ReadError;
+
+ /* make sure this is an 'MTrk' chunk */
+ if (temp == SMF_CHUNK_TYPE_TRACK)
+ break;
+
+ { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING,"Unexpected chunk type: 0x%08x\n", temp); */ }
+ }
+
+ /* initalize some data */
+ pSMFData->streams[i].ticks = 0;
+ pSMFData->streams[i].fileHandle = pSMFData->fileHandle;
+
+ /* NULL the file handle so we don't try to close it twice */
+ pSMFData->fileHandle = NULL;
+
+ /* save this file position as the start of the track */
+ pSMFData->streams[i].startFilePos = (EAS_I32) chunkStart + SMF_CHUNK_INFO_SIZE;
+
+ /* initalize the MIDI parser data */
+ EAS_InitMIDIStream(&pSMFData->streams[i].midiStream);
+
+ /* parse the first delta time in each stream */
+ if ((result = SMF_GetDeltaTime(hwInstData, &pSMFData->streams[i])) != EAS_SUCCESS)
+ goto ReadError;
+
+ if (pSMFData->streams[i].ticks < ticks)
+ {
+ ticks = pSMFData->streams[i].ticks;
+ pSMFData->nextStream = &pSMFData->streams[i];
+ }
+
+ /* more tracks to do, create a duplicate file handle */
+ if (i < (pSMFData->numStreams - 1))
+ {
+ if ((result = EAS_HWDupHandle(hwInstData, pSMFData->streams[i].fileHandle, &pSMFData->fileHandle)) != EAS_SUCCESS)
+ goto ReadError;
+ }
+ }
+
+ /* update the time of the next event */
+ if (pSMFData->nextStream)
+ SMF_UpdateTime(pSMFData, pSMFData->nextStream->ticks);
+
+ return EAS_SUCCESS;
+
+ /* ugly goto: but simpler than structured */
+ ReadError:
+ if (result == EAS_EOF)
+ return EAS_ERROR_FILE_FORMAT;
+ return result;
+}
+
+/*----------------------------------------------------------------------------
+ * SMF_UpdateTime()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * Update the millisecond time base by converting the ticks into millieconds
+ *
+ * Inputs:
+ *
+ *
+ * Outputs:
+ *
+ *
+ * Side Effects:
+ *
+ *----------------------------------------------------------------------------
+*/
+static void SMF_UpdateTime (S_SMF_DATA *pSMFData, EAS_U32 ticks)
+{
+ EAS_U32 temp1, temp2;
+
+ if (pSMFData->flags & SMF_FLAGS_CHASE_MODE)
+ return;
+
+ temp1 = (ticks >> 10) * pSMFData->tickConv;
+ temp2 = (ticks & 0x3ff) * pSMFData->tickConv;
+ pSMFData->time += (EAS_I32)((temp1 << 8) + (temp2 >> 2));
+}
+