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