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 /* 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 */
SMF_State(S_EAS_DATA * pEASData,EAS_VOID_PTR pInstData,EAS_I32 * pState)376 EAS_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 */
SMF_Close(S_EAS_DATA * pEASData,EAS_VOID_PTR pInstData)418 EAS_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 */
SMF_Reset(S_EAS_DATA * pEASData,EAS_VOID_PTR pInstData)473 EAS_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 */
SMF_Pause(S_EAS_DATA * pEASData,EAS_VOID_PTR pInstData)536 EAS_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 */
SMF_Resume(S_EAS_DATA * pEASData,EAS_VOID_PTR pInstData)569 EAS_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 */
SMF_SetData(S_EAS_DATA * pEASData,EAS_VOID_PTR pInstData,EAS_I32 param,EAS_I32 value)601 EAS_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 */
SMF_GetData(S_EAS_DATA * pEASData,EAS_VOID_PTR pInstData,EAS_I32 param,EAS_I32 * pValue)688 EAS_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 */
SMF_GetVarLenData(EAS_HW_DATA_HANDLE hwInstData,EAS_FILE_HANDLE fileHandle,EAS_U32 * pData)750 static 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 */
SMF_GetDeltaTime(EAS_HW_DATA_HANDLE hwInstData,S_SMF_STREAM * pSMFStream)784 static 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 */
SMF_ParseMetaEvent(S_EAS_DATA * pEASData,S_SMF_DATA * pSMFData,S_SMF_STREAM * pSMFStream)812 static 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 */
SMF_ParseSysEx(S_EAS_DATA * pEASData,S_SMF_DATA * pSMFData,S_SMF_STREAM * pSMFStream,EAS_U8 f0,EAS_INT parserMode)916 static 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 */
SMF_ParseEvent(S_EAS_DATA * pEASData,S_SMF_DATA * pSMFData,S_SMF_STREAM * pSMFStream,EAS_INT parserMode)965 static 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 */
SMF_ParseHeader(EAS_HW_DATA_HANDLE hwInstData,S_SMF_DATA * pSMFData)1042 EAS_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 */
SMF_UpdateTime(S_SMF_DATA * pSMFData,EAS_U32 ticks)1192 static 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