Ok, a little update, I'm eager to find a working solution for a Midi2AHK/AHK2Midi library...
Experiment: Use 3rd party application to write Midi messages to file:
As
demonstrated here, one can use midimon, or some other app to capture midi, write to file, and read it in.
Unfortunately, this generates an
ever growing file, that cannot be flushed without stopping the writing, since the file is in use
Experiment: Use CMDret-AHK functions to write 3rd party application Midi messages to memory:
My next experiment was with midimon and the
CMDret-AHK functions. I found out that the
lpbuffer variable somehow seemed to contain the latest received data, so I adapted the function to capture that.
Unfortunately, this
slowly eats memory, and is unstable (I force-close midimon to stop sofar), and, since the data is written to memory and not flushed, there is not much advantage over the previous method yet. I have no idea how to do the flushing of the buffer or a 'correct' close of midimon.
Maybe someone with a little more knowledge of DLLCalls could look at this.
Code:
#NoEnv
SetBatchLines, -1
OnExit, Cleanup
; check for existance of midimon.exe in script directory
IfNotExist, midimon.exe
{
MsgBox, 16, Error,
(
Error!
midimon.exe not found in script directory
Make sure you have a working copy of the midimon executable
in the directory where your script resides.
You can get it from:
http://www.sreal.com:8000/~div/midi-utilities/
Exiting...
)
ExitApp
}
; check for running instance of midimon, close if found
Process, Exist, midimon.exe
If errorlevel != 0
Process, Close, %errorlevel%
; Gui
Gui, +AlwaysOnTop
Gui, Add, ListBox, vListBoxTrigger x2 y2 w436 h380
Gui, Add, Button, x2 y376 w86 h20 gStart, Start fetching
Gui, Add, Button, x90 y376 w86 h20 gStop, Stop fetching
Gui, Add, Text, x200 y376 w200 h40, EXPERIMENTAL! Use at your own risk!
Gui, Show, center h400 w440, MIDIasInput4ahk - Method CMDret-AHK
Return
Start:
CMDret_RunReturn("midimon")
Return
Stop:
Process, Close, %cmdretPID%
return
Cleanup:
GuiClose:
Process, Close, %cmdretPID%
ExitApp
return
UpdateMidi(fromMidimon)
{
;GuiControl,, ListBoxTrigger, |
GuiControl,, ListBoxTrigger, |%fromMidimon%
}
; ******************************************************************
; CMDret-AHK functions
; version 1.08 beta
;
; Updated: March 31, 2006
; by: corrupt
; Code modifications and/or contributions made by:
; Laszlo, shimanov, toralf
; ******************************************************************
; Usage:
; CMDin - command to execute
; ******************************************************************
; Known Issues:
; - If using dir be sure to specify a path (example: cmd /c dir c:\)
; - Running 16 bit console applications may not produce output. Use
; a 32 bit application to start the 16 bit process to receive output
; ******************************************************************
; Additional requirements:
; - none
; ******************************************************************
; Code Start
; ******************************************************************
CMDret_RunReturn(CMDin)
{
Global cmdretPID testing
idltm := A_TickCount + 20
CMsize = 1
VarSetCapacity(CMDout, 1, 32)
VarSetCapacity(sui,68, 0)
VarSetCapacity(pi, 16, 0)
VarSetCapacity(pa, 12, 0)
Loop, 4 {
DllCall("RtlFillMemory", UInt,&pa+A_Index-1, UInt,1, UChar,12 >> 8*A_Index-8)
DllCall("RtlFillMemory", UInt,&pa+8+A_Index-1, UInt,1, UChar,1 >> 8*A_Index-8)
}
IF (DllCall("CreatePipe", "UInt*",hRead, "UInt*",hWrite, "UInt",&pa, "Int",0) <> 0) {
Loop, 4
DllCall("RtlFillMemory", UInt,&sui+A_Index-1, UInt,1, UChar,68 >> 8*A_Index-8)
DllCall("GetStartupInfo", "UInt", &sui)
Loop, 4 {
DllCall("RtlFillMemory", UInt,&sui+44+A_Index-1, UInt,1, UChar,257 >> 8*A_Index-8)
DllCall("RtlFillMemory", UInt,&sui+60+A_Index-1, UInt,1, UChar,hWrite >> 8*A_Index-8)
DllCall("RtlFillMemory", UInt,&sui+64+A_Index-1, UInt,1, UChar,hWrite >> 8*A_Index-8)
DllCall("RtlFillMemory", UInt,&sui+48+A_Index-1, UInt,1, UChar,0 >> 8*A_Index-8)
}
IF (DllCall("CreateProcess", Int,0, Str,CMDin, Int,0, Int,0, Int,1, "UInt",0, Int,0, Int,0, UInt,&sui, UInt,&pi) <> 0) {
Loop, 4
cmdretPID += *(&pi+8+A_Index-1) << 8*A_Index-8
Loop {
idltm2 := A_TickCount - idltm
If (idltm2 < 10) {
DllCall("Sleep", Int, 10)
Continue
}
IF (DllCall("PeekNamedPipe", "uint", hRead, "uint", 0, "uint", 0, "uint", 0, "uint*", bSize, "uint", 0 ) <> 0 ) {
Process, Exist, %cmdretPID%
IF (ErrorLevel OR bSize > 0) {
IF (bSize > 0) {
VarSetCapacity(lpBuffer, bSize+1)
IF (DllCall("ReadFile", "UInt",hRead, "Str", lpBuffer, "Int",bSize, "UInt*",bRead, "Int",0) > 0) {
IF (bRead > 0) {
TRead += bRead
VarSetCapacity(CMcpy, (bRead+CMsize+1), 0)
CMcpy = a
DllCall("RtlMoveMemory", "UInt", &CMcpy, "UInt", &CMDout, "Int", CMsize)
DllCall("RtlMoveMemory", "UInt", &CMcpy+CMsize, "UInt", &lpBuffer, "Int", bRead)
; ********************************** ADDED
testing := lpbuffer
UpdateMidi(testing)
; ********************************** /ADDED
CMsize += bRead
VarSetCapacity(CMDout, (CMsize + 1), 0)
CMDout=a
DllCall("RtlMoveMemory", "UInt", &CMDout, "UInt", &CMcpy, "Int", CMsize)
}
}
}
}
ELSE
break
}
ELSE
break
idltm := A_TickCount
}
}
cmdretPID=
DllCall("CloseHandle", UInt, hWrite)
DllCall("CloseHandle", UInt, hRead)
}
IF (StrLen(CMDout) < TRead) {
VarSetCapacity(CMcpy, TRead, 32)
TRead2 = %TRead%
Loop {
DllCall("RtlZeroMemory", "UInt", &CMcpy, Int, TRead)
NULLptr := StrLen(CMDout)
cpsize := Tread - NULLptr
DllCall("RtlMoveMemory", "UInt", &CMcpy, "UInt", (&CMDout + NULLptr + 2), "Int", (cpsize - 1))
DllCall("RtlZeroMemory", "UInt", (&CMDout + NULLptr), Int, cpsize)
DllCall("RtlMoveMemory", "UInt", (&CMDout + NULLptr), "UInt", &CMcpy, "Int", cpsize)
TRead2 --
IF (StrLen(CMDout) > TRead2)
break
}
}
StringTrimLeft, CMDout, CMDout, 1
Return, CMDout
}
Idea: Create custom driver that uses SendMessage/GetMessage to communicate:
Create a program (or maybe better: a DLL), that can do the following:
Midi2AHK:
- Open a Midi In port, and start listening
- Send the received Midi information as a system message
Now one could use an AutoHotkey script to monitor for those messages (OnMessage).
AHK2Midi:
- Listen to a specific system message
- Open a Midi Out port, and start sending
Now one could use an AutoHotkey script to send those messages (PostMessage/SendMessage).
Ideally:
- Option to set Midi In (and Out) channel
- Option to set Midi Thru
- Option to set the window handle and system message number to send on/listen to
- Option to transform/filter messages to send (like in cinmidi, further below)
While stumbling around all kinds of sites, looking for a solution, I found two interesting projects:
Improv
Quote:
Improv is a C++ environment for writing programs that enable musician/computer interaction using MIDI instruments. Improv programs can be written in special pre-defined environments, or they can be written from scratch using just the basic MIDI input and output classes.
Quote:
Permission is granted to use Improv code for non-commercial purposes including music composition, music performance or academic research and education. All other uses of Improv must be licensed.
PortMidiPortMidi Portable Real-Time MIDI Library, BSD license.
Quote:
PortMusic is a set of APIs and library implementations for music.
PortMusic is open-source and runs on Windows, Macintosh, and Linux.
Currently, libraries support Audio I/O and MIDI I/O.
Our goal is to extend the
PortAudio foundation to support MIDI, sound files, and perhaps other basic music software interfaces.
I'm no programmer, but this doesn't look too difficult, so:
would any C/C++ coder be willing to take a look at those sources? To get you started ...

... here is some code about Midi In, really doesn't look that difficult (but again: I'm no programmer):
From:
Improv How-To -
Basic MIDI inputQuote:
Example 1 demonstrates how to receive a MIDI message from an external MIDI synthesizer connected to your computer via a MIDI cable. MIDI synthesizers and other MIDI devices send MIDI messages which are typically three-bytes long. MIDI messages start with the MIDI command byte (a number between 128 and 255) which is then followed by 1, 2, or a variable length of parameter bytes depending on the MIDI command. Typical MIDI messages include note-ons, note-offs, program changes, continuous controllers, and system-exclusive messages, among others.
Example 1: A simple MIDI input program.
#include "improv.h"
int main(void) {
MidiInput midi(0);
MidiMessage message;
while (1) {
while (midi.getCount > 0) {
message = midi.extract();
cout << message << endl;
}
}
return 0;
}
Line 1 includes the file improv.h which contains definitions needed for reading MIDI input.
Line 4 creates a MidiInput object connected to the first available MIDI input port (0).
You can ask the MidiInput class how many input ports are available by using the member function getNumPorts().
You can ask for the names of the ports with the MidiInput member function getName().
Line 5 creates a MidiMessage object which will be used to extract MIDI messages from the MidiInput object whenever MIDI data arrives.
Lines 7-12 form a continuous loop which will extract MIDI messages as they arrive at the MIDI input port and print out the message to the terminal.
Lines 8-11 check the MidiInput object to see if it has any messages waiting in its buffer. If so, then extract the messages one-by-one and print them. The extract command extracts the oldest MIDI message which has not yet been extracted.
You can examine MIDI messages without extracting them by using the [] operator; for example, to look at the most recent complete MIDI message which has arrived, use midi[0]. To look at the second-to-last message which has arrived, you would use midi[1] or midi[-1].
Line 9 removes one incoming MIDI message from the input buffer and stores it in the message variable.
Where it looks like
red could be replaced by a
SendMessage/
PostMessageMore elaborate example can be found here
cinmidiQuote:
Display/Record MIDI input data.
Usage: cinmidi.exe [-a][-i][-x|-d|-2] [-o output][-p port]
Options:
-a = display MIDI timing in absolute time instead of delta time
-i = don't display interpretation of MIDI message as comments
-d = display MIDI bytes in decimal form
-x = display MIDI bytes in hexadecimal form
-2 = display MIDI bytes in binary form
-n = do not display note-off messages
-f string = filter out specified MIDI commands (e.g. "bc" will
filter out control change and patch change messages)
-c string = filter out specified MIDI channels, offset zero
(e.g., "09AF", filters channels 1, 10, 11, and 16)
-p port = use the specified MIDI input port
-l = list MIDI input ports and then exit
-o filename = send display to file as well as to screen
-u string = specify a user name for the header information
-s = display time values in seconds
-k = disable keyboard input
--options = list all options, default values, and aliases
Quote:
...
// function declarations
void checkOptions (Options& opts);
void displayMessage (ostream& out, MidiMessage message, int style);
void displayHeader (ostream& out);
...
Where it looks like
red could be replaced by a
SendMessage/
PostMessage
PortMidi doesn't look that much different/more difficult...
To all you programmers: I keep my fingers crossed... (I installed Dev-C++ and fiddled around myself, but I'm a programming noob)
