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) }