 |
AutoHotkey Community Let's help each other out
|
| View previous topic :: View next topic |
| Author |
Message |
Zan Guest
|
Posted: Fri Sep 01, 2006 3:25 am Post subject: |
|
|
Note that you can very easily integrate AutoHotKey with Div's Midi Utilities sendmidi program.
However, it is quite resource heavy and not very fast, ie there is a little bit of lag. I assume this is because everytime you use sendmidi it has to open up the midi device, as opposed to a program that opens it up and leaves it open.
What would be ideal is if someone could build a little app that opened up the device, then accept windows messages which would be translated into midi events. |
|
| Back to top |
|
 |
BoBo Guest
|
|
| Back to top |
|
 |
majkinetor
Joined: 24 May 2006 Posts: 4511 Location: Belgrade
|
Posted: Fri Sep 01, 2006 6:01 pm Post subject: |
|
|
Thats great  _________________
 |
|
| Back to top |
|
 |
daonlyfreez
Joined: 16 Mar 2005 Posts: 949 Location: Berlin
|
Posted: Tue Sep 05, 2006 8:01 pm Post subject: |
|
|
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. |
PortMidi
PortMidi 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 input
| Quote: | 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/PostMessage
More elaborate example can be found here cinmidi
| Quote: |
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)  _________________
mirror 1 • mirror 2 • mirror 3 • ahk4.me • PM or  |
|
| Back to top |
|
 |
hamoid
Joined: 17 Mar 2005 Posts: 19 Location: 10247 Berlin
|
|
| Back to top |
|
 |
hamoid
Joined: 17 Mar 2005 Posts: 19 Location: 10247 Berlin
|
Posted: Sun Nov 05, 2006 11:41 am Post subject: |
|
|
I just did a new experiment in my quest for controlling Photoshop and other programs with a MIDI controller :)
MIDI-OX can be scripted using Windows Script Host (WSH).
So I did a small .js program that receives MIDI data and converts it to key strokes. This is without using AHK.
| Code: | var mox;
var n;
var notesOn;
var baseChan;
var SH;
SH = WScript.CreateObject("WScript.Shell")
mox = WScript.CreateObject("Midiox.MoxScript.1", "On_");
n = mox.InstanceCount;
notesOn = 0;
baseChan = 0;
mox.DivertMidiInput = 1;
mox.FireMidiInput = 1;
WScript.Echo( "Press OK to end MIDI translate Loop" );
mox.FireMidiInput = 0;
mox.DivertMidiInput = 0;
mox = null;
function On_MidiInput( ts, port, stat, dat1, dat2)
{
if (dat2)
{
SH.SendKeys("+{HOME}" + (dat2*2))
}
}
|
Double clicking in this .js file starts MIDI-OX. Then I switch to something, like notepad, and move a midi controller. It sends Shift+Home (to select previous text) and types the new number many times per second, so you control a number in notepad with MIDI. The fun part is, if I am in Photoshop, open an image, hit CTRL+L (levels), put the cursor at the end of one of the number fields and move the MIDI controller, the image brightness changes accordingly, in real time.
Of course this is a very limited example: I can't assing different controllers to different inputs in Photoshop. That would require AHK MIDI support (to focus on desired input fields, I don't think that can be done with WSH).
A slow workaround is possible: MIDI-OX receives data. I filter the data and convert to SendKeys sequences. I program AHK to parse those key sequences. AHK focuses on fields and sends keys again. So basically, building a SendKeys communication between .js and AHK. A bit twisted and probably slow, but would work. |
|
| Back to top |
|
 |
hamoid
Joined: 17 Mar 2005 Posts: 19 Location: 10247 Berlin
|
Posted: Sun Nov 05, 2006 12:06 pm Post subject: Open Sound Control (OSC) in AHK |
|
|
This is something new: maybe something better than MIDI could be implemented?
Open Sound Control (OSC) is an modern open standard for communicating computers, software and devices. It tries to solve the limitations of MIDI, which was created very long time ago.
OSC allows communication between programs, either local or through the Internet.
There are lots of implementations:
http://cnmat.berkeley.edu/OpenSoundControl/
Here a C++ library:
http://www.audiomulch.com/~rossb/code/oscpack/
Adding OSC to AHK would allow sending and receiving commands from most programming languages and many types of software. For example it could allow communicating two computers running AHK, without doing DLL tricks.
About the original post, it looks like MIDI can run inside OSC, so implementing OSC should "solve" the original MIDI idea in this thread.
Anybody interested? :) |
|
| Back to top |
|
 |
Chris Site Admin
Joined: 02 Mar 2004 Posts: 10716
|
Posted: Sun Nov 05, 2006 3:54 pm Post subject: |
|
|
| I'd be interested in working on this someday, but it will probably be a long time due to other priorities. Hopefully, someone will beat me to it by developing an interface DLL, or by directly modifying AutoHotkey. |
|
| Back to top |
|
 |
Anatoly Larkin
Joined: 28 Nov 2004 Posts: 32 Location: USA
|
Posted: Fri Nov 24, 2006 8:23 am Post subject: DllCall("winmm.dll\midiOutOpen", |
|
|
So, I have been trying to follow the idea and use winmm.dll to open and play notes through my onboard midi.
I do this
| Code: |
result := DllCall("winmm.dll\midiOutOpen"
, Int, h_midiout
, UInt, 0
, UInt, dwCallback
, UInt, dwCallbackInstance
, UInt, dwFlags
, "UInt")
|
and the result = 11
h_midiout doesn't get initialized to anything.
Any clue? _________________ ||||
------
!! |
|
| Back to top |
|
 |
PhiLho
Joined: 27 Dec 2005 Posts: 6836 Location: France (near Paris)
|
Posted: Fri Nov 24, 2006 8:58 am Post subject: |
|
|
Cannot test it, but since h_midiout receives a value, you have to give its address:
| Code: | h_midiout := 0000 ; Important!
result := DllCall("winmm.dll\midiOutOpen"
, UInt, &h_midiout
, UInt, uDeviceID
, UInt, 0
, UInt, 0
, UInt, dwFlags
, "UInt")
|
_________________
vPhiLho := RegExReplace("Philippe Lhoste", "^(\w{3})\w*\s+\b(\w{3})\w*$", "$1$2") |
|
| Back to top |
|
 |
Anatoly Larkin
Joined: 28 Nov 2004 Posts: 32 Location: USA
|
Posted: Fri Nov 24, 2006 6:58 pm Post subject: |
|
|
Thanks.
Your suggestion improved things to the point where now the result = 0, rather than "11".
However, I don't know if h_midiout gets anything.
If I do
nothing gets put in the msgbox.
might it contain some value that msgbox cannot display.
In which case, how do I view it? _________________ ||||
------
!! |
|
| Back to top |
|
 |
PhiLho
Joined: 27 Dec 2005 Posts: 6836 Location: France (near Paris)
|
Posted: Fri Nov 24, 2006 7:20 pm Post subject: |
|
|
Strange, it should have 0000 or, if success, a number.
You should check also ErrorLevel.
What values do you put in uDeviceID and dwFlags? (No idea of what should go there, I just took a look at the function page...) _________________
vPhiLho := RegExReplace("Philippe Lhoste", "^(\w{3})\w*\s+\b(\w{3})\w*$", "$1$2") |
|
| Back to top |
|
 |
Anatoly Larkin
Joined: 28 Nov 2004 Posts: 32 Location: USA
|
Posted: Fri Nov 24, 2006 9:05 pm Post subject: |
|
|
Ok, I think everythin is fine now.
I made sure to initialize every single dw variable to := 0 first.
And now I am getting some 3 byte thing put in h_midiout (obviously, the handle).
Now, the next struggle is to get the device to play something through things like midiOutShortMsg.
I'll see if I run into more problems. _________________ ||||
------
!! |
|
| Back to top |
|
 |
Chris Site Admin
Joined: 02 Mar 2004 Posts: 10716
|
Posted: Sat Nov 25, 2006 2:30 am Post subject: |
|
|
By the way, I don't recommend using Var:=0000 because it's undocumented behavior that could change. Anything to the right of the := operator is an expression, and in this case it's purely numeric so could wind up being stored as a single zero someday.
So instead, I think Var:="0000" is preferable. |
|
| Back to top |
|
 |
Zed Gecko
Joined: 23 Sep 2006 Posts: 149
|
Posted: Sun Dec 10, 2006 6:08 am Post subject: |
|
|
regarding this topic, i found a webpage:
http://www.logosfoundation.org/gmt/gmt_midi.html
I don´t understand a lot of it, but it seems to me,
that it contains the sourcecode for a dll, which handles
MIDI In/Output. It says that i can be compiled in C or in C++.
I have no experience with any of this, but maybe someone
else could look at this and make us a usable dll |
|
| Back to top |
|
 |
|
|
You can post new topics in this forum You can reply to topics in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|