AutoHotkey and MIDI

I’ve been toying with AutoHotkey and MIDI a bit lately and thought I would share my findings in case anyone else finds it interesting.
I’ve cobbled together a very simple “library” file that can be used to:

  • Return all available MIDI input as well as output ports
  • Open and Close MIDI input and output ports
  • Send Note On and Note Off messages
  • Send Continuous Controller messages (MIDI CC)
  • Receive the following MIDI messages:
    • Note On
    • Note Off
    • Aftertouch
    • CC
    • Program change
    • Channel pressure
    • Pitchbend
    • SysEx (!)

Below is an example of how to use it.

#NoEnv
SendMode Input
SetWorkingDir %A_ScriptDir%
setbatchlines -1	; Never sleep
#SingleInstance force
#Persistent
#include %A_ScriptDir%\MIDI.ahk	;Path to the library

;SET UP ---------------------------------------
MIDI_IN_CHANNEL_FILTER := 0	; MIDI channel 1
MIDI_OUT_CH := 1	; MIDI channel 2

; Get all MIDI ports
midiInDevices := getMidiInDevices()
midiOutDevices := getMidiOutDevices()

; Display all MIDI ports
for index, device in midiInDevices
{
	portName := index-1 . ": " . device
	strInPorts .= portName . "`n"
}
for index, device in midiOutDevices
{
	strOutPorts .= index-1 . ": " . device . "`n"
}
MsgBox, % "MIDI In`n" strInPorts "`nMIDI Out`n" strOutPorts

; Open MIDI In
midiInDeviceId := 4
openMidiIn(midiInDeviceId)

; Open MIDI Out
midiOutDeviceId := 5
openMidiOut(midiOutDeviceId)
return


; MIDI OUT ------------------------------------
F10::
MIDI_NoteOn(1, 100)
KeyWait, F10	;Prevents key repeat
return

F10 Up::
MIDI_NoteOff(1)
return

F11::
MIDI_CC(21, 55)
return

F12::
MIDI_CC(50, 64)
return

; MIDI IN -------------------------------------
MidiControlChange:
MsgBox, % "CC#" midiEvent.controller ", value=" midiEvent.value
return

MidiControlChange62:
MsgBox, % "I received MIDI CC 62`nwith a value of " midiEvent.value
return

MidiNoteOn:
MsgBox, % "Note#" midiEvent.noteNumber ", velocity=" midiEvent.velocity
return

MidiNoteOff60:
MsgBox, % "Note Off. Note #60`nNote Off velocity=" midiEvent.velocity
return

MidiSysEx:
Msg :=
raw :=
loopMax := SysexMessage.MaxIndex() - 2
raw := SysexMessage[1] . " "
Loop, %loopMax%
{
	raw .= SysexMessage[A_Index+1] . " "
	dec := Format("{:i}", "0x" SysexMessage[A_Index+1])
	ansi := Chr(dec)
	Msg .= ansi
	OutputDebug, % "A_Index=" . A_Index . ", element=" . SysexMessage[A_Index+1]
}
raw .= SysexMessage[SysexMessage.MaxIndex()]
MsgBox, % "SysEx`n`nRaw hex:`n" . raw . "`n`nASCII:`n" . Msg
return

What can you use this for?

Easily convert keyboard and mouse actions into MIDI.
Turn a joystick or game controller into a MIDI controller. (Not tested, but shouldn’t be hard to accomplish.)
Interface with MIDI Remote in new interesting ways, such as building on-screen soft keys for Cubase functions with feedback.
Display parameter values from your MIDI controller that lacks a value read-out, or a VST parameter your screenless MIDI Controller is controlling via MIDI Remote.
Probably more fun things that I can’t think of right now.

Caveats

This has been tested with AutoHotkey v1 only.

I have been using this with a virtual MIDI port (loopMIDI) and noticed that MIDI data sent would echo back to the input port. My simple solution to this is to use different MIDI Channels for input and output and neglect data that are not received on the specified channel. MIDI_IN_CHANNEL_FILTER is used for this as shown in the example above. I did not observe this behavior when using physical MIDI ports.

I have put together this for my own personal use and the code is presented “as is”. I do not guarantee that it will work for you.
I am unlikely to provide extensive support.
I am unlikely to implement additional features. AutoHotkey has a large user base and is very well documented. Any additional functions or features you might be able to dream up is probably better implemented by someone else anyway.

AHK MIDI.zip (2.9 KB)

3 Likes