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
52static const EAS_U8 smfHeader[] = { 'M', 'T', 'h', 'd' };
53
54/* local prototypes */
55static EAS_RESULT SMF_GetVarLenData (EAS_HW_DATA_HANDLE hwInstData, EAS_FILE_HANDLE fileHandle, EAS_U32 *pData);
56static EAS_RESULT SMF_ParseMetaEvent (S_EAS_DATA *pEASData, S_SMF_DATA *pSMFData, S_SMF_STREAM *pSMFStream);
57static EAS_RESULT SMF_ParseSysEx (S_EAS_DATA *pEASData, S_SMF_DATA *pSMFData, S_SMF_STREAM *pSMFStream, EAS_U8 f0, EAS_INT parserMode);
58static EAS_RESULT SMF_ParseEvent (S_EAS_DATA *pEASData, S_SMF_DATA *pSMFData, S_SMF_STREAM *pSMFStream, EAS_INT parserMode);
59static EAS_RESULT SMF_GetDeltaTime (EAS_HW_DATA_HANDLE hwInstData, S_SMF_STREAM *pSMFStream);
60static 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*/
70const 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*/
104EAS_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*/
174EAS_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 */
219EAS_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*/
271EAS_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 */
381EAS_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*/
423EAS_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*/
478EAS_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*/
541EAS_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 */
574EAS_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 */
606EAS_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 */
693EAS_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*/
755static 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*/
789static 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*/
817static 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*/
921static 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*/
970static 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 */
1047EAS_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_U32 chunkSize;
1053    EAS_U32 chunkStart;
1054    EAS_U32 temp;
1055    EAS_U32 ticks;
1056
1057    /* rewind the file and find the end of the header chunk */
1058    if ((result = EAS_HWFileSeek(hwInstData, pSMFData->fileHandle, pSMFData->fileOffset + SMF_OFS_HEADER_SIZE)) != EAS_SUCCESS)
1059        goto ReadError;
1060    if ((result = EAS_HWGetDWord(hwInstData, pSMFData->fileHandle, &chunkSize, EAS_TRUE)) != EAS_SUCCESS)
1061        goto ReadError;
1062
1063    /* determine the number of tracks */
1064    if ((result = EAS_HWFileSeek(hwInstData, pSMFData->fileHandle, pSMFData->fileOffset + SMF_OFS_NUM_TRACKS)) != EAS_SUCCESS)
1065        goto ReadError;
1066    if ((result = EAS_HWGetWord(hwInstData, pSMFData->fileHandle, &pSMFData->numStreams, EAS_TRUE)) != EAS_SUCCESS)
1067        goto ReadError;
1068
1069    /* limit the number of tracks */
1070    if (pSMFData->numStreams > MAX_SMF_STREAMS)
1071    {
1072        { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "SMF file contains %u tracks, playing %d tracks\n", pSMFData->numStreams, MAX_SMF_STREAMS); */ }
1073        pSMFData->numStreams = MAX_SMF_STREAMS;
1074    } else if (pSMFData->numStreams == 0)
1075    {
1076        /* avoid 0 sized allocation */
1077        return EAS_ERROR_PARAMETER_RANGE;
1078    }
1079
1080    /* get the time division */
1081    if ((result = EAS_HWGetWord(hwInstData, pSMFData->fileHandle, &division, EAS_TRUE)) != EAS_SUCCESS)
1082        goto ReadError;
1083
1084    /* setup default timebase for 120 bpm */
1085    pSMFData->ppqn = 192;
1086    if (!division || division & 0x8000)
1087        { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING,"No support for SMPTE code timebase\n"); */ }
1088    else
1089        pSMFData->ppqn = (division & 0x7fff);
1090    pSMFData->tickConv = (EAS_U16) (((SMF_DEFAULT_TIMEBASE * 1024) / pSMFData->ppqn + 500) / 1000);
1091
1092    /* dynamic memory allocation, allocate memory for streams */
1093    if (pSMFData->streams == NULL)
1094    {
1095        pSMFData->streams = EAS_HWMalloc(hwInstData,sizeof(S_SMF_STREAM) * pSMFData->numStreams);
1096        if (pSMFData->streams == NULL)
1097            return EAS_ERROR_MALLOC_FAILED;
1098
1099        /* zero the memory to insure complete initialization */
1100        EAS_HWMemSet((void *)(pSMFData->streams), 0, sizeof(S_SMF_STREAM) * pSMFData->numStreams);
1101    }
1102
1103    /* find the start of each track */
1104    chunkStart = (EAS_U32) pSMFData->fileOffset;
1105    ticks = 0x7fffffffL;
1106    pSMFData->nextStream = NULL;
1107    for (i = 0; i < pSMFData->numStreams; i++)
1108    {
1109
1110        for (;;)
1111        {
1112
1113            /* calculate start of next chunk - checking for errors */
1114            temp = chunkStart + SMF_CHUNK_INFO_SIZE + chunkSize;
1115            if (temp <= chunkStart)
1116            {
1117                { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING,"Error in chunk size at offset %d\n", chunkStart); */ }
1118                return EAS_ERROR_FILE_FORMAT;
1119            }
1120            chunkStart = temp;
1121
1122            /* seek to the start of the next chunk */
1123            if ((result = EAS_HWFileSeek(hwInstData, pSMFData->fileHandle, (EAS_I32) chunkStart)) != EAS_SUCCESS)
1124                goto ReadError;
1125
1126            /* read the chunk identifier */
1127            if ((result = EAS_HWGetDWord(hwInstData, pSMFData->fileHandle, &temp, EAS_TRUE)) != EAS_SUCCESS)
1128                goto ReadError;
1129
1130            /* read the chunk size */
1131            if ((result = EAS_HWGetDWord(hwInstData, pSMFData->fileHandle, &chunkSize, EAS_TRUE)) != EAS_SUCCESS)
1132                goto ReadError;
1133
1134            /* make sure this is an 'MTrk' chunk */
1135            if (temp == SMF_CHUNK_TYPE_TRACK)
1136                break;
1137
1138            { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING,"Unexpected chunk type: 0x%08x\n", temp); */ }
1139        }
1140
1141        /* initalize some data */
1142        pSMFData->streams[i].ticks = 0;
1143        pSMFData->streams[i].fileHandle = pSMFData->fileHandle;
1144
1145        /* NULL the file handle so we don't try to close it twice */
1146        pSMFData->fileHandle = NULL;
1147
1148        /* save this file position as the start of the track */
1149        pSMFData->streams[i].startFilePos = (EAS_I32) chunkStart + SMF_CHUNK_INFO_SIZE;
1150
1151        /* initalize the MIDI parser data */
1152        EAS_InitMIDIStream(&pSMFData->streams[i].midiStream);
1153
1154        /* parse the first delta time in each stream */
1155        if ((result = SMF_GetDeltaTime(hwInstData, &pSMFData->streams[i])) != EAS_SUCCESS)
1156                goto ReadError;
1157
1158        if (pSMFData->streams[i].ticks < ticks)
1159        {
1160            ticks = pSMFData->streams[i].ticks;
1161            pSMFData->nextStream = &pSMFData->streams[i];
1162        }
1163
1164        /* more tracks to do, create a duplicate file handle */
1165        if (i < (pSMFData->numStreams - 1))
1166        {
1167            if ((result = EAS_HWDupHandle(hwInstData, pSMFData->streams[i].fileHandle, &pSMFData->fileHandle)) != EAS_SUCCESS)
1168                goto ReadError;
1169        }
1170    }
1171
1172    /* update the time of the next event */
1173    if (pSMFData->nextStream)
1174        SMF_UpdateTime(pSMFData, pSMFData->nextStream->ticks);
1175
1176    return EAS_SUCCESS;
1177
1178    /* ugly goto: but simpler than structured */
1179    ReadError:
1180        if (result == EAS_EOF)
1181            return EAS_ERROR_FILE_FORMAT;
1182        return result;
1183}
1184
1185/*----------------------------------------------------------------------------
1186 * SMF_UpdateTime()
1187 *----------------------------------------------------------------------------
1188 * Purpose:
1189 * Update the millisecond time base by converting the ticks into millieconds
1190 *
1191 * Inputs:
1192 *
1193 *
1194 * Outputs:
1195 *
1196 *
1197 * Side Effects:
1198 *
1199 *----------------------------------------------------------------------------
1200*/
1201static void SMF_UpdateTime (S_SMF_DATA *pSMFData, EAS_U32 ticks)
1202{
1203    EAS_U32 temp1, temp2;
1204
1205    if (pSMFData->flags & SMF_FLAGS_CHASE_MODE)
1206        return;
1207
1208    temp1 = (ticks >> 10) * pSMFData->tickConv;
1209    temp2 = (ticks & 0x3ff) * pSMFData->tickConv;
1210    pSMFData->time += (EAS_I32)((temp1 << 8) + (temp2 >> 2));
1211}
1212
1213