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