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