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