saifaqqad / vmr.ahk Goto Github PK
View Code? Open in Web Editor NEWAutoHotkey wrapper class for Voicemeeter's Remote API
Home Page: https://saifaqqad.github.io/VMR.ahk/
AutoHotkey wrapper class for Voicemeeter's Remote API
Home Page: https://saifaqqad.github.io/VMR.ahk/
2-REMOTE API: Command.Lock function allow to lock/unlock GUI (like Menu option).
https://forum.vb-audio.com/viewtopic.php?p=5739&sid=e42f1b27d8b6d94ea1833632218cb630#p5739
I don't know if the parameter is accessible in read mode (Get)
Hello, could you please convert the vmr.ahk code to be compatible with ahkv2? Thanks
peak_level := Max(voicemeeter.bus[1].level*) is working, but hot to retrieve the current peak level of a strip?
Thanks so much for writing this! I've found it very useful.
Modified the following that allows user to restart Voicemeeter if it goes down while script is running.
/* Original
IsParametersDirty(){
return DllCall(VBVMR.VM_DLL . "\VBVMR_IsParametersDirty")
}
*/
; Modified 2020-10-02
IsParametersDirty(){
a:= DllCall(VBVMR.VM_DLL . "\VBVMR_IsParametersDirty")
if ( a < 0 )
{
msgbox Voicemeeter is down, please restart then then press OK
return 1
}
return a
}
I noticed that a few new Parameters were added to the Voicemeeter Remote API as of November 2022 with the new Version 3. One of them being the GainLayer Input Strip Parameter.
Is there any Way of making use of this Parameter in the current state of vmr.ahk?
The setParameter function does not work as far as I can tell because the GainLayer itself needs the Parameter of the desired Bus AND the desired gain which cannot be accomplished.
I would much appreciate any solution to using GainLayer in vmr.ahk
Thanks in Advance
Hi @SaifAqqad. Thanks so much for adding ahkpm support to this library!
I was just playing around with generating an ahkpm demo using the VMR.ahk package and noticed that while there is a version 1.0.0 tagged, there hasn't been a new version released since you added ahkpm support.
That means that when users install via ahkpm, they'll get 1.0.0, but when they try to use ahkpm include
, they'll get an error about VMR.ahk not having an ahkpm.json file.
6-MacroButton: New command Show(0/1) to Hide/Show the MacroButtons Application.
Show(0); Wait(1000); Show(1);
I think that this is not available in the Voicemeeter API, and that it is not planned, but I am curious to have an answer that status this.
Hi,` the GUI app (buses and strips) is working fine; changing volume or output device with a hotkey within the main GUI script works too, but if I change volume (bus or strip) or any output device (e.g., A1 to A2) from another AHK script, the GUI freezes. After reloading, it starts working again.
I think parameter updating stops working while making any changes from another ahk script. I like this GUI script. Any help is appreciated.
Thanks
edit:
hi,
I made two GUIs, one for the buses and the other for the strips.
You can show or hide any of the GUIs with a button on the GUI.
Now, if I make any changes to the volume or to the output devices from another script, the GUI does not freeze.
Thanks for the help.
regards
#Include, %A_ScriptDir%\..\dist\VMR.ahk
SetBatchLines, 20ms
Global vm, GUI_hwnd, is_win_pos_changing:=0
vm := new VMR().login()
showUI()
vm.onUpdateLevels:= Func("syncLevel") ; register level callback func
vm.onUpdateParameters:= Func("syncParameters") ; register params callback func
OnMessage(0x46, Func("onPosChanging"))
showUI(){
global
;gui1, buses
Gui, 1: +HwndGUI_hwnd
xPos := 45
Loop % vm.bus.Length() { ; add UI controls for each bus
yPos:=42, funcObj:=""
yPos+= 40
Gui,1: Add, Progress, x%xPos% y%yPos% w10 h100 Range-72-20 c0x70C399 Background0x2C3D4D vertical Hwndbus_%A_Index%_level
;bus gain
yPos+= 110
Gui,1: Add, Edit, w48 h20 x%xPos% y%yPos% ReadOnly
Gui,1: Add, UpDown,Hwndbus_%A_Index%_gain Range-60-12 x0
funcObj:= Func("updateParam").bind("bus", "gain", A_Index)
GuiControl +g, % bus_%A_Index%_gain, % FuncObj
;bus mute
yPos+= 32
Gui, 1: Font, cC3C3C3 s11 norm, Calibri
Gui, 1:Add, CheckBox, x%xPos% y%yPos% c7e7de5 Hwndbus_%A_Index%_mute, Mute
funcObj:= Func("updateParam").bind("bus", "mute", A_Index)
GuiControl +g, % bus_%A_Index%_mute, % FuncObj
xPos+=52
}
;gui2 strips
Gui, 2: +HwndGUI_hwnd
xPos:= 20
Loop % vm.strip.Length() { ; add UI controls for each strip
yPos:=42 funcObj:=""
yPos+= 40
Gui, 2: Add, Progress, x%xPos% y%yPos% w10 h100 Range-72-20 c0x70C399 Background0x2C3D4D vertical Hwndstrip_%A_Index%_level
;strip gain
yPos+= 110
Gui, 2: Font, cC3C3C3 s12 bold, Calibri
Gui, 2:Add, Edit, w48 h20 x%xPos% y%yPos% ReadOnly
Gui, 2:Add, UpDown,Hwndstrip_%A_Index%_gain Range-60-12 x0
funcObj:= Func("updateParam").bind("strip", "gain", A_Index)
GuiControl +g, % strip_%A_Index%_gain, % FuncObj
;strip mute
yPos+= 32
Gui, 2:Font, cC3C3C3 s11 norm, Calibri
Gui, 2: Add, CheckBox, x%xPos% y%yPos% c7e7de5 Hwndstrip_%A_Index%_mute, Mute
funcObj:= Func("updateParam").bind("strip", "mute", A_Index)
GuiControl +g, % strip_%A_Index%_mute, % FuncObj
xPos+=52
}
syncParameters() ; get initial values for gui controls
;gui1 show
Gui, 1:+ToolWindow -Caption +LastFound
Gui, 1: Add, Text, cYellow x150 y248, vmBuses
Gui, 1:Color, 224e70
WinSet,Transparent, 150
Gui,1: Show, x1496 y678 W340 H272, buses
;gui2 show
Gui, 2: +ToolWindow -Caption +LastFound
Gui, 2:Add, Text, cYellow x150 y248, vmStrips
Gui, 2: Color, 224e70
WinSet,Transparent, 150
Gui,2: Show, x1134 y678 W290 H272, strips
}
return
; update voicemeeter bus parameters when they change on the AHK UI
updateParam(type, param, index){
GuiControlGet, val,,% %type%_%index%_%param%
if(param == "device"){
RegExMatch(val, "iO)(?<driver>\w+): (?<name>.+)", match)
if(match)
vm[type][index].device[match.driver]:= match.name
else
vm[type][index].device:= ""
}else{
vm[type][index][param]:= val
}
SetTimer, syncParameters, -500 ; make sure params are in sync
}
; sync AHK UI controls with voicemeeter bus parameters
syncParameters(){
Loop % vm.bus.Length() {
GuiControl,1:, % bus_%A_Index%_gain, % vm.bus[A_Index].gain
GuiControl,1:, % bus_%A_Index%_mute, % Format("{:i}", vm.bus[A_Index].mute) ; convert 0.0/1.0 to 0/1
}
Loop % vm.strip.Length() {
GuiControl,2:, % strip_%A_Index%_gain, % vm.strip[A_Index].gain
GuiControl,2:, % strip_%A_Index%_mute, % Format("{:i}", vm.strip[A_Index].mute) ; convert 0.0/1.0 to 0/1
}
}
; sync level meters with vm bus levels
syncLevel(){
if(is_win_pos_changing) ;dont update levels if the window is changing its position
return
Loop % vm.bus.Length() {
GuiControl,1:, % bus_%A_Index%_level, % Max(vm.bus[A_Index].level*) ; get peak level for the bus
}
Loop % vm.strip.Length() {
GuiControl,2:, % strip_%A_Index%_level, % Max(vm.strip[A_Index].level*) ; get peak level for the bus
}
}
onPosChanging(){
is_win_pos_changing:=1
SetTimer, onPosChanged, -50
}
onPosChanged(){
is_win_pos_changing:=0
}
I have this error in a completely random way, I also put you the related autohotkey code to try to reproduce it
#if WinExist("ahk_exe VALORANT-Win64-Shipping.exe")
~*u::
~*v::
if (vmr.strip[1].B1 == false && WinActive("ahk_exe VALORANT-Win64-Shipping.exe"))
vmr.strip[1].B1 := !vmr.strip[1].B1
return
~*u Up::
~*v Up::
if (vmr.strip[1].B1 == true && WinActive("ahk_exe VALORANT-Win64-Shipping.exe"))
vmr.strip[1].B1 := !vmr.strip[1].B1
return
#if
Hi, I am trying to load an mp3 file, but it gives me this error. I don't really know if I did something wrong but maybe it's a bug. I figured out that the same error occurs when I don't put in anything at all, so this could just be me fucking up but I am certain that the file is in that location and I've checked it multiple times, but yea it still could be lol
voicemeeter := new VMR()
voicemeeter.login()
voicemeeter.recorder["mode.PlayOnLoad"]:= true
voicemeeter.recorder.load:= "D:\Music\Trance-Music-for-Racing-Game.mp3"
SetParameterString(p_prefix, p_parameter, p_value){
this.IsParametersDirty()
errLevel := DllCall(VBVMR.FUNC_ADDR["SetParameterString" . VBVMR.STR_TYPE], "AStr", p_prefix . "." . p_parameter , VBVMR.STR_TYPE . "Str" , p_value , "Int")
if (errLevel<0)
Throw, Format("`nVBVMR_SetParameterString returned {}`nDllCall returned {}", errLevel, ErrorLevel)
return p_value
}
GetParameterFloat(p_prefix, p_parameter){
local value
this.IsParametersDirty()
VarSetCapacity(value, 4)
errLevel := DllCall(VBVMR.FUNC_ADDR.GetParameterFloat, "AStr" , p_prefix . "." . p_parameter , "Ptr" , &value, "Int")
if (errLevel<0)
Throw, Format("`nVBVMR_GetParameterFloat returned {}`nDllCall returned {}", errLevel, ErrorLevel)
value := NumGet(&value, 0, "Float")
return value
}
May i make a request? Can you make the Strips UI in ahk v1 the way you made the buses(UI)?
I tried (Voicemeeter Compact) by Onyx and Iris in Python, but it takes almost 10% of CPU whereas ahk takes only 1 or 1.5%.
I'm also using Cantabile to run the vst plugins along with Voicemeeter that also takes some percentage of the CPU.
look forward to hearing from you.
edit:
I tried and made it. (Strip UI), ### Thanks
regards
First of all, thank you for providing this code, but when I was trying to use voicemeeter.recorder.goto, it set the position in the song always to 0. When searching for the reason, I saw that in "class recorder_base"(line 496ff.) "goto" is defined like "load" to require a string as its value, but "goto" only works if its value type is a float.
class recorder_base {
__Set(p_name,p_value){
local type:= "Float"
if p_name contains goto,load
type:= "String"
return (VBVMR)["SetParameter" type]("Recorder", p_name, p_value)
}
__Get(p_name){
local type:= "Float"
if p_name contains goto,load
type:= "String"
return (VBVMR)["GetParameter" type]("Recorder", p_name)
}
class recorder_base {
__Set(p_name,p_value){
local type:= "Float"
if p_name contains goto
{
type:= "Float"
}
else if p_name contains load
{
type:= "String"
}
return (VBVMR)["SetParameter" type]("Recorder", p_name, p_value)
}
__Get(p_name){
local type:= "Float"
if p_name contains goto
{
type:= "Float"
}
else if p_name contains load
{
type:= "String"
}
return (VBVMR)["GetParameter" type]("Recorder", p_name)
}
#Requires AutoHotkey v2
#include VMR.ahk
voicemeeter := VMR().Login()
/*
voicemeeter controls
*/
Volume_Mute::{
voicemeeter.Strip[6].mute := -1
voicemeeter.Strip[7].mute := -1
voicemeeter.Strip[8].mute := -1
if is_muted := voicemeeter.Strip[6].mute
DisplayOsd("UnMuted", 1000)
else
DisplayOsd("Muted", 1000)
}
F23::{
if (voicemeeter.Strip[6].A1 == 1) {
DisplayOsd("Headphones", 1000)
voicemeeter.Strip[6].A2 := true
voicemeeter.Strip[7].A2 := true
voicemeeter.Strip[8].A2 := true
voicemeeter.Strip[6].A1 := false
voicemeeter.Strip[7].A1 := false
voicemeeter.Strip[8].A1 := false
} else {
DisplayOsd("Speakers", 1000)
voicemeeter.Strip[6].A2 := false
voicemeeter.Strip[7].A2 := false
voicemeeter.Strip[8].A2 := false
voicemeeter.Strip[6].A1 := true
voicemeeter.Strip[7].A1 := true
voicemeeter.Strip[8].A1 := true
}
}
F24::{
if (voicemeeter.Strip[6].A3 == 1) {
DisplayOsd("TV off", 1000)
voicemeeter.Strip[6].A3 := false
voicemeeter.Strip[7].A3 := false
voicemeeter.Strip[8].A3 := false
} else {
DisplayOsd("TV on", 1000)
voicemeeter.Strip[6].A3 := true
voicemeeter.Strip[7].A3 := true
voicemeeter.Strip[8].A3 := true
}
}
F16 & Volume_Up::{
voicemeeter.Bus[1].Increment("gain", 1).Then(gain => DisplayBusVol(1, gain))
voicemeeter.Bus[2].Increment("gain", 1).Then(gain => DisplayBusVol(2, gain))
voicemeeter.Bus[3].Increment("gain", 1).Then(gain => DisplayBusVol(3, gain))
}
F16 & Volume_Down::{
voicemeeter.Bus[1].Increment("gain", -1).Then(gain => DisplayBusVol(1, gain))
voicemeeter.Bus[2].Increment("gain", -1).Then(gain => DisplayBusVol(2, gain))
voicemeeter.Bus[3].Increment("gain", -1).Then(gain => DisplayBusVol(3, gain))
}
DisplayBusVol(*) {
SetTimer () => DisplayOsd(
"Gain: " voicemeeter.Bus[1].gain
"," voicemeeter.Bus[2].gain
"," voicemeeter.Bus[3].gain,
500
), -100
}
Volume_Down::{
voicemeeter.Strip[6].Increment("gain", -1).Then(gain => DisplayVol(6, gain))
voicemeeter.Strip[7].Increment("gain", -1).Then(gain => DisplayVol(7, gain))
voicemeeter.Strip[8].Increment("gain", -1).Then(gain => DisplayVol(8, gain))
}
Volume_Up::{
voicemeeter.Strip[6].Increment("gain", 1).Then(gain => DisplayVol(6, gain))
voicemeeter.Strip[7].Increment("gain", 1).Then(gain => DisplayVol(7, gain))
voicemeeter.Strip[8].Increment("gain", 1).Then(gain => DisplayVol(8, gain))
}
DisplayVol(*) {
SetTimer () => DisplayOsd(
"Gain: " voicemeeter.Strip[6].gain
"," voicemeeter.Strip[7].gain
"," voicemeeter.Strip[8].gain,
500
), -100
}
DisplayOsd(text, timeout) {
textColor := "d400cd"
bgColor := "000000"
g := Gui("+AlwaysOnTop +ToolWindow +E0x20 -Caption -SysMenu -Owner", "OSD")
g.BackColor := bgColor
g.SetFont "s16", "fixedsys"
g.AddText "x10 y10 h40 c" textColor, text
g.Show "NoActivate x10 y10 h35"
SetTimer () => g.Destroy(), -Abs(timeout)
}
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.