eas_tonecontrol.c revision 56c99cd2c2c1e6ab038dac5fced5b92ccf11ff6c
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 */
63static EAS_RESULT TC_CheckFileType (S_EAS_DATA *pEASData, EAS_FILE_HANDLE fileHandle, EAS_VOID_PTR *ppHandle, EAS_I32 offset);
64static EAS_RESULT TC_Prepare (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData);
65static EAS_RESULT TC_Time (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_U32 *pTime);
66static EAS_RESULT TC_Event (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_INT parserMode);
67static EAS_RESULT TC_State (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_STATE *pState);
68static EAS_RESULT TC_Close (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData);
69static EAS_RESULT TC_Reset (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData);
70static EAS_RESULT TC_Pause (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData);
71static EAS_RESULT TC_Resume (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData);
72static EAS_RESULT TC_SetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 value);
73static EAS_RESULT TC_GetData (S_EAS_DATA *pEASData, EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 *pValue);
74static EAS_RESULT TC_ParseHeader (S_EAS_DATA *pEASData, S_TC_DATA* pData);
75static EAS_RESULT TC_StartNote (S_EAS_DATA *pEASData, S_TC_DATA* pData, EAS_INT parserMode, EAS_I8 note);
76static EAS_RESULT TC_GetRepeat (S_EAS_DATA *pEASData, S_TC_DATA* pData, EAS_INT parserMode);
77static EAS_RESULT TC_PlayBlock (S_EAS_DATA *pEASData, S_TC_DATA* pData);
78static EAS_RESULT TC_BlockEnd (S_EAS_DATA *pEASData, S_TC_DATA* pData);
79static EAS_RESULT TC_GetVolume (S_EAS_DATA *pEASData, S_TC_DATA* pData);
80static EAS_RESULT TC_GetTempo (S_EAS_DATA *pEASData, S_TC_DATA* pData);
81static EAS_RESULT TC_GetResolution (S_EAS_DATA *pEASData, S_TC_DATA* pData);
82static EAS_RESULT TC_GetNextChar (EAS_HW_DATA_HANDLE hwInstData, S_TC_DATA *pData, EAS_I8 *pValue);
83static void TC_PutBackChar (S_TC_DATA *pData, EAS_I8 value);
84
85/* calculate a new tick time based on resolution & tempo */
86EAS_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*/
100const 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*/
134static 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*/
186static 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 */
227static 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*/
256static 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 */
389static 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*/
431static 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*/
470static 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*/
511static 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 */
544static 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 */
576static 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 */
603static 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*/
643static 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*/
721static 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*/
749static 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*/
778static 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*/
822static 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*/
845static 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*/
866static 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*/
890static 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*/
914static 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*/
935static void TC_PutBackChar (S_TC_DATA *pData, EAS_I8 value)
936{
937
938    pData->dataByte = value;
939    pData->byteAvail = EAS_TRUE;
940}
941
942