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)

6 Likes

Hi there, I have some knobs on my mechanical keyboard. How can I use this script to use those knobs as MIDI controllers so that I can assign these knobs to control the gain of my tracks in Cubase (for example)? I tried running the script but kept getting different error messages (I don’t have any knowledge of scripting). Any help would be appreciated!

Hey thanks for this! I have been needing to bind some keys to midi in order to control some plugins for a while, and your example probably saved me a few hours of messing around with a different AHK midi class library. Yours worked like a charm. Cheers!

1 Like

I’m glad you found it useful.
Out of curiosity and perhaps as inspiration to others, would you mind sharing your use case?

I use the ADPTR Metric A/B Plugin, which has a switch for a/bing your mix against reference mixes. I wanted to be able to trigger the A/B switch using my keyboard, and not have to reach for an external midi device. So the autohotkey script allows me to send midi to cubase from a key binding (using loopMIDI) which then is translated by a generic remote to map to the plugin.
Honestly it’s kind of silly that all of these hacky steps are required to do something very simple, but that said, in other DAWS this sort of setup may not even be possible.

1 Like

I have made some tweaks to the library that improves handling incoming SysEx messages as well as a more reliable way of closing MIDI In ports.

Updated version:
AHK MIDI.zip (3.0 KB)

1 Like

Have you considered making a video tutorial that explains the usage of AHK with MIDI in greater detail? I would like to learn more about your methods, but as a beginner in this subject it seems a bit overwhelming.

Hi there! No, making tutorial videos is not something that appeals to me. Sorry.
I suggest trying the sample script provided above to get an idea of what is possible. If you’re new to AHK there are plenty of resources available online to help you get started. The program itself also comes with an excellent help file with examples of its many commands and procedures.
If you have more specific questions, I will try and answer them as best as I can.

1 Like

Looks brilliant!
I just wanting a simple way to send to generic remote for some stuff to test (still on Cubase atm)…is this AHKv2?
EIDT: Just read again…v1 only :frowning:

I have Arturia Keylab MK2 midi keyboard. In the middle of the keyboard there is a “rotary knob” that you can turn. You turn it to browse between different presets (in Arturia collection plugins) and then you have to press the knob to select the preset.

I’m wondering if it’s possible to use AHK to bypass the pressing part? In other words, if I turn the knob one click to the right I would like it to select the next preset without me pressing down the knob. Do you think it would be possible?

Example: I turn the knob one click to the right and it triggers: 1). The knob turning one click to the right 2). A short delay 3). The pressing down of the knob. I hope it makes sense.

No. In user mode the jog direction and push are handled internally by the firmware. Jog can be handled externally in DAW mode, since it transmits midi messages. But… what’s wrong with pushing the knob? Is it defected?

It’s not defected - it’s just slightly annoying. I like browsing presets from top to bottom - one VST at a time - and see if I like something. And if I do, then I add a preset to favorites, for later.

I usually press down arrow key to go to the next preset, so I thought it would be handy if I could just turn the jog, as you call it, when using the Arturia keyboard.

The display on the keyboard is nice, but it’s too small for me to get a proper overview of all the presets as a list, for which a regular PC monitor is the best, imo. This is why it’s impractical for me to quickly skip several presets just to pick the one I need by pressing down the jog/knob.

And sometimes there is the risk that if I press it down, then I accidentally turn it to either side. Plus it’s just an unnecessary movement that adds strain to my hands in the long run.

I hope this clarifies why I wish to eliminate the pressing part from my workflow entirely.

If it was completely up to me, then my preference would be to bind the pressing down of the jog to: add current preset to favorites (the heart symbol). And I guess it could be possible using image recognition in AHK (clicking the heart). But the signals from the midi keyboard should be translated into something that AHK could recognize, and that is what’s a mystery to me.

There has been a misunderstanding. I was referring to the User Mode Presets, while you to the V collection instruments (Analog Lab Mode). In your cases, the jog IS sending midi messages, so it is doable to transform from the one to the other. For example, you could transform the jog left/right to preset up/down midi messages, even with adding delays, or just trigger the jog-push after some delay.

1 Like

Correct. AHK v2 is basically a fork from v1. Version 2 introduced many great improvements at the expense of backward compatibility. The differences are so great that it can almost be considered a different language.
Around 2021 when v2 was first released I made a half hearted effort to convert my many scripts to this updated version but gave up mostly due to lack of time. I also reckoned I wouldn’t really be gaining anything at the end of the day, so I have been sticking with v1 since.

Some things are worth noting though. The runtime engines for both v1 and v2 can happily coexists on the same machine. The AutoHotKey launcher makes an effort of detecting what version the script was written in but I would recommend using the #Requires directive.
There are also documented ways of loading v1 libraries in v2. import_v1lib and v1_func/v1_class_lib. Note that I personally have no experience with either of these approaches.

Final words
I may take up the challenge to convert the MIDI.ahk library into v2. Especially with the advent of AI as an aid.
Here is an article with some history and the differences between v1 and v2. It is written by Lexikos, the developer of AutoHotKey v2 and in my opinion does carry some bias. Interesting read none the less.

1 Like

What you did really inspired me…but I ended up just using sendmidi and it was pretty simple to get going; prefer native but its ok for now. Im actually porting much of my MTP stuff over to AHK. The tartarus is turning into the most efficient piece of gear I have ever bought, that little scroll wheel adapts as a fader/pan/track scroll blah blah…wish I had of spent a lot more time on AHK in the first place. Still use MTP for realtime midi but for controllers, gesturing etc its just absolutely wild…cubase or reaper.

Thanks for all your efforts :slight_smile:

i.e. If your script was v2, I would use it over having an external dependency

2 Likes