Controlling VoiceMeeter Banana with AHK Topic is solved
Controlling VoiceMeeter Banana with AHK
Hello,
I don't know if you're familiar with VoiceMeeter Banana. It's a virtual audio mixer, a kind of alternative to the standard volume mixer in Windows. I say a kind of, because it does far more than just simple volume control.
I want to write a custom user interface for it, but I don't want to rely on mouse coordinates and such. Luckily the program has its own API and I think that this should be possible. I quote:
"VoicemeeterRemote API is given by a simple standard DLL file for Windows to control Voicemeeter Audio Engine by another application, programmed in any language. It allows using voicemeeter functions and take advantage of Voicemeeter features in a client application. For example to make a more adapted user interface to a specific workflow, or to simply make a macro command to set different parameters at once, change the devices configuration and any kind of automation…"
So I guess that the building blocks are there but the problem is that I have no clue how to start putting them together. The program has an official development toolkit, I started reading it, but I almost got a headache, because I have no idea how to apply any of the things there to AutoHotkey. That being said, I don't think that it's that complicated - I believe that it's just my lack of knowledge and understanding.
I know that it's a lot to ask, but would someone here have the time to make a simple example for me how to retrieve and set a VoiceMeeter parameter from AHK? VoiceMeeter has its own possibilities to create macros, but for my needs these are insufficient. Naturally, I'd gladly pay you for the service, because I feel that I have reached my limit with this one.
The official toolkit can be found here:
http://vbaudio.jcedeveloppement.com/for ... ?f=8&t=346
I believe that the native VoiceMeeter scripting method only calls the VoicemeeterRemote API, so if you want to take a quick glance at what the commands do or look like, you can also take a look at the official user manual. That can be found here:
http://www.vb-audio.com/Voicemeeter/Voi ... Manual.pdf
The part on macros begins at page 26.
Now I don't even know how to close this post, because I realize that what I'm asking for goes far beyond the typical, but I'm really clueless how to go about this. I know that the AHK script will use DLL calls or COM objects (possibly both), but that's about it.
Thanks a million and have a nice day.
I don't know if you're familiar with VoiceMeeter Banana. It's a virtual audio mixer, a kind of alternative to the standard volume mixer in Windows. I say a kind of, because it does far more than just simple volume control.
I want to write a custom user interface for it, but I don't want to rely on mouse coordinates and such. Luckily the program has its own API and I think that this should be possible. I quote:
"VoicemeeterRemote API is given by a simple standard DLL file for Windows to control Voicemeeter Audio Engine by another application, programmed in any language. It allows using voicemeeter functions and take advantage of Voicemeeter features in a client application. For example to make a more adapted user interface to a specific workflow, or to simply make a macro command to set different parameters at once, change the devices configuration and any kind of automation…"
So I guess that the building blocks are there but the problem is that I have no clue how to start putting them together. The program has an official development toolkit, I started reading it, but I almost got a headache, because I have no idea how to apply any of the things there to AutoHotkey. That being said, I don't think that it's that complicated - I believe that it's just my lack of knowledge and understanding.
I know that it's a lot to ask, but would someone here have the time to make a simple example for me how to retrieve and set a VoiceMeeter parameter from AHK? VoiceMeeter has its own possibilities to create macros, but for my needs these are insufficient. Naturally, I'd gladly pay you for the service, because I feel that I have reached my limit with this one.
The official toolkit can be found here:
http://vbaudio.jcedeveloppement.com/for ... ?f=8&t=346
I believe that the native VoiceMeeter scripting method only calls the VoicemeeterRemote API, so if you want to take a quick glance at what the commands do or look like, you can also take a look at the official user manual. That can be found here:
http://www.vb-audio.com/Voicemeeter/Voi ... Manual.pdf
The part on macros begins at page 26.
Now I don't even know how to close this post, because I realize that what I'm asking for goes far beyond the typical, but I'm really clueless how to go about this. I know that the AHK script will use DLL calls or COM objects (possibly both), but that's about it.
Thanks a million and have a nice day.
Re: Controlling VoiceMeeter Banana with AHK
That's a really cool application, and definitely worth a look for all the folks out there. Especially the "ducking"-option is interesting for gamers and those who create (tutorial) videos. Hope to see some AHK Code coding gurus fiddeling with its Dll soon. Good luck and thx for sharing it
PS ... and that MIDI and macro stuff ...
PS ... and that MIDI and macro stuff ...
Re: Controlling VoiceMeeter Banana with AHK
Thanks for the reply, BoBo.
It's the coolest app I've seen in a while. It even enables you to stream audio within your home network. The possibilities are almost endless + it's donationware so anyone can have the things it offers in their arsenal.
It's the coolest app I've seen in a while. It even enables you to stream audio within your home network. The possibilities are almost endless + it's donationware so anyone can have the things it offers in their arsenal.
-
- Posts: 22
- Joined: 17 Dec 2017, 11:03
- Contact:
Re: Controlling VoiceMeeter Banana with AHK
I don't know if this is still needed but basically to get data you need to work with pointers to memory locations. To get the memory address you call the VAR with '&' in front like this '&volLvlB0'
I've removed a lot of code for simplifications sake to explain how retrieving data works.
Note that the memory address must be a part of the AHK and not the memory address of the value in the voicemeeter program
Also note that the above is a place to start as it is still not working for me. I can retrieve data but it's not correct, so i think my offset is incorrect and i can't figure out how to fix it, but if you msgbox the statusLvl0 variable it will show you 0 and that, according to voicemeeterremote.h file, is correct like it worked in setting the memory address to the current level
https://github.com/mirror/equalizerapo/ ... erRemote.h
I've removed a lot of code for simplifications sake to explain how retrieving data works.
Code: Select all
global volLvlB0 = -35.0 ;setting 'volLvlB0' to -35.0 just to initialise in memory
...
Volume_Up:: ;react to volume up
readVolLvl() ;call function
volLvlB0 += 1 ;add 1 to volume variable for BUS0 (A1 OUT)
...
adjustVolLvl() ;Function called to adjust the volume
return
Volume_Down:: ;react to volume down
readVolLvl() ;call function
volLvlB0 -= 1 ;subtract 1 from volume variable for BUS0 (A1 OUT)
...
adjustVolLvl() ;Function called to adjust the volume
return
...
readVolLvl(){ ;function to read volume level from memory
statusLvl0 := DllCall(VMR_FUNCTIONS["GetParameterFloat"], "AStr", "Bus[0].Gain", "Ptr", &volLvlB0, "Int") ;calls dll and set passed memory address to current gain/volume note we are passing the memory address as a pointer of volLvlB0 by adding & in front of it
volLvlB0 := NumGet(&volLvlB0, Offset=0, Type = "Float") ;get data from the memory address as float (according to VoicemeeterRemote.h it is a float)
}
Also note that the above is a place to start as it is still not working for me. I can retrieve data but it's not correct, so i think my offset is incorrect and i can't figure out how to fix it, but if you msgbox the statusLvl0 variable it will show you 0 and that, according to voicemeeterremote.h file, is correct like it worked in setting the memory address to the current level
https://github.com/mirror/equalizerapo/ ... erRemote.h
-
- Posts: 22
- Joined: 17 Dec 2017, 11:03
- Contact:
Re: Controlling VoiceMeeter Banana with AHK
in case any one knows how to fix this I would be very happy It doesn't retrieve the correct data and i have no clue why.
Full script
Full script
Code: Select all
;#NoTrayIcon
#Persistent
#SingleInstance force
#NoEnv ; Recommended for performance and compatibility with future AutoHotkey releases.
;#Warn ; Recommended for catching common errors.
;#InstallKeybdHook ;https://autohotkey.com/docs/commands/_InstallKeybdHook.htm
#MaxHotkeysPerInterval 99000000
#HotkeyInterval 99000000
#KeyHistory 0
#UseHook
ListLines Off
Process, Priority, , H
SetBatchLines, -1
SetKeyDelay, -1, -1
SetMouseDelay, -1
SetDefaultMouseSpeed, 0
SetWinDelay, -1
SetControlDelay, -1
SendMode Input ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir%
SetTitleMatchMode, 2
SetTitleMatchMode, Fast
OnExit("cleanup_before_exit")
global volLvlB0 = -35.0
global volLvlB1 = -35.0
global volLvlB2 = -35.0
global VMR_FUNCTIONS := {}
global VMR_DLL_DRIVE := "C:"
global VMR_DLL_DIRPATH := "Program Files (x86)\VB\Voicemeeter"
global VMR_DLL_FILENAME_32 := "VoicemeeterRemote.dll"
global VMR_DLL_FILENAME_64 := "VoicemeeterRemote64.dll"
global VMR_DLL_FULL_PATH := VMR_DLL_DRIVE . "\" . VMR_DLL_DIRPATH . "\"
Sleep, 500
if (A_Is64bitOS) {
VMR_DLL_FULL_PATH .= VMR_DLL_FILENAME_64
} else {
VMR_DLL_FULL_PATH .= VMR_DLL_FILENAME_32
}
; == START OF EXECUTION ==
; ========================
; Load the VoicemeeterRemote DLL:
; This returns a module handle
global VMR_MODULE := DllCall("LoadLibrary", "Str", VMR_DLL_FULL_PATH, "Ptr")
if (ErrorLevel || VMR_MODULE == 0)
die("Attempt to load VoiceMeeter Remote DLL failed.")
; Populate VMR_FUNCTIONS
add_vmr_function("Login")
add_vmr_function("Logout")
add_vmr_function("RunVoicemeeter")
add_vmr_function("SetParameterFloat")
add_vmr_function("GetParameterFloat")
add_vmr_function("GetVoicemeeterVersion")
; "Login" to Voicemeeter, by calling the function in the DLL named 'VBVMR_Login()'...
login_result := DllCall(VMR_FUNCTIONS["Login"], "Int")
if (ErrorLevel || login_result < 0)
die("VoiceMeeter Remote login failed.")
; If the login returns 1, that apparently means that Voicemeeter isn't running,
; so we start it; pass 1 to run Voicemeeter, or 2 for Voicemeeter Banana:
if (login_result == 1) {
DllCall(VMR_FUNCTIONS["RunVoicemeeter"], "Int", 2, "Int")
if (ErrorLevel)
die("Attempt to run VoiceMeeter failed.")
Sleep 2000
}
spoOpen := 0
; == HOTKEYS ==
; =============
loop{ ;used to change audio out mode when spotify opens
WinGetTitle, spotifyname, ahk_class SpotifyMainWindow
if(spotifyname != "" && spoOpen = 0){
DllCall(VMR_FUNCTIONS["SetParameterFloat"], "AStr", "Bus[0].mode.Repeat", "Float", 1.0, "Int")
spoOpen := 1
} else if (spoOpen = 1 && spotifyname = ""){
DllCall(VMR_FUNCTIONS["SetParameterFloat"], "AStr", "Bus[0].mode.Normal", "Float", 1.0, "Int")
spoOpen := 0
} else {
;do nothing
}
Sleep, 5000
ToolTip
}
Volume_Up::
readVolLvl()
volLvlB0 += 1
volLvlB1 += 1
volLvlB2 += 1
adjustVolLvl()
return
Volume_Down::
readVolLvl()
volLvlB0 -= 1
volLvlB1 -= 1
volLvlB2 -= 1
adjustVolLvl()
return
; == Functions ==
; ===============
;DllCall(VMR_FUNCTIONS["SetParameterFloat"], "AStr", "Strip[0].B1", "Float", 1.0, "Int")
readVolLvl(){
DllCall(VMR_FUNCTIONS["GetParameterFloat"], "AStr", "Bus[0].Gain", "Ptr", &volLvlB0, "Int")
DllCall(VMR_FUNCTIONS["GetParameterFloat"], "AStr", "Bus[1].Gain", "Ptr", &volLvlB1, "Int")
DllCall(VMR_FUNCTIONS["GetParameterFloat"], "AStr", "Bus[2].Gain", "Ptr", &volLvlB2, "Int")
volLvlB0 := NumGet(volLvlB0, "Float")
volLvlB1 := NumGet(volLvlB1, "Float")
volLvlB2 := NumGet(volLvlB2, "Float")
}
adjustVolLvl() {
if (volLvlB0 > 12.0){
volLvlB0 = 12.0
} else if (volLvlB0 < -60.0) {
volLvlB0 = -60.0
}
if (volLvlB1 > 12.0){
volLvlB1 = 12.0
} else if (volLvlB1 < -60.0) {
volLvlB1 = -60.0
}
if (volLvlB2 > 12.0){
volLvlB2 = 12.0
} else if (volLvlB2 < -60.0) {
volLvlB2 = -60.0
}
DllCall(VMR_FUNCTIONS["SetParameterFloat"], "AStr", "Bus[0].Gain", "Float", volLvlB0, "Int")
DllCall(VMR_FUNCTIONS["SetParameterFloat"], "AStr", "Bus[1].Gain", "Float", volLvlB1, "Int")
DllCall(VMR_FUNCTIONS["SetParameterFloat"], "AStr", "Bus[2].Gain", "Float", volLvlB2, "Int")
}
add_vmr_function(func_name) {
VMR_FUNCTIONS[func_name] := DllCall("GetProcAddress", "Ptr", VMR_MODULE, "AStr", "VBVMR_" . func_name, "Ptr")
if (ErrorLevel || VMR_FUNCTIONS[func_name] == 0)
die("Failed to register VMR function " . func_name . ".")
}
cleanup_before_exit(exit_reason, exit_code) {
DllCall(VMR_FUNCTIONS["Logout"], "Int")
; OnExit functions must return 0 to allow the app to exit.
return 0
}
die(die_string:="UNSPECIFIED FATAL ERROR.", exit_status:=254) {
MsgBox 16, FATAL ERROR, %die_string%
ExitApp exit_status
}
FHex( int, pad=8 ) { ; Function by [VxE]. Formats an integer (decimals are truncated) as hex.
; "Pad" may be the minimum number of digits that should appear on the right of the "0x".
Static hx := "0123456789ABCDEF"
If !( 0 < int |= 0 )
Return !int ? "0x0" : "-" FHex( -int, pad )
s := 1 + Floor( Ln( int ) / Ln( 16 ) )
h := SubStr( "0x0000000000000000", 1, pad := pad < s ? s + 2 : pad < 16 ? pad + 2 : 18 )
u := A_IsUnicode = 1
Loop % s
NumPut( *( &hx + ( ( int & 15 ) << u ) ), h, pad - A_Index << u, "UChar" ), int >>= 4
Return h
}
-
- Posts: 2
- Joined: 02 Jul 2015, 14:06
- Contact:
Re: Controlling VoiceMeeter Banana with AHK Topic is solved
I know this is almost a year later but I got it working (mostly) fine for me with the code below. I included the full code in case anyone else wants to give it a go. I couldn't find any other instances of it working for anyone else according to google. These were the two major changes that made it work.
-After reading the AHK docs for DllCall ( who'd of thought) I added NumGet before the DllCall and it started giving me the correct value.
-Per BNK3R Boy here I added isParametersDirty. For some reason the display can get off from what the number actually is and this seems to remedy it for the most part. If you're having issues I recommend to start debugging here.
I also made the GetParametersFloat function a bit more stand alone so you can use it to get almost any parameter.
On mine I set a hotkey to cycle through the audio sources and set the location in currentAudio.
If you're wanting to add a GUI and display a parameter from Voicemeeter I recommend separating the isParametersDirty and having it on its own thread per the Voicemeeter docs.
-After reading the AHK docs for DllCall ( who'd of thought) I added NumGet before the DllCall and it started giving me the correct value.
-Per BNK3R Boy here I added isParametersDirty. For some reason the display can get off from what the number actually is and this seems to remedy it for the most part. If you're having issues I recommend to start debugging here.
I also made the GetParametersFloat function a bit more stand alone so you can use it to get almost any parameter.
On mine I set a hotkey to cycle through the audio sources and set the location in currentAudio.
If you're wanting to add a GUI and display a parameter from Voicemeeter I recommend separating the isParametersDirty and having it on its own thread per the Voicemeeter docs.
Code: Select all
[Codebox=autohotkey file=Untitled.ahk][/Codebox]
;~ #NoTrayIcon
#Persistent
#SingleInstance force
#NoEnv ; Recommended for performance and compatibility with future AutoHotkey releases.
#Warn ; Recommended for catching common errors.
#MaxHotkeysPerInterval 99000000
#HotkeyInterval 99000000
#KeyHistory 0
#UseHook
ListLines Off
SendMode Input ; Recommended for new scripts due to its superior speed and reliability.
SetTitleMatchMode RegEx
StringCaseSense Off
SetWorkingDir %A_ScriptDir% ; Ensures a consistent starting directory.
OnExit("cleanup_before_exit")
SetFormat, Float, 0.3
global currentAudio := "Bus[0]"
global VMR_FUNCTIONS := {}
global VMR_DLL_DRIVE := "C:"
global VMR_DLL_DIRPATH := "Program Files (x86)\VB\Voicemeeter"
global VMR_DLL_FILENAME_32 := "VoicemeeterRemote.dll"
global VMR_DLL_FILENAME_64 := "VoicemeeterRemote64.dll"
global VMR_DLL_FULL_PATH := VMR_DLL_DRIVE . "\" . VMR_DLL_DIRPATH . "\"
Sleep, 500
if (A_Is64bitOS) {
VMR_DLL_FULL_PATH .= VMR_DLL_FILENAME_64
} else {
VMR_DLL_FULL_PATH .= VMR_DLL_FILENAME_32
}
; == START OF EXECUTION ==
; ========================
; Load the VoicemeeterRemote DLL:
; This returns a module handle
global VMR_MODULE := DllCall("LoadLibrary", "Str", VMR_DLL_FULL_PATH, "Ptr")
if (ErrorLevel || VMR_MODULE == 0)
die("Attempt to load VoiceMeeter Remote DLL failed.")
; Populate VMR_FUNCTIONS
add_vmr_function("Login")
add_vmr_function("Logout")
add_vmr_function("RunVoicemeeter")
add_vmr_function("SetParameterFloat")
add_vmr_function("GetParameterFloat")
add_vmr_function("IsParametersDirty")
; "Login" to Voicemeeter, by calling the function in the DLL named 'VBVMR_Login()'...
login_result := DllCall(VMR_FUNCTIONS["Login"], "Int")
if (ErrorLevel || login_result < 0)
die("VoiceMeeter Remote login failed.")
; If the login returns 1, that apparently means that Voicemeeter isn't running,
; so we start it; pass 1 to run Voicemeeter, or 2 for Voicemeeter Banana:
if (login_result == 1) {
DllCall(VMR_FUNCTIONS["RunVoicemeeter"], "Int", 2, "Int")
if (ErrorLevel)
die("Attempt to run VoiceMeeter failed.")
Sleep 2000
}
; == HOTKEYS ==
; =============
;add your code here
Media_Play_Pause::
if (currentAudio = "Bus[0]")
currentAudio := "Bus[1]"
else if (currentAudio = "Bus[1]")
currentAudio := "Strip[0]"
else if (currentAudio = "Strip[0]")
currentAudio := "Strip[1]"
else
currentAudio := "Bus[0]"
return
Volume_Up::
cLvl := readParam(currentAudio . ".Gain")
if (cLvl != ""){
cLvl += 1
adjustVolLvl(currentAudio . ".Gain", cLvl)
}
return
Volume_Down::
cLvl := readParam(currentAudio . ".Gain")
if (cLvl != ""){
cLvl -= 1
adjustVolLvl(currentAudio . ".Gain", cLvl)
}
return
Volume_Mute::
cM := readParam(currentAudio . ".Mute")
if (cM != "")
adjustMute(currentAudio . ".Mute", cM)
return
; == Functions ==
; ===============
readParam(loc){
Loop
{
pDirty := DLLCall(VMR_FUNCTIONS["IsParametersDirty"]) ;Check if parameters have changed.
if (pDirty==0) ;0 = no new paramters.
break
else if (pDirty<0) ;-1 = error, -2 = no server
return ""
else ;1 = New parameters -> update your display. (this only applies if YOU have a display, couldn't find any code to update VM display which can get off sometimes)
if A_Index > 200
return ""
sleep, 20
}
tParamVal := 0.0
NumPut(0.0, tParamVal, 0, "Float")
statusLvl := DllCall(VMR_FUNCTIONS["GetParameterFloat"], "AStr", loc, "Ptr", &tParamVal, "Int")
tParamVal := NumGet(tParamVal, 0, "Float")
if (statusLvl < 0)
return ""
else
return tParamVal
}
adjustVolLvl(loc, tVol) {
if (tVol > 12.0)
tVol = 12.0
else if (tVol < -60.0)
tVol = -60.0
DllCall(VMR_FUNCTIONS["SetParameterFloat"], "AStr", loc, "Float", tVol, "Int")
}
adjustMute(loc, tM) {
if (tM = 0)
tM = 1
else
tM = 0
DllCall(VMR_FUNCTIONS["SetParameterFloat"], "AStr", loc, "Float", tM, "Int")
}
add_vmr_function(func_name) {
VMR_FUNCTIONS[func_name] := DllCall("GetProcAddress", "Ptr", VMR_MODULE, "AStr", "VBVMR_" . func_name, "Ptr")
if (ErrorLevel || VMR_FUNCTIONS[func_name] == 0)
die("Failed to register VMR function " . func_name . ".")
}
cleanup_before_exit(exit_reason, exit_code) {
DllCall(VMR_FUNCTIONS["Logout"], "Int")
; OnExit functions must return 0 to allow the app to exit.
return 0
}
die(die_string:="UNSPECIFIED FATAL ERROR.", exit_status:=254) {
MsgBox 16, FATAL ERROR, %die_string%
ExitApp exit_status
}
Re: Controlling VoiceMeeter Banana with AHK
Thank you, Soscan2062,
I haven't tested your code yet, but it looks good and so I've accepted your answer. I'll definitively give it a go at some point. Honestly, some of those things still look somewhat advanced to me, but I think that I'll manage to get it to work - especially with the comments you have included.
BTW, a new version of VoiceMeeter called Potato will soon be released (perhaps it's out already). I hope that the developer hasn't changed the way the DLL works...
I haven't tested your code yet, but it looks good and so I've accepted your answer. I'll definitively give it a go at some point. Honestly, some of those things still look somewhat advanced to me, but I think that I'll manage to get it to work - especially with the comments you have included.
BTW, a new version of VoiceMeeter called Potato will soon be released (perhaps it's out already). I hope that the developer hasn't changed the way the DLL works...
Re: Controlling VoiceMeeter Banana with AHK
I hope my 2 lines code was helpful.Soscan2062 wrote: ↑25 Nov 2018, 13:18I know this is almost a year later but I got it working (mostly) fine for me with the code below. I included the full code in case anyone else wants to give it a go. I couldn't find any other instances of it working for anyone else according to google. These were the two major changes that made it work.
-After reading the AHK docs for DllCall ( who'd of thought) I added NumGet before the DllCall and it started giving me the correct value.
-Per BNK3R Boy here I added isParametersDirty. For some reason the display can get off from what the number actually is and this seems to remedy it for the most part. If you're having issues I recommend to start debugging here.
I also made the GetParametersFloat function a bit more stand alone so you can use it to get almost any parameter.
On mine I set a hotkey to cycle through the audio sources and set the location in currentAudio.
If you're wanting to add a GUI and display a parameter from Voicemeeter I recommend separating the isParametersDirty and having it on its own thread per the Voicemeeter docs.Code: Select all
[Codebox=autohotkey file=Untitled.ahk][/Codebox] ;~ #NoTrayIcon #Persistent #SingleInstance force #NoEnv ; Recommended for performance and compatibility with future AutoHotkey releases. #Warn ; Recommended for catching common errors. #MaxHotkeysPerInterval 99000000 #HotkeyInterval 99000000 #KeyHistory 0 #UseHook ListLines Off SendMode Input ; Recommended for new scripts due to its superior speed and reliability. SetTitleMatchMode RegEx StringCaseSense Off SetWorkingDir %A_ScriptDir% ; Ensures a consistent starting directory. OnExit("cleanup_before_exit") SetFormat, Float, 0.3 global currentAudio := "Bus[0]" global VMR_FUNCTIONS := {} global VMR_DLL_DRIVE := "C:" global VMR_DLL_DIRPATH := "Program Files (x86)\VB\Voicemeeter" global VMR_DLL_FILENAME_32 := "VoicemeeterRemote.dll" global VMR_DLL_FILENAME_64 := "VoicemeeterRemote64.dll" global VMR_DLL_FULL_PATH := VMR_DLL_DRIVE . "\" . VMR_DLL_DIRPATH . "\" Sleep, 500 if (A_Is64bitOS) { VMR_DLL_FULL_PATH .= VMR_DLL_FILENAME_64 } else { VMR_DLL_FULL_PATH .= VMR_DLL_FILENAME_32 } ; == START OF EXECUTION == ; ======================== ; Load the VoicemeeterRemote DLL: ; This returns a module handle global VMR_MODULE := DllCall("LoadLibrary", "Str", VMR_DLL_FULL_PATH, "Ptr") if (ErrorLevel || VMR_MODULE == 0) die("Attempt to load VoiceMeeter Remote DLL failed.") ; Populate VMR_FUNCTIONS add_vmr_function("Login") add_vmr_function("Logout") add_vmr_function("RunVoicemeeter") add_vmr_function("SetParameterFloat") add_vmr_function("GetParameterFloat") add_vmr_function("IsParametersDirty") ; "Login" to Voicemeeter, by calling the function in the DLL named 'VBVMR_Login()'... login_result := DllCall(VMR_FUNCTIONS["Login"], "Int") if (ErrorLevel || login_result < 0) die("VoiceMeeter Remote login failed.") ; If the login returns 1, that apparently means that Voicemeeter isn't running, ; so we start it; pass 1 to run Voicemeeter, or 2 for Voicemeeter Banana: if (login_result == 1) { DllCall(VMR_FUNCTIONS["RunVoicemeeter"], "Int", 2, "Int") if (ErrorLevel) die("Attempt to run VoiceMeeter failed.") Sleep 2000 } ; == HOTKEYS == ; ============= ;add your code here Media_Play_Pause:: if (currentAudio = "Bus[0]") currentAudio := "Bus[1]" else if (currentAudio = "Bus[1]") currentAudio := "Strip[0]" else if (currentAudio = "Strip[0]") currentAudio := "Strip[1]" else currentAudio := "Bus[0]" return Volume_Up:: cLvl := readParam(currentAudio . ".Gain") if (cLvl != ""){ cLvl += 1 adjustVolLvl(currentAudio . ".Gain", cLvl) } return Volume_Down:: cLvl := readParam(currentAudio . ".Gain") if (cLvl != ""){ cLvl -= 1 adjustVolLvl(currentAudio . ".Gain", cLvl) } return Volume_Mute:: cM := readParam(currentAudio . ".Mute") if (cM != "") adjustMute(currentAudio . ".Mute", cM) return ; == Functions == ; =============== readParam(loc){ Loop { pDirty := DLLCall(VMR_FUNCTIONS["IsParametersDirty"]) ;Check if parameters have changed. if (pDirty==0) ;0 = no new paramters. break else if (pDirty<0) ;-1 = error, -2 = no server return "" else ;1 = New parameters -> update your display. (this only applies if YOU have a display, couldn't find any code to update VM display which can get off sometimes) if A_Index > 200 return "" sleep, 20 } tParamVal := 0.0 NumPut(0.0, tParamVal, 0, "Float") statusLvl := DllCall(VMR_FUNCTIONS["GetParameterFloat"], "AStr", loc, "Ptr", &tParamVal, "Int") tParamVal := NumGet(tParamVal, 0, "Float") if (statusLvl < 0) return "" else return tParamVal } adjustVolLvl(loc, tVol) { if (tVol > 12.0) tVol = 12.0 else if (tVol < -60.0) tVol = -60.0 DllCall(VMR_FUNCTIONS["SetParameterFloat"], "AStr", loc, "Float", tVol, "Int") } adjustMute(loc, tM) { if (tM = 0) tM = 1 else tM = 0 DllCall(VMR_FUNCTIONS["SetParameterFloat"], "AStr", loc, "Float", tM, "Int") } add_vmr_function(func_name) { VMR_FUNCTIONS[func_name] := DllCall("GetProcAddress", "Ptr", VMR_MODULE, "AStr", "VBVMR_" . func_name, "Ptr") if (ErrorLevel || VMR_FUNCTIONS[func_name] == 0) die("Failed to register VMR function " . func_name . ".") } cleanup_before_exit(exit_reason, exit_code) { DllCall(VMR_FUNCTIONS["Logout"], "Int") ; OnExit functions must return 0 to allow the app to exit. return 0 } die(die_string:="UNSPECIFIED FATAL ERROR.", exit_status:=254) { MsgBox 16, FATAL ERROR, %die_string% ExitApp exit_status }
here some lines from another project to clear values.
Code: Select all
fncvmrv:=Func("clear_vmr_variable")
SetTimer, %fncvmrv%, 5
clear_vmr_variable() {
DLLCall(VMR_FUNCTIONS["IsParametersDirty"])
}
Ich dreh doch schon am Rad!
-
- Posts: 1
- Joined: 24 Oct 2020, 21:17
Re: Controlling VoiceMeeter Banana with AHK
Alright, so it's been 2 years since the last reply. I, along with probably many others, came across this thread while learning to control VoiceMeeter with AutoHotkey. I've learned quite a bit about the VoiceMeeterRemoteAPI, including the magic "Is Parameter Dirty".
Specifically, it's not some magical function that you call once. What it does is return a True / False boolean based on whether or not VoiceMeeter is ready to send you updated data. MEANING, you don't just call it and forget it.
Instead, you call it before trying to query anything from VoiceMeeter. For example, if you have a Volume Up / Down hotkey, but then go change the volume manually in the VoiceMeeter App, you should query the actual volume first, before applying any changes.
It turns out that VoiceMeeter takes some amount of time (more than 0 seconds) to actually change from Dirty to Not Dirty. So you cannot simply say "if not dirty, get volume". Instead, you must loop, repeatedly asking if Dirty, sleeping some amount of time, and asking again. Then break out or return once it's NOT Dirty to finally go Get the volume from VoiceMeeter.
Anyways, that understanding, as well as some other scripts I took inspiration from, has led me to build the following script. I've added a TON of comments, explaining what the hell is going on and why.
There's only a little bit of extra, as I like using CapsLock as my modifier so that it does not conflict with Ctrl and Alt when using Adobe Illustrator & Premiere, but also passes through to Games where I used it for PushToTalk. I also use Vim a lot, where I want CapsLock to be Esc temporarily when in Vim. I also have remapped my mouse keys to Numpad using Logitech and so my hotkeys show numpads. But that's all explained in the comments.
All of the startup is at the top. Utility functions in the middle, CapsLock stuff below, and Hotkey assignments and control logic at the bottom.
Read the script, read all the comments, and let me know here if it's of any help.
I spent a long time formatting. Hope it helps!
Specifically, it's not some magical function that you call once. What it does is return a True / False boolean based on whether or not VoiceMeeter is ready to send you updated data. MEANING, you don't just call it and forget it.
Instead, you call it before trying to query anything from VoiceMeeter. For example, if you have a Volume Up / Down hotkey, but then go change the volume manually in the VoiceMeeter App, you should query the actual volume first, before applying any changes.
It turns out that VoiceMeeter takes some amount of time (more than 0 seconds) to actually change from Dirty to Not Dirty. So you cannot simply say "if not dirty, get volume". Instead, you must loop, repeatedly asking if Dirty, sleeping some amount of time, and asking again. Then break out or return once it's NOT Dirty to finally go Get the volume from VoiceMeeter.
Anyways, that understanding, as well as some other scripts I took inspiration from, has led me to build the following script. I've added a TON of comments, explaining what the hell is going on and why.
There's only a little bit of extra, as I like using CapsLock as my modifier so that it does not conflict with Ctrl and Alt when using Adobe Illustrator & Premiere, but also passes through to Games where I used it for PushToTalk. I also use Vim a lot, where I want CapsLock to be Esc temporarily when in Vim. I also have remapped my mouse keys to Numpad using Logitech and so my hotkeys show numpads. But that's all explained in the comments.
All of the startup is at the top. Utility functions in the middle, CapsLock stuff below, and Hotkey assignments and control logic at the bottom.
Read the script, read all the comments, and let me know here if it's of any help.
I spent a long time formatting. Hope it helps!
Code: Select all
;=========================================================================================
; Env Setup
;=========================================================================================
#NoEnv ; Recommended for performance and compatibility with future AutoHotkey releases.
; #Warn ; Enable warnings to assist with detecting common errors.
SendMode Input ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir% ; Ensures a consistent starting directory.
#SingleInstance Force ; Determines whether a script is allowed to run again when it is already running. "Force" skips the dialog box and replaces the old instance automatically
#MaxHotkeysPerInterval 200 ; Allow more hotkeys in rapid succession... helps with the scroll wheel
DetectHiddenWindows, On ; Allow detecting Windows that are minimized to Tray (such as Voicemeeterpro.exe)
;=========================================================================================
; VoiceMeeter Remote API Connection & Variable Initialization
;=========================================================================================
global voicemeeter_open := 0 ; Try to track the status of the VoiceMeeter Window
; Might get out of sync if you manually Minimize or Show the VoiceMeeter window, so just hit Show/Hide hotkey toggle twice to fix
OpenVoicemeeter() ; Call OpenVoicemeeter function to ensure Voicemeeter is running before the rest of the script executes
; Will launch if closed, or bring to foreground if already running
; Either way, the DllLoad should always work because Voicemeeter will be running
WinWait, ahk_exe voicemeeterpro.exe ; Wait for voicemeeter
; Should be opening one way or another because of OpenVoicemeeter()
DllLoad := DllCall("LoadLibrary", "Str", "C:\Program Files (x86)\VB\Voicemeeter\VoicemeeterRemote64.dll") ; Set this to your VoiceMeeter install directory
VMLogin() ; Connect to VoiceMeeter
OnExit("VMLogout") ; When script exists, disconnect from VoiceMeeter
; Set Initial State
ApplyVolume(0.0) ; Output volume to 0.0 (VoiceMeeter's default, does NOT mean Mute)
UnMuteVolume() ; Make sure it's not Muted
SetSpeakersOutput() ; Select the Speakers as output, NOT Headphones, assuming you configured VoiceMeeter to have Speakers on A1 and Headphones on A2. Change to however you like
;=========================================================================================
; Utility Functions
;=========================================================================================
VMLogin() {
Login := DllCall("VoicemeeterRemote64\VBVMR_Login")
}
VMLogout() {
Logout := DllCall("VoicemeeterRemote64\VBVMR_Logout")
}
OpenVoicemeeter() {
IfWinExist ahk_exe voicemeeterpro.exe ; If VoiceMeeter is already running, bring it up from the Tray and bring it to Foreground
{
WinShow ahk_exe voicemeeterpro.exe
WinActivate ahk_exe voicemeeterpro.exe ; Sometimes WinShow does not bring it in front of say, Spotify. So running WinActivate right after gives it focus and brings it all the way to the foreground
}
else ; If VoiceMeeter is NOT running, run the .exe, wait for it to launch, then show and bring foreground
{
Run C:\Program Files (x86)\VB\Voicemeeter\voicemeeterpro.exe ; Set this to your VoiceMeeter install directory
WinWait ahk_exe voicemeeterpro.exe
WinShow ahk_exe voicemeeterpro.exe
WinActivate ahk_exe voicemeeterpro.exe ; Sometimes WinShow does not bring it in front of say, Spotify. So running WinActivate right after gives it focus and brings it all the way to the foreground
}
voicemeeter_open := 1 ; Set state of VoiceMeeter for toggling between Open and Closed
}
HideVoicemeeter() {
WinHide ahk_exe voicemeeterpro.exe ; Hide VoiceMeeter back into the Tray. This assumes you configured VoiceMeeter to "minimize to tray" rather than taskbar in it's settings
voicemeeter_open := 0 ; Set state of VoiceMeeter for toggling between Open and Closed
}
WaitForNotDirty() {
Loop
{
Dirty := DllCall("VoicemeeterRemote64\VBVMR_IsParametersDirty") ; Check to see if VoiceMeeter says that parameters have changed elsewhere
if (Dirty != 0) ; This would happen if you changed something in the app directly
sleep, 20 ; It can stay dirty for a decent amount of time, in computer terms. Like a few hundred milliseconds
else ; If it is still Dirty, wait a moment and check again
return 1 ; If it is NOT Dirty, Return 1 (True)
}
}
GetVolume() {
WaitForNotDirty() ; Make sure the VoiceMeeter parameters are not dirty before querying anything
vm_volume := 0.0 ; Initialize variable
NumPut(0.0, vm_volume, 0, "Float") ; Force it to be a float
; The POINTER to the variable vm_volume is being sent to the Dll
Result := DllCall("VoicemeeterRemote64\VBVMR_GetParameterFloat", "AStr", "Bus[0].Gain", "Ptr", &vm_volume, "Int") ; The "Result" is just a Success or Error Code
; The actual "volume" of Speaker Channel (A1) is shoved into the memory address associated with the POINTER
vm_volume := NumGet(vm_volume, 0, "Float") ; Make sure the value of that variable is a Float after the Dll ; Pointers are passed using the ' & ' before a variable
vm_volume := Round(vm_volume) ; Round the float so it's nicer to use later
return vm_volume
}
ApplyVolume(vol_lvl) {
if (vol_lvl > 12.0) { ; If the volume is trying to go above 12.0, set it back to 12.0 as a Max
vol_lvl := 12.0
} else if (vol_lvl < -60.0) { ; If the volume is trying to go below -60.0, set it back to -60.0 as the Min
vol_lvl := -60.0
}
Result := DllCall("VoicemeeterRemote64\VBVMR_SetParameterFloat", "AStr", "Bus[0].Gain", "Float", vol_lvl) ; Set the Speakers to vol_lvl
Result := DllCall("VoicemeeterRemote64\VBVMR_SetParameterFloat", "AStr", "Bus[1].Gain", "Float", vol_lvl) ; Set the Headphones to vol_lvl
}
GetIsMuted() {
WaitForNotDirty() ; Make sure the VoiceMeeter parameters are not dirty before querying anything
is_muted := 0.0 ; Initialize variable
NumPut(0.0, is_muted, 0, "Float") ; Force it to be a float
; The POINTER to the variable is_muted is being sent to the Dll
Result := DllCall("VoicemeeterRemote64\VBVMR_GetParameterFloat", "AStr", "Bus[0].Mute", "Ptr", &is_muted, "Int") ; The "Result" is just a Success or Error Code
; The actual "muted state" of Speaker Channel (A1) is shoved into the memory address associated with the POINTER
is_muted := NumGet(is_muted, 0, "Float") ; Make sure the value of that variable is a Float after the Dll ; Pointers are passed using the ' & ' before a variable
return is_muted ; For some reason, not doing this makes the variable unusable
}
MuteVolume() {
Result := DllCall("VoicemeeterRemote64\VBVMR_SetParameterFloat", "AStr", "Bus[0].Mute", "Float", 1.0) ; Sets Speaker Mute button to On
Result := DllCall("VoicemeeterRemote64\VBVMR_SetParameterFloat", "AStr", "Bus[1].Mute", "Float", 1.0) ; Sets Headphone Mute button to On
}
UnMuteVolume() {
Result := DllCall("VoicemeeterRemote64\VBVMR_SetParameterFloat", "AStr", "Bus[0].Mute", "Float", 0.0) ; Sets Speaker Mute button to Off
Result := DllCall("VoicemeeterRemote64\VBVMR_SetParameterFloat", "AStr", "Bus[1].Mute", "Float", 0.0) ; Sets Headphone Mute button to Off
}
GetCurrentOutput() {
WaitForNotDirty() ; Make sure the VoiceMeeter parameters are not dirty before querying anything
a1_active := 0.0 ; Initialize variable
NumPut(0.0, a1_active, 0, "Float") ; Force it to be a float
; The POINTER to the variable is_muted is being sent to the Dll
Result := DllCall("VoicemeeterRemote64\VBVMR_GetParameterFloat", "AStr", "Strip[3].A1", "Ptr", &a1_active, "Int") ; The "Result" is just a Success or Error Code
; The actual "muted state" of Speaker Channel (A1) is shoved into the memory address associated with the POINTER
a1_active := NumGet(a1_active, 0, "Float") ; Make sure the value of that variable is a Float after the Dll ; Pointers are passed using the ' & ' before a variable
return a1_active ; For some reason, not doing this makes the variable unusable
}
SetSpeakersOutput() {
Result := DllCall("VoicemeeterRemote64\VBVMR_SetParameterFloat", "AStr", "Strip[3].A1", "Float", 1.0) ; Sets Output Channel A1 to On (Speakers On)
Result := DllCall("VoicemeeterRemote64\VBVMR_SetParameterFloat", "AStr", "Strip[3].A2", "Float", 0.0) ; Sets Output Channel A2 to Off (Headphones Off)
}
SetHeadphonesOutput() {
Result := DllCall("VoicemeeterRemote64\VBVMR_SetParameterFloat", "AStr", "Strip[3].A1", "Float", 0.0) ; Sets Output Channel A1 to Off (Speakers Off)
Result := DllCall("VoicemeeterRemote64\VBVMR_SetParameterFloat", "AStr", "Strip[3].A2", "Float", 1.0) ; Sets Output Channel A2 to On (Headphones On)
}
;=========================================================================================
; CapsLock Configuration
;=========================================================================================
SetCapsLockState, AlwaysOff ; Keep CapsLock OFF, even when it is pressed and/or used in other hotkey combinations
~CapsLock::RAlt ; Reassign CapsLock to Right Alt for use in games
; It's a key that doesn't conflict with other apps, but can still be detected in games as a separate key from normal Left Alt
; Shows up as RAlt or sometimes Alt Gr
; We also don't want this to be Esc all the time (such as for Vim), because Esc is not a mappable key in games
; This is contextually switched to Esc in the next section
~CapsLock & Esc:: ; Override the override, explicitly toggle CapsLock On or back Off when CapsLock & Esc are pressed together
GetKeyState, CapsLockState, CapsLock, T
if CapsLockState = D
SetCapsLockState, AlwaysOff
else
SetCapsLockState, AlwaysOn
return
;=========================================================================================
; Vim Context CapsLock -> Esc
;=========================================================================================
#IfWinActive ahk_class Vim ; When Vim window is active
~CapsLock::Esc ; Map CapsLock to Esc
; Tilde means the CapsLock is still passed through to Windows, but it is eaten by "SetCapsLockState, Always Off"
; More importantly, it also makes the hotkey trigger immediately, rather than on "key UP", which means that Esc fires the moment CapsLock is pressed DOWN
#IfWinActive ; Snap out of Vim window scope stuff, go back to normal scope for the rest of the script
;=========================================================================================
; Volume VoiceMeeter Control
;=========================================================================================
; Numpads are used in some places because that's what I've bound my mouse buttons to using Logitech software
; Volume Up
~CapsLock & WheelUp:: ; Get volume from VoiceMeeter, in case it's been changed directly
ApplyVolume(GetVolume() + 2.0) ; Increase the volume in steps of 2, more reasonable when using scroll wheel
return
; Volume Down
~CapsLock & WheelDown::
ApplyVolume(GetVolume() - 2.0) ; Same, but decrease volume
return
; Return Volume to Default 0.0
~CapsLock & Numpad0:: ; Numpad0 is the "Thumb Sniper Button" on my Logitech G502
ApplyVolume(0.0) ; 0.0 is the "default" for VoiceMeeter, so clicking this gets us back without having to look
return
; Volume Mute
~CapsLock & MButton:: ; MButton is "Middle Mouse Button", aka click the scroll wheel down
if (GetIsMuted() = 0.0) {
MuteVolume()
} else {
UnMuteVolume()
}
return
; Switch Audio Devices
~CapsLock & Up:: ; Up is talking about keyboard Arrow Keys
if (GetCurrentOutput() = 1) {
SetHeadphonesOutput()
} else {
SetSpeakersOutput()
}
return
; Open VoiceMeeter Banana
~CapsLock & Numpad7:: ; Numpad7 is the little button up near the scroll wheel click/freespin toggle. Labeled G9 on Logitech G502
if (voicemeeter_open = 0) { ; Control variable gets toggled to easily decide whether to Open or Hide when re-clicking the same hotkey repeatedly
OpenVoicemeeter() ; Runs new or shows existing VoiceMeeter
} else {
HideVoiceMeeter() ; Hides running VoicemMeeter to Tray
}
return
;=========================================================================================
; Media Controls
;=========================================================================================
; Numpads are used in some places because that's what I've bound my mouse buttons to using Logitech software
~CapsLock & Right:: ; Right is talking about keyboard Arrow Keys
Send {Media_Next}
return
~CapsLock & Left:: ; Left is talking about keyboard Arrow Keys
Send {Media_Prev}
return
~CapsLock & Down:: ; Down is talking about keyboard Arrow Keys
Send {Media_Play_Pause}
return
~CapsLock & Numpad4:: ; Numpad4 is click Mouse Wheel Right on my Logitech G502
Send {Media_Next}
return
~CapsLock & Numpad3:: ; Numpad3 is click Mouse Wheel Left on my Logitech G502
Send {Media_Prev}
return
;=========================================================================================
; Open with Vim
;=========================================================================================
~CapsLock & Enter:: ; Initialize the built-in Clipboard variable
Clipboard = ; Assumes you have single-click selected a file in Explorer
Send ^c ; Literally sends "Control C" to Windows, which fires "Copy"
ClipWait, 1 ; Wait for the Clipboard to be filled with something, only wait 1 second, in case something screws up
Run, C:\Program Files (x86)\Vim\vim81\gvim.exe `"%clipboard%`" ; Run gvim.exe passing along the copied filename, escaping the " using ` so that the run command literally contains quotes, which fixes filepaths with spaces and special characters
SetKeyDelay,-1 ; When this fires, the context switches to VIM, so CapsLock becomes "Esc"
Send,{Blind}{RAlt Up} ; Therefore "RAlt Up" is never sent, meaing that "RAlt" get stuck "Down"
return ; These two lines simulate what usually happens when you let go of "~CapsLock::Ralt"
-
- Posts: 1
- Joined: 13 Jun 2021, 03:40
Re: Controlling VoiceMeeter Banana with AHK
Any idea how to make the `recorder.load` work? The below returns a value of -1 (which appears to be a generic VB error)
Code: Select all
DllCall(VMR_FUNCTIONS["SetParameterFloat"], "AStr", "recorder.load", "AStr", "F:\sounds\alarm.mp3", "Int")
-
- Posts: 1
- Joined: 14 Jul 2021, 19:13
Re: Controlling VoiceMeeter Banana with AHK
Since this thread seems to get revived every 2 years, I'll do it again.
All i need it is AHK to send a keyboard shortcut (CTL ALT B) when input A1 is unmuted, and another when muted (CTL ATL Y). This would allow me to automate my "on air" status light.
All i need it is AHK to send a keyboard shortcut (CTL ALT B) when input A1 is unmuted, and another when muted (CTL ATL Y). This would allow me to automate my "on air" status light.
Re: Controlling VoiceMeeter Banana with AHK
Just something I was working on, it works, not good enough since it has some milliseconds of delay but it works. I used the library from https://github.com/onyx-and-iris/voicemeeter-api-powershell (you need to install it manually on your PowerShell by pasting Install-Module -Name Voicemeeter -Scope CurrentUser). From there you can send things to voicemeeter through PowerShell:
From that, I created the code bellow, it will simply open powershell if not open and send the desired command through a function. This example will mute strip[0] with ctrl + ç, all commands can be found on the voicemeeter documentation.
Code: Select all
$vmr = Get-RemoteBanana
$vmr.strip[0].mute=1
# [... other commands, some logic and after you are done:]
$vmr.Logout()
Code: Select all
#InstallKeybdHook
#InstallMouseHook
#Persistent
#NoEnv
#SingleInstance Force
SetTitleMatchMode 2 ; allow partial titles
SetKeyDelay, -1,1 ; faster key strokes
; make sure you went to powershell and pasted the following command to install the library from https://github.com/onyx-and-iris/voicemeeter-api-powershell
; Install-Module -Name Voicemeeter -Scope CurrentUser
WriteMessageToPowerShell(instance,message) {
IfWinExist VoiceMeeterPowershell
{
; If PowerShell is already open, send the PowerShell code to the existing window named VoiceMeeterPowershell
ControlSendRaw,,if (!(Test-Path variable:\vmr) ) {$vmr = %instance%}`n,VoiceMeeterPowershell ; check if the instance exists
ControlSend,,$vmr.%message%`n,VoiceMeeterPowershell ; send the command
}
else
{
; If PowerShell is not open, start a new process in Minimized mode, name it and send the PowerShell code
Run, powershell.exe -NoExit -WindowStyle Minimized -Command $host.UI.RawUI.WindowTitle = 'VoiceMeeterPowershell'`n,,;Hide
WinWait,VoiceMeeterPowershell,, 10 ; wait up to 10 seconds to powershell to be ready
ControlSend,,$vmr = %instance%`n,VoiceMeeterPowershell ; declare the instance
ControlSend,,$vmr.%message%`n,VoiceMeeterPowershell ; send the command
}
}
^ç::
; Example of calling the function with the desired command
WriteMessageToPowerShell("Get-RemoteBanana","strip[0].mute=1")
; Im kinda lazy to do it, but here you can call some audio feedback
return
Re: Controlling VoiceMeeter Banana with AHK
Second upgrade:
Code: Select all
#InstallKeybdHook
#InstallMouseHook
#Persistent
#NoEnv
#SingleInstance Force
SetTitleMatchMode 2 ; allow partial titles
SetKeyDelay, -1,1 ; faster key strokes
IsNumberDefined(myNumber) {
global myNumbers
for index, value in myNumbers
{
if (value == myNumber)
return true
}
return false
}
; make sure you went to powershell and pasted the following command to install the library from https://github.com/onyx-and-iris/voicemeeter-api-powershell
; Install-Module -Name Voicemeeter -Scope CurrentUser
myNumbers := [] ; Initialize an empty array to store defined numbers
WriteMessageToPowerShell(instance,shortcut,isToggle, message) {
IfWinExist VoiceMeeterPowershell
{
if (!IsNumberDefined(shortcut))
{
; If PowerShell is already open, send the PowerShell code to the existing window named VoiceMeeterPowershell
sendToPowerShell(isToggle,shortcut,message)
}
ControlSend,,&$%shortcut%`n,VoiceMeeterPowershell ; send the command
}
else
{
; If PowerShell is not open, start a new process in hidden mode, name it, and send the PowerShell code
Run, powershell.exe -NoExit -WindowStyle Minimized -Command $host.UI.RawUI.WindowTitle = 'VoiceMeeterPowershell'`n,,;Hide
WinWait, VoiceMeeterPowershell,, 10 ; wait up to 10 seconds for PowerShell to be ready
ControlSend,,$vmr = %instance%`n,VoiceMeeterPowershell ; declare the instance
sendToPowerShell(isToggle,shortcut,message)
ControlSend,,&$%shortcut%`n,VoiceMeeterPowershell ; send the command
}
}
SendToPowerShell(isToggle,shortcut,message){
global myNumbers
if (isToggle == "toggle")
{
ControlSendRaw,,$%shortcut% = { echo $vmr.%message%
ControlSend,,{VKBF}
ControlSendRaw,,$vmr.%message% = -not $vmr.%message% }`n,VoiceMeeterPowershell ; create a shortcut of the command
}
Else
{
ControlSendRaw,,$%shortcut% = { echo $vmr.%message%
ControlSend,,{VKBF}
ControlSendRaw,,$vmr.%message% }`n,VoiceMeeterPowershell ; create a shortcut of the command
}
myNumbers.Push(shortcut)
}
^ç::
; Example of calling the function with the desired command
; first parameter is the Voicemeeter you are using
; second parameter is the shortcut you want, DO NOT REPEAT THEM, use just one character for more speed
; third parameter is if it's toggle, it will get the value and invert it
; forth parameter is the command, if toggle you must ocult the assignment
WriteMessageToPowerShell("Get-RemoteBanana","0","none", "strip[1].mute=1")
WriteMessageToPowerShell("Get-RemoteBanana","1","toggle", "strip[0].mute")
return