• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*----------------------------------------------------------------------------
2  *
3  * File:
4  * eas_tonecontrol.c
5  *
6  * Contents and purpose:
7  * MMAPI ToneControl parser
8  *
9  * Copyright Sonic Network Inc. 2006
10 
11  * Licensed under the Apache License, Version 2.0 (the "License");
12  * you may not use this file except in compliance with the License.
13  * You may obtain a copy of the License at
14  *
15  *      http://www.apache.org/licenses/LICENSE-2.0
16  *
17  * Unless required by applicable law or agreed to in writing, software
18  * distributed under the License is distributed on an "AS IS" BASIS,
19  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20  * See the License for the specific language governing permissions and
21  * limitations under the License.
22  *
23  *----------------------------------------------------------------------------
24  * Revision Control:
25  *   $Revision: 795 $
26  *   $Date: 2007-08-01 00:14:45 -0700 (Wed, 01 Aug 2007) $
27  *----------------------------------------------------------------------------
28 */
29 
30 #include "eas_data.h"
31 #include "eas_miditypes.h"
32 #include "eas_parser.h"
33 #include "eas_report.h"
34 #include "eas_host.h"
35 #include "eas_midi.h"
36 #include "eas_config.h"
37 #include "eas_vm_protos.h"
38 #include "eas_tcdata.h"
39 
40 
41 /* default channel and program for TC playback */
42 #define TC_CHANNEL              0
43 #define TC_PROGRAM              80
44 #define TC_VELOCITY             127
45 
46 #define TC_FIELD_SILENCE        -1
47 #define TC_FIELD_VERSION        -2
48 #define TC_FIELD_TEMPO          -3
49 #define TC_FIELD_RESOLUTION     -4
50 #define TC_FIELD_BLOCK_START    -5
51 #define TC_FIELD_BLOCK_END      -6
52 #define TC_FIELD_PLAY_BLOCK     -7
53 #define TC_FIELD_SET_VOLUME     -8
54 #define TC_FIELD_REPEAT         -9
55 #define TC_FIELD_INVALID        -10
56 
57 /* convert 0-100 volume to 0-127 velocity using fixed point */
58 #define TC_VOLUME_CONV          21307064
59 #define TC_VOLUME_SHIFT         24
60 
61 
62 /* local prototypes */
63 static EAS_RESULT TC_CheckFileType (S_EAS_DATA *pEASData, EAS_FILE_HANDLE fileHandle, EAS_VOID_PTR *ppHandle, EAS_I32 offset);
64 static EAS_RESULT TC_Prepare (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData);
65 static EAS_RESULT TC_Time (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_U32 *pTime);
66 static EAS_RESULT TC_Event (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_INT parserMode);
67 static EAS_RESULT TC_State (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_STATE *pState);
68 static EAS_RESULT TC_Close (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData);
69 static EAS_RESULT TC_Reset (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData);
70 static EAS_RESULT TC_Pause (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData);
71 static EAS_RESULT TC_Resume (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData);
72 static EAS_RESULT TC_SetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 value);
73 static EAS_RESULT TC_GetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 *pValue);
74 static EAS_RESULT TC_ParseHeader (S_EAS_DATA *pEASData, S_TC_DATA* pData);
75 static EAS_RESULT TC_StartNote (S_EAS_DATA *pEASData, S_TC_DATA* pData, EAS_INT parserMode, EAS_I8 note);
76 static EAS_RESULT TC_GetRepeat (S_EAS_DATA *pEASData, S_TC_DATA* pData, EAS_INT parserMode);
77 static EAS_RESULT TC_PlayBlock (S_EAS_DATA *pEASData, S_TC_DATA* pData);
78 static EAS_RESULT TC_BlockEnd (S_EAS_DATA *pEASData, S_TC_DATA* pData);
79 static EAS_RESULT TC_GetVolume (S_EAS_DATA *pEASData, S_TC_DATA* pData);
80 static EAS_RESULT TC_GetTempo (S_EAS_DATA *pEASData, S_TC_DATA* pData);
81 static EAS_RESULT TC_GetResolution (S_EAS_DATA *pEASData, S_TC_DATA* pData);
82 static EAS_RESULT TC_GetNextChar (EAS_HW_DATA_HANDLE hwInstData, S_TC_DATA *pData, EAS_I8 *pValue);
83 static void TC_PutBackChar (S_TC_DATA *pData, EAS_I8 value);
84 
85 /* calculate a new tick time based on resolution & tempo */
TC_CalcTimeBase(S_TC_DATA * pData)86 EAS_INLINE void TC_CalcTimeBase (S_TC_DATA *pData)
87 {
88 
89     /* ticks in 256ths of a millisecond */
90     pData->tick = ((60 * 1000) << 8) / (pData->tempo * pData->resolution);
91 }
92 
93 /*----------------------------------------------------------------------------
94  *
95  * EAS_TC_Parser
96  *
97  * This structure contains the functional interface for the iMelody parser
98  *----------------------------------------------------------------------------
99 */
100 const S_FILE_PARSER_INTERFACE EAS_TC_Parser =
101 {
102     TC_CheckFileType,
103     TC_Prepare,
104     TC_Time,
105     TC_Event,
106     TC_State,
107     TC_Close,
108     TC_Reset,
109     TC_Pause,
110     TC_Resume,
111     NULL,
112     TC_SetData,
113     TC_GetData,
114     NULL
115 };
116 
117 /*----------------------------------------------------------------------------
118  * TC_CheckFileType()
119  *----------------------------------------------------------------------------
120  * Purpose:
121  * Check the file type to see if we can parse it
122  *
123  * Inputs:
124  * pEASData         - pointer to overall EAS data structure
125  * handle           - pointer to file handle
126  *
127  * Outputs:
128  *
129  *
130  * Side Effects:
131  *
132  *----------------------------------------------------------------------------
133 */
TC_CheckFileType(S_EAS_DATA * pEASData,EAS_FILE_HANDLE fileHandle,EAS_VOID_PTR * ppHandle,EAS_I32 offset)134 static EAS_RESULT TC_CheckFileType (S_EAS_DATA *pEASData, EAS_FILE_HANDLE fileHandle, EAS_VOID_PTR *ppHandle, EAS_I32 offset)
135 {
136     S_TC_DATA data;
137     S_TC_DATA *pData;
138 
139     /* init data */
140     EAS_HWMemSet(&data, 0, sizeof(S_TC_DATA));
141     data.fileHandle = fileHandle;
142     data.fileOffset = offset;
143     *ppHandle= NULL;
144 
145     /* see if we can parse the header */
146     if (TC_ParseHeader(pEASData, &data) == EAS_SUCCESS)
147     {
148 
149         /* check for static memory allocation */
150         if (pEASData->staticMemoryModel)
151             pData = EAS_CMEnumOptData(EAS_MODULE_MMAPI_TONE_CONTROL);
152         else
153             pData = EAS_HWMalloc(pEASData->hwInstData, sizeof(S_TC_DATA));
154         if (!pData)
155             return EAS_ERROR_MALLOC_FAILED;
156 
157         /* copy data to persistent storage */
158         EAS_HWMemCpy(pData, &data, sizeof(S_TC_DATA));
159 
160         /* return a pointer to the instance data */
161         pData->state = EAS_STATE_OPEN;
162         *ppHandle = pData;
163     }
164 
165     return EAS_SUCCESS;
166 }
167 
168 /*----------------------------------------------------------------------------
169  * TC_Prepare()
170  *----------------------------------------------------------------------------
171  * Purpose:
172  * Prepare to parse the file. Allocates instance data (or uses static allocation for
173  * static memory model).
174  *
175  * Inputs:
176  * pEASData         - pointer to overall EAS data structure
177  * handle           - pointer to file handle
178  *
179  * Outputs:
180  *
181  *
182  * Side Effects:
183  *
184  *----------------------------------------------------------------------------
185 */
TC_Prepare(S_EAS_DATA * pEASData,EAS_VOID_PTR pInstData)186 static EAS_RESULT TC_Prepare (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
187 {
188     S_TC_DATA* pData;
189     EAS_RESULT result;
190 
191     /* check for valid state */
192     pData = (S_TC_DATA*) pInstData;
193     if (pData->state != EAS_STATE_OPEN)
194         return EAS_ERROR_NOT_VALID_IN_THIS_STATE;
195 
196     /* instantiate a synthesizer */
197     if ((result = VMInitMIDI(pEASData, &pData->pSynth)) != EAS_SUCCESS)
198     {
199         { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "VMInitMIDI returned %d\n", result); */ }
200         return result;
201     }
202 
203     /* set to ready state */
204     pData->state = EAS_STATE_READY;
205     return EAS_SUCCESS;
206 }
207 
208 /*----------------------------------------------------------------------------
209  * TC_Time()
210  *----------------------------------------------------------------------------
211  * Purpose:
212  * Returns the time of the next event in msecs
213  *
214  * Inputs:
215  * pEASData         - pointer to overall EAS data structure
216  * handle           - pointer to file handle
217  * pTime            - pointer to variable to hold time of next event (in msecs)
218  *
219  * Outputs:
220  *
221  *
222  * Side Effects:
223  *
224  *----------------------------------------------------------------------------
225 */
226 /*lint -esym(715, pEASData) reserved for future use */
TC_Time(S_EAS_DATA * pEASData,EAS_VOID_PTR pInstData,EAS_U32 * pTime)227 static EAS_RESULT TC_Time (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_U32 *pTime)
228 {
229     S_TC_DATA *pData;
230 
231     pData = (S_TC_DATA*) pInstData;
232 
233     /* return time in milliseconds */
234     /*lint -e{704} use shift instead of division */
235     *pTime = pData->time >> 8;
236     return EAS_SUCCESS;
237 }
238 
239 /*----------------------------------------------------------------------------
240  * TC_Event()
241  *----------------------------------------------------------------------------
242  * Purpose:
243  * Parse the next event in the file
244  *
245  * Inputs:
246  * pEASData         - pointer to overall EAS data structure
247  * handle           - pointer to file handle
248  *
249  * Outputs:
250  *
251  *
252  * Side Effects:
253  *
254  *----------------------------------------------------------------------------
255 */
TC_Event(S_EAS_DATA * pEASData,EAS_VOID_PTR pInstData,EAS_INT parserMode)256 static EAS_RESULT TC_Event (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_INT parserMode)
257 {
258     S_TC_DATA* pData;
259     EAS_RESULT result;
260     EAS_I8 temp;
261 
262     pData = (S_TC_DATA*) pInstData;
263     if (pData->state >= EAS_STATE_OPEN)
264         return EAS_SUCCESS;
265 
266     /* initialize MIDI channel when the track starts playing */
267     if (pData->time == 0)
268     {
269         /* set program to square lead */
270         VMProgramChange(pEASData->pVoiceMgr, pData->pSynth, TC_CHANNEL, TC_PROGRAM);
271 
272         /* set channel volume to max */
273         VMControlChange(pEASData->pVoiceMgr, pData->pSynth, TC_CHANNEL, 7, 127);
274     }
275 
276     /* check for end of note */
277     if (pData->note >= 0)
278     {
279         /* stop the note */
280         VMStopNote(pEASData->pVoiceMgr, pData->pSynth, TC_CHANNEL, (EAS_U8) pData->note, 0);
281 
282         /* check for repeat note */
283         if (pData->repeatCount)
284         {
285             pData->repeatCount--;
286             pData->time += pData->length;
287             if ((pData->note >= 0) && (parserMode == eParserModePlay))
288                 VMStartNote(pEASData->pVoiceMgr, pData->pSynth, TC_CHANNEL, (EAS_U8) pData->note, pData->volume);
289             return EAS_SUCCESS;
290         }
291 
292         pData->note = TC_FIELD_SILENCE;
293     }
294 
295     /* parse stream until we get a note or rest */
296     for (;;)
297     {
298 
299         /* get next byte from stream */
300         if ((result = TC_GetNextChar(pEASData->hwInstData, pData, &temp)) != EAS_SUCCESS)
301         {
302             if (result == EAS_EOF)
303             {
304                 pData->state = EAS_STATE_STOPPING;
305                 return EAS_SUCCESS;
306             }
307             break;
308         }
309 
310         /* check for musical events */
311         if (temp >= TC_FIELD_SILENCE)
312         {
313             result = TC_StartNote(pEASData, pData, parserMode, temp);
314             break;
315         }
316 
317         /* must be a control field */
318         switch (temp)
319         {
320             case TC_FIELD_TEMPO:
321                 result = TC_GetTempo(pEASData, pData);
322                 break;
323 
324             case TC_FIELD_RESOLUTION:
325                 result = TC_GetResolution(pEASData, pData);
326                 break;
327 
328             case TC_FIELD_SET_VOLUME:
329                 result = TC_GetVolume(pEASData, pData);
330                 break;
331 
332             case TC_FIELD_REPEAT:
333                 result = TC_GetRepeat(pEASData, pData, parserMode);
334                 break;
335 
336             case TC_FIELD_PLAY_BLOCK:
337                 result = TC_PlayBlock(pEASData, pData);
338                 break;
339 
340             case TC_FIELD_BLOCK_START:
341                 result = TC_GetNextChar(pEASData->hwInstData, pData, &temp);
342                 break;
343 
344             case TC_FIELD_BLOCK_END:
345                 result = TC_BlockEnd(pEASData, pData);
346                 break;
347 
348             default:
349                 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "Unexpected byte 0x%02x in ToneControl stream\n", temp); */ }
350                 result = EAS_ERROR_FILE_FORMAT;
351         }
352 
353         /* check for error */
354         if (result != EAS_SUCCESS)
355             break;
356     }
357 
358     /* check for error */
359     if (result != EAS_SUCCESS)
360     {
361         if (result == EAS_EOF)
362             result = EAS_ERROR_FILE_FORMAT;
363         pData->state = EAS_STATE_ERROR;
364     }
365     else
366         pData->state = EAS_STATE_PLAY;
367     return result;
368 }
369 
370 /*----------------------------------------------------------------------------
371  * TC_State()
372  *----------------------------------------------------------------------------
373  * Purpose:
374  * Returns the current state of the stream
375  *
376  * Inputs:
377  * pEASData         - pointer to overall EAS data structure
378  * handle           - pointer to file handle
379  * pState           - pointer to variable to store state
380  *
381  * Outputs:
382  *
383  *
384  * Side Effects:
385  *
386  *----------------------------------------------------------------------------
387 */
388 /*lint -esym(715, pEASData) reserved for future use */
TC_State(S_EAS_DATA * pEASData,EAS_VOID_PTR pInstData,EAS_I32 * pState)389 static EAS_RESULT TC_State (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 *pState)
390 {
391     S_TC_DATA* pData;
392 
393     /* establish pointer to instance data */
394     pData = (S_TC_DATA*) pInstData;
395 
396     /* if stopping, check to see if synth voices are active */
397     if (pData->state == EAS_STATE_STOPPING)
398     {
399         if (VMActiveVoices(pData->pSynth) == 0)
400             pData->state = EAS_STATE_STOPPED;
401     }
402 
403     if (pData->state == EAS_STATE_PAUSING)
404     {
405         if (VMActiveVoices(pData->pSynth) == 0)
406             pData->state = EAS_STATE_PAUSED;
407     }
408 
409     /* return current state */
410     *pState = pData->state;
411     return EAS_SUCCESS;
412 }
413 
414 /*----------------------------------------------------------------------------
415  * TC_Close()
416  *----------------------------------------------------------------------------
417  * Purpose:
418  * Close the file and clean up
419  *
420  * Inputs:
421  * pEASData         - pointer to overall EAS data structure
422  * handle           - pointer to file handle
423  *
424  * Outputs:
425  *
426  *
427  * Side Effects:
428  *
429  *----------------------------------------------------------------------------
430 */
TC_Close(S_EAS_DATA * pEASData,EAS_VOID_PTR pInstData)431 static EAS_RESULT TC_Close (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
432 {
433     S_TC_DATA* pData;
434     EAS_RESULT result;
435 
436     pData = (S_TC_DATA*) pInstData;
437 
438     /* close the file */
439     if ((result = EAS_HWCloseFile(pEASData->hwInstData, pData->fileHandle)) != EAS_SUCCESS)
440             return result;
441 
442     /* free the synth */
443     if (pData->pSynth != NULL)
444         VMMIDIShutdown(pEASData, pData->pSynth);
445 
446     /* if using dynamic memory, free it */
447     if (!pEASData->staticMemoryModel)
448         EAS_HWFree(pEASData->hwInstData, pData);
449 
450     return EAS_SUCCESS;
451 }
452 
453 /*----------------------------------------------------------------------------
454  * TC_Reset()
455  *----------------------------------------------------------------------------
456  * Purpose:
457  * Reset the sequencer. Used for locating backwards in the file.
458  *
459  * Inputs:
460  * pEASData         - pointer to overall EAS data structure
461  * handle           - pointer to file handle
462  *
463  * Outputs:
464  *
465  *
466  * Side Effects:
467  *
468  *----------------------------------------------------------------------------
469 */
TC_Reset(S_EAS_DATA * pEASData,EAS_VOID_PTR pInstData)470 static EAS_RESULT TC_Reset (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
471 {
472     S_TC_DATA* pData;
473     EAS_RESULT result;
474 
475     pData = (S_TC_DATA*) pInstData;
476 
477     /* reset the synth */
478     VMReset(pEASData->pVoiceMgr, pData->pSynth, EAS_TRUE);
479 
480     /* reset time to zero */
481     pData->time = 0;
482 
483     /* reset file position and re-parse header */
484     pData->state = EAS_STATE_ERROR;
485     if ((result = EAS_HWFileSeek(pEASData->hwInstData, pData->fileHandle, pData->fileOffset)) != EAS_SUCCESS)
486         return result;
487     if ((result = TC_ParseHeader (pEASData,  pData)) != EAS_SUCCESS)
488         return result;
489 
490     pData->state = EAS_STATE_READY;
491     return EAS_SUCCESS;
492 }
493 
494 /*----------------------------------------------------------------------------
495  * TC_Pause()
496  *----------------------------------------------------------------------------
497  * Purpose:
498  * Pauses the sequencer. Mutes all voices and sets state to pause.
499  *
500  * Inputs:
501  * pEASData         - pointer to overall EAS data structure
502  * handle           - pointer to file handle
503  *
504  * Outputs:
505  *
506  *
507  * Side Effects:
508  *
509  *----------------------------------------------------------------------------
510 */
TC_Pause(S_EAS_DATA * pEASData,EAS_VOID_PTR pInstData)511 static EAS_RESULT TC_Pause (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
512 {
513     S_TC_DATA *pData;
514 
515     /* can't pause a stopped stream */
516     pData = (S_TC_DATA*) pInstData;
517     if (pData->state == EAS_STATE_STOPPED)
518         return EAS_ERROR_ALREADY_STOPPED;
519 
520     /* mute the synthesizer */
521     VMMuteAllVoices(pEASData->pVoiceMgr, pData->pSynth);
522     pData->state = EAS_STATE_PAUSING;
523     return EAS_SUCCESS;
524 }
525 
526 /*----------------------------------------------------------------------------
527  * TC_Resume()
528  *----------------------------------------------------------------------------
529  * Purpose:
530  * Resume playing after a pause, sets state back to playing.
531  *
532  * Inputs:
533  * pEASData         - pointer to overall EAS data structure
534  * handle           - pointer to file handle
535  *
536  * Outputs:
537  *
538  *
539  * Side Effects:
540  *
541  *----------------------------------------------------------------------------
542 */
543 /*lint -esym(715, pEASData) reserved for future use */
TC_Resume(S_EAS_DATA * pEASData,EAS_VOID_PTR pInstData)544 static EAS_RESULT TC_Resume (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData)
545 {
546     S_TC_DATA *pData;
547 
548     /* can't resume a stopped stream */
549     pData = (S_TC_DATA*) pInstData;
550     if (pData->state == EAS_STATE_STOPPED)
551         return EAS_ERROR_ALREADY_STOPPED;
552 
553     /* nothing to do but resume playback */
554     pData->state = EAS_STATE_PLAY;
555     return EAS_SUCCESS;
556 }
557 
558 /*----------------------------------------------------------------------------
559  * TC_SetData()
560  *----------------------------------------------------------------------------
561  * Purpose:
562  * Return file type
563  *
564  * Inputs:
565  * pEASData         - pointer to overall EAS data structure
566  * handle           - pointer to file handle
567  *
568  * Outputs:
569  *
570  *
571  * Side Effects:
572  *
573  *----------------------------------------------------------------------------
574 */
575 /*lint -esym(715, pEASData, pInstData, value) reserved for future use */
TC_SetData(S_EAS_DATA * pEASData,EAS_VOID_PTR pInstData,EAS_I32 param,EAS_I32 value)576 static EAS_RESULT TC_SetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 value)
577 {
578     /* we don't parse any metadata, but we need to return success here */
579     if (param == PARSER_DATA_METADATA_CB)
580         return EAS_SUCCESS;
581 
582     return EAS_ERROR_INVALID_PARAMETER;
583 }
584 
585 /*----------------------------------------------------------------------------
586  * TC_GetData()
587  *----------------------------------------------------------------------------
588  * Purpose:
589  * Return file type
590  *
591  * Inputs:
592  * pEASData         - pointer to overall EAS data structure
593  * handle           - pointer to file handle
594  *
595  * Outputs:
596  *
597  *
598  * Side Effects:
599  *
600  *----------------------------------------------------------------------------
601 */
602 /*lint -e{715} common with other parsers */
TC_GetData(S_EAS_DATA * pEASData,EAS_VOID_PTR pInstData,EAS_I32 param,EAS_I32 * pValue)603 static EAS_RESULT TC_GetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 *pValue)
604 {
605     S_TC_DATA *pData;
606 
607     pData = (S_TC_DATA *) pInstData;
608     switch (param)
609     {
610         /* return file type as TC */
611         case PARSER_DATA_FILE_TYPE:
612             *pValue = EAS_FILE_MMAPI_TONE_CONTROL;
613             break;
614 
615         case PARSER_DATA_SYNTH_HANDLE:
616             *pValue = (EAS_I32) pData->pSynth;
617             break;
618 
619     default:
620             return EAS_ERROR_INVALID_PARAMETER;
621     }
622     return EAS_SUCCESS;
623 }
624 
625 /*----------------------------------------------------------------------------
626  * TC_ParseHeader()
627  *----------------------------------------------------------------------------
628  * Purpose:
629  * Prepare to parse the file. Allocates instance data (or uses static allocation for
630  * static memory model).
631  *
632  * Inputs:
633  * pEASData         - pointer to overall EAS data structure
634  * handle           - pointer to file handle
635  *
636  * Outputs:
637  *
638  *
639  * Side Effects:
640  *
641  *----------------------------------------------------------------------------
642 */
TC_ParseHeader(S_EAS_DATA * pEASData,S_TC_DATA * pData)643 static EAS_RESULT TC_ParseHeader (S_EAS_DATA *pEASData, S_TC_DATA* pData)
644 {
645     EAS_RESULT result;
646     EAS_I8 temp;
647 
648     /* initialize some defaults */
649     pData->time = 0;
650     pData->tempo = 120;
651     pData->resolution = 64;
652     pData->volume = 127;
653     pData->repeatCount = 0;
654     pData->note = TC_FIELD_SILENCE;
655     pData->byteAvail = EAS_FALSE;
656 
657     /* set default timebase */
658     TC_CalcTimeBase(pData);
659 
660     /* seek to start of data */
661     if ((result = EAS_HWFileSeek(pEASData->hwInstData, pData->fileHandle, pData->fileOffset)) != EAS_SUCCESS)
662         return result;
663 
664     /* get version */
665     if ((result = TC_GetNextChar(pEASData->hwInstData, pData, &temp)) != EAS_SUCCESS)
666         return result;
667 
668     /* check for version number */
669     if (temp == TC_FIELD_VERSION)
670     {
671         TC_GetNextChar(pEASData->hwInstData, pData, &temp);
672 //      { /* dpp: EAS_ReportEx(_EAS_SEVERITY_INFO, "ToneControl sequence version %d\n", temp); */ }
673     }
674     else
675         return EAS_ERROR_FILE_FORMAT;
676 
677     /* parse the header data until we find the first note or block */
678     for (;;)
679     {
680 
681         /* get next byte from stream */
682         if ((result = TC_GetNextChar(pEASData->hwInstData, pData, &temp)) != EAS_SUCCESS)
683             return result;
684 
685         /* check for tempo */
686         if (temp == TC_FIELD_TEMPO)
687         {
688             if ((result = TC_GetTempo(pEASData, pData)) != EAS_SUCCESS)
689                 return result;
690         }
691 
692         /* or resolution */
693         else if (temp == TC_FIELD_TEMPO)
694         {
695             if ((result = TC_GetResolution(pEASData, pData)) != EAS_SUCCESS)
696                 return result;
697         }
698 
699         /* must be music data */
700         else if (temp > TC_FIELD_INVALID)
701         {
702             TC_PutBackChar(pData, temp);
703             return EAS_SUCCESS;
704         }
705 
706         /* unknown codes */
707         else
708         {
709             { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "Unexpected byte 0x%02x in ToneControl stream\n", temp); */ }
710             return EAS_ERROR_FILE_FORMAT;
711         }
712     }
713 }
714 
715 /*----------------------------------------------------------------------------
716  * TC_StartNote()
717  *----------------------------------------------------------------------------
718  * Process a note or silence event
719  *----------------------------------------------------------------------------
720 */
TC_StartNote(S_EAS_DATA * pEASData,S_TC_DATA * pData,EAS_INT parserMode,EAS_I8 note)721 static EAS_RESULT TC_StartNote (S_EAS_DATA *pEASData, S_TC_DATA* pData, EAS_INT parserMode, EAS_I8 note)
722 {
723     EAS_I8 duration;
724 
725     /* get the duration */
726     if (TC_GetNextChar(pEASData->hwInstData, pData, &duration) != EAS_SUCCESS)
727         return EAS_ERROR_FILE_FORMAT;
728 
729     /* calculate time of next event */
730     pData->length = (EAS_I32) duration * pData->tick;
731     pData->time += pData->length;
732 
733     /* start the note */
734     if ((note >= 0) && (parserMode == eParserModePlay))
735     {
736         VMStartNote(pEASData->pVoiceMgr, pData->pSynth, TC_CHANNEL, (EAS_U8) note, pData->volume);
737         pData->note = note;
738     }
739 
740     return EAS_SUCCESS;
741 }
742 
743 /*----------------------------------------------------------------------------
744  * TC_GetRepeat()
745  *----------------------------------------------------------------------------
746  * Process a repeat code
747  *----------------------------------------------------------------------------
748 */
TC_GetRepeat(S_EAS_DATA * pEASData,S_TC_DATA * pData,EAS_INT parserMode)749 static EAS_RESULT TC_GetRepeat (S_EAS_DATA *pEASData, S_TC_DATA* pData, EAS_INT parserMode)
750 {
751     EAS_I8 count;
752 
753     /* get the repeat count */
754     if (TC_GetNextChar(pEASData->hwInstData, pData, &count) != EAS_SUCCESS)
755         return EAS_ERROR_FILE_FORMAT;
756 
757     /* validiate it */
758     if (count < 2)
759         return EAS_ERROR_FILE_FORMAT;
760 
761     /* calculate time of next event */
762     pData->time += pData->length;
763     pData->repeatCount = count - 2;
764 
765     /* start the note */
766     if ((pData->note >= 0) && (parserMode == eParserModePlay))
767         VMStartNote(pEASData->pVoiceMgr, pData->pSynth, TC_CHANNEL, (EAS_U8) pData->note, pData->volume);
768 
769     return EAS_SUCCESS;
770 }
771 
772 /*----------------------------------------------------------------------------
773  * TC_PlayBlock()
774  *----------------------------------------------------------------------------
775  * Play a block of notes
776  *----------------------------------------------------------------------------
777 */
TC_PlayBlock(S_EAS_DATA * pEASData,S_TC_DATA * pData)778 static EAS_RESULT TC_PlayBlock (S_EAS_DATA *pEASData, S_TC_DATA* pData)
779 {
780     EAS_RESULT result;
781     EAS_I8 blockNum;
782     EAS_I8 temp;
783     EAS_I8 temp2;
784 
785     /* get the block number */
786     if (TC_GetNextChar(pEASData->hwInstData, pData, &blockNum) != EAS_SUCCESS)
787         return EAS_ERROR_FILE_FORMAT;
788 
789     /* validiate it */
790     if (blockNum < 0)
791         return EAS_ERROR_FILE_FORMAT;
792 
793     /* save the current position */
794     if ((result = EAS_HWFilePos(pEASData->hwInstData, pData->fileHandle, &pData->restorePos)) != EAS_SUCCESS)
795         return result;
796 
797     /* return to start of file */
798     pData->byteAvail = EAS_FALSE;
799     if ((result = EAS_HWFileSeek(pEASData->hwInstData, pData->fileHandle, pData->fileOffset)) != EAS_SUCCESS)
800         return result;
801 
802     /* find the block */
803     for (;;)
804     {
805         if (TC_GetNextChar(pEASData->hwInstData, pData, &temp) != EAS_SUCCESS)
806             return EAS_ERROR_FILE_FORMAT;
807 
808         if (TC_GetNextChar(pEASData->hwInstData, pData, &temp2) != EAS_SUCCESS)
809             return EAS_ERROR_FILE_FORMAT;
810 
811         if ((temp == TC_FIELD_BLOCK_START) && (temp2 == blockNum))
812             return EAS_SUCCESS;
813     }
814 }
815 
816 /*----------------------------------------------------------------------------
817  * TC_BlockEnd()
818  *----------------------------------------------------------------------------
819  * Handle end of block
820  *----------------------------------------------------------------------------
821 */
TC_BlockEnd(S_EAS_DATA * pEASData,S_TC_DATA * pData)822 static EAS_RESULT TC_BlockEnd (S_EAS_DATA *pEASData, S_TC_DATA* pData)
823 {
824     EAS_I8 blockNum;
825 
826     /* get the block number */
827     if (TC_GetNextChar(pEASData->hwInstData, pData, &blockNum) != EAS_SUCCESS)
828         return EAS_ERROR_FILE_FORMAT;
829 
830     /* validiate it */
831     if (blockNum < 0)
832         return EAS_ERROR_FILE_FORMAT;
833 
834     /* if we were playing this block, restore to previous position */
835     pData->byteAvail = EAS_FALSE;
836     return EAS_HWFileSeek(pEASData->hwInstData, pData->fileHandle, pData->restorePos);
837 }
838 
839 /*----------------------------------------------------------------------------
840  * TC_GetVolume()
841  *----------------------------------------------------------------------------
842  * Get the volume field and process it
843  *----------------------------------------------------------------------------
844 */
TC_GetVolume(S_EAS_DATA * pEASData,S_TC_DATA * pData)845 static EAS_RESULT TC_GetVolume (S_EAS_DATA *pEASData, S_TC_DATA* pData)
846 {
847     EAS_I8 volume;
848 
849     /* get volume */
850     if (TC_GetNextChar(pEASData->hwInstData, pData, &volume) != EAS_SUCCESS)
851         return EAS_ERROR_FILE_FORMAT;
852     if ((volume < 0) || (volume > 100))
853         return EAS_ERROR_FILE_FORMAT;
854 
855     /* save volume */
856     pData->volume = (EAS_U8) ((EAS_I32) (volume * TC_VOLUME_CONV + 1) >> TC_VOLUME_SHIFT);
857     return EAS_SUCCESS;
858 }
859 
860 /*----------------------------------------------------------------------------
861  * TC_GetTempo()
862  *----------------------------------------------------------------------------
863  * Get the tempo field and process it
864  *----------------------------------------------------------------------------
865 */
TC_GetTempo(S_EAS_DATA * pEASData,S_TC_DATA * pData)866 static EAS_RESULT TC_GetTempo (S_EAS_DATA *pEASData, S_TC_DATA* pData)
867 {
868     EAS_I8 tempo;
869 
870     /* get tempo */
871     if (TC_GetNextChar(pEASData->hwInstData, pData, &tempo) != EAS_SUCCESS)
872         return EAS_ERROR_FILE_FORMAT;
873     if (tempo < 5)
874         return EAS_ERROR_FILE_FORMAT;
875 
876     /* save tempo */
877     pData->tempo = tempo;
878 
879     /* calculate new timebase */
880     TC_CalcTimeBase(pData);
881     return EAS_SUCCESS;
882 }
883 
884 /*----------------------------------------------------------------------------
885  * TC_GetResolution()
886  *----------------------------------------------------------------------------
887  * Get the resolution field and process it
888  *----------------------------------------------------------------------------
889 */
TC_GetResolution(S_EAS_DATA * pEASData,S_TC_DATA * pData)890 static EAS_RESULT TC_GetResolution (S_EAS_DATA *pEASData, S_TC_DATA* pData)
891 {
892     EAS_I8 resolution;
893 
894     /* get resolution */
895     if (TC_GetNextChar(pEASData->hwInstData, pData, &resolution) != EAS_SUCCESS)
896         return EAS_ERROR_FILE_FORMAT;
897     if (resolution < 0)
898         return EAS_ERROR_FILE_FORMAT;
899 
900     /* save tempo */
901     pData->resolution = resolution;
902 
903     /* calculate new timebase */
904     TC_CalcTimeBase(pData);
905     return EAS_SUCCESS;
906 }
907 
908 /*----------------------------------------------------------------------------
909  * TC_GetNextChar()
910  *----------------------------------------------------------------------------
911  * Fetch the next character from the stream
912  *----------------------------------------------------------------------------
913 */
TC_GetNextChar(EAS_HW_DATA_HANDLE hwInstData,S_TC_DATA * pData,EAS_I8 * pValue)914 static EAS_RESULT TC_GetNextChar (EAS_HW_DATA_HANDLE hwInstData, S_TC_DATA *pData, EAS_I8 *pValue)
915 {
916 
917     /* get character from "put back" buffer */
918     if (pData->byteAvail)
919     {
920         pData->byteAvail = EAS_FALSE;
921         *pValue = pData->dataByte;
922         return EAS_SUCCESS;
923     }
924 
925     /* get character from file */
926     return EAS_HWGetByte(hwInstData, pData->fileHandle, pValue);
927 }
928 
929 /*----------------------------------------------------------------------------
930  * TC_PutBackChar()
931  *----------------------------------------------------------------------------
932  * Put back the character
933  *----------------------------------------------------------------------------
934 */
TC_PutBackChar(S_TC_DATA * pData,EAS_I8 value)935 static void TC_PutBackChar (S_TC_DATA *pData, EAS_I8 value)
936 {
937 
938     pData->dataByte = value;
939     pData->byteAvail = EAS_TRUE;
940 }
941 
942