'=========================================================================== ' Subject: MPU-401 I/O ROUTINES Date: 11-06-97 (16:33) ' Author: Andrew Below Code: QB, QBasic, PDS ' Origin: bel@obninsk.ru Packet: SOUND.ABC '=========================================================================== '.---==== MPU-401 IO Routines ====----. '------------Public Domain------------- 'Some MIDI information taken from SWAG FAQs from John Guillory '(UART Info, Read/Write bitmasks), Colin Buckley (assembler routines 'code, Note On/Note Off messages), Steven Tallent (some other Pascal 'MIDI routines), Gunter Nagler (messages help) and, of course, from 'Jeff Glatts technical MIDI page (link from http://www.iicm.edu/midilinks) 'contains full MIDI specification, command and controllers descriptions 'and SysEx information, with C/C++ examples. 'ALL this code translated from assembler and Pascal, so I can't 'preview that these SUBs will work on all MPU-401 compatible synthesizers, 'but on my (Turtle Beach Tropez Classic, ICS WaveFront chip and MPU 'emulation) all of these works OK. ' 'Remember, some of these SUBs works ONLY on TB Tropez (I take them from 'TB User Guide) and using SysEx messages that won't work on other cards '(in these messages used Manufacturer ID and Product ID for TB). Here are 'these SUBs: SetDrumChannel, ClearDrumChannel, SetSynthVolume, 'DisableMIDIIn, EnableMIDIIn. All other SUBs use standart MIDI commands 'and should work on all MPU-401 synthesizers. ' 'Code translated by Andrew Below, 'all your additions and updated/modified versions of this program 'please E-mail to: bel@obninsk.ru or onxman@geocities.com. ' DECLARE SUB MPUReset () DECLARE SUB ChangeProgram (Channel!, PNumber!) DECLARE SUB SendSysEx (s$) DECLARE SUB NoteOn (Channel!, Note!, Velocity!) DECLARE SUB NoteOff (Channel!, Note!, Velocity!) DECLARE SUB Delay (d&) DECLARE SUB MPUSendString (s$) DECLARE SUB MPUSendByte (b!) DECLARE FUNCTION Hx$ (N!) DECLARE FUNCTION MPUByteHere! () DECLARE FUNCTION MPUGetByte! () 'Constants CONST Address = &H330 'Change this for your address CONST NOn = &H90, NOff = &H80 'Note On and Note Off command codes CONST PChange = &HC0 'Change Program command code CONST Receive = &H40, Send = &H80 'Receive/Send bit maskes CONST MReset = &HFF, UART = &H3F 'Reset and UART mode command codes CONST True = 1, False = NOT True 'True/False constants 'The next part of a program is MIDI In command analyzer. I have only 'MIDI keyboard, NOT external synthesizer, so actual commands for me are 'Note On (press key), Note Off (release key) and Controller Change '(on/off keyboard). If you have external synthesizer you may have 'also Program Change (included here) and Pitch Wheel (not included here). 'So, modify the next code for work with your keyboard/synthesizer. WIDTH 80, 50 'Set 50 strokes mode CLS MPUReset 'Reset MPU ChangeProgram 0, 50 'Set THRU channel program - Synth Strings 1 DO UNTIL INKEY$ <> "" IF MPUByteHere = True THEN b = MPUGetByte h$ = Hx$(b) PRINT USING ("&&"); h$; " ³ "; PRINT USING ("###"); b; PRINT " ³ "; HN$ = MID$(h$, 1, 1) 'Get high nibble (status byte) LN$ = MID$(h$, 2, 1) 'Get low nibble (command byte) SELECT CASE HN$ CASE "9": PRINT "Note On on channel " + LN$ NNum = True: NType = 1 CASE "8": PRINT "Note Off on channel " + LN$ NNum = True: NType = 0 CASE "C": PRINT "Program Change on channel " + LN$ PNum = True CASE "B": PRINT "Controller Change on channel " + LN$ CNum = True CASE "F": IF LN$ = "0" THEN PRINT "SysEx start" CASE "F": IF LN$ = "7" THEN PRINT "SysEx end" CASE ELSE IF NVel = True THEN IF NType = 1 THEN PRINT "Attack Velocity" IF NType = 0 THEN PRINT "Decay Velocity" NVel = False END IF IF NNum = True THEN PRINT "Note Number" NNum = False NVel = True END IF IF PNum = True THEN PRINT "Program Number" PNum = False END IF IF CValue = True THEN PRINT "Controller Value" CValue = False END IF IF CNum = True THEN PRINT "Controller Number" CNum = False CValue = True END IF END SELECT END IF LOOP END 'Change program on specified MIDI channel 'Program cannot be changed if this MIDI channel sets as '"Enhanced Drum Program channel" SUB ChangeProgram (Channel, PNumber) MPUSendByte (PChange + Channel) MPUSendByte (PNumber) END SUB 'Clears channel from Enhanced Drum Program SUB ClearDrumChannel (Channel) SendSysEx ("F0 00 00 65 10 00 22 " + Hx$(Channel) + " F7") ChangeProgram Channel, 0 END SUB 'Just a delay SUB Delay (d&) FOR i& = 0 TO d&: NEXT i& END SUB 'Disables MIDI-In to Synthesizer directive SUB DisableMIDIIn SendSysEx ("F0 00 00 65 10 00 1D F7") END SUB 'Enables MIDI-In to Synthesizer directive SUB EnableMIDIIn SendSysEx ("F0 00 00 65 10 00 1E F7") END SUB FUNCTION Hx$ (N) h$ = HEX$(N) IF LEN(h$) < 2 THEN h$ = "0" + h$ Hx$ = h$ END FUNCTION 'Is there a byte ready to be received? 'If yes, return True (1), else - return False FUNCTION MPUByteHere IF (INP(Address + 1) AND Send) = 0 THEN MPUByteHere = True ELSE MPUByteHere = False END IF END FUNCTION 'Get byte from MPU 'If you want to get byte only from buffer use 'the MPUByteHere function for check bytes in buffer FUNCTION MPUGetByte MPUGetByte = INP(&H330) END FUNCTION 'Reset MPU-401 'This is not 100% right code, becouse sometimes after execution 'of this SUB synthesizer plays a "garbage notes" SUB MPUReset MPUSendByte (MReset) IF MPUByteHere THEN MPUSendByte (UART) END SUB 'Send a byte to MPU when MPU is ready SUB MPUSendByte (b) DO UNTIL (INP(Address + 1) AND Receive) = 0: LOOP OUT Address, b END SUB 'Send a string to MPU (useful for multi-command messages) SUB MPUSendString (s$) FOR i = 1 TO LEN(s$) MPUSendByte (ASC(MID$(s$, i, 1))) NEXT i END SUB 'Note Off 'Quiets specified note (0-127) on one of MIDI channels (0-15) 'with specified "out" Velocity (Volume) SUB NoteOff (Channel, Note, Velocity) MPUSendByte (NOff + Channel) MPUSendByte (Note) MPUSendByte (Velocity) END SUB 'Note On 'Plays specified Note (0-127) on one of MIDI channels (0-15) 'with specified attack Velocity (Volume) SUB NoteOn (Channel, Note, Velocity) MPUSendByte (NOn + Channel) MPUSendByte (Note) MPUSendByte (Velocity) END SUB 'Send SysEx message to the MPU 'String s$ contains the SysEx messages in hex format, e.g. 'SendSysEx("F0 00 14 50 20 1F 00 F7") 'This message don't do anything, it is just example 'All SysEx messages must be starting from F0 byte and ended with F7 SUB SendSysEx (s$) FOR i = 1 TO LEN(s$) STEP 3 b$ = MID$(s$, i, 2) MPUSendByte (VAL("&H" + b$)) NEXT i END SUB 'Set specified MIDI channel as Enhanced Drum Program channel SUB SetDrumChannel (Channel) SendSysEx ("F0 00 00 65 10 00 33 " + Hx$(Channel) + "F7") END SUB 'Sets synthesizer volume (0-127) SUB SetSynthVolume (V) SendSysEx ("F0 00 00 65 10 00 09 " + Hx$(V) + " F7") END SUB