| /*---------------------------------------------------------------------------- |
| * |
| * File: |
| * eas_ota.c |
| * |
| * Contents and purpose: |
| * OTA 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_otadata.h" |
| |
| /* increase gain for mono ringtones */ |
| #define OTA_GAIN_OFFSET 8 |
| |
| /* file definitions */ |
| #define OTA_RINGTONE 0x25 |
| #define OTA_SOUND 0x1d |
| #define OTA_UNICODE 0x22 |
| |
| /* song type definitions */ |
| #define OTA_BASIC_SONG_TYPE 0x01 |
| #define OTA_TEMPORARY_SONG_TYPE 0x02 |
| |
| /* instruction ID coding */ |
| #define OTA_PATTERN_HEADER_ID 0x00 |
| #define OTA_NOTE_INST_ID 0x01 |
| #define OTA_SCALE_INST_ID 0x02 |
| #define OTA_STYLE_INST_ID 0x03 |
| #define OTA_TEMPO_INST_ID 0x04 |
| #define OTA_VOLUME_INST_ID 0x05 |
| |
| /* note durations */ |
| #define OTA_NORMAL_DURATION 0x00 |
| #define OTA_DOTTED_NOTE 0x01 |
| #define OTA_DOUBLE_DOTTED_NOTE 0x02 |
| #define OTA_TRIPLET_NOTE 0x03 |
| |
| /* loop count value for infinite loop */ |
| #define OTA_INFINITE_LOOP 0x0f |
| |
| /* length of 32nd note in 1/256ths of a msec for 63 BPM tempo */ |
| #define DEFAULT_TICK_CONV 30476 |
| |
| /* default channel and program for OTA playback */ |
| #define OTA_CHANNEL 0 |
| #define OTA_PROGRAM 80 |
| #define OTA_VEL_MUL 4 |
| #define OTA_VEL_OFS 67 |
| #define OTA_VEL_DEFAULT 95 |
| |
| /* multiplier for fixed point triplet conversion */ |
| #define TRIPLET_MULTIPLIER 683 |
| #define TRIPLET_SHIFT 10 |
| |
| /* local prototypes */ |
| static EAS_RESULT OTA_CheckFileType (S_EAS_DATA *pEASData, EAS_FILE_HANDLE fileHandle, EAS_VOID_PTR *ppHandle, EAS_I32 offset); |
| static EAS_RESULT OTA_Prepare (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData); |
| static EAS_RESULT OTA_Time (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_U32 *pTime); |
| static EAS_RESULT OTA_Event (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_INT parserMode); |
| static EAS_RESULT OTA_State (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_STATE *pState); |
| static EAS_RESULT OTA_Close (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData); |
| static EAS_RESULT OTA_Reset (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData); |
| static EAS_RESULT OTA_Pause (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData); |
| static EAS_RESULT OTA_Resume (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData); |
| static EAS_RESULT OTA_SetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 value); |
| static EAS_RESULT OTA_GetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 *pValue); |
| static EAS_RESULT OTA_ParseHeader (S_EAS_DATA *pEASData, S_OTA_DATA* pData); |
| static EAS_RESULT OTA_FetchBitField (EAS_HW_DATA_HANDLE hwInstData, S_OTA_DATA *pData, EAS_I32 numBits, EAS_U8 *pValue); |
| static EAS_RESULT OTA_SavePosition (EAS_HW_DATA_HANDLE hwInstData, S_OTA_DATA *pData, S_OTA_LOC *pLoc); |
| static EAS_RESULT OTA_RestorePosition (EAS_HW_DATA_HANDLE hwInstData, S_OTA_DATA *pData, S_OTA_LOC *pLoc); |
| |
| |
| /*---------------------------------------------------------------------------- |
| * |
| * EAS_OTA_Parser |
| * |
| * This structure contains the functional interface for the OTA parser |
| *---------------------------------------------------------------------------- |
| */ |
| const S_FILE_PARSER_INTERFACE EAS_OTA_Parser = |
| { |
| OTA_CheckFileType, |
| OTA_Prepare, |
| OTA_Time, |
| OTA_Event, |
| OTA_State, |
| OTA_Close, |
| OTA_Reset, |
| OTA_Pause, |
| OTA_Resume, |
| NULL, |
| OTA_SetData, |
| OTA_GetData, |
| NULL |
| }; |
| |
| /*---------------------------------------------------------------------------- |
| * |
| * bpmTable |
| * |
| * BPM conversion table. Converts bpm values to 256ths of a millisecond for a 32nd note |
| *---------------------------------------------------------------------------- |
| */ |
| static const EAS_U32 bpmTable[32] = |
| { |
| 76800, 68571, 61935, 54857, |
| 48000, 42667, 38400, 34286, |
| 30476, 27429, 24000, 21333, |
| 19200, 17143, 15360, 13714, |
| 12000, 10667, 9600, 8533, |
| 7680, 6737, 6000, 5408, |
| 4800, 4267, 3840, 3398, |
| 3024, 2685, 2400, 2133 |
| }; |
| |
| /*---------------------------------------------------------------------------- |
| * OTA_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 OTA_CheckFileType (S_EAS_DATA *pEASData, EAS_FILE_HANDLE fileHandle, EAS_VOID_PTR *ppHandle, EAS_I32 offset) |
| { |
| S_OTA_DATA* pData; |
| EAS_RESULT result; |
| EAS_INT cmdLen; |
| EAS_INT state; |
| EAS_U8 temp; |
| |
| /* read the first byte, should be command length */ |
| *ppHandle = NULL; |
| if ((result = EAS_HWGetByte(pEASData->hwInstData, fileHandle, &temp)) != EAS_SUCCESS) |
| return result; |
| |
| /* read all the commands */ |
| cmdLen = temp; |
| state = 0; |
| while (cmdLen--) |
| { |
| |
| /* read the command, upper 7 bits */ |
| if ((result = EAS_HWGetByte(pEASData->hwInstData, fileHandle, &temp)) != EAS_SUCCESS) |
| return result; |
| temp = temp >> 1; |
| |
| if (state == 0) |
| { |
| if (temp != OTA_RINGTONE) |
| break; |
| state++; |
| } |
| else |
| { |
| |
| if (temp == OTA_SOUND) |
| { |
| |
| /* check for static memory allocation */ |
| if (pEASData->staticMemoryModel) |
| pData = EAS_CMEnumData(EAS_CM_OTA_DATA); |
| else |
| pData = EAS_HWMalloc(pEASData->hwInstData, sizeof(S_OTA_DATA)); |
| if (!pData) |
| { |
| { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "Malloc failed in OTA_Prepare\n"); */ } |
| return EAS_ERROR_MALLOC_FAILED; |
| } |
| EAS_HWMemSet(pData, 0, sizeof(S_OTA_DATA)); |
| |
| /* return a pointer to the instance data */ |
| pData->fileHandle = fileHandle; |
| pData->fileOffset = offset; |
| pData->state = EAS_STATE_OPEN; |
| *ppHandle = pData; |
| break; |
| } |
| |
| if (temp != OTA_UNICODE) |
| break; |
| } |
| } |
| |
| /* not recognized */ |
| return EAS_SUCCESS; |
| } |
| |
| /*---------------------------------------------------------------------------- |
| * OTA_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 OTA_Prepare (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData) |
| { |
| S_OTA_DATA* pData; |
| EAS_RESULT result; |
| |
| /* check for valid state */ |
| pData = (S_OTA_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 = OTA_ParseHeader(pEASData, pData)) != EAS_SUCCESS) |
| return result; |
| |
| pData->state = EAS_STATE_READY; |
| return EAS_SUCCESS; |
| } |
| |
| /*---------------------------------------------------------------------------- |
| * OTA_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) common decoder interface - pEASData not used */ |
| static EAS_RESULT OTA_Time (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_U32 *pTime) |
| { |
| S_OTA_DATA *pData; |
| |
| pData = (S_OTA_DATA*) pInstData; |
| |
| /* return time in milliseconds */ |
| /*lint -e{704} use shift instead of division */ |
| *pTime = pData->time >> 8; |
| return EAS_SUCCESS; |
| } |
| |
| /*---------------------------------------------------------------------------- |
| * OTA_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 OTA_Event (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_INT parserMode) |
| { |
| S_OTA_DATA* pData; |
| EAS_RESULT result; |
| EAS_U32 duration; |
| EAS_U8 temp; |
| |
| pData = (S_OTA_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 */ |
| if (parserMode != eParserModeMetaData) |
| VMProgramChange(pEASData->pVoiceMgr, pData->pSynth, OTA_CHANNEL, OTA_PROGRAM); |
| |
| /* set channel volume to max */ |
| if (parserMode != eParserModeMetaData) |
| VMControlChange(pEASData->pVoiceMgr, pData->pSynth, OTA_CHANNEL, 7, 127); |
| } |
| |
| /* check for end of note */ |
| if (pData->note) |
| { |
| /* stop the note */ |
| VMStopNote(pEASData->pVoiceMgr, pData->pSynth, OTA_CHANNEL, pData->note, 0); |
| pData->note = 0; |
| |
| /* check for rest between notes */ |
| if (pData->restTicks) |
| { |
| pData->time += (EAS_I32) pData->restTicks; |
| pData->restTicks = 0; |
| return EAS_SUCCESS; |
| } |
| } |
| |
| /* if not in a pattern, read the pattern header */ |
| while (pData->current.patternLen == 0) |
| { |
| |
| /* check for loop - don't do infinite loops when locating */ |
| if (pData->loopCount && ((parserMode == eParserModePlay) || (pData->loopCount != OTA_INFINITE_LOOP))) |
| { |
| /* if not infinite loop, decrement loop count */ |
| if (pData->loopCount != OTA_INFINITE_LOOP) |
| pData->loopCount--; |
| |
| /* back to start of pattern*/ |
| if ((result = OTA_RestorePosition(pEASData->hwInstData, pData, &pData->patterns[pData->currentPattern])) != EAS_SUCCESS) |
| return result; |
| } |
| |
| /* if no previous position to restore, continue forward */ |
| else if (pData->restore.fileOffset < 0) |
| { |
| |
| /* check for end of song */ |
| if (pData->numPatterns == 0) |
| { |
| pData->state = EAS_STATE_STOPPING; |
| VMReleaseAllVoices(pEASData->pVoiceMgr, pData->pSynth); |
| return EAS_SUCCESS; |
| } |
| |
| /* read the next pattern header */ |
| if ((result = OTA_FetchBitField(pEASData->hwInstData, pData, 3, &temp)) != EAS_SUCCESS) |
| return result; |
| if (temp != OTA_PATTERN_HEADER_ID) |
| { |
| { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "Expected OTA pattern header\n"); */ } |
| return EAS_ERROR_FILE_FORMAT; |
| } |
| |
| /* get the pattern ID */ |
| if ((result = OTA_FetchBitField(pEASData->hwInstData, pData, 2, &pData->currentPattern)) != EAS_SUCCESS) |
| return result; |
| |
| /* get the loop count */ |
| if ((result = OTA_FetchBitField(pEASData->hwInstData, pData, 4, &pData->loopCount)) != EAS_SUCCESS) |
| return result; |
| |
| /* get the pattern length */ |
| if ((result = OTA_FetchBitField(pEASData->hwInstData, pData, 8, &pData->current.patternLen)) != EAS_SUCCESS) |
| return result; |
| |
| /* if pattern definition, save the current position */ |
| if (pData->current.patternLen) |
| { |
| if ((result = OTA_SavePosition(pEASData->hwInstData, pData, &pData->patterns[pData->currentPattern])) != EAS_SUCCESS) |
| return result; |
| } |
| |
| /* if pattern length is zero, repeat a previous pattern */ |
| else |
| { |
| /* make sure it's a valid pattern */ |
| if (pData->patterns[pData->currentPattern].fileOffset < 0) |
| { |
| { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "OTA pattern error, invalid pattern specified\n"); */ } |
| return EAS_ERROR_FILE_FORMAT; |
| } |
| |
| /* save current position and data */ |
| if ((result = OTA_SavePosition(pEASData->hwInstData, pData, &pData->restore)) != EAS_SUCCESS) |
| return result; |
| |
| /* seek to the pattern in the file */ |
| if ((result = OTA_RestorePosition(pEASData->hwInstData, pData, &pData->patterns[pData->currentPattern])) != EAS_SUCCESS) |
| return result; |
| } |
| |
| /* decrement pattern count */ |
| pData->numPatterns--; |
| } |
| |
| /* restore previous position */ |
| else |
| { |
| if ((result = OTA_RestorePosition(pEASData->hwInstData, pData, &pData->restore)) != EAS_SUCCESS) |
| return result; |
| } |
| } |
| |
| /* get the next event */ |
| if ((result = OTA_FetchBitField(pEASData->hwInstData, pData, 3, &temp)) != EAS_SUCCESS) |
| return result; |
| |
| switch (temp) |
| { |
| case OTA_NOTE_INST_ID: |
| /* fetch note value */ |
| if ((result = OTA_FetchBitField(pEASData->hwInstData, pData, 4, &pData->note)) != EAS_SUCCESS) |
| return result; |
| |
| /* fetch note duration */ |
| if ((result = OTA_FetchBitField(pEASData->hwInstData, pData, 3, &temp)) != EAS_SUCCESS) |
| return result; |
| duration = pData->tick * (0x20 >> temp); |
| |
| /* fetch note duration modifier */ |
| if ((result = OTA_FetchBitField(pEASData->hwInstData, pData, 2, &temp)) != EAS_SUCCESS) |
| return result; |
| switch (temp) |
| { |
| case OTA_NORMAL_DURATION: |
| break; |
| |
| case OTA_DOTTED_NOTE: |
| duration += duration >> 1; |
| break; |
| |
| case OTA_DOUBLE_DOTTED_NOTE: |
| duration += (duration >> 1) + (duration >> 2); |
| break; |
| |
| case OTA_TRIPLET_NOTE: |
| duration = (duration * TRIPLET_MULTIPLIER) >> TRIPLET_SHIFT; |
| break; |
| |
| default: |
| { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "Unrecognized note duration ignored\n"); */ } |
| break; |
| } |
| |
| /* check for note */ |
| if (pData->note) |
| { |
| |
| /* determine note length based on style */ |
| switch (pData->style) |
| { |
| case 0: |
| pData->restTicks = duration >> 4; |
| break; |
| case 1: |
| pData->restTicks = 0; |
| break; |
| case 2: |
| pData->restTicks = duration >> 1; |
| break; |
| default: |
| { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "Unrecognized note style ignored\n"); */ } |
| } |
| |
| /* add octave */ |
| pData->note += pData->octave; |
| if (parserMode == eParserModePlay) |
| VMStartNote(pEASData->pVoiceMgr, pData->pSynth, OTA_CHANNEL, pData->note, pData->velocity); |
| pData->time += (EAS_I32) duration - (EAS_I32) pData->restTicks; |
| } |
| |
| /* this is a rest */ |
| else |
| pData->time += (EAS_I32) duration; |
| break; |
| |
| case OTA_SCALE_INST_ID: |
| /* fetch octave */ |
| if ((result = OTA_FetchBitField(pEASData->hwInstData, pData, 2, &temp)) != EAS_SUCCESS) |
| return result; |
| pData->octave = (EAS_U8) (temp * 12 + 59); |
| break; |
| |
| case OTA_STYLE_INST_ID: |
| /* fetch note style */ |
| if ((result = OTA_FetchBitField(pEASData->hwInstData, pData, 2, &pData->style)) != EAS_SUCCESS) |
| return result; |
| break; |
| |
| case OTA_TEMPO_INST_ID: |
| /* fetch tempo */ |
| if ((result = OTA_FetchBitField(pEASData->hwInstData, pData, 5, &temp)) != EAS_SUCCESS) |
| return result; |
| pData->tick = bpmTable[temp]; |
| break; |
| |
| case OTA_VOLUME_INST_ID: |
| /* fetch volume */ |
| if ((result = OTA_FetchBitField(pEASData->hwInstData, pData, 4, &temp)) != EAS_SUCCESS) |
| return result; |
| pData->velocity = temp ? (EAS_U8) (temp * OTA_VEL_MUL + OTA_VEL_OFS) : 0; |
| break; |
| |
| default: |
| { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "Unexpected instruction ID in OTA stream\n"); */ } |
| return EAS_ERROR_FILE_FORMAT; |
| } |
| |
| /* decrement pattern length */ |
| pData->current.patternLen--; |
| return EAS_SUCCESS; |
| } |
| |
| /*---------------------------------------------------------------------------- |
| * OTA_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) common decoder interface - pEASData not used */ |
| static EAS_RESULT OTA_State (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 *pState) |
| { |
| S_OTA_DATA* pData; |
| |
| /* establish pointer to instance data */ |
| pData = (S_OTA_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; |
| } |
| |
| /*---------------------------------------------------------------------------- |
| * OTA_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 OTA_Close (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData) |
| { |
| S_OTA_DATA* pData; |
| EAS_RESULT result; |
| |
| pData = (S_OTA_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; |
| } |
| |
| /*---------------------------------------------------------------------------- |
| * OTA_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 OTA_Reset (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData) |
| { |
| S_OTA_DATA* pData; |
| EAS_RESULT result; |
| |
| pData = (S_OTA_DATA*) pInstData; |
| |
| /* reset the synth */ |
| VMReset(pEASData->pVoiceMgr, pData->pSynth, EAS_TRUE); |
| pData->note = 0; |
| |
| /* reset file position and re-parse header */ |
| pData->state = EAS_STATE_ERROR; |
| if ((result = OTA_ParseHeader (pEASData, pData)) != EAS_SUCCESS) |
| return result; |
| |
| pData->state = EAS_STATE_READY; |
| return EAS_SUCCESS; |
| } |
| |
| /*---------------------------------------------------------------------------- |
| * OTA_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 OTA_Pause (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData) |
| { |
| S_OTA_DATA *pData; |
| |
| /* can't pause a stopped stream */ |
| pData = (S_OTA_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; |
| } |
| |
| /*---------------------------------------------------------------------------- |
| * OTA_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) common decoder interface - pEASData not used */ |
| static EAS_RESULT OTA_Resume (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData) |
| { |
| S_OTA_DATA *pData; |
| |
| /* can't resume a stopped stream */ |
| pData = (S_OTA_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; |
| } |
| |
| /*---------------------------------------------------------------------------- |
| * OTA_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) common decoder interface - pEASData not used */ |
| static EAS_RESULT OTA_SetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 value) |
| { |
| S_OTA_DATA *pData; |
| |
| pData = (S_OTA_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; |
| } |
| |
| /*---------------------------------------------------------------------------- |
| * OTA_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) common decoder interface - pEASData not used */ |
| static EAS_RESULT OTA_GetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 *pValue) |
| { |
| S_OTA_DATA *pData; |
| |
| pData = (S_OTA_DATA*) pInstData; |
| switch (param) |
| { |
| /* return file type as OTA */ |
| case PARSER_DATA_FILE_TYPE: |
| *pValue = EAS_FILE_OTA; |
| 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 = OTA_GAIN_OFFSET; |
| break; |
| |
| default: |
| return EAS_ERROR_INVALID_PARAMETER; |
| } |
| return EAS_SUCCESS; |
| } |
| |
| /*---------------------------------------------------------------------------- |
| * OTA_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 OTA_ParseHeader (S_EAS_DATA *pEASData, S_OTA_DATA* pData) |
| { |
| EAS_RESULT result; |
| EAS_INT i; |
| EAS_INT state; |
| EAS_U8 temp; |
| EAS_U8 titleLen; |
| |
| /* initialize some data */ |
| pData->flags = 0; |
| pData->time = 0; |
| pData->tick = DEFAULT_TICK_CONV; |
| pData->patterns[0].fileOffset = pData->patterns[1].fileOffset = |
| pData->patterns[2].fileOffset = pData->patterns[3].fileOffset = -1; |
| pData->current.bitCount = 0; |
| pData->current.patternLen = 0; |
| pData->loopCount = 0; |
| pData->restore.fileOffset = -1; |
| pData->note = 0; |
| pData->restTicks = 0; |
| pData->velocity = OTA_VEL_DEFAULT; |
| pData->style = 0; |
| pData->octave = 59; |
| |
| /* seek to start of data */ |
| if ((result = EAS_HWFileSeek(pEASData->hwInstData, pData->fileHandle, pData->fileOffset)) != EAS_SUCCESS) |
| return result; |
| |
| /* read the first byte, should be command length */ |
| if ((result = EAS_HWGetByte(pEASData->hwInstData, pData->fileHandle, &temp)) != EAS_SUCCESS) |
| return result; |
| |
| /* read all the commands */ |
| i = temp; |
| state = 0; |
| while (i--) |
| { |
| |
| /* fetch command, always starts on byte boundary */ |
| pData->current.bitCount = 0; |
| if ((result = OTA_FetchBitField(pEASData->hwInstData, pData, 7, &temp)) != EAS_SUCCESS) |
| return result; |
| |
| if (state == 0) |
| { |
| if (temp != OTA_RINGTONE) |
| { |
| { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "Expected OTA Ring Tone Programming type\n"); */ } |
| return EAS_ERROR_FILE_FORMAT; |
| } |
| state++; |
| } |
| else |
| { |
| |
| if (temp == OTA_SOUND) |
| break; |
| |
| if (temp == OTA_UNICODE) |
| pData->flags |= OTA_FLAGS_UNICODE; |
| else |
| { |
| { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "Expected OTA Sound or Unicode type\n"); */ } |
| return EAS_ERROR_FILE_FORMAT; |
| } |
| } |
| } |
| |
| /* get song type */ |
| if ((result = OTA_FetchBitField(pEASData->hwInstData, pData, 3, &temp)) != EAS_SUCCESS) |
| return result; |
| |
| /* check for basic song type */ |
| if (temp == OTA_BASIC_SONG_TYPE) |
| { |
| /* fetch title length */ |
| if ((result = OTA_FetchBitField(pEASData->hwInstData, pData, 4, &titleLen)) != EAS_SUCCESS) |
| return result; |
| |
| /* if unicode, double the length */ |
| if (pData->flags & OTA_FLAGS_UNICODE) |
| titleLen = (EAS_U8) (titleLen << 1); |
| |
| /* zero the metadata buffer */ |
| if (pData->metadata.buffer) |
| EAS_HWMemSet(pData->metadata.buffer, 0, pData->metadata.bufferSize); |
| |
| /* read the song title */ |
| for (i = 0; i < titleLen; i++) |
| { |
| /* fetch character */ |
| if ((result = OTA_FetchBitField(pEASData->hwInstData, pData, 8, &temp)) != EAS_SUCCESS) |
| return result; |
| |
| /* check for metadata callback */ |
| if (pData->metadata.callback) |
| { |
| if (i < (pData->metadata.bufferSize - 1)) |
| pData->metadata.buffer[i] = (char) temp; |
| } |
| } |
| |
| /* if host has registered callback, call it now */ |
| if (pData->metadata.callback) |
| (*pData->metadata.callback)(EAS_METADATA_TITLE, pData->metadata.buffer, pData->metadata.pUserData); |
| } |
| |
| /* must be temporary song */ |
| else if (temp != OTA_TEMPORARY_SONG_TYPE) |
| { |
| { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "Expected OTA basic or temporary song type\n"); */ } |
| return EAS_ERROR_FILE_FORMAT; |
| } |
| |
| /* get the song length */ |
| if ((result = OTA_FetchBitField(pEASData->hwInstData, pData, 8, &pData->numPatterns)) != EAS_SUCCESS) |
| return result; |
| |
| /* sanity check */ |
| if (pData->numPatterns == 0) |
| { |
| { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "OTA number of patterns is zero\n"); */ } |
| return EAS_ERROR_FILE_FORMAT; |
| } |
| |
| /* at start of first pattern */ |
| return EAS_SUCCESS; |
| } |
| |
| /*---------------------------------------------------------------------------- |
| * OTA_FetchBitField() |
| *---------------------------------------------------------------------------- |
| * Purpose: |
| * Fetch a specified number of bits from the input stream |
| * |
| * Inputs: |
| * |
| * |
| * Outputs: |
| * |
| * |
| * Side Effects: |
| * |
| *---------------------------------------------------------------------------- |
| */ |
| static EAS_RESULT OTA_FetchBitField (EAS_HW_DATA_HANDLE hwInstData, S_OTA_DATA *pData, EAS_I32 numBits, EAS_U8 *pValue) |
| { |
| EAS_RESULT result; |
| EAS_I32 bitsLeft; |
| EAS_U8 value; |
| |
| value = 0; |
| |
| /* do we have enough bits? */ |
| bitsLeft = pData->current.bitCount - numBits; |
| |
| /* not enough bits, assemble them from 2 characters */ |
| if (bitsLeft < 0) |
| { |
| /* grab the remaining bits from the previous byte */ |
| if (pData->current.bitCount) |
| /*lint -e{504,734} this is a legitimate shift operation */ |
| value = pData->current.dataByte << -bitsLeft; |
| |
| /* read the next byte */ |
| if ((result = EAS_HWGetByte(hwInstData, pData->fileHandle, &pData->current.dataByte)) != EAS_SUCCESS) |
| return result; |
| bitsLeft += 8; |
| } |
| |
| /* more bits than needed? */ |
| if (bitsLeft > 0) |
| { |
| value |= pData->current.dataByte >> bitsLeft; |
| pData->current.bitCount = (EAS_U8) bitsLeft; |
| pData->current.dataByte = pData->current.dataByte & (0xff >> (8 - bitsLeft)); |
| } |
| |
| /* exactly the right number of bits */ |
| else |
| { |
| value |= pData->current.dataByte; |
| pData->current.bitCount = 0; |
| } |
| |
| *pValue = value; |
| return EAS_SUCCESS; |
| } |
| |
| /*---------------------------------------------------------------------------- |
| * OTA_SavePosition() |
| *---------------------------------------------------------------------------- |
| * Purpose: |
| * |
| * |
| * Inputs: |
| * |
| * |
| * Outputs: |
| * |
| * |
| * Side Effects: |
| * |
| *---------------------------------------------------------------------------- |
| */ |
| static EAS_RESULT OTA_SavePosition (EAS_HW_DATA_HANDLE hwInstData, S_OTA_DATA *pData, S_OTA_LOC *pLoc) |
| { |
| EAS_HWMemCpy(pLoc, &pData->current, sizeof(S_OTA_LOC)); |
| return EAS_HWFilePos(hwInstData, pData->fileHandle, &pLoc->fileOffset); |
| } |
| |
| /*---------------------------------------------------------------------------- |
| * OTA_RestorePosition() |
| *---------------------------------------------------------------------------- |
| * Purpose: |
| * |
| * |
| * Inputs: |
| * |
| * |
| * Outputs: |
| * |
| * |
| * Side Effects: |
| * |
| *---------------------------------------------------------------------------- |
| */ |
| static EAS_RESULT OTA_RestorePosition (EAS_HW_DATA_HANDLE hwInstData, S_OTA_DATA *pData, S_OTA_LOC *pLoc) |
| { |
| EAS_HWMemCpy(&pData->current, pLoc, sizeof(S_OTA_LOC)); |
| pData->restore.fileOffset = -1; |
| return EAS_HWFileSeek(hwInstData, pData->fileHandle, pLoc->fileOffset); |
| } |
| |