AutoHotkey Community

It is currently May 27th, 2012, 9:54 am

All times are UTC [ DST ]




Post new topic Reply to topic  [ 65 posts ]  Go to page Previous  1, 2, 3, 4, 5  Next
Author Message
 Post subject:
PostPosted: September 1st, 2006, 4:25 am 
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.


Report this post
Top
  
Reply with quote  
 Post subject:
PostPosted: September 1st, 2006, 3:09 pm 
Zan's recom: [Div's Midi Utilities] 8)


Report this post
Top
  
Reply with quote  
 Post subject:
PostPosted: September 1st, 2006, 7:01 pm 
Offline

Joined: May 24th, 2006, 2:49 pm
Posts: 4511
Location: Belgrade
Thats great :!:

_________________
Image


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: September 5th, 2006, 9:01 pm 
Offline

Joined: March 16th, 2005, 10:33 pm
Posts: 969
Location: Frisia
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 ... :wink: ... 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) :)

_________________
Image mirror 1mirror 2mirror 3ahk4.me • PM or Image


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: October 3rd, 2006, 12:55 pm 
Offline

Joined: March 17th, 2005, 10:25 am
Posts: 19
Location: 10247 Berlin
I tried looking for help at groups.google.com

Here seems to be information about MIDI.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: November 5th, 2006, 12:41 pm 
Offline

Joined: March 17th, 2005, 10:25 am
Posts: 19
Location: 10247 Berlin
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.


Report this post
Top
 Profile  
Reply with quote  
PostPosted: November 5th, 2006, 1:06 pm 
Offline

Joined: March 17th, 2005, 10:25 am
Posts: 19
Location: 10247 Berlin
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? :)


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: November 5th, 2006, 4:54 pm 
Offline

Joined: March 2nd, 2004, 3:36 pm
Posts: 10720
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.


Report this post
Top
 Profile  
Reply with quote  
PostPosted: November 24th, 2006, 9:23 am 
Offline

Joined: November 28th, 2004, 10:01 am
Posts: 32
Location: USA
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?

_________________
||||
------
!!


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: November 24th, 2006, 9:58 am 
Offline

Joined: December 27th, 2005, 1:46 pm
Posts: 6837
Location: France (near Paris)
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")

_________________
Image vPhiLho := RegExReplace("Philippe Lhoste", "^(\w{3})\w*\s+\b(\w{3})\w*$", "$1$2")


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: November 24th, 2006, 7:58 pm 
Offline

Joined: November 28th, 2004, 10:01 am
Posts: 32
Location: USA
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
Code:
msgbox %h_midiout%


nothing gets put in the msgbox.

might it contain some value that msgbox cannot display.
In which case, how do I view it?

_________________
||||
------
!!


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: November 24th, 2006, 8:20 pm 
Offline

Joined: December 27th, 2005, 1:46 pm
Posts: 6837
Location: France (near Paris)
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...)

_________________
Image vPhiLho := RegExReplace("Philippe Lhoste", "^(\w{3})\w*\s+\b(\w{3})\w*$", "$1$2")


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: November 24th, 2006, 10:05 pm 
Offline

Joined: November 28th, 2004, 10:01 am
Posts: 32
Location: USA
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.

_________________
||||
------
!!


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: November 25th, 2006, 3:30 am 
Offline

Joined: March 2nd, 2004, 3:36 pm
Posts: 10720
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.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: December 10th, 2006, 7:08 am 
Offline

Joined: September 23rd, 2006, 1:58 pm
Posts: 149
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


Report this post
Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 65 posts ]  Go to page Previous  1, 2, 3, 4, 5  Next

All times are UTC [ DST ]


Who is online

Users browsing this forum: No registered users and 3 guests


You can post new topics in this forum
You can reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Powered by phpBB® Forum Software © phpBB Group