AutoHotkey Community

It is currently May 26th, 2012, 6:53 am

All times are UTC [ DST ]




Post new topic Reply to topic  [ 80 posts ]  Go to page 1, 2, 3, 4, 5, 6  Next
Author Message
 Post subject: MIDI input library
PostPosted: April 12th, 2008, 4:18 pm 
Offline

Joined: April 9th, 2008, 8:46 am
Posts: 35
Location: Espoo, Finland
(Moved from the relevant topic in Wishlist http://www.autohotkey.com/forum/topic2831.html.. now in the correct sub forum)


I made a small library for integrating midi input to ahk scripts. It consists of a dll that uses a callback function to store the most recent value for each key velocity, cc, pitch wheel and channel aftertouch for every channel. To get midi input as windows messages you command the dll to send specific data only, and to specific message numbers. You can also directly ask any of the stored most recent values

I also wrote an ahk layer on top of the dll supposed to make it easier to use. It also creates a tray icon menu to select/change midi in device.

The dll file: http://ihme.org/~orbik/midi4ahk/midi_in.dll
Dll source (as a vc++ project): http://ihme.org/~orbik/midi4ahk/dll_source/
Dll function reference: http://ihme.org/~orbik/midi4ahk/midi_in.readme.txt

midi_in_lib.ahk:
Code:
midi_in_Open(defaultDevID = -1)
{
   global
   if ((midi_in_hModule := DllCall("LoadLibrary", Str,A_ScriptDir . "\midi_in.dll")) == 0)
   {
      MsgBox Cannot load library midi_in.dll"
      return 1
   }
   if (defaultDevID >= DllCall("midi_in.dll\getNumDevs"))
      defaultDevID := -1
   
   midi_in_MakeTrayMenu(defaultDevID)
   if (defaultDevID >= 0)
      midi_in_OpenDevice(defaultDevID)
   return 0
}

midi_in_MakeTrayMenu(defaultDevID)
{
   numDevs := DllCall("midi_in.dll\getNumDevs")
   global midi_in_lastSelectedMenuItem
   
   Menu devNameMenu, Add, No input, sub_menu_openinput
   Menu devNameMenu, Add ; separator
   if (defaultDevID < 0)
      midi_in_lastSelectedMenuItem := "No Input"

   loop %numDevs%
   {
      devID := A_Index-1
      if ((devName := DllCall("midi_in.dll\getDevName", Int,devID, Str)) == 0)
      {
         MsgBox, Error in creating midi input device list
         return 1
      }
      Menu devNameMenu, Add, %devName%, sub_menu_openinput
      if (devID == defaultDevID)
      {
         Menu devNameMenu, Check, %devName%
         midi_in_lastSelectedMenuItem := devName
      }
   }
   Menu TRAY, Add, MIDI-in device, :devNameMenu
}

sub_menu_openinput:
   midi_in_OpenDevice(A_ThisMenuItemPos-3)
   ; Move the check mark to new position
   Menu %A_ThisMenu%, Check, %A_ThisMenuItem%
   Menu %A_ThisMenu%, Uncheck, %midi_in_lastSelectedMenuItem%
   midi_in_lastSelectedMenuItem := A_ThisMenuItem
return

midi_in_OpenDevice(deviceID) ;deviceID < 0 means no input
{
   Critical
   midi_in_Stop()
   
   Gui +LastFound
   hWnd := WinExist()

   curDevID := DllCall("midi_in.dll\getCurDevID", Int)
   if (deviceID == curDevID)
      return 0
   if (curDevID >= 0)
      result := DllCall("midi_in.dll\close")
   if (result)
   {
      MsgBox Error closing midi device`nmidi_in.dll\close returned %result%
      return 1
   }
   if (deviceID < 0)
      return 0

      result := DllCall("midi_in.dll\open", UInt,hWnd, Int,deviceID, Int)
   if (result)
   {
      MsgBox Error opening midi device`nmidi_in.dll\open(%hWnd%, %deviceID%) returned %result%
      return 1
   }
;   MsgBox Press OK to start midi input
   midi_in_Start()
   return 0
}
   
midi_in_Close()
{
   global
   if (midi_in_hModule)
   DllCall("FreeLibrary", UInt,midi_in_hModule), midi_in_hModule := ""
}

midi_in_Start()
{
   DllCall("midi_in.dll\start")
}

midi_in_Stop()
{
   DllCall("midi_in.dll\stop")
}

listenNote(noteNumber, funcName, channel=0)
{
   global msgNum
   GoSub, sub_increase_msgnum
   DllCall("midi_in.dll\listenNote", Int,noteNumber, Int,channel, Int,msgNum)
   OnMessage(msgNum, funcName)
}

listenNoteRange(rangeStart, rangeEnd, funcName, flags=0, channel=0)
{
   global msgNum
   GoSub, sub_increase_msgnum
   msgCount := DllCall("midi_in.dll\listenNoteRange", int,rangeStart, int,rangeEnd, int,(flags & 0x07), int,channel, int,msgNum)

   
   if (msgCount <= 0)
      return
   if (flags & 0x01)
      loop %msgCount%
      {
         OnMessage(msgNum, funcName . A_Index)
         GoSub, sub_increase_msgnum
      }
   else
      OnMessage(msgNum, funcName)
}

listenCC(ccNumber, funcName, channel=0)
{
   global msgNum
   GoSub, sub_increase_msgnum
   DllCall("midi_in.dll\listenCC", Int,ccNumber, Int,channel, Int,msgNum)
   OnMessage(msgNum, funcName)
}

listenWheel(funcName, channel=0)
{
   global msgNum
   GoSub, sub_increase_msgnum
   DllCall("midi_in.dll\listenWheel", Int,channel, Int,msgNum)
   OnMessage(msgNum, funcName)
}

listenChanAT(funcName, channel=0)
{
   global msgNum
   GoSub, sub_increase_msgnum
   DllCall("midi_in.dll\listenChanAT", Int,channel, Int,msgNum)
   OnMessage(msgNum, funcName)
}


getNoteOn(noteNumber, channel)
{
   return DllCall("midi_in.dll\getNoteOn", Int,noteNumber, Int,channel)
}

getCC(ccNumber, channel)
{
   return DllCall("midi_in.dll\getCC", Int,ccNumber, Int,channel)
}

getWheel(channel)
{
   return DllCall("midi_in.dll\getWheel", Int,channel)
}

getChanAT(channel)
{
   return DllCall("midi_in.dll\getChanAT", Int,channel)
}

sub_increase_msgnum:
   if msgNum
      msgNum++
   else
      msgNum := 0x2000
return


And an example script to use the library:
Code:
SendMode Input
SetWorkingDir %A_ScriptDir%

OnExit, sub_exit
if (midi_in_Open(0))
   ExitApp

;--------------------  Midi "hotkey" mappings  -----------------------
listenNoteRange(48, 52, "playSomeSounds", 0x02)

return
;----------------------End of auto execute section--------------------

sub_exit:
   midi_in_Close()
ExitApp

;-------------------------Miscellaneous hotkeys-----------------------
Esc::ExitApp

;-------------------------Midi "hotkey" functions---------------------
playSomeSounds(note, vel)
{
   if (vel) ; vel == 0 means note off
   {
      SoundPlay drum%note%.wav
   }
}

;-------------------------  Midi input library  ----------------------
#include midi_in_lib.ahk


All relevant files: http://ihme.org/~orbik/midi4ahk/


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: April 12th, 2008, 4:40 pm 
Offline

Joined: February 14th, 2005, 4:05 pm
Posts: 4710
Location: Boulder, CO
This looks very nice. Thanks for sharing it!

Do I understand right the following? If I connect my USB midi keyboard to a PC, I can play music and listen to it through the PC sound system. That is, I don’t need another set of speakers or sound synthesizers. I only need to run a midi player AHK script. The logic of the player script has to be:
- midi_in_Open
- listenNoteRange
- listenWheel
- listen...
- Gui event or Hotkey: midi_in_Close

In the call to listenNoteRange a function name is given (which can have a 0..7 number affixed). They will be called, whenever a keyboard key is hit, with the parameters note, and the velocity of the key press. These functions have to call the necessary Windows midi API functions to play the sound. Similarly, in the call to listenWheel another function name is given, which has to process the wheel events.

At every call the listening functions increase the message number, starting from 0x2000, for which the player function reacts. The maximum is 0xFFFFFFFF, that will practically never be reached: 0xFFFFDFFF/10/60/60/24/365 = ~13.6 years of continuous playing, with 10 keys hit every second, therefore we need not worry about recycling expired message numbers.

The player function has to return really fast, so it can handle frequent calls. Maybe it has just to queue up requests and return. A timer subroutine will then perform the actions (sound playing), otherwise some keys get lost at fast music playing. An alternative is to set MaxThreads to a large value (20) in the AHK script. One can experiment with Critical, too, (or globally, with Thread, Interrupt, -1) what should buffer messages until the function returns. (It is not documented, how many unprocessed messages can AHK buffer – hopefully enough for normal players.)


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: April 12th, 2008, 8:35 pm 
Offline

Joined: April 9th, 2008, 8:46 am
Posts: 35
Location: Espoo, Finland
Laszlo wrote:
...
I connect my USB midi keyboard to a PC, I can play music and listen to it through the PC sound system. That is, I don’t need another set of speakers or sound synthesizers. I only need to run a midi player AHK script.

In theory you could make the script play sounds on midi input but what's the point of that? If you want music, just play a vst soft synth or the sound card's midi out with something like midi-ox.
The point here is to make your midi controller useful outside the standard midi applications. (launch apps, key presses, numerical data input with knobs...)

I guess I should make better documentation before posting stuff like this. I'll update the above post when ready.

Here's how you'd use it:

1: call midi_in_Open(deviceID)
Leave deviceID blank to default to no input and select it later

2: call listen*** for all input types you want to react to
A specified function will be called when matching midi message is received. With listenNoteRange you can have each note call a different function (set flag 1), where the function name is of the form funcName#, and # = position of the key within the specified range.
e.g. listenRange(36, 47, myfynction) will call myfunction1 on Note On and Note Off messages where note number is 36, myfunction2 when it's 37, etc

3: Each function called on midi input will receive 2 or 1 values as arguments: wParam and lParam. In note on/off messages wParam is the note number and lParam the velocity. More information in the readme file.


The message numbers are only incresed when the listen* functions are called. The other way would be to directly call the corresponding dll function with a desired message number, then call OnMessage with the same number and a function name. I didnt know how to avoid this extra layer (receive message -> call function), so I did my best to hide it.

Laszlo wrote:
The player function has to return really fast, so it can handle frequent calls. Maybe it has just to queue up requests and return.


I've understood that, with functions called by OnMessage, only 1 thread will execute per function, and other messages can still start a thread while another is executing.

Anyway I don't understand why you would use the midi input to play music. Ok, my example plays drum sounds, but it's just to show how it works.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: April 12th, 2008, 9:59 pm 
Offline

Joined: February 14th, 2005, 4:05 pm
Posts: 4710
Location: Boulder, CO
orbik wrote:
In theory you could make the script play sounds on midi input but what's the point of that?
It could be a very small (hundred-line) script, which takes input from the midi keyboard *and* from the PC keyboard. If your midi keyboard does not have enough buttons, or if they are not configurable, a PC keyboard key could activate pre-set instruments, patches, a metronome, drums, auto-base... You can make a synthesizer with your own effects, etc. I am sure you can find programs for all these, but not the way you may like it.

Of course, it is cool to start a Windows application with a short tune or with an accord played on the midi keyboard, especially, if it is on your desk, anyway. You can have tunes for often repeated info, like your name or address, but my Logitech G15 PC keyboard has three sets of 18 programmable keys, more than enough for my day-to-day work.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: April 14th, 2008, 10:55 pm 
Offline

Joined: March 3rd, 2008, 2:48 pm
Posts: 12
Location: Inside your mind.
Quote:
It could be a very small (hundred-line) script, which takes input from the midi keyboard *and* from the PC keyboard. If your midi keyboard does not have enough buttons, or if they are not configurable, a PC keyboard key could activate pre-set instruments, patches, a metronome, drums, auto-base...


Has this been done on this thread, or is there a script to do this availible from either of you?

I would attempt to code it myself, but I am currently knee deep in getting the "midi keyboard" script (located under topic: http://www.autohotkey.com/forum/viewtop ... 952#190952 )
to work on my computer... (an issue which stems from my PC only).

So I ask...

Is there a working wonder script for the like?

_________________
ImageImage
ImageImage


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: April 15th, 2008, 10:57 am 
Offline

Joined: April 9th, 2008, 8:46 am
Posts: 35
Location: Espoo, Finland
@ ProfessorY91
Although not sure what you're after, you probably don't need this library, but MIDI Output http://www.autohotkey.com/forum/viewtopic.php?p=119466#119466.
To use your default midi out port (midi mapper) just use -1 for device id.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: May 17th, 2008, 4:31 pm 
running your ahk script gives me an error message, that

Code:
DllCall("FreeLibrary", UInt,midi_in_hModule), midi_in_hModule := ""


is not a recognised function. What's the FreeLibrary .dll?


Report this post
Top
  
Reply with quote  
 Post subject:
PostPosted: May 17th, 2008, 4:47 pm 
Do you have the latest version of AutoHotkey?

Anyways, FreeLibrary is part of the Win32 API, and is documented here:
http://msdn.microsoft.com/en-us/library/ms683152.aspx


Report this post
Top
  
Reply with quote  
 Post subject:
PostPosted: May 17th, 2008, 5:03 pm 
Offline

Joined: February 13th, 2008, 10:13 pm
Posts: 11
didn't have the latest version, now installed, sorted out that issue... now to try this thing out

edit: the test patch works, this is fantastic news, thankyou very much Orbik


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: May 19th, 2008, 10:26 pm 
Offline

Joined: February 13th, 2008, 10:13 pm
Posts: 11
this is brilliant. Not sure I've really understood it all yet, but this is the first time I've used dllcall. Have it working for me by editing, copy and pasting the example script into the form I want (with functions from the readme), and then pasting in my autohotkey script for Ableton.

Anyway to set a midi device as default from this script. I presume that I can use
Code:
defaultDevID := -1

in the midi_in_lib.ahk, but I'm curious if I can do it from outside of that (e.g. in this case from what was the example file)?

I don't think I understand the difference between the Listen and Get functions. When would you use e.g. getCC instead of listenCC?

Thanks again, been wanting this for some time.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: May 20th, 2008, 9:01 am 
Quote:
Anyway to set a midi device as default from this script. I presume that I can use
Code:
defaultDevID := -1

in the midi_in_lib.ahk, but I'm curious if I can do it from outside of that (e.g. in this case from what was the example file)?


You need not (necessarily) change the lib. Use something like this:

Code:
; midi_in_Stop()
midi_in_Close() ; closes current device
midi_in_Open(0) ; opens device


Quote:
I don't think I understand the difference between the Listen and Get functions. When would you use e.g. getCC instead of listenCC?


The Listen functions create an OnMessage() handler, they provide the script with the stream of data you asked to get (in the example file the sub-routine playSomeSounds is created with the listenNoteRange function call).

The Get functions return the most recent value (only).

HTH


Report this post
Top
  
Reply with quote  
 Post subject:
PostPosted: May 20th, 2008, 1:48 pm 
Offline

Joined: February 13th, 2008, 10:13 pm
Posts: 11
thanks n-l-i-d


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: May 20th, 2008, 3:55 pm 
Short tutorial wrote:
"What is this anyway, what can it do, why should I use it and what do I need?"

What is this anyway?

Important: do not confuse the midi file format with the protocol. The midi protocol is used for sending/receiving event information real-time between midi-enabled devices, usually musical instruments.

Now, with this excellent dll, you can directly use incoming midi information from within AutoHotkey (it has become fully midi-enabled).

What can it do?

Wikipedia wrote:
Every MIDI connection is a one-way connection from the MIDI Out connector of the sending device to the MIDI In connector of the receiving device. Each such connection can carry a stream of MIDI messages, with most messages representing a common musical performance event or gesture such as note-on, note-off, controller value change (including volume, pedal, modulation signals, etc.), pitch bend, program change, aftertouch, channel pressure. All of those messages include channel number. There are 16 possible channels in the protocol. The channels are used to separate "voices" or "instruments", somewhat like tracks in a multi-track mixer.


A sending device (also called: Midi-controller) probably has:

* keys / pads / buttons

Properties:
- note (which one is hit/released)
- velocity (how hard is it hit)
- aftertouch (if pressed after hit, how hard is the key pressed)

...it might also have:

* knobs / sliders / wheels

Properties:
- layout (up-down, left-right or centered)
- value

Good to note:

- Midi-controllers output 0 thru 127 as values

Why should I use it?

Examples:
- launch an application after a keypress (or combination: melody)
- scroll a page with a rotating knob or slider
- control your Photoshop pots and sliders with real knobs and sliders

So, for very little money, you can add a multitude of extra controllers to your computer.

What do I need

1. Get midi support for your computer if not available. You could:
- use the soundcard's (SoundBlaster) gameport2midi option
- add a soundcard with direct midi support
- hook up an external converter with midi support (firewire/usb)
2. Get a midi-cable
3. Hook up any midi-controller's midi-output with the midi-input from your computer
4. Check the midi routing in the software/drivers for your specific midi-support
5. Set up the script/dll to start receiving
6. Start sending from the midi-controller

You can now control your computer with your midi-controller...

Sources:
MIDI controller @ Wikipedia
Musical Instrument Digital Interface @ Wikipedia
The MIDI 1.0 Protocol @ Wikipedia
Game port/DA-15 connector @ Wikipedia


Report this post
Top
  
Reply with quote  
 Post subject:
PostPosted: June 28th, 2008, 12:41 am 
Offline

Joined: February 13th, 2008, 10:13 pm
Posts: 11
Could anybody tell me how to use timers in conjunction with this? Such that I can listen for a string of cc's, and if I do not receive one (or after the cc inout stops) in a set time, I can activate a subroutine...

I have been trying to emulate the Novation Speed-dial - use one dial to clickdrag the mouse up or down, when over a control. Here's what I have, which works to a degree:

Code:
OnExit, sub_exit
if (midi_in_Open(13))
   ExitApp

;--------------------  Midi "hotkey" mappings  -----------------------
listenCC(122, "DialL", 16)
listenCC(123, "DialR", 16)

return
;----------------------End of auto execute section--------------------

sub_exit:
   midi_in_Close()
ExitApp

;-------------------------Miscellaneous hotkeys-----------------------
Esc::ExitApp

;-------------------------Midi "hotkey" functions---------------------


DialL(Note,Vel)
{
   if (1)
   GoSub TurnLeft 
}
return

DialR(Note,Vel)
{
   if (65)
   GoSub TurnRight
}
return

;-------------------------  Midi input library  ----------------------
#include midi_in_lib.ahk

;-------------------------  Subs  ----------------------

TurnLeft:
{
Mouseclickdrag, Left, 0, 0, -2, 2, 100, r
}
return

TurnRight:
Mouseclickdrag, Left, 0, 0, 2, -2, 100, r
return



I'm not sure where to use a timer - tried it in the Gosub's, but each of those is activated each time a value is received by ListenCC - in other words I am getting a string of 2 pixel clickdrag's, rather than one longer one; a longer drag would disable the actual mouse while in use, and optionally allow me to use Mousegetpos to return the cursor to it's original position.
I assume that I need the timer at the top of the script somewhere, but I've got to the point of tiredness where I can't figure it out for myself, so any help would be appreciated.
Thanks.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: August 7th, 2008, 9:52 am 
Offline

Joined: February 20th, 2007, 1:37 pm
Posts: 198
Location: D.C.
this looks like it really might do something that I've been wanting for a long time. If I understand you correctly, orbik, I'll be able to use my MIDI keyboard to (in this instance) send a different text string with each note. In such case, I would not want anything except for the note number to be sent, and have that map to a text string. Does that sound about right ?

I'll try downloading this library at some point soon and playing around with it. Thanks for your great work!


Last edited by ribbet.1 on September 6th, 2009, 4:30 pm, edited 1 time in total.

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

All times are UTC [ DST ]


Who is online

Users browsing this forum: Exabot [Bot], mrhobbeys, nothing, siterip, Stigg and 11 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