Posts: 149
Threads: 32
Joined: Feb 2005
This is just a quick knock together example of how to access individual midi commands in FB under windows. Perhaps a better Play command?
I will post a more complete example later (hide the messy midi commands and all).
Edit: The example here has been removed. See the full version below.
Posts: 149
Threads: 32
Joined: Feb 2005
Here is a little cleaned up version. The first is the bi file with all of the messy stuff.
Code: 'Midi_Acc
'A collection of tools to play midi notes on a Windows box
'Hopefully this will someday include code for *nix systems as well
'
'Not every possible function is included here, but there are enough
'to send your own custom commands to the Midi Device
'
'R Keeling 25 March 2005
'randy@keeling.com
'$include once: 'win/mmsystem.bi'
'the ratio between notes... 2 ^ (1 / 12)
const RBN = 1.0594630943592952646
'the log(RBN)
const LRBN = 0.057762265046662109118
'RBN squared minus 1
const SRBNM = 0.1224620483093729814
'the Hertz Frequency of the lowest Midi Note
const MidiNoteZeroFreq = 8.1757989156437073336
'Highest Octave in Midi
Const MaxOctave = 10
'MIDI Mapper
Const MIDI_MAPPER = -1
'midi message constants
' MIDI Controller Numbers Constants
Const midiMOD_WHEEL = 1
Const midiBREATH_CONTROLLER = 2
Const midiFOOT_CONTROLLER = 4
Const midiPORTAMENTO_TIME = 5
Const midiMAIN_VOLUME = 7
Const midiBALANCE = 8
Const midiPAN = 10
Const midiEXPRESS_CONTROLLER = 11
Const midiDAMPER_PEDAL = 64
Const midiPORTAMENTO = 65
Const midiSOSTENUTO = 66
Const midiSOFT_PEDAL = 67
Const midiHOLD_2 = 69
Const midiEXTERNAL_FX_DEPTH = 91
Const midiTREMELO_DEPTH = 92
Const midiCHORUS_DEPTH = 93
Const midiDETUNE_DEPTH = 94
Const midiPHASER_DEPTH = 95
Const midiDATA_INCREMENT = 96
Const midiDATA_DECREMENT = 97
' MIDI status messages
Const midiNOTE_OFF = &H80
Const midiNOTE_ON = &H90
Const midiKEY_PRESSURE = &HA0
Const midiCONTROLLER_CHANGE = &HB0
Const midiPROGRAM_CHANGE = &HC0
Const midiCHANNEL_PRESSURE = &HD0
Const midiPITCH_BEND = &HE0
'midi note constants
'for different octaves
'use Octave*12+note
'Octaves run from 0 to 10
const midiC = 0
const midiCs = 1
const midiDb = 1
const midiD = 2
const midiDs = 3
const midiEb = 3
const midiE = 4
const midiFb = 4
const midiF = 5
const midiEs = 5
const midiFs = 6
const midiGb = 6
const midiG = 7
const midiGs = 8
const midiAb = 8
const midiA = 9
const midiAs = 10
const midiBb = 10
const midiB = 11
const midiCb = 11
'instruments
const midiAcoustic_Grand_Piano = 0
const midiBright_Piano = 1
const midiElectric_Grand_Piano = 2
const midiHonky_Tonk_Piano = 3
const midiElectric_piano_1 = 4
const midiElectric_Piano_2 = 5
const midiHarpsichord = 6
const midiClavinet = 7
const midiCelesta = 8
const midiGlockenspiel = 9
const midimusic_Box = 10
const midiVibraphone = 11
const midiMarimba = 12
const midiXylophone = 13
const midiTubular_bells = 14
const midiDulcimer = 15
const midiDrawbar_Organ = 16
const midiPercussive_Organ = 17
const midiRock_Organ = 18
const midiChurch_Organ = 19
const midiReed_Organ = 20
const midiAccordion = 21
const midiHarmonica = 22
const midiTango_Accordion = 23
const midiNylon_String_Guitar = 24
const midiSteel_String_Guitar = 25
const midiJazz_Guitar = 26
const midiClean_Electric_Guitar = 27
const midiMuted_Electric_Guitar = 28
const midiOverdrive_Guitar = 29
const midiDistortion_Guitar = 30
const midiGuitar_Harmonics = 31
const midiAccoustic_Bass = 32
const midiFingered_Bass = 33
const midiPicked_Bass = 34
const midiFretless_Bass = 35
const midiSlap_Bass_1 = 36
const midiSlap_Bass_2 = 37
const midiSynth_Bass_1 = 38
const midiSynth_Bass_2 = 39
const midiViolin = 40
const midiViola = 41
const midiCello = 42
const midiContrabass = 43
const midiTremolo_Strings = 44
const midiPizzicato_Strings = 45
const midiOrchestral_Harp = 46
const midiTimpani = 47
const midiString_Ensemble_1 = 48
const midiString_Ensemble_2 = 49
const midiSynth_Strings_1 = 50
const midiSynth_Strings_2 = 51
const midiChoir_ahh = 52
const midiChoir_oohh = 53
const midiSynth_Voice = 54
const midiOrchestral_Hit = 55
const midiTrumpet = 56
const midiTrombone = 57
const midiTuba = 58
const midiMuted_Trumpet = 59
const midiFrench_Horn = 60
const midiBrass_Section = 61
const midiSynth_Brass_1 = 62
const midiSynth_Brass_2 = 63
const midiSoprano_Sax = 64
const midiAlto_Sax = 65
const midiTenor_Sax = 66
const midiBaritone_Sax = 67
const midiOboe = 68
const midiEnglish_Horn = 69
const midiBassoon = 70
const midiClarinet = 71
const midiPiccolo = 72
const midiFlute = 73
const midiRecorder = 74
const midiPan_flute = 75
const midiBlown_Bottle = 76
const midiShakuhachi = 77
const midiWhistle = 78
const midiOcarina = 79
const midiSquare_Wave = 80
const midiSawtooth_Wave = 81
const midiCaliope = 82
const midiChiff = 83
const midiCharang = 84
const midiVoice = 85
const midiFifths = 86
const midiBass_Lead = 87
const midiNew_Age = 88
const midiWarm = 89
const midiPolySynth = 90
const midiChoir = 91
const midiBowed = 92
const midiMetallic = 93
const midiHalo = 94
const midiSweep = 95
const midiFX_Rain = 96
const midiFX_Soundtrack = 97
const midiFX_Crystal = 98
const midiFX_Atmosphere = 99
const midiFX_Brightness = 100
const midiFX_Goblins = 101
const midiFX_Echo_Drops = 102
const midiFX_Star_Theme = 103
const midiSitar = 104
const midiBanjo = 105
const midiShamisen = 106
const midiKoto = 107
const midiKalimba = 108
const midiBagpipe = 109
const midiFiddle = 110
const midiShanai = 111
const midiTinkle_bell = 112
const midiAgogo = 113
const midiSteel_Drums = 114
const midiWoodblock = 115
const midiTaiko_Drum = 116
const midiMelodic_Tom = 117
const midiSynth_Drum = 118
const midiReverse_Cymbal = 119
const midiGuitar_Fret_Noise = 120
const midiBreath_Noise = 121
const midiSeashore = 122
const midiBird_Tweet = 123
const midiTelephone_Ring = 124
const midiHelicopter = 125
const midiApplause = 126
const midiGunshot = 127
dim shared MidiError as string
function MidiGetError() as string
MidiGetError = MidiError
end function
sub MidiSend(ByVal hMidiOut as integer, ByVal statusmsg As integer,_
ByVal data1msg As integer, ByVal data2msg As integer = 0)
Dim midiMessage As integer
dim lowint As integer
dim highint As integer
lowint = (data1msg * &H100) + statusmsg
highint = (data2msg * &H100) * &H100
midiMessage = lowint + highint
midiOutShortMsg hMidiOut, midiMessage
End sub
function MidiOpen (ByVal MidiDevice as integer = MIDI_MAPPER ) as integer
dim hMidiDevice as integer
dim nMidiDevices as integer
MidiError = ""
nMidiDevices = midiOutGetNumDevs()
if nMidiDevices <=0 then
MidiError = "No Midi Devices Found!"
MidiOpen = -1
exit function
end if
midiOutOpen hMidiDevice, MidiDevice, 0, 0, CALLBACK_NULL
MidiOpen = hMidiDevice
end function
sub MidiClose (ByVal hMidiOut as integer)
midiOutClose hmidiOut
MidiError = ""
end sub
sub MidiSetInstrument (ByVal hMidiOut as integer, _
ByVal Instrument as integer = 0, ByVal Channel as integer = 0)
MidiError = ""
if channel < 0 or channel > 15 then
MidiError = "Channel Out Of Range: " + STR$(Channel)
exit sub
end if
if instrument < 0 or instrument > 127 then
MidiError = "Instrument Out Of Range: " + STR$(instrument)
exit sub
end if
MidiSend hmidiOut, midiPROGRAM_CHANGE + Channel , Instrument
end sub
sub MidiSetMainVolume (ByVal hMidiOut as integer, _
ByVal Volume as integer = 127, ByVal Channel as integer = 0)
MidiError = ""
if channel < 0 or channel > 15 then
MidiError = "Channel Out Of Range: " + STR$(Channel)
exit sub
end if
if Volume < 0 or Volume > 127 then
MidiError = "Volume Out Of Range: " + STR$(Volume)
exit sub
end if
MidiSend hMidiOut, midiCONTROLLER_CHANGE + Channel, midiMAIN_VOLUME, Volume
end sub
sub MidiNoteOn (Byval hMidiOut as integer, ByVal Note as integer, _
Octave as integer = 4, Velocity as integer = 127, Channel as integer = 0)
MidiError = ""
if channel < 0 or channel > 15 then
MidiError = "Channel Out Of Range: " + STR$(Channel)
exit sub
end if
if Velocity < 0 or Velocity > 127 then
MidiError = "Velocity Out Of Range: " + STR$(Velocity)
exit sub
end if
if Note < 0 or Note > 11 then
MidiError = "Note Out Of Range: " + STR$(Note)
exit sub
end if
if Octave < 0 or Octave > MaxOctave then
MidiError = "Octave Out Of Range: " + STR$(Octave)
exit sub
end if
if Octave = 9 and Note > midiG then
MidiError = "Notes above G (7) in " + str$(MaxOctave) + "th Octave are not allowed in MIDI"
exit sub
end if
MidiSend hmidiOut, midiNOTE_ON + Channel, Octave * 12 + Note, Velocity
end sub
sub MidiNoteOff (Byval hMidiOut as integer, ByVal Note as integer, _
Octave as integer = 4, Channel as integer = 0)
MidiError = ""
if channel < 0 or channel > 15 then
MidiError = "Channel Out Of Range: " + STR$(Channel)
exit sub
end if
if Note < 0 or Note > 11 then
MidiError = "Note Out Of Range: " + STR$(Note)
exit sub
end if
if Octave < 0 or Octave > MaxOctave then
MidiError = "Octave Out Of Range: " + STR$(Octave)
exit sub
end if
if Octave = 9 and Note > midiG then
MidiError = "Notes above G (7) in " + str$(MaxOctave) + "th Octave are not allowed in MIDI"
exit sub
end if
MidiSend hmidiOut, midiNOTE_OFF + Channel, Octave * 12 + Note
end sub
sub MidiSetKeyPressure (ByVal hMidiOut as integer, ByVal Note as Integer, _
ByVal Octave as integer = 4, ByVal Value as integer = 127, ByVal Channel as integer = 0)
MidiError = ""
if channel < 0 or channel > 15 then
MidiError = "Channel Out Of Range: " + STR$(Channel)
exit sub
end if
if Value < 0 or Value > 127 then
MidiError = "Value in Set Key Pressure Out Of Range: " + STR$(Value)
exit sub
end if
if Note < 0 or Note > 11 then
MidiError = "Note Out Of Range: " + STR$(Note)
exit sub
end if
if Octave < 0 or Octave > MaxOctave then
MidiError = "Octave Out Of Range: " + STR$(Octave)
exit sub
end if
if Octave = 9 and Note > midiG then
MidiError = "Notes above G (7) in " + str$(MaxOctave) + "th Octave are not allowed in MIDI"
exit sub
end if
MidiSend hMidiOut, midiKEY_PRESSURE + Channel, Octave * 12 + Note, Value
end sub
sub MidiSetControlChange (ByVal hMidiOut as integer, _
ByVal ControlNumber as integer, _
ByVal Value as integer = 127, _
ByVal Channel as integer = 0)
MidiError = ""
if channel < 0 or channel > 15 then
MidiError = "Channel Out Of Range: " + STR$(Channel)
exit sub
end if
if Value < 0 or Value > 127 then
MidiError = "Value in Set Key Pressure Out Of Range: " + STR$(Value)
exit sub
end if
if ControlNumber < 0 or ControlNumber > 127 then
MidiError = "Control Number Out Of Range: " + STR$(ControlNumber)
exit sub
end if
MidiSend hMidiOut, midiCONTROLLER_CHANGE + Channel, ControlNumber, Value
end sub
sub MidiSetProgramChange (ByVal hMidiOut as integer, _
ByVal ProgramNumber as integer, _
ByVal Channel as integer = 0)
MidiError = ""
if channel < 0 or channel > 15 then
MidiError = "Channel Out Of Range: " + STR$(Channel)
exit sub
end if
if ProgramNumber < 0 or ProgramNumber > 127 then
MidiError = "Program Number Out Of Range: " + STR$(ProgramNumber)
exit sub
end if
MidiSend hMidiOut, midiPROGRAM_CHANGE + Channel, ProgramNumber
end sub
sub MidiSetPitchBend (ByVal hMidiOut as integer, _
ByVal Value1 as integer = 0, _
ByVal Value2 as integer = 64, _
ByVal Channel as integer = 0)
'Value1 is the LSB
'Value2 is the MSB
'a value of 0h00 0h40 (LSB first) means no pitch shift
'a value of 0h00 0h00 (LSB first) means to shift as low as possible
'a value of 0h7f 0h7f (LSB first) means to shift as high as possible
MidiError = ""
if channel < 0 or channel > 15 then
MidiError = "Channel Out Of Range: " + STR$(Channel)
exit sub
end if
if Value1 < 0 or Value1 > 127 then
MidiError = "Value1 in Set Pitch Bend Out Of Range: " + STR$(Value1)
exit sub
end if
if Value2 < 0 or Value2 > 127 then
MidiError = "Value2 in Set Pitch Bend Out Of Range: " + STR$(Value2)
exit sub
end if
MidiSend hMidiOut, midiPITCH_BEND + Channel, Value1, Value2
end sub
sub MidiSetChannelPressure (ByVal hMidiOut as integer, _
ByVal ChannelPressure as integer, _
ByVal Channel as integer = 0)
MidiError = ""
if channel < 0 or channel > 15 then
MidiError = "Channel Out Of Range: " + STR$(Channel)
exit sub
end if
if ChannelPressure < 0 or ChannelPressure > 127 then
MidiError = "Channel Pressure Out Of Range: " + STR$(ChannelPressure)
exit sub
end if
MidiSend hMidiOut, midiCHANNEL_PRESSURE + Channel, ChannelPressure
end sub
sub PlayNote (ByVal Note as integer, ByVal Octave as integer, _
ByVal Duration as single, ByVal Instrument as integer = 0, _
ByVal Volume as integer = 127)
dim tMidi as integer
dim t as single
tMidi = MidiOpen
MidiSetInstrument tMidi, Instrument
MidiNoteOn tMidi,Note,Octave, Volume
t = timer + Duration
do while t > timer
sleep 10
loop
MidiNoteOff tMidi, Note, Octave
MidiClose tMidi
end sub
function MidiSound (ByVal frequency as single, ByVal Duration as single, _
ByVal Voice as integer = midiTrumpet) as integer
'This routine uses midi to generate a frequency
'to do this, we must find a midi note below the
'requested frequency (f) and pitch bend up from
'that note (x).
'
'A basic equation for the frequency of a midi note
'is b*n^x = frequency. This comes from the fact
'that is western music (and thus MIDI) the distance
'between two semi-tones is the 12th root of 2 (lets call it n).
'And this comes from the fact that there are tweleve
'in the western scale (C, C#, D, D#, E, F, F#, G, G#, A, A#, B)
'and that when you go up an octave, the frequency of the note
'doubles. So if you start from some base note and keep multiplying
'by n you will get each successive note. The base note that we
'will start with is note 0 in midi (midi notes run from 0 - 127).
'But we are now forced to know what the frequency of note zero is.
'In MIDI standard, note 69 is 440 exactly (A above middle C on a piano).
'We can now solve for b.
'
'b*n^x = Frequency
'b*(2^(1/12))^69 = 440
'b = 8.1757989156437073336
'
'so Midi note zero (C in the lowest octave) has a
'frequency of approx. 8.1758 hertz
'
'We can now the frequency of any midi note. We can also find the
'midi note with a given frequency. I will call these notes Virtual Notes
'because they will often be fractional notes (9.34 or 143.1234 for example)
'that aren't really midi notes (midi notes are integers only). However,
'we can use these notes to find where we start for a given frequency and
'pitch bend up to that note.
'
'To find a base note (x) we can solve our first equation for x.
'b*n^x = f
'x = (log(f/b))/(log(n))
'
'this is a virtual note, what we want is the first real note before this
'so we can bend from that real note to our desired virtual note. This is
'done with the int function.
'x = int((log(f/b))/(log(n)))
'
'Now that we have a note to bend from we must learn how to bend.
'The GM MIDI standard states that a pitch bend should range from two
'semi-tones (notes) below the note, to two semi-tones above the note. This
'means that if our note was G, we could bend from F below it, to A above it).
'The command for pitch bend is set up so that it takes 2 bytes, a msb (most
'sign. byte) and a lsb (least sign. byte). Think of msb as the main bend,
'and the lsb as fine tuning. The values each range from 0 to 127. A value
'of msb = 64, lsb = 0 means no bending at all, msb = 127, lsb = 127 is a full
'2 semi-tone bend up, and msb = 0, lsb = 0 is 2 full semi-tones down. To make
'it easier think of it as pitch bending running from 0 to 128
'with 64 in the middle. And since we are only bending up, we just have to
'go from (0 to 64). We will add 64 to this to get the right bend. To start,
'we will set our frequency between the base note value and 2 semi-tones up
'then scale this between 0 and 64.
'
'lf (lower frequency) = b*n^x
'hf (higher frequency) = b*n^(x+2)
'
'if let k be our ratio we get
'
'k = 64 * ((f - lf)/(hf-lf))
'
'the msb would be the integer part of k + 64, while the lsb would be fractional
'part of k scaled from 0 to 127. Even though the range of msb could run
'from 64 to 128 (which is outside of the max value of 127) it won't because
'we are only bending at most 1 semi-tone.
'
'msb = int(k) + 64
'lsb = int((k - int(k)) * 127)
'
'At this point we could be done, but if we look at the equations, they
'could be cleaned up a bit.
'
'k, when we write it all out is
'
'k = 64 * ((f - b*n^x)/(b*n^(x+2) - b*n^x))
'
'that's three multiplications of b alone. If we factor out b and
'cancel it we get
'k = 64 * ((f/b - n^x)/(n^(x+2) - n^x))
'one division for 3 multiplications isn't bad but we can
'also clean up the denominator a little.
'n^(x+2) - n^x = n^x*n^2 - n^x = n^x(n^2 -1)
'
'since n is a constant, why solve n^2-1 each time, make it a const
'n^2 - 1 is approx. 0.1224620483093729814, so if we call this z we have
'
'k = 64 * ((f/b - n^x)/(n^x*z))
'
'and we can save another power evaluation buy setting w = n^x
'
'k = 64 * ((f/b - w)/(w*z))
dim f as single
dim x as integer
dim w as single
dim k as single
dim s as single
dim msb as integer
dim lsb as integer
f = frequency
'we are only going to allow sound frequency between 10 and 14000 hertz
'the average human with GOOD hearing can hear between 20-20000 hertz
'so this isn't too bad
'
'besides, after 5000 its almost all the same :-) annoying
if f < 10 then f = 10
if f > 14000 then f = 14000
'we use this a couple of times, so lets save it
'b = MidiNoteZeroFreq
s = f / MidiNoteZeroFreq
'LRBN is the Log of the Ration Between Two Notes, i.e. the Log(n)
x = int(log(s)/LRBN)
w = RBN^x
k = 64 * ((s - w)/(w*SRBNM))
msb = int(k) + 64
lsb = int((k - int(k)) * 127)
'do the midi stuff
dim hMidi as integer
'the channel, I used 15, because the default play note is on 0
dim o as integer
dim n as integer
dim t as single
n = x mod 12
o = (x-n)/12
const c = 15
hMidi = MidiOpen
MidiSetMainVolume hMidi, 127, c
MidiSetInstrument hMidi, Voice, c
MidiSetPitchBend hMidi, lsb, msb, c
MidiNoteOn hMidi, n, o, 127, c
t = timer + duration
do while t > timer
sleep 10
loop
MidiNoteOff hMidi, n, o, c
MidiSetPitchBend hMidi, 0, 64, c
MidiClose hMidi
end function
And a small driver program might be:
Code: '$include: 'midi_acc.bi'
option explicit
'Do it the hard way
dim hMidi as integer
hMidi = MidiOpen
MidiSetMainVolume hMidi, 127
MidiSetInstrument hMidi, midiCello
MidiNoteOn hMidi, midiC,3
sleep
MidiNoteOff hMidi, midiC,3
MidiClose hMidi
do while inkey$<>"":loop
'Play a single note the easy way
PlayNote midiG, 5, 1.5, midiChurch_Organ
'Play An F# Major scale in the 6th octave on flute
PlayNote midiFs, 6, .5, midiFlute
PlayNote midiGs, 6, .5, midiFlute
PlayNote midiAs, 6, .5, midiFlute
PlayNote midiB, 6, .5, midiFlute
PlayNote midiCs, 7, .5, midiFlute
PlayNote midiDs, 7, .5, midiFlute
PlayNote midiF, 7, .5, midiFlute
PlayNote midiFs, 7, .5, midiFlute
dim i as integer
for i = 0 to 5000 step 1000
midisound i,.1
next i
? "Done"
sleep
EDITED: Added MidiSound Function
Posts: 129
Threads: 15
Joined: Mar 2005
Posts: 639
Threads: 137
Joined: Jul 2002
To Keeling:
I have just this afternoon tested your little FB MIDI-based proggie out, and I dare believe that it is an EXCELLENT QB-like âSoundâ command that FreeBASIC 100% needs (and deserves, as well!)!!! d==b You know why? Because during that test there, the MIDI-based notes came out *so* clearly on the computer that I use! Perfect, I must say, my man!!
I must personally congratulate you on a spectactular job well done here, Keeling, from the very bottom of my heart!!! :king: Hey, I mean it now.
Talk to you later, and God BLESS you this coming weekend!! !
SUCH A REAL PLEASURE MEETING YOU HANDSOMELY,
Adigun Azikiwe Polack
One of the Founders of â Aura Flowâ ::: Continuing Developer of â Frantic Journeyâ
Current Developer of â Star Angelic Sluggerâ ::: Webmaster of the â AAP Official Projects Squadâ
Original Creator of the â The New FreeBASIC 8-Bit Palette Machineâ
url=http://dhost.hopto.org/aapproj/] [/url]
Your *official* home of the FreeBasic GFX Demo Central, now holding over 150 FB graphics demos so far!!! !
Posts: 149
Threads: 32
Joined: Feb 2005
:oops: What more can I say but thanks.
I am currently working on making a FB Sound command to mimmic QB's using the Midi pitch shift to match the input hertz. The range will only be from around 5 - 14,000 due to the limits of Midi but that should do for most applications.
Again, thanks for the kind words.
Randy
Posts: 437
Threads: 32
Joined: Mar 2004
Are you able to put that in a downloadable ZIP please?
Posts: 149
Threads: 32
Joined: Feb 2005
I guess so. But it is really just one file to cut and paste. :-)
Here it is. In the file is the bi file, a driver program and a compiled exe. A whole 9k. :-)
EDIT: The zip file here has also been updated.
Posts: 320
Threads: 24
Joined: Feb 2005
The only problem that I have with it is that the PlayNote function opens and closes the midi device every time it runs, so it makes all the notes staccato. Also, the PlayNote has no support for Channels!
Perhaps I'll modify it so that PlayNote takes the midi device as well as the channel, and maybe make it so that its a state machine, like OpenGL, so you can play multiple notes on the same channel at the same time with PlayNote.
.14159265358979323846264338327950288419716939937510582709445
Glarplesnarkleflibbertygibbertygarbethparkentalelelangathaffendoinkadonkeydingdonkaspamahedron.
Posts: 149
Threads: 32
Joined: Feb 2005
That's why there is the 'hard method', so that you can keep the midi device open, set the channel, whatever you want. The play note is there just for a quick one off note.
Look at the driver program I posted and in the .bi file at the functions:
MidiOpen
MidiClose
MidiNoteOn
MidiNoteOff
There are also functions in there for more advanced midi messages.
I am open to all ideas for the tool. If there is anything I can do to help or if you have some ideas for functions to add, let me know.
|