diff --git a/.gitignore b/.gitignore index deb9af1..745c504 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ -*.bak -*.ini +MidiToMacro.ini +/SOURCE +/.vs +games_list.ini +config.ini \ No newline at end of file diff --git a/MidiInAndGuiMonitor.ahk b/MidiInAndGuiMonitor.ahk index 24cb938..d23729b 100644 --- a/MidiInAndGuiMonitor.ahk +++ b/MidiInAndGuiMonitor.ahk @@ -110,6 +110,11 @@ Return ; MIDI Rules dispatcher +LoadModules: + ; Include the ModuleLoader script + #Include ModuleLoader.ahk +Return + MidiRules: if (statusbyte >= 128 and statusbyte <= 159) { ; Note off/on isNoteOn := (statusbyte >= 144 and byte2 > 0) diff --git a/MidiRules.ahk b/MidiRules.ahk index cd8549d..c51c1d2 100644 --- a/MidiRules.ahk +++ b/MidiRules.ahk @@ -1,4 +1,3 @@ - ;************************************************* ;* RULES - MIDI FILTERS ;************************************************* @@ -13,36 +12,94 @@ ProcessNote(device, channel, note, velocity, isNoteOn) { } ProcessCC(device, channel, cc, value) { - if (cc = 21 or cc = 29) { - scaled_value := ConvertCCValueToScale(value, 0, 127) - vol := scaled_value * 100 - SoundSet, vol + + ;************************************************* + ;* Rotary Knob 1 (Master Volume Control) * + ;************************************************* + + if (cc = 21) { + scaled_value := ConvertCCValueToScale(value, 7, 120) + vol := Round(scaled_value * 100) ; Scale to percentage + SoundSet, vol, MASTER + ; Minimizing feedback to optimize speed DisplayOutput("Volume", vol) - } else if (cc = 51) { - Send {Volume_Mute} - DisplayOutput("Volume", "Mute") - } else if (cc = 52 and value != 0) { - Send {Volume_Down} - DisplayOutput("Volume", "Down") - } else if (cc = 53 and value != 0) { - Send {Volume_Up} - DisplayOutput("Volume", "Up") - } else if (cc = 54 and value != 0) { - Send {Media_Play_Pause} - DisplayOutput("Media", "Play/Pause") - } else if (cc = 55 and value != 0) { - Send {Media_Stop} - DisplayOutput("Media", "Stop") - } else if (cc = 56 and value != 0) { - Send {Media_Prev} - DisplayOutput("Media", "Previous") - } else if (cc = 57 and value != 0) { - Send {Media_Next} - DisplayOutput("Media", "Next") - } else if (cc = 58 and value != 0) { - ; Place a cue marker in Sound Forge 9 - ControlSend, , {Alt down}m{Alt up}, ahk_class #32770 - DisplayOutput("Sound Forge", "Place Cue Marker") + } + + if (cc = 0 and value = 1) { + SoundSet, 1, MASTER, MUTE ; Unmute + DisplayOutput("Volume Unmuted", "") + } else if (cc = 0 and value = 0) { + SoundSet, 0, MASTER, MUTE ; Mute + DisplayOutput("Volume Muted", "") + } + + ;************************************************* + ;* Rotary Knob 2 (Mozilla Firefox Volume Control)* + ;************************************************* + + if (cc = 22) { + scaled_value := ConvertCCValueToScale(value, 7, 120) + vol := Round(scaled_value, 2) ; Scale to the range used by nircmd + ; Adjust Firefox volume using nircmd + Run, nircmd setappvolume firefox.exe %vol% + ; Minimizing feedback to optimize speed + scaled_value := ConvertCCValueToScale(vol, 0, 127) + percentage := Round(vol * 100) ; Scale to percentage + DisplayOutput("Firefox Volume", percentage) + } + + if (cc = 1 and value = 1) { + ; Mute Mozilla Firefox + Run, nircmd muteappvolume firefox.exe 1 + DisplayOutput("Firefox Muted", "") + } else if (cc = 1 and value = 0) { + ; Unmute Mozilla Firefox + Run, nircmd muteappvolume firefox.exe 0 + DisplayOutput("Firefox Unmuted", "") + } + + ;************************************************* + ;* Rotary Knob 3 (Games Volume Control) * + ;************************************************* + + if (cc = 23) { + scaled_value := ConvertCCValueToScale(value, 7, 120) + vol := Round(scaled_value, 2) ; Scale to the range used by nircmd + ; Read the list of game executables from the file + FileRead, gameList, game_exe_list.txt + Loop, Parse, gameList, `n, `r + { + if (A_LoopField != "") { + ; Adjust game volume using nircmd + Run, nircmd setappvolume %A_LoopField% %vol% + } + } + ; Minimizing feedback to optimize speed + scaled_value := ConvertCCValueToScale(vol, 0, 127) + percentage := Round(vol * 100) ; Scale to percentage + DisplayOutput("Games Volume", percentage) + } + + if (cc = 2 and value = 1) { + ; Mute all games + FileRead, gameList, game_exe_list.txt + Loop, Parse, gameList, `n, `r + { + if (A_LoopField != "") { + Run, nircmd muteappvolume %A_LoopField% 1 + } + } + DisplayOutput("Games Muted", "") + } else if (cc = 2 and value = 0) { + ; Unmute all games + FileRead, gameList, game_exe_list.txt + Loop, Parse, gameList, `n, `r + { + if (A_LoopField != "") { + Run, nircmd muteappvolume %A_LoopField% 0 + } + } + DisplayOutput("Games Unmuted", "") } } @@ -52,5 +109,4 @@ ProcessPC(device, channel, note, velocity) { ProcessPitchBend(device, channel, value) { -} - +} \ No newline at end of file diff --git a/NirCmd.chm b/NirCmd.chm new file mode 100644 index 0000000..52af31f Binary files /dev/null and b/NirCmd.chm differ diff --git a/Original_README.md b/Original_README.md new file mode 100644 index 0000000..bcb0a6c --- /dev/null +++ b/Original_README.md @@ -0,0 +1,89 @@ +# MidiToMacro + +This is an AutoHotKey script for Windows, to map MIDI input values to hotkeys or macros. + +You can use this script to bind CC messages to media keys (play/pause/next), volume sliders, or unusual keyboard combinations (ctrl+shift+alt+F13) which you can assign in programs like StreamLabs OBS. + +It's cobbled together from scripts found on the AHK forums. It originally supported mapping MIDI inputs to a virtual joystick using vJoy, and to MIDI outputs; this functionality has been removed. All credit goes to the original authors. + +## Running + +Double click on `MidiToMacro.ahk`. + +To launch the program when Windows starts, you can add a shortcut to the file in your Start Menu\Startup folder. + +The first time you launch the script, you will be prompted to choose a MIDI input device. If you need to change it later, you can right click on the system tray icon and click `MidiSet`. Or, you can open the `MidiMon`, and change the input in the "Midi Input" dropdown menu; the script will automatically reload. + +To see a log of recent MIDI input messages and any output events, right click on the system tray icon and click `MidiMon`. You can close this window, and the script will keep running in the background. + +## Adding rules + +You can add rules to the file `MidiRules.ahk`. + +There are four handler functions you can modify: + +- `ProcessNote`: handles note on/off events +- `ProcessCC`: handles CC (Control Change, or Continuous Control) events +- `ProcessPC`: handles patch change events +- `ProcessPitchBend`: handle pitch bend events + +Within each function, you can have a series of `if/else` blocks. + +``` +if (cc = 21) { + ; ... +} else if (cc = 51) { + ; ... +} else if (cc = 52 and value != 0) { + ; ... +} +``` + +A rule to toggle the mute button when receiving CC 51 might look like this: + +``` +if (cc = 51) { + Send {Volume_Mute} + DisplayOutput("Volume", "Mute") +} +``` + +`Send {Volume_Mute}` simulates pressing the "mute" button on your keyboard. `DisplayOutput("Volume", "Mute")` logs a message to the MidiMon GUI. + +A rule to press the play/pause button might look like this: + +``` +if (cc = 54 and value != 0) { + Send {Media_Play_Pause} + DisplayOutput("Media", "Play/Pause") +} +``` + +`value != 0` lets us detect button presses, and ignores button releases, on our MIDI controller. (Without this clause, we'd send the keyboard macro twice; once for the button press, and agin for the button release.) + +Here's a rule to map a continuous control from a slider to the main Windows mixer volume: + +``` +if (cc = 21 or cc = 29) { + scaled_value := ConvertCCValueToScale(value, 0, 127) + vol := scaled_value * 100 + SoundSet, vol + DisplayOutput("Volume", vol) +} +``` + +`ConvertCCValueToScale` is a utility function from `CommonFunctions.ahk`. It converts a value within a give range into a floating point number between 0 and 1. + +Here's a rule to trigger a keyboard shortcut in a specific application; in this example, Sound Forge 9: + +``` +if (cc = 58 and value != 0) { + ; Place a cue marker in Sound Forge 9 + ControlSend, , {Alt down}m{Alt up}, ahk_class #32770 + DisplayOutput("Sound Forge", "Place Cue Marker") +} +``` + +You can use AutoHotKey's "WindowSpy" script to identify windows, or controls within an application, for use with `ahk_class`. + +You can find [a list of standard CC messages online](https://www.midi.org/specifications-old/item/table-3-control-change-messages-data-bytes-2). You could use any control number without a specified control function, including numbers between 20-31, 52-63, and 102-119. But, any control number should work fine. diff --git a/README.md b/README.md index bcb0a6c..00a76e3 100644 --- a/README.md +++ b/README.md @@ -1,89 +1,23 @@ -# MidiToMacro +# MidiToMacro - LaunchKey Mini [Mk3] Fork -This is an AutoHotKey script for Windows, to map MIDI input values to hotkeys or macros. +I made this fork to use my Launchkey Mini [Mk3] as a Stream Deck. -You can use this script to bind CC messages to media keys (play/pause/next), volume sliders, or unusual keyboard combinations (ctrl+shift+alt+F13) which you can assign in programs like StreamLabs OBS. +It is a fork of [laurence-myers/midi-to-macro](https://github.com/laurence-myers/midi-to-macro). It uses Modules to easily enable or disable different controls and is designed to be easily modified via the Config.ini file. -It's cobbled together from scripts found on the AHK forums. It originally supported mapping MIDI inputs to a virtual joystick using vJoy, and to MIDI outputs; this functionality has been removed. All credit goes to the original authors. +## TODO -## Running +Planned Features -Double click on `MidiToMacro.ahk`. +- Modules -To launch the program when Windows starts, you can add a shortcut to the file in your Start Menu\Startup folder. +- Laravel WebUI for modification of Inputs / Outputs -The first time you launch the script, you will be prompted to choose a MIDI input device. If you need to change it later, you can right click on the system tray icon and click `MidiSet`. Or, you can open the `MidiMon`, and change the input in the "Midi Input" dropdown menu; the script will automatically reload. +## Functionality -To see a log of recent MIDI input messages and any output events, right click on the system tray icon and click `MidiMon`. You can close this window, and the script will keep running in the background. +For more info on the functionality, please see the [Original_README](Original_README.md) file. -## Adding rules +### Dependencies -You can add rules to the file `MidiRules.ahk`. +- NirCmd has been added to the project as a dependency. It is included so you will not need to download it. -There are four handler functions you can modify: - -- `ProcessNote`: handles note on/off events -- `ProcessCC`: handles CC (Control Change, or Continuous Control) events -- `ProcessPC`: handles patch change events -- `ProcessPitchBend`: handle pitch bend events - -Within each function, you can have a series of `if/else` blocks. - -``` -if (cc = 21) { - ; ... -} else if (cc = 51) { - ; ... -} else if (cc = 52 and value != 0) { - ; ... -} -``` - -A rule to toggle the mute button when receiving CC 51 might look like this: - -``` -if (cc = 51) { - Send {Volume_Mute} - DisplayOutput("Volume", "Mute") -} -``` - -`Send {Volume_Mute}` simulates pressing the "mute" button on your keyboard. `DisplayOutput("Volume", "Mute")` logs a message to the MidiMon GUI. - -A rule to press the play/pause button might look like this: - -``` -if (cc = 54 and value != 0) { - Send {Media_Play_Pause} - DisplayOutput("Media", "Play/Pause") -} -``` - -`value != 0` lets us detect button presses, and ignores button releases, on our MIDI controller. (Without this clause, we'd send the keyboard macro twice; once for the button press, and agin for the button release.) - -Here's a rule to map a continuous control from a slider to the main Windows mixer volume: - -``` -if (cc = 21 or cc = 29) { - scaled_value := ConvertCCValueToScale(value, 0, 127) - vol := scaled_value * 100 - SoundSet, vol - DisplayOutput("Volume", vol) -} -``` - -`ConvertCCValueToScale` is a utility function from `CommonFunctions.ahk`. It converts a value within a give range into a floating point number between 0 and 1. - -Here's a rule to trigger a keyboard shortcut in a specific application; in this example, Sound Forge 9: - -``` -if (cc = 58 and value != 0) { - ; Place a cue marker in Sound Forge 9 - ControlSend, , {Alt down}m{Alt up}, ahk_class #32770 - DisplayOutput("Sound Forge", "Place Cue Marker") -} -``` - -You can use AutoHotKey's "WindowSpy" script to identify windows, or controls within an application, for use with `ahk_class`. - -You can find [a list of standard CC messages online](https://www.midi.org/specifications-old/item/table-3-control-change-messages-data-bytes-2). You could use any control number without a specified control function, including numbers between 20-31, 52-63, and 102-119. But, any control number should work fine. +- Git Bash is what I used to run .sh files on Windows. I have not tested it with cmd or powershell or any other bash clients. diff --git a/game_exe_list.txt b/game_exe_list.txt new file mode 100644 index 0000000..c9d05c3 --- /dev/null +++ b/game_exe_list.txt @@ -0,0 +1,2 @@ +# This file is looped through and works best with 3 or 4 games in the list or it starts to lag. This comment is also read, so delete it if you want to speed it up by 1 line. +MTGA.exe \ No newline at end of file diff --git a/nircmd.exe b/nircmd.exe new file mode 100644 index 0000000..5d575ff Binary files /dev/null and b/nircmd.exe differ diff --git a/nircmdc.exe b/nircmdc.exe new file mode 100644 index 0000000..847c69a Binary files /dev/null and b/nircmdc.exe differ