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