1) Sequenced by Andrew Frazier, ACCOLADE |
In this tool-assisted education video, I demonstrate how to write a MIDI
player from scratch. MIDI is a Music Interchange file format which describes music in abstract terms such as "piano key C-4 pressed down". A synthesizer is required to reproduce audible music from MIDI files. In this tool-assisted education video, I create one such synthesizer program from scratch, using the OPL3 hardware synthesizer as the backend for generating the actual sounds. You see me type every line. The resulting program contains the rudimentary features for playing most MIDI files just fine, including the pitch slides and pans, but it lacks other special effects like vibrato, nor does it support any more tunings than the standard equal-interval mathematical tuning. It supports the loopStart/loopEnd mechanism found in authentic Final Fantasy VII MIDI files. This is probably my best MS-DOS MIDI player so far (at least in some
aspects), and I have written two. The first one, written in 1998, however
eventually evolved also to convert them into S3Ms… Music played during this video: |
Since it can be hard to follow the big picture of what's going on with the
source code in this video, I placed the syntax-highlighted source code
available at the following web address:
http://bisqwit.iki.fi/jutut/kuvat/programming_examples/midiplay.html
P.S. Eventually this program evolved into a C++ program,
AdlMIDI, that is
actually quite formidable a MIDI player already! Check it out here.
0 REM MIDI PLAYER EXAMPLE PROGRAM COPYRIGHT (C) 2010 JOEL YLILUOMA 1 DEFINT A-Z: OPTION BASE 0: KEY OFF: REM http://iki.fi/bisqwit/ 2 DIM adl(180, 11), chins(17), chpan(17), chpit(17) 3 FOR i = 0 TO 180: FOR x = 0 TO 11: READ adl(i, x): NEXT x, i 4 GOTO 100 ' Begin program 5 'Set up OPL parameters (In: c = channel (0..17)) 6 'Out: p=I/O port, q = per-OPL channel (0..8), o=operator offset 7 p = &H388 + 2 * (c\9): q = c MOD 9: o = (q MOD 3) + 8 * (q\3): RETURN 8 'OPL_NoteOff(c): c=channel. CHANGES: q,p,o 9 GOSUB 5: OUT p, &HB0 + q: OUT p+1, chpit(c) AND &HDF: RETURN 10 'OPL_NoteOn(c,h#): c=channel,h#=hertz (0..131071). CHANGES: q,p,o,x 11 GOSUB 5 12 x = &H2000: WHILE h# >= 1023.5: h# = h#/2: x=x+&H400: WEND 'Calculate octave 13 x = x + CINT(h#) 14 OUT p, &HA0 + q: OUT p+1, x AND 255: x = x \ 256 15 OUT p, &HB0 + q: OUT p+1, x: chpit(c) = x: RETURN 16 'Technically, AdLib requires some delay between consecutive OUT commands. 17 'However, BASIC is slow enough, that this is not an issue really. 18 'The paradigm "OUT p,index: OUT p+1,value" works perfectly. 20 'OPL_Touch(c,v): c=channel, v=volume. CHANGES: q,p,o,i,v 21 'The formula below: SOLVE(V=127*127 * 2^( (A-63) / 8), A) 22 IF v < 72 THEN v = 0 ELSE v = LOG(v) * 11.541561# - 48.818955# 25 'OPL_Touch_Real(c,v): Same as OPL_Touch, except takes logarithmic volume. 26 GOSUB 5: i = chins(c) 27 OUT p, &H40+o: q = adl(i, 2): GOSUB 29 28 OUT p, &H43+o: q = adl(i, 3) 29 OUT p+1, (q OR 63) - v + ((q AND 63) * v) \ 63: RETURN 30 'OPL_Patch(c): c=channel. CHANGES: q,p,o,x,i 31 GOSUB 5: i = chins(c): q = p+1 32 FOR x = 0 TO 1 33 OUT p, &H20+o+x*3: OUT q, adl(i, 0+x) 34 OUT p, &H60+o+x*3: OUT q, adl(i, 4+x) 35 OUT p, &H80+o+x*3: OUT q, adl(i, 6+x) 36 OUT p, &HE0+o+x*3: OUT q, adl(i, 8+x) 37 NEXT: RETURN 38 'OPL_Pan(c): c=channel. CHANGES: q,p,o 39 GOSUB 5: OUT p, &HC0+q: OUT p+1, adl(chins(c),10)-chpan(c): RETURN 40 'OPL_Reset(). CHANGES: c,q,p,o,x,y,v,i 41 'Detect OPL3 (send pulses to timer, opl3 enable register) 42 c=0: GOSUB 5: FOR y = 3 TO 4: OUT p, 4: OUT p+1, y*32: NEXT: ?INP(p) 43 c=9: GOSUB 5: FOR y = 0 TO 2: OUT p, 5: OUT p+1, y AND 1: NEXT 44 'Reset OPL3 (turn off all notes, set vital settings) 45 c=0: GOSUB 5: OUT p, 1: OUT p+1, 32 'Enable wave 46 c=0: GOSUB 5: OUT p,&HBD:OUT p+1, 0 'Set melodic mode 47 c=9: GOSUB 5: OUT p, 5: OUT p+1, 1 'Enable OPL3 48 c=9: GOSUB 5: OUT p, 4: OUT p+1, 0 'Select mode 0 (also try 63 for fun!) 50 'OPL_Silence(): Silence all OPL channels. CHANGES q,p,o,v,c,i 51 v = 0: FOR c = 0 TO 17: GOSUB 8: GOSUB 25: NEXT: RETURN 70 'ReadString(): Read N bytes from file. Input: x. Output: s$. CHANGES x 71 s$ = "": WHILE x > 0: x = x - 1: GET #1: s$ = s$ + b$: WEND: RETURN 75 'ReadVarLen(): Read variable length int from file. Output: x#. CHANGES x 76 x# = 0 77 GET #1: x = ASC(b$): x# = x# * 128 + (x AND 127): IF x >= 128 THEN 77 78 RETURN 80 'ConvertInteger(): Parses s$ as big-endian integer, stores to x#. CHANGES x 81 x# = 0: FOR x=1 TO LEN(s$): x# = x# * 256 + ASC(MID$(s$, x, 1)): NEXT: RETURN 82 'The reason why # (DOUBLE) is used to store integer values 83 'is that GW-BASIC has only one integer type: INTEGER. 84 'It supports values in the range -32768..+32737. Making it unsuitable 85 'for storing values such as delta-times and track lengths, both of which 86 'are often greater than +32767 in common MIDI files. QuickBASIC supports 87 'the LONG datatype instead, but I was aiming for GW-BASIC compatibility 88 'in order to utilize my cool syntax highlighter for the majority 89 'of the duration of demonstrating this program on Youtube. :) 94 'Ethical subroutine 95 GOTO 97 96 KILL filename$ 97 RETURN 100 '*** MAIN MIDI PLAYER PROGRAM *** 101 'Information about each track: 102 DIM tkPtr%(100), tkDelay#(100), tkStatus(100), playwait# 103 'The same, but for loop-beginning: 104 DIM loPtr%(100), loDelay#(100), loStatus(100), loopwait# 105 'The same, but cached just in case loop-beginning must be written: 106 DIM rbPtr%(100), rbDelay#(100), rbStatus(100) 109 'Persistent settings for each MIDI channel. 110 DIM ChPatch(15), ChBend#(15), ChVolume(15), ChPanning(15), ChVibrato(15) 120 'For each active note, we need the following: 121 ' Original note number (ORIGINAL, not simulated) 122 ' Keyoff (Search Key) 123 ' Aftertouch (Search Key) 124 ' Simulated note number 125 ' Keyon -> OPL_NoteOn 126 ' Bend -> OPL_NoteOn 127 ' Keyon/touch pressure 128 ' Ctrl:Volume 129 ' Adlib channel number 130 ' All of above 131 ' MIDI channel (multi search key) 132 ' Bend 133 ' Ctrl:Volume 134 ' Ctrl:Pan 140 DIM ActCount(15) 'number of active notes on each midi channel 141 DIM ActTone(15,127) 'orignotenumber -> simulated notenumber 142 DIM ActAdlChn(15,127) 'orignotenumber -> adlib channel 143 DIM ActVol(15,127) 'orignotenumber -> pressure 144 DIM ActRev(15,127) 'orignotenumber -> activeindex (index to ActList) 145 DIM ActList(15,99) 'activeindex -> orignotenumber (index to ActVol etc) 146 DIM chon(17),chage#(17)'adlchannel -> is_on,age (for channel allocation) 148 DIM chm(17),cha(17) 'adlchannel -> midichn,activeindex (for collisions) 149 DIM chx(17),chc(17) 'adlchannel -> x coordinate, color (for graphics) 160 filename$ = "chmmr.mid" '<- FILENAME OF THE MIDI TO BE PLAYED 161 GOSUB 40 ' Reset AdLib 162 GOSUB 40 ' ...twice (just in case someone misprogrammed OPL3 previously) 163 PLAYmode$ = "T255P64" 164 PLAYmode% = 255 * 64 'Event tick frequency 165 PLAYbuflen = 2 'Number of events to keep buffered 166 COLOR 8: CLS : txtline = 2: PRINT "Press Q to quit; Space to pause" 169 GOSUB 200 ' Load and play MIDI file 170 'Begin main program (this simply monitors the playing status) 171 'Beware that the timer routine accesses a lot of global variables. 172 paused = 0 173 hlt(0) = &HCBF4' HLT; RETF. 174 hlt = VARPTR(hlt(0)) 175 CALL hlt 176 ' ^This subroutine saves power. It also makes DOSBox faster. 177 ' However, QuickBASIC has a different syntax for calling ASM 178 ' subroutines, so the line 175 will have to be changed when 179 ' porting to QuickBASIC. Disable it, or try this (qb /Lqb.qlb): 180 'DEF SEG = VARSEG(hlt(0)): CALL ABSOLUTE(hlt) 182 y$ = INKEY$ 183 IF y$ <> " " THEN 186 184 paused = 1 - paused 185 IF paused THEN ? "Pause": PLAY STOP ELSE ? "Ok": PLAY ON 186 IF y$ <> "q" AND y$ <> CHR$(27) AND y$ <> CHR$(3) THEN 174 190 PLAY OFF: PRINT "End!": GOSUB 50: KEY ON: END 200 'Subroutine: Load MIDI file 210 OPEN filename$ FOR RANDOM AS #1 LEN = 1: FIELD #1, 1 AS b$ 'Open file 211 'QuickBASIC has BINARY access mode, which allows to use INPUT$() rather 212 'than the silly fixed-size GET command, but GW-BASIC does not support 213 'BINARY, so we use RANDOM here. The difference between BINARY and INPUT 214 'is that INPUT chokes on EOF characters and translates CRLFs, making it 215 'unsuitable for working with MIDI files. 220 x = 4: GOSUB 70: IF s$ <> "MThd" THEN ERROR 13'Invalid type file 230 x = 4: GOSUB 70: GOSUB 80: IF x# <> 6 THEN ERROR 13 231 x = 2: GOSUB 70: GOSUB 80: Fmt = x# 232 x = 2: GOSUB 70: GOSUB 80: TrackCount = x#: IF TrackCount>100 THEN ERROR 6 233 x = 2: GOSUB 70: GOSUB 80: DeltaTicks = x# 234 InvDeltaTicks# = PLAYmode% / (240000000# * DeltaTicks) 240 Tempo# = 1000000# * InvDeltaTicks# 241 bendsense# = 2 / 8192# 250 FOR tk = 1 TO TrackCount 251 x = 4: GOSUB 70: IF s$ <> "MTrk" THEN ERROR 13'Invalid type file 252 x = 4: GOSUB 70: GOSUB 80: TrackLength% = x#: y% = LOC(1) 253 GOSUB 75: tkDelay#(tk) = x# 'Read next event time 254 tkPtr%(tk) = LOC(1) 'Save first event file position 255 GET #1, y% + TrackLength% 'Skip to beginning of next track header 270 NEXT 275 FOR a = 0 TO 15: ChVolume(a) = 127: NEXT 281 PLAY ON: ON PLAY(PLAYbuflen) GOSUB 300 'Set up periodic event handler 282 began = 0: loopStart = 1: playwait# = 0: PLAY "MLMB" 300 'Subroutine: Timer callback. Called when the PLAY buffer is exhausted. 301 IF began THEN playwait# = playwait# - 1# 302 'For each track where delay=0, parse events and read new delay. 303 WHILE playwait# < .5#: GOSUB 310: WEND 304 'Repopulate the PLAY buffer 305 WHILE PLAY(0) < PLAYbuflen: PLAY PLAYmode$: WEND: RETURN 310 'Subroutine: Process events on any track that is due 311 FOR tk = 1 TO TrackCount 312 rbPtr%(tk) = tkPtr%(tk) 313 rbDelay#(tk) = tkDelay#(tk) 314 rbStatus(tk) = tkStatus(tk) 315 IF tkStatus(tk) < 0 OR tkDelay#(tk) > 0 THEN 319 316 GOSUB 350 'Handle event 317 GOSUB 75: tkDelay#(tk) = tkDelay#(tk) + x# 'Read next event time 318 tkPtr%(tk) = LOC(1) 'Save modified file position 319 NEXT 320 IF loopStart = 0 THEN 338 321 'Save loop begin 322 FOR tk = 1 TO TrackCount 323 loPtr%(tk) = rbPtr%(tk) 324 loDelay#(tk) = rbDelay#(tk) 325 loStatus(tk) = rbStatus(tk) 326 NEXT 327 loopwait# = playwait# 328 loopStart = 0 329 GOTO 338 330 'Return to loop beginning 331 FOR tk = 1 TO TrackCount 332 tkPtr%(tk) = loPtr%(tk) 333 tkDelay#(tk) = loDelay#(tk) 334 tkStatus(tk) = loStatus(tk) 335 NEXT 336 loopEnd = 0 337 playwait# = loopwait# 338 'Find shortest delay from all tracks 339 nd# = -1 340 FOR tk = 1 TO TrackCount 341 IF tkStatus(tk) < 0 THEN 343 342 IF nd# = -1 OR tkDelay#(tk) < nd# THEN nd# = tkDelay#(tk) 343 NEXT 344 'Schedule the next playevent to be processed after that delay 345 FOR tk = 1 TO TrackCount: tkDelay#(tk) = tkDelay#(tk) - nd#: NEXT 346 t# = nd# * Tempo#: IF began THEN playwait# = playwait# + t# 347 FOR a = 0 TO 17: chage#(a) = chage#(a) + t#: NEXT 348 IF t# < 0 OR loopEnd THEN 330 ELSE RETURN 'Loop or done 350 'Subroutine: Read an event from track, and read next delay. Input: tk 351 'Note that we continuously access the disk file during playback. 352 'This is perfectly fine when we are running on a modern OS, possibly 353 'under the encapsulation of DOSBox, but if you're running this on 354 'vanilla MS-DOS without e.g. SMARTDRV, you are going to find this 355 'program very unsuitable for your MIDI playing needs, for the disk 356 'drive would probably make more noise than the soundcard does. 357 'We cannot cache tracks because of memory constraints in GW-BASIC. 358 'In QBASIC, we could do better, but it would be cumbersome to implement. 359 GET #1, tkPtr%(tk) + 1: b = ASC(b$) 360 IF b < &HF0 THEN 380 361 'Subroutine for Fx special events 362 '? tk;":";HEX$(b);" at $";hex$(LOC(1)) 363 IF b = &HF7 OR b = &HF0 THEN 76'SysEx. Ignore Varlen. 364 IF b = &HF3 THEN GET #1: RETURN 365 IF b = &HF2 THEN GET #1: GET #1: RETURN 370 'Subroutine for special event FF 371 GET #1: evtype = ASC(b$): GOSUB 75: x = x#: GOSUB 70 372 IF evtype = &H2F THEN tkStatus(tk) = -1 373 IF evtype = &H51 THEN GOSUB 80: Tempo# = x# * InvDeltaTicks# 374 IF evtype = 6 AND s$ = "loopStart" THEN loopStart = 1 375 IF evtype = 6 AND s$ = "loopEnd" THEN loopEnd = 1 376 IF evtype < 1 OR evtype > 6 THEN RETURN 377 txtline = 3 + (txtline - 2) MOD 20 378 LOCATE txtline, 1: COLOR 8: PRINT "Meta"; evtype; ": "; s$: RETURN 380 'Subroutine for any normal event (80..EF) 381 IF b < 128 THEN b = tkStatus(tk) OR &H80: GET #1, tkPtr%(tk) 382 MidCh = b AND 15: tkStatus(tk) = b 383 '? tk;":";HEX$(b);" at $";hex$(LOC(1)) 384 ON b\16 - 7 GOTO 400,420,460,470,490,492,495 'Choose event handler 400 'Event: 8x Note Off 401 GET #1: note = ASC(b$): GET #1': vol=ASC(b$) 402 ChBend#(MidCh) = 0 403 n = ActRev(MidCh,note): IF n = 0 THEN RETURN 404 m = MidCh: GOTO 600 'deallocate active note, and return 420 'Event: 9x Note On 421 GET #1: note = ASC(b$): GET #1: vol = ASC(b$) 422 IF vol = 0 THEN 402' Sometimes noteoffs are optimized as 0-vol noteons 423 IF ActRev(MidCh,note) THEN RETURN 'Ignore repeat notes w/o keyoffs 424 'Determine the instrument and the note value (tone) 425 tone = note: i = ChPatch(MidCh) 426 IF MidCh = 9 THEN i = 128+note-35: tone = adl(i,11) 'Translate percussion 427 '(MIDI channel 9 always plays percussion and ignores the patch number) 429 'Allocate AdLib channel (the physical sound channel for the note) 430 bs# = -9: c = -1 431 FOR a = 0 TO 17 432 s# = chage#(a) 433 IF chon(a) = 0 THEN s# = s# + 3d3 ' Empty channel = privileged 434 IF chins(a) = i THEN s# = s# + .2# ' Same instrument = good 435 IF i<128 AND chins(a)>127 THEN s# = s#*2+9'Percussion is inferior to melody 436 IF s# > bs# THEN bs# = s#: c = a ' Best candidate wins 437 NEXT 438 IF chon(c) THEN m=chm(c): n=cha(c): GOSUB 600 'Collision: Kill old note 439 chon(c) = 1: chins(c) = i: chage#(c) = 0: began = 1 440 'Allocate active note for MIDI channel 441 '"Active note" helps dealing with further events affecting this note. 442 n = ActCount(MidCh) + 1 443 ActList(MidCh, n) = note 444 ActRev(MidCh,note) = n 445 ActCount(MidCh) = n 449 'Record info about this note 450 ActTone(MidCh,note) = tone 451 ActAdlChn(MidCh,note) = c 452 ActVol(MidCh,note) = vol 453 chm(c) = MidCh: cha(c) = n ' Save this note's origin so collision works. 454 GOSUB 30 ' OPL_Patch 455 GOSUB 530' OPL_Pan with ChPanning 456 GOSUB 540' OPL_Touch with ChVolume 457 chx(c) = 1 + (tone+63)MOD 80: chc(c) = 9+(chins(c)MOD 6) 458 LOCATE 20-c, chx(c): COLOR chc(c): PRINT "#"; 459 GOTO 520 ' OPL_NoteOn with ChBend, and return 460 'Event: Ax Note touch 461 GET #1: note = ASC(b$): GET #1: vol = ASC(b$) 462 IF ActRev(MidCh,note) = 0 THEN RETURN'Ignore touch if note is not active 463 c = ActAdlChn(MidCh,note) 464 LOCATE 20-c, chx(c): COLOR chc(c): PRINT "&"; 465 ActVol(MidCh,note) = vol 466 GOTO 540 'update note volume, and return 470 'Event: Bx Controller change 471 GET #1: ctrlno = ASC(b$): GET #1: value = ASC(b$) 472 IF ctrlno = 1 THEN ChVibrato(MidCh) = value 'TODO: handle 473 IF ctrlno = 6 THEN bendsense# = value / 8192# 474 IF ctrlno = 7 THEN ChVolume(MidCh) = value: mop = 2: GOTO 500 475 IF ctrlno = 10 THEN 482 'Pan 476 IF ctrlno = 121 THEN 486 'Reset controllers 477 IF ctrlno = 123 THEN mop=5: GOTO 500 'All notes off on channel 478 'Other ctrls worth considering: 479 ' 0 = choose bank (bank+patch identifies the instrument) 480 ' 64 = sustain pedal (when used, noteoff does not produce an adlib keyoff) 481 RETURN 482 'Ctrl 10: Alter the panning of the channel: 483 p = 0: IF value < 48 THEN p = 32 ELSE IF value > 79 THEN p = 16 484 ChPanning(MidCh) = p 485 mop=4: GOTO 500 486 'Ctrl 121: Reset all controllers to their default values. 487 ChBend#(MidCh)=0: ChVibrato(MidCh)=0: ChPan(MidCh)=0 488 mop=1: GOSUB 500: mop=2: GOSUB 500: mop=4: GOTO 500 490 'Event: Cx Patch change 491 GET #1: ChPatch(MidCh) = ASC(b$): RETURN 492 'Event: Dx Channel after-touch 493 GET #1: vol = ASC(b$): mop=3:GOTO 500 'TODO: Verify, is this correct action? 495 'Event: Ex Wheel/bend 496 GET #1: a = ASC(b$): GET #1 497 ChBend#(MidCh) = (a + ASC(b$) * 128 - 8192) * bendsense# 498 mop = 1: GOTO 500 'Update pitches, and return 500 'Subroutine: Update all live notes. Input: MidCh,mop 501 'Update when mop: 1=pitches; 2=volumes; 3=pressures; 4=pans, 5=off 502 x1 = ActCount(MidCh) 503 FOR a = 1 TO x1 504 note = ActList(MidCh, a) 505 c = ActAdlChn(MidCh,note): ON mop GOSUB 508,509,510,530,511 506 NEXT 507 RETURN 508 tone = ActTone(MidCh,note): GOTO 520 509 vol = ActVol(MidCh, note): GOTO 540 510 ActVol(MidCh,note) = vol: GOTO 540 511 m = MidCh: n = a: GOTO 600 520 'Subroutine: Update note pitch. Input: c,MidCh,tone. CHANGES q,p,o,x,h# 521 ' 907*2^(n/12) * (8363) / 44100 522 h# = 172.00093# * EXP(.057762265#*(tone+ChBend#(MidCh))): GOTO 10'OPL_NoteOn 530 'Subroutine: Update note pan. Input: c,MidCh 531 chpan(c) = ChPanning(MidCh): GOTO 38 'OPL_Pan 540 'Subroutine: Update note volume. Input: c,MidCh,vol. CHANGES q,p,o,v 541 v = vol * ChVolume(MidCh): GOTO 20 'OPL_Touch 600 'Subroutine: Deallocate active note (m = MidCh, n = index). CHANGES c,x,q 601 'Uses m instead of MidCh because called also from alloc-collision code. 602 x = ActCount(m) ' Find how many active notes 603 q = ActList(m,n) ' q=note to be deactivated 604 ActRev(m,q) = 0 ' that note is no more 605 ActCount(m) = x-1 ' The list is now shorter 606 c = ActAdlChn(m,q) ' But wait, which adlib channel was it on, again? 607 chon(c)=0: chage#(c)=0: GOSUB 8'OPL_NoteOff 608 LOCATE 20-c, chx(c): COLOR 1: PRINT "."; 610 IF n = x THEN RETURN' Did we delete the last note? 611 q = ActList(m,x) ' q = last note in list 612 ActList(m,n) = q ' move into the deleted slot 613 ActRev(m,q) = n 614 cha(ActAdlChn(m,q)) = n 615 RETURN 760 'This FM Instrument Data comes from Miles Sound System, as used 761 'in the following PC games: Star Control III and Asterix, under 762 'the name AIL (Audio Interface Library). Today, AIL has been 763 'released as "open-source freeware" under the name Miles Sound System. 764 'AIL was used in more than fifty PC games, but so far, I have found 765 'this particular set of General MIDI FM patches only in SC3 and Asterix. 766 'Other games using AIL used different sets of FM patches. There is no 767 'particular reason for preferring this patch set, and in fact, the 768 'descendant of this program, ADLMIDI, http://iki.fi/source/adlmidi.html , 769 'features a large set of different FM patch sets to choose from. 773 'In the Youtube video, I enter this huge blob of DATA lines very quickly 774 'by using a preprogrammed input TSR, "inputter", which I made just for 775 'this purpose. 776 'It inputs the inline command "COLOR 0,4", turning text into black on red, 777 'and then starts entering DATA lines in an arbitrary geometrical fashion. 778 'After inputting, the text color is reset to normal with "COLOR 7,0". 779 'Of course, the whole time, the poor syntax highlighter TSR (synhili) 780 'does its best to make sense of whatever is being displayed on the screen, 781 'causing the text to stay red only for a short while, whereever the cursor 782 'was last. The resulting effect looks very cool, and most importantly, 783 'the long sequence of DATA gets input in a non-boring manner. 790 'The data bytes are: 791 ' [0,1] AM/VIB/EG/KSR/Multiple bits for carrier and modulator respectively 792 ' [2,3] KSL/Attenuation settings for carrier and modulator respectively 793 ' [4,5] Attack and decay rates for carrier and modulator respectively 794 ' [6,7] Sustain and release rates for carrier and modulator respectively 795 ' [8,9] Wave select settings for carrier and modulator respectively 796 ' [10] Feedback/connection bits for the channel (also stereo/pan bits) 797 ' [11] For percussive instruments (GP35..GP87), the tone to play 800 DATA 1, 1,143, 6,242,242,244,247,0,0, 56, 0: REM GM1:AcouGrandPiano 801 DATA 1, 1, 75, 0,242,242,244,247,0,0, 56, 0: REM GM2:BrightAcouGrand 802 DATA 1, 1, 73, 0,242,242,244,246,0,0, 56, 0: REM GM3:ElecGrandPiano 803 DATA 129, 65, 18, 0,242,242,247,247,0,0, 54, 0: REM GM4:Honky-tonkPiano 804 DATA 1, 1, 87, 0,241,242,247,247,0,0, 48, 0: REM GM5:Rhodes Piano 805 DATA 1, 1,147, 0,241,242,247,247,0,0, 48, 0: REM GM6:Chorused Piano 806 DATA 1, 22,128, 14,161,242,242,245,0,0, 56, 0: REM GM7:Harpsichord 807 DATA 1, 1,146, 0,194,194,248,248,0,0, 58, 0: REM GM8:Clavinet 808 DATA 12,129, 92, 0,246,243,244,245,0,0, 48, 0: REM GM9:Celesta 809 DATA 7, 17,151,128,243,242,242,241,0,0, 50, 0: REM GM10:Glockenspiel 810 DATA 23, 1, 33, 0, 84,244,244,244,0,0, 50, 0: REM GM11:Music box 811 DATA 152,129, 98, 0,243,242,246,246,0,0, 48, 0: REM GM12:Vibraphone 812 DATA 24, 1, 35, 0,246,231,246,247,0,0, 48, 0: REM GM13:Marimba 813 DATA 21, 1,145, 0,246,246,246,246,0,0, 52, 0: REM GM14:Xylophone 814 DATA 69,129, 89,128,211,163,243,243,0,0, 60, 0: REM GM15:Tubular Bells 815 DATA 3,129, 73,128,117,181,245,245,1,0, 52, 0: REM GM16:Dulcimer 816 DATA 113, 49,146, 0,246,241, 20, 7,0,0, 50, 0: REM GM17:Hammond Organ 817 DATA 114, 48, 20, 0,199,199, 88, 8,0,0, 50, 0: REM GM18:Percussive Organ 818 DATA 112,177, 68, 0,170,138, 24, 8,0,0, 52, 0: REM GM19:Rock Organ 819 DATA 35,177,147, 0,151, 85, 35, 20,1,0, 52, 0: REM GM20:Church Organ 820 DATA 97,177, 19,128,151, 85, 4, 4,1,0, 48, 0: REM GM21:Reed Organ 821 DATA 36,177, 72, 0,152, 70, 42, 26,1,0, 60, 0: REM GM22:Accordion 822 DATA 97, 33, 19, 0,145, 97, 6, 7,1,0, 58, 0: REM GM23:Harmonica 823 DATA 33,161, 19,137,113, 97, 6, 7,0,0, 54, 0: REM GM24:Tango Accordion 824 DATA 2, 65,156,128,243,243,148,200,1,0, 60, 0: REM GM25:Acoustic Guitar1 825 DATA 3, 17, 84, 0,243,241,154,231,1,0, 60, 0: REM GM26:Acoustic Guitar2 826 DATA 35, 33, 95, 0,241,242, 58,248,0,0, 48, 0: REM GM27:Electric Guitar1 827 DATA 3, 33,135,128,246,243, 34,248,1,0, 54, 0: REM GM28:Electric Guitar2 828 DATA 3, 33, 71, 0,249,246, 84, 58,0,0, 48, 0: REM GM29:Electric Guitar3 829 DATA 35, 33, 74, 5,145,132, 65, 25,1,0, 56, 0: REM GM30:Overdrive Guitar 830 DATA 35, 33, 74, 0,149,148, 25, 25,1,0, 56, 0: REM GM31:Distorton Guitar 831 DATA 9,132,161,128, 32,209, 79,248,0,0, 56, 0: REM GM32:Guitar Harmonics 832 DATA 33,162, 30, 0,148,195, 6,166,0,0, 50, 0: REM GM33:Acoustic Bass 833 DATA 49, 49, 18, 0,241,241, 40, 24,0,0, 58, 0: REM GM34:Electric Bass 1 834 DATA 49, 49,141, 0,241,241,232,120,0,0, 58, 0: REM GM35:Electric Bass 2 835 DATA 49, 50, 91, 0, 81,113, 40, 72,0,0, 60, 0: REM GM36:Fretless Bass 836 DATA 1, 33,139, 64,161,242,154,223,0,0, 56, 0: REM GM37:Slap Bass 1 837 DATA 33, 33,139, 8,162,161, 22,223,0,0, 56, 0: REM GM38:Slap Bass 2 838 DATA 49, 49,139, 0,244,241,232,120,0,0, 58, 0: REM GM39:Synth Bass 1 839 DATA 49, 49, 18, 0,241,241, 40, 24,0,0, 58, 0: REM GM40:Synth Bass 2 840 DATA 49, 33, 21, 0,221, 86, 19, 38,1,0, 56, 0: REM GM41:Violin 841 DATA 49, 33, 22, 0,221,102, 19, 6,1,0, 56, 0: REM GM42:Viola 842 DATA 113, 49, 73, 0,209, 97, 28, 12,1,0, 56, 0: REM GM43:Cello 843 DATA 33, 35, 77,128,113,114, 18, 6,1,0, 50, 0: REM GM44:Contrabass 844 DATA 241,225, 64, 0,241,111, 33, 22,1,0, 50, 0: REM GM45:Tremulo Strings 845 DATA 2, 1, 26,128,245,133,117, 53,1,0, 48, 0: REM GM46:Pizzicato String 846 DATA 2, 1, 29,128,245,243,117,244,1,0, 48, 0: REM GM47:Orchestral Harp 847 DATA 16, 17, 65, 0,245,242, 5,195,1,0, 50, 0: REM GM48:Timpany 848 DATA 33,162,155, 1,177,114, 37, 8,1,0, 62, 0: REM GM49:String Ensemble1 849 DATA 161, 33,152, 0,127, 63, 3, 7,1,1, 48, 0: REM GM50:String Ensemble2 850 DATA 161, 97,147, 0,193, 79, 18, 5,0,0, 58, 0: REM GM51:Synth Strings 1 851 DATA 33, 97, 24, 0,193, 79, 34, 5,0,0, 60, 0: REM GM52:SynthStrings 2 852 DATA 49,114, 91,131,244,138, 21, 5,0,0, 48, 0: REM GM53:Choir Aahs 853 DATA 161, 97,144, 0,116,113, 57,103,0,0, 48, 0: REM GM54:Voice Oohs 854 DATA 113,114, 87, 0, 84,122, 5, 5,0,0, 60, 0: REM GM55:Synth Voice 855 DATA 144, 65, 0, 0, 84,165, 99, 69,0,0, 56, 0: REM GM56:Orchestra Hit 856 DATA 33, 33,146, 1,133,143, 23, 9,0,0, 60, 0: REM GM57:Trumpet 857 DATA 33, 33,148, 5,117,143, 23, 9,0,0, 60, 0: REM GM58:Trombone 858 DATA 33, 97,148, 0,118,130, 21, 55,0,0, 60, 0: REM GM59:Tuba 859 DATA 49, 33, 67, 0,158, 98, 23, 44,1,1, 50, 0: REM GM60:Muted Trumpet 860 DATA 33, 33,155, 0, 97,127,106, 10,0,0, 50, 0: REM GM61:French Horn 861 DATA 97, 34,138, 6,117,116, 31, 15,0,0, 56, 0: REM GM62:Brass Section 862 DATA 161, 33,134,131,114,113, 85, 24,1,0, 48, 0: REM GM63:Synth Brass 1 863 DATA 33, 33, 77, 0, 84,166, 60, 28,0,0, 56, 0: REM GM64:Synth Brass 2 864 DATA 49, 97,143, 0,147,114, 2, 11,1,0, 56, 0: REM GM65:Soprano Sax 865 DATA 49, 97,142, 0,147,114, 3, 9,1,0, 56, 0: REM GM66:Alto Sax 866 DATA 49, 97,145, 0,147,130, 3, 9,1,0, 58, 0: REM GM67:Tenor Sax 867 DATA 49, 97,142, 0,147,114, 15, 15,1,0, 58, 0: REM GM68:Baritone Sax 868 DATA 33, 33, 75, 0,170,143, 22, 10,1,0, 56, 0: REM GM69:Oboe 869 DATA 49, 33,144, 0,126,139, 23, 12,1,1, 54, 0: REM GM70:English Horn 870 DATA 49, 50,129, 0,117, 97, 25, 25,1,0, 48, 0: REM GM71:Bassoon 871 DATA 50, 33,144, 0,155,114, 33, 23,0,0, 52, 0: REM GM72:Clarinet 872 DATA 225,225, 31, 0,133,101, 95, 26,0,0, 48, 0: REM GM73:Piccolo 873 DATA 225,225, 70, 0,136,101, 95, 26,0,0, 48, 0: REM GM74:Flute 874 DATA 161, 33,156, 0,117,117, 31, 10,0,0, 50, 0: REM GM75:Recorder 875 DATA 49, 33,139, 0,132,101, 88, 26,0,0, 48, 0: REM GM76:Pan Flute 876 DATA 225,161, 76, 0,102,101, 86, 38,0,0, 48, 0: REM GM77:Bottle Blow 877 DATA 98,161,203, 0,118, 85, 70, 54,0,0, 48, 0: REM GM78:Shakuhachi 878 DATA 98,161,153, 0, 87, 86, 7, 7,0,0, 59, 0: REM GM79:Whistle 879 DATA 98,161,147, 0,119,118, 7, 7,0,0, 59, 0: REM GM80:Ocarina 880 DATA 34, 33, 89, 0,255,255, 3, 15,2,0, 48, 0: REM GM81:Lead 1 squareea 881 DATA 33, 33, 14, 0,255,255, 15, 15,1,1, 48, 0: REM GM82:Lead 2 sawtooth 882 DATA 34, 33, 70,128,134,100, 85, 24,0,0, 48, 0: REM GM83:Lead 3 calliope 883 DATA 33,161, 69, 0,102,150, 18, 10,0,0, 48, 0: REM GM84:Lead 4 chiff 884 DATA 33, 34,139, 0,146,145, 42, 42,1,0, 48, 0: REM GM85:Lead 5 charang 885 DATA 162, 97,158, 64,223,111, 5, 7,0,0, 50, 0: REM GM86:Lead 6 voice 886 DATA 32, 96, 26, 0,239,143, 1, 6,0,2, 48, 0: REM GM87:Lead 7 fifths 887 DATA 33, 33,143,128,241,244, 41, 9,0,0, 58, 0: REM GM88:Lead 8 brass 888 DATA 119,161,165, 0, 83,160,148, 5,0,0, 50, 0: REM GM89:Pad 1 new age 889 DATA 97,177, 31,128,168, 37, 17, 3,0,0, 58, 0: REM GM90:Pad 2 warm 890 DATA 97, 97, 23, 0,145, 85, 52, 22,0,0, 60, 0: REM GM91:Pad 3 polysynth 891 DATA 113,114, 93, 0, 84,106, 1, 3,0,0, 48, 0: REM GM92:Pad 4 choir 892 DATA 33,162,151, 0, 33, 66, 67, 53,0,0, 56, 0: REM GM93:Pad 5 bowedpad 893 DATA 161, 33, 28, 0,161, 49,119, 71,1,1, 48, 0: REM GM94:Pad 6 metallic 894 DATA 33, 97,137, 3, 17, 66, 51, 37,0,0, 58, 0: REM GM95:Pad 7 halo 895 DATA 161, 33, 21, 0, 17,207, 71, 7,1,0, 48, 0: REM GM96:Pad 8 sweep 896 DATA 58, 81,206, 0,248,134,246, 2,0,0, 50, 0: REM GM97:FX 1 rain 897 DATA 33, 33, 21, 0, 33, 65, 35, 19,1,0, 48, 0: REM GM98:FX 2 soundtrack 898 DATA 6, 1, 91, 0,116,165,149,114,0,0, 48, 0: REM GM99:FX 3 crystal 899 DATA 34, 97,146,131,177,242,129, 38,0,0, 60, 0: REM GM100:FX 4 atmosphere 900 DATA 65, 66, 77, 0,241,242, 81,245,1,0, 48, 0: REM GM101:FX 5 brightness 901 DATA 97,163,148,128, 17, 17, 81, 19,1,0, 54, 0: REM GM102:FX 6 goblins 902 DATA 97,161,140,128, 17, 29, 49, 3,0,0, 54, 0: REM GM103:FX 7 echoes 903 DATA 164, 97, 76, 0,243,129,115, 35,1,0, 52, 0: REM GM104:FX 8 sci-fi 904 DATA 2, 7,133, 3,210,242, 83,246,0,1, 48, 0: REM GM105:Sitar 905 DATA 17, 19, 12,128,163,162, 17,229,1,0, 48, 0: REM GM106:Banjo 906 DATA 17, 17, 6, 0,246,242, 65,230,1,2, 52, 0: REM GM107:Shamisen 907 DATA 147,145,145, 0,212,235, 50, 17,0,1, 56, 0: REM GM108:Koto 908 DATA 4, 1, 79, 0,250,194, 86, 5,0,0, 60, 0: REM GM109:Kalimba 909 DATA 33, 34, 73, 0,124,111, 32, 12,0,1, 54, 0: REM GM110:Bagpipe 910 DATA 49, 33,133, 0,221, 86, 51, 22,1,0, 58, 0: REM GM111:Fiddle 911 DATA 32, 33, 4,129,218,143, 5, 11,2,0, 54, 0: REM GM112:Shanai 912 DATA 5, 3,106,128,241,195,229,229,0,0, 54, 0: REM GM113:Tinkle Bell 913 DATA 7, 2, 21, 0,236,248, 38, 22,0,0, 58, 0: REM GM114:Agogo Bells 914 DATA 5, 1,157, 0,103,223, 53, 5,0,0, 56, 0: REM GM115:Steel Drums 915 DATA 24, 18,150, 0,250,248, 40,229,0,0, 58, 0: REM GM116:Woodblock 916 DATA 16, 0,134, 3,168,250, 7, 3,0,0, 54, 0: REM GM117:Taiko Drum 917 DATA 17, 16, 65, 3,248,243, 71, 3,2,0, 52, 0: REM GM118:Melodic Tom 918 DATA 1, 16,142, 0,241,243, 6, 2,2,0, 62, 0: REM GM119:Synth Drum 919 DATA 14,192, 0, 0, 31, 31, 0,255,0,3, 62, 0: REM GM120:Reverse Cymbal 920 DATA 6, 3,128,136,248, 86, 36,132,0,2, 62, 0: REM GM121:Guitar FretNoise 921 DATA 14,208, 0, 5,248, 52, 0, 4,0,3, 62, 0: REM GM122:Breath Noise 922 DATA 14,192, 0, 0,246, 31, 0, 2,0,3, 62, 0: REM GM123:Seashore 923 DATA 213,218,149, 64, 55, 86,163, 55,0,0, 48, 0: REM GM124:Bird Tweet 924 DATA 53, 20, 92, 8,178,244, 97, 21,2,0, 58, 0: REM GM125:Telephone 925 DATA 14,208, 0, 0,246, 79, 0,245,0,3, 62, 0: REM GM126:Helicopter 926 DATA 38,228, 0, 0,255, 18, 1, 22,0,1, 62, 0: REM GM127:Applause/Noise 927 DATA 0, 0, 0, 0,243,246,240,201,0,2, 62, 0: REM GM128:Gunshot 928 DATA 16, 17, 68, 0,248,243,119, 6,2,0, 56, 35: REM GP35:Ac Bass Drum 929 DATA 16, 17, 68, 0,248,243,119, 6,2,0, 56, 35: REM GP36:Bass Drum 1 930 DATA 2, 17, 7, 0,249,248,255,255,0,0, 56, 52: REM GP37:Side Stick 931 DATA 0, 0, 0, 0,252,250, 5, 23,2,0, 62, 48: REM GP38:Acoustic Snare 932 DATA 0, 1, 2, 0,255,255, 7, 8,0,0, 48, 58: REM GP39:Hand Clap 933 DATA 0, 0, 0, 0,252,250, 5, 23,2,0, 62, 60: REM GP40:Electric Snare 934 DATA 0, 0, 0, 0,246,246, 12, 6,0,0, 52, 47: REM GP41:Low Floor Tom 935 DATA 12, 18, 0, 0,246,251, 8, 71,0,2, 58, 43: REM GP42:Closed High Hat 936 DATA 0, 0, 0, 0,246,246, 12, 6,0,0, 52, 49: REM GP43:High Floor Tom 937 DATA 12, 18, 0, 5,246,123, 8, 71,0,2, 58, 43: REM GP44:Pedal High Hat 938 DATA 0, 0, 0, 0,246,246, 12, 6,0,0, 52, 51: REM GP45:Low Tom 939 DATA 12, 18, 0, 0,246,203, 2, 67,0,2, 58, 43: REM GP46:Open High Hat 940 DATA 0, 0, 0, 0,246,246, 12, 6,0,0, 52, 54: REM GP47:Low-Mid Tom 941 DATA 0, 0, 0, 0,246,246, 12, 6,0,0, 52, 57: REM GP48:High-Mid Tom 942 DATA 14,208, 0, 0,246,159, 0, 2,0,3, 62, 72: REM GP49:Crash Cymbal 1 943 DATA 0, 0, 0, 0,246,246, 12, 6,0,0, 52, 60: REM GP50:High Tom 944 DATA 14, 7, 8, 74,248,244, 66,228,0,3, 62, 76: REM GP51:Ride Cymbal 1 945 DATA 14,208, 0, 10,245,159, 48, 2,0,0, 62, 84: REM GP52:Chinese Cymbal 946 DATA 14, 7, 10, 93,228,245,228,229,3,1, 54, 36: REM GP53:Ride Bell 947 DATA 2, 5, 3, 10,180,151, 4,247,0,0, 62, 65: REM GP54:Tambourine 948 DATA 78,158, 0, 0,246,159, 0, 2,0,3, 62, 84: REM GP55:Splash Cymbal 949 DATA 17, 16, 69, 8,248,243, 55, 5,2,0, 56, 83: REM GP56:Cow Bell 950 DATA 14,208, 0, 0,246,159, 0, 2,0,3, 62, 84: REM GP57:Crash Cymbal 2 951 DATA 128, 16, 0, 13,255,255, 3, 20,3,0, 60, 24: REM GP58:Vibraslap 952 DATA 14, 7, 8, 74,248,244, 66,228,0,3, 62, 77: REM GP59:Ride Cymbal 2 953 DATA 6, 2, 11, 0,245,245, 12, 8,0,0, 54, 60: REM GP60:High Bongo 954 DATA 1, 2, 0, 0,250,200,191,151,0,0, 55, 65: REM GP61:Low Bongo 955 DATA 1, 1, 81, 0,250,250,135,183,0,0, 54, 59: REM GP62:Mute High Conga 956 DATA 1, 2, 84, 0,250,248,141,184,0,0, 54, 51: REM GP63:Open High Conga 957 DATA 1, 2, 89, 0,250,248,136,182,0,0, 54, 45: REM GP64:Low Conga 958 DATA 1, 0, 0, 0,249,250, 10, 6,3,0, 62, 71: REM GP65:High Timbale 959 DATA 0, 0,128, 0,249,246,137,108,3,0, 62, 60: REM GP66:Low Timbale 960 DATA 3, 12,128, 8,248,246,136,182,3,0, 63, 58: REM GP67:High Agogo 961 DATA 3, 12,133, 0,248,246,136,182,3,0, 63, 53: REM GP68:Low Agogo 962 DATA 14, 0, 64, 8,118,119, 79, 24,0,2, 62, 64: REM GP69:Cabasa 963 DATA 14, 3, 64, 0,200,155, 73,105,0,2, 62, 71: REM GP70:Maracas 964 DATA 215,199,220, 0,173,141, 5, 5,3,0, 62, 61: REM GP71:Short Whistle 965 DATA 215,199,220, 0,168,136, 4, 4,3,0, 62, 61: REM GP72:Long Whistle 966 DATA 128, 17, 0, 0,246,103, 6, 23,3,3, 62, 44: REM GP73:Short Guiro 967 DATA 128, 17, 0, 9,245, 70, 5, 22,2,3, 62, 40: REM GP74:Long Guiro 968 DATA 6, 21, 63, 0, 0,247,244,245,0,0, 49, 69: REM GP75:Claves 969 DATA 6, 18, 63, 0, 0,247,244,245,3,0, 48, 68: REM GP76:High Wood Block 970 DATA 6, 18, 63, 0, 0,247,244,245,0,0, 49, 63: REM GP77:Low Wood Block 971 DATA 1, 2, 88, 0,103,117,231, 7,0,0, 48, 74: REM GP78:Mute Cuica 972 DATA 65, 66, 69, 8,248,117, 72, 5,0,0, 48, 60: REM GP79:Open Cuica 973 DATA 10, 30, 64, 78,224,255,240, 5,3,0, 56, 80: REM GP80:Mute Triangle 974 DATA 10, 30,124, 82,224,255,240, 2,3,0, 56, 64: REM GP81:Open Triangle 975 DATA 14, 0, 64, 8,122,123, 74, 27,0,2, 62, 72: REM GP82 976 DATA 14, 7, 10, 64,228, 85,228, 57,3,1, 54, 73: REM GP83 977 DATA 5, 4, 5, 64,249,214, 50,165,3,0, 62, 70: REM GP84 978 DATA 2, 21, 63, 0, 0,247,243,245,3,0, 56, 68: REM GP85 979 DATA 1, 2, 79, 0,250,248,141,181,0,0, 55, 48: REM GP86 980 DATA 0, 0, 0, 0,246,246, 12, 6,0,0, 52, 53: REM GP87 |