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