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     if (!pSMFData->nextStream) {
285         return EAS_ERROR_FILE_FORMAT;
286     }
287 
288 
289     /* get current ticks */
290     ticks = pSMFData->nextStream->ticks;
291 
292     /* assume that an error occurred */
293     pSMFData->state = EAS_STATE_ERROR;
294 
295 #ifdef JET_INTERFACE
296     /* if JET has track muted, set parser mode to mute */
297     if (pSMFData->nextStream->midiStream.jetData & MIDI_FLAGS_JET_MUTE)
298         parserMode = eParserModeMute;
299 #endif
300 
301     /* parse the next event from all the streams */
302     if ((result = SMF_ParseEvent(pEASData, pSMFData, pSMFData->nextStream, parserMode)) != EAS_SUCCESS)
303     {
304         /* check for unexpected end-of-file */
305         if (result != EAS_EOF)
306             return result;
307 
308         /* indicate end of track for this stream */
309         pSMFData->nextStream->ticks = SMF_END_OF_TRACK;
310     }
311 
312     /* get next delta time, unless already at end of track */
313     else if (pSMFData->nextStream->ticks != SMF_END_OF_TRACK)
314     {
315         if ((result = SMF_GetDeltaTime(pEASData->hwInstData, pSMFData->nextStream)) != EAS_SUCCESS)
316         {
317             /* check for unexpected end-of-file */
318             if (result != EAS_EOF)
319                 return result;
320 
321             /* indicate end of track for this stream */
322             pSMFData->nextStream->ticks = SMF_END_OF_TRACK;
323         }
324 
325         /* if zero delta to next event, stay with this stream */
326         else if (pSMFData->nextStream->ticks == ticks)
327         {
328             pSMFData->state = EAS_STATE_PLAY;
329             return EAS_SUCCESS;
330         }
331     }
332 
333     /* find next event in all streams */
334     temp = 0x7ffffff;
335     pSMFData->nextStream = NULL;
336     for (i = 0; i < pSMFData->numStreams; i++)
337     {
338         if (pSMFData->streams[i].ticks < temp)
339         {
340             temp = pSMFData->streams[i].ticks;
341             pSMFData->nextStream = &pSMFData->streams[i];
342         }
343     }
344 
345     /* are there any more events to parse? */
346     if (pSMFData->nextStream)
347     {
348         pSMFData->state = EAS_STATE_PLAY;
349 
350         /* update the time of the next event */
351         SMF_UpdateTime(pSMFData, pSMFData->nextStream->ticks - ticks);
352     }
353     else
354     {
355         pSMFData->state = EAS_STATE_STOPPING;
356         VMReleaseAllVoices(pEASData->pVoiceMgr, pSMFData->pSynth);
357     }
358 
359     return EAS_SUCCESS;
360 }
361 
362 /*----------------------------------------------------------------------------
363  * SMF_State()
364  *----------------------------------------------------------------------------
365  * Purpose:
366  * Returns the current state of the stream
367  *
368  * Inputs:
369  * pEASData         - pointer to overall EAS data structure
370  * handle           - pointer to file handle
371  * pState           - pointer to variable to store state
372  *
373  * Outputs:
374  *
375  *
376  * Side Effects:
377  *
378  *----------------------------------------------------------------------------
379 */
380 /*lint -esym(715, pEASData) reserved for future use */
SMF_State(S_EAS_DATA * pEASData,EAS_VOID_PTR pInstData,EAS_I32 * pState)381 EAS_RESULT SMF_State (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 *pState)
382 {
383     S_SMF_DATA* pSMFData;
384 
385     /* establish pointer to instance data */
386     pSMFData = (S_SMF_DATA*) pInstData;
387 
388     /* if stopping, check to see if synth voices are active */
389     if (pSMFData->state == EAS_STATE_STOPPING)
390     {
391         if (VMActiveVoices(pSMFData->pSynth) == 0)
392             pSMFData->state = EAS_STATE_STOPPED;
393     }
394 
395     if (pSMFData->state == EAS_STATE_PAUSING)
396     {
397         if (VMActiveVoices(pSMFData->pSynth) == 0)
398             pSMFData->state = EAS_STATE_PAUSED;
399     }
400 
401     /* return current state */
402     *pState = pSMFData->state;
403     return EAS_SUCCESS;
404 }
405 
406 /*----------------------------------------------------------------------------
407  * SMF_Close()
408  *----------------------------------------------------------------------------
409  * Purpose:
410  * Close the file and clean up
411  *
412  * Inputs:
413  * pEASData         - pointer to overall EAS data structure
414  * handle           - pointer to file handle
415  *
416  * Outputs:
417  *
418  *
419  * Side Effects:
420  *
421  *----------------------------------------------------------------------------
422 */
SMF_Close(S_EAS_DATA * pEASData,EAS_VOID_PTR pInstData)423 EAS_RESULT SMF_Close (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
424 {
425     S_SMF_DATA* pSMFData;
426     EAS_I32 i;
427     EAS_RESULT result;
428 
429     pSMFData = (S_SMF_DATA*) pInstData;
430 
431     /* close all the streams */
432     for (i = 0; i < pSMFData->numStreams; i++)
433     {
434         if (pSMFData->streams[i].fileHandle != NULL)
435         {
436             if ((result = EAS_HWCloseFile(pEASData->hwInstData, pSMFData->streams[i].fileHandle)) != EAS_SUCCESS)
437                 return result;
438         }
439     }
440     if (pSMFData->fileHandle != NULL)
441         if ((result = EAS_HWCloseFile(pEASData->hwInstData, pSMFData->fileHandle)) != EAS_SUCCESS)
442             return result;
443 
444     /* free the synth */
445     if (pSMFData->pSynth != NULL)
446         VMMIDIShutdown(pEASData, pSMFData->pSynth);
447 
448     /* if using dynamic memory, free it */
449     if (!pEASData->staticMemoryModel)
450     {
451         if (pSMFData->streams)
452             EAS_HWFree(pEASData->hwInstData, pSMFData->streams);
453 
454         /* free the instance data */
455         EAS_HWFree(pEASData->hwInstData, pSMFData);
456     }
457 
458     return EAS_SUCCESS;
459 }
460 
461 /*----------------------------------------------------------------------------
462  * SMF_Reset()
463  *----------------------------------------------------------------------------
464  * Purpose:
465  * Reset the sequencer. Used for locating backwards in the file.
466  *
467  * Inputs:
468  * pEASData         - pointer to overall EAS data structure
469  * handle           - pointer to file handle
470  *
471  * Outputs:
472  *
473  *
474  * Side Effects:
475  *
476  *----------------------------------------------------------------------------
477 */
SMF_Reset(S_EAS_DATA * pEASData,EAS_VOID_PTR pInstData)478 EAS_RESULT SMF_Reset (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
479 {
480     S_SMF_DATA* pSMFData;
481     EAS_I32 i;
482     EAS_RESULT result;
483     EAS_U32 ticks;
484 
485     pSMFData = (S_SMF_DATA*) pInstData;
486 
487     /* reset time to zero */
488     pSMFData->time = 0;
489 
490     /* reset the synth */
491     VMReset(pEASData->pVoiceMgr, pSMFData->pSynth, EAS_TRUE);
492 
493     /* find the start of each track */
494     ticks = 0x7fffffffL;
495     pSMFData->nextStream = NULL;
496     for (i = 0; i < pSMFData->numStreams; i++)
497     {
498 
499         /* reset file position to first byte of data in track */
500         if ((result = EAS_HWFileSeek(pEASData->hwInstData, pSMFData->streams[i].fileHandle, pSMFData->streams[i].startFilePos)) != EAS_SUCCESS)
501             return result;
502 
503         /* initalize some data */
504         pSMFData->streams[i].ticks = 0;
505 
506         /* initalize the MIDI parser data */
507         EAS_InitMIDIStream(&pSMFData->streams[i].midiStream);
508 
509         /* parse the first delta time in each stream */
510         if ((result = SMF_GetDeltaTime(pEASData->hwInstData,&pSMFData->streams[i])) != EAS_SUCCESS)
511             return result;
512         if (pSMFData->streams[i].ticks < ticks)
513         {
514             ticks = pSMFData->streams[i].ticks;
515             pSMFData->nextStream = &pSMFData->streams[i];
516         }
517     }
518 
519 
520     pSMFData->state = EAS_STATE_READY;
521     return EAS_SUCCESS;
522 }
523 
524 /*----------------------------------------------------------------------------
525  * SMF_Pause()
526  *----------------------------------------------------------------------------
527  * Purpose:
528  * Pauses the sequencer. Mutes all voices and sets state to pause.
529  *
530  * Inputs:
531  * pEASData         - pointer to overall EAS data structure
532  * handle           - pointer to file handle
533  *
534  * Outputs:
535  *
536  *
537  * Side Effects:
538  *
539  *----------------------------------------------------------------------------
540 */
SMF_Pause(S_EAS_DATA * pEASData,EAS_VOID_PTR pInstData)541 EAS_RESULT SMF_Pause (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
542 {
543     S_SMF_DATA *pSMFData;
544 
545     /* can't pause a stopped stream */
546     pSMFData = (S_SMF_DATA*) pInstData;
547     if (pSMFData->state == EAS_STATE_STOPPED)
548         return EAS_ERROR_ALREADY_STOPPED;
549 
550     /* mute the synthesizer */
551     VMMuteAllVoices(pEASData->pVoiceMgr, pSMFData->pSynth);
552     pSMFData->state = EAS_STATE_PAUSING;
553     return EAS_SUCCESS;
554 }
555 
556 /*----------------------------------------------------------------------------
557  * SMF_Resume()
558  *----------------------------------------------------------------------------
559  * Purpose:
560  * Resume playing after a pause, sets state back to playing.
561  *
562  * Inputs:
563  * pEASData         - pointer to overall EAS data structure
564  * handle           - pointer to file handle
565  *
566  * Outputs:
567  *
568  *
569  * Side Effects:
570  *
571  *----------------------------------------------------------------------------
572 */
573 /*lint -esym(715, pEASData) reserved for future use */
SMF_Resume(S_EAS_DATA * pEASData,EAS_VOID_PTR pInstData)574 EAS_RESULT SMF_Resume (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
575 {
576     S_SMF_DATA *pSMFData;
577 
578     /* can't resume a stopped stream */
579     pSMFData = (S_SMF_DATA*) pInstData;
580     if (pSMFData->state == EAS_STATE_STOPPED)
581         return EAS_ERROR_ALREADY_STOPPED;
582 
583     /* nothing to do but resume playback */
584     pSMFData->state = EAS_STATE_PLAY;
585     return EAS_SUCCESS;
586 }
587 
588 /*----------------------------------------------------------------------------
589  * SMF_SetData()
590  *----------------------------------------------------------------------------
591  * Purpose:
592  * Sets parser parameters
593  *
594  * Inputs:
595  * pEASData         - pointer to overall EAS data structure
596  * handle           - pointer to file handle
597  *
598  * Outputs:
599  *
600  *
601  * Side Effects:
602  *
603  *----------------------------------------------------------------------------
604 */
605 /*lint -esym(715, pEASData) reserved for future use */
SMF_SetData(S_EAS_DATA * pEASData,EAS_VOID_PTR pInstData,EAS_I32 param,EAS_I32 value)606 EAS_RESULT SMF_SetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 value)
607 {
608     S_SMF_DATA *pSMFData;
609 
610     pSMFData = (S_SMF_DATA*) pInstData;
611     switch (param)
612     {
613 
614         /* set metadata callback */
615         case PARSER_DATA_METADATA_CB:
616             EAS_HWMemCpy(&pSMFData->metadata, (void*) value, sizeof(S_METADATA_CB));
617             break;
618 
619 #ifdef JET_INTERFACE
620         /* set jet segment and track ID of all tracks for callback function */
621         case PARSER_DATA_JET_CB:
622             {
623                 EAS_U32 i;
624                 EAS_U32 bit = (EAS_U32) value;
625                 bit = (bit << JET_EVENT_SEG_SHIFT) & JET_EVENT_SEG_MASK;
626                 for (i = 0; i < pSMFData->numStreams; i++)
627                     pSMFData->streams[i].midiStream.jetData =
628                         (pSMFData->streams[i].midiStream.jetData &
629                         ~(JET_EVENT_TRACK_MASK | JET_EVENT_SEG_MASK)) |
630                         i << JET_EVENT_TRACK_SHIFT | bit | MIDI_FLAGS_JET_CB;
631                 pSMFData->flags |= SMF_FLAGS_JET_STREAM;
632             }
633             break;
634 
635         /* set state of all mute flags at once */
636         case PARSER_DATA_MUTE_FLAGS:
637             {
638                 EAS_INT i;
639                 EAS_U32 bit = (EAS_U32) value;
640                 for (i = 0; i < pSMFData->numStreams; i++)
641                 {
642                     if (bit & 1)
643                         pSMFData->streams[i].midiStream.jetData |= MIDI_FLAGS_JET_MUTE;
644                     else
645                         pSMFData->streams[i].midiStream.jetData &= ~MIDI_FLAGS_JET_MUTE;
646                     bit >>= 1;
647                 }
648             }
649             break;
650 
651         /* set track mute */
652         case PARSER_DATA_SET_MUTE:
653             if (value < pSMFData->numStreams)
654                 pSMFData->streams[value].midiStream.jetData |= MIDI_FLAGS_JET_MUTE;
655             else
656                 return EAS_ERROR_PARAMETER_RANGE;
657             break;
658 
659         /* clear track mute */
660         case PARSER_DATA_CLEAR_MUTE:
661             if (value < pSMFData->numStreams)
662                 pSMFData->streams[value].midiStream.jetData &= ~MIDI_FLAGS_JET_MUTE;
663             else
664                 return EAS_ERROR_PARAMETER_RANGE;
665             break;
666 #endif
667 
668         default:
669             return EAS_ERROR_INVALID_PARAMETER;
670     }
671 
672     return EAS_SUCCESS;
673 }
674 
675 /*----------------------------------------------------------------------------
676  * SMF_GetData()
677  *----------------------------------------------------------------------------
678  * Purpose:
679  * Retrieves parser parameters
680  *
681  * Inputs:
682  * pEASData         - pointer to overall EAS data structure
683  * handle           - pointer to file handle
684  *
685  * Outputs:
686  *
687  *
688  * Side Effects:
689  *
690  *----------------------------------------------------------------------------
691 */
692 /*lint -esym(715, pEASData) reserved for future use */
SMF_GetData(S_EAS_DATA * pEASData,EAS_VOID_PTR pInstData,EAS_I32 param,EAS_I32 * pValue)693 EAS_RESULT SMF_GetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 *pValue)
694 {
695     S_SMF_DATA *pSMFData;
696 
697     pSMFData = (S_SMF_DATA*) pInstData;
698     switch (param)
699     {
700         /* return file type */
701         case PARSER_DATA_FILE_TYPE:
702             if (pSMFData->numStreams == 1)
703                 *pValue = EAS_FILE_SMF0;
704             else
705                 *pValue = EAS_FILE_SMF1;
706             break;
707 
708 /* now handled in eas_public.c */
709 #if 0
710         case PARSER_DATA_POLYPHONY:
711             if (pSMFData->pSynth)
712                 VMGetPolyphony(pEASData->pVoiceMgr, pSMFData->pSynth, pValue);
713             else
714                 return EAS_ERROR_NOT_VALID_IN_THIS_STATE;
715             break;
716 
717         case PARSER_DATA_PRIORITY:
718             if (pSMFData->pSynth)
719                 VMGetPriority(pEASData->pVoiceMgr, pSMFData->pSynth, pValue);
720             break;
721 
722         /* set transposition */
723         case PARSER_DATA_TRANSPOSITION:
724             *pValue = pSMFData->transposition;
725             break;
726 #endif
727 
728         case PARSER_DATA_SYNTH_HANDLE:
729             *pValue = (EAS_I32) pSMFData->pSynth;
730             break;
731 
732         default:
733             return EAS_ERROR_INVALID_PARAMETER;
734     }
735 
736     return EAS_SUCCESS;
737 }
738 
739 /*----------------------------------------------------------------------------
740  * SMF_GetVarLenData()
741  *----------------------------------------------------------------------------
742  * Purpose:
743  * Reads a varible length quantity from an SMF file
744  *
745  * Inputs:
746  *
747  *
748  * Outputs:
749  *
750  *
751  * Side Effects:
752  *
753  *----------------------------------------------------------------------------
754 */
SMF_GetVarLenData(EAS_HW_DATA_HANDLE hwInstData,EAS_FILE_HANDLE fileHandle,EAS_U32 * pData)755 static EAS_RESULT SMF_GetVarLenData (EAS_HW_DATA_HANDLE hwInstData, EAS_FILE_HANDLE fileHandle, EAS_U32 *pData)
756 {
757     EAS_RESULT result;
758     EAS_U32 data;
759     EAS_U8 c;
760 
761     /* read until bit 7 is zero */
762     data = 0;
763     do
764     {
765         if ((result = EAS_HWGetByte(hwInstData, fileHandle,&c)) != EAS_SUCCESS)
766             return result;
767         data = (data << 7) | (c & 0x7f);
768     } while (c & 0x80);
769     *pData = data;
770     return EAS_SUCCESS;
771 }
772 
773 /*----------------------------------------------------------------------------
774  * SMF_GetDeltaTime()
775  *----------------------------------------------------------------------------
776  * Purpose:
777  * Reads a varible length quantity from an SMF file
778  *
779  * Inputs:
780  *
781  *
782  * Outputs:
783  *
784  *
785  * Side Effects:
786  *
787  *----------------------------------------------------------------------------
788 */
SMF_GetDeltaTime(EAS_HW_DATA_HANDLE hwInstData,S_SMF_STREAM * pSMFStream)789 static EAS_RESULT SMF_GetDeltaTime (EAS_HW_DATA_HANDLE hwInstData, S_SMF_STREAM *pSMFStream)
790 {
791     EAS_RESULT result;
792     EAS_U32 ticks;
793 
794     if ((result = SMF_GetVarLenData(hwInstData, pSMFStream->fileHandle, &ticks)) != EAS_SUCCESS)
795         return result;
796 
797     pSMFStream->ticks += ticks;
798     return EAS_SUCCESS;
799 }
800 
801 /*----------------------------------------------------------------------------
802  * SMF_ParseMetaEvent()
803  *----------------------------------------------------------------------------
804  * Purpose:
805  * Reads a varible length quantity from an SMF file
806  *
807  * Inputs:
808  *
809  *
810  * Outputs:
811  *
812  *
813  * Side Effects:
814  *
815  *----------------------------------------------------------------------------
816 */
SMF_ParseMetaEvent(S_EAS_DATA * pEASData,S_SMF_DATA * pSMFData,S_SMF_STREAM * pSMFStream)817 static EAS_RESULT SMF_ParseMetaEvent (S_EAS_DATA *pEASData, S_SMF_DATA *pSMFData, S_SMF_STREAM *pSMFStream)
818 {
819     EAS_RESULT result;
820     EAS_U32 len;
821     EAS_I32 pos;
822     EAS_U32 temp;
823     EAS_U8 c;
824 
825     /* get the meta-event type */
826     if ((result = EAS_HWGetByte(pEASData->hwInstData, pSMFStream->fileHandle, &c)) != EAS_SUCCESS)
827         return result;
828 
829     /* get the length */
830     if ((result = SMF_GetVarLenData(pEASData->hwInstData, pSMFStream->fileHandle, &len)) != EAS_SUCCESS)
831         return result;
832 
833     /* get the current file position so we can skip the event */
834     if ((result = EAS_HWFilePos(pEASData->hwInstData, pSMFStream->fileHandle, &pos)) != EAS_SUCCESS)
835         return result;
836     pos += (EAS_I32) len;
837 
838     /* end of track? */
839     if (c == SMF_META_END_OF_TRACK)
840     {
841         { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "Meta-event: end of track\n", c, len); */ }
842         pSMFStream->ticks = SMF_END_OF_TRACK;
843     }
844 
845     /* tempo event? */
846     else if (c == SMF_META_TEMPO)
847     {
848         /* read the 3-byte timebase value */
849         temp = 0;
850         while (len--)
851         {
852             if ((result = EAS_HWGetByte(pEASData->hwInstData, pSMFStream->fileHandle, &c)) != EAS_SUCCESS)
853                 return result;
854             temp = (temp << 8) | c;
855         }
856 
857         pSMFData->tickConv = (EAS_U16) (((temp * 1024) / pSMFData->ppqn + 500) / 1000);
858         pSMFData->flags |= SMF_FLAGS_HAS_TEMPO;
859     }
860 
861     /* check for time signature - see iMelody spec V1.4 section 4.1.2.2.3.6 */
862     else if (c == SMF_META_TIME_SIGNATURE)
863     {
864         pSMFData->flags |= SMF_FLAGS_HAS_TIME_SIG;
865     }
866 
867     /* if the host has registered a metadata callback return the metadata */
868     else if (pSMFData->metadata.callback)
869     {
870         EAS_I32 readLen;
871         E_EAS_METADATA_TYPE metaType;
872 
873         metaType = EAS_METADATA_UNKNOWN;
874 
875         /* only process title on the first track */
876         if (c == SMF_META_SEQTRK_NAME)
877             metaType = EAS_METADATA_TITLE;
878         else if (c == SMF_META_TEXT)
879             metaType = EAS_METADATA_TEXT;
880         else if (c == SMF_META_COPYRIGHT)
881             metaType = EAS_METADATA_COPYRIGHT;
882         else if (c == SMF_META_LYRIC)
883             metaType = EAS_METADATA_LYRIC;
884 
885         if (metaType != EAS_METADATA_UNKNOWN)
886         {
887             readLen = pSMFData->metadata.bufferSize - 1;
888             if ((EAS_I32) len < readLen)
889                 readLen = (EAS_I32) len;
890             if ((result = EAS_HWReadFile(pEASData->hwInstData, pSMFStream->fileHandle, pSMFData->metadata.buffer, readLen, &readLen)) != EAS_SUCCESS)
891                 return result;
892             pSMFData->metadata.buffer[readLen] = 0;
893             pSMFData->metadata.callback(metaType, pSMFData->metadata.buffer, pSMFData->metadata.pUserData);
894         }
895     }
896 
897     /* position file to next event - in case we ignored all or part of the meta-event */
898     if ((result = EAS_HWFileSeek(pEASData->hwInstData, pSMFStream->fileHandle, pos)) != EAS_SUCCESS)
899         return result;
900 
901     { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "Meta-event: type=%02x, len=%d\n", c, len); */ }
902     return EAS_SUCCESS;
903 }
904 
905 /*----------------------------------------------------------------------------
906  * SMF_ParseSysEx()
907  *----------------------------------------------------------------------------
908  * Purpose:
909  * Reads a varible length quantity from an SMF file
910  *
911  * Inputs:
912  *
913  *
914  * Outputs:
915  *
916  *
917  * Side Effects:
918  *
919  *----------------------------------------------------------------------------
920 */
SMF_ParseSysEx(S_EAS_DATA * pEASData,S_SMF_DATA * pSMFData,S_SMF_STREAM * pSMFStream,EAS_U8 f0,EAS_INT parserMode)921 static EAS_RESULT SMF_ParseSysEx (S_EAS_DATA *pEASData, S_SMF_DATA *pSMFData, S_SMF_STREAM *pSMFStream, EAS_U8 f0, EAS_INT parserMode)
922 {
923     EAS_RESULT result;
924     EAS_U32 len;
925     EAS_U8 c;
926 
927     /* get the length */
928     if ((result = SMF_GetVarLenData(pEASData->hwInstData, pSMFStream->fileHandle, &len)) != EAS_SUCCESS)
929         return result;
930 
931     /* start of SysEx message? */
932     if (f0 == 0xf0)
933     {
934         if ((result = EAS_ParseMIDIStream(pEASData, pSMFData->pSynth, &pSMFStream->midiStream, f0, parserMode)) != EAS_SUCCESS)
935             return result;
936     }
937 
938     /* feed the SysEx to the stream parser */
939     while (len--)
940     {
941         if ((result = EAS_HWGetByte(pEASData->hwInstData, pSMFStream->fileHandle, &c)) != EAS_SUCCESS)
942             return result;
943         if ((result = EAS_ParseMIDIStream(pEASData, pSMFData->pSynth, &pSMFStream->midiStream, c, parserMode)) != EAS_SUCCESS)
944             return result;
945 
946         /* check for GM system ON */
947         if (pSMFStream->midiStream.flags & MIDI_FLAG_GM_ON)
948             pSMFData->flags |= SMF_FLAGS_HAS_GM_ON;
949     }
950 
951     return EAS_SUCCESS;
952 }
953 
954 /*----------------------------------------------------------------------------
955  * SMF_ParseEvent()
956  *----------------------------------------------------------------------------
957  * Purpose:
958  * Reads a varible length quantity from an SMF file
959  *
960  * Inputs:
961  *
962  *
963  * Outputs:
964  *
965  *
966  * Side Effects:
967  *
968  *----------------------------------------------------------------------------
969 */
SMF_ParseEvent(S_EAS_DATA * pEASData,S_SMF_DATA * pSMFData,S_SMF_STREAM * pSMFStream,EAS_INT parserMode)970 static EAS_RESULT SMF_ParseEvent (S_EAS_DATA *pEASData, S_SMF_DATA *pSMFData, S_SMF_STREAM *pSMFStream, EAS_INT parserMode)
971 {
972     EAS_RESULT result;
973     EAS_U8 c;
974 
975     /* get the event type */
976     if ((result = EAS_HWGetByte(pEASData->hwInstData, pSMFStream->fileHandle, &c)) != EAS_SUCCESS)
977         return result;
978 
979     /* parse meta-event */
980     if (c == 0xff)
981     {
982         if ((result = SMF_ParseMetaEvent(pEASData, pSMFData, pSMFStream)) != EAS_SUCCESS)
983             return result;
984     }
985 
986     /* parse SysEx */
987     else if ((c == 0xf0) || (c == 0xf7))
988     {
989         if ((result = SMF_ParseSysEx(pEASData, pSMFData, pSMFStream, c, parserMode)) != EAS_SUCCESS)
990             return result;
991     }
992 
993     /* parse MIDI message */
994     else
995     {
996         if ((result = EAS_ParseMIDIStream(pEASData, pSMFData->pSynth, &pSMFStream->midiStream, c, parserMode)) != EAS_SUCCESS)
997             return result;
998 
999         /* keep streaming data to the MIDI parser until the message is complete */
1000         while (pSMFStream->midiStream.pending)
1001         {
1002             if ((result = EAS_HWGetByte(pEASData->hwInstData, pSMFStream->fileHandle, &c)) != EAS_SUCCESS)
1003                 return result;
1004             if ((result = EAS_ParseMIDIStream(pEASData, pSMFData->pSynth, &pSMFStream->midiStream, c, parserMode)) != EAS_SUCCESS)
1005                 return result;
1006         }
1007 
1008     }
1009 
1010     /* chase mode logic */
1011     if (pSMFData->time == 0)
1012     {
1013         if (pSMFData->flags & SMF_FLAGS_CHASE_MODE)
1014         {
1015             if (pSMFStream->midiStream.flags & MIDI_FLAG_FIRST_NOTE)
1016                 pSMFData->flags &= ~SMF_FLAGS_CHASE_MODE;
1017         }
1018         else if ((pSMFData->flags & SMF_FLAGS_SETUP_BAR) == SMF_FLAGS_SETUP_BAR)
1019             pSMFData->flags = (pSMFData->flags & ~SMF_FLAGS_SETUP_BAR) | SMF_FLAGS_CHASE_MODE;
1020     }
1021 
1022     return EAS_SUCCESS;
1023 }
1024 
1025 /*----------------------------------------------------------------------------
1026  * SMF_ParseHeader()
1027  *----------------------------------------------------------------------------
1028  * Purpose:
1029  * Parses the header of an SMF file, allocates memory the stream parsers and initializes the
1030  * stream parsers.
1031  *
1032  * Inputs:
1033  * pEASData         - pointer to overall EAS data structure
1034  * pSMFData         - pointer to parser instance data
1035  * fileHandle       - file handle
1036  * fileOffset       - offset in the file where the header data starts, usually 0
1037  *
1038  *
1039  * Outputs:
1040  *
1041  *
1042  * Side Effects:
1043  *
1044  *----------------------------------------------------------------------------
1045 */
1046 /*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)1047 EAS_RESULT SMF_ParseHeader (EAS_HW_DATA_HANDLE hwInstData, S_SMF_DATA *pSMFData)
1048 {
1049     EAS_RESULT result;
1050     EAS_I32 i;
1051     EAS_U16 division;
1052     EAS_U16 numStreams;
1053     EAS_U32 chunkSize;
1054     EAS_U32 chunkStart;
1055     EAS_U32 temp;
1056     EAS_U32 ticks;
1057 
1058     /* explicitly set numStreams to 0. It will later be used by SMF_Close to
1059      * determine whether we have valid streams or not. */
1060     pSMFData->numStreams = 0;
1061 
1062     /* rewind the file and find the end of the header chunk */
1063     if ((result = EAS_HWFileSeek(hwInstData, pSMFData->fileHandle, pSMFData->fileOffset + SMF_OFS_HEADER_SIZE)) != EAS_SUCCESS)
1064         goto ReadError;
1065     if ((result = EAS_HWGetDWord(hwInstData, pSMFData->fileHandle, &chunkSize, EAS_TRUE)) != EAS_SUCCESS)
1066         goto ReadError;
1067 
1068     /* determine the number of tracks */
1069     if ((result = EAS_HWFileSeek(hwInstData, pSMFData->fileHandle, pSMFData->fileOffset + SMF_OFS_NUM_TRACKS)) != EAS_SUCCESS)
1070         goto ReadError;
1071     if ((result = EAS_HWGetWord(hwInstData, pSMFData->fileHandle, &numStreams, EAS_TRUE)) != EAS_SUCCESS)
1072         goto ReadError;
1073 
1074     /* limit the number of tracks */
1075     if (numStreams > MAX_SMF_STREAMS)
1076     {
1077         { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "SMF file contains %u tracks, playing %d tracks\n", numStreams, MAX_SMF_STREAMS); */ }
1078         numStreams = MAX_SMF_STREAMS;
1079     } else if (numStreams == 0)
1080     {
1081         /* avoid 0 sized allocation */
1082         return EAS_ERROR_PARAMETER_RANGE;
1083     }
1084 
1085     /* get the time division */
1086     if ((result = EAS_HWGetWord(hwInstData, pSMFData->fileHandle, &division, EAS_TRUE)) != EAS_SUCCESS)
1087         goto ReadError;
1088 
1089     /* setup default timebase for 120 bpm */
1090     pSMFData->ppqn = 192;
1091     if (!division || division & 0x8000)
1092         { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING,"No support for SMPTE code timebase\n"); */ }
1093     else
1094         pSMFData->ppqn = (division & 0x7fff);
1095     pSMFData->tickConv = (EAS_U16) (((SMF_DEFAULT_TIMEBASE * 1024) / pSMFData->ppqn + 500) / 1000);
1096 
1097     /* dynamic memory allocation, allocate memory for streams */
1098     if (pSMFData->streams == NULL)
1099     {
1100         pSMFData->streams = EAS_HWMalloc(hwInstData,sizeof(S_SMF_STREAM) * numStreams);
1101         if (pSMFData->streams == NULL)
1102             return EAS_ERROR_MALLOC_FAILED;
1103 
1104         /* zero the memory to insure complete initialization */
1105         EAS_HWMemSet((void *)(pSMFData->streams), 0, sizeof(S_SMF_STREAM) * numStreams);
1106     }
1107     pSMFData->numStreams = numStreams;
1108 
1109     /* find the start of each track */
1110     chunkStart = (EAS_U32) pSMFData->fileOffset;
1111     ticks = 0x7fffffffL;
1112     pSMFData->nextStream = NULL;
1113     for (i = 0; i < pSMFData->numStreams; i++)
1114     {
1115 
1116         for (;;)
1117         {
1118 
1119             /* calculate start of next chunk - checking for errors */
1120             temp = chunkStart + SMF_CHUNK_INFO_SIZE + chunkSize;
1121             if (temp <= chunkStart)
1122             {
1123                 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING,"Error in chunk size at offset %d\n", chunkStart); */ }
1124                 return EAS_ERROR_FILE_FORMAT;
1125             }
1126             chunkStart = temp;
1127 
1128             /* seek to the start of the next chunk */
1129             if ((result = EAS_HWFileSeek(hwInstData, pSMFData->fileHandle, (EAS_I32) chunkStart)) != EAS_SUCCESS)
1130                 goto ReadError;
1131 
1132             /* read the chunk identifier */
1133             if ((result = EAS_HWGetDWord(hwInstData, pSMFData->fileHandle, &temp, EAS_TRUE)) != EAS_SUCCESS)
1134                 goto ReadError;
1135 
1136             /* read the chunk size */
1137             if ((result = EAS_HWGetDWord(hwInstData, pSMFData->fileHandle, &chunkSize, EAS_TRUE)) != EAS_SUCCESS)
1138                 goto ReadError;
1139 
1140             /* make sure this is an 'MTrk' chunk */
1141             if (temp == SMF_CHUNK_TYPE_TRACK)
1142                 break;
1143 
1144             { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING,"Unexpected chunk type: 0x%08x\n", temp); */ }
1145         }
1146 
1147         /* initalize some data */
1148         pSMFData->streams[i].ticks = 0;
1149         pSMFData->streams[i].fileHandle = pSMFData->fileHandle;
1150 
1151         /* NULL the file handle so we don't try to close it twice */
1152         pSMFData->fileHandle = NULL;
1153 
1154         /* save this file position as the start of the track */
1155         pSMFData->streams[i].startFilePos = (EAS_I32) chunkStart + SMF_CHUNK_INFO_SIZE;
1156 
1157         /* initalize the MIDI parser data */
1158         EAS_InitMIDIStream(&pSMFData->streams[i].midiStream);
1159 
1160         /* parse the first delta time in each stream */
1161         if ((result = SMF_GetDeltaTime(hwInstData, &pSMFData->streams[i])) != EAS_SUCCESS)
1162                 goto ReadError;
1163 
1164         if (pSMFData->streams[i].ticks < ticks)
1165         {
1166             ticks = pSMFData->streams[i].ticks;
1167             pSMFData->nextStream = &pSMFData->streams[i];
1168         }
1169 
1170         /* more tracks to do, create a duplicate file handle */
1171         if (i < (pSMFData->numStreams - 1))
1172         {
1173             if ((result = EAS_HWDupHandle(hwInstData, pSMFData->streams[i].fileHandle, &pSMFData->fileHandle)) != EAS_SUCCESS)
1174                 goto ReadError;
1175         }
1176     }
1177 
1178     /* update the time of the next event */
1179     if (pSMFData->nextStream)
1180         SMF_UpdateTime(pSMFData, pSMFData->nextStream->ticks);
1181 
1182     return EAS_SUCCESS;
1183 
1184     /* ugly goto: but simpler than structured */
1185     ReadError:
1186         if (result == EAS_EOF)
1187             return EAS_ERROR_FILE_FORMAT;
1188         return result;
1189 }
1190 
1191 /*----------------------------------------------------------------------------
1192  * SMF_UpdateTime()
1193  *----------------------------------------------------------------------------
1194  * Purpose:
1195  * Update the millisecond time base by converting the ticks into millieconds
1196  *
1197  * Inputs:
1198  *
1199  *
1200  * Outputs:
1201  *
1202  *
1203  * Side Effects:
1204  *
1205  *----------------------------------------------------------------------------
1206 */
SMF_UpdateTime(S_SMF_DATA * pSMFData,EAS_U32 ticks)1207 static void SMF_UpdateTime (S_SMF_DATA *pSMFData, EAS_U32 ticks)
1208 {
1209     EAS_U32 temp1, temp2;
1210 
1211     if (pSMFData->flags & SMF_FLAGS_CHASE_MODE)
1212         return;
1213 
1214     temp1 = (ticks >> 10) * pSMFData->tickConv;
1215     temp2 = (ticks & 0x3ff) * pSMFData->tickConv;
1216     pSMFData->time += (EAS_I32)((temp1 << 8) + (temp2 >> 2));
1217 }
1218 
1219