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