From 56c99cd2c2c1e6ab038dac5fced5b92ccf11ff6c Mon Sep 17 00:00:00 2001 From: Dave Sparks Date: Mon, 24 Aug 2009 17:35:45 -0700 Subject: Sonivox whitespace cleanup --- arm-fm-22k/lib_src/eas_smf.c | 2382 +++++++++++++++++++++--------------------- 1 file changed, 1191 insertions(+), 1191 deletions(-) (limited to 'arm-fm-22k/lib_src/eas_smf.c') 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)); +} + -- cgit v1.2.3