Automation Challenge
Re: Automation Challenge
In terms of the WMP
The mp3 file mentioning was just an example to convey my idea more clearly.
In reality, the files are not sound files (but they are executable)
The mp3 file mentioning was just an example to convey my idea more clearly.
In reality, the files are not sound files (but they are executable)
Re: Automation Challenge
If you look at the SendMIDI home page, it contains a list of the commands for port, SysEx, etc. If you post an example of your information that matches any of the parameters there, we can see if a simple demonstration works, and then incorporate from there.
Re: Automation Challenge
I did look into it, believe me! and I've seen all the commands
I also have ALL the data necessary: I know the midi port name and channel number but I don't know WHERE
to place the code in the script. Can you show me an example? without the need of having midi device
Midi device name: VV
Channel receive/send: 1
How do I place it in the script?
Your script works perfectly but it's not listening to anything
I also have ALL the data necessary: I know the midi port name and channel number but I don't know WHERE
to place the code in the script. Can you show me an example? without the need of having midi device
Midi device name: VV
Channel receive/send: 1
How do I place it in the script?
Your script works perfectly but it's not listening to anything
Re: Automation Challenge
Oh.....
You are saying that i should post this section of the problem over there? I got it!!!
You are saying that i should post this section of the problem over there? I got it!!!
Re: Automation Challenge
You can post it here. Is there a MIDI string that you have? You would probably need to test yourself with SendMIDI (just some Windows command lines) to see if you get any of the commands working. The home page there has examples already made, so you would change the device, channel, etc. Once you have strings that work, they can be incorporated into a test script.
It looks like ReceiveMIDI would store the commands for you, and SendMIDI would then be used to replay them. Would look at ReceiveMIDI first, to see if you can store a sequence.
It looks like ReceiveMIDI would store the commands for you, and SendMIDI would then be used to replay them. Would look at ReceiveMIDI first, to see if you can store a sequence.
The output of the ReceiveMIDI tool is compatible with the SendMIDI tool, allowing you to store MIDI message sequences and play them back later.
Re: Automation Challenge
Thank you again Mikey for not giving up on me!!!
Maybe I simply not referring to the home page you are. Can you post a link to what you are referring to?
Here's a typical string of Sysex message that my synth produces:
F0 43 73 01 52 25 00 01 01 00 01 01 F7
I can place the name of the Midi device - not a problem, just show me where - Let's assume the name is: VV, Port:1 , Channel:1
Maybe I simply not referring to the home page you are. Can you post a link to what you are referring to?
Here's a typical string of Sysex message that my synth produces:
F0 43 73 01 52 25 00 01 01 00 01 01 F7
I can place the name of the Midi device - not a problem, just show me where - Let's assume the name is: VV, Port:1 , Channel:1
Re: Automation Challenge
But hold on:
The point was that the script will activate a listening button either way. So, the provision of the string message is irrelevant
I thought that the script upon clicking on F3 will go into listening mode and I will click on whatever and a given string will be generated.
Are we on the same page with the functionality?
The point was that the script will activate a listening button either way. So, the provision of the string message is irrelevant
I thought that the script upon clicking on F3 will go into listening mode and I will click on whatever and a given string will be generated.
Are we on the same page with the functionality?
Re: Automation Challenge
I see. I went back to your original post.
1. On demand: receive (capture) a MIDI string -> assign to file
2. Open the file -> send (play) the MIDI string (did you want this?)
3. Receive (capture) the MIDI string -> Assigned file will open
#1 and #2 are straightforward. For #3, it appears that ReceiveMIDI may be able to do that, but it would have to be tested to see if CPU performance is an issue.
1. On demand: receive (capture) a MIDI string -> assign to file
2. Open the file -> send (play) the MIDI string (did you want this?)
3. Receive (capture) the MIDI string -> Assigned file will open
#1 and #2 are straightforward. For #3, it appears that ReceiveMIDI may be able to do that, but it would have to be tested to see if CPU performance is an issue.
This tool is mainly intended for quickly monitoring the messages that are sent to your computer from a particular MIDI device. By providing filter commands, it's possible to only focus on particular MIDI messages.
Re: Automation Challenge
As for midi You can use this library
https://github.com/dannywarren/AutoHotkey-Midi
https://github.com/dannywarren/AutoHotkey-Midi
Re: Automation Challenge
mikeyww wrote: ↑03 Nov 2020, 13:50I see. I went back to your original post.
1. On demand: receive (capture) a MIDI string -> assign to file -- On demand = F3 ? If yes so my answer is ----->YES YES YES!
2. Open the file -> send (play) the MIDI string (did you want this?) ----> YES!
3. Receive (capture) the MIDI string -> Assigned file will open ------> YES!
#1 and #2 are straightforward. For #3, it appears that ReceiveMIDI may be able to do that, but it would have to be tested to see if CPU performance is an issue.
This tool is mainly intended for quickly monitoring the messages that are sent to your computer from a particular MIDI device. By providing filter commands, it's possible to only focus on particular MIDI messages.
Re: Automation Challenge
1. Are you saying that a revision of the script is necessary?
2. And thank you for the link - I Was looking elsewhere. LOL
3. Do I add the script line or you already placed such a thing in the script?
My understanding is that I need to add the following and simply change the parameters that suit my configuration?
#include AutoHotkey-Midi/Midi.ahk
midi := new Midi()
midi.OpenMidiIn( 5 )
Return
2. And thank you for the link - I Was looking elsewhere. LOL
3. Do I add the script line or you already placed such a thing in the script?
My understanding is that I need to add the following and simply change the parameters that suit my configuration?
#include AutoHotkey-Midi/Midi.ahk
midi := new Midi()
midi.OpenMidiIn( 5 )
Return
Re: Automation Challenge
I can't advise you specifically, but yes, I would try a separate AHK or Windows batch script to see if you can demonstrate that you can use ReceiveMIDI to capture a string. The author of the @malcev-mentioned AHK script might be able to help or provide you with examples, too, upon request. I don't know whether @malcev or someone else produced that.
Re: Automation Challenge
Code is not perfect - need change some uint to ptr.
But for me it works like this (I have 2 midi devices)
But for me it works like this (I have 2 midi devices)
Code: Select all
; Midi.ahk
; Add MIDI input event handling to your AutoHotkey scripts
;
; Danny Warren <[email protected]>
; https://github.com/dannywarren/AutoHotkey-Midi
;
; Always use gui mode when using the midi library, since we need something to
; attach midi events to
Gui, +LastFound
; Defines the string size of midi devices returned by windows (see mmsystem.h)
Global MIDI_DEVICE_NAME_LENGTH := 32
; Defines the size of a midi input struct MIDIINCAPS (see mmsystem.h)
Global MIDI_DEVICE_IN_STRUCT_LENGTH := 44
; Defines for midi event callbacks (see mmsystem.h)
Global MIDI_CALLBACK_WINDOW := 0x10000
Global MIDI_CALLBACK_TASK := 0x20000
Global MIDI_CALLBACK_FUNCTION := 0x30000
; Defines for midi event types (see mmsystem.h)
Global MIDI_OPEN := 0x3C1
Global MIDI_CLOSE := 0x3C2
Global MIDI_DATA := 0x3C3
Global MIDI_LONGDATA := 0x3C4
Global MIDI_ERROR := 0x3C5
Global MIDI_LONGERROR := 0x3C6
Global MIDI_MOREDATA := 0x3CC
; Defines the size of the standard chromatic scale
Global MIDI_NOTE_SIZE := 12
; Defines the midi notes
Global MIDI_NOTES := [ "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B" ]
; Defines the octaves for midi notes
Global MIDI_OCTAVES := [ -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8 ]
; This is where we will keep the most recent midi in event data so that it can
; be accessed via the Midi object, since we cannot store it in the object due
; to how events work
; We will store the last event by the handle used to open the midi device, so
; at least we won't clobber midi events from other devices if the user wants
; to fetch them specifically
Global __midiInEvent := {}
Global __midiInHandleEvent := {}
; List of all midi input devices on the system
Global __midiInDevices := {}
; List of midi input devices to listen to messages for, we do this globally
; since only one instance of the class can listen to a device anyhow
Global __midiInOpenHandles := {}
; Count of open handles, since ahk doesn't have a method to actually count the
; members of an array (it instead just returns the highest index, which isn't
; the same thing)
Global __midiInOpenHandlesCount := 0
; Holds a refence to the system wide midi dll, so we don't have to open it
; multiple times
Global __midiDll := 0
; The window to attach the midi callback listener to, which will default to
; our gui window
Global __midiInCallbackWindow := WinExist()
; Default label prefix
Global midiLabelPrefix := "Midi"
; Enable or disable label event handling
Global midiLabelCallbacks := True
; Enable or disable lazy midi in event debugging via tooltips
Global midiEventTooltips := False
midi := new Midi()
midi.OpenMidiIn( 0 )
midi.OpenMidiIn( 1 )
; Midi class interface
Class Midi
{
; Instance creation
__New()
{
; Initialize midi environment
this.LoadMidi()
this.QueryMidiInDevices()
this.SetupDeviceMenus()
}
; Instance destruction
__Delete()
{
; Close all midi in devices and then unload the midi environment
this.CloseMidiIns()
this.UnloadMidi()
}
; Load midi dlls
LoadMidi()
{
__midiDll := DllCall( "LoadLibrary", "Str", "winmm.dll", "Ptr" )
If ( ! __midiDll )
{
MsgBox, Missing system midi library winmm.dll
ExitApp
}
}
; Unload midi dlls
UnloadMidi()
{
If ( __midiDll )
{
DllCall( "FreeLibrary", "Ptr", __midiDll )
}
}
; Open midi in device and start listening
OpenMidiIn( midiInDeviceId )
{
__OpenMidiIn( midiInDeviceId )
}
; Close midi in device and stop listening
CloseMidiIn( midiInDeviceId )
{
__CLoseMidiIn( midiInDeviceId )
}
; Close all currently open midi in devices
CloseMidiIns()
{
If ( ! __midiInOpenHandlesCount )
{
Return
}
; We have to store the handles we are going to close in advance, because
; autohotkey gets confused if we are removing things from an array while
; iterative over it
deviceIdsToClose := {}
; Iterate once to get a list of ids to close
For midiInDeviceId In __midiInOpenHandles
{
deviceIdsToClose.Insert( midiInDeviceId )
}
; Iterate again to actually close them
For index, midiInDeviceId In deviceIdsToClose
{
this.CloseMidiIn( midiInDeviceId )
}
}
; Query the system for a list of active midi input devices
QueryMidiInDevices()
{
midiInDevices := []
deviceCount := DllCall( "winmm.dll\midiOutGetNumDevs" ) - 1
Loop %deviceCount%
{
midiInDevice := {}
deviceNumber := A_Index - 1
VarSetCapacity( midiInStruct, MIDI_DEVICE_IN_STRUCT_LENGTH, 0 )
midiQueryResult := DllCall( "winmm.dll\midiInGetDevCapsA", UINT, deviceNumber, PTR, &midiInStruct, UINT, MIDI_DEVICE_IN_STRUCT_LENGTH )
; Error handling
If ( midiQueryResult )
{
MsgBox, Failed to query midi devices
Return
}
manufacturerId := NumGet( midiInStruct, 0, "USHORT" )
productId := NumGet( midiInStruct, 2, "USHORT" )
driverVersion := NumGet( midiInStruct, 4, "UINT" )
deviceName := StrGet( &midiInStruct + 8, MIDI_DEVICE_NAME_LENGTH, "CP0" )
support := NumGet( midiInStruct, 4, "UINT" )
midiInDevice.deviceNumber := deviceNumber
midiInDevice.deviceName := deviceName
midiInDevice.productID := productID
midiInDevice.manufacturerID := manufacturerID
midiInDevice.driverVersion := ( driverVersion & 0xF0 ) . "." . ( driverVersion & 0x0F )
__MidiEventDebug( midiInDevice )
midiInDevices.Insert( deviceNumber, midiInDevice )
}
__midiInDevices := midiInDevices
}
; Set up device selection menus
SetupDeviceMenus()
{
For key, value In __midiInDevices
{
menuName := value.deviceName
Menu, __MidInDevices, Add, %menuName%, __SelectMidiInDevice
}
Menu, Tray, Add
Menu, Tray, Add, MIDI Input Devices, :__MidInDevices
Return
__SelectMidiInDevice:
midiInDeviceId := A_ThisMenuItemPos - 1
if ( __midiInOpenHandles[midiInDeviceId] > 0 )
{
__CloseMidiIn( midiInDeviceId )
}
else
{
__OpenMidiIn( midiInDeviceId )
}
Return
}
; Returns the last midi in event values
MidiIn()
{
Return __MidiInEvent
}
}
; Open a handle to a midi device and start listening for messages
__OpenMidiIn( midiInDeviceId )
{
; Look this device up in our device list
device := __midiInDevices[midiInDeviceId]
; Create variable to store the handle the dll open will give us
; NOTE: Creating variables this way doesn't work with class variables, so
; we have to create it locally and then store it in the class later after
VarSetCapacity( midiInHandle, 4, 0 )
; Open the midi device and attach event callbacks
midiInOpenResult := DllCall( "winmm.dll\midiInOpen", UINT, &midiInHandle, UINT, midiInDeviceId, UINT, __midiInCallbackWindow, UINT, 0, UINT, MIDI_CALLBACK_WINDOW )
; Error handling
If ( midiInOpenResult || ! midiInHandle )
{
MsgBox, Failed to open midi in device
Return
}
; Fetch the actual handle value from the pointer
midiInHandle := NumGet( midiInHandle, UINT )
; Start monitoring midi signals
midiInStartResult := DllCall( "winmm.dll\midiInStart", UINT, midiInHandle )
; Error handling
If ( midiInStartResult )
{
MsgBox, Failed to start midi in device
Return
}
; Create a spot in our global event storage for this midi input handle
__MidiInHandleEvent[midiInHandle] := {}
; Register a callback for each midi event
; We only need to do this once for all devices, so only do it if we are
; the first device to be opened
if ( ! __midiInOpenHandlesCount )
{
OnMessage( MIDI_OPEN, "__MidiInCallback" )
OnMessage( MIDI_CLOSE, "__MidiInCallback" )
OnMessage( MIDI_DATA, "__MidiInCallback" )
OnMessage( MIDI_LONGDATA, "__MidiInCallback" )
OnMessage( MIDI_ERROR, "__MidiInCallback" )
OnMessage( MIDI_LONGERROR, "__MidiInCallback" )
OnMessage( MIDI_MOREDATA, "__MidiInCallback" )
}
; Add this device handle to our list of open devices
__midiInOpenHandles.Insert( midiInDeviceId, midiInHandle )
; Increase the tally for the number of open handles we have
__midiInOpenHandlesCount++
; Check this device as enabled in the menu
menuDeviceName := device.deviceName
Menu __MidInDevices, Check, %menuDeviceName%
}
__CloseMidiIn( midiInDeviceId )
{
; Look this device up in our device list
device := __midiInDevices[midiInDeviceId]
; Unregister callbacks if we are the last open handle
if ( __midiInOpenHandlesCount <= 1 )
{
OnMessage( MIDI_OPEN, "" )
OnMessage( MIDI_CLOSE, "" )
OnMessage( MIDI_DATA, "" )
OnMessage( MIDI_LONGDATA, "" )
OnMessage( MIDI_ERROR, "" )
OnMessage( MIDI_LONGERROR, "" )
OnMessage( MIDI_MOREDATA, "" )
}
; Destroy any midi in events that might be left over
__MidiInHandleEvent[midiInHandle] := {}
; Stop monitoring midi
midiInStopResult := DllCall( "winmm.dll\midiInStop", UINT, __midiInOpenHandles[midiInDeviceId] )
; Error handling
If ( midiInStartResult )
{
MsgBox, Failed to stop midi in device
Return
}
; Close the midi handle
midiInStopResult := DllCall( "winmm.dll\midiInClose", UINT, __midiInOpenHandles[midiInDeviceId] )
; Error handling
If ( midiInStartResult )
{
MsgBox, Failed to close midi in device
Return
}
; Finally, remove the handle from the array
__midiInOpenHandles.Remove( midiInDeviceId )
; Decrease the tally for the number of open handles we have
__midiInOpenHandlesCount--
; Check this device as enabled in the menu
menuDeviceName := device.deviceName
Menu __MidInDevices, Uncheck, %menuDeviceName%
}
; Event callback for midi input event
; Note that since this is a callback method, it has no concept of "this" and
; can't access class members
__MidiInCallback( wParam, lParam, msg )
{
; Will hold the midi event object we are building for this event
midiEvent := {}
; Will hold the labels we call so the user can capture this midi event, we
; always start with a generic ":Midi" label so it always gets called first
labelCallbacks := [ midiLabel ]
; Grab the raw midi bytes
rawBytes := lParam
; Split up the raw midi bytes as per the midi spec
highByte := lParam & 0xF0
lowByte := lParam & 0x0F
data1 := (lParam >> 8) & 0xFF
data2 := (lParam >> 16) & 0xFF
msgbox % highByte "`n" lowByte "`n" data1 "`n" data2
}
; Send event information to a listening debugger
__MidiEventDebug( midiEvent )
{
debugStr := ""
For key, value In midiEvent
debugStr .= key . ":" . value . "`n"
debugStr .= "---`n"
; Always output event debug to any listening debugger
OutputDebug, % debugStr
; If lazy tooltip debugging is enabled, do that too
if midiEventTooltips
ToolTip, % debugStr
}
Re: Automation Challenge
Thank you, @malcev. @elad770, please feel free to let me know here if you figure out how to get this part working! The MIDI experts here or there might be able to help you. I can assist after that point but probably not before, because I don't have any access to MIDI. It looks like the code here might be an alternative to ReceiveMIDI.
Re: Automation Challenge
malcev,
Is this a separate script that runs simultaneously? or this is a part I need to copy-paste at the end of the current script?
Is this a separate script that runs simultaneously? or this is a part I need to copy-paste at the end of the current script?
Re: Automation Challenge
Try like this
Code: Select all
Global mode := ""
DllCall("LoadLibrary", "Str", "winmm.dll", "Ptr")
OnMessage(MIDI_OPEN := 0x3C1, "MidiInCallback")
OnMessage(MIDI_CLOSE := 0x3C2, "MidiInCallback")
OnMessage(MIDI_DATA := 0x3C3, "MidiInCallback")
OnMessage(MIDI_LONGDATA := 0x3C4, "MidiInCallback")
OnMessage(MIDI_ERROR := 0x3C5, "MidiInCallback")
OnMessage(MIDI_LONGERROR := 0x3C6, "MidiInCallback")
OnMessage(MIDI_MOREDATA := 0x3CC, "MidiInCallback")
OpenMidiIn(0)
OpenMidiIn(1)
return
f1::
mode := "listen"
; some code
mode := ""
return
OpenMidiIn(id)
{
DllCall("winmm.dll\midiInOpen", "ptr*", midiInHandle, "uint", id, "ptr", A_ScriptHwnd, "uint", 0, "uint", MIDI_CALLBACK_WINDOW := 0x10000)
DllCall("winmm.dll\midiInStart", "ptr", midiInHandle)
return
}
MidiInCallback(wParam, lParam, msg)
{
Critical
highByte := lParam & 0xF0
lowByte := lParam & 0x0F
data1 := (lParam >> 8) & 0xFF
data2 := (lParam >> 16) & 0xFF
if (data2 = 0)
return
key := highByte "-" lowByte "-" data1
if (mode = "listen")
{
msgbox % "listen`n" key
return
}
; here get info from ini file if key exist then run its value
msgbox % "run`n" key
return
}
Re: Automation Challenge
malcev,
Can you pinpoint to me (preferable with screen capture), with parameters I need to change?
Can you pinpoint to me (preferable with screen capture), with parameters I need to change?
Re: Automation Challenge
I'm a bit confused because I don't know if I need to use your script instead or in addition?
I feel we are SO CLOSE!!!!
can you please be more specific as if you are talking to a 10-year-old?
I feel we are SO CLOSE!!!!
can you please be more specific as if you are talking to a 10-year-old?
Re: Automation Challenge
Could be malcev and Mikey are coordinating to work it out and I'm just in the middle, whining? LOL
I'm so sorry if this is the case
I'm so sorry if this is the case
Re: Automation Challenge
Run this code.
Have You got any messages when You press Your midi?
Have You got any messages when You press Your midi?
Code: Select all
Global mode := ""
DllCall("LoadLibrary", "Str", "winmm.dll", "Ptr")
OnMessage(MIDI_OPEN := 0x3C1, "MidiInCallback")
OnMessage(MIDI_CLOSE := 0x3C2, "MidiInCallback")
OnMessage(MIDI_DATA := 0x3C3, "MidiInCallback")
OnMessage(MIDI_LONGDATA := 0x3C4, "MidiInCallback")
OnMessage(MIDI_ERROR := 0x3C5, "MidiInCallback")
OnMessage(MIDI_LONGERROR := 0x3C6, "MidiInCallback")
OnMessage(MIDI_MOREDATA := 0x3CC, "MidiInCallback")
OpenMidiIn(0)
OpenMidiIn(1)
return
f1::
mode := "listen"
; some code
mode := ""
return
OpenMidiIn(id)
{
hr := DllCall("winmm.dll\midiInOpen", "ptr*", midiInHandle, "uint", id, "ptr", A_ScriptHwnd, "uint", 0, "uint", MIDI_CALLBACK_WINDOW := 0x10000)
if hr or ErrorLevel
{
msgbox % "midiInOpen error `nid = " id "`nhr = " hr "`nErrorLevel = " ErrorLevel
exitapp
}
hr := DllCall("winmm.dll\midiInStart", "ptr", midiInHandle)
if hr or ErrorLevel
{
msgbox % "midiInStart error `nid = " id "`nhr = " hr "`nErrorLevel = " ErrorLevel
exitapp
}
return
}
MidiInCallback(wParam, lParam, msg)
{
Critical
highByte := lParam & 0xF0
lowByte := lParam & 0x0F
data1 := (lParam >> 8) & 0xFF
data2 := (lParam >> 16) & 0xFF
if (data2 = 0)
return
key := highByte "-" lowByte "-" data1
if (mode = "listen")
{
msgbox % "listen`n" key
return
}
; here get info from ini file if key exist then run its value
msgbox % "run`n" key
return
}