/*---------------------------------------------------------------------------- | |
* | |
* File: | |
* eas_rtttl.c | |
* | |
* Contents and purpose: | |
* RTTTL parser | |
* | |
* 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. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
* | |
*---------------------------------------------------------------------------- | |
* Revision Control: | |
* $Revision: 795 $ | |
* $Date: 2007-08-01 00:14:45 -0700 (Wed, 01 Aug 2007) $ | |
*---------------------------------------------------------------------------- | |
*/ | |
#include "eas_data.h" | |
#include "eas_miditypes.h" | |
#include "eas_parser.h" | |
#include "eas_report.h" | |
#include "eas_host.h" | |
#include "eas_midi.h" | |
#include "eas_config.h" | |
#include "eas_vm_protos.h" | |
#include "eas_rtttldata.h" | |
#include "eas_ctype.h" | |
/* increase gain for mono ringtones */ | |
#define RTTTL_GAIN_OFFSET 8 | |
/* maximum title length including colon separator */ | |
#define RTTTL_MAX_TITLE_LEN 32 | |
#define RTTTL_INFINITE_LOOP 15 | |
/* length of 32nd note in 1/256ths of a msec for 63 BPM tempo */ | |
#define DEFAULT_TICK_CONV 30476 | |
#define TICK_CONVERT 1920000 | |
/* default channel and program for RTTTL playback */ | |
#define RTTTL_CHANNEL 0 | |
#define RTTTL_PROGRAM 80 | |
#define RTTTL_VELOCITY 127 | |
/* note used for rest */ | |
#define RTTTL_REST 1 | |
/* multiplier for fixed point triplet conversion */ | |
#define TRIPLET_MULTIPLIER 683 | |
#define TRIPLET_SHIFT 10 | |
/* local prototypes */ | |
static EAS_RESULT RTTTL_CheckFileType (S_EAS_DATA *pEASData, EAS_FILE_HANDLE fileHandle, EAS_VOID_PTR *ppHandle, EAS_I32 offset); | |
static EAS_RESULT RTTTL_Prepare (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData); | |
static EAS_RESULT RTTTL_Time (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_U32 *pTime); | |
static EAS_RESULT RTTTL_Event (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_INT parserMode); | |
static EAS_RESULT RTTTL_State (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_STATE *pState); | |
static EAS_RESULT RTTTL_Close (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData); | |
static EAS_RESULT RTTTL_Reset (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData); | |
static EAS_RESULT RTTTL_Pause (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData); | |
static EAS_RESULT RTTTL_Resume (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData); | |
static EAS_RESULT RTTTL_SetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 value); | |
static EAS_RESULT RTTTL_GetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 *pValue); | |
static EAS_RESULT RTTTL_GetStyle (EAS_HW_DATA_HANDLE hwInstData, S_RTTTL_DATA *pData); | |
static EAS_RESULT RTTTL_GetDuration (EAS_HW_DATA_HANDLE hwInstData, S_RTTTL_DATA *pData, EAS_I8 *pDuration); | |
static EAS_RESULT RTTTL_GetOctave (EAS_HW_DATA_HANDLE hwInstData, S_RTTTL_DATA *pData, EAS_U8 *pOctave); | |
static EAS_RESULT RTTTL_GetTempo (EAS_HW_DATA_HANDLE hwInstData, S_RTTTL_DATA *pData); | |
static EAS_RESULT RTTTL_GetNumber (EAS_HW_DATA_HANDLE hwInstData, S_RTTTL_DATA *pData, EAS_I32 *pValue); | |
static EAS_RESULT RTTTL_ParseHeader (S_EAS_DATA *pEASData, S_RTTTL_DATA* pData, EAS_BOOL metaData); | |
static EAS_RESULT RTTTL_GetNextChar (EAS_HW_DATA_HANDLE hwInstData, S_RTTTL_DATA *pData, EAS_I8 *pValue); | |
static EAS_RESULT RTTTL_PeekNextChar (EAS_HW_DATA_HANDLE hwInstData, S_RTTTL_DATA *pData, EAS_I8 *pValue); | |
/* inline functions */ | |
EAS_INLINE void RTTTL_PutBackChar (S_RTTTL_DATA *pData, EAS_I8 value) { pData->dataByte = value; } | |
/* lookup table for note values */ | |
static const EAS_U8 noteTable[] = { 21, 23, 12, 14, 16, 17, 19, 23 }; | |
/*---------------------------------------------------------------------------- | |
* | |
* EAS_RTTTL_Parser | |
* | |
* This structure contains the functional interface for the iMelody parser | |
*---------------------------------------------------------------------------- | |
*/ | |
const S_FILE_PARSER_INTERFACE EAS_RTTTL_Parser = | |
{ | |
RTTTL_CheckFileType, | |
RTTTL_Prepare, | |
RTTTL_Time, | |
RTTTL_Event, | |
RTTTL_State, | |
RTTTL_Close, | |
RTTTL_Reset, | |
RTTTL_Pause, | |
RTTTL_Resume, | |
NULL, | |
RTTTL_SetData, | |
RTTTL_GetData, | |
NULL | |
}; | |
/*---------------------------------------------------------------------------- | |
* RTTTL_CheckFileType() | |
*---------------------------------------------------------------------------- | |
* Purpose: | |
* Check the file type to see if we can parse it | |
* | |
* Inputs: | |
* pEASData - pointer to overall EAS data structure | |
* handle - pointer to file handle | |
* | |
* Outputs: | |
* | |
* | |
* Side Effects: | |
* | |
*---------------------------------------------------------------------------- | |
*/ | |
static EAS_RESULT RTTTL_CheckFileType (S_EAS_DATA *pEASData, EAS_FILE_HANDLE fileHandle, EAS_VOID_PTR *ppHandle, EAS_I32 offset) | |
{ | |
S_RTTTL_DATA data; | |
S_RTTTL_DATA *pData; | |
/* see if we can parse the header */ | |
data.fileHandle = fileHandle; | |
data.fileOffset = offset; | |
*ppHandle= NULL; | |
if (RTTTL_ParseHeader (pEASData, &data, EAS_FALSE) == EAS_SUCCESS) | |
{ | |
/* check for static memory allocation */ | |
if (pEASData->staticMemoryModel) | |
pData = EAS_CMEnumData(EAS_CM_RTTTL_DATA); | |
else | |
pData = EAS_HWMalloc(pEASData->hwInstData, sizeof(S_RTTTL_DATA)); | |
if (!pData) | |
return EAS_ERROR_MALLOC_FAILED; | |
EAS_HWMemSet(pData, 0, sizeof(S_RTTTL_DATA)); | |
/* return a pointer to the instance data */ | |
pData->fileHandle = fileHandle; | |
pData->fileOffset = offset; | |
pData->state = EAS_STATE_OPEN; | |
*ppHandle = pData; | |
} | |
return EAS_SUCCESS; | |
} | |
/*---------------------------------------------------------------------------- | |
* RTTTL_Prepare() | |
*---------------------------------------------------------------------------- | |
* Purpose: | |
* Prepare to parse the file. Allocates instance data (or uses static allocation for | |
* static memory model). | |
* | |
* Inputs: | |
* pEASData - pointer to overall EAS data structure | |
* handle - pointer to file handle | |
* | |
* Outputs: | |
* | |
* | |
* Side Effects: | |
* | |
*---------------------------------------------------------------------------- | |
*/ | |
static EAS_RESULT RTTTL_Prepare (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData) | |
{ | |
S_RTTTL_DATA* pData; | |
EAS_RESULT result; | |
/* check for valid state */ | |
pData = (S_RTTTL_DATA*) pInstData; | |
if (pData->state != EAS_STATE_OPEN) | |
return EAS_ERROR_NOT_VALID_IN_THIS_STATE; | |
/* instantiate a synthesizer */ | |
if ((result = VMInitMIDI(pEASData, &pData->pSynth)) != EAS_SUCCESS) | |
{ | |
{ /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "VMInitMIDI returned %d\n", result); */ } | |
return result; | |
} | |
pData->state = EAS_STATE_ERROR; | |
if ((result = RTTTL_ParseHeader (pEASData, pData, (EAS_BOOL) (pData->metadata.callback != NULL))) != EAS_SUCCESS) | |
{ | |
/* if using dynamic memory, free it */ | |
if (!pEASData->staticMemoryModel) | |
EAS_HWFree(pEASData->hwInstData, pData); | |
return result; | |
} | |
pData->state = EAS_STATE_READY; | |
return EAS_SUCCESS; | |
} | |
/*---------------------------------------------------------------------------- | |
* RTTTL_Time() | |
*---------------------------------------------------------------------------- | |
* Purpose: | |
* Returns the time of the next event in msecs | |
* | |
* Inputs: | |
* pEASData - pointer to overall EAS data structure | |
* handle - pointer to file handle | |
* pTime - pointer to variable to hold time of next event (in msecs) | |
* | |
* Outputs: | |
* | |
* | |
* Side Effects: | |
* | |
*---------------------------------------------------------------------------- | |
*/ | |
/*lint -esym(715, pEASData) reserved for future use */ | |
static EAS_RESULT RTTTL_Time (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_U32 *pTime) | |
{ | |
S_RTTTL_DATA *pData; | |
pData = (S_RTTTL_DATA*) pInstData; | |
/* return time in milliseconds */ | |
/*lint -e{704} use shift instead of division */ | |
*pTime = pData->time >> 8; | |
return EAS_SUCCESS; | |
} | |
/*---------------------------------------------------------------------------- | |
* RTTTL_Event() | |
*---------------------------------------------------------------------------- | |
* Purpose: | |
* Parse the next event in the file | |
* | |
* Inputs: | |
* pEASData - pointer to overall EAS data structure | |
* handle - pointer to file handle | |
* | |
* Outputs: | |
* | |
* | |
* Side Effects: | |
* | |
*---------------------------------------------------------------------------- | |
*/ | |
static EAS_RESULT RTTTL_Event (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_INT parserMode) | |
{ | |
S_RTTTL_DATA* pData; | |
EAS_RESULT result; | |
EAS_I32 ticks; | |
EAS_I32 temp; | |
EAS_I8 c; | |
EAS_U8 note; | |
EAS_U8 octave; | |
pData = (S_RTTTL_DATA*) pInstData; | |
if (pData->state >= EAS_STATE_OPEN) | |
return EAS_SUCCESS; | |
/* initialize MIDI channel when the track starts playing */ | |
if (pData->time == 0) | |
{ | |
/* set program to square lead */ | |
VMProgramChange(pEASData->pVoiceMgr, pData->pSynth, RTTTL_CHANNEL, RTTTL_PROGRAM); | |
/* set channel volume to max */ | |
VMControlChange(pEASData->pVoiceMgr, pData->pSynth, RTTTL_CHANNEL, 7, 127); | |
} | |
/* check for end of note */ | |
if (pData->note) | |
{ | |
/* stop the note */ | |
VMStopNote(pEASData->pVoiceMgr, pData->pSynth, RTTTL_CHANNEL, pData->note, 0); | |
pData->note = 0; | |
/* check for rest between notes */ | |
if (pData->restTicks) | |
{ | |
pData->time += pData->restTicks; | |
pData->restTicks = 0; | |
return EAS_SUCCESS; | |
} | |
} | |
/* parse the next event */ | |
octave = pData->octave; | |
note = 0; | |
ticks = pData->duration * pData->tick; | |
for (;;) | |
{ | |
/* get next character */ | |
if ((result = RTTTL_GetNextChar(pEASData->hwInstData, pData, &c)) != EAS_SUCCESS) | |
{ | |
if (result != EAS_EOF) | |
return result; | |
/* end of file, if no notes to process, check for looping */ | |
if (!note) | |
{ | |
/* if no loop set state to stopping */ | |
if (pData->repeatCount == 0) | |
{ | |
pData->state = EAS_STATE_STOPPING; | |
VMReleaseAllVoices(pEASData->pVoiceMgr, pData->pSynth); | |
return EAS_SUCCESS; | |
} | |
/* decrement loop count */ | |
if (pData->repeatCount != RTTTL_INFINITE_LOOP) | |
pData->repeatCount--; | |
/* if locating, ignore infinite loops */ | |
else if (parserMode != eParserModePlay) | |
{ | |
pData->state = EAS_STATE_STOPPING; | |
VMReleaseAllVoices(pEASData->pVoiceMgr, pData->pSynth); | |
return EAS_SUCCESS; | |
} | |
/* loop back to start of notes */ | |
if ((result = EAS_HWFileSeek(pEASData->hwInstData, pData->fileHandle, pData->repeatOffset)) != EAS_SUCCESS) | |
return result; | |
continue; | |
} | |
/* still have a note to process */ | |
else | |
c = ','; | |
} | |
/* bpm */ | |
if (c == 'b') | |
{ | |
/* peek at next character */ | |
if ((result = RTTTL_PeekNextChar(pEASData->hwInstData, pData, &c)) != EAS_SUCCESS) | |
return result; | |
/* if a number, must be octave or tempo */ | |
if (IsDigit(c)) | |
{ | |
if ((result = RTTTL_GetNumber(pEASData->hwInstData, pData, &temp)) != EAS_SUCCESS) | |
return result; | |
/* check for octave first */ | |
if ((temp >= 4) && (temp <= 7)) | |
{ | |
octave = (EAS_U8) temp; | |
} | |
/* check for tempo */ | |
else if ((temp >= 25) && (temp <= 900)) | |
{ | |
pData->tick = TICK_CONVERT / (EAS_U32) temp; | |
} | |
/* don't know what it was */ | |
else | |
return EAS_ERROR_FILE_FORMAT; | |
} | |
/* must be a note */ | |
else | |
{ | |
note = noteTable[1]; | |
} | |
} | |
/* octave */ | |
else if (c == 'o') | |
{ | |
if ((result = RTTTL_GetOctave(pEASData->hwInstData, pData, &pData->octave)) != EAS_SUCCESS) | |
return result; | |
} | |
/* style */ | |
else if (c == 's') | |
{ | |
if ((result = RTTTL_GetStyle(pEASData->hwInstData, pData)) != EAS_SUCCESS) | |
return result; | |
} | |
/* duration or octave */ | |
else if (IsDigit(c)) | |
{ | |
RTTTL_PutBackChar(pData, c); | |
/* duration comes before note */ | |
if (!note) | |
{ | |
if ((result = RTTTL_GetDuration(pEASData->hwInstData, pData, &c)) != EAS_SUCCESS) | |
return result; | |
ticks = c * pData->tick; | |
} | |
/* octave comes after note */ | |
else | |
{ | |
if ((result = RTTTL_GetOctave(pEASData->hwInstData, pData, &octave)) != EAS_SUCCESS) | |
return result; | |
} | |
} | |
/* note or rest */ | |
else if ((c >= 'a') && (c <= 'h')) | |
{ | |
note = noteTable[c - 'a']; | |
} | |
else if (c == 'p') | |
{ | |
note = RTTTL_REST; | |
} | |
/* dotted note */ | |
else if (c == '.') | |
{ | |
/*lint -e{704} shift for performance */ | |
ticks += ticks >> 1; | |
} | |
/* accidental */ | |
else if (c == '#') | |
{ | |
if (note) | |
note++; | |
} | |
/* end of event */ | |
else if ((c == ',') && note) | |
{ | |
/* handle note events */ | |
if (note != RTTTL_REST) | |
{ | |
/* save note and start it */ | |
pData->note = note + octave; | |
if (parserMode == eParserModePlay) | |
VMStartNote(pEASData->pVoiceMgr, pData->pSynth, RTTTL_CHANNEL, pData->note, RTTTL_VELOCITY); | |
/* determine note length */ | |
switch (pData->style) | |
{ | |
/* natural */ | |
case 'n': | |
/*lint -e{704} shift for performance */ | |
pData->restTicks = ticks >> 4; | |
break; | |
/* continuous */ | |
case 'c': | |
pData->restTicks = 0; | |
break; | |
/* staccato */ | |
case 's': | |
/*lint -e{704} shift for performance */ | |
pData->restTicks = ticks >> 1; | |
break; | |
default: | |
{ /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "RTTTL_Event: Unexpected style type %c\n", pData->style); */ } | |
break; | |
} | |
/* next event is at end of this note */ | |
pData->time += ticks - pData->restTicks; | |
} | |
/* rest */ | |
else | |
pData->time += ticks; | |
/* event found, return to caller */ | |
break; | |
} | |
} | |
pData->state = EAS_STATE_PLAY; | |
return EAS_SUCCESS; | |
} | |
/*---------------------------------------------------------------------------- | |
* RTTTL_State() | |
*---------------------------------------------------------------------------- | |
* Purpose: | |
* Returns the current state of the stream | |
* | |
* Inputs: | |
* pEASData - pointer to overall EAS data structure | |
* handle - pointer to file handle | |
* pState - pointer to variable to store state | |
* | |
* Outputs: | |
* | |
* | |
* Side Effects: | |
* | |
*---------------------------------------------------------------------------- | |
*/ | |
/*lint -esym(715, pEASData) reserved for future use */ | |
static EAS_RESULT RTTTL_State (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 *pState) | |
{ | |
S_RTTTL_DATA* pData; | |
/* establish pointer to instance data */ | |
pData = (S_RTTTL_DATA*) pInstData; | |
/* if stopping, check to see if synth voices are active */ | |
if (pData->state == EAS_STATE_STOPPING) | |
{ | |
if (VMActiveVoices(pData->pSynth) == 0) | |
pData->state = EAS_STATE_STOPPED; | |
} | |
if (pData->state == EAS_STATE_PAUSING) | |
{ | |
if (VMActiveVoices(pData->pSynth) == 0) | |
pData->state = EAS_STATE_PAUSED; | |
} | |
/* return current state */ | |
*pState = pData->state; | |
return EAS_SUCCESS; | |
} | |
/*---------------------------------------------------------------------------- | |
* RTTTL_Close() | |
*---------------------------------------------------------------------------- | |
* Purpose: | |
* Close the file and clean up | |
* | |
* Inputs: | |
* pEASData - pointer to overall EAS data structure | |
* handle - pointer to file handle | |
* | |
* Outputs: | |
* | |
* | |
* Side Effects: | |
* | |
*---------------------------------------------------------------------------- | |
*/ | |
static EAS_RESULT RTTTL_Close (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData) | |
{ | |
S_RTTTL_DATA* pData; | |
EAS_RESULT result; | |
pData = (S_RTTTL_DATA*) pInstData; | |
/* close the file */ | |
if ((result = EAS_HWCloseFile(pEASData->hwInstData, pData->fileHandle)) != EAS_SUCCESS) | |
return result; | |
/* free the synth */ | |
if (pData->pSynth != NULL) | |
VMMIDIShutdown(pEASData, pData->pSynth); | |
/* if using dynamic memory, free it */ | |
if (!pEASData->staticMemoryModel) | |
EAS_HWFree(pEASData->hwInstData, pData); | |
return EAS_SUCCESS; | |
} | |
/*---------------------------------------------------------------------------- | |
* RTTTL_Reset() | |
*---------------------------------------------------------------------------- | |
* Purpose: | |
* Reset the sequencer. Used for locating backwards in the file. | |
* | |
* Inputs: | |
* pEASData - pointer to overall EAS data structure | |
* handle - pointer to file handle | |
* | |
* Outputs: | |
* | |
* | |
* Side Effects: | |
* | |
*---------------------------------------------------------------------------- | |
*/ | |
static EAS_RESULT RTTTL_Reset (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData) | |
{ | |
S_RTTTL_DATA* pData; | |
EAS_RESULT result; | |
pData = (S_RTTTL_DATA*) pInstData; | |
/* reset the synth */ | |
VMReset(pEASData->pVoiceMgr, pData->pSynth, EAS_TRUE); | |
/* reset time to zero */ | |
pData->time = 0; | |
pData->note = 0; | |
/* reset file position and re-parse header */ | |
pData->state = EAS_STATE_ERROR; | |
if ((result = EAS_HWFileSeek(pEASData->hwInstData, pData->fileHandle, pData->fileOffset)) != EAS_SUCCESS) | |
return result; | |
if ((result = RTTTL_ParseHeader (pEASData, pData, EAS_TRUE)) != EAS_SUCCESS) | |
return result; | |
pData->state = EAS_STATE_READY; | |
return EAS_SUCCESS; | |
} | |
/*---------------------------------------------------------------------------- | |
* RTTTL_Pause() | |
*---------------------------------------------------------------------------- | |
* Purpose: | |
* Pauses the sequencer. Mutes all voices and sets state to pause. | |
* | |
* Inputs: | |
* pEASData - pointer to overall EAS data structure | |
* handle - pointer to file handle | |
* | |
* Outputs: | |
* | |
* | |
* Side Effects: | |
* | |
*---------------------------------------------------------------------------- | |
*/ | |
static EAS_RESULT RTTTL_Pause (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData) | |
{ | |
S_RTTTL_DATA *pData; | |
/* can't pause a stopped stream */ | |
pData = (S_RTTTL_DATA*) pInstData; | |
if (pData->state == EAS_STATE_STOPPED) | |
return EAS_ERROR_ALREADY_STOPPED; | |
/* mute the synthesizer */ | |
VMMuteAllVoices(pEASData->pVoiceMgr, pData->pSynth); | |
pData->state = EAS_STATE_PAUSING; | |
return EAS_SUCCESS; | |
} | |
/*---------------------------------------------------------------------------- | |
* RTTTL_Resume() | |
*---------------------------------------------------------------------------- | |
* Purpose: | |
* Resume playing after a pause, sets state back to playing. | |
* | |
* Inputs: | |
* pEASData - pointer to overall EAS data structure | |
* handle - pointer to file handle | |
* | |
* Outputs: | |
* | |
* | |
* Side Effects: | |
* | |
*---------------------------------------------------------------------------- | |
*/ | |
/*lint -esym(715, pEASData) reserved for future use */ | |
static EAS_RESULT RTTTL_Resume (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData) | |
{ | |
S_RTTTL_DATA *pData; | |
/* can't resume a stopped stream */ | |
pData = (S_RTTTL_DATA*) pInstData; | |
if (pData->state == EAS_STATE_STOPPED) | |
return EAS_ERROR_ALREADY_STOPPED; | |
/* nothing to do but resume playback */ | |
pData->state = EAS_STATE_PLAY; | |
return EAS_SUCCESS; | |
} | |
/*---------------------------------------------------------------------------- | |
* RTTTL_SetData() | |
*---------------------------------------------------------------------------- | |
* Purpose: | |
* Return file type | |
* | |
* Inputs: | |
* pEASData - pointer to overall EAS data structure | |
* handle - pointer to file handle | |
* | |
* Outputs: | |
* | |
* | |
* Side Effects: | |
* | |
*---------------------------------------------------------------------------- | |
*/ | |
/*lint -esym(715, pEASData) reserved for future use */ | |
static EAS_RESULT RTTTL_SetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 value) | |
{ | |
S_RTTTL_DATA *pData; | |
pData = (S_RTTTL_DATA *) pInstData; | |
switch (param) | |
{ | |
/* set metadata callback */ | |
case PARSER_DATA_METADATA_CB: | |
EAS_HWMemCpy(&pData->metadata, (void*) value, sizeof(S_METADATA_CB)); | |
break; | |
default: | |
return EAS_ERROR_INVALID_PARAMETER; | |
} | |
return EAS_SUCCESS; | |
} | |
/*---------------------------------------------------------------------------- | |
* RTTTL_GetData() | |
*---------------------------------------------------------------------------- | |
* Purpose: | |
* Return file type | |
* | |
* Inputs: | |
* pEASData - pointer to overall EAS data structure | |
* handle - pointer to file handle | |
* | |
* Outputs: | |
* | |
* | |
* Side Effects: | |
* | |
*---------------------------------------------------------------------------- | |
*/ | |
/*lint -esym(715, pEASData) reserved for future use */ | |
static EAS_RESULT RTTTL_GetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 *pValue) | |
{ | |
S_RTTTL_DATA *pData; | |
pData = (S_RTTTL_DATA *) pInstData; | |
switch (param) | |
{ | |
/* return file type as RTTTL */ | |
case PARSER_DATA_FILE_TYPE: | |
*pValue = EAS_FILE_RTTTL; | |
break; | |
#if 0 | |
/* set transposition */ | |
case PARSER_DATA_TRANSPOSITION: | |
*pValue = pData->transposition; | |
break; | |
#endif | |
case PARSER_DATA_SYNTH_HANDLE: | |
*pValue = (EAS_I32) pData->pSynth; | |
break; | |
case PARSER_DATA_GAIN_OFFSET: | |
*pValue = RTTTL_GAIN_OFFSET; | |
break; | |
default: | |
return EAS_ERROR_INVALID_PARAMETER; | |
} | |
return EAS_SUCCESS; | |
} | |
/*---------------------------------------------------------------------------- | |
* RTTTL_GetStyle() | |
*---------------------------------------------------------------------------- | |
* Purpose: | |
* | |
* | |
* Inputs: | |
* | |
* | |
* Outputs: | |
* | |
* | |
* Side Effects: | |
* | |
*---------------------------------------------------------------------------- | |
*/ | |
static EAS_RESULT RTTTL_GetStyle (EAS_HW_DATA_HANDLE hwInstData, S_RTTTL_DATA *pData) | |
{ | |
EAS_RESULT result; | |
EAS_I8 style; | |
/* get style */ | |
if ((result = RTTTL_GetNextChar(hwInstData, pData, &style)) != EAS_SUCCESS) | |
return result; | |
if ((style != 's') && (style != 'n') && (style != 'c')) | |
return EAS_ERROR_FILE_FORMAT; | |
pData->style = style; | |
return EAS_SUCCESS; | |
} | |
/*---------------------------------------------------------------------------- | |
* RTTTL_GetDuration() | |
*---------------------------------------------------------------------------- | |
* Purpose: | |
* | |
* | |
* Inputs: | |
* | |
* | |
* Outputs: | |
* | |
* | |
* Side Effects: | |
* | |
*---------------------------------------------------------------------------- | |
*/ | |
static EAS_RESULT RTTTL_GetDuration (EAS_HW_DATA_HANDLE hwInstData, S_RTTTL_DATA *pData, EAS_I8 *pDuration) | |
{ | |
EAS_RESULT result; | |
EAS_I32 duration; | |
EAS_I8 temp; | |
/* get the duration */ | |
if ((result = RTTTL_GetNumber(hwInstData, pData, &duration)) != EAS_SUCCESS) | |
return result; | |
if ((duration != 1) && (duration != 2) && (duration != 4) && (duration != 8) && (duration != 16) && (duration != 32)) | |
return EAS_ERROR_FILE_FORMAT; | |
temp = 64; | |
while (duration) | |
{ | |
/*lint -e{704} shift for performance */ | |
duration = duration >> 1; | |
/*lint -e{702} use shift for performance */ | |
temp = temp >> 1; | |
} | |
*pDuration = temp; | |
return EAS_SUCCESS; | |
} | |
/*---------------------------------------------------------------------------- | |
* RTTTL_GetOctave() | |
*---------------------------------------------------------------------------- | |
* Purpose: | |
* | |
* | |
* Inputs: | |
* | |
* | |
* Outputs: | |
* | |
* | |
* Side Effects: | |
* | |
*---------------------------------------------------------------------------- | |
*/ | |
static EAS_RESULT RTTTL_GetOctave (EAS_HW_DATA_HANDLE hwInstData, S_RTTTL_DATA *pData, EAS_U8 *pOctave) | |
{ | |
EAS_RESULT result; | |
EAS_I32 octave; | |
/* get the tempo */ | |
if ((result = RTTTL_GetNumber(hwInstData, pData, &octave)) != EAS_SUCCESS) | |
return result; | |
if ((octave < 4) || (octave > 7)) | |
return EAS_ERROR_FILE_FORMAT; | |
*pOctave = (EAS_U8) (octave * 12); | |
return EAS_SUCCESS; | |
} | |
/*---------------------------------------------------------------------------- | |
* RTTTL_GetTempo() | |
*---------------------------------------------------------------------------- | |
* Purpose: | |
* | |
* | |
* Inputs: | |
* | |
* | |
* Outputs: | |
* | |
* | |
* Side Effects: | |
* | |
*---------------------------------------------------------------------------- | |
*/ | |
static EAS_RESULT RTTTL_GetTempo (EAS_HW_DATA_HANDLE hwInstData, S_RTTTL_DATA *pData) | |
{ | |
EAS_RESULT result; | |
EAS_I32 tempo; | |
/* get the tempo */ | |
if ((result = RTTTL_GetNumber(hwInstData, pData, &tempo)) != EAS_SUCCESS) | |
return result; | |
if ((tempo < 25) || (tempo > 900)) | |
return EAS_ERROR_FILE_FORMAT; | |
pData->tick = TICK_CONVERT / (EAS_U32) tempo; | |
return EAS_SUCCESS; | |
} | |
/*---------------------------------------------------------------------------- | |
* RTTTL_GetNumber() | |
*---------------------------------------------------------------------------- | |
* Purpose: | |
* | |
* | |
* Inputs: | |
* | |
* | |
* Outputs: | |
* | |
* | |
* Side Effects: | |
* | |
*---------------------------------------------------------------------------- | |
*/ | |
static EAS_RESULT RTTTL_GetNumber (EAS_HW_DATA_HANDLE hwInstData, S_RTTTL_DATA *pData, EAS_I32 *pValue) | |
{ | |
EAS_RESULT result; | |
EAS_INT temp; | |
EAS_I8 c; | |
*pValue = -1; | |
temp = 0; | |
for (;;) | |
{ | |
if ((result = RTTTL_PeekNextChar(hwInstData, pData, &c)) != EAS_SUCCESS) | |
{ | |
if ((result == EAS_EOF) && (*pValue != -1)) | |
return EAS_SUCCESS; | |
return result; | |
} | |
if (IsDigit(c)) | |
{ | |
pData->dataByte = 0; | |
temp = temp * 10 + c - '0'; | |
*pValue = temp; | |
} | |
else | |
return EAS_SUCCESS; | |
} | |
} | |
/*---------------------------------------------------------------------------- | |
* RTTTL_ParseHeader() | |
*---------------------------------------------------------------------------- | |
* Purpose: | |
* Prepare to parse the file. Allocates instance data (or uses static allocation for | |
* static memory model). | |
* | |
* Inputs: | |
* pEASData - pointer to overall EAS data structure | |
* handle - pointer to file handle | |
* | |
* Outputs: | |
* | |
* | |
* Side Effects: | |
* | |
*---------------------------------------------------------------------------- | |
*/ | |
static EAS_RESULT RTTTL_ParseHeader (S_EAS_DATA *pEASData, S_RTTTL_DATA* pData, EAS_BOOL metaData) | |
{ | |
EAS_RESULT result; | |
EAS_I32 i; | |
EAS_I8 temp; | |
EAS_I8 control; | |
/* initialize some defaults */ | |
pData->time = 0; | |
pData->tick = DEFAULT_TICK_CONV; | |
pData->note = 0; | |
pData->duration = 4; | |
pData ->restTicks = 0; | |
pData->octave = 60; | |
pData->repeatOffset = -1; | |
pData->repeatCount = 0; | |
pData->style = 'n'; | |
pData->dataByte = 0; | |
metaData = metaData && (pData->metadata.buffer != NULL) && (pData->metadata.callback != NULL); | |
/* seek to start of data */ | |
if ((result = EAS_HWFileSeek(pEASData->hwInstData, pData->fileHandle, pData->fileOffset)) != EAS_SUCCESS) | |
return result; | |
/* zero the metadata buffer */ | |
if (metaData) | |
EAS_HWMemSet(pData->metadata.buffer, 0, pData->metadata.bufferSize); | |
/* read the title */ | |
for (i = 0; i < RTTTL_MAX_TITLE_LEN; i++) | |
{ | |
if ((result = EAS_HWGetByte(pEASData->hwInstData, pData->fileHandle, &temp)) != EAS_SUCCESS) | |
return result; | |
if (temp == ':') | |
break; | |
/* pass along metadata */ | |
if (metaData) | |
{ | |
if (i < (pData->metadata.bufferSize- 1)) | |
pData->metadata.buffer[i] = (char) temp; | |
} | |
} | |
/* check for error in title */ | |
if (i == RTTTL_MAX_TITLE_LEN) | |
return EAS_ERROR_FILE_FORMAT; | |
/* pass along metadata */ | |
if (metaData) | |
(*pData->metadata.callback)(EAS_METADATA_TITLE, pData->metadata.buffer, pData->metadata.pUserData); | |
/* control fields */ | |
for (;;) | |
{ | |
/* get control type */ | |
if ((result = RTTTL_GetNextChar(pEASData->hwInstData, pData, &control)) != EAS_SUCCESS) | |
return result; | |
/* next char should be equal sign */ | |
if ((result = RTTTL_GetNextChar(pEASData->hwInstData, pData, &temp)) != EAS_SUCCESS) | |
return result; | |
if (temp != '=') | |
return EAS_ERROR_FILE_FORMAT; | |
/* get the control value */ | |
switch (control) | |
{ | |
/* bpm */ | |
case 'b': | |
if ((result = RTTTL_GetTempo(pEASData->hwInstData, pData)) != EAS_SUCCESS) | |
return result; | |
break; | |
/* duration */ | |
case 'd': | |
if ((result = RTTTL_GetDuration(pEASData->hwInstData, pData, &temp)) != EAS_SUCCESS) | |
return result; | |
pData->duration = temp; | |
break; | |
/* loop */ | |
case 'l': | |
if ((result = RTTTL_GetNumber(pEASData->hwInstData, pData, &i)) != EAS_SUCCESS) | |
return result; | |
if ((i < 0) || (i > 15)) | |
return EAS_ERROR_FILE_FORMAT; | |
pData->repeatCount = (EAS_U8) i; | |
break; | |
/* octave */ | |
case 'o': | |
if ((result = RTTTL_GetOctave(pEASData->hwInstData, pData, &pData->octave)) != EAS_SUCCESS) | |
return result; | |
break; | |
/* get style */ | |
case 's': | |
if ((result = RTTTL_GetStyle(pEASData->hwInstData, pData)) != EAS_SUCCESS) | |
return result; | |
break; | |
/* unrecognized control */ | |
default: | |
return EAS_ERROR_FILE_FORMAT; | |
} | |
/* next character should be comma or colon */ | |
if ((result = RTTTL_GetNextChar(pEASData->hwInstData, pData, &temp)) != EAS_SUCCESS) | |
return result; | |
/* check for end of control field */ | |
if (temp == ':') | |
break; | |
/* must be a comma */ | |
if (temp != ',') | |
return EAS_ERROR_FILE_FORMAT; | |
} | |
/* should be at the start of the music block */ | |
if ((result = EAS_HWFilePos(pEASData->hwInstData, pData->fileHandle, &pData->repeatOffset)) != EAS_SUCCESS) | |
return result; | |
return EAS_SUCCESS; | |
} | |
/*---------------------------------------------------------------------------- | |
* RTTTL_GetNextChar() | |
*---------------------------------------------------------------------------- | |
* Purpose: | |
* | |
* | |
* Inputs: | |
* | |
* | |
* Outputs: | |
* | |
* | |
* Side Effects: | |
* | |
*---------------------------------------------------------------------------- | |
*/ | |
static EAS_RESULT RTTTL_GetNextChar (EAS_HW_DATA_HANDLE hwInstData, S_RTTTL_DATA *pData, EAS_I8 *pValue) | |
{ | |
EAS_RESULT result; | |
EAS_I8 temp; | |
*pValue = 0; | |
for(;;) | |
{ | |
/* check for character that has been put back */ | |
if (pData->dataByte) | |
{ | |
temp = pData->dataByte; | |
pData->dataByte = 0; | |
} | |
else | |
{ | |
if ((result = EAS_HWGetByte(hwInstData, pData->fileHandle, &temp)) != EAS_SUCCESS) | |
return result; | |
} | |
/* ignore white space */ | |
if (!IsSpace(temp)) | |
{ | |
*pValue = ToLower(temp); | |
return EAS_SUCCESS; | |
} | |
} | |
} | |
/*---------------------------------------------------------------------------- | |
* RTTTL_PeekNextChar() | |
*---------------------------------------------------------------------------- | |
* Purpose: | |
* | |
* | |
* Inputs: | |
* | |
* | |
* Outputs: | |
* | |
* | |
* Side Effects: | |
* | |
*---------------------------------------------------------------------------- | |
*/ | |
static EAS_RESULT RTTTL_PeekNextChar (EAS_HW_DATA_HANDLE hwInstData, S_RTTTL_DATA *pData, EAS_I8 *pValue) | |
{ | |
EAS_RESULT result; | |
EAS_I8 temp; | |
*pValue = 0; | |
for(;;) | |
{ | |
/* read a character from the file, if necessary */ | |
if (!pData->dataByte) | |
{ | |
if ((result = EAS_HWGetByte(hwInstData, pData->fileHandle, &pData->dataByte)) != EAS_SUCCESS) | |
return result; | |
} | |
temp = pData->dataByte; | |
/* ignore white space */ | |
if (!IsSpace(temp)) | |
{ | |
*pValue = ToLower(temp); | |
return EAS_SUCCESS; | |
} | |
pData->dataByte = 0; | |
} | |
} | |