• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*----------------------------------------------------------------------------
2  *
3  * File:
4  * eas_smf.c
5  *
6  * Contents and purpose:
7  * SMF Type 0 and 1 File Parser
8  *
9  * For SMF timebase analysis, see "MIDI Sequencer Analysis.xls".
10  *
11  * Copyright Sonic Network Inc. 2005
12 
13  * Licensed under the Apache License, Version 2.0 (the "License");
14  * you may not use this file except in compliance with the License.
15  * You may obtain a copy of the License at
16  *
17  *      http://www.apache.org/licenses/LICENSE-2.0
18  *
19  * Unless required by applicable law or agreed to in writing, software
20  * distributed under the License is distributed on an "AS IS" BASIS,
21  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22  * See the License for the specific language governing permissions and
23  * limitations under the License.
24  *
25  *----------------------------------------------------------------------------
26  * Revision Control:
27  *   $Revision: 803 $
28  *   $Date: 2007-08-01 09:57:00 -0700 (Wed, 01 Aug 2007) $
29  *----------------------------------------------------------------------------
30 */
31 
32 #define LOG_TAG "Sonivox"
33 #include "log/log.h"
34 
35 #include "eas_data.h"
36 #include "eas_miditypes.h"
37 #include "eas_parser.h"
38 #include "eas_report.h"
39 #include "eas_host.h"
40 #include "eas_midi.h"
41 #include "eas_config.h"
42 #include "eas_vm_protos.h"
43 #include "eas_smfdata.h"
44 #include "eas_smf.h"
45 
46 #ifdef JET_INTERFACE
47 #include "jet_data.h"
48 #endif
49 
50 //3 dls: The timebase for this module is adequate to keep MIDI and
51 //3 digital audio synchronized for only a few minutes. It should be
52 //3 sufficient for most mobile applications. If better accuracy is
53 //3 required, more fractional bits should be added to the timebase.
54 
55 static const EAS_U8 smfHeader[] = { 'M', 'T', 'h', 'd' };
56 
57 /* local prototypes */
58 static EAS_RESULT SMF_GetVarLenData (EAS_HW_DATA_HANDLE hwInstData, EAS_FILE_HANDLE fileHandle, EAS_U32 *pData);
59 static EAS_RESULT SMF_ParseMetaEvent (S_EAS_DATA *pEASData, S_SMF_DATA *pSMFData, S_SMF_STREAM *pSMFStream);
60 static EAS_RESULT SMF_ParseSysEx (S_EAS_DATA *pEASData, S_SMF_DATA *pSMFData, S_SMF_STREAM *pSMFStream, EAS_U8 f0, EAS_INT parserMode);
61 static EAS_RESULT SMF_ParseEvent (S_EAS_DATA *pEASData, S_SMF_DATA *pSMFData, S_SMF_STREAM *pSMFStream, EAS_INT parserMode);
62 static EAS_RESULT SMF_GetDeltaTime (EAS_HW_DATA_HANDLE hwInstData, S_SMF_STREAM *pSMFStream);
63 static void SMF_UpdateTime (S_SMF_DATA *pSMFData, EAS_U32 ticks);
64 
65 
66 /*----------------------------------------------------------------------------
67  *
68  * SMF_Parser
69  *
70  * This structure contains the functional interface for the SMF parser
71  *----------------------------------------------------------------------------
72 */
73 const S_FILE_PARSER_INTERFACE EAS_SMF_Parser =
74 {
75     SMF_CheckFileType,
76     SMF_Prepare,
77     SMF_Time,
78     SMF_Event,
79     SMF_State,
80     SMF_Close,
81     SMF_Reset,
82 #ifdef JET_INTERFACE
83     SMF_Pause,
84     SMF_Resume,
85 #else
86     NULL,
87     NULL,
88 #endif
89     NULL,
90     SMF_SetData,
91     SMF_GetData,
92     NULL
93 };
94 
95 /*----------------------------------------------------------------------------
96  * SMF_CheckFileType()
97  *----------------------------------------------------------------------------
98  * Purpose:
99  * Check the file type to see if we can parse it
100  *
101  * Inputs:
102  * pEASData         - pointer to overall EAS data structure
103  * handle           - pointer to file handle
104  *
105  * Outputs:
106  *
107  *
108  * Side Effects:
109  *
110  *----------------------------------------------------------------------------
111 */
SMF_CheckFileType(S_EAS_DATA * pEASData,EAS_FILE_HANDLE fileHandle,EAS_VOID_PTR * ppHandle,EAS_I32 offset)112 EAS_RESULT SMF_CheckFileType (S_EAS_DATA *pEASData, EAS_FILE_HANDLE fileHandle, EAS_VOID_PTR *ppHandle, EAS_I32 offset)
113 {
114     S_SMF_DATA* pSMFData;
115     EAS_RESULT result;
116 
117     /* seek to starting offset - usually 0 */
118     *ppHandle = NULL;
119     if ((result = EAS_HWFileSeek(pEASData->hwInstData, fileHandle, offset)) != EAS_SUCCESS)
120         return result;
121 
122 #ifdef FILE_HEADER_SEARCH
123     /* search through file for header - slow method */
124     if (pEASData->searchHeaderFlag)
125     {
126         result = EAS_SearchFile(pEASData, fileHandle, smfHeader, sizeof(smfHeader), &offset);
127         if (result != EAS_SUCCESS)
128             return (result == EAS_EOF) ? EAS_SUCCESS : result;
129     }
130 
131     /* read the first 4 bytes of the file - quick method */
132     else
133 #endif
134     {
135         EAS_U8 header[4];
136         EAS_I32 count;
137         if ((result = EAS_HWReadFile(pEASData->hwInstData, fileHandle, header, sizeof(header), &count)) != EAS_SUCCESS)
138             return result;
139 
140         /* check for 'MThd' - If no match then return SUCCESS with NULL handle
141          * to indicate not an SMF file. */
142         if ((header[0] != 'M') || (header[1] != 'T') || (header[2] != 'h') || (header[3] != 'd'))
143             return EAS_SUCCESS;
144     }
145 
146     /* check for static memory allocation */
147     if (pEASData->staticMemoryModel)
148         pSMFData = EAS_CMEnumData(EAS_CM_SMF_DATA);
149     else
150     {
151         pSMFData = EAS_HWMalloc(pEASData->hwInstData, sizeof(S_SMF_DATA));
152         EAS_HWMemSet((void *)pSMFData,0, sizeof(S_SMF_DATA));
153     }
154     if (!pSMFData)
155         return EAS_ERROR_MALLOC_FAILED;
156 
157     /* initialize some critical data */
158     pSMFData->fileHandle = fileHandle;
159     pSMFData->fileOffset = offset;
160     pSMFData->pSynth = NULL;
161     pSMFData->time = 0;
162     pSMFData->state = EAS_STATE_OPEN;
163     *ppHandle = pSMFData;
164 
165     return EAS_SUCCESS;
166 }
167 
168 /*----------------------------------------------------------------------------
169  * SMF_Prepare()
170  *----------------------------------------------------------------------------
171  * Purpose:
172  * Prepare to parse the file. Allocates instance data (or uses static allocation for
173  * static memory model).
174  *
175  * Inputs:
176  * pEASData         - pointer to overall EAS data structure
177  * handle           - pointer to file handle
178  *
179  * Outputs:
180  *
181  *
182  * Side Effects:
183  *
184  *----------------------------------------------------------------------------
185 */
SMF_Prepare(S_EAS_DATA * pEASData,EAS_VOID_PTR pInstData)186 EAS_RESULT SMF_Prepare (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
187 {
188     S_SMF_DATA* pSMFData;
189     EAS_RESULT result;
190 
191     /* check for valid state */
192     pSMFData = (S_SMF_DATA *) pInstData;
193     if (pSMFData->state != EAS_STATE_OPEN)
194         return EAS_ERROR_NOT_VALID_IN_THIS_STATE;
195 
196     /* instantiate a synthesizer */
197     if ((result = VMInitMIDI(pEASData, &pSMFData->pSynth)) != EAS_SUCCESS)
198     {
199         { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "VMInitMIDI returned %d\n", result); */ }
200         return result;
201     }
202 
203     /* parse the file header and setup the individual stream parsers */
204     if ((result = SMF_ParseHeader(pEASData->hwInstData, pSMFData)) != EAS_SUCCESS)
205         return result;
206 
207     /* ready to play */
208     pSMFData->state = EAS_STATE_READY;
209     return EAS_SUCCESS;
210 }
211 
212 /*----------------------------------------------------------------------------
213  * SMF_Time()
214  *----------------------------------------------------------------------------
215  * Purpose:
216  * Returns the time of the next event in msecs
217  *
218  * Inputs:
219  * pEASData         - pointer to overall EAS data structure
220  * handle           - pointer to file handle
221  * pTime            - pointer to variable to hold time of next event (in msecs)
222  *
223  * Outputs:
224  *
225  *
226  * Side Effects:
227  *
228  *----------------------------------------------------------------------------
229 */
230 /*lint -esym(715, pEASData) reserved for future use */
SMF_Time(S_EAS_DATA * pEASData,EAS_VOID_PTR pInstData,EAS_U32 * pTime)231 EAS_RESULT SMF_Time (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_U32 *pTime)
232 {
233     S_SMF_DATA *pSMFData;
234 
235     pSMFData = (S_SMF_DATA*) pInstData;
236 
237     /* sanity check */
238 #ifdef _CHECKED_BUILD
239     if (pSMFData->state == EAS_STATE_STOPPED)
240     {
241         { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "Can't ask for time on a stopped stream\n"); */ }
242     }
243 
244     if (pSMFData->nextStream == NULL)
245     {
246         { /* dpp: EAS_ReportEx( _EAS_SEVERITY_ERROR, "no is NULL\n"); */ }
247     }
248 #endif
249 
250 #if 0
251     /* return time in milliseconds */
252     /* if chase mode, lie about time */
253     if (pSMFData->flags & SMF_FLAGS_CHASE_MODE)
254         *pTime = 0;
255 
256     else
257 #endif
258 
259         /*lint -e{704} use shift instead of division */
260         *pTime = pSMFData->time >> 8;
261 
262     *pTime = pSMFData->time >> 8;
263     return EAS_SUCCESS;
264 }
265 
266 /*----------------------------------------------------------------------------
267  * SMF_Event()
268  *----------------------------------------------------------------------------
269  * Purpose:
270  * Parse the next event in the file
271  *
272  * Inputs:
273  * pEASData         - pointer to overall EAS data structure
274  * handle           - pointer to file handle
275  *
276  * Outputs:
277  *
278  *
279  * Side Effects:
280  *
281  *----------------------------------------------------------------------------
282 */
SMF_Event(S_EAS_DATA * pEASData,EAS_VOID_PTR pInstData,EAS_INT parserMode)283 EAS_RESULT SMF_Event (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_INT parserMode)
284 {
285     S_SMF_DATA* pSMFData;
286     EAS_RESULT result;
287     EAS_I32 i;
288     EAS_U32 ticks;
289     EAS_U32 temp;
290 
291     /* establish pointer to instance data */
292     pSMFData = (S_SMF_DATA*) pInstData;
293     if (pSMFData->state >= EAS_STATE_OPEN)
294         return EAS_SUCCESS;
295 
296     if (!pSMFData->nextStream) {
297         return EAS_ERROR_FILE_FORMAT;
298     }
299 
300 
301     /* get current ticks */
302     ticks = pSMFData->nextStream->ticks;
303 
304     /* assume that an error occurred */
305     pSMFData->state = EAS_STATE_ERROR;
306 
307 #ifdef JET_INTERFACE
308     /* if JET has track muted, set parser mode to mute */
309     if (pSMFData->nextStream->midiStream.jetData & MIDI_FLAGS_JET_MUTE)
310         parserMode = eParserModeMute;
311 #endif
312 
313     /* parse the next event from all the streams */
314     if ((result = SMF_ParseEvent(pEASData, pSMFData, pSMFData->nextStream, parserMode)) != EAS_SUCCESS)
315     {
316         /* check for unexpected end-of-file */
317         if (result != EAS_EOF)
318             return result;
319 
320         /* indicate end of track for this stream */
321         pSMFData->nextStream->ticks = SMF_END_OF_TRACK;
322     }
323 
324     /* get next delta time, unless already at end of track */
325     else if (pSMFData->nextStream->ticks != SMF_END_OF_TRACK)
326     {
327         if ((result = SMF_GetDeltaTime(pEASData->hwInstData, pSMFData->nextStream)) != EAS_SUCCESS)
328         {
329             /* check for unexpected end-of-file */
330             if (result != EAS_EOF)
331                 return result;
332 
333             /* indicate end of track for this stream */
334             pSMFData->nextStream->ticks = SMF_END_OF_TRACK;
335         }
336 
337         /* if zero delta to next event, stay with this stream */
338         else if (pSMFData->nextStream->ticks == ticks)
339         {
340             pSMFData->state = EAS_STATE_PLAY;
341             return EAS_SUCCESS;
342         }
343     }
344 
345     /* find next event in all streams */
346     temp = 0x7ffffff;
347     pSMFData->nextStream = NULL;
348     for (i = 0; i < pSMFData->numStreams; i++)
349     {
350         if (pSMFData->streams[i].ticks < temp)
351         {
352             temp = pSMFData->streams[i].ticks;
353             pSMFData->nextStream = &pSMFData->streams[i];
354         }
355     }
356 
357     /* are there any more events to parse? */
358     if (pSMFData->nextStream)
359     {
360         pSMFData->state = EAS_STATE_PLAY;
361 
362         /* update the time of the next event */
363         SMF_UpdateTime(pSMFData, pSMFData->nextStream->ticks - ticks);
364     }
365     else
366     {
367         pSMFData->state = EAS_STATE_STOPPING;
368         VMReleaseAllVoices(pEASData->pVoiceMgr, pSMFData->pSynth);
369     }
370 
371     return EAS_SUCCESS;
372 }
373 
374 /*----------------------------------------------------------------------------
375  * SMF_State()
376  *----------------------------------------------------------------------------
377  * Purpose:
378  * Returns the current state of the stream
379  *
380  * Inputs:
381  * pEASData         - pointer to overall EAS data structure
382  * handle           - pointer to file handle
383  * pState           - pointer to variable to store state
384  *
385  * Outputs:
386  *
387  *
388  * Side Effects:
389  *
390  *----------------------------------------------------------------------------
391 */
392 /*lint -esym(715, pEASData) reserved for future use */
SMF_State(S_EAS_DATA * pEASData,EAS_VOID_PTR pInstData,EAS_I32 * pState)393 EAS_RESULT SMF_State (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 *pState)
394 {
395     S_SMF_DATA* pSMFData;
396 
397     /* establish pointer to instance data */
398     pSMFData = (S_SMF_DATA*) pInstData;
399 
400     /* if stopping, check to see if synth voices are active */
401     if (pSMFData->state == EAS_STATE_STOPPING)
402     {
403         if (VMActiveVoices(pSMFData->pSynth) == 0)
404             pSMFData->state = EAS_STATE_STOPPED;
405     }
406 
407     if (pSMFData->state == EAS_STATE_PAUSING)
408     {
409         if (VMActiveVoices(pSMFData->pSynth) == 0)
410             pSMFData->state = EAS_STATE_PAUSED;
411     }
412 
413     /* return current state */
414     *pState = pSMFData->state;
415     return EAS_SUCCESS;
416 }
417 
418 /*----------------------------------------------------------------------------
419  * SMF_Close()
420  *----------------------------------------------------------------------------
421  * Purpose:
422  * Close the file and clean up
423  *
424  * Inputs:
425  * pEASData         - pointer to overall EAS data structure
426  * handle           - pointer to file handle
427  *
428  * Outputs:
429  *
430  *
431  * Side Effects:
432  *
433  *----------------------------------------------------------------------------
434 */
SMF_Close(S_EAS_DATA * pEASData,EAS_VOID_PTR pInstData)435 EAS_RESULT SMF_Close (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
436 {
437     S_SMF_DATA* pSMFData;
438     EAS_I32 i;
439     EAS_RESULT result;
440 
441     pSMFData = (S_SMF_DATA*) pInstData;
442 
443     /* close all the streams */
444     for (i = 0; i < pSMFData->numStreams; i++)
445     {
446         if (pSMFData->streams[i].fileHandle != NULL)
447         {
448             if ((result = EAS_HWCloseFile(pEASData->hwInstData, pSMFData->streams[i].fileHandle)) != EAS_SUCCESS)
449                 return result;
450         }
451     }
452     if (pSMFData->fileHandle != NULL)
453         if ((result = EAS_HWCloseFile(pEASData->hwInstData, pSMFData->fileHandle)) != EAS_SUCCESS)
454             return result;
455 
456     /* free the synth */
457     if (pSMFData->pSynth != NULL)
458         VMMIDIShutdown(pEASData, pSMFData->pSynth);
459 
460     /* if using dynamic memory, free it */
461     if (!pEASData->staticMemoryModel)
462     {
463         if (pSMFData->streams)
464             EAS_HWFree(pEASData->hwInstData, pSMFData->streams);
465 
466         /* free the instance data */
467         EAS_HWFree(pEASData->hwInstData, pSMFData);
468     }
469 
470     return EAS_SUCCESS;
471 }
472 
473 /*----------------------------------------------------------------------------
474  * SMF_Reset()
475  *----------------------------------------------------------------------------
476  * Purpose:
477  * Reset the sequencer. Used for locating backwards in the file.
478  *
479  * Inputs:
480  * pEASData         - pointer to overall EAS data structure
481  * handle           - pointer to file handle
482  *
483  * Outputs:
484  *
485  *
486  * Side Effects:
487  *
488  *----------------------------------------------------------------------------
489 */
SMF_Reset(S_EAS_DATA * pEASData,EAS_VOID_PTR pInstData)490 EAS_RESULT SMF_Reset (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
491 {
492     S_SMF_DATA* pSMFData;
493     EAS_I32 i;
494     EAS_RESULT result;
495     EAS_U32 ticks;
496 
497     pSMFData = (S_SMF_DATA*) pInstData;
498 
499     /* reset time to zero */
500     pSMFData->time = 0;
501 
502     /* reset the synth */
503     VMReset(pEASData->pVoiceMgr, pSMFData->pSynth, EAS_TRUE);
504 
505     /* find the start of each track */
506     ticks = 0x7fffffffL;
507     pSMFData->nextStream = NULL;
508     for (i = 0; i < pSMFData->numStreams; i++)
509     {
510 
511         /* reset file position to first byte of data in track */
512         if ((result = EAS_HWFileSeek(pEASData->hwInstData, pSMFData->streams[i].fileHandle, pSMFData->streams[i].startFilePos)) != EAS_SUCCESS)
513             return result;
514 
515         /* initalize some data */
516         pSMFData->streams[i].ticks = 0;
517 
518         /* initalize the MIDI parser data */
519         EAS_InitMIDIStream(&pSMFData->streams[i].midiStream);
520 
521         /* parse the first delta time in each stream */
522         if ((result = SMF_GetDeltaTime(pEASData->hwInstData,&pSMFData->streams[i])) != EAS_SUCCESS)
523             return result;
524         if (pSMFData->streams[i].ticks < ticks)
525         {
526             ticks = pSMFData->streams[i].ticks;
527             pSMFData->nextStream = &pSMFData->streams[i];
528         }
529     }
530 
531 
532     pSMFData->state = EAS_STATE_READY;
533     return EAS_SUCCESS;
534 }
535 
536 #ifdef JET_INTERFACE
537 /*----------------------------------------------------------------------------
538  * SMF_Pause()
539  *----------------------------------------------------------------------------
540  * Purpose:
541  * Pauses the sequencer. Mutes all voices and sets state to pause.
542  *
543  * Inputs:
544  * pEASData         - pointer to overall EAS data structure
545  * handle           - pointer to file handle
546  *
547  * Outputs:
548  *
549  *
550  * Side Effects:
551  *
552  *----------------------------------------------------------------------------
553 */
SMF_Pause(S_EAS_DATA * pEASData,EAS_VOID_PTR pInstData)554 EAS_RESULT SMF_Pause (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
555 {
556     S_SMF_DATA *pSMFData;
557 
558     /* can't pause a stopped stream */
559     pSMFData = (S_SMF_DATA*) pInstData;
560     if (pSMFData->state == EAS_STATE_STOPPED)
561         return EAS_ERROR_ALREADY_STOPPED;
562 
563     /* mute the synthesizer */
564     VMMuteAllVoices(pEASData->pVoiceMgr, pSMFData->pSynth);
565     pSMFData->state = EAS_STATE_PAUSING;
566     return EAS_SUCCESS;
567 }
568 
569 /*----------------------------------------------------------------------------
570  * SMF_Resume()
571  *----------------------------------------------------------------------------
572  * Purpose:
573  * Resume playing after a pause, sets state back to playing.
574  *
575  * Inputs:
576  * pEASData         - pointer to overall EAS data structure
577  * handle           - pointer to file handle
578  *
579  * Outputs:
580  *
581  *
582  * Side Effects:
583  *
584  *----------------------------------------------------------------------------
585 */
586 /*lint -esym(715, pEASData) reserved for future use */
SMF_Resume(S_EAS_DATA * pEASData,EAS_VOID_PTR pInstData)587 EAS_RESULT SMF_Resume (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
588 {
589     S_SMF_DATA *pSMFData;
590 
591     /* can't resume a stopped stream */
592     pSMFData = (S_SMF_DATA*) pInstData;
593     if (pSMFData->state == EAS_STATE_STOPPED)
594         return EAS_ERROR_ALREADY_STOPPED;
595 
596     /* nothing to do but resume playback */
597     pSMFData->state = EAS_STATE_PLAY;
598     return EAS_SUCCESS;
599 }
600 #endif
601 
602 /*----------------------------------------------------------------------------
603  * SMF_SetData()
604  *----------------------------------------------------------------------------
605  * Purpose:
606  * Sets parser parameters
607  *
608  * Inputs:
609  * pEASData         - pointer to overall EAS data structure
610  * handle           - pointer to file handle
611  *
612  * Outputs:
613  *
614  *
615  * Side Effects:
616  *
617  *----------------------------------------------------------------------------
618 */
619 /*lint -esym(715, pEASData) reserved for future use */
SMF_SetData(S_EAS_DATA * pEASData,EAS_VOID_PTR pInstData,EAS_I32 param,EAS_I32 value)620 EAS_RESULT SMF_SetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 value)
621 {
622     S_SMF_DATA *pSMFData;
623 
624     pSMFData = (S_SMF_DATA*) pInstData;
625     switch (param)
626     {
627 
628         /* set metadata callback */
629         case PARSER_DATA_METADATA_CB:
630             EAS_HWMemCpy(&pSMFData->metadata, (void*) value, sizeof(S_METADATA_CB));
631             break;
632 
633 #ifdef JET_INTERFACE
634         /* set jet segment and track ID of all tracks for callback function */
635         case PARSER_DATA_JET_CB:
636             {
637                 EAS_U32 i;
638                 EAS_U32 bit = (EAS_U32) value;
639                 bit = (bit << JET_EVENT_SEG_SHIFT) & JET_EVENT_SEG_MASK;
640                 for (i = 0; i < pSMFData->numStreams; i++)
641                     pSMFData->streams[i].midiStream.jetData =
642                         (pSMFData->streams[i].midiStream.jetData &
643                         ~(JET_EVENT_TRACK_MASK | JET_EVENT_SEG_MASK)) |
644                         i << JET_EVENT_TRACK_SHIFT | bit | MIDI_FLAGS_JET_CB;
645                 pSMFData->flags |= SMF_FLAGS_JET_STREAM;
646             }
647             break;
648 
649         /* set state of all mute flags at once */
650         case PARSER_DATA_MUTE_FLAGS:
651             {
652                 EAS_INT i;
653                 EAS_U32 bit = (EAS_U32) value;
654                 for (i = 0; i < pSMFData->numStreams; i++)
655                 {
656                     if (bit & 1)
657                         pSMFData->streams[i].midiStream.jetData |= MIDI_FLAGS_JET_MUTE;
658                     else
659                         pSMFData->streams[i].midiStream.jetData &= ~MIDI_FLAGS_JET_MUTE;
660                     bit >>= 1;
661                 }
662             }
663             break;
664 
665         /* set track mute */
666         case PARSER_DATA_SET_MUTE:
667             if (value < pSMFData->numStreams)
668                 pSMFData->streams[value].midiStream.jetData |= MIDI_FLAGS_JET_MUTE;
669             else
670                 return EAS_ERROR_PARAMETER_RANGE;
671             break;
672 
673         /* clear track mute */
674         case PARSER_DATA_CLEAR_MUTE:
675             if (value < pSMFData->numStreams)
676                 pSMFData->streams[value].midiStream.jetData &= ~MIDI_FLAGS_JET_MUTE;
677             else
678                 return EAS_ERROR_PARAMETER_RANGE;
679             break;
680 #endif
681 
682         default:
683             return EAS_ERROR_INVALID_PARAMETER;
684     }
685 
686     return EAS_SUCCESS;
687 }
688 
689 /*----------------------------------------------------------------------------
690  * SMF_GetData()
691  *----------------------------------------------------------------------------
692  * Purpose:
693  * Retrieves parser parameters
694  *
695  * Inputs:
696  * pEASData         - pointer to overall EAS data structure
697  * handle           - pointer to file handle
698  *
699  * Outputs:
700  *
701  *
702  * Side Effects:
703  *
704  *----------------------------------------------------------------------------
705 */
706 /*lint -esym(715, pEASData) reserved for future use */
SMF_GetData(S_EAS_DATA * pEASData,EAS_VOID_PTR pInstData,EAS_I32 param,EAS_I32 * pValue)707 EAS_RESULT SMF_GetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 *pValue)
708 {
709     S_SMF_DATA *pSMFData;
710 
711     pSMFData = (S_SMF_DATA*) pInstData;
712     switch (param)
713     {
714         /* return file type */
715         case PARSER_DATA_FILE_TYPE:
716             if (pSMFData->numStreams == 1)
717                 *pValue = EAS_FILE_SMF0;
718             else
719                 *pValue = EAS_FILE_SMF1;
720             break;
721 
722 /* now handled in eas_public.c */
723 #if 0
724         case PARSER_DATA_POLYPHONY:
725             if (pSMFData->pSynth)
726                 VMGetPolyphony(pEASData->pVoiceMgr, pSMFData->pSynth, pValue);
727             else
728                 return EAS_ERROR_NOT_VALID_IN_THIS_STATE;
729             break;
730 
731         case PARSER_DATA_PRIORITY:
732             if (pSMFData->pSynth)
733                 VMGetPriority(pEASData->pVoiceMgr, pSMFData->pSynth, pValue);
734             break;
735 
736         /* set transposition */
737         case PARSER_DATA_TRANSPOSITION:
738             *pValue = pSMFData->transposition;
739             break;
740 #endif
741 
742         case PARSER_DATA_SYNTH_HANDLE:
743             *pValue = (EAS_I32) pSMFData->pSynth;
744             break;
745 
746         default:
747             return EAS_ERROR_INVALID_PARAMETER;
748     }
749 
750     return EAS_SUCCESS;
751 }
752 
753 /*----------------------------------------------------------------------------
754  * SMF_GetVarLenData()
755  *----------------------------------------------------------------------------
756  * Purpose:
757  * Reads a varible length quantity from an SMF file
758  *
759  * Inputs:
760  *
761  *
762  * Outputs:
763  *
764  *
765  * Side Effects:
766  *
767  *----------------------------------------------------------------------------
768 */
SMF_GetVarLenData(EAS_HW_DATA_HANDLE hwInstData,EAS_FILE_HANDLE fileHandle,EAS_U32 * pData)769 static EAS_RESULT SMF_GetVarLenData (EAS_HW_DATA_HANDLE hwInstData, EAS_FILE_HANDLE fileHandle, EAS_U32 *pData)
770 {
771     EAS_RESULT result;
772     EAS_U32 data;
773     EAS_U8 c;
774 
775     /* read until bit 7 is zero */
776     data = 0;
777     do
778     {
779         if ((result = EAS_HWGetByte(hwInstData, fileHandle,&c)) != EAS_SUCCESS)
780             return result;
781         data = (data << 7) | (c & 0x7f);
782     } while (c & 0x80);
783     *pData = data;
784     return EAS_SUCCESS;
785 }
786 
787 /*----------------------------------------------------------------------------
788  * SMF_GetDeltaTime()
789  *----------------------------------------------------------------------------
790  * Purpose:
791  * Reads a varible length quantity from an SMF file
792  *
793  * Inputs:
794  *
795  *
796  * Outputs:
797  *
798  *
799  * Side Effects:
800  *
801  *----------------------------------------------------------------------------
802 */
SMF_GetDeltaTime(EAS_HW_DATA_HANDLE hwInstData,S_SMF_STREAM * pSMFStream)803 static EAS_RESULT SMF_GetDeltaTime (EAS_HW_DATA_HANDLE hwInstData, S_SMF_STREAM *pSMFStream)
804 {
805     EAS_RESULT result;
806     EAS_U32 ticks;
807 
808     if ((result = SMF_GetVarLenData(hwInstData, pSMFStream->fileHandle, &ticks)) != EAS_SUCCESS)
809         return result;
810 
811     /* number of ticks must not exceed 32-bits */
812     if (ticks > (UINT32_MAX - pSMFStream->ticks))
813         return EAS_ERROR_FILE_FORMAT;
814 
815     pSMFStream->ticks += ticks;
816     return EAS_SUCCESS;
817 }
818 
819 /*----------------------------------------------------------------------------
820  * SMF_ParseMetaEvent()
821  *----------------------------------------------------------------------------
822  * Purpose:
823  * Reads a varible length quantity from an SMF file
824  *
825  * Inputs:
826  *
827  *
828  * Outputs:
829  *
830  *
831  * Side Effects:
832  *
833  *----------------------------------------------------------------------------
834 */
SMF_ParseMetaEvent(S_EAS_DATA * pEASData,S_SMF_DATA * pSMFData,S_SMF_STREAM * pSMFStream)835 static EAS_RESULT SMF_ParseMetaEvent (S_EAS_DATA *pEASData, S_SMF_DATA *pSMFData, S_SMF_STREAM *pSMFStream)
836 {
837     EAS_RESULT result;
838     EAS_U32 len;
839     EAS_I32 pos;
840     EAS_U32 temp;
841     EAS_U8 c;
842 
843     /* get the meta-event type */
844     if ((result = EAS_HWGetByte(pEASData->hwInstData, pSMFStream->fileHandle, &c)) != EAS_SUCCESS)
845         return result;
846 
847     /* get the length */
848     if ((result = SMF_GetVarLenData(pEASData->hwInstData, pSMFStream->fileHandle, &len)) != EAS_SUCCESS)
849         return result;
850 
851     /* get the current file position so we can skip the event */
852     if ((result = EAS_HWFilePos(pEASData->hwInstData, pSMFStream->fileHandle, &pos)) != EAS_SUCCESS)
853         return result;
854 
855     /* prevent a large unsigned length from being treated as a negative length */
856     if ((EAS_I32) len < 0) {
857         /* note that EAS_I32 is a long, which can be 64-bits on some computers */
858         ALOGE("%s() negative len = %ld", __func__, (long) len);
859         android_errorWriteLog(0x534e4554, "68953854");
860         return EAS_ERROR_FILE_FORMAT;
861     }
862     /* prevent numeric overflow caused by a very large len, assume pos > 0 */
863     const EAS_I32 EAS_I32_MAX = 0x7FFFFFFF;
864     if ((EAS_I32) len > (EAS_I32_MAX - pos)) {
865         ALOGE("%s() too large len = %ld", __func__, (long) len);
866         android_errorWriteLog(0x534e4554, "68953854");
867         return EAS_ERROR_FILE_FORMAT;
868     }
869 
870     pos += (EAS_I32) len;
871 
872     /* end of track? */
873     if (c == SMF_META_END_OF_TRACK)
874     {
875         { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "Meta-event: end of track\n", c, len); */ }
876         pSMFStream->ticks = SMF_END_OF_TRACK;
877     }
878 
879     /* tempo event? */
880     else if (c == SMF_META_TEMPO)
881     {
882         /* read the 3-byte timebase value */
883         temp = 0;
884         while (len)
885         {
886             len--;
887             if ((result = EAS_HWGetByte(pEASData->hwInstData, pSMFStream->fileHandle, &c)) != EAS_SUCCESS)
888                 return result;
889             temp = (temp << 8) | c;
890         }
891         {
892             // pSMFData->tickConv = (EAS_U16) (((temp * 1024) / pSMFData->ppqn + 500) / 1000);
893             uint64_t temp64;
894             if (__builtin_mul_overflow(temp, 1024u, &temp64) ||
895                     pSMFData->ppqn == 0 ||
896                     (temp64 /= pSMFData->ppqn, false) ||
897                     __builtin_add_overflow(temp64, 500, &temp64) ||
898                     (temp64 /= 1000, false) ||
899                     temp64 > 65535) {
900                 pSMFData->tickConv = 65535;
901             } else {
902                 pSMFData->tickConv = (EAS_U16) temp64;
903             }
904         }
905         pSMFData->flags |= SMF_FLAGS_HAS_TEMPO;
906     }
907 
908     /* check for time signature - see iMelody spec V1.4 section 4.1.2.2.3.6 */
909     else if (c == SMF_META_TIME_SIGNATURE)
910     {
911         pSMFData->flags |= SMF_FLAGS_HAS_TIME_SIG;
912     }
913 
914     /* if the host has registered a metadata callback return the metadata */
915     else if (pSMFData->metadata.callback)
916     {
917         EAS_I32 readLen;
918         E_EAS_METADATA_TYPE metaType;
919 
920         metaType = EAS_METADATA_UNKNOWN;
921 
922         /* only process title on the first track */
923         if (c == SMF_META_SEQTRK_NAME)
924             metaType = EAS_METADATA_TITLE;
925         else if (c == SMF_META_TEXT)
926             metaType = EAS_METADATA_TEXT;
927         else if (c == SMF_META_COPYRIGHT)
928             metaType = EAS_METADATA_COPYRIGHT;
929         else if (c == SMF_META_LYRIC)
930             metaType = EAS_METADATA_LYRIC;
931 
932         if (metaType != EAS_METADATA_UNKNOWN)
933         {
934             readLen = pSMFData->metadata.bufferSize - 1;
935             if ((EAS_I32) len < readLen)
936                 readLen = (EAS_I32) len;
937             if ((result = EAS_HWReadFile(pEASData->hwInstData, pSMFStream->fileHandle, pSMFData->metadata.buffer, readLen, &readLen)) != EAS_SUCCESS)
938                 return result;
939             pSMFData->metadata.buffer[readLen] = 0;
940             pSMFData->metadata.callback(metaType, pSMFData->metadata.buffer, pSMFData->metadata.pUserData);
941         }
942     }
943 
944     /* position file to next event - in case we ignored all or part of the meta-event */
945     if ((result = EAS_HWFileSeek(pEASData->hwInstData, pSMFStream->fileHandle, pos)) != EAS_SUCCESS)
946         return result;
947 
948     { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "Meta-event: type=%02x, len=%d\n", c, len); */ }
949     return EAS_SUCCESS;
950 }
951 
952 /*----------------------------------------------------------------------------
953  * SMF_ParseSysEx()
954  *----------------------------------------------------------------------------
955  * Purpose:
956  * Reads a varible length quantity from an SMF file
957  *
958  * Inputs:
959  *
960  *
961  * Outputs:
962  *
963  *
964  * Side Effects:
965  *
966  *----------------------------------------------------------------------------
967 */
SMF_ParseSysEx(S_EAS_DATA * pEASData,S_SMF_DATA * pSMFData,S_SMF_STREAM * pSMFStream,EAS_U8 f0,EAS_INT parserMode)968 static EAS_RESULT SMF_ParseSysEx (S_EAS_DATA *pEASData, S_SMF_DATA *pSMFData, S_SMF_STREAM *pSMFStream, EAS_U8 f0, EAS_INT parserMode)
969 {
970     EAS_RESULT result;
971     EAS_U32 len;
972     EAS_U8 c;
973 
974     /* get the length */
975     if ((result = SMF_GetVarLenData(pEASData->hwInstData, pSMFStream->fileHandle, &len)) != EAS_SUCCESS)
976         return result;
977 
978     /* start of SysEx message? */
979     if (f0 == 0xf0)
980     {
981         if ((result = EAS_ParseMIDIStream(pEASData, pSMFData->pSynth, &pSMFStream->midiStream, f0, parserMode)) != EAS_SUCCESS)
982             return result;
983     }
984 
985     /* feed the SysEx to the stream parser */
986     while (len)
987     {
988         len--;
989         if ((result = EAS_HWGetByte(pEASData->hwInstData, pSMFStream->fileHandle, &c)) != EAS_SUCCESS)
990             return result;
991         if ((result = EAS_ParseMIDIStream(pEASData, pSMFData->pSynth, &pSMFStream->midiStream, c, parserMode)) != EAS_SUCCESS)
992             return result;
993 
994         /* check for GM system ON */
995         if (pSMFStream->midiStream.flags & MIDI_FLAG_GM_ON)
996             pSMFData->flags |= SMF_FLAGS_HAS_GM_ON;
997     }
998 
999     return EAS_SUCCESS;
1000 }
1001 
1002 /*----------------------------------------------------------------------------
1003  * SMF_ParseEvent()
1004  *----------------------------------------------------------------------------
1005  * Purpose:
1006  * Reads a varible length quantity from an SMF file
1007  *
1008  * Inputs:
1009  *
1010  *
1011  * Outputs:
1012  *
1013  *
1014  * Side Effects:
1015  *
1016  *----------------------------------------------------------------------------
1017 */
SMF_ParseEvent(S_EAS_DATA * pEASData,S_SMF_DATA * pSMFData,S_SMF_STREAM * pSMFStream,EAS_INT parserMode)1018 static EAS_RESULT SMF_ParseEvent (S_EAS_DATA *pEASData, S_SMF_DATA *pSMFData, S_SMF_STREAM *pSMFStream, EAS_INT parserMode)
1019 {
1020     EAS_RESULT result;
1021     EAS_U8 c;
1022 
1023     /* get the event type */
1024     if ((result = EAS_HWGetByte(pEASData->hwInstData, pSMFStream->fileHandle, &c)) != EAS_SUCCESS)
1025         return result;
1026 
1027     /* parse meta-event */
1028     if (c == 0xff)
1029     {
1030         if ((result = SMF_ParseMetaEvent(pEASData, pSMFData, pSMFStream)) != EAS_SUCCESS)
1031             return result;
1032     }
1033 
1034     /* parse SysEx */
1035     else if ((c == 0xf0) || (c == 0xf7))
1036     {
1037         if ((result = SMF_ParseSysEx(pEASData, pSMFData, pSMFStream, c, parserMode)) != EAS_SUCCESS)
1038             return result;
1039     }
1040 
1041     /* parse MIDI message */
1042     else
1043     {
1044         if ((result = EAS_ParseMIDIStream(pEASData, pSMFData->pSynth, &pSMFStream->midiStream, c, parserMode)) != EAS_SUCCESS)
1045             return result;
1046 
1047         /* keep streaming data to the MIDI parser until the message is complete */
1048         while (pSMFStream->midiStream.pending)
1049         {
1050             if ((result = EAS_HWGetByte(pEASData->hwInstData, pSMFStream->fileHandle, &c)) != EAS_SUCCESS)
1051                 return result;
1052             if ((result = EAS_ParseMIDIStream(pEASData, pSMFData->pSynth, &pSMFStream->midiStream, c, parserMode)) != EAS_SUCCESS)
1053                 return result;
1054         }
1055 
1056     }
1057 
1058     /* chase mode logic */
1059     if (pSMFData->time == 0)
1060     {
1061         if (pSMFData->flags & SMF_FLAGS_CHASE_MODE)
1062         {
1063             if (pSMFStream->midiStream.flags & MIDI_FLAG_FIRST_NOTE)
1064                 pSMFData->flags &= ~SMF_FLAGS_CHASE_MODE;
1065         }
1066         else if ((pSMFData->flags & SMF_FLAGS_SETUP_BAR) == SMF_FLAGS_SETUP_BAR)
1067             pSMFData->flags = (pSMFData->flags & ~SMF_FLAGS_SETUP_BAR) | SMF_FLAGS_CHASE_MODE;
1068     }
1069 
1070     return EAS_SUCCESS;
1071 }
1072 
1073 /*----------------------------------------------------------------------------
1074  * SMF_ParseHeader()
1075  *----------------------------------------------------------------------------
1076  * Purpose:
1077  * Parses the header of an SMF file, allocates memory the stream parsers and initializes the
1078  * stream parsers.
1079  *
1080  * Inputs:
1081  * pEASData         - pointer to overall EAS data structure
1082  * pSMFData         - pointer to parser instance data
1083  * fileHandle       - file handle
1084  * fileOffset       - offset in the file where the header data starts, usually 0
1085  *
1086  *
1087  * Outputs:
1088  *
1089  *
1090  * Side Effects:
1091  *
1092  *----------------------------------------------------------------------------
1093 */
1094 /*lint -e{801} we know that 'goto' is deprecated - but it's cleaner in this case */
SMF_ParseHeader(EAS_HW_DATA_HANDLE hwInstData,S_SMF_DATA * pSMFData)1095 EAS_RESULT SMF_ParseHeader (EAS_HW_DATA_HANDLE hwInstData, S_SMF_DATA *pSMFData)
1096 {
1097     EAS_RESULT result;
1098     EAS_I32 i;
1099     EAS_U16 division;
1100     EAS_U16 numStreams;
1101     EAS_U32 chunkSize;
1102     EAS_U32 chunkStart;
1103     EAS_U32 temp;
1104     EAS_U32 ticks;
1105 
1106     /* explicitly set numStreams to 0. It will later be used by SMF_Close to
1107      * determine whether we have valid streams or not. */
1108     pSMFData->numStreams = 0;
1109 
1110     /* rewind the file and find the end of the header chunk */
1111     if ((result = EAS_HWFileSeek(hwInstData, pSMFData->fileHandle, pSMFData->fileOffset + SMF_OFS_HEADER_SIZE)) != EAS_SUCCESS)
1112         goto ReadError;
1113     if ((result = EAS_HWGetDWord(hwInstData, pSMFData->fileHandle, &chunkSize, EAS_TRUE)) != EAS_SUCCESS)
1114         goto ReadError;
1115 
1116     /* determine the number of tracks */
1117     if ((result = EAS_HWFileSeek(hwInstData, pSMFData->fileHandle, pSMFData->fileOffset + SMF_OFS_NUM_TRACKS)) != EAS_SUCCESS)
1118         goto ReadError;
1119     if ((result = EAS_HWGetWord(hwInstData, pSMFData->fileHandle, &numStreams, EAS_TRUE)) != EAS_SUCCESS)
1120         goto ReadError;
1121 
1122     /* limit the number of tracks */
1123     if (numStreams > MAX_SMF_STREAMS)
1124     {
1125         { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "SMF file contains %u tracks, playing %d tracks\n", numStreams, MAX_SMF_STREAMS); */ }
1126         numStreams = MAX_SMF_STREAMS;
1127     } else if (numStreams == 0)
1128     {
1129         /* avoid 0 sized allocation */
1130         return EAS_ERROR_PARAMETER_RANGE;
1131     }
1132 
1133     /* get the time division */
1134     if ((result = EAS_HWGetWord(hwInstData, pSMFData->fileHandle, &division, EAS_TRUE)) != EAS_SUCCESS)
1135         goto ReadError;
1136 
1137     /* setup default timebase for 120 bpm */
1138     pSMFData->ppqn = 192;
1139     if (!division || division & 0x8000)
1140         { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING,"No support for SMPTE code timebase\n"); */ }
1141     else
1142         pSMFData->ppqn = (division & 0x7fff);
1143     pSMFData->tickConv = (EAS_U16) (((SMF_DEFAULT_TIMEBASE * 1024) / pSMFData->ppqn + 500) / 1000);
1144 
1145     /* dynamic memory allocation, allocate memory for streams */
1146     if (pSMFData->streams == NULL)
1147     {
1148         pSMFData->streams = EAS_HWMalloc(hwInstData,sizeof(S_SMF_STREAM) * numStreams);
1149         if (pSMFData->streams == NULL)
1150             return EAS_ERROR_MALLOC_FAILED;
1151 
1152         /* zero the memory to insure complete initialization */
1153         EAS_HWMemSet((void *)(pSMFData->streams), 0, sizeof(S_SMF_STREAM) * numStreams);
1154     }
1155     pSMFData->numStreams = numStreams;
1156 
1157     /* find the start of each track */
1158     chunkStart = (EAS_U32) pSMFData->fileOffset;
1159     ticks = 0x7fffffffL;
1160     pSMFData->nextStream = NULL;
1161     for (i = 0; i < pSMFData->numStreams; i++)
1162     {
1163 
1164         for (;;)
1165         {
1166 
1167             /* calculate start of next chunk - checking for errors */
1168             temp = chunkStart + SMF_CHUNK_INFO_SIZE + chunkSize;
1169             if (temp <= chunkStart)
1170             {
1171                 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING,"Error in chunk size at offset %d\n", chunkStart); */ }
1172                 return EAS_ERROR_FILE_FORMAT;
1173             }
1174             chunkStart = temp;
1175 
1176             /* seek to the start of the next chunk */
1177             if ((result = EAS_HWFileSeek(hwInstData, pSMFData->fileHandle, (EAS_I32) chunkStart)) != EAS_SUCCESS)
1178                 goto ReadError;
1179 
1180             /* read the chunk identifier */
1181             if ((result = EAS_HWGetDWord(hwInstData, pSMFData->fileHandle, &temp, EAS_TRUE)) != EAS_SUCCESS)
1182                 goto ReadError;
1183 
1184             /* read the chunk size */
1185             if ((result = EAS_HWGetDWord(hwInstData, pSMFData->fileHandle, &chunkSize, EAS_TRUE)) != EAS_SUCCESS)
1186                 goto ReadError;
1187 
1188             /* make sure this is an 'MTrk' chunk */
1189             if (temp == SMF_CHUNK_TYPE_TRACK)
1190                 break;
1191 
1192             { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING,"Unexpected chunk type: 0x%08x\n", temp); */ }
1193         }
1194 
1195         /* initalize some data */
1196         pSMFData->streams[i].ticks = 0;
1197         pSMFData->streams[i].fileHandle = pSMFData->fileHandle;
1198 
1199         /* NULL the file handle so we don't try to close it twice */
1200         pSMFData->fileHandle = NULL;
1201 
1202         /* save this file position as the start of the track */
1203         pSMFData->streams[i].startFilePos = (EAS_I32) chunkStart + SMF_CHUNK_INFO_SIZE;
1204 
1205         /* initalize the MIDI parser data */
1206         EAS_InitMIDIStream(&pSMFData->streams[i].midiStream);
1207 
1208         /* parse the first delta time in each stream */
1209         if ((result = SMF_GetDeltaTime(hwInstData, &pSMFData->streams[i])) != EAS_SUCCESS)
1210                 goto ReadError;
1211 
1212         if (pSMFData->streams[i].ticks < ticks)
1213         {
1214             ticks = pSMFData->streams[i].ticks;
1215             pSMFData->nextStream = &pSMFData->streams[i];
1216         }
1217 
1218         /* more tracks to do, create a duplicate file handle */
1219         if (i < (pSMFData->numStreams - 1))
1220         {
1221             if ((result = EAS_HWDupHandle(hwInstData, pSMFData->streams[i].fileHandle, &pSMFData->fileHandle)) != EAS_SUCCESS)
1222                 goto ReadError;
1223         }
1224     }
1225 
1226     /* update the time of the next event */
1227     if (pSMFData->nextStream)
1228         SMF_UpdateTime(pSMFData, pSMFData->nextStream->ticks);
1229 
1230     return EAS_SUCCESS;
1231 
1232     /* ugly goto: but simpler than structured */
1233     ReadError:
1234         if (result == EAS_EOF)
1235             return EAS_ERROR_FILE_FORMAT;
1236         return result;
1237 }
1238 
1239 /*----------------------------------------------------------------------------
1240  * SMF_UpdateTime()
1241  *----------------------------------------------------------------------------
1242  * Purpose:
1243  * Update the millisecond time base by converting the ticks into millieconds
1244  *
1245  * Inputs:
1246  *
1247  *
1248  * Outputs:
1249  *
1250  *
1251  * Side Effects:
1252  *
1253  *----------------------------------------------------------------------------
1254 */
SMF_UpdateTime(S_SMF_DATA * pSMFData,EAS_U32 ticks)1255 static void SMF_UpdateTime (S_SMF_DATA *pSMFData, EAS_U32 ticks)
1256 {
1257     EAS_U32 temp1, temp2;
1258 
1259     if (pSMFData->flags & SMF_FLAGS_CHASE_MODE)
1260         return;
1261 
1262     temp1 = (ticks >> 10) * pSMFData->tickConv;
1263     temp2 = (ticks & 0x3ff) * pSMFData->tickConv;
1264     pSMFData->time += (EAS_I32)((temp1 << 8) + (temp2 >> 2));
1265 }
1266 
1267