/*---------------------------------------------------------------------------- * * 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) $ *---------------------------------------------------------------------------- */ #define LOG_TAG "Sonivox" #include <log/log.h> #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; ALOGD("%s() OTA file recognized", __func__); 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))) { ALOGV("%s() loop backwards, loopCount = %d", __func__, pData->loopCount); /* 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); }