1 /*---------------------------------------------------------------------------*
2   Project:  MIDI sequencer application for AX synthesizer
3   File:     seq.c
4 
5   Copyright (C)1998-2006 Nintendo  All Rights Reserved.
6 
7   These coded instructions, statements, and computer programs contain
8   proprietary information of Nintendo of America Inc. and/or Nintendo
9   Company Ltd., and are protected by Federal copyright law.  They may
10   not be disclosed to third parties or copied or duplicated in any form,
11   in whole or in part, without the prior written consent of Nintendo.
12 
13   $Log: seq.c,v $
14   Revision 1.7  2007/08/08 02:43:07  aka
15   Added __init flag.
16 
17   Revision 1.6  2006/12/27 04:17:23  aka
18   Revised comments.
19 
20   Revision 1.5  2006/12/27 02:51:14  aka
21   Revised comments.
22 
23   Revision 1.4  2006/11/21 04:10:55  aka
24   Removed assertion of the zero buffer.
25 
26   Revision 1.3  2006/01/31 06:35:21  aka
27   Changed arguments of SEQAddSequence().
28 
29   Revision 1.2  2005/11/08 01:43:00  aka
30   Changed suiting to Revolution's audio spec.
31 
32   Revision 1.1.1.1  2005/05/12 02:15:50  yasuh-to
33   Imported from dolphin tree.
34 
35     5     03/03/05 3:39p Akagi
36     Fixed timing of calling controller callback.
37 
38     4     6/14/02 1:35p Billyjack
39     - fix for tempo... used to be a tiny bit slow
40 
41     3     8/16/01 12:26p Billyjack
42     added zeroBuffer offset to API
43 
44     2     5/11/01 4:17p Billyjack
45     fixed problem with songs ending and set state to SEQ_STATE_RUN
46 
47     1     5/09/01 1:14p Billyjack
48     created
49 
50   $NoKeywords: $
51  *---------------------------------------------------------------------------*/
52 #include <revolution.h>
53 #include <revolution/seq.h>
54 
55 /*---------------------------------------------------------------------------*
56     parse MIDI chunk name
57  *---------------------------------------------------------------------------*/
58 #define __SEQChunkName(a, b, c, d)(   \
59             ((a & 0xff) << 24)  |   \
60             ((b & 0xff) << 16)  |   \
61             ((c & 0xff) << 8)   |   \
62             (d & 0xff))
63 
64 
65 /*---------------------------------------------------------------------------*
66     table for MIDI event length in bytes
67  *---------------------------------------------------------------------------*/
68 static u8 __SEQMidiEventLength[] =
69 {
70     2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, //  0x80 - 0x8F
71     2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, //  0x90 - 0x9F
72     2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, //  0xA0 - 0xAF
73     2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, //  0xB0 - 0xBF
74     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, //  0xC0 - 0xCF
75     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, //  0xD0 - 0xDF
76     2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, //  0xE0 - 0xEF
77     0, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0  //  0xF0 - 0xFF
78 };
79 
80 /*---------------------------------------------------------------------------*
81     list of sequences to run
82  *---------------------------------------------------------------------------*/
83 static SEQSEQUENCE *__SEQSequenceList;
84 
85 /*---------------------------------------------------------------------------*
86     Internal variables for the mixer
87  *---------------------------------------------------------------------------*/
88 static BOOL __init = FALSE;
89 
90 /*---------------------------------------------------------------------------*
91     push the specified sequence into the sequence list
92  *---------------------------------------------------------------------------*/
__SEQPushSequenceList(SEQSEQUENCE * sequence)93 static void __SEQPushSequenceList(SEQSEQUENCE *sequence)
94 {
95     int old;
96 
97     old = OSDisableInterrupts();
98 
99     if (__SEQSequenceList)
100         sequence->next      = __SEQSequenceList;
101     else
102         sequence->next      = NULL;
103 
104     __SEQSequenceList       = sequence;
105 
106     OSRestoreInterrupts(old);
107 }
108 
109 
110 /*---------------------------------------------------------------------------*
111     remove the specified sequence from list
112  *---------------------------------------------------------------------------*/
__SEQRemoveSequenceFromList(SEQSEQUENCE * sequence)113 static void __SEQRemoveSequenceFromList(SEQSEQUENCE *sequence)
114 {
115     int         old;
116     SEQSEQUENCE *thisSequence;
117 
118     old = OSDisableInterrupts();
119 
120     thisSequence        = __SEQSequenceList;
121     __SEQSequenceList   = NULL;
122 
123     while (thisSequence)
124     {
125         SEQSEQUENCE *next = thisSequence->next;
126 
127         if (thisSequence != sequence)
128             __SEQPushSequenceList(thisSequence);
129 
130         thisSequence = next;
131     }
132 
133     OSRestoreInterrupts(old);
134 }
135 
136 
137 /*---------------------------------------------------------------------------*
138     get MIDI variable length integer
139  *---------------------------------------------------------------------------*/
__SEQGetIntTrack(SEQTRACK * track)140 static u32 __SEQGetIntTrack(SEQTRACK *track)
141 {
142     u32 value;
143 
144     ASSERT(track);
145 
146     value = (u32)(*track->current & 0x7f);
147 
148     while (*track->current & 0x80)
149     {
150         track->current++;
151         value = (value << 7) + (*track->current & 0x7f);
152     }
153 
154     track->current++;
155 
156     return value;
157 }
158 
159 
160 /*---------------------------------------------------------------------------*
161     system exclusive events
162  *---------------------------------------------------------------------------*/
__SEQHandleSysExEvent(SEQTRACK * track)163 static void __SEQHandleSysExEvent(SEQTRACK *track)
164 {
165     u32 length;
166 
167     ASSERT(track);
168 
169     length              =   __SEQGetIntTrack(track);
170     track->current      +=  length;
171 }
172 
173 
174 /*---------------------------------------------------------------------------*
175     set the ticks per frame based on beats per second
176  *---------------------------------------------------------------------------*/
__SEQSetTicksPerFrame(SEQTRACK * track,f32 bps)177 static void __SEQSetTicksPerFrame(SEQTRACK *track, f32 bps)
178 {
179     SEQSEQUENCE *sequence;
180 
181     ASSERT(track);
182 
183     sequence = (SEQSEQUENCE*)track->sequence;
184 
185     track->beatsPerSec      = bps;
186     track->ticksPerFrame    =
187         (u32)(0x00010000 * (
188                   (f32)AX_IN_SAMPLES_PER_FRAME / (   // 96 samples / frame
189                       (f32)AX_IN_SAMPLES_PER_SEC  /  // 32000Hz
190                       bps /
191                       sequence->timeFormat)));
192 }
193 
194 
195 /*---------------------------------------------------------------------------*
196     handle tempo meta events
197  *---------------------------------------------------------------------------*/
__SEQTempoMetaEvent(SEQTRACK * track)198 static void __SEQTempoMetaEvent(SEQTRACK *track)
199 {
200     u32         data;
201     f32         beatsPerSec;
202 
203     data = (u32)(*track->current);
204     track->current++;
205     data = (data << 8) + (u32)(*track->current);
206     track->current++;
207     data = (data << 8) + (u32)(*track->current);
208     track->current++;
209 
210     beatsPerSec = 1000000.0f / data;
211 
212     __SEQSetTicksPerFrame(track, beatsPerSec);
213 }
214 
215 
216 /*---------------------------------------------------------------------------*
217     rewind the track to starting position
218  *---------------------------------------------------------------------------*/
__SEQTrackEnd(SEQTRACK * track)219 static void __SEQTrackEnd(SEQTRACK *track)
220 {
221     SEQSEQUENCE *sequence;
222 
223     ASSERT(track);
224 
225     sequence = track->sequence;
226     sequence->tracksRunning--;
227 
228     track->state = SEQ_STATE_STOP;
229 
230     if (sequence->tracksRunning == 0)
231         sequence->end = 1;
232 }
233 
234 /*---------------------------------------------------------------------------*
235     handle meta events
236  *---------------------------------------------------------------------------*/
__SEQHandleMetaEvent(SEQTRACK * track)237 static void __SEQHandleMetaEvent(SEQTRACK *track)
238 {
239     u8  type;
240     u32 length;
241 
242     ASSERT(track);
243 
244     type = *track->current;
245     track->current++;
246 
247     switch (type)
248     {
249     case 47:    // end of track
250 
251         __SEQTrackEnd(track);
252 
253         break;
254 
255     case 81:    // tempo
256 
257         length =   __SEQGetIntTrack(track);
258         __SEQTempoMetaEvent(track);
259 
260         break;
261 
262     default:
263 
264         length =   __SEQGetIntTrack(track);
265         track->current  +=  length;
266 
267         break;
268     }
269 }
270 
271 
272 /*---------------------------------------------------------------------------*
273     handle MIDI events to pass to the synth
274  *---------------------------------------------------------------------------*/
__SEQHandleSynthEvent(SYNSYNTH * synth,SEQTRACK * track)275 static void __SEQHandleSynthEvent(SYNSYNTH *synth, SEQTRACK *track)
276 {
277     u8  ch[3];
278     u32 bytes;
279 
280     bytes = __SEQMidiEventLength[track->status - 0x80];
281 
282     ch[0] = track->status;
283 
284     switch (bytes)
285     {
286     case 0:
287 
288         break;
289 
290     case 1:
291 
292         ch[1] = *track->current;
293         track->current++;
294 
295         break;
296 
297     case 2:
298 
299         ch[1] = *track->current;
300         track->current++;
301         ch[2] = *track->current;
302         track->current++;
303 
304         break;
305     }
306 
307     // perform controller callback if any
308     if ((ch[0] & 0xf0) == 0xb0)
309     {
310         SEQCALLBACK callback = ((SEQSEQUENCE*)(track->sequence))->callback[ch[1]];
311 
312         if (callback)
313             (*callback)(track, ch[1]);
314     }
315 
316     SYNMidiInput(synth, ch);
317 }
318 
319 
320 /*---------------------------------------------------------------------------*
321     run the next event on the MIDI stream
322  *---------------------------------------------------------------------------*/
__SEQRunEvent(SYNSYNTH * synth,SEQTRACK * track)323 static void __SEQRunEvent(SYNSYNTH *synth, SEQTRACK *track)
324 {
325     u8 event;
326 
327     ASSERT(synth);
328     ASSERT(track);
329 
330     event = *track->current;
331 
332     if (event >= 0x80)
333     {
334         track->status = event;
335         track->current++;
336     }
337 
338     switch (track->status)
339     {
340     case 0xf0:  // system exclusive
341     case 0xf7:  // special system exclusive
342 
343         __SEQHandleSysExEvent(track);
344 
345         break;
346 
347     case 0xff:  // meta events
348 
349         __SEQHandleMetaEvent(track);
350 
351         break;
352 
353     default:    // send the event to the synth
354 
355         __SEQHandleSynthEvent(synth, track);
356 
357         break;
358     }
359 
360     if (track->current >= track->end)
361         __SEQTrackEnd(track);
362 }
363 
364 
365 /*---------------------------------------------------------------------------*
366     initialize tracks
367  *---------------------------------------------------------------------------*/
__SEQInitTracks(SEQSEQUENCE * sequence,u8 * read,int tracks)368 static void __SEQInitTracks(SEQSEQUENCE *sequence, u8 *read, int tracks)
369 {
370     int i;
371     u8  *p;
372 
373     i = 0;
374     p = read;
375 
376     while (tracks)
377     {
378         while(1)
379         {
380             u32 chunk;
381             u32 bytes;
382 
383             chunk   = *(u32*)p; p += 4;
384             bytes   = *(u32*)p; p += 4;
385 
386             if (chunk == __SEQChunkName('M', 'T', 'r', 'k'))
387             {
388                 SEQTRACK *track = &sequence->track[i];
389 
390                 track->sequence             = sequence;
391                 track->start                = p;
392                 track->end                  = p + bytes;
393                 track->current              = p;
394                 track->defaultTicksPerFrame =
395                     (u32)(0x00010000 * (
396                               (f32)AX_IN_SAMPLES_PER_FRAME / (   // 96 samples / frame
397                                   60.0f / 120.0f *
398                                   (f32)AX_IN_SAMPLES_PER_SEC  /  // 32000Hz
399                                   sequence->timeFormat)));
400                 track->state                = SEQ_STATE_STOP;
401 
402                 p += bytes;
403 
404                 break;
405             }
406 
407             p += bytes;
408         }
409 
410         tracks--;
411         i++;
412     }
413 }
414 
415 
416 /*---------------------------------------------------------------------------*
417     reads header from MIDI stream
418  *---------------------------------------------------------------------------*/
__SEQReadHeader(SEQSEQUENCE * sequence,u8 * midiStream)419 static void __SEQReadHeader(SEQSEQUENCE *sequence, u8 *midiStream)
420 {
421     u8  *read;
422     u32 bytes;
423     u32 fileType;
424 
425     read = midiStream;
426 
427     ASSERTMSG(
428         *(u32*)read == __SEQChunkName('M', 'T', 'h', 'd'),
429         "!!!midiStream is not a valid MIDI file\n!!!"
430         );
431 
432     read += 4;
433 
434     bytes = *(u32*)read;
435 
436     read += 4;
437 
438     fileType                = *(u16*)(read);    read += 2;
439     sequence->nTracks       = *(u16*)(read);    read += 2;
440     sequence->timeFormat    = *(s16*)(read);    read += 2;
441 
442     ASSERTMSG(
443         sequence->timeFormat >= 0,
444         "!!!SEQ does not support SMPTE time!!!\n"
445         );
446 
447     bytes -= 6;
448 
449     read += bytes;
450 
451     // load up tracks according to file type
452     switch (fileType)
453     {
454     case 0: // type 0 only play first track
455 
456         sequence->nTracks = 1;
457 
458         __SEQInitTracks(sequence, read, 1);
459 
460         break;
461 
462     case 1:
463 
464         ASSERTMSG(
465             sequence->nTracks < SEQ_MAX_TRACKS,
466             "exceeded SEQ_MAX_TRACKS, please increase SEQ_MAX_TRACKS\n"
467             );
468 
469         __SEQInitTracks(sequence, read, sequence->nTracks);
470 
471         break;
472 
473     default:
474 
475         ASSERTMSG(0, "!!!Invalid MIDI file type\n!!!");
476 
477         break;
478     }
479 
480     sequence->tracksRunning = sequence->nTracks;
481 }
482 
483 
484 /*---------------------------------------------------------------------------*
485     Exposed API functions
486  *---------------------------------------------------------------------------*/
487 
488 /*---------------------------------------------------------------------------*
489     initialize the sequencer
490  *---------------------------------------------------------------------------*/
SEQInit(void)491 void SEQInit(void)
492 {
493     if (__init)
494     {
495         return;
496     }
497 
498     __SEQSequenceList = NULL;
499 
500     __init = TRUE;
501 }
502 
503 
504 /*---------------------------------------------------------------------------*
505     quit the sequencer gracefully
506  *---------------------------------------------------------------------------*/
SEQQuit(void)507 void SEQQuit(void)
508 {
509     __SEQSequenceList = NULL;
510 
511     __init = FALSE;
512 }
513 
514 
515 /*---------------------------------------------------------------------------*
516     run one audio frame's worth of events
517  *---------------------------------------------------------------------------*/
SEQRunAudioFrame(void)518 void SEQRunAudioFrame(void)
519 {
520     SEQSEQUENCE *sequence = __SEQSequenceList;
521 
522     if (!__init)
523     {
524         return;
525     }
526 
527     // go through all the sequences
528     while (sequence)
529     {
530         if ((sequence->state == SEQ_STATE_RUN) ||
531             (sequence->state == SEQ_STATE_RUNLOOPED))
532         {
533             u32 i;
534 
535             for (i = 0; i < sequence->nTracks; i++)
536             {
537                 SEQTRACK *track = &sequence->track[i];
538 
539                 if ((track->state == SEQ_STATE_RUN) ||
540                     (track->state == SEQ_STATE_RUNLOOPED))
541                 {
542                     u32 ticks = track->ticksPerFrame;
543 
544                     if (track->delay > ticks)
545                     {
546                         track->delay -= ticks;
547                     }
548                     else
549                     {
550                         while (ticks >= track->delay)
551                         {
552                             ticks -= track->delay;
553                             __SEQRunEvent(&sequence->synth, track);
554 
555                             if (track->state == SEQ_STATE_STOP)
556                                 break;
557 
558                             track->delay =  __SEQGetIntTrack(track) << 16;
559                         }
560 
561                         track->delay -= ticks;
562                     }
563                 }
564             }
565         }
566 
567         if (sequence->end)
568         {
569             if (sequence->state == SEQ_STATE_RUNLOOPED)
570             {
571                 SEQSetState(sequence, SEQ_STATE_STOP);
572                 SEQSetState(sequence, SEQ_STATE_RUNLOOPED);
573             }
574             else
575             {
576                 SEQSetState(sequence, SEQ_STATE_STOP);
577             }
578         }
579 
580         sequence = sequence->next;
581     }
582 }
583 
584 
585 /*---------------------------------------------------------------------------*
586     initialize and add the specified sequence to sequence list
587  *---------------------------------------------------------------------------*/
588 void
SEQAddSequence(SEQSEQUENCE * sequence,u8 * midiStream,u8 * wavetable,u8 * samples,u8 * zerobuffer,u32 priorityVoiceAlloc,u32 priorityNoteOn,u32 priorityNoteRelease)589 SEQAddSequence(
590     SEQSEQUENCE     *sequence,          // user allocated SEQSEQUENCE
591     u8              *midiStream,        // pointer to MIDI stream
592     u8              *wavetable,         // pointer to wave table
593     u8              *samples,           // pointer to samples
594     u8              *zerobuffer,        // pointer to zero buffer
595     u32             priorityVoiceAlloc, // priority for allocating notes
596     u32             priorityNoteOn,     // priority for notes that are on
597     u32             priorityNoteRelease // priority for notes in release stage
598     )
599 {
600     int i;
601 
602     ASSERT(sequence);
603     ASSERT(midiStream);
604     ASSERT(wavetable);
605     ASSERT(samples);
606     //ASSERT(zerobuffer);
607     ASSERT((priorityVoiceAlloc < 32) && (priorityVoiceAlloc > 0));
608     ASSERT((priorityNoteOn < 32) && (priorityNoteOn > 0));
609     ASSERT((priorityNoteRelease < 32) && (priorityNoteRelease > 0));
610 
611     // initialize the synth
612     SYNInitSynth(
613         &sequence->synth,
614         wavetable,
615         samples,
616         zerobuffer,
617         priorityVoiceAlloc,
618         priorityNoteOn,
619         priorityNoteRelease
620         );
621 
622     // initialize data members
623     sequence->state = SEQ_STATE_STOP;
624 
625     // 0 controller callbacks
626     for (i = 0; i < 128; i++)
627     {
628         sequence->callback[i] = NULL;
629     }
630 
631     // read the MIDI file header
632     __SEQReadHeader(sequence, midiStream);
633 
634     // put it in a list of sequences to service
635     __SEQPushSequenceList(sequence);
636 }
637 
638 
639 /*---------------------------------------------------------------------------*
640     remove the specified sequence from sequence list
641  *---------------------------------------------------------------------------*/
SEQRemoveSequence(SEQSEQUENCE * sequence)642 void SEQRemoveSequence(SEQSEQUENCE *sequence)
643 {
644     ASSERT(sequence);
645 
646     // remove from list of running sequences
647     __SEQRemoveSequenceFromList(sequence);
648 
649     // shut down the synth for the sequence
650     SYNQuitSynth(&sequence->synth);
651 }
652 
653 
654 /*---------------------------------------------------------------------------*
655     register callback for controller event
656  *---------------------------------------------------------------------------*/
SEQRegisterControllerCallback(SEQSEQUENCE * sequence,u8 controller,SEQCALLBACK callback)657 void SEQRegisterControllerCallback(
658             SEQSEQUENCE     *sequence,          // user initialized SEQSEQUENCE
659             u8              controller,         // MIDI controller
660             SEQCALLBACK     callback            // callback function
661             )
662 {
663     ASSERT(sequence);
664     ASSERT(controller < 128);
665     ASSERT(callback);
666 
667     sequence->callback[controller] = callback;
668 }
669 
670 
671 /*---------------------------------------------------------------------------*
672     set specified sequence to specified state
673  *---------------------------------------------------------------------------*/
SEQSetState(SEQSEQUENCE * sequence,u32 state)674 void SEQSetState(SEQSEQUENCE *sequence, u32 state)
675 {
676     int i;
677 
678     ASSERT(sequence);
679 
680     switch (state)
681     {
682     case SEQ_STATE_RUN:
683     case SEQ_STATE_RUNLOOPED:
684 
685         // if the previous state was SEQ_STATE_STOP start the tracks from
686         // the beginning
687         if (sequence->state == SEQ_STATE_STOP)
688         {
689             int old = OSDisableInterrupts();
690 
691             for (i = 0; i < sequence->nTracks; i++)
692             {
693 
694                 SEQTRACK *track = &sequence->track[i];
695 
696 
697                 track->current          = track->start;
698                 track->ticksPerFrame    = track->defaultTicksPerFrame;
699                 track->delay            = __SEQGetIntTrack(track) << 16;
700                 track->state            = SEQ_STATE_RUN;
701 
702             }
703 
704             sequence->tracksRunning = sequence->nTracks;
705 
706             OSRestoreInterrupts(old);
707         }
708 
709         sequence->end = 0;
710 
711         break;
712 
713         // we also to silence all sounds from the synth
714     case SEQ_STATE_STOP:
715     case SEQ_STATE_PAUSE:
716 
717         for (i = 0; i < 16; i++)
718         {
719             int old;
720 
721             u8 ch[3];
722 
723             old = OSDisableInterrupts();
724 
725             ch[0] = (u8)(0xb0 | i);
726             ch[1] = 0x7b;
727             ch[2] = 0;
728 
729             SYNMidiInput(&sequence->synth, ch);
730 
731             OSRestoreInterrupts(old);
732         }
733 
734         break;
735     }
736 
737     sequence->state = state;
738 }
739 
740 
741 /*---------------------------------------------------------------------------*
742     get current state of sequence
743  *---------------------------------------------------------------------------*/
SEQGetState(SEQSEQUENCE * sequence)744 u32 SEQGetState(SEQSEQUENCE *sequence)
745 {
746     ASSERT(sequence);
747 
748     return sequence->state;
749 }
750 
751 
752 /*---------------------------------------------------------------------------*
753     set specifiec track(s) or specified sequence to specified BPM
754  *---------------------------------------------------------------------------*/
SEQSetTempo(SEQSEQUENCE * sequence,u32 trackIndex,f32 bpm)755 void SEQSetTempo(SEQSEQUENCE *sequence, u32 trackIndex, f32 bpm)
756 {
757     ASSERT(sequence);
758     ASSERT((trackIndex < sequence->nTracks) || (trackIndex == SEQ_ALL_TRACKS));
759 
760     if (trackIndex == SEQ_ALL_TRACKS)
761     {
762         int i;
763 
764         for (i = 0; i < sequence->nTracks; i++)
765             __SEQSetTicksPerFrame(&sequence->track[i], bpm / 60);
766     }
767     else
768     {
769         __SEQSetTicksPerFrame(&sequence->track[trackIndex], bpm / 60);
770     }
771 }
772 
773 
774 /*---------------------------------------------------------------------------*
775     get current tempo for specified track of specified sequencer
776  *---------------------------------------------------------------------------*/
SEQGetTempo(SEQSEQUENCE * sequence,u32 trackIndex)777 f32 SEQGetTempo(SEQSEQUENCE *sequence, u32 trackIndex)
778 {
779     ASSERT(sequence);
780     ASSERT(trackIndex < sequence->nTracks);
781 
782     return sequence->track[trackIndex].beatsPerSec * 60;
783 }
784 
785 
786 /*---------------------------------------------------------------------------*
787     set specified sequence to specified volume 0x00000001 = 0.1dB
788  *---------------------------------------------------------------------------*/
SEQSetVolume(SEQSEQUENCE * sequence,s32 dB)789 void SEQSetVolume(SEQSEQUENCE *sequence, s32 dB)
790 {
791     ASSERT(sequence);
792 
793     SYNSetMasterVolume(&sequence->synth, dB);
794 }
795 
796 
797 /*---------------------------------------------------------------------------*
798     get current volume for specified sequence
799  *---------------------------------------------------------------------------*/
SEQGetVolume(SEQSEQUENCE * sequence)800 s32 SEQGetVolume(SEQSEQUENCE *sequence)
801 {
802     ASSERT(sequence);
803 
804     return SYNGetMasterVolume(&sequence->synth);
805 }
806