commit 7d8d8449dc7ac07cb1435c4fd1d0bfa411ad1113 Author: Laurence Dougal Myers Date: Sat Jan 18 20:51:46 2014 +1100 First commit for midi_to_joy. Setup for mapping any CC controller (up to values of 112) to an X axis on a VJoy joystick. diff --git a/.hgignore b/.hgignore new file mode 100644 index 0000000..e69de29 diff --git a/MidiRules.ahk b/MidiRules.ahk new file mode 100644 index 0000000..9aa6d79 --- /dev/null +++ b/MidiRules.ahk @@ -0,0 +1,126 @@ + +;************************************************* +;* RULES - MIDI FILTERS +;************************************************* + +/* + 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 + + ; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 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. + + ; ++++++++++++++++++++++++++++++++ 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 + ;gosub, sendcc + + { + ; ++++++++++++++++++++++++++++++++ examples of CC rules ++++++++++ feel free to add more. + if byte1 not in %cc_msg% ; if the byte1 value is one of these... + { + tmp_axis_val := Floor((byte2 / 112) * AxisMax_X) + VJoy_SetAxis(tmp_axis_val, iInterface, HID_USAGE_X) + + cc := byte1 ; pass them as is, no change. + gosub, ShowMidiInMessage + GuiControl,12:, MidiMsOut, CC %statusbyte% %chan% %cc% %byte2% + gosub, ShowMidiOutMessage + ;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, ShowMidiInMessage + 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 + +;************************************************* +;* MIDI OUTPUT LABELS TO CALL +;************************************************* + +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. + gosub, ShowMidiOutMessage +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: + gosub, ShowMidiOutMessage + ;GuiControl,12:, MidiMsOutSend, ProgChOut:%statusbyte% %chan% %byte1% %byte2% + midiOutShortMsg(h_midiout, statusbyte, pc, byte2) + /* + COULD BE TRANSLATED TO SOME OTHER MIDI MESSAGE IF NEEDED. + */ +Return + + + diff --git a/Midi_In_and_GuiMonitor.ahk b/Midi_In_and_GuiMonitor.ahk new file mode 100644 index 0000000..5f92866 --- /dev/null +++ b/Midi_In_and_GuiMonitor.ahk @@ -0,0 +1,95 @@ +/* + PARSE - LAST MIDI MESSAGE RECEIVED - + Midi monitor. +*/ + +;************************************************* +;* MIDI INPUT PARSE FUNCTION +;* +;************************************************* + +; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! no edit below here .... + +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, stb + + 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 + pitchb := (byte2 << 7) | byte1 ;(midiMsg >> 8) & 0x7F7F masking to extract the pbs + + + if statusbyte between 176 and 191 ; test for cc + stb := "CC" ; if so then set stp to cc + if statusbyte between 144 and 159 + stb := "NoteOn" + if statusbyte between 128 and 143 + stb := "NoteOff" + if statusbyte between 224 and 239 + stb := "PitchB" + gosub, ShowMidiInMessage ; show updated midi input message on midi monitor gui. + gosub, MidiRules ; run the label in file MidiRules.ahk Edit that file. + + } +; end of MidiMsgDetect funciton + +Return + +;************************************************* +;* SHOW MIDI INPUT ON GUI MONITOR +;************************************************* + +ShowMidiInMessage: ; update the midimonitor gui + +Gui,14:default +Gui,14:ListView, In1 ; see the first listview midi in monitor + LV_Add("",stb,statusbyte,chan,byte1,byte2) + LV_ModifyCol(1,"center") + LV_ModifyCol(2,"center") + LV_ModifyCol(3,"center") + LV_ModifyCol(4,"center") + LV_ModifyCol(5,"center") + If (LV_GetCount() > 10) + { + LV_Delete(1) + } +return + +;************************************************* +;* SHOW MIDI OUTPUT ON GUI MONITOR +;************************************************* + +ShowMidiOutMessage: ; update the midimonitor gui + +Gui,14:default +Gui,14:ListView, Out1 ; see the second listview midi out monitor + LV_Add("",stb,statusbyte,chan,byte1,byte2) + LV_ModifyCol(1,"center") + LV_ModifyCol(2,"center") + LV_ModifyCol(3,"center") + LV_ModifyCol(4,"center") + LV_ModifyCol(5,"center") + If (LV_GetCount() > 10) + { + LV_Delete(1) + } +return + +;************************************************* +;* MIDI MONITOR GUI CODE +;************************************************* + +midiMon: ; midi monitor gui with listviews +gui,14:destroy +gui,14:default +gui,14:add,text, x80 y5, Midi Input ; %TheChoice% + Gui,14:Add, DropDownList, x40 y20 w140 Choose%TheChoice% vMidiInPort gDoneInChange altsubmit, %MiList% ; ( +gui,14:add,text, x305 y5, Midi Ouput ; %TheChoice2% + Gui,14:Add, DropDownList, x270 y20 w140 Choose%TheChoice2% vMidiOutPort gDoneOutChange altsubmit , %MoList% +Gui,14:Add, ListView, x5 r11 w220 Backgroundblack caqua Count10 vIn1, EventType|StatB|Ch|Byte1|Byte2| +gui,14:Add, ListView, x+5 r11 w220 Backgroundblack cyellow Count10 vOut1, EventType|StatB|Ch|Byte1|Byte2| +gui,14:Show, autosize xcenter y5, MidiMonitor + +Return \ No newline at end of file diff --git a/Midi_under_the_hood.ahk b/Midi_under_the_hood.ahk new file mode 100644 index 0000000..23a7de4 --- /dev/null +++ b/Midi_under_the_hood.ahk @@ -0,0 +1,557 @@ + +; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! no edit below here, unless you know what you are doing. + +;************************************************* +;* MIDI UNDER THE HOOD +;* DO NOT EDIT IN HERE, unless you know what +;* what you are doing! +;************************************************* + +;**************************************************************************************************************** +;******************************************** 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 + +*/ + +;************************************************* +;* GET PORTS LIST PARSE +;************************************************* +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 + +;************************************************* +;* LOAD UP SOME STUFF. +;************************************************* +;----------------------------------------------------------------- + +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 -------------------------------- + menu, tray, add, MidiMon + global MidiInDevice, MidiOutDevice, version ; version var is set at the beginning. + IfExist, %version%.ini + { + IniRead, MidiInDevice, %version%.ini, Settings, MidiInDevice , %MidiInDevice% ; read the midi In port from ini file + IniRead, MidiOutDevice, %version%.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() + } + } +;************************************************* +;* WRITE TO INI FILE FUNCTION +;************************************************* + +;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%.ini, Settings, MidiInDevice + IniWrite, %MidiOutDevice%, %version%.ini, Settings, MidiOutDevice + } + +;************************************************* +;* PORT TESTING +;************************************************* +;------------ 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 + +;************************************************* +;* MIDI SET GUI +;************************************************* +; ------------------ end of port testing --------------------------- + + +MidiSet: ; midi port selection gui + + ; ------------- MIDI INPUT SELECTION ----------------------- + Gui, 6: Destroy + Gui, 2: Destroy + Gui, 3: Destroy + Gui, 4: 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 + + + + +;44444444444444444444444444 NEED TO EDIT THIS TO REFLECT CHANGES IN GENMCE PRIOR TO SEND OUT + +DoneInChange: + gui +lastfound + Gui, Submit, NoHide + Gui, Flash + 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 = %UDport% ; only for testing +Return + +DoneOutChange: + gui +lastfound + Gui, Submit, NoHide + Gui, Flash + + 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 - UNDER THE HOOD +;************************************************* +; ********************** 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%.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 INPUT / OUTPUT UNDER THE HOOD +;************************************************* + +;############################################## 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 IN PORT HANDLING +;************************************************* + +;--- 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 %uDeviceID% + 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 + } + +;************************************************* +;* MIDI OUT LIBRARY FROM lASZLO/TOMB +; Modified by JimF - removed long message +; handling as well as combining status byte with ch +; see commented out section below if you want to change it back +;************************************************* +; =============== 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 %uDeviceID% + 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) +} diff --git a/VJoy_Test.ahk b/VJoy_Test.ahk new file mode 100644 index 0000000..ae40d70 --- /dev/null +++ b/VJoy_Test.ahk @@ -0,0 +1,460 @@ +; VJoy_Test.ahk + +#include %A_ScriptDir%\VJoy_lib.ahk + + VJoy_Init() + nButtons := VJoy_GetVJDButtonNumber(iInterface) + + cbtn := 1 + + StatStr := (status = VJD_STAT_OWN) ? "OWN" : "FREE" ; only FREE state required. + + AxisMax_X := VJoy_GetVJDAxisMax(iInterface, HID_USAGE_X) + AxisMax_Y := VJoy_GetVJDAxisMax(iInterface, HID_USAGE_Y) + AxisMax_Z := VJoy_GetVJDAxisMax(iInterface, HID_USAGE_Z) + AxisMax_RX := VJoy_GetVJDAxisMax(iInterface, HID_USAGE_RX) + AxisMax_RY := VJoy_GetVJDAxisMax(iInterface, HID_USAGE_RY) + AxisMax_RZ := VJoy_GetVJDAxisMax(iInterface, HID_USAGE_RZ) + AxisMax_SL0 := VJoy_GetVJDAxisMax(iInterface, HID_USAGE_SL0) + AxisMax_SL1 := VJoy_GetVJDAxisMax(iInterface, HID_USAGE_SL1) + AxisMax_WHL := VJoy_GetVJDAxisMax(iInterface, HID_USAGE_WHL) + + ; build gui + Gui, Add, Text, w80 , Status: %StatStr% + Gui, add, Button, x100 y5 Default gBtnReset, Re&set + Gui, add, Button, x150 y5 gBtnReload, &Reload + Gui, add, Button, x200 y5 gBtnjoycpl, Open &cpl + Gui, Add, Text, x10, %nButtons% Buttons supported + Gui, Add, Button, x140 y30 gBtnTestAllON, Test all On + Gui, Add, Button, x210 y30 gBtnTestAllOFF, Off + + Loop, %nButtons% + { + bX := ((Mod((A_Index-1), 8)) * 30 ) + 10 + bY := FLOOR((A_Index-1) / 8) * 24 + 70 + Gui, add, Button, h5 x%bx% y%by% gBtn%A_Index%, %A_Index% + } + + nexty := 180 + if (VJoy_GetVJDAxisExist(iInterface, HID_USAGE_X)) { + Gui, Add, Text, x10 w130 y%nexty%, Axis X: 0 / %AxisMax_X% + Gui, Add, Slider, x140 y%nexty% vAxisX gSliderXChanged + nexty += 40 + } else { + Gui, Add, Text, x0 y0 + } + + if (VJoy_GetVJDAxisExist(iInterface, HID_USAGE_Y)) { + Gui, Add, Text, x10 w130 y%nexty%, Axis Y: 0 / %AxisMax_Y% + Gui, Add, Slider, x140 y%nexty% vAxisY gSliderYChanged + nexty += 40 + } else { + Gui, Add, Text, x0 y0 + } + + if (VJoy_GetVJDAxisExist(iInterface, HID_USAGE_Z)) { + Gui, Add, Text, x10 w130 y%nexty%, Axis Z: 0 / %AxisMax_Z% + Gui, Add, Slider, x140 y%nexty% vAxisZ gSliderZChanged + nexty += 40 + } else { + Gui, Add, Text, x0 y0 + } + + if (VJoy_GetVJDAxisExist(iInterface, HID_USAGE_RX)) { + Gui, Add, Text, x10 w130 y%nexty%, Axis RX: 0 / %AxisMax_RX% + Gui, Add, Slider, x140 y%nexty% vAxisRX gSliderRXChanged + nexty += 40 + } else { + Gui, Add, Text, x0 y0 + } + + if (VJoy_GetVJDAxisExist(iInterface, HID_USAGE_RY)) { + Gui, Add, Text, x10 w130 y%nexty%, Axis RY: 0 / %AxisMax_RY% + Gui, Add, Slider, x140 y%nexty% vAxisRY gSliderRYChanged + nexty += 40 + } else { + Gui, Add, Text, x0 y0 + } + + if (VJoy_GetVJDAxisExist(iInterface, HID_USAGE_RZ)) { + Gui, Add, Text, x10 w130 y%nexty%, Axis RZ: 0 / %AxisMax_RZ% + Gui, Add, Slider, x140 y%nexty% vAxisRZ gSliderRZChanged + nexty += 40 + } else { + Gui, Add, Text, x0 y0 + } + + if (VJoy_GetVJDAxisExist(iInterface, HID_USAGE_SL0)) { + Gui, Add, Text, x10 w130 y%nexty%, Slider0: 0 / %AxisMax_SL0% + Gui, Add, Slider, x140 y%nexty% vAxisSL0 gSliderSL0Changed + nexty += 40 + } else { + Gui, Add, Text, x0 y0 + } + + if (VJoy_GetVJDAxisExist(iInterface, HID_USAGE_SL1)) { + Gui, Add, Text, x10 w130 y%nexty%, Slider1: 0 / %AxisMax_SL1% + Gui, Add, Slider, x140 y%nexty% vAxisSL1 gSliderSL1Changed + nexty += 40 + } else { + Gui, Add, Text, x0 y0 + } + + if (VJoy_GetVJDAxisExist(iInterface, HID_USAGE_WHL)) { + Gui, Add, Text, x10 w130 y%nexty%, Wheel: 0 / %AxisMax_WHL% + Gui, Add, Slider, x140 y%nexty% vAxisWHL gSliderWHLChanged + nexty += 40 + } else { + Gui, Add, Text, x0 y0 + } + + if (ContPovNumber) { + Gui, Add, Text, x10 y%nexty%,Number of Continuous POVs: %ContPovNumber% + nexty += 20 + Gui, Add, Text, x10 y%nexty%, Continuous Pov test + Gui, Add, Slider, x140 y%nexty% vPovValSlider gSliderContPov + nexty+=40 + Gui, Add, Edit, x10 w80 y%nexty% vPovValDirect gEditContPov, -1 + Loop, %ContPovNumber% + { + _contpov_listing := % _contpov_listing . A_Index + if (A_Index < ContPovNumber) { + _contpov_listing := % _contpov_listing . "|" + } + } + Gui, Add, ListBox, x140 w40 y%nexty% vContPovChoice gContPovChoose, %_contpov_listing% + nexty += 30 + } + + if (DiscPovNumber) { + Gui, Add, Text, x10 y%nexty%,Number of Descrete POVs: %DiscPovNumber% + nexty += 20 + + tmpy := nexty + Gui, Add, Button, x160 y%tmpy% gBtnPovN, N + tmpy := nexty + 30 + Gui, Add, Button, x155 y%tmpy% gBtnPovNeu, Neu + tmpy := nexty + 60 + Gui, Add, Button, x160 y%tmpy% gBtnPovS, S + tmpy := nexty + 30 + Gui, Add, Button, x120 y%tmpy% gBtnPovW, W + Gui, Add, Button, x200 y%tmpy% gBtnPovE, E + + Loop, %DiscPovNumber% + { + _contpov_listing := % _contpov_listing . A_Index + if (A_Index < DiscPovNumber) { + _contpov_listing := % _contpov_listing . "|" + } + } + Gui, Add, ListBox, x10 w40 y%nexty% vDiscPovChoice gDiscPovChoose, %_contpov_listing% + + nexty += 100 + } + GetKeyState, _JoyStat, JoyInfo + Gui, Add, Text, x10 y%nexty%, JoyInfo: %_JoyStat% + Gui, Show + +return + +SliderXChanged: + Gui, Submit, NoHide + tmp_axis_val := Floor(AxisMax_X * AxisX / 100) + VJoy_SetAxis(tmp_axis_val, iInterface, HID_USAGE_X) + ControlSetText, Static3, Axis X %tmp_axis_val% / %AxisMax_X% +return + +SliderYChanged: + Gui, Submit, NoHide + tmp_axis_val := Floor(AxisMax_Y * AxisY / 100) + VJoy_SetAxis(tmp_axis_val, iInterface, HID_USAGE_Y) + ControlSetText, Static4, Axis Y %tmp_axis_val% / %AxisMax_Y% +return + +SliderZChanged: + Gui, Submit, NoHide + tmp_axis_val := Floor(AxisMax_Z * AxisZ / 100) + VJoy_SetAxis(tmp_axis_val, iInterface, HID_USAGE_Z) + ControlSetText, Static5, Axis Z %tmp_axis_val% / %AxisMax_Z% +return + +SliderRXChanged: + Gui, Submit, NoHide + tmp_axis_val := Floor(AxisMax_RX * AxisRX / 100) + VJoy_SetAxis(tmp_axis_val, iInterface, HID_USAGE_RX) + ControlSetText, Static6, Axis RX %tmp_axis_val% / %AxisMax_RX% +return + +SliderRYChanged: + Gui, Submit, NoHide + tmp_axis_val := Floor(AxisMax_RY * AxisRY / 100) + VJoy_SetAxis(tmp_axis_val, iInterface, HID_USAGE_RY) + ControlSetText, Static7, Axis RY %tmp_axis_val% / %AxisMax_RY% +return + +SliderRZChanged: + Gui, Submit, NoHide + tmp_axis_val := Floor(AxisMax_RZ * AxisRZ / 100) + VJoy_SetAxis(tmp_axis_val, iInterface, HID_USAGE_RZ) + ControlSetText, Static8, Axis RZ %tmp_axis_val% / %AxisMax_RZ% +return + +SliderSL0Changed: + Gui, Submit, NoHide + tmp_axis_val := Floor(AxisMax_SL0 * AxisSL0 / 100) + VJoy_SetAxis(tmp_axis_val, iInterface, HID_USAGE_SL0) + ControlSetText, Static9, Slider0 %tmp_axis_val% / %AxisMax_SL0% +return + +SliderSL1Changed: + Gui, Submit, NoHide + tmp_axis_val := Floor(AxisMax_SL1 * AxisSL1 / 100) + VJoy_SetAxis(tmp_axis_val, iInterface, HID_USAGE_SL1) + ControlSetText, Static10, Slider1 %tmp_axis_val% / %AxisMax_SL1% +return + +SliderWHLChanged: + Gui, Submit, NoHide + tmp_axis_val := Floor(AxisMax_WHL * AxisWHL / 100) + VJoy_SetAxis(tmp_axis_val, iInterface, HID_USAGE_WHL) + ControlSetText, Static11, Wheel %tmp_axis_val% / %AxisMax_WHL% +return + +BtnReload: +F12:: + VJoy_Close(iInterface) + Reload +return + +;GuiClose: +; ExitApp +;return + +OnExit: + VJoy_Close(iInterface) +return + +; GUIBtn test all buttons +BtnTestAllON: + Loop, %nButtons% + { + VJoy_SetBtn(1, iInterface, A_Index) + } +return +BtnTestAllOFF: + Loop, %nButtons% + { + VJoy_SetBtn(0, iInterface, A_Index) + } +return + +BtnTest(id, btn) { + VJoy_SetBtn(1, id, btn) + Sleep, 100 + VJoy_SetBtn(0, id, btn) ; Release button 1 +} + +; GUIBtn1 for test button1 +Btn1: + BtnTest(iInterface, 1) +return +Btn2: + BtnTest(iInterface, 2) +return +Btn3: + BtnTest(iInterface, 3) +return +Btn4: + BtnTest(iInterface, 4) +return +Btn5: + BtnTest(iInterface, 5) +return +Btn6: + BtnTest(iInterface, 6) +return +Btn7: + BtnTest(iInterface, 7) +return +Btn8: + BtnTest(iInterface, 8) +return +Btn9: + BtnTest(iInterface, 9) +return +Btn10: + BtnTest(iInterface, 10) +return +Btn11: + BtnTest(iInterface, 11) +return +Btn12: + BtnTest(iInterface, 12) +return +Btn13: + BtnTest(iInterface, 13) +return +Btn14: + BtnTest(iInterface, 14) +return +Btn15: + BtnTest(iInterface, 15) +return +Btn16: + BtnTest(iInterface, 16) +return +Btn17: + BtnTest(iInterface, 17) +return +Btn18: + BtnTest(iInterface, 18) +return +Btn19: + BtnTest(iInterface, 19) +return +Btn20: + BtnTest(iInterface, 20) +return +Btn21: + BtnTest(iInterface, 21) +return +Btn22: + BtnTest(iInterface, 22) +return +Btn23: + BtnTest(iInterface, 23) +return +Btn24: + BtnTest(iInterface, 24) +return +Btn25: + BtnTest(iInterface, 25) +return +Btn26: + BtnTest(iInterface, 26) +return +Btn27: + BtnTest(iInterface, 27) +return +Btn28: + BtnTest(iInterface, 28) +return +Btn29: + BtnTest(iInterface, 29) +return +Btn30: + BtnTest(iInterface, 30) +return +Btn31: + BtnTest(iInterface, 31) +return +Btn32: + BtnTest(iInterface, 32) +return + +; Open Game Control Panel +Btnjoycpl: + RunWait %ComSpec% /C start joy.cpl,, Hide +return + +BtnReset: + AxisX := AxisY := AxisZ := AxisRX := AxisRY := AxisRZ := Slider0 := Slider1 := 0 + Gui, Submit, NoHide + + VJoy_Close(iInterface) + VJoy_Init() +return + +EditContPov: + Gui, Submit, NoHide + + GuiControlGet, ContPovChoice + if (ContPovChoice < 1 or ContPovChoice > ContPovNumber) { + MsgBox, Please select a pov + return + } + + if (PovValDirect < -1) { + PovValDirect := -1 + Gui, Submit, NoHide + return + } + if (PovValDirect > 35999) { + PovValDirect := 35999 + Gui, Submit, NoHide + return + } + VJoy_SetContPov(PovValDirect, iInterface, ContPovChoice) +return + +SliderContPov: + Gui, Submit, NoHide + GuiControlGet, ContPovChoice + if (ContPovChoice < 1 or ContPovChoice > ContPovNumber) { + MsgBox, Please select a pov + return + } + PovValDirect := Floor(35999 * PovValSlider / 100) + ControlSetText, Edit1, %PovValDirect% + VJoy_SetContPov(PovValDirect, iInterface, ContPovChoice) +return + +ContPovChoose: + Gui, Submit, NoHide + GuiControlGet, ContPovChoice +return + +BtnPovNeu: + Gui, Submit, NoHide + GuiControlGet, DiscPovChoice + if (DiscPovChoice < 1 or DiscPovChoice > DiscPovNumber) { + MsgBox, Please select a pov + return + } + VJoy_SetDiscPov(-1, iInterface, DiscPovChoice) +return + +BtnPovN: + Gui, Submit, NoHide + GuiControlGet, DiscPovChoice + if (DiscPovChoice < 1 or DiscPovChoice > DiscPovNumber) { + MsgBox, Please select a pov + return + } + VJoy_SetDiscPov(0, iInterface, DiscPovChoice) +return + +BtnPovE: + Gui, Submit, NoHide + GuiControlGet, DiscPovChoice + if (DiscPovChoice < 1 or DiscPovChoice > DiscPovNumber) { + MsgBox, Please select a pov + return + } + VJoy_SetDiscPov(1, iInterface, DiscPovChoice) +return + +BtnPovS: + Gui, Submit, NoHide + GuiControlGet, DiscPovChoice + if (DiscPovChoice < 1 or DiscPovChoice > DiscPovNumber) { + MsgBox, Please select a pov + return + } + VJoy_SetDiscPov(2, iInterface, DiscPovChoice) +return + +BtnPovW: + Gui, Submit, NoHide + GuiControlGet, DiscPovChoice + if (DiscPovChoice < 1 or DiscPovChoice > DiscPovNumber) { + MsgBox, Please select a pov + return + } + VJoy_SetDiscPov(3, iInterface, DiscPovChoice) +return + +DiscPovChoose: + Gui, Submit, NoHide + GuiControlGet, DiscPovChoice +return \ No newline at end of file diff --git a/VJoy_lib.ahk b/VJoy_lib.ahk new file mode 100644 index 0000000..508d8dc --- /dev/null +++ b/VJoy_lib.ahk @@ -0,0 +1,175 @@ +;VJoy_lib.ahk + + iInterface = 2 ; Default target vJoy device + + ; ported from VjdStat in vjoyinterface.h + VJD_STAT_OWN := 0 ; The vJoy Device is owned by this application. + VJD_STAT_FREE := 1 ; The vJoy Device is NOT owned by any application (including this one). + VJD_STAT_BUSY := 2 ; The vJoy Device is owned by another application. It cannot be acquired by this application. + VJD_STAT_MISS := 3 ; The vJoy Device is missing. It either does not exist or the driver is down. + VJD_STAT_UNKN := 4 ; Unknown + + ;HID Descriptor definitions(ported from public.h + HID_USAGE_X := 0x30 + HID_USAGE_Y := 0x31 + HID_USAGE_Z := 0x32 + HID_USAGE_RX := 0x33 + HID_USAGE_RY := 0x34 + HID_USAGE_RZ := 0x35 + HID_USAGE_SL0 := 0x36 + HID_USAGE_SL1 := 0x37 + HID_USAGE_WHL := 0x38 + HID_USAGE_POV := 0x39 + +VJoy_init() { + Global iInterface, VJD_STAT_OWN, VJD_STAT_FREE, VJD_STAT_BUSY, VJD_STAT_MISS, VJD_STAT_UNKN, ContPovNumber, DiscPovNumber, hDLL + + SetWorkingDir, %A_ScriptDir% + curdir:=A_WorkingDir + + if (!hDLL) { + dllpath = %A_ScriptDir%\vJoyInterface.dll + hDLL := DLLCall("LoadLibrary", "Str", dllpath) + if (!hDLL) { + MsgBox, LoadLibrary %dllpath% fail + } + } + + result := DllCall("vJoyInterface.dll\vJoyEnabled", "Int") + if (ErrorLevel = 4) { + MsgBox, Error! VJoy library "vJoyInterface.dll" is not found!`nErrorLevel:%ErrorLevel% + ExitApp + } + if (!result) { + MsgBox, Error! VJoy interface is not installed!`nErrorLevel:%ErrorLevel% + ExitApp + } + + status := DllCall("vJoyInterface\GetVJDStatus", "Int", iInterface) + if (status = VJD_STAT_OWN) { + ToolTip, vJoy Device %iInterface% is already owned by this feeder + } else if (status = VJD_STAT_FREE) { + ToolTip, vJoy Device %iInterface% is free + } else if (status = VJD_STAT_BUSY) { + MsgBox vJoy Device %iInterface% is already owned by another feeder`nCannot continue`n + ExitApp + } else if (status = VJD_STAT_MISS) { + MsgBox vJoy Device %iInterface% is not installed or disabled`nCannot continue`n + ExitApp + } else { + MsgBox vJoy Device %iInterface% general error`nCannot continue`n + ExitApp + } + Sleep, 50 + ToolTip + + ; Get the number of buttons and POV Hat switchessupported by this vJoy device + ContPovNumber := DllCall("vJoyInterface\GetVJDContPovNumber", "UInt", iInterface, "Int") + DiscPovNumber := DllCall("vJoyInterface\GetVJDDiscPovNumber", "UInt", iInterface, "Int") + + ; Acquire the target device + if (status = VJD_STAT_FREE) { + ac_jvd := VJoy_AcquireVJD(iInterface) + } + if ((status = VJD_STAT_OWN) || ((status = VJD_STAT_FREE) && (!ac_jvd))) { + MsgBox % "Failed to acquire vJoy device number % iInterface " + ExitApp + } else { + ToolTip, Acquired: vJoy device number %iInterface% + } + Sleep, 50 + ToolTip + + VJoy_ResetVJD(iInterface) + +; VJoy_RelinquishVJD(iInterface) + + return +} + +VJoy_AcquireVJD(id) { + return DllCall("vJoyInterface\AcquireVJD", "UInt", id) +} + + +VJoy_GetVJDStatus(id) { + status := DllCall("vJoyInterface\GetVJDStatus", "UInt", id) + return status +} + +VJoy_GetVJDButtonNumber(id) { + res := DllCall("vJoyInterface\GetVJDButtonNumber", "Int", id) + return res +} + +VJoy_SetBtn(sw, id, btn_id) { + res := DllCall("vJoyInterface\SetBtn", "Int", sw, "UInt", id, "UChar", btn_id) + if (!res) { + MsgBox, SetBtn(%sw%, %id%, %btn_id%) err: %ErrorLevel%`nnLastError: %A_LastError% + } + return res +} + +VJoy_ResetAll() { + res := DllCall("vJoyInterface\ResetAll") + return res +} + +VJoy_ResetVJD(id) { + res := DllCall("vJoyInterface\ResetVJD", "UInt", id) + return res +} + +VJoy_GetVJDAxisMax(id, usage) { + res := DllCall("vJoyInterface\GetVJDAxisMax", "Int", id, "Int", usage, "IntP", Max_Axis) + return Max_Axis +} + +VJoy_GetVJDAxisExist(id, usage) { + Axis_t := DllCall("vJoyInterface\GetVJDAxisExist", "UInt", id, "UInt", usage) + if (!Axis_t) { +; MsgBox, GetVJDAxisExist Error!`nErrorLevel:%ErrorLevel% + } + if (ErrorLevel) { + ToolTip, GetVJDAxisExist Warning!`nErrorLevel:%ErrorLevel% + ToolTip + } + return Axis_t +} + +VJoy_SetAxis(axis_val, id, usage) { + res := DllCall("vJoyInterface\SetAxis", "Int", axis_val, "UInt", id, "UInt", usage) + if (!res) { + MsgBox, SetAxis Error!`nErrorLevel:%ErrorLevel% + } + return res +} + +VJoy_RelinquishVJD(id) { + DllCall("vJoyInterface\RelinquishVJD", "UInt", id) +} + +VJoy_Close(id) { + VJoy_ResetAll() + VJoy_RelinquishVJD(id) + if (hDLL) { + DLLCall("FreeLibraly", "Ptr", hDLL) + hDLL:= + } +} + +VJoy_SetDiscPov(Value, id, nPov) { + _res := DllCall("vJoyInterface\SetDiscPov", "Int", Value, "UInt", id, "UChar", nPov) + if (!_res) { + MsgBox, SetDiscPov err: %ErrorLevel% + } + return _ef_res +} + +VJoy_SetContPov(Value, id, nPov) { + _res := DllCall("vJoyInterface\SetContPov", "Int", Value, "UInt", id, "UChar", nPov) + if (!_res) { + MsgBox, SetContPov err: %ErrorLevel% + } + return _ef_res +} \ No newline at end of file diff --git a/VJoy_lib_1.ahk b/VJoy_lib_1.ahk new file mode 100644 index 0000000..c4af3c0 --- /dev/null +++ b/VJoy_lib_1.ahk @@ -0,0 +1,839 @@ +; VJoy_lib.ahk Ver1.1 +; Original code by Axlar - http://www.autohotkey.com/board/topic/87690- +; modded by evilC - VJoy_SetAxis fix (Bad ternary operator) + + VJD_MAXDEV := 16 + + ; ported from VjdStat in vjoyinterface.h + VJD_STAT_OWN := 0 ; The vJoy Device is owned by this application. + VJD_STAT_FREE := 1 ; The vJoy Device is NOT owned by any application (including this one). + VJD_STAT_BUSY := 2 ; The vJoy Device is owned by another application. It cannot be acquired by this application. + VJD_STAT_MISS := 3 ; The vJoy Device is missing. It either does not exist or the driver is down. + VJD_STAT_UNKN := 4 ; Unknown + + ; HID Descriptor definitions(ported from public.h + HID_USAGE_X := 0x30 + HID_USAGE_Y := 0x31 + HID_USAGE_Z := 0x32 + HID_USAGE_RX:= 0x33 + HID_USAGE_RY:= 0x34 + HID_USAGE_RZ:= 0x35 + HID_USAGE_SL0:= 0x36 + HID_USAGE_SL1:= 0x37 + + VJDev := Object() + +; Load lib from already load or current/system directory +VJoy_LoadLibrary() { + Global hVJDLL + if (hVJDLL) { + return hVJDLL + } + + ; Load dll from any path or get handle of already loaded + hVJDLL := DLLCall("LoadLibrary", "Str", "vJoyInterface") + if (hVJDLL) { + return hVJDLL + } + + ; If dll deployed into current and it was wrong, warn + dllpath = %A_ScriptDir%\vJoyInterface.dll + if (FileExist(dllpath)) { + if (A_Is64bitOS) { + is64bit = 64-bitOS + } + AHKEd := (A_PtrSize = 4) ? "32-bit" : "64-bit" + RequiredDLL := (A_PtrSize = 4) ? "x86" : "x64" + dll_info := GetFileVersion(dllpath) + if (dll_info and !InStr(dll_info, RequiredDLL)) { + isWrong = =wrong! + } + MsgBox, + ( +LoadLibrary %dllpath% failed! +Exiting. +Make sure %RequiredDLL% vJoyInterface.dll is in %A_ScriptDir% + (%dll_info%%isWrong%) + AutoHotkey: %AHKEd% + OSVersion:%A_OSVersion% %is64bit% + ) + } + return 0 +} + +GetFileVersion(pszFilePath) { + dwSize := DLLCall("Version\GetFileVersionInfoSize", "Str", pszFilePath) + if (!dwSize) { + return + } + VarSetCapacity(pvData, dwSize) + if (!DLLCall("Version\GetFileVersionInfo", "Str", pszFilePath + , "Int", 0, "Int", dwSize, "Ptr", &pvData)) { + return + } + ; Get British product version string + if (!DLLCall("Version\VerQueryValue", "UInt", &pvData, "Str" + , "\\StringFileInfo\\040904b0\\ProductVersion", "UIntP" + , lpTranslate, "UInt", 0)) { + return + } + return StrGet(lpTranslate) +} + +Class VJoyDev { + __New(dev_id) { + + Global NoticeDone, hVJDLL, VJD_STAT_OWN, VJD_STAT_FREE, VJD_STAT_BUSY, VJD_STAT_MISS, VJD_STAT_UNKN,HID_USAGE_X,HID_USAGE_Y,HID_USAGE_Z,HID_USAGE_RX,HID_USAGE_RY,HID_USAGE_RZ,HID_USAGE_SL0,HID_USAGE_SL1 + + if (!hVJDLL) { + hVJDLL := VJoy_LoadLibrary() + } + if (!hVJDLL) { + if (!NoticeDone) { + NoticeDone := True + MsgBox, [VJoy Constructer] LoadLibrary vJoyInterface.dll failed! + } + return + } + + this.DeviceEnabled := DllCall("vJoyInterface.dll\vJoyEnabled") + if (ErrorLevel = 4) { + MsgBox, Error! VJoy library "vJoyInterface.dll" is not found!`nErrorLevel:%ErrorLevel% + return + } + if (!this.DeviceEnabled) { + ;MsgBox, Error! VJoy interface is not installed!`nErrorLevel:%ErrorLevel% + return + } + + DeviceStatus := DllCall("vJoyInterface\GetVJDStatus", "UInt", dev_id) + if (DeviceStatus = VJD_STAT_OWN) { + stat_str = VJD_STAT_OWN + ;ToolTip, vJoy Device %dev_id% is already owned by this feeder + } else if (DeviceStatus = VJD_STAT_FREE) { + ;ToolTip, vJoy Device %dev_id% is free + stat_str = VJD_STAT_FREE + } else if (DeviceStatus = VJD_STAT_BUSY) { + MsgBox vJoy Device %dev_id% is already owned by another feeder`nCannot continue`n + stat_str = VJD_STAT_BUSY + return + } else if (DeviceStatus = VJD_STAT_MISS) { + ;MsgBox vJoy Device %dev_id% is not installed or disabled`nCannot continue`n + stat_str = VJD_STAT_MISS + return + } else { + stat_str = VJD_STAT_UNKN + MsgBox vJoy Device %dev_id% general error`nCannot continue`n + return + } + ;ToolTip + + ; Get the number of buttons and POV Hat switchessupported by this vJoy device + this.ContPovNumber := DllCall("vJoyInterface\GetVJDContPovNumber", "UInt", dev_id) + this.ContPov := Object() + Loop, % this.ContPovNumber ; insert dummy + this.ContPov.Insert(A_Index, 0) + + this.DiscPovNumber := DllCall("vJoyInterface\GetVJDDiscPovNumber", "UInt", dev_id) + this.DiscPov := Object() + Loop, % this.DiscPovNumber ; insert dummy + this.DiscPov.Insert(A_Index, 0) + + this.NumberOfButtons := DllCall("vJoyInterface\GetVJDButtonNumber", "Int", dev_id) + this.Btn := Object() + Loop, % this.NumberOfButtons ; insert dummy + this.Btn.Insert(A_Index, 0) + + this.AxisExist_X := DllCall("vJoyInterface\GetVJDAxisExist", "Int", dev_id, "Int", HID_USAGE_X ) + this.AxisExist_Y := DllCall("vJoyInterface\GetVJDAxisExist", "Int", dev_id, "Int", HID_USAGE_Y ) + this.AxisExist_Z := DllCall("vJoyInterface\GetVJDAxisExist", "Int", dev_id, "Int", HID_USAGE_Z ) + this.AxisExist_RX := DllCall("vJoyInterface\GetVJDAxisExist", "Int", dev_id, "Int", HID_USAGE_RX ) + this.AxisExist_RY := DllCall("vJoyInterface\GetVJDAxisExist", "Int", dev_id, "Int", HID_USAGE_RY ) + this.AxisExist_RZ := DllCall("vJoyInterface\GetVJDAxisExist", "Int", dev_id, "Int", HID_USAGE_RZ ) + this.AxisExist_SL0 := DllCall("vJoyInterface\GetVJDAxisExist", "Int", dev_id, "Int", HID_USAGE_SL0) + this.AxisExist_SL1 := DllCall("vJoyInterface\GetVJDAxisExist", "Int", dev_id, "Int", HID_USAGE_SL1) + + if (DllCall("vJoyInterface\GetVJDAxisMax", "Int", dev_id, "Int", HID_USAGE_X, "IntP", nResult)) { + this.AxisMax_X := nResult + } + if (DllCall("vJoyInterface\GetVJDAxisMax", "Int", dev_id, "Int", HID_USAGE_Y, "IntP", nResult)) { + this.AxisMax_Y := nResult + } + if (DllCall("vJoyInterface\GetVJDAxisMax", "Int", dev_id, "Int", HID_USAGE_Z, "IntP", nResult)) { + this.AxisMax_Z := nResult + } + if (DllCall("vJoyInterface\GetVJDAxisMax", "Int", dev_id, "Int", HID_USAGE_RX, "IntP", nResult)) { + this.AxisMax_RX := nResult + } + if (DllCall("vJoyInterface\GetVJDAxisMax", "Int", dev_id, "Int", HID_USAGE_RY, "IntP", nResult)) { + this.AxisMax_RY := nResult + } + if (DllCall("vJoyInterface\GetVJDAxisMax", "Int", dev_id, "Int", HID_USAGE_RZ, "IntP", nResult)) { + this.AxisMax_RZ := nResult + } + if (DllCall("vJoyInterface\GetVJDAxisMax", "Int", dev_id, "Int", HID_USAGE_SL0, "IntP", nResult)) { + this.Slider0_Max := nResult + } + if (DllCall("vJoyInterface\GetVJDAxisMax", "Int", dev_id, "Int", HID_USAGE_SL1, "IntP", nResult)) { + this.Slider1_Max := nResult + } + + ; Acquire the target device + if (DeviceStatus = VJD_STAT_FREE) { + ac_jvd := DllCall("vJoyInterface\AcquireVJD", "UInt", dev_id) + if (!ac_jvd) { + MsgBox, Dev:%dev_id% aquire fail ErrorLevel: %ErrorLevel% + } + } + + if (DeviceStatus = VJD_STAT_OWN) { + MsgBox % "Failed to acquire vJoy device number: " dev_id "`n(Other process owned device)" + return + } else if (DeviceStatus = VJD_STAT_FREE and !ac_jvd ) { + MsgBox % "Failed to acquire vJoy device number: " dev_id "`nAcquired: " ac_jvd + return + } else { + ;ToolTip, % "Acquired: vJoy device number: " dev_id + } + ;ToolTip + + this.DeviceID := dev_id + this.DeviceStatus := DeviceStatus + this.Reset() + this.DeviceReady := True + + return this + } + + __Delete() { + this.Relinquish() + } + + SetAxis(axis_val, usage) { + res := DllCall("vJoyInterface\SetAxis", "Int", axis_val, "UInt", this.DeviceID, "UInt", usage) + if (!res) { + MsgBox, SetAxis(%axis_val%`,%usage%) Error!`nErrorLevel:%ErrorLevel% + } + return res + } + + SetAxis_X(axis_val) { + Global HID_USAGE_X + new_val := parse_rel_val(axis_val, this.Axis_X, this.AxisMax_X) + res := this.SetAxis(new_val, HID_USAGE_X) + if (res) { + this.Axis_X := new_val + } + return res + } + SetAxis_Y(axis_val) { + Global HID_USAGE_Y + new_val := parse_rel_val(axis_val, this.Axis_Y, this.AxisMax_Y) + res := this.SetAxis(new_val, HID_USAGE_Y) + if (res) { + this.Axis_Y := new_val + } + return res + } + SetAxis_Z(axis_val) { + Global HID_USAGE_Z + new_val := parse_rel_val(axis_val, this.Axis_Z, this.AxisMax_Z) + res := this.SetAxis(new_val, HID_USAGE_Z) + if (res) { + this.Axis_Z := new_val + } + return res + } + SetAxis_RX(axis_val) { + Global HID_USAGE_RX + new_val := parse_rel_val(axis_val, this.Axis_RX, this.AxisMax_RX) + res := this.SetAxis(new_val, HID_USAGE_RX) + if (res) { + this.Axis_RX := new_val + } + return res + } + SetAxis_RY(axis_val) { + Global HID_USAGE_RY + new_val := parse_rel_val(axis_val, this.Axis_RY, this.AxisMax_RY) + res := this.SetAxis(new_val, HID_USAGE_RY) + if (res) { + this.Axis_RY := new_val + } + return res + } + SetAxis_RZ(axis_val) { + Global HID_USAGE_RZ + new_val := parse_rel_val(axis_val, this.Axis_RZ, this.AxisMax_RZ) + res := this.SetAxis(new_val, HID_USAGE_RZ) + if (res) { + this.Axis_RZ := new_val + } + return res + } + SetAxis_SL0(axis_val) { + Global HID_USAGE_SL0 + new_val := parse_rel_val(axis_val, this.Axis_SL0, this.AxisMax_SL0) + res := this.SetAxis(new_val, HID_USAGE_SL0) + if (res) { + this.Slider0 := new_val + } + return res + } + SetAxis_SL1(axis_val) { + Global HID_USAGE_SL1 + new_val := parse_rel_val(axis_val, this.Axis_SL1, this.AxisMax_SL1) + res := this.SetAxis(new_val, HID_USAGE_SL1) + if (res) { + this.Slider1 := new_val + } + return res + } + + GetBtn(bid) { + if (bid < 1 or bid > this.NumberOfButtons) { + return 0 + } + return this.Btn[bid] + } + + SetBtn(sw, btn_id) { + if (btn_id < 1 or btn_id > this.NumberOfButtons) { + MsgBox, SetBtn: range check error! + return 0 + } + res := DllCall("vJoyInterface\SetBtn", "Int", sw, "UInt", this.DeviceID, "UChar", btn_id) + if (res) { + this.Btn[btn_id] := sw + } + return res + } + + SetDiscPov(Value, nPov) { + _res := DllCall("vJoyInterface\SetDiscPov", "Int", Value, "UInt", this.DeviceID, "UChar", nPov) + if (!_res) { + MsgBox, SetDiscPov err: %ErrorLevel% + } else { + this.DiscPov[nPov] := Value + } + return _res + } + + SetContPov(Value, nPov) { + _res := DllCall("vJoyInterface\SetContPov", "Int", Value, "UInt", this.DeviceID, "UChar", nPov) + if (!_res) { + MsgBox, SetContPov err: %ErrorLevel% + } else { + this.ContPov[nPov] := Value + } + return _res + } + + Reset() { + ; Reset local state values + this.Axis_X := 0 + this.Axis_Y := 0 + this.Axis_Z := 0 + this.Axis_RX := 0 + this.Axis_RY := 0 + this.Axis_RZ := 0 + this.Slider0 := 0 + this.Slider1 := 0 + + for i in this.ContPov + this.ContPov[i] := 0 + for i in this.DiscPov + this.DiscPov[i] := 0 + for i in this.Btn + this.Btn[i] := 0 + return DllCall("vJoyInterface\ResetVJD", "UInt", this.DeviceID) + } + + Relinquish() { + return DllCall("vJoyInterface\RelinquishVJD", "UInt", this.DeviceID) + } +} + +VJoy_init(id := 1) { + Global VJDev, VJD_MAXDEV + if (id < 1 || id > VJD_MAXDEV) { + MsgBox, [%A_ThisFunc%] Device %id% is invalid. Please specify 1-%VJD_MAXDEV%. + return + } + VJDev[id] := new VJoyDev(id) + return VJDev[id] +} + + +VJoy_DeviceErr(id) { + Global VJD_MAXDEV, VJDev + if (id < 1 or id > VJD_MAXDEV) { + MsgBox, [%A_ThisFunc%] Device %id% is invalid. Please specify 1-%VJD_MAXDEV%. + return True + } + if (!VJDev[id].DeviceReady) { + MsgBox, [%A_ThisFunc%] Device %id% is not ready. + return True + } + return False +} +VJoy_Ready(id) { + Global VJD_MAXDEV, VJDev + if (id < 1 || id > VJD_MAXDEV) { + return False + } + return VJDev[id].DeviceReady +} + +VJoy_ResetVJD(id) { + Global VJDev + if (VJoy_DeviceErr(id)) + return False + return VJDev[id].Reset() +} + +VJoy_ResetAll() { + return DllCall("vJoyInterface\ResetAll") +} + +; Release device +VJoy_RelinquishVJD(id) { + Global VJDev + if (VJoy_DeviceErr(id)) + return False + return VJDev[id].Relinquish() +} + +; Acquire device - added by evilC +VJoy_AcquireVJD(id) { + Global VJDev + if (VJoy_DeviceErr(id)) + return False + return DllCall("vJoyInterface\AcquireVJD", "UInt", id) +} + +; destructor +VJoy_Close() { + Global VJDev + + VJoy_ResetAll() + + for idx, dev in VJDev + dev.delete + if (hVJDLL) { + DLLCall("FreeLibraly", "Ptr", hVJDLL) + hVJDLL:= + } +} + +VJoy_GetContPovNumber(id) { + Global VJDev + if (VJoy_DeviceErr(id)) + return False + return VJDev[id].ContPovNumber +} + +VJoy_GetDiscPovNumber(id) { + Global VJDev + if (VJoy_DeviceErr(id)) + return False + return VJDev[id].DiscPovNumber +} + +VJoy_GetVJDButtonNumber(id) { + Global VJDev + if (VJoy_DeviceErr(id)) + return False + return VJDev[id].NumberOfButtons +} + +VJoy_GetAxisExist_X(id) { + Global VJDev + if (VJoy_DeviceErr(id)) + return False + return VJDev[id].AxisExist_X +} +VJoy_GetAxisExist_Y(id) { + Global VJDev + if (VJoy_DeviceErr(id)) + return False + return VJDev[id].AxisExist_Y +} +VJoy_GetAxisExist_Z(id) { + Global VJDev + if (VJoy_DeviceErr(id)) + return False + return VJDev[id].AxisExist_Z +} +VJoy_GetAxisExist_RX(id) { + Global VJDev + if (VJoy_DeviceErr(id)) + return False + return VJDev[id].AxisExist_RX +} +VJoy_GetAxisExist_RY(id) { + Global VJDev + if (VJoy_DeviceErr(id)) + return False + return VJDev[id].AxisExist_RY +} +VJoy_GetAxisExist_RZ(id) { + Global VJDev + if (VJoy_DeviceErr(id)) + return False + return VJDev[id].AxisExist_RZ +} +VJoy_GetAxisExist_SL0(id) { + Global VJDev + if (VJoy_DeviceErr(id)) + return False + return VJDev[id].AxisExist_SL0 +} +VJoy_GetAxisExist_SL1(id) { + Global VJDev + if (VJoy_DeviceErr(id)) + return False + return VJDev[id].AxisExist_SL1 +} + + +VJoy_GetAxisMax_X(id) { + Global VJDev + if (VJoy_DeviceErr(id)) + return False + return VJDev[id].AxisMax_X +} +VJoy_GetAxisMax_Y(id) { + Global VJDev + if (VJoy_DeviceErr(id)) + return False + return VJDev[id].AxisMax_Y +} +VJoy_GetAxisMax_Z(id) { + Global VJDev + if (VJoy_DeviceErr(id)) + return False + return VJDev[id].AxisMax_Z +} +VJoy_GetAxisMax_RX(id) { + Global VJDev + if (VJoy_DeviceErr(id)) + return False + return VJDev[id].AxisMax_RX +} +VJoy_GetAxisMax_RY(id) { + Global VJDev + if (VJoy_DeviceErr(id)) + return False + return VJDev[id].AxisMax_RY +} +VJoy_GetAxisMax_RZ(id) { + Global VJDev + if (VJoy_DeviceErr(id)) + return False + return VJDev[id].AxisMax_RZ +} +VJoy_GetAxisMax_SL0(id) { + Global VJDev + if (VJoy_DeviceErr(id)) + return False + return VJDev[id].Slider0_Max +} +VJoy_GetAxisMax_SL1(id) { + Global VJDev + if (VJoy_DeviceErr(id)) + return False + return VJDev[id].Slider1_Max +} + +; for compatibility +VJoy_GetVJDAxisMax(id, usage) { + + Global VJDev, HID_USAGE_X,HID_USAGE_Y,HID_USAGE_Z,HID_USAGE_RX,HID_USAGE_RY,HID_USAGE_RZ,HID_USAGE_SL0,HID_USAGE_SL1 + if (VJoy_DeviceErr(id)) + return False + + return (usage = HID_USAGE_X) ? VJDev[id].AxisMax_X : + (usage = HID_USAGE_Y) ? VJDev[id].AxisMax_Y : + (usage = HID_USAGE_Z) ? VJDev[id].AxisMax_Z : + (usage = HID_USAGE_RX) ? VJDev[id].AxisMax_RX : + (usage = HID_USAGE_RY) ? VJDev[id].AxisMax_RY : + (usage = HID_USAGE_RZ) ? VJDev[id].AxisMax_RZ : + (usage = HID_USAGE_SL0) ? VJDev[id].AxisMax_Y : + VJDev[id].AxisMax_SL1 +} + +VJoy_GetVJDAxisExist(id, usage) { + + Global VJDev, HID_USAGE_X,HID_USAGE_Y,HID_USAGE_Z,HID_USAGE_RX,HID_USAGE_RY,HID_USAGE_RZ,HID_USAGE_SL0,HID_USAGE_SL1 + if (VJoy_DeviceErr(id)) + return False + + return (usage = HID_USAGE_X) ? VJDev[id].AxisExist_X : + (usage = HID_USAGE_Y) ? VJDev[id].AxisExist_Y : + (usage = HID_USAGE_Z) ? VJDev[id].AxisExist_Z : + (usage = HID_USAGE_RX) ? VJDev[id].AxisExist_RX : + (usage = HID_USAGE_RY) ? VJDev[id].AxisExist_RY : + (usage = HID_USAGE_RZ) ? VJDev[id].AxisExist_RZ : + (usage = HID_USAGE_SL0) ? VJDev[id].AxisExist_Y : + VJDev[id].AxisExist_SL1 +} + +VJoy_GetBtn(id, btn_id) { + Global VJDev + if (VJoy_DeviceErr(id)) + return False + return VJDev[id].GetBtn(btn_id) +} + +VJoy_SetBtn(sw, id, btn_id) { + Global VJDev + if (VJoy_DeviceErr(id)) + return False + res := VJDev[id].SetBtn(sw, btn_id) + if (!res) { + MsgBox, SetBtn(%sw%, %id%, %btn_id%) err: %ErrorLevel%`nnLastError: %A_LastError% + } + return res +} + +VJoy_GetAxis_X(id) { + Global VJDev + if (VJoy_DeviceErr(id)) + return False + return VJDev[id].Axis_X +} +VJoy_GetAxis_Y(id) { + Global VJDev + if (VJoy_DeviceErr(id)) + return False + return VJDev[id].Axis_Y +} +VJoy_GetAxis_Z(id) { + Global VJDev + if (VJoy_DeviceErr(id)) + return False + return VJDev[id].Axis_Z +} +VJoy_GetAxis_RX(id) { + Global VJDev + if (VJoy_DeviceErr(id)) + return False + return VJDev[id].Axis_RX +} +VJoy_GetAxis_RY(id) { + Global VJDev + if (VJoy_DeviceErr(id)) + return False + return VJDev[id].Axis_RY +} +VJoy_GetAxis_RZ(id) { + Global VJDev + if (VJoy_DeviceErr(id)) + return False + return VJDev[id].Axis_RZ +} +VJoy_GetAxis_SL0(id) { + Global VJDev + if (VJoy_DeviceErr(id)) + return False + return VJDev[id].Slider0 +} +VJoy_GetAxis_SL1(id) { + Global VJDev + if (VJoy_DeviceErr(id)) + return False + return VJDev[id].Slider1 +} + +VJoy_SetAxis_X(axis_val, id) { + Global VJDev + if (VJoy_DeviceErr(id)) + return False + return VJDev[id].SetAxis_X(axis_val) +} +VJoy_SetAxis_Y(axis_val, id) { + Global VJDev + if (VJoy_DeviceErr(id)) + return False + return VJDev[id].SetAxis_Y(axis_val) +} +VJoy_SetAxis_Z(axis_val, id) { + Global VJDev + if (VJoy_DeviceErr(id)) + return False + return VJDev[id].SetAxis_Z(axis_val) +} +VJoy_SetAxis_RX(axis_val, id) { + Global VJDev + if (VJoy_DeviceErr(id)) + return False + return VJDev[id].SetAxis_RX(axis_val) +} +VJoy_SetAxis_RY(axis_val, id) { + Global VJDev + if (VJoy_DeviceErr(id)) + return False + return VJDev[id].SetAxis_RY(axis_val) +} +VJoy_SetAxis_RZ(axis_val, id) { + Global VJDev + if (VJoy_DeviceErr(id)) + return False + return VJDev[id].SetAxis_RZ(axis_val) +} +VJoy_SetAxis_SL0(axis_val, id) { + Global VJDev + if (VJoy_DeviceErr(id)) + return False + return VJDev[id].SetAxis_SL0(axis_val) +} +VJoy_SetAxis_SL1(axis_val, id) { + Global VJDev + if (VJoy_DeviceErr(id)) + return False + return VJDev[id].SetAxis_SL1(axis_val) +} + +; for compatibility +VJoy_SetAxis(axis_val, id, usage) { + Global VJDev, HID_USAGE_X,HID_USAGE_Y,HID_USAGE_Z,HID_USAGE_RX,HID_USAGE_RY,HID_USAGE_RZ,HID_USAGE_SL0,HID_USAGE_SL1 + if (VJoy_DeviceErr(id)) + return False + + if (usage == HID_USAGE_X){ + return VJDev[id].SetAxis_X(axis_val) + } else if (usage == HID_USAGE_Y){ + return VJDev[id].SetAxis_Y(axis_val) + } else if (usage == HID_USAGE_Z){ + return VJDev[id].SetAxis_Z(axis_val) + } else if (usage == HID_USAGE_RX){ + return VJDev[id].SetAxis_RX(axis_val) + } else if (usage == HID_USAGE_RY){ + return VJDev[id].SetAxis_RY(axis_val) + } else if (usage == HID_USAGE_RZ){ + return VJDev[id].SetAxis_RZ(axis_val) + } else if (usage == HID_USAGE_SL0){ + return VJDev[id].SetAxis_SL0(axis_val) + } else if (usage == HID_USAGE_SL1){ + return VJDev[id].SetAxis_SL1(axis_val) + } else { + MsgBox, Unknown Axis: %usage% + } +} + +VJoy_GetDiscPov(id, nPov) { + Global VJDev + if (VJoy_DeviceErr(id)) + return False + return VJDev[id].DiscPov[nPov] +} +VJoy_SetDiscPov(Value, id, nPov) { + Global VJDev + if (VJoy_DeviceErr(id)) + return False + return VJDev[id].SetDiscPov(Value, nPov) +} + +VJoy_GetContPov(id, nPov) { + Global VJDev + if (VJoy_DeviceErr(id)) + return False + return VJDev[id].ContPov[nPov] +} +VJoy_SetContPov(Value, id, nPov) { + Global VJDev + if (VJoy_DeviceErr(id)) + return False + return VJDev[id].SetContPov(Value, nPov) +} + +; for debug: dump value of structure +VJoy_Dump(id) { + Global VJDev + if (VJoy_DeviceErr(id)) + return False + + num := VJoy_GetVJDButtonNumber(id) + for idx, btn in VJDev[id].Btn + { + if (idx<10) + buf1 .= "_" + buf1 .= idx . "|" + buf2 .= "_" . btn . "|" + } + str_btn = Button(%num%):`n %buf1%`n %buf2%`n + + if (VJoy_GetAxisMax_X(id)) { + str_btn .= "Axis_X: " . VJoy_GetAxis_X(id) . "`n" + } + if (VJoy_GetAxisMax_Y(id)) { + str_btn .= "Axis_Y: " . VJoy_GetAxis_Y(id) . "`n" + } + if (VJoy_GetAxisMax_Z(id)) { + str_btn .= "Axis_Z: " . VJoy_GetAxis_Z(id) . "`n" + } + if (VJoy_GetAxisMax_RX(id)) { + str_btn .= "Axis_RX: " . VJoy_GetAxis_RX(id) . "`n" + } + if (VJoy_GetAxisMax_RY(id)) { + str_btn .= "Axis_RY: " . VJoy_GetAxis_RY(id) . "`n" + } + if (VJoy_GetAxisMax_RZ(id)) { + str_btn .= "Axis_RZ: " . VJoy_GetAxis_RZ(id) . "`n" + } + if (VJoy_GetAxisMax_SL0(id)) { + str_btn .= "Axis_SL0: " . VJoy_GetAxis_SL0(id) . "`n" + } + if (VJoy_GetAxisMax_SL1(id)) { + str_btn .= "Axis_SL1: " . VJoy_GetAxis_SL1(id) . "`n" + } + + num := VJoy_GetContPovNumber(id) + if (num) { + for idx, btn in VJDev[id].ContPov + { + Loop, % (StrLen(btn) - 1) + buf3 .= "_" + buf3 .= idx . "|" + buf4 .= btn . "|" + } + str_cont = ContPov(%num%):`n %buf3%`n %buf4%`n + } else { + str_cont = No Continuous Button.`n + } + str_btn .= str_cont + + num := VJoy_GetDiscPovNumber(id) + if (num) { + for idx, btn in VJDev[id].DiscPov + { + Loop, % (StrLen(btn) - 1) + buf5 .= "_" + buf5 .= idx . "|" + buf6 .= btn . "|" + } + str_Disc = DiscPov(%num%):`n %buf5%`n %buf6%`n + } else { + str_Disc = No Discrete Button.`n + } + str_btn .= str_Disc + ToolTip, %str_btn% +} + +parse_rel_val(invar, curval, max) { + if (InStr(invar, "+")) { + StringReplace, _buffer, invar, + + res := curval + _buffer + if (res > max) + return max + return res + } else if (InStr(invar, "-")) { + StringReplace, _buffer, invar, - + res := curval - _buffer + if (res < 0) + return 0 + return res + } + return invar +} \ No newline at end of file diff --git a/joystick/joy_info.ahk b/joystick/joy_info.ahk new file mode 100644 index 0000000..1c43fa2 --- /dev/null +++ b/joystick/joy_info.ahk @@ -0,0 +1,84 @@ +; none of this is written by genmce - just lifted from ahk forum +; I think Chris wrote it - good for joystick info. + +; July 6, 2005: Added auto-detection of joystick number. +; May 8, 2005 : Fixed: JoyAxes is no longer queried as a means of +; detecting whether the joystick is connected. Some joysticks are +; gamepads and don't have even a single axis. + +; If you want to unconditionally use a specific joystick number, change +; the following value from 0 to the number of the joystick (1-16). +; A value of 0 causes the joystick number to be auto-detected: +JoystickNumber = 2 + +; END OF CONFIG SECTION. Do not make changes below this point unless +; you wish to alter the basic functionality of the script. + +; Auto-detect the joystick number if called for: +if JoystickNumber <= 0 +{ + Loop 16 ; Query each joystick number to find out which ones exist. + { + GetKeyState, JoyName, %A_Index%JoyName + if JoyName <> + { + JoystickNumber = %A_Index% + break + } + } + if JoystickNumber <= 0 + { + MsgBox The system does not appear to have any joysticks. + ExitApp + } +} + +#SingleInstance +SetFormat, float, 03 ; Omit decimal point from axis position percentages. +GetKeyState, joy_buttons, %JoystickNumber%JoyButtons +GetKeyState, joy_name, %JoystickNumber%JoyName +GetKeyState, joy_info, %JoystickNumber%JoyInfo +Loop +{ + buttons_down = + Loop, %joy_buttons% + { + GetKeyState, joy%a_index%, %JoystickNumber%joy%a_index% + if joy%a_index% = D + buttons_down = %buttons_down%%a_space%%a_index% + } + GetKeyState, joyx, %JoystickNumber%JoyX + axis_info = X%joyx% + GetKeyState, joyy, %JoystickNumber%JoyY + axis_info = %axis_info%%a_space%%a_space%Y%joyy% + IfInString, joy_info, Z + { + GetKeyState, joyz, %JoystickNumber%JoyZ + axis_info = %axis_info%%a_space%%a_space%Z%joyz% + } + IfInString, joy_info, R + { + GetKeyState, joyr, %JoystickNumber%JoyR + axis_info = %axis_info%%a_space%%a_space%R%joyr% + } + IfInString, joy_info, U + { + GetKeyState, joyu, %JoystickNumber%JoyU + axis_info = %axis_info%%a_space%%a_space%U%joyu% + } + IfInString, joy_info, V + { + GetKeyState, joyv, %JoystickNumber%JoyV + axis_info = %axis_info%%a_space%%a_space%V%joyv% + } + IfInString, joy_info, P + { + GetKeyState, joyp, %JoystickNumber%JoyPOV + axis_info = %axis_info%%a_space%%a_space%POV%joyp% + } + ToolTip, %joy_name% (#%JoystickNumber%):`n%axis_info%`nButtons Down: %buttons_down%`n`n(right-click the tray icon to exit) + Sleep, 100 +} +return + +esc::ExitApp diff --git a/joystuff.ahk b/joystuff.ahk new file mode 100644 index 0000000..ebf4a24 --- /dev/null +++ b/joystuff.ahk @@ -0,0 +1,107 @@ + +go_joystuff: + +gosub,joydisplay + +;************************************************* +;* add this to autoexec section of main generic midi program this section all about joystick +;************************************************* +;gosub,joydisplay ; only if you want the joydisplay to show. +SetTimer, stick, 75 ; run the loop every 50 ms - much smoother than 100 +joyX_last := -1 ; initialize +joyY_last := -1 +joyZ_last := -1 +joyR_last := -1 + IniRead, joyXnum, %version%io.ini, joystick, xaxis , %xaxis% ; read the midi In port from ini file + IniRead, joyYnum, %version%io.ini, joystick, yaxis , %yaxis% ; read the midi out port from ini file + IniRead, joyZnum, %version%io.ini, joystick, zaxis , %zaxis% ; read the midi In port from ini file + IniRead, joyRnum, %version%io.ini, joystick, raxis , %raxis% ; read the midi out port from ini file + guicontrol,2:,joyXnum, %JoyXnum% +;joyXnum := 7 ; midi output CC number for this axis +;JoyYnum := 1 +;joyZnum := 10 +;joyRnum := 11 +;************************************************* +;* end section to add to autoexec +;************************************************* +;************************************************* +;* add #include joystuff.ahk to generic midi program file +;************************************************* +;************************************************* +;* save below as joystuff.ahk put it in the same folder as generic midi +;************************************************* +return +joydisplay: ; for testging only. + Gui,2: +LastFound +AlwaysOnTop +Caption +ToolWindow ; +ToolWindow avoids a taskbar button and an alt-tab menu item. + Gui,2: Color, %CustomColor% + Gui,2: Font, s9 ; Set a large font size (32-point). + Gui,2: Add, Text, vMyText w300, ; XX & YY serve to auto-size the window. + gui,2:add,edit, vjoyXnum + Gui,2: Show, AutoSize xcenter ycenter NoActivate ; NoActivate avoids deactivating the currently active window. + +return + +stick: ; label for running joystick detection + { + GetKeyState, JoyX, JoyX ; Get position of X axis. + GetKeyState, JoyY, JoyY ; Get position of Y axis. + getkeystate, joyz, joyz ; lever 3rd axis on my joystick - ms force feedback2 (picked it up at a yardsale for $1. + getkeystate, joyr, joyr ; twist stick rotation axis on my stick + + joyXval := round(joyx*1.27) ; assumes Joyx 0-100 conversion to 0 -127 + joyYval := round((joyy/(-100)+1)*127) ; inverted output joyY 100 - 0 conversion to 127 - 0 (thanks skylord5816 ahk chat) + joyZval := round((joyZ/(-100)+1)*127) ; + joyrval := round(joyr*1.27) ;joy rotation + + GuiControl,2:, MyText, X%joyX% = ccX%joyXval% | Y%joyY% = ccY%joyYval% | R%joyR% = ccR%joyRval% | Z%joyz% = ccZ%joyzval% ; updates the joydisplay, active. + + If (joyXVal != joyx_last) ; if it has moved then send another message. + { + stb = CC + statusbyte = 176 ; chan 1 + 175 + byte1 = %joyXnum% + byte2 = %joyXval% + midiOutShortMsg(h_midiout, statusbyte, byte1, byte2) ; commented out just to run stand alone - showing the settimer is working. + gosub, ShowMidiOutMessage + joyx_last := joyXval + ;MsgBox joylast + + } + if (joyYval != joyY_last) + { + stb = CC + statusbyte = 176 ; chan 1 + 175 + byte1 = %joyYnum% + byte2 = %joyYval% + midiOutShortMsg(h_midiout, statusbyte, byte1, byte2) + gosub, ShowMidiOutMessage + joyY_last := joyYval + + } + If (joyRVal != joyR_last) + { + stb = CC + statusbyte = 176 ; chan 1 + 175 + byte1 = %joyRnum% + byte2 = %joyRval% + midiOutShortMsg(h_midiout, statusbyte, byte1, byte2) ; commented out just to run stand alone - showing the settimer is working. + gosub, ShowMidiOutMessage + joyR_last := joyRval + ;MsgBox joylast + + } + if (joyzval != joyZ_last) + { + stb = CC + statusbyte = 176 ; chan 1 + 175 + byte1 = %joyZnum% + byte2 = %joyZval% + midiOutShortMsg(h_midiout, statusbyte, byte1, byte2) ; commented out just to run stand alone - showing the settimer is working. + gosub, ShowMidiOutMessage + joyZ_last := joyZval + + } + Else + Return ; if none of them have moved, nothing to do. + } +return diff --git a/midi_to_joy_1.ahk b/midi_to_joy_1.ahk new file mode 100644 index 0000000..952d326 --- /dev/null +++ b/midi_to_joy_1.ahk @@ -0,0 +1,172 @@ +/* + ;************************************************* + ;* GENERIC MIDI APP V.0.6 + ;************************************************* +; Last edited 12/16/2010 10:46 PM by genmce + + + Spliting this program into multiple files for readability. + + + + I fear that adding so many different examples my 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 of 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.ahk file. + By creating hotkeys that generate midi messages in the hotkeyTOmidi.ahk file. + + 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.6 + + Joystick to midi + + Mouse to midi + + v. 0.5 + + changing this to multiple files for better readability, hopefully, I did not make it more confusing?? + + will leave version 4 up on the site and add these versions with a download location for version 5 + + New midi monitor, little smaller, little cleaner, I hope. + + Does NOT detect when midi input = midi output so display will get stuck in midi loop - just change your midi ports. + + Auto detect ini file, if it does not exist, one will be generated. + + ini file name is generated from script name, so if you change you script name, it will generate a new ini file. Don't worry, it will do that every time to change the main program file name. + + + 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 +#Include VJoy_lib.ahk + +#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 = midi_to_joy_1 ; Change this to suit you. +;************************************************* +VJoy_Init() +AxisMax_X := VJoy_GetVJDAxisMax(iInterface, HID_USAGE_X) +readini() ; load values from the ini file, via the readini function - see Midi_under_the_hood.ahk file +gosub, MidiPortRefresh ; used to refresh the input and output port lists - see Midi_under_the_hood.ahk file +port_test(numports,numports2) ; test the ports - check for valid ports? - see Midi_under_the_hood.ahk file +gosub, midiin_go ; opens the midi input port listening routine see Midi_under_the_hood.ahk file +gosub, midiout ; opens the midi out port see Midi_under_the_hood.ahk file +gosub, midiMon ; see below - a monitor gui - see Midi_In_and_GuiMonitor.ahk + + +;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! end edit here + +;************************************************* +;* VARIBLES TO SET @ STARTUP +;************************************************* + +cc_msg = 73,74 ; ++++++++++++++++ you might want to add other vars that load in auto execute section +; varibles below are for keyboarcc +channel = 1 ; default channel =1 +ccnum = 7 ; 7 is volume +volVal = 0 ; Default zero for volume +volDelta = 1 ; Amount to change volume +; end of vars for hotkey and keyboardcc + +/* + yourVar = 0 + yourVarDelta = 3 + yourVarCCnum = 1 ; modwheel +*/ + +;settimer, KeyboardCCs, 70 ; timer (loop of code) to run the KeyboardCCs at the 70 interval + +;gosub, go_xymouse + +return ; !!!! no edit here, need this line to end the auto exec section. + +;************************************************* +;* END OF AUTOEXEC SECTION +;************************************************* + +/* + The rest of the script has been broken out to other parts for readability, I hope! +*/ + +;************************************************* +;* INCLUDE FILES - +;* these files need to be in the same folder +;************************************************* +; include files below - you need each of these files in the same folder as this file for this to work. + +#Include Midi_In_and_GuiMonitor.ahk ; this file contains: the function to parse midi message into parts we can work with and a midi monitor. +#Include MidiRules.ahk ; this file contains: Rules for manipulating midi input then sending modified midi output. +#Include hotkeyTOmidi_1.ahk ; this file contains: examples of HOTKEY generated midi messages to be output - the easy way! +#Include hotkeyTOmidi_2.ahk ; this file contains: examples of HOTKEY generated midi messages to be output - the BEST way! +;#include joystuff.ahk ; this file contains: joystick stuff. +;#include xy_mouse.ahk + +#Include Midi_under_the_hood.ahk ; this file contains: (DO NOT EDIT THIS FILE) all the dialogs to set up midi ports and midi message handling. \ No newline at end of file diff --git a/midi_to_joy_1.ini b/midi_to_joy_1.ini new file mode 100644 index 0000000..107a9c6 Binary files /dev/null and b/midi_to_joy_1.ini differ diff --git a/midi_to_joy_1io.ini b/midi_to_joy_1io.ini new file mode 100644 index 0000000..e69de29 diff --git a/vJoyInterface.dll b/vJoyInterface.dll new file mode 100644 index 0000000..e93a41b Binary files /dev/null and b/vJoyInterface.dll differ diff --git a/x32/vJoyInterface.dll b/x32/vJoyInterface.dll new file mode 100644 index 0000000..911f092 Binary files /dev/null and b/x32/vJoyInterface.dll differ diff --git a/x64/vJoyInterface.dll b/x64/vJoyInterface.dll new file mode 100644 index 0000000..e93a41b Binary files /dev/null and b/x64/vJoyInterface.dll differ