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