Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

Midi Input/Output combined - with System Exclusive!


  • Please log in to reply
112 replies to this topic
genmce
  • Members
  • 144 posts
  • Last active: May 21 2015 03:09 PM
  • Joined: 10 Jan 2009

Version 0.6 at download link below.
Added conversion for joystick and mouse to midi.

* These scripts were written for AHK - BASIC and have not been tested on AHK_ L
EDIT - using scite for ahk ver 3 beta, they seem to work compiled AHK_L ansi, not on unicode - up to you to test.

Update - 10/26/2010
*For System Exclusive examples - see page 4, this thread,
Many thanks to dorfl68 and the paddy forum! :lol:
*note - the generic midi program link on this post and this file does NOT have the sys_ex stuff... yet.
It will be incorporated, eventually into version 0.7.

-----------

There are two threads already out here, one on midi output and one on midi input.
After spending time on both of those threads, it seemed to me it was time for another thread that combined them both.

That is this thread and the general_midi_app.

The app includes:
- listbox midi port selection, (gui)
- ini read/write of port selection
- a gui monitor (revised - using listview gui - looks nicer)
- midi input (your selected port)
- midi output (your selected port)
- examples of MidiRules - converting midi notes (transposing), example of converting cc to a different cc
- Could convert midi to keyboard/mouse actions (no examples given)
- 2 different hotkey generated midi message examples.
- Joystick - multiple axes conversion to midi example
- Mouse movement to midi example
- Working on mouse wheel to midi conversion (not finished - just needs to add midi output code.

This is intended to streamline the process, so one can jump in and start playing with midi, quickly.

It does not include the midi_in.dll (orbik's cool dll) (I had problems with that on different systems due to dependencies and with some of the listen(functions).
Don't get me wrong - The midi in portion of this script still uses Orbik's code (nearly verbatim) - his script that calls the winmm.dll directly.

It does require the user to setup rules to filter midi data to manipulate using if else or ifequal ... etc.. methods. Once you get used to it, it is a much more direct way to deal with the midi data, in my opinion.

I know when i first came here, midi is what brought me.
I wanted to do scripting for midi. I think there are others that want to do this as well. This is provided to help lure them into this world.
There may be errors in the script, for that, I apologize.

So many people have worked very hard on this and most of it comes directly from this forum in one form or another. I do not wish to take credit, except for bringing the elements together into one place.
TomB, Laszlo, JimF, Orbik and many, many others, too many for me to even know who did what...contributed. If you contributed and I did not mention you, please forgive me. Tell me here and I will add you.

Please post your creations here -
Please post script revisions to this forum post.
Please post your derivative projects here, so that others can learn from what you do.
Please share, as I am sharing, so that others may learn and grow!

Here ya go -

Site keeps getting hacked - uped to new site - Oct. 11, 2013
Lastest version 0.6 - Dec. 17, 2010
*Link, above, fixed to point to site instead of file - Site is sometimes slow.

Ver 0.6 with joystick to midi and mouse to midi.

I will leave version 0.4 below for reference.
Version 0.4 below.

/*
; Last edited 9/5/2010 8:55 AM by genmce


I fear that adding so many different examples may make this more difficult to use. ....
I may have to pull different parts, MidiRules into an include file the same goes for the hotkeys midi generation...
Ah well...

****************************************************************
Please post your revisions back to this forum post.
Please post your derivative projects back to this page, so that others can learn from what you do.
Please share, as I am sharing, so that others may learn and grow!
****************************************************************



Generic Midi Program
Basic structural framework for a midi program in ahk.
The description what this is for is contained in the first post on the topic Midi Input/Output Combined at the ahk forum.
Please read it, if you want to know more.
I have added a few more examples for different midi data types as well as more, meaningful (I hope), documentation to the script.
You are strongly encouraged to visit http://www.midi.org/techspecs/midimessages.php (decimal values), to learn more
about midi data. It will help you create your own midi rules.
I have combined much of the work of others here.
It is a working script, most of the heavy lifing has been done for you.
You will need to create your own midi rules.
By creating or modifying if statements in the section of the MidiRules label.
I don't claim to be an expert on this, just a guy who pulled disparate things together.
I really need help from someone to add sysex functionality.

****** Sections with !!!!!!!!!!!!!!!!!!!!!! - don't edit between these, unless you know what you are doing (uykwyad) !

****** Sections with ++++++++++++++++++++++ Edit between these marks. You won't break it, I don't think???

v. 0.4
+ added an example of hotkey generating midi (volume controller)
+ added a second example for you create your own hotkey generated midi message.


v. 0.3 changes
+ Adding text for how to add new rules.
+ Midi Rules, name used instead of filters, seems more appropriate.
+ Moved all rules outside the detector function, hoping to make it easier to understand and use.
+ abandoned the idea of dynamic code and a gui to create rules, omg, someone else do that, please!
+ Adding more examples for rules.
+ more to come, maybe...
+ removed that notemsg var - did not need it, not sure why i used it... nevermind


TODO -
+ add a way to echo all midi input that is not filtered or modified to the output. Like a gui switch (sometimes you want it all sometimes you don't)
+ make the midi monitor easier to use and read, also a toggle for it to be on or off.
- midi monitor columnar with data columns to show statusbyte, byte1 and byte2 as well as midi chan.
So need a heading grid for each ....
+ bring back sendnote() funtion instead of a gosub, same for each type of midi data.
+ Figure out how to do SYSEX with it, PLEASE HELP ME ON THIS!

Generic midi program v. - 0.2 changes

- fixed bad note off detection...
- select input and output port
- open and close selected midi ports
- write ports to ini file
- send receive midi data
- modify received midi data, send it to output port
- uses contributions by so many different people. See midi input and midi output forum posts. Links in midi under the hood section below
- "under the hood" midi functions and subroutines at the end of this file
- post your creations back to this midi I/O thread - that way we can all build on it.

Notes - All midi in/out lib stuff is included here, no external dlls, besides winmm.dll required.
Use of this - you should only need to edit the input part - great way to practice you if else logic and midi manipulation.
This does not do sysex. I really want to develop that, but not sure how to go about it.
Perhaps one day.

*/

; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! no edit here

#Persistent
#SingleInstance
SendMode Input ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir% ; Ensures a consistent starting directory.

if A_OSVersion in WIN_NT4,WIN_95,WIN_98,WIN_ME ; if not xp or 2000 quit
{
MsgBox This script requires Windows 2000/XP or later.
ExitApp
}

; ===============
version = Genmce_Generic_Midi_App_ ; Change this to suit you.
; ===============

readini() ; load values from the ini file, via the readini function - see below.
gosub, MidiPortRefresh ; used to refresh the input and output port lists - see label below
port_test(numports,numports2) ; test the ports - check for valid ports?
gosub, midiin_go ; opens the midi input port listening routine
gosub, midiout ; opens the midi out port
gosub, midiMon ; see below - a monitor gui - for learning mostly - you will probably comment this out eventually.
; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! end edit here

; =============== set variables you may use in MidiRules section

cc_msg = 73,74 ; ++++++++++++++++ you might want to add other vars that load in auto execute section This example goes with

; varibles below are for keyboarcc
channel = 1 ; default channel =1
ccnum = 7 ; 7 is volume
volVal = 0 ; Default zero for volume
volDelta = 10 ; Amount to change volume
; end of vars for hotkey and keyboardcc

/*
yourVar = 0
yourVarDelta = 3
yourVarCCnum = 1 ; modwheel
*/

settimer, KeyboardCCs, 70 ; timer to run the KeyboardCCs at the 70 interval

return ; !!!! no edit here, need this line to end the auto exec section.

; =============== end of auto execute section =====================

/*
PARSE - LAST MIDI MESSAGE RECEIVED -
*/
; =============== MIDI input MESSAGE PARSE ===============

; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! no edit below here ....
; don't edit this part!
MidiMsgDetect(hInput, midiMsg, wMsg) ; Midi input section in "under the hood" calls this function each time a midi message is received. Then the midi message is broken up into parts for manipulation. See http://www.midi.org/techspecs/midimessages.php (decimal values).
{
global statusbyte, chan, note, cc, byte1, byte2

statusbyte := midiMsg & 0xFF ; EXTRACT THE STATUS BYTE (WHAT KIND OF MIDI MESSAGE IS IT?)
chan := (statusbyte & 0x0f) + 1 ; WHAT MIDI CHANNEL IS THE MESSAGE ON?
byte1 := (midiMsg >> 8) & 0xFF ; THIS IS DATA1 VALUE = NOTE NUMBER OR CC NUMBER
byte2 := (midiMsg >> 16) & 0xFF ; DATA2 VALUE IS NOTE VELEOCITY OR CC VALUE

; GUI for midi monitor
GuiControl,12:, MidiMs, MidiMon:%statusbyte% %chan% %byte1% %byte2% ; MidiMs (green text in gui) in midimon gui below - this is for display you will probably comment this out evenually.

gosub, MidiRules ; run the subroutine below

} ; end of MidiMsgDetect funciton


; =============== filters/rules subroutine tests

/*
The MidiRules section is for modifying midi input from some other source.
*See hotkeys below if you wish to generate midi messages from hotkeys.

Write your own MidiRules and put them in this section.
Keep rules together under proper section, notes, cc, program change etc.
Keep them after the statusbyte has been determined.
Examples for each type of rule will be shown.
The example below is for note type message.

Remember byte1 for a noteon/off is the note number, byte2 is the velocity of that note.
example
ifequal, byte1, 20 ; if the note number coming in is note # 20
{
byte1 := (do something in here) ; could be do something to the velocity(byte2)
gosub, SendNote ; send the note out.
}
*/

MidiRules: ; write your own rules in here, look for : ++++++ for where you might want to add
; stay away from !!!!!!!!!!

; =============== Is midi input a Note On or Note off message? ===============
if statusbyte between 128 and 159 ; see range of values for notemsg var defined in autoexec section. "in" used because ranges of note on and note off
{ ; beginning of note block

if statusbyte between 144 and 159 ; detect if note message is "note on"
GuiControl,12:, MidiMsOut, noteOn:%statusbyte% %chan% %byte1% %byte2% ; display noteOn message in gui
if statusbyte between 128 and 143 ; detect if note message is "note off"
GuiControl,12:, MidiMsOut, noteOff:%statusbyte% %chan% %byte1% %byte2% ; display note off in gui

; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! above end of no edit

; =============== add your note MidiRules here ==; ===============

/*
Write your own note filters and put them in this section.
Remember byte1 for a noteon/off is the note number, byte2 is the velocity of that note.
example
ifequal, byte1, 20 ; if the note number coming in is note # 20
{
byte1 := (do something in here) ; could be do something to the velocity(byte2)
gosub, SendNote ; send the note out.
}
*/
; ++++++++++++++++++++++++++++++++ examples of note rules ++++++++++ feel free to add more.

ifequal, byte1, 20 ; if the note number coming in is note # 20
{
byte1 := (byte1 +1) ; transpose that note up 1 note number
gosub, SendNote ; send the note out.
}

ifequal, byte1, 30 ; if the note number coming in is note # 30
{
send , {NumLock} ; send a keypress when note number 20 is received.
}

; a little more complex filter two notes
if ((byte1 != 60) and (byte1 != 62)) ; if note message is not(!) 60 and not(!) 62 send the note out - ie - do nothing except statements above (note 20 and 30 have things to do) to it.
{
gosub, SendNote ; send it out the selected output midi port
;msgbox, ,straight note, note %byte1% message, 1 ; this messagebox for testing only.
}

IfEqual, byte1, 60 ; if the note number is middle C (60) (you can change this)
{
byte1 := (byte1 + 5) ;transpost up 5 steps
gosub, SendNote ;(h_midiout, note) ;send a note transposed up 5 notes.
;msgbox, ,transpose up 5, note on %byte1% message, 1 ; for testing only - show msgbox for 1 sec
}

IfEqual, byte1, 62 ; if note on is note number 62 (just another example of note detection)
{
byte1 := (byte1 -5) ;transpose down 5 steps
gosub, SendNote
;msgbox, ,transpose down 5, note on %byte1% message, 1 ; for testing only, uncomment if you need it.
}
; ++++++++++++++++++++++++++++++++ End of examples of note rules ++++++++++
} ; end of note block

; =============== all cc detection ----
; is input cc?

if statusbyte between 176 and 191 ; check status byte for cc 176-191 is the range for CC messages ; !!!!!!!! no edit this line, uykwyad
{
; ++++++++++++++++++++++++++++++++ examples of CC rules ++++++++++ feel free to add more.
if byte1 in %cc_msg%
{
cc := (byte1 + 3) ; Will change all cc#'s up 3 for a different controller number
GuiControl,12:, MidiMsOut, CC %statusbyte% %chan% %cc% %byte2%
;midiOutShortMsg(h_midiout, statusbyte, cc, byte2)
gosub, sendCC
}
else if byte1 not in %cc_msg% ; if the byte1 value is one of these...
{
cc := byte1 ; pass them as is, no change.
GuiControl,12:, MidiMsOut, CC %statusbyte% %chan% %cc% %byte2%
gosub, sendCC
}
; ++++++++++++++++++++++++++++++++ examples of cc rules ends ++++++++++++
}

; Is midi input a Program Change?
if statusbyte between 192 and 208 ; check if message is in range of program change messages for byte1 values. ; !!!!!!!!!!!! no edit
{
; ++++++++++++++++++++++++++++++++ examples of program change rules ++++++++++
; Sorry I have not created anything for here nor for pitchbends....

GuiControl,12:, MidiMsOut, ProgC:%statusbyte% %chan% %byte1% %byte2%
gosub, sendPC
; need something for it to do here, could be converting to a cc or a note or changing the value of the pc
; however, at this point the only thing that happens is the gui change, not midi is output here.
; you may want to make a SendPc: label below
; ++++++++++++++++++++++++++++++++ examples of program change rules ++++++++++
}
;msgbox filter triggered
; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ end of edit section

Return

; ++++++++++++++++++++++++ hotkeys here +++++++++++++++++++++++++=
/*
Generate midi messages from computer keyboard (mouse) events.
Examples below

Each hotkey is defined to change the value of a varible.
example f12::Vol=U ; When f12 is pushed down, this will set the value of that varible to U.
The keyboardcc loop is running every 70, so it detects the change for vol varible and triggers that process.
See keyboardcc section (where all the work is done).
f12 up::vol= ; when f12 is let go, the vol var is set to blank and keyboardcc does nothing.
Same for f11 except the vol var is either D or blank.
*/

; here are a few examples of adding controller hotkeys.

F12::Vol = U ;
f12 up::Vol =
F11::Vol = D ; Mod wheel down
f11 up::Vol =

/*
; here are two more examples you can add

F10::yourVar = U ; set yourVar to U for changing value up
F10 UP::yourVar = ; set yourVar blank to stop message generation
F9::yourVar = D ; set yourVar to D for changing value down
f9::yourVar = ; set yourVar to blank to stop message generation
*/
; ++++++++++++++++++++++++++++++ end hotkey defs ++++++++++++++++++++

; +++++++++++++++++++++++++++++ begin keyboardccs section +++++++++++

/*
Process Definitions for hotkey generated midi controllers.
Unless you are good at arrays (I am not)
You will need to add three statements like these for each controller you wish to generate from a pair of hotkeys.
*/

KeyboardCCs:

; =============== this is a section for volume control
If Vol = U
{
VolVal := VolVal + VolDelta ;see top where vars defined.
If VolVal > 127 ; check for max value reached
VolVal:= 127, Vol:=""
midiOutShortMsg(h_midiout, (channel+175), CCnum, VolVal) ; (channel+175 will make the correct statusbyte for cc message) CCnum var defined in autoexec section at top, as
}
If Vol = D
{
volVal := volVal- volDelta
If volVal < 0 ; check min value reached.
VolVal:=0, Vol=""
midiOutShortMsg(h_midiout, (channel+175), CCnum, VolVal)
}
if Vol = ; set the var to blank
{
; do nothing.
}
; =============== end of volume control

;++++++++++++++++++++++++++++++ edit below as needed
/*
; ********* remember you can't use yourVar unless you define it somewhere,
; ********* same goes for yourVarCCnum and yourVarDelta.... see beginning of script where they are commented out but set to be defined.

; examples to be triggered by f9 and f10 defined above but commented out.
; **********

If yourVar = U
{
yourVarVal := yourVarVal + yourVarDelta ;see top where vars defined.
If yourVarVal > 127 ; check for max value reached
yourVarVal:= 127, yourVar:=""
midiOutShortMsg(h_midiout, (channel+175), yourVarCCnum, yourVarVal) ; yourVarCCnum is defined in autoexec section and yourVarVal is defined in the lines directly above.
}
If yourVar = D
{
yourVarVal := yourVarVal- yourVarDelta
If yourVarVal < 0 ; check min value reached.
yourVarVal:=0, yourVar=""
midiOutShortMsg(h_midiout, (channel+175), yourVarCCnum, yourVarVal)
}
if yourVar = ; set the var to blank
{
; do nothing.
}

*/
Return

; ========================= end ============================

; =============== Send midi output data =============================

SendNote: ;(h_midiout,Note) ; send out note messages ; this should probably be a funciton but... eh.
;{
GuiControl,12:, MidiMsOutSend, NoteOut:%statusbyte% %chan% %byte1% %byte2%
;global chan, EventType, NoteVel
;MidiStatus := 143 + chan
note = %byte1% ; this var is added to allow transpostion of a note
midiOutShortMsg(h_midiout, statusbyte, note, byte2) ; call the midi funcitons with these params.
Return

SendCC: ; not sure i actually did anything changing cc's here but it is possible.

GuiControl,12:, MidiMsOutSend, CCOut:%statusbyte% %chan% %cc% %byte2%
midiOutShortMsg(h_midiout, statusbyte, cc, byte2)
;MsgBox, 0, ,sendcc triggered , 1
Return

SendPC:
GuiControl,12:, MidiMsOutSend, ProgChOut:%statusbyte% %chan% %byte1% %byte2%
midiOutShortMsg(h_midiout, statusbyte, pc, byte2)
/*
COULD BE TRANSLATED TO SOME OTHER MIDI MESSAGE BESIDED PROGRAM CHANGE.
*/
Return






; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! no edit below here, unless you know what you are doing.

; =============== midi monitor gui ==========================

midiMon: ;just a simple gui window for a midi monitor of sorts.

Gui, 12: +LastFound +AlwaysOnTop +Caption +ToolWindow ; +ToolWindow avoids a taskbar button and an alt-tab menu item.
Gui,12: Color, white ; %CustomColor% ;blue ;
Gui,12: Font, s15 ; Set a large font size (32-point).
Gui,12: Add, Text, w250 vMidiMs cgreen, XXXXX YYYYY ; XX & YY serve to auto-size the window.
gui, 12: add, text, w250 vMidiMsOut cblue, XXXXX YYYYY
gui, 12: add, text, w250 vMidiMsOutSend cred, XXXXX YYYYY
Gui,12: Show, xcenter y0 w500 NoActivate, %version% Midi Monitor - thingy ; NoActivate avoids deactivating the currently active window.

; =============== end monitor gui
;****************************************************************************************************************
;******************************************** midi "under the hood" *********************************************
/*
This part is meant to take care of the "under the hood" midi input and output selection and save selection to an ini file.
Hopefully it simplifies usage for others out here trying to do things with midi and ahk.

* use it as an include.

The code here was taken/modified from the work by TomB/Lazslo on Midi Output
http://www.autohotkey.com/forum/viewtopic.php?t=18711&highlight=midi+output

Orbik's Midi input thread
http://www.autohotkey.com/forum/topic30715.html
This method does NOT use the midi_in.dll, it makes direct calls to the winmm.dll

Many different people took part in the creation of this file.

; Last edited 6/17/2010 11:30 AM by genmce

*/

MidiPortRefresh: ; get the list of ports

MIlist := MidiInsList(NumPorts)
Loop Parse, MIlist, |
{
}
TheChoice := MidiInDevice + 1

MOlist := MidiOutsList(NumPorts2)
Loop Parse, MOlist, |
{
}
TheChoice2 := MidiOutDevice + 1

return

;-----------------------------------------------------------------

ReadIni() ; also set up the tray Menu
{
Menu, tray, add, MidiSet ; set midi ports tray item
Menu, tray, add, ResetAll ; Delete the ini file for testing --------------------------------

global MidiInDevice, MidiOutDevice, version ; version var is set at the beginning.
IfExist, %version%io.ini
{
IniRead, MidiInDevice, %version%io.ini, Settings, MidiInDevice , %MidiInDevice% ; read the midi In port from ini file
IniRead, MidiOutDevice, %version%io.ini, Settings, MidiOutDevice , %MidiOutDevice% ; read the midi out port from ini file
}
Else ; no ini exists and this is either the first run or reset settings.
{
MsgBox, 1, No ini file found, Select midi ports?
IfMsgBox, Cancel
ExitApp
IfMsgBox, yes
gosub, midiset
;WriteIni()
}
}

;CALLED TO UPDATE INI WHENEVER SAVED PARAMETERS CHANGE
WriteIni()
{
global MidiInDevice, MidiOutDevice, version

IfNotExist, %version%io.ini ; if no ini
FileAppend,, %version%io.ini ; make one with the following entries.
IniWrite, %MidiInDevice%, %version%io.ini, Settings, MidiInDevice
IniWrite, %MidiOutDevice%, %version%io.ini, Settings, MidiOutDevice
}

;------------ port testing to make sure selected midi port is valid --------------------------------

port_test(numports,numports2) ; confirm selected ports exist ; CLEAN THIS UP STILL

{
global midiInDevice, midiOutDevice, midiok

; ----- In port selection test based on numports
If MidiInDevice not Between 0 and %numports%
{
MidiIn := 0 ; this var is just to show if there is an error - set if the ports are valid = 1, invalid = 0
;MsgBox, 0, , midi in port Error ; (this is left only for testing)
If (MidiInDevice = "") ; if there is no midi in device
MidiInerr = Midi In Port EMPTY. ; set this var = error message
;MsgBox, 0, , midi in port EMPTY
If (midiInDevice > %numports%) ; if greater than the number of ports on the system.
MidiInnerr = Midi In Port Invalid. ; set this error message
;MsgBox, 0, , midi in port out of range
}
Else
{
MidiIn := 1 ; setting var to non-error state or valid
}
; ----- out port selection test based on numports2
If MidiOutDevice not Between 0 and %numports2%
{
MidiOut := 0 ; set var to 0 as Error state.
If (MidiOutDevice = "") ; if blank
MidiOuterr = Midi Out Port EMPTY. ; set this error message
;MsgBox, 0, , midi o port EMPTY
If (midiOutDevice > %numports2%) ; if greater than number of availble ports
MidiOuterr = Midi Out Port Out Invalid. ; set this error message
;MsgBox, 0, , midi out port out of range
}
Else
{
MidiOut := 1 ;set var to 1 as valid state.
}
; ---- test to see if ports valid, if either invalid load the gui to select.
;midicheck(MCUin,MCUout)
If (%MidiIn% = 0) Or (%MidiOut% = 0)
{
MsgBox, 49, Midi Port Error!,%MidiInerr%`n%MidiOuterr%`n`nLaunch Midi Port Selection!
IfMsgBox, Cancel
ExitApp
midiok = 0 ; Not sure if this is really needed now....
Gosub, MidiSet ;Gui, show Midi Port Selection
}
Else
{
midiok = 1
Return ; DO NOTHING - PERHAPS DO THE NOT TEST INSTEAD ABOVE.
}
}
Return

; ------------------ end of port testing ---------------------------


MidiSet: ; midi port selection gui

; ------------- MIDI INPUT SELECTION -----------------------
;Gui, Destroy
;Gosub, Suspendit
Gui, 6: Destroy
Gui, 2: Destroy
Gui, 3: Destroy
Gui, 4: Destroy
;Gui, 5: Destroy
Gui, 4: +LastFound +AlwaysOnTop +Caption +ToolWindow ;-SysMenu
Gui, 4: Font, s12
Gui, 4: add, text, x10 y10 w300 cmaroon, Select Midi Ports. ; Text title
Gui, 4: Font, s8
Gui, 4: Add, Text, x10 y+10 w175 Center , Midi In Port ;Just text label
Gui, 4: font, s8
; midi ins list box
Gui, 4: Add, ListBox, x10 w200 h100 Choose%TheChoice% vMidiInPort gDoneInChange AltSubmit, %MiList% ; --- midi in listing of ports
;Gui, Add, DropDownList, x10 w200 h120 Choose%TheChoice% vMidiInPort gDoneInChange altsubmit, %MiList% ; ( you may prefer this style, may need tweak)

; --------------- MidiOutSet ---------------------
Gui, 4: Add, TEXT, x220 y40 w175 Center, Midi Out Port ; gDoneOutChange
; midi outlist box
Gui, 4: Add, ListBox, x220 y62 w200 h100 Choose%TheChoice2% vMidiOutPort gDoneOutChange AltSubmit, %MoList% ; --- midi out listing
;Gui, Add, DropDownList, x220 y97 w200 h120 Choose%TheChoice2% vMidiOutPort gDoneOutChange altsubmit , %MoList%
Gui, 4: add, Button, x10 w205 gSet_Done, Done - Reload script.
Gui, 4: add, Button, xp+205 w205 gCancel, Cancel
;gui, 4: add, checkbox, x10 y+10 vNotShown gDontShow, Do Not Show at startup.
;IfEqual, NotShown, 1
;guicontrol, 4:, NotShown, 1
Gui, 4: show , , %version% Midi Port Selection ; main window title and command to show it.

Return

;-----------------gui done change stuff - see label in both gui listbox line

DoneInChange:
Gui, 4: Submit, NoHide
Gui, 4: Flash
If %MidiInPort%
UDPort:= MidiInPort - 1, MidiInDevice:= UDPort ; probably a much better way do this, I took this from JimF's qwmidi without out editing much.... it does work same with doneoutchange below.
GuiControl, 4:, UDPort, %MidiIndevice%
WriteIni()
;MsgBox, 32, , midi in device = %MidiInDevice%`nmidiinport = %MidiInPort%`nport = %port%`ndevice= %device% `n UDPort = 
port% ; only for testing
Return

DoneOutChange:
Gui, 4: Submit, NoHide
Gui, 4: Flash
If %MidiOutPort%
UDPort2:= MidiOutPort - 1 , MidiOutDevice:= UDPort2
GuiControl, 4: , UDPort2, %MidiOutdevice%
WriteIni()
;Gui, Destroy
Return

;------------------------ end of the doneout change stuff.

Set_Done: ; aka reload program, called from midi selection gui
Gui, 3: Destroy
Gui, 4: Destroy
sleep, 100
Reload
Return

Cancel:
Gui, Destroy
Gui, 2: Destroy
Gui, 3: Destroy
Gui, 4: Destroy
Gui, 5: Destroy
Return

; ********************** Midi output detection

MidiOut: ; Function to load new settings from midi out menu item
OpenCloseMidiAPI()
h_midiout := midiOutOpen(MidiOutDevice) ; OUTPUT PORT 1 SEE BELOW FOR PORT 2
return

ResetAll: ; for development only, leaving this in for a program reset if needed by user
MsgBox, 33, %version% - Reset All?, This will delete ALL settings`, and restart this program!
IfMsgBox, OK
{
FileDelete, %version%io.ini ; delete the ini file to reset ports, probably a better way to do this ...
Reload ; restart the app.
}
IfMsgBox, Cancel
Return

GuiClose: ; on x exit app
Suspend, Permit ; allow Exit to work Paused. I just added this yesterday 3.16.09 Can now quit when Paused.

MsgBox, 4, Exit %version%, Exit %version% %ver%? ;
IfMsgBox No
Return
Else IfMsgBox Yes
midiOutClose(h_midiout)

Gui, 6: Destroy
Gui, 2: Destroy
Gui, 3: Destroy
Gui, 4: Destroy
Gui, 5: Destroy
gui, 7: destroy
;gui,
Sleep 100
;winclose, Midi_in_2 ;close the midi in 2 ahk file
ExitApp


;############################################## MIDI LIB from orbik and lazslo#############
;-------- orbiks midi input code --------------
; Set up midi input and callback_window based on the ini file above.
; This code copied from ahk forum Orbik's post on midi input

; nothing below here to edit.

; =============== midi in =====================

Midiin_go:
DeviceID := MidiInDevice ; midiindevice from IniRead above assigned to deviceid
CALLBACK_WINDOW := 0x10000 ; from orbiks code for midi input

Gui, +LastFound ; set up the window for midi data to arrive.
hWnd := WinExist() ;MsgBox, 32, , line 176 - mcu-input is := %MidiInDevice% , 3 ; this is just a test to show midi device selection

hMidiIn =
VarSetCapacity(hMidiIn, 4, 0)
result := DllCall("winmm.dll\midiInOpen", UInt,&hMidiIn, UInt,DeviceID, UInt,hWnd, UInt,0, UInt,CALLBACK_WINDOW, "UInt")
If result
{
MsgBox, Error, midiInOpen Returned %result%`n
;GoSub, sub_exit
}

hMidiIn := NumGet(hMidiIn) ; because midiInOpen writes the value in 32 bit binary Number, AHK stores it as a string
result := DllCall("winmm.dll\midiInStart", UInt,hMidiIn)
If result
{
MsgBox, Error, midiInStart Returned %result%`nRight Click on the Tray Icon - Left click on MidiSet to select valid midi_in port.
;GoSub, sub_exit
}

OpenCloseMidiAPI()

; ----- the OnMessage listeners ----

; #define MM_MIM_OPEN 0x3C1 /* MIDI input */
; #define MM_MIM_CLOSE 0x3C2
; #define MM_MIM_DATA 0x3C3
; #define MM_MIM_LONGDATA 0x3C4
; #define MM_MIM_ERROR 0x3C5
; #define MM_MIM_LONGERROR 0x3C6

OnMessage(0x3C1, "MidiMsgDetect") ; calling the function MidiMsgDetect in get_midi_in.ahk
OnMessage(0x3C2, "MidiMsgDetect")
OnMessage(0x3C3, "MidiMsgDetect")
OnMessage(0x3C4, "MidiMsgDetect")
OnMessage(0x3C5, "MidiMsgDetect")
OnMessage(0x3C6, "MidiMsgDetect")

Return

;--- MIDI INS LIST FUNCTIONS - port handling -----

MidiInsList(ByRef NumPorts)
{ ; Returns a "|"-separated list of midi output devices
local List, MidiInCaps, PortName, result
VarSetCapacity(MidiInCaps, 50, 0)
VarSetCapacity(PortName, 32) ; PortNameSize 32

NumPorts := DllCall("winmm.dll\midiInGetNumDevs") ; #midi output devices on system, First device ID = 0

Loop %NumPorts%
{
result := DllCall("winmm.dll\midiInGetDevCapsA", UInt,A_Index-1, UInt,&MidiInCaps, UInt,50, UInt)
If (result OR ErrorLevel) {
List .= "|-Error-"
Continue
}
DllCall("RtlMoveMemory", Str,PortName, UInt,&MidiInCaps+8, UInt,32) ; PortNameOffset 8, PortNameSize 32
List .= "|" PortName
}
Return SubStr(List,2)
}

MidiInGetNumDevs() { ; Get number of midi output devices on system, first device has an ID of 0
Return DllCall("winmm.dll\midiInGetNumDevs")
}
MidiInNameGet(uDeviceID = 0) { ; Get name of a midiOut device for a given ID

;MIDIOUTCAPS struct
; WORD wMid;
; WORD wPid;
; MMVERSION vDriverVersion;
; CHAR szPname[MAXPNAMELEN];
; WORD wTechnology;
; WORD wVoices;
; WORD wNotes;
; WORD wChannelMask;
; DWORD dwSupport;

VarSetCapacity(MidiInCaps, 50, 0) ; allows for szPname to be 32 bytes
OffsettoPortName := 8, PortNameSize := 32
result := DllCall("winmm.dll\midiInGetDevCapsA", UInt,uDeviceID, UInt,&MidiInCaps, UInt,50, UInt)

If (result OR ErrorLevel) {
MsgBox Error %result% (ErrorLevel = %ErrorLevel%) in retrieving the name of midi Input ÞviceID%
Return -1
}

VarSetCapacity(PortName, PortNameSize)
DllCall("RtlMoveMemory", Str,PortName, Uint,&MidiInCaps+OffsettoPortName, Uint,PortNameSize)
Return PortName
}

MidiInsEnumerate() { ; Returns number of midi output devices, creates global array MidiOutPortName with their names
local NumPorts, PortID
MidiInPortName =
NumPorts := MidiInGetNumDevs()

Loop %NumPorts% {
PortID := A_Index -1
MidiInPortName%PortID% := MidiInNameGet(PortID)
}
Return NumPorts
}


; =============== end of midi selection stuff


MidiOutsList(ByRef NumPorts)
{ ; Returns a "|"-separated list of midi output devices
local List, MidiOutCaps, PortName, result
VarSetCapacity(MidiOutCaps, 50, 0)
VarSetCapacity(PortName, 32) ; PortNameSize 32

NumPorts := DllCall("winmm.dll\midiOutGetNumDevs") ; #midi output devices on system, First device ID = 0

Loop %NumPorts%
{
result := DllCall("winmm.dll\midiOutGetDevCapsA", UInt,A_Index-1, UInt,&MidiOutCaps, UInt,50, UInt)
If (result OR ErrorLevel)
{
List .= "|-Error-"
Continue
}
DllCall("RtlMoveMemory", Str,PortName, UInt,&MidiOutCaps+8, UInt,32) ; PortNameOffset 8, PortNameSize 32
List .= "|" PortName
}
Return SubStr(List,2)
}
;---------------------midiOut from TomB and Lazslo and JimF --------------------------------

;THATS THE END OF MY STUFF (JimF) THE REST ID WHAT LASZLo AND PAXOPHONE WERE USING ALREADY
;AHK FUNCTIONS FOR MIDI OUTPUT - calling winmm.dll
;http://msdn.microsoft.com/library/default.asp?url=/library/en-us/multimed/htm/_win32_multimedia_functions.asp
;Derived from Midi.ahk dated 29 August 2008 - streaming support removed - (JimF)


OpenCloseMidiAPI() { ; at the beginning to load, at the end to unload winmm.dll
static hModule
If hModule
DllCall("FreeLibrary", UInt,hModule), hModule := ""
If (0 = hModule := DllCall("LoadLibrary",Str,"winmm.dll")) {
MsgBox Cannot load libray winmm.dll
Exit
}
}

;FUNCTIONS FOR SENDING SHORT MESSAGES

midiOutOpen(uDeviceID = 0) { ; Open midi port for sending individual midi messages --> handle
strh_midiout = 0000

result := DllCall("winmm.dll\midiOutOpen", UInt,&strh_midiout, UInt,uDeviceID, UInt,0, UInt,0, UInt,0, UInt)
If (result or ErrorLevel) {
MsgBox There was an Error opening the midi port.`nError code %result%`nErrorLevel = %ErrorLevel%
Return -1
}
Return UInt@(&strh_midiout)
}

midiOutShortMsg(h_midiout, MidiStatus, Param1, Param2) { ;Channel,
;h_midiout: handle to midi output device returned by midiOutOpen
;EventType, Channel combined -> MidiStatus byte: http://www.harmony-central.com/MIDI/Doc/table1.html
;Param3 should be 0 for PChange, ChanAT, or Wheel
;Wheel events: entire Wheel value in Param2 - the function splits it into two bytes
/*
If (EventType = "NoteOn" OR EventType = "N1")
MidiStatus := 143 + Channel
Else If (EventType = "NoteOff" OR EventType = "N0")
MidiStatus := 127 + Channel
Else If (EventType = "CC")
MidiStatus := 175 + Channel
Else If (EventType = "PolyAT" OR EventType = "PA")
MidiStatus := 159 + Channel
Else If (EventType = "ChanAT" OR EventType = "AT")
MidiStatus := 207 + Channel
Else If (EventType = "PChange" OR EventType = "PC")
MidiStatus := 191 + Channel
Else If (EventType = "Wheel" OR EventType = "W") {
MidiStatus := 223 + Channel
Param2 := Param1 >> 8 ; MSB of wheel value
Param1 := Param1 & 0x00FF ; strip MSB
}
*/
result := DllCall("winmm.dll\midiOutShortMsg", UInt,h_midiout, UInt, MidiStatus|(Param1<<8)|(Param2<<16), UInt)
If (result or ErrorLevel) {
MsgBox There was an Error Sending the midi event: (%result%`, %ErrorLevel%)
Return -1
}
}

midiOutClose(h_midiout) { ; Close MidiOutput
Loop 9 {
result := DllCall("winmm.dll\midiOutClose", UInt,h_midiout)
If !(result or ErrorLevel)
Return
Sleep 250
}
MsgBox Error in closing the midi output port. There may still be midi events being Processed.
Return -1
}

;UTILITY FUNCTIONS
MidiOutGetNumDevs() { ; Get number of midi output devices on system, first device has an ID of 0
Return DllCall("winmm.dll\midiOutGetNumDevs")
}

MidiOutNameGet(uDeviceID = 0) { ; Get name of a midiOut device for a given ID

;MIDIOUTCAPS struct
; WORD wMid;
; WORD wPid;
; MMVERSION vDriverVersion;
; CHAR szPname[MAXPNAMELEN];
; WORD wTechnology;
; WORD wVoices;
; WORD wNotes;
; WORD wChannelMask;
; DWORD dwSupport;

VarSetCapacity(MidiOutCaps, 50, 0) ; allows for szPname to be 32 bytes
OffsettoPortName := 8, PortNameSize := 32
result := DllCall("winmm.dll\midiOutGetDevCapsA", UInt,uDeviceID, UInt,&MidiOutCaps, UInt,50, UInt)

If (result OR ErrorLevel) {
MsgBox Error %result% (ErrorLevel = %ErrorLevel%) in retrieving the name of midi output ÞviceID%
Return -1
}

VarSetCapacity(PortName, PortNameSize)
DllCall("RtlMoveMemory", Str,PortName, Uint,&MidiOutCaps+OffsettoPortName, Uint,PortNameSize)
Return PortName
}

MidiOutsEnumerate() { ; Returns number of midi output devices, creates global array MidiOutPortName with their names
local NumPorts, PortID
MidiOutPortName =
NumPorts := MidiOutGetNumDevs()

Loop %NumPorts% {
PortID := A_Index -1
MidiOutPortName%PortID% := MidiOutNameGet(PortID)
}
Return NumPorts
}

UInt@(ptr) {
Return *ptr | *(ptr+1) << 8 | *(ptr+2) << 16 | *(ptr+3) << 24
}

PokeInt(p_value, p_address) { ; Windows 2000 and later
DllCall("ntdll\RtlFillMemoryUlong", UInt,p_address, UInt,4, UInt,p_value)
}

KeyMce/GenMce - mackie emulator for pc keyboard/Convert your controller to mackie.
Midi I/O - Want to play with midi/ahk? links dead.. pm me


Ivan68
  • Members
  • 13 posts
  • Last active: Dec 22 2010 05:59 PM
  • Joined: 15 Jun 2010
Great!
:D :D
Many thanks for your effort!

genmce
  • Members
  • 144 posts
  • Last active: May 21 2015 03:09 PM
  • Joined: 10 Jan 2009
Glad someone noticed!

KeyMce/GenMce - mackie emulator for pc keyboard/Convert your controller to mackie.
Midi I/O - Want to play with midi/ahk? links dead.. pm me


flatron85
  • Members
  • 6 posts
  • Last active: Nov 17 2010 06:57 PM
  • Joined: 13 Oct 2009
thanks because this script actually works for me. the other ones didn't for whatever silly reason!
urm now i'm going to figure out how to get the various midi buttons to do things in windows...

EDIT:
nup again this is way out of my league to be playing with, sure this midi monitor works and the config wizard is a blast but how can I let it listen for certain buttons to be pressed?

will check back occasionally :)

genmce
  • Members
  • 144 posts
  • Last active: May 21 2015 03:09 PM
  • Joined: 10 Jan 2009
Sorry I have not been around, summer vacation - eh.

You will need to know the information about your button.
Press it with the program running and you should see the data from the button show up on the crappy midi monitor thingy.

Then you will edit .ahk file to add this item...
hmmm... not sure this will make sense.

I probably need to put a gui in here.
I also need to figure out how to do an array so one could keep adding filters ....
I wonder if I can get some help on doing an array like this? TLM was helping me before on arrays... I'll ask him.

I digress.

line 102
I am assuming your button is a CC? Control Change.

This code just detects if any cc is input, if it is then the cc# (data1) is changed up 3, then output to the selected midi port.

; Is midi input a CC?  
  if statusbyte between 176 and 192 ; check status byte for cc 
    {
        cc := (byte1 + 3) ; Set var cc and change the cc number up 3 for a different controller number
        GuiControl,12:, MidiMsOut, %statusbyte% %chan% %cc% %byte2% 
        gosub, sendCC
    }

Replace the above code with the below code.
It will detect if you have pressed a controller on midi chan1. (statusbyte = 176)
If that controller is CC#80 (data1) it will press the numlock key, if it is cc#90 it will press the capslock key.
Replace the 80 or 90 with the number of your (button) cc# and try it.

ifequal, statusbyte, 176 ; is data input a cc message, midi ch 1?
   {
       ifegual, byte1, 80 ; byte1 for cc is the cc# (is this cc, cc#80?)
          { 
               send {NumLock} ; if it was cc80 then send a numlock keypress 
           }
         
       ifequal, byte1, 90 ; is this cc, cc#90 ?
           { 
               send {CapsLock} ; if it was cc90 then send a capslock 
           }
              
     }


See how you get on with that. PM me if you like and I will help you more.

Remember a CC# = data1 or what controller number it is.

CC value = data2 or what value that controller is on. example on = 127 and off = 0 (for a button)
The above code does not care what cc value it is, only if it is the correct cc#

KeyMce/GenMce - mackie emulator for pc keyboard/Convert your controller to mackie.
Midi I/O - Want to play with midi/ahk? links dead.. pm me


mQueue1
  • Guests
  • Last active:
  • Joined: --
genmce,

thank you for putting together this awesome script. i've been working to create a midi control map for my pc for about a month now and I was wondering....would it be possible to use a midi event queue with this script?

genmce(guest)
  • Guests
  • Last active:
  • Joined: --

genmce,

thank you for putting together this awesome script. i've been working to create a midi control map for my pc for about a month now and I was wondering....would it be possible to use a midi event queue with this script?


Good question, I don't know.
Related, I want to figure out how to do sysex with ahk.
At this time, I don't know.

There are scripts on the midi out thread that talk about some queue talk there...

mQueue1
  • Guests
  • Last active:
  • Joined: --
I'm going to try using the Critical function to see how that works with queueing....

What roadblocks have you found with sysex?

genmce(guest)
  • Guests
  • Last active:
  • Joined: --

genmce,

thank you for putting together this awesome script. i've been working to create a midi control map for my pc for about a month now and I was wondering....would it be possible to use a midi event queue with this script?


Good question, I don't know.
Related, I want to figure out how to do sysex with ahk.
At this time, I don't know.

There are scripts on the midi out thread that talk about some queue talk there...


Let me rephrase that... I thought about the question a few seconds longer.
Depends on what you are after - can you be more specific with your question?

Do you want to use a single midi keyboard event to send multiple pc keyboard presses? Yes, that is possible...
More details please.

genmce
  • Members
  • 144 posts
  • Last active: May 21 2015 03:09 PM
  • Joined: 10 Jan 2009
New - version 0.3 posted for you pleasure. See top post in thread.

KeyMce/GenMce - mackie emulator for pc keyboard/Convert your controller to mackie.
Midi I/O - Want to play with midi/ahk? links dead.. pm me


tdg
  • Members
  • 7 posts
  • Last active: Feb 10 2012 04:56 PM
  • Joined: 04 Sep 2010
Sorry, have been trying for the last 2 hours to get my head aound this...

All I want to do is send a volume change with a key press....

If I connect up my BCF2000 controller, the message window shows that 176 1 7 0-127 is working, it also pumps this out to my specified midi receiver.... so I tried doing this:

#!6:: GuiControl,12:, MidiMsOutSend, CCOut:176 1 7 0
midiOutShortMsg(h_midiout, statusbyte, cc, byte2)
Return

The message window shows CCOut:176 1 7 0 but my receiver never gets the message. :(

What have I missed?

Thanks for your help, props to all who made this ahk happen, it's amazing...

genmce
  • Members
  • 144 posts
  • Last active: May 21 2015 03:09 PM
  • Joined: 10 Jan 2009

Sorry, have been trying for the last 2 hours to get my head aound this...

All I want to do is send a volume change with a key press....

If I connect up my BCF2000 controller, the message window shows that 176 1 7 0-127 is working, it also pumps this out to my specified midi receiver.... so I tried doing this:

#!6:: GuiControl,12:, MidiMsOutSend, CCOut:176 1 7 0
midiOutShortMsg(h_midiout, statusbyte, cc, byte2)
Return

The message window shows CCOut:176 1 7 0 but my receiver never gets the message. :(

What have I missed?

Thanks for your help, props to all who made this ahk happen, it's amazing...


Yeah - sorry, I did not even give any examples of midi messages from a hotkey! All my examples are of converting midi input.
I will get back later (possibly today) and post a new version with some examples of that.
I have no time right now.

KeyMce/GenMce - mackie emulator for pc keyboard/Convert your controller to mackie.
Midi I/O - Want to play with midi/ahk? links dead.. pm me


genmce(guest)
  • Guests
  • Last active:
  • Joined: --

I'm going to try using the Critical function to see how that works with queueing....

What roadblocks have you found with sysex?



That I don't know how to pack the message.

genmce
  • Members
  • 144 posts
  • Last active: May 21 2015 03:09 PM
  • Joined: 10 Jan 2009
See new version 0.4 at the top.

Added hotkey generated midi message examples.

Makes it kind of complex to read now tho...
I May need to revisit and break things out into include files, to improve readability ... etc...

KeyMce/GenMce - mackie emulator for pc keyboard/Convert your controller to mackie.
Midi I/O - Want to play with midi/ahk? links dead.. pm me


tdg
  • Members
  • 7 posts
  • Last active: Feb 10 2012 04:56 PM
  • Joined: 04 Sep 2010
Sorry for the delay in thanking you for doing that.....

You are right, it is a bit confusing.... a lot of stuff in there.

Still, I am happy because I have managed to do what I wanted so thank you very much. :)