eas_smf.c revision e442bb7cd6a085b33a4dd52c0e20a157ada7feb1
11d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert/*----------------------------------------------------------------------------
21d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert *
31d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * File:
41d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * eas_smf.c
51d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert *
61d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * Contents and purpose:
71d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * SMF Type 0 and 1 File Parser
81d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert *
91d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * For SMF timebase analysis, see "MIDI Sequencer Analysis.xls".
101d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert *
111d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * Copyright Sonic Network Inc. 2005
121d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
131d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * Licensed under the Apache License, Version 2.0 (the "License");
141d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * you may not use this file except in compliance with the License.
151d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * You may obtain a copy of the License at
161d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert *
171d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert *      http://www.apache.org/licenses/LICENSE-2.0
181d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert *
191d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * Unless required by applicable law or agreed to in writing, software
201d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * distributed under the License is distributed on an "AS IS" BASIS,
211d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
221d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * See the License for the specific language governing permissions and
231d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * limitations under the License.
241d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert *
251d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert *----------------------------------------------------------------------------
261d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * Revision Control:
271d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert *	 $Revision: 803 $
281d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert *	 $Date: 2007-08-01 09:57:00 -0700 (Wed, 01 Aug 2007) $
291d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert *----------------------------------------------------------------------------
301d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert*/
311d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
321d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert#include "eas_data.h"
331d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert#include "eas_miditypes.h"
341d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert#include "eas_parser.h"
351d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert#include "eas_report.h"
361d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert#include "eas_host.h"
371d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert#include "eas_midi.h"
381d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert#include "eas_config.h"
391d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert#include "eas_vm_protos.h"
401d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert#include "eas_smfdata.h"
411d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert#include "eas_smf.h"
421d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
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