mirror of
https://github.com/onyx-and-iris/vmrcli.git
synced 2025-12-13 18:17:51 +00:00
Compare commits
94 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6390f52420 | |||
| fe969193f6 | |||
| 1950d6dd8e | |||
| ca15785789 | |||
| 12522667d3 | |||
| aa6317c79e | |||
| 57266334e8 | |||
| 514e7fda7a | |||
| 354efdfe73 | |||
| 7bbf438878 | |||
| dc8395c404 | |||
| 0affb2bf2d | |||
| 7f84267b5a | |||
| 8e37cec719 | |||
| 95820c3043 | |||
| 87c2192403 | |||
| 29510feb8e | |||
| f2a3247077 | |||
| 452bf6f6de | |||
| 22b7e9a765 | |||
| 9388844acb | |||
| 955edb781c | |||
| a719af8265 | |||
| 7c46f30e62 | |||
| 41a256786f | |||
| 6179374eaa | |||
| 086f5dd28a | |||
| 2c1c7033d5 | |||
| 218186781d | |||
| ca803c09ed | |||
| 9eb0d2f623 | |||
| 453797b0d9 | |||
| de70cd39cf | |||
| b225ba5cc3 | |||
| 35335a60aa | |||
| a7c0bc1620 | |||
| 25692a9f35 | |||
| a05b029e9d | |||
| 68c2022ad7 | |||
| ff2970f4c5 | |||
| f60fc231b0 | |||
| c1dad8b99c | |||
| 2f2e503ae3 | |||
| ff69837f19 | |||
| b35a29396b | |||
| 39540e9c3e | |||
| af98dead75 | |||
| ad91a7f4e6 | |||
| 8d9e70b79a | |||
| d7d42e4b21 | |||
| babec7abbb | |||
| b2bdd21da5 | |||
| 88fe1f5782 | |||
| 82823687db | |||
| 3d00c7dd4f | |||
| c95ff0e163 | |||
| 48bb15e8f2 | |||
| dc8a4036c8 | |||
| 6b7e0afb91 | |||
| 477267e278 | |||
| bc7568a191 | |||
| c5c9360d32 | |||
| 9ee3f42334 | |||
| 61d81b0abf | |||
| 03b45129dd | |||
| b4519cc2cc | |||
| 19a3a16b74 | |||
| 09793afafa | |||
| 8bdbfe9b04 | |||
| f87977165a | |||
| 36eadb4b58 | |||
| 930093da7f | |||
| a28db25bcc | |||
| 83a85fd7c5 | |||
| f6437fcbe7 | |||
| fb54c4a492 | |||
| 8aeb793a41 | |||
| b6b4b04a8a | |||
| b7fa15d87d | |||
| b95c40265c | |||
| 4b64ae95fd | |||
| f8d2f80cbf | |||
| 4b79b7f849 | |||
| 3ec98ea391 | |||
| faad5bc2c8 | |||
| cc0ec73ef4 | |||
| c45df11286 | |||
| a383aaa36b | |||
| 28945b72c5 | |||
| 97fc9ca9ce | |||
| 12a55a52f2 | |||
| d4afbfa881 | |||
| ea1f05d323 | |||
| 900ed23ebf |
5
.gitignore
vendored
5
.gitignore
vendored
@ -51,4 +51,9 @@ Module.symvers
|
|||||||
Mkfile.old
|
Mkfile.old
|
||||||
dkms.conf
|
dkms.conf
|
||||||
|
|
||||||
|
# Task Runner
|
||||||
|
.task/
|
||||||
|
|
||||||
.vscode/
|
.vscode/
|
||||||
|
|
||||||
|
test*
|
||||||
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2024 Onyx and Iris
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
78
README.md
78
README.md
@ -1,10 +1,10 @@
|
|||||||
# VMRCLI Command Line Utility
|
# Voicemeeter Remote Command Line Utility
|
||||||
|
|
||||||
## `Tested against`
|
## `Tested against`
|
||||||
|
|
||||||
- Basic 1.1.1.1
|
- Basic 1.1.1.9
|
||||||
- Banana 2.1.1.1
|
- Banana 2.1.1.9
|
||||||
- Potato 3.1.1.1
|
- Potato 3.1.1.9
|
||||||
|
|
||||||
## `Requirements`
|
## `Requirements`
|
||||||
|
|
||||||
@ -13,42 +13,71 @@
|
|||||||
## `Use`
|
## `Use`
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
./vmrcli.exe [-h] [-i] [-k] [-D] <api commands>
|
.\vmrcli.exe [-h] [-v] [-i|-I] [-f] [-k] [-l] [-e] [-c] [-m] [-s] <api commands>
|
||||||
```
|
```
|
||||||
|
|
||||||
Where:
|
Where:
|
||||||
|
|
||||||
- `h`: Prints the help dialogue.
|
- `h`: Print the help message.
|
||||||
- `i`: Enable interactive mode. If set, any api commands passed on the command line will be ignored.
|
- `v`: Print the version of vmrcli.
|
||||||
|
- `i`: Enable interactive mode, use (-I) to disable the '>>' prompt.
|
||||||
|
- If set, any api commands passed on the command line will be ignored.
|
||||||
|
- `f`: Do not split input on spaces.
|
||||||
- `k`: The kind of Voicemeeter (basic, banana or potato). Use this to launch the GUI.
|
- `k`: The kind of Voicemeeter (basic, banana or potato). Use this to launch the GUI.
|
||||||
- `D`: Set log level 0=TRACE, 1=DEBUG, 2=INFO, 3=WARN, 4=ERROR, 5=FATAL
|
- `l`: Set log level, must be one of TRACE, DEBUG, INFO, WARN, ERROR, or FATAL
|
||||||
|
- `e`: Enable extra console output (toggle, set messages)
|
||||||
|
- `c`: Load a user configuration (give the full file path)
|
||||||
|
- `m`: Launch the MacroButtons application
|
||||||
|
- `s`: Launch the StreamerView application
|
||||||
|
|
||||||
## `API Commands`
|
## `API Commands`
|
||||||
|
|
||||||
- Commands starting with `!` will be toggled, use it with boolean parameters.
|
- Commands starting with `!` will be toggled, use it with boolean parameters.
|
||||||
- Commands containing `=` will set a value.
|
- Commands containing `=` will set a value. (use `+=` and `-=` to increment/decrement)
|
||||||
- All other commands with get a value.
|
- All other commands with get a value.
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
|
|
||||||
Launch basic GUI, set log level to INFO, Toggle Strip 0 Mute, then print its new value
|
Launch basic GUI, set log level to INFO, Toggle Strip 0 Mute, print its new value, then decrease Bus 0 Gain by 3.8
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
./vmrcli.exe -kbasic -D2 !strip[0].mute strip[0].mute
|
.\vmrcli.exe -kbasic -lINFO !strip[0].mute strip[0].mute bus[0].gain-=3.8
|
||||||
```
|
```
|
||||||
|
|
||||||
Launch banana GUI, set log level to DEBUG, set Strip 0 label to podmic then print Strip 2 label
|
Launch banana GUI, set log level to DEBUG, set Strip 0 label to podmic then print Strip 2 label
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
./vmrcli.exe -kbanana -D1 strip[0].label=podmic strip[2].label
|
.\vmrcli.exe -kbanana -lDEBUG strip[0].label=podmic strip[2].label
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### `String Commands With Spaces`
|
||||||
|
|
||||||
|
It may be desirable to send a string request containing spaces, for example to change an output device. By default the CLI splits such strings, to avoid this pass the `-f` flag. It's probably best to use this with single commands only due to its effect on how the CLI parses strings. Also note the inclusion of the double quotation marks, it seems the C API requires them.
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
.\vmrcli.exe -lDEBUG -f bus[1].device.wdm='"Realtek Digital Output (Realtek(R) Audio)"'
|
||||||
|
|
||||||
|
.\vmrcli.exe -lDEBUG -f strip[0].label='"My Podmic"'
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `Quick Commands`
|
||||||
|
|
||||||
|
A short list of quick commands are available:
|
||||||
|
|
||||||
|
- `lock`: command.lock=1
|
||||||
|
- `unlock`: command.lock=0
|
||||||
|
- `show`: command.show=1
|
||||||
|
- `hide`: command.show=0
|
||||||
|
- `restart`: command.restart=1
|
||||||
|
|
||||||
|
They may be used in direct or interactive mode.
|
||||||
|
|
||||||
## `Interactive Mode`
|
## `Interactive Mode`
|
||||||
|
|
||||||
Running the following command in Powershell:
|
Running the following command in Powershell:
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
./vmrcli.exe -i
|
.\vmrcli.exe -i
|
||||||
```
|
```
|
||||||
|
|
||||||
Will open an interactive prompt:
|
Will open an interactive prompt:
|
||||||
@ -65,21 +94,36 @@ API commands follow the same rules as listed above. Entering `Q` or `q` will exi
|
|||||||
Scripts can be loaded from text files, for example in Powershell:
|
Scripts can be loaded from text files, for example in Powershell:
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
./vmrcli.exe -D1 $(Get-Content .\example_commands.txt)
|
.\vmrcli.exe -lDEBUG $(Get-Content .\example_commands.txt)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
You may also pipe a scripts contents to the CLI:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
$(Get-Content .\example_commands.txt) | .\vmrcli.exe -lDEBUG -I
|
||||||
|
```
|
||||||
|
|
||||||
|
Multiple API commands can be in a single line, they may be separated by space, `;` or `,`.
|
||||||
|
|
||||||
|
Lines starting with `#` will be interpreted as comments.
|
||||||
|
|
||||||
## `Build`
|
## `Build`
|
||||||
|
|
||||||
Run the included `makefile` with [GNU Make](https://www.gnu.org/software/make/).
|
Run the included `makefile` with [GNU Make](https://www.gnu.org/software/make/).
|
||||||
|
|
||||||
By default the log.c module is built with coloured logging enabled. To disable this you can override the `LOG_USE_COLOR` variable, for example:
|
The binary in [Releases][releases] is compiled with coloured logging enabled. To disable this you can override the `LOG_USE_COLOR` variable, for example:
|
||||||
|
|
||||||
`make LOG_USE_COLOR=no`
|
`make LOG_USE_COLOR=no`
|
||||||
|
|
||||||
## `Official Documentation`
|
## `Official Documentation`
|
||||||
|
|
||||||
- [Voicemeeter Remote C API](https://github.com/onyx-and-iris/Voicemeeter-SDK/blob/main/VoicemeeterRemoteAPI.pdf)
|
- [Voicemeeter Remote C API][remoteapi-docs]
|
||||||
|
|
||||||
## `Special Thanks`
|
## `Special Thanks`
|
||||||
|
|
||||||
- [rxi](https://github.com/rxi) for writing the [log.c](https://github.com/rxi/log.c) package
|
- [rxi][rxi-user] for writing the [log.c][log-c] package
|
||||||
|
|
||||||
|
[releases]: https://github.com/onyx-and-iris/vmrcli/releases
|
||||||
|
[remoteapi-docs]: https://github.com/onyx-and-iris/Voicemeeter-SDK/blob/main/VoicemeeterRemoteAPI.pdf
|
||||||
|
[rxi-user]: https://github.com/rxi
|
||||||
|
[log-c]: https://github.com/rxi/log.c
|
||||||
|
|||||||
71
Taskfile.yml
Normal file
71
Taskfile.yml
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
version: '3'
|
||||||
|
|
||||||
|
dotenv: ['.env']
|
||||||
|
|
||||||
|
vars:
|
||||||
|
PROGRAM: vmrcli
|
||||||
|
SHELL: pwsh
|
||||||
|
|
||||||
|
CC: gcc
|
||||||
|
|
||||||
|
SRC_DIR: src
|
||||||
|
INC_DIR: include
|
||||||
|
OBJ_DIR: obj
|
||||||
|
BIN_DIR: bin
|
||||||
|
|
||||||
|
CPPFLAGS: -I{{.INC_DIR}} -MMD -MP {{if eq .LOG_USE_COLOR "yes"}}-DLOG_USE_COLOR{{end}}
|
||||||
|
|
||||||
|
CFLAGS: -O -Wall -W -pedantic -ansi -std=c2x
|
||||||
|
LDFLAGS: -Llib
|
||||||
|
LDLIBS: -lm
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
default:
|
||||||
|
desc: Build vmrcli for Windows
|
||||||
|
deps: [build]
|
||||||
|
|
||||||
|
build:
|
||||||
|
desc: Build vmrcli for Windows
|
||||||
|
deps: [link]
|
||||||
|
|
||||||
|
link:
|
||||||
|
desc: Link all files in obj/ for Windows
|
||||||
|
deps: [compile]
|
||||||
|
cmds:
|
||||||
|
- |
|
||||||
|
{{.SHELL}} -Command "
|
||||||
|
if (!(Test-Path -Path '{{.BIN_DIR}}')) {
|
||||||
|
New-Item -ItemType Directory -Path '{{.BIN_DIR}}'
|
||||||
|
}
|
||||||
|
|
||||||
|
{{.CC}} {{.LDFLAGS}} {{.OBJ_DIR}}/*.o {{.LDLIBS}} -o {{.BIN_DIR}}/{{.PROGRAM}}.exe"
|
||||||
|
sources:
|
||||||
|
- '{{.OBJ_DIR}}/**'
|
||||||
|
generates:
|
||||||
|
- '{{.BIN_DIR}}/{{.PROGRAM}}.exe'
|
||||||
|
|
||||||
|
compile:
|
||||||
|
desc: Compile all files in src/ and include/ for Windows
|
||||||
|
cmds:
|
||||||
|
- |
|
||||||
|
{{.SHELL}} -Command "
|
||||||
|
if (!(Test-Path -Path '{{.OBJ_DIR}}')) {
|
||||||
|
New-Item -ItemType Directory -Path '{{.OBJ_DIR}}'
|
||||||
|
}
|
||||||
|
|
||||||
|
Get-ChildItem -Path '{{.SRC_DIR}}' -Filter '*.c' |
|
||||||
|
ForEach-Object { \$_.Name -replace '\.c$', '' } |
|
||||||
|
ForEach-Object { {{.CC}} {{.CPPFLAGS}} {{.CFLAGS}} -c {{.SRC_DIR}}/\$_.c -o {{.OBJ_DIR}}/\$_.o }"
|
||||||
|
sources:
|
||||||
|
- '{{.SRC_DIR}}/**'
|
||||||
|
- '{{.INC_DIR}}/**'
|
||||||
|
generates:
|
||||||
|
- '{{.OBJ_DIR}}/**'
|
||||||
|
|
||||||
|
clean:
|
||||||
|
desc: Remove all files in obj/ and bin/
|
||||||
|
cmds:
|
||||||
|
- |
|
||||||
|
{{.SHELL}} -Command "
|
||||||
|
if (Test-Path -Path '{{.OBJ_DIR}}') { Remove-Item -Path '{{.OBJ_DIR}}' -Recurse -Force }
|
||||||
|
if (Test-Path -Path '{{.BIN_DIR}}') { Remove-Item -Path '{{.BIN_DIR}}' -Recurse -Force }"
|
||||||
@ -1,8 +1,14 @@
|
|||||||
strip[0].mute
|
# Strip 0
|
||||||
!strip[0].mute
|
strip[0].mute !strip[0].mute strip[0].mute strip[0].gain strip[0].label=podmic strip[0].label
|
||||||
strip[0].mute
|
|
||||||
strip[1].mute=1
|
# Strip 1
|
||||||
strip[1].mute
|
strip[1].mute=1 strip[1].mute strip[1].limit-=8
|
||||||
strip[0].gain
|
|
||||||
strip[0].label=podmic
|
# Strip 2
|
||||||
strip[0].label
|
strip[2].gain-=5 strip[2].comp+=4.8
|
||||||
|
|
||||||
|
# Bus 0
|
||||||
|
bus[0].label
|
||||||
|
|
||||||
|
# Bus 1
|
||||||
|
bus[1].gain-=5.8
|
||||||
@ -1,5 +1,5 @@
|
|||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
/* Voicemeeter Remote API. V.Burel©2015-2023 */
|
/* Voicemeeter Remote API. V.Burel<EFBFBD>2015-2023 */
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
/* This Library allows communication with Voicemeeter applications */
|
/* This Library allows communication with Voicemeeter applications */
|
||||||
/* 4 Client Applications can be connected to remote Voicemeeter. */
|
/* 4 Client Applications can be connected to remote Voicemeeter. */
|
||||||
@ -19,15 +19,14 @@
|
|||||||
/* long = 32 bit integer */
|
/* long = 32 bit integer */
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
#ifndef __VOICEMEETER_REMOTE_H__
|
#ifndef __VOICEMEETER_REMOTE_H__
|
||||||
#define __VOICEMEETER_REMOTE_H__
|
#define __VOICEMEETER_REMOTE_H__
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C"
|
||||||
|
{
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#define VBVMR_RESULT_OK 0
|
#define VBVMR_RESULT_OK 0
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
@ -56,7 +55,6 @@ long __stdcall VBVMR_Login(void);
|
|||||||
|
|
||||||
long __stdcall VBVMR_Logout(void);
|
long __stdcall VBVMR_Logout(void);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@brief Run Voicemeeter Application (get installation directory and run Voicemeeter Application).
|
@brief Run Voicemeeter Application (get installation directory and run Voicemeeter Application).
|
||||||
@param vType : Voicemeeter type (1 = Voicemeeter, 2= Voicemeeter Banana, 3= Voicemeeter Potato, 6 = Potato x64 bits).
|
@param vType : Voicemeeter type (1 = Voicemeeter, 2= Voicemeeter Banana, 3= Voicemeeter Potato, 6 = Potato x64 bits).
|
||||||
@ -67,19 +65,8 @@ long __stdcall VBVMR_Logout(void);
|
|||||||
|
|
||||||
long __stdcall VBVMR_RunVoicemeeter(long vType);
|
long __stdcall VBVMR_RunVoicemeeter(long vType);
|
||||||
|
|
||||||
|
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
/* */
|
/* */
|
||||||
/* General Information */
|
/* General Information */
|
||||||
@ -134,26 +121,8 @@ long __stdcall VBVMR_GetVoicemeeterType(long * pType);
|
|||||||
|
|
||||||
long __stdcall VBVMR_GetVoicemeeterVersion(long *pVersion);
|
long __stdcall VBVMR_GetVoicemeeterVersion(long *pVersion);
|
||||||
|
|
||||||
|
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
/* */
|
/* */
|
||||||
/* Get parameters */
|
/* Get parameters */
|
||||||
@ -205,14 +174,6 @@ long __stdcall VBVMR_GetParameterStringW(char * szParamName, unsigned short * ws
|
|||||||
|
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
/* */
|
/* */
|
||||||
/* Get levels */
|
/* Get levels */
|
||||||
@ -283,7 +244,6 @@ long __stdcall VBVMR_GetParameterStringW(char * szParamName, unsigned short * ws
|
|||||||
|
|
||||||
long __stdcall VBVMR_GetLevel(long nType, long nuChannel, float *pValue);
|
long __stdcall VBVMR_GetLevel(long nType, long nuChannel, float *pValue);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@brief Get MIDI message from M.I.D.I. input device used by Voicemeeter M.I.D.I. mapping.
|
@brief Get MIDI message from M.I.D.I. input device used by Voicemeeter M.I.D.I. mapping.
|
||||||
(this function must be called from one thread only)
|
(this function must be called from one thread only)
|
||||||
@ -301,10 +261,8 @@ long __stdcall VBVMR_GetLevel(long nType, long nuChannel, float * pValue);
|
|||||||
-6: no MIDI data
|
-6: no MIDI data
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
long __stdcall VBVMR_GetMidiMessage(unsigned char *pMIDIBuffer, long nbByteMax);
|
long __stdcall VBVMR_GetMidiMessage(unsigned char *pMIDIBuffer, long nbByteMax);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@brief Send MIDI message to M.I.D.I. output device used by Voicemeeter M.I.D.I. mapping.
|
@brief Send MIDI message to M.I.D.I. output device used by Voicemeeter M.I.D.I. mapping.
|
||||||
(this function must be called from one thread only)
|
(this function must be called from one thread only)
|
||||||
@ -319,21 +277,10 @@ long __stdcall VBVMR_GetMidiMessage(unsigned char *pMIDIBuffer, long nbByteMax);
|
|||||||
-5: cannot send MIDI data
|
-5: cannot send MIDI data
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
long __stdcall VBVMR_SendMidiMessage(unsigned char *pMIDIBuffer, long nbByte);
|
long __stdcall VBVMR_SendMidiMessage(unsigned char *pMIDIBuffer, long nbByte);
|
||||||
|
|
||||||
|
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
/* */
|
/* */
|
||||||
/* Set Parameters */
|
/* Set Parameters */
|
||||||
@ -361,8 +308,6 @@ long __stdcall VBVMR_SendMidiMessage(unsigned char *pMIDIBuffer, long nbByte);
|
|||||||
|
|
||||||
long __stdcall VBVMR_SetParameterFloat(char *szParamName, float Value);
|
long __stdcall VBVMR_SetParameterFloat(char *szParamName, float Value);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@brief Set a single string parameters .
|
@brief Set a single string parameters .
|
||||||
@param szParamName : Null Terminal ASCII String giving the name of the parameter (see parameters name table)
|
@param szParamName : Null Terminal ASCII String giving the name of the parameter (see parameters name table)
|
||||||
@ -379,13 +324,9 @@ long __stdcall VBVMR_SetParameterFloat(char * szParamName, float Value);
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
long __stdcall VBVMR_SetParameterStringA(char *szParamName, char *szString);
|
long __stdcall VBVMR_SetParameterStringA(char *szParamName, char *szString);
|
||||||
long __stdcall VBVMR_SetParameterStringW(char *szParamName, unsigned short *wszString);
|
long __stdcall VBVMR_SetParameterStringW(char *szParamName, unsigned short *wszString);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@brief Set one or several parameters by a script ( < 48 kB ).
|
@brief Set one or several parameters by a script ( < 48 kB ).
|
||||||
@param szParamName : Null Terminal ASCII String giving the script
|
@param szParamName : Null Terminal ASCII String giving the script
|
||||||
@ -410,24 +351,12 @@ long __stdcall VBVMR_SetParameterStringW(char * szParamName, unsigned short * ws
|
|||||||
long __stdcall VBVMR_SetParameters(char *szParamScript);
|
long __stdcall VBVMR_SetParameters(char *szParamScript);
|
||||||
long __stdcall VBVMR_SetParametersW(unsigned short *szParamScript);
|
long __stdcall VBVMR_SetParametersW(unsigned short *szParamScript);
|
||||||
|
|
||||||
|
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
/* DEVICES ENUMERATOR */
|
/* DEVICES ENUMERATOR */
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
/** @name Device Enumeration Functions
|
/** @name Device Enumeration Functions
|
||||||
* @{ */
|
* @{ */
|
||||||
|
|
||||||
@ -474,15 +403,8 @@ long __stdcall VBVMR_Input_GetDeviceNumber(void);
|
|||||||
long __stdcall VBVMR_Input_GetDeviceDescA(long zindex, long *nType, char *szDeviceName, char *szHardwareId);
|
long __stdcall VBVMR_Input_GetDeviceDescA(long zindex, long *nType, char *szDeviceName, char *szHardwareId);
|
||||||
long __stdcall VBVMR_Input_GetDeviceDescW(long zindex, long *nType, unsigned short *wszDeviceName, unsigned short *wszHardwareId);
|
long __stdcall VBVMR_Input_GetDeviceDescW(long zindex, long *nType, unsigned short *wszDeviceName, unsigned short *wszHardwareId);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
/* VB-AUDIO CALLBACK */
|
/* VB-AUDIO CALLBACK */
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
@ -497,14 +419,12 @@ long __stdcall VBVMR_Input_GetDeviceDescW(long zindex, long * nType, unsigned sh
|
|||||||
/** @name VB-Audio Callback Functions
|
/** @name VB-Audio Callback Functions
|
||||||
* @{ */
|
* @{ */
|
||||||
|
|
||||||
|
|
||||||
typedef struct tagVBVMR_AUDIOINFO
|
typedef struct tagVBVMR_AUDIOINFO
|
||||||
{
|
{
|
||||||
long samplerate;
|
long samplerate;
|
||||||
long nbSamplePerFrame;
|
long nbSamplePerFrame;
|
||||||
} VBVMR_T_AUDIOINFO, *VBVMR_PT_AUDIOINFO, *VBVMR_LPT_AUDIOINFO;
|
} VBVMR_T_AUDIOINFO, *VBVMR_PT_AUDIOINFO, *VBVMR_LPT_AUDIOINFO;
|
||||||
|
|
||||||
|
|
||||||
typedef struct tagVBVMR_AUDIOBUFFER
|
typedef struct tagVBVMR_AUDIOBUFFER
|
||||||
{
|
{
|
||||||
long audiobuffer_sr; // Sampling Rate
|
long audiobuffer_sr; // Sampling Rate
|
||||||
@ -515,7 +435,6 @@ typedef struct tagVBVMR_AUDIOBUFFER
|
|||||||
float *audiobuffer_w[128]; // nbo output pointers containing frame of nbs sample (of 32bits float)
|
float *audiobuffer_w[128]; // nbo output pointers containing frame of nbs sample (of 32bits float)
|
||||||
} VBVMR_T_AUDIOBUFFER, *VBVMR_PT_AUDIOBUFFER, *VBVMR_LPT_AUDIOBUFFER;
|
} VBVMR_T_AUDIOBUFFER, *VBVMR_PT_AUDIOBUFFER, *VBVMR_LPT_AUDIOBUFFER;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@brief VB-AUDIO Callback is called for different task to Initialize, perform and end your process.
|
@brief VB-AUDIO Callback is called for different task to Initialize, perform and end your process.
|
||||||
VB-AUDIO Callback is part of single TIME CRITICAL Thread.
|
VB-AUDIO Callback is part of single TIME CRITICAL Thread.
|
||||||
@ -533,10 +452,8 @@ typedef struct tagVBVMR_AUDIOBUFFER
|
|||||||
@return : 0: always 0 (unused).
|
@return : 0: always 0 (unused).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
typedef long(__stdcall *T_VBVMR_VBAUDIOCALLBACK)(void *lpUser, long nCommand, void *lpData, long nnn);
|
typedef long(__stdcall *T_VBVMR_VBAUDIOCALLBACK)(void *lpUser, long nCommand, void *lpData, long nnn);
|
||||||
|
|
||||||
|
|
||||||
#define VBVMR_CBCOMMAND_STARTING 1 // command to initialize data according SR and buffer size
|
#define VBVMR_CBCOMMAND_STARTING 1 // command to initialize data according SR and buffer size
|
||||||
// info = (VBVMR_LPT_AUDIOINFO)lpData
|
// info = (VBVMR_LPT_AUDIOINFO)lpData
|
||||||
|
|
||||||
@ -696,7 +613,6 @@ long __stdcall VBVMR_AudioCallbackRegister(long mode, T_VBVMR_VBAUDIOCALLBACK pC
|
|||||||
long __stdcall VBVMR_AudioCallbackStart(void);
|
long __stdcall VBVMR_AudioCallbackStart(void);
|
||||||
long __stdcall VBVMR_AudioCallbackStop(void);
|
long __stdcall VBVMR_AudioCallbackStop(void);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@brief unregister your callback to release voicemeeter virtual driver
|
@brief unregister your callback to release voicemeeter virtual driver
|
||||||
(this function will automatically call VBVMR_AudioCallbackStop() function)
|
(this function will automatically call VBVMR_AudioCallbackStop() function)
|
||||||
@ -708,15 +624,8 @@ long __stdcall VBVMR_AudioCallbackStop(void);
|
|||||||
|
|
||||||
long __stdcall VBVMR_AudioCallbackUnregister(void);
|
long __stdcall VBVMR_AudioCallbackUnregister(void);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
/* */
|
/* */
|
||||||
/* Macro Buttons */
|
/* Macro Buttons */
|
||||||
@ -772,18 +681,8 @@ long __stdcall VBVMR_MacroButton_SetStatus(long nuLogicalButton, float fValue, l
|
|||||||
#define VBVMR_MACROBUTTON_MODE_TRIGGER 0x00000003 // change Trigger State
|
#define VBVMR_MACROBUTTON_MODE_TRIGGER 0x00000003 // change Trigger State
|
||||||
#define VBVMR_MACROBUTTON_MODE_COLOR 0x00000004 // change color
|
#define VBVMR_MACROBUTTON_MODE_COLOR 0x00000004 // change color
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
/* 'C' STRUCTURED INTERFACE */
|
/* 'C' STRUCTURED INTERFACE */
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
@ -800,7 +699,6 @@ typedef long (__stdcall *T_VBVMR_GetParameterFloat)(char * szParamName, float *
|
|||||||
typedef long(__stdcall *T_VBVMR_GetParameterStringA)(char *szParamName, char *szString);
|
typedef long(__stdcall *T_VBVMR_GetParameterStringA)(char *szParamName, char *szString);
|
||||||
typedef long(__stdcall *T_VBVMR_GetParameterStringW)(char *szParamName, unsigned short *wszString);
|
typedef long(__stdcall *T_VBVMR_GetParameterStringW)(char *szParamName, unsigned short *wszString);
|
||||||
|
|
||||||
|
|
||||||
typedef long(__stdcall *T_VBVMR_GetLevel)(long nType, long nuChannel, float *pValue);
|
typedef long(__stdcall *T_VBVMR_GetLevel)(long nType, long nuChannel, float *pValue);
|
||||||
typedef long(__stdcall *T_VBVMR_GetMidiMessage)(unsigned char *pMIDIBuffer, long nbByteMax);
|
typedef long(__stdcall *T_VBVMR_GetMidiMessage)(unsigned char *pMIDIBuffer, long nbByteMax);
|
||||||
typedef long(__stdcall *T_VBVMR_SendMidiMessage)(unsigned char *pMIDIBuffer, long nbByteMax);
|
typedef long(__stdcall *T_VBVMR_SendMidiMessage)(unsigned char *pMIDIBuffer, long nbByteMax);
|
||||||
@ -827,8 +725,6 @@ typedef long (__stdcall *T_VBVMR_MacroButton_IsDirty)(void);
|
|||||||
typedef long(__stdcall *T_VBVMR_MacroButton_GetStatus)(long nuLogicalButton, float *pValue, long bitmode);
|
typedef long(__stdcall *T_VBVMR_MacroButton_GetStatus)(long nuLogicalButton, float *pValue, long bitmode);
|
||||||
typedef long(__stdcall *T_VBVMR_MacroButton_SetStatus)(long nuLogicalButton, float fValue, long bitmode);
|
typedef long(__stdcall *T_VBVMR_MacroButton_SetStatus)(long nuLogicalButton, float fValue, long bitmode);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct tagVBVMR_INTERFACE
|
typedef struct tagVBVMR_INTERFACE
|
||||||
{
|
{
|
||||||
T_VBVMR_Login VBVMR_Login;
|
T_VBVMR_Login VBVMR_Login;
|
||||||
@ -867,20 +763,13 @@ typedef struct tagVBVMR_INTERFACE
|
|||||||
T_VBVMR_MacroButton_GetStatus VBVMR_MacroButton_GetStatus;
|
T_VBVMR_MacroButton_GetStatus VBVMR_MacroButton_GetStatus;
|
||||||
T_VBVMR_MacroButton_SetStatus VBVMR_MacroButton_SetStatus;
|
T_VBVMR_MacroButton_SetStatus VBVMR_MacroButton_SetStatus;
|
||||||
|
|
||||||
|
} T_VBVMR_INTERFACE, *PT_VBVMR_INTERFACE, *LPT_VBVMR_INTERFACE, *PT_VMR;
|
||||||
} T_VBVMR_INTERFACE, *PT_VBVMR_INTERFACE, *LPT_VBVMR_INTERFACE;
|
|
||||||
|
|
||||||
#ifdef VBUSE_LOCALLIB
|
#ifdef VBUSE_LOCALLIB
|
||||||
// internal used (not public)
|
// internal used (not public)
|
||||||
void __stdcall VBVMR_SetHinstance(HINSTANCE hinst);
|
void __stdcall VBVMR_SetHinstance(HINSTANCE hinst);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
/* VBAN RT PACKET */
|
/* VBAN RT PACKET */
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
@ -971,7 +860,6 @@ typedef struct tagVBAN_VMRT_PACKET //packedt ident: 0
|
|||||||
#define VMRTSTATE_MODE_SEL 0x10000000
|
#define VMRTSTATE_MODE_SEL 0x10000000
|
||||||
#define VMRTSTATE_MODE_MONITOR 0x20000000
|
#define VMRTSTATE_MODE_MONITOR 0x20000000
|
||||||
|
|
||||||
|
|
||||||
#pragma pack(1)
|
#pragma pack(1)
|
||||||
|
|
||||||
// long = 4 bytes
|
// long = 4 bytes
|
||||||
@ -1046,7 +934,6 @@ typedef struct tagVBAN_VMPARAM_STRIP
|
|||||||
#define VMRT_EQTYPE_LOSHELF 5
|
#define VMRT_EQTYPE_LOSHELF 5
|
||||||
#define VMRT_EQTYPE_HISHELF 6
|
#define VMRT_EQTYPE_HISHELF 6
|
||||||
|
|
||||||
|
|
||||||
#define expected_size_T_VBAN_VMPARAM_STRIP (8 + (8 * sizeof(short)) + (2 * 6) + (3 * 6 * sizeof(float)) + ((11 + 9 + 6 + 7) * sizeof(short)))
|
#define expected_size_T_VBAN_VMPARAM_STRIP (8 + (8 * sizeof(short)) + (2 * 6) + (3 * 6 * sizeof(float)) + ((11 + 9 + 6 + 7) * sizeof(short)))
|
||||||
// 170
|
// 170
|
||||||
|
|
||||||
@ -1080,14 +967,8 @@ long VBVMR_LoginEx(long properties);
|
|||||||
|
|
||||||
long VBVMR_MB_PushSettings(void *lpParam);
|
long VBVMR_MB_PushSettings(void *lpParam);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif /*__VOICEMEETER_REMOTE_H__*/
|
#endif /*__VOICEMEETER_REMOTE_H__*/
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1,9 +0,0 @@
|
|||||||
#ifndef __CDLL_H__
|
|
||||||
#define __CDLL_H__
|
|
||||||
|
|
||||||
#include <windows.h>
|
|
||||||
#include "VoicemeeterRemote.h"
|
|
||||||
|
|
||||||
long initialize_dll_interfaces(T_VBVMR_INTERFACE *iVMR);
|
|
||||||
|
|
||||||
#endif /*__CDLL_H__*/
|
|
||||||
17
include/interface.h
Normal file
17
include/interface.h
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2024 Onyx and Iris
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the MIT license. See `ivmr.c` for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __IVMR_H__
|
||||||
|
#define __IVMR_H__
|
||||||
|
|
||||||
|
#include "VoicemeeterRemote.h"
|
||||||
|
|
||||||
|
#define IS_64_BIT sizeof(void *) == 8
|
||||||
|
|
||||||
|
PT_VMR create_interface();
|
||||||
|
|
||||||
|
#endif /* __IVMR_H__ */
|
||||||
@ -1,9 +1,24 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2024 Onyx and Iris
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the MIT license. See `util.c` for details.
|
||||||
|
*/
|
||||||
|
|
||||||
#ifndef __UTIL_H__
|
#ifndef __UTIL_H__
|
||||||
#define __UTIL_H__
|
#define __UTIL_H__
|
||||||
|
|
||||||
void remove_name_in_path(char *szPath);
|
struct quickcommand
|
||||||
int replace_multiple_space_with_one(char *s, size_t len);
|
{
|
||||||
char *kind_as_string(char *s, enum kind kind, int n);
|
char *name;
|
||||||
char *version_as_string(char *, long v, int n);
|
char *fullcommand;
|
||||||
|
};
|
||||||
|
|
||||||
|
void remove_last_part_of_path(char *fullpath);
|
||||||
|
int log_level_from_string(const char *level);
|
||||||
|
char *kind_as_string(char *s, int kind, int n);
|
||||||
|
char *version_as_string(char *s, long v, int n);
|
||||||
|
bool is_comment(char *s);
|
||||||
|
struct quickcommand *command_in_quickcommands(const char *command, const struct quickcommand *quickcommands, int n);
|
||||||
|
|
||||||
#endif /* __UTIL_H__ */
|
#endif /* __UTIL_H__ */
|
||||||
@ -1,36 +0,0 @@
|
|||||||
#ifndef __VMR_H__
|
|
||||||
#define __VMR_H__
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include "voicemeeterRemote.h"
|
|
||||||
|
|
||||||
enum kind
|
|
||||||
{
|
|
||||||
BASIC = 1,
|
|
||||||
BANANA,
|
|
||||||
POTATO,
|
|
||||||
BASICX64,
|
|
||||||
BANANAX64,
|
|
||||||
POTATOX64,
|
|
||||||
};
|
|
||||||
|
|
||||||
long login(T_VBVMR_INTERFACE *iVMR, int kind);
|
|
||||||
long logout(T_VBVMR_INTERFACE *iVMR);
|
|
||||||
long run_voicemeeter(T_VBVMR_INTERFACE *iVMR, int kind);
|
|
||||||
long type(T_VBVMR_INTERFACE *iVMR, long *type);
|
|
||||||
long version(T_VBVMR_INTERFACE *iVMR, long *version);
|
|
||||||
|
|
||||||
bool is_pdirty(T_VBVMR_INTERFACE *iVMR);
|
|
||||||
long get_parameter_float(T_VBVMR_INTERFACE *iVMR, char *param, float *f);
|
|
||||||
long get_parameter_string(T_VBVMR_INTERFACE *iVMR, char *param, unsigned short *s);
|
|
||||||
long set_parameter_float(T_VBVMR_INTERFACE *iVMR, char *param, float val);
|
|
||||||
long set_parameter_string(T_VBVMR_INTERFACE *iVMR, char *param, char *s);
|
|
||||||
long set_parameters(T_VBVMR_INTERFACE *iVMR, char *command);
|
|
||||||
|
|
||||||
bool is_mdirty(T_VBVMR_INTERFACE *iVMR);
|
|
||||||
long macrobutton_getstatus(T_VBVMR_INTERFACE *iVMR, long n, float *val, long mode);
|
|
||||||
long macrobutton_setstatus(T_VBVMR_INTERFACE *iVMR, long n, float val, long mode);
|
|
||||||
|
|
||||||
void clear_dirty(T_VBVMR_INTERFACE *iVMR);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
46
include/wrapper.h
Normal file
46
include/wrapper.h
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2024 Onyx and Iris
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the MIT license. See `wrapper.c` for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __WRAPPER_H__
|
||||||
|
#define __WRAPPER_H__
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "voicemeeterRemote.h"
|
||||||
|
|
||||||
|
enum kind : int
|
||||||
|
{
|
||||||
|
UNKNOWN = -1,
|
||||||
|
BASIC = 1,
|
||||||
|
BANANA,
|
||||||
|
POTATO,
|
||||||
|
BASICX64,
|
||||||
|
BANANAX64,
|
||||||
|
POTATOX64,
|
||||||
|
MACROBUTTONS = 11,
|
||||||
|
STREAMERVIEW
|
||||||
|
};
|
||||||
|
|
||||||
|
long login(PT_VMR vmr, int kind);
|
||||||
|
long logout(PT_VMR vmr);
|
||||||
|
long run_voicemeeter(PT_VMR vmr, int kind);
|
||||||
|
long type(PT_VMR vmr, long *type);
|
||||||
|
long version(PT_VMR vmr, long *version);
|
||||||
|
|
||||||
|
bool is_pdirty(PT_VMR vmr);
|
||||||
|
long get_parameter_float(PT_VMR vmr, char *param, float *f);
|
||||||
|
long get_parameter_string(PT_VMR vmr, char *param, wchar_t *s);
|
||||||
|
long set_parameter_float(PT_VMR vmr, char *param, float val);
|
||||||
|
long set_parameter_string(PT_VMR vmr, char *param, char *s);
|
||||||
|
long set_parameters(PT_VMR vmr, char *command);
|
||||||
|
|
||||||
|
bool is_mdirty(PT_VMR vmr);
|
||||||
|
long macrobutton_getstatus(PT_VMR vmr, long n, float *val, long mode);
|
||||||
|
long macrobutton_setstatus(PT_VMR vmr, long n, float val, long mode);
|
||||||
|
|
||||||
|
void clear(PT_VMR vmr, bool (*f)(PT_VMR));
|
||||||
|
|
||||||
|
#endif /* __WRAPPER_H__ */
|
||||||
19
makefile
19
makefile
@ -1,38 +1,53 @@
|
|||||||
|
# Program name
|
||||||
program = vmrcli
|
program = vmrcli
|
||||||
|
|
||||||
|
# Compiler
|
||||||
CC = gcc
|
CC = gcc
|
||||||
|
|
||||||
|
# Directories
|
||||||
SRC_DIR := src
|
SRC_DIR := src
|
||||||
OBJ_DIR := obj
|
OBJ_DIR := obj
|
||||||
BIN_DIR := bin
|
BIN_DIR := bin
|
||||||
|
|
||||||
|
# Executable and source/object files
|
||||||
EXE := $(BIN_DIR)/$(program).exe
|
EXE := $(BIN_DIR)/$(program).exe
|
||||||
SRC := $(wildcard $(SRC_DIR)/*.c)
|
SRC := $(wildcard $(SRC_DIR)/*.c)
|
||||||
OBJ := $(SRC:$(SRC_DIR)/%.c=$(OBJ_DIR)/%.o)
|
OBJ := $(SRC:$(SRC_DIR)/%.c=$(OBJ_DIR)/%.o)
|
||||||
|
|
||||||
|
# Conditional compilation flags for logging
|
||||||
LOG_USE_COLOR ?= yes
|
LOG_USE_COLOR ?= yes
|
||||||
ifeq ($(LOG_USE_COLOR), yes)
|
ifeq ($(LOG_USE_COLOR), yes)
|
||||||
CPPFLAGS := -Iinclude -MMD -MP -DLOG_USE_COLOR
|
CPPFLAGS := -Iinclude -MMD -MP -DLOG_USE_COLOR
|
||||||
else
|
else
|
||||||
CPPFLAGS := -Iinclude -MMD -MP
|
CPPFLAGS := -Iinclude -MMD -MP
|
||||||
endif
|
endif
|
||||||
CFLAGS = -O -Wall -W -pedantic -ansi -std=c99
|
|
||||||
|
# Compiler and linker flags
|
||||||
|
CFLAGS = -O -Wall -W -pedantic -ansi -std=c2x
|
||||||
LDFLAGS := -Llib
|
LDFLAGS := -Llib
|
||||||
LDLIBS := -lm
|
LDLIBS := -lm
|
||||||
|
|
||||||
|
# Phony targets
|
||||||
.PHONY: all clean
|
.PHONY: all clean
|
||||||
|
|
||||||
|
# Default target
|
||||||
all: $(EXE)
|
all: $(EXE)
|
||||||
|
|
||||||
|
# Link the executable
|
||||||
$(EXE): $(OBJ) | $(BIN_DIR)
|
$(EXE): $(OBJ) | $(BIN_DIR)
|
||||||
$(CC) $(LDFLAGS) $^ $(LDLIBS) -o $@
|
$(CC) $(LDFLAGS) $^ $(LDLIBS) -o $@
|
||||||
|
|
||||||
|
# Compile source files to object files
|
||||||
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c | $(OBJ_DIR)
|
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c | $(OBJ_DIR)
|
||||||
$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
|
$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
|
||||||
|
|
||||||
|
# Create necessary directories
|
||||||
$(BIN_DIR) $(OBJ_DIR):
|
$(BIN_DIR) $(OBJ_DIR):
|
||||||
pwsh -Command New-Item -Path $@ -ItemType Directory
|
pwsh -Command New-Item -Path $@ -ItemType Directory
|
||||||
|
|
||||||
|
# Clean up generated files
|
||||||
clean:
|
clean:
|
||||||
pwsh -Command Remove-Item -Recurse $(EXE), $(OBJ_DIR)
|
pwsh -Command Remove-Item -Recurse $(BIN_DIR), $(OBJ_DIR) -force
|
||||||
|
|
||||||
|
# Include dependency files
|
||||||
-include $(OBJ:.o=.d)
|
-include $(OBJ:.o=.d)
|
||||||
|
|||||||
159
src/cdll.c
159
src/cdll.c
@ -1,159 +0,0 @@
|
|||||||
#include <stdbool.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include "cdll.h"
|
|
||||||
#include "util.h"
|
|
||||||
|
|
||||||
/*******************************************************************************/
|
|
||||||
/** GET VOICEMEETER DIRECTORY **/
|
|
||||||
/*******************************************************************************/
|
|
||||||
|
|
||||||
#define INSTALLER_UNINST_KEY "VB:Voicemeeter {17359A74-1236-5467}"
|
|
||||||
|
|
||||||
#ifndef KEY_WOW64_32KEY
|
|
||||||
#define KEY_WOW64_32KEY 0x0200
|
|
||||||
#endif
|
|
||||||
|
|
||||||
bool __cdecl registry_get_voicemeeter_folder(char *szDir)
|
|
||||||
{
|
|
||||||
char szKey[256];
|
|
||||||
char sss[1024];
|
|
||||||
DWORD nnsize = 1024;
|
|
||||||
HKEY hkResult;
|
|
||||||
LONG rep;
|
|
||||||
DWORD pptype = REG_SZ;
|
|
||||||
sss[0] = 0;
|
|
||||||
const char uninstDirKey[] = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
|
|
||||||
|
|
||||||
// build Voicemeeter uninstallation key
|
|
||||||
strcpy(szKey, uninstDirKey);
|
|
||||||
strcat(szKey, "\\");
|
|
||||||
strcat(szKey, INSTALLER_UNINST_KEY);
|
|
||||||
|
|
||||||
// open key
|
|
||||||
rep = RegOpenKeyEx(HKEY_LOCAL_MACHINE, szKey, 0, KEY_READ, &hkResult);
|
|
||||||
if (rep != ERROR_SUCCESS)
|
|
||||||
{
|
|
||||||
// if not present we consider running in 64bit mode and force to read 32bit registry
|
|
||||||
rep = RegOpenKeyEx(HKEY_LOCAL_MACHINE, szKey, 0, KEY_READ | KEY_WOW64_32KEY, &hkResult);
|
|
||||||
}
|
|
||||||
if (rep != ERROR_SUCCESS)
|
|
||||||
return false;
|
|
||||||
// read uninstall path from registry
|
|
||||||
rep = RegQueryValueEx(hkResult, "UninstallString", 0, &pptype, (unsigned char *)sss, &nnsize);
|
|
||||||
RegCloseKey(hkResult);
|
|
||||||
|
|
||||||
if (pptype != REG_SZ)
|
|
||||||
return false;
|
|
||||||
if (rep != ERROR_SUCCESS)
|
|
||||||
return false;
|
|
||||||
// remove name to get the path only
|
|
||||||
remove_name_in_path(sss);
|
|
||||||
if (nnsize > 512)
|
|
||||||
nnsize = 512;
|
|
||||||
strncpy(szDir, sss, nnsize);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*******************************************************************************/
|
|
||||||
/** GET DLL INTERFACE **/
|
|
||||||
/*******************************************************************************/
|
|
||||||
long initialize_dll_interfaces(T_VBVMR_INTERFACE *iVMR)
|
|
||||||
{
|
|
||||||
HMODULE G_H_Module = NULL;
|
|
||||||
char szDllName[1024];
|
|
||||||
memset(iVMR, 0, sizeof(T_VBVMR_INTERFACE));
|
|
||||||
|
|
||||||
// get Voicemeeter installation directory
|
|
||||||
if (!registry_get_voicemeeter_folder(szDllName))
|
|
||||||
{
|
|
||||||
// Voicemeeter not installed
|
|
||||||
return -100;
|
|
||||||
}
|
|
||||||
// use right dll according to O/S type
|
|
||||||
if (sizeof(void *) == 8)
|
|
||||||
strcat(szDllName, "\\VoicemeeterRemote64.dll");
|
|
||||||
else
|
|
||||||
strcat(szDllName, "\\VoicemeeterRemote.dll");
|
|
||||||
|
|
||||||
// Load Dll
|
|
||||||
G_H_Module = LoadLibrary(szDllName);
|
|
||||||
if (G_H_Module == NULL)
|
|
||||||
return -101;
|
|
||||||
|
|
||||||
// Get function pointers
|
|
||||||
iVMR->VBVMR_Login = (T_VBVMR_Login)GetProcAddress(G_H_Module, "VBVMR_Login");
|
|
||||||
iVMR->VBVMR_Logout = (T_VBVMR_Logout)GetProcAddress(G_H_Module, "VBVMR_Logout");
|
|
||||||
iVMR->VBVMR_RunVoicemeeter = (T_VBVMR_RunVoicemeeter)GetProcAddress(G_H_Module, "VBVMR_RunVoicemeeter");
|
|
||||||
iVMR->VBVMR_GetVoicemeeterType = (T_VBVMR_GetVoicemeeterType)GetProcAddress(G_H_Module, "VBVMR_GetVoicemeeterType");
|
|
||||||
iVMR->VBVMR_GetVoicemeeterVersion = (T_VBVMR_GetVoicemeeterVersion)GetProcAddress(G_H_Module, "VBVMR_GetVoicemeeterVersion");
|
|
||||||
|
|
||||||
iVMR->VBVMR_IsParametersDirty = (T_VBVMR_IsParametersDirty)GetProcAddress(G_H_Module, "VBVMR_IsParametersDirty");
|
|
||||||
iVMR->VBVMR_GetParameterFloat = (T_VBVMR_GetParameterFloat)GetProcAddress(G_H_Module, "VBVMR_GetParameterFloat");
|
|
||||||
iVMR->VBVMR_GetParameterStringA = (T_VBVMR_GetParameterStringA)GetProcAddress(G_H_Module, "VBVMR_GetParameterStringA");
|
|
||||||
iVMR->VBVMR_GetParameterStringW = (T_VBVMR_GetParameterStringW)GetProcAddress(G_H_Module, "VBVMR_GetParameterStringW");
|
|
||||||
iVMR->VBVMR_GetLevel = (T_VBVMR_GetLevel)GetProcAddress(G_H_Module, "VBVMR_GetLevel");
|
|
||||||
iVMR->VBVMR_GetMidiMessage = (T_VBVMR_GetMidiMessage)GetProcAddress(G_H_Module, "VBVMR_GetMidiMessage");
|
|
||||||
|
|
||||||
iVMR->VBVMR_SetParameterFloat = (T_VBVMR_SetParameterFloat)GetProcAddress(G_H_Module, "VBVMR_SetParameterFloat");
|
|
||||||
iVMR->VBVMR_SetParameters = (T_VBVMR_SetParameters)GetProcAddress(G_H_Module, "VBVMR_SetParameters");
|
|
||||||
iVMR->VBVMR_SetParametersW = (T_VBVMR_SetParametersW)GetProcAddress(G_H_Module, "VBVMR_SetParametersW");
|
|
||||||
iVMR->VBVMR_SetParameterStringA = (T_VBVMR_SetParameterStringA)GetProcAddress(G_H_Module, "VBVMR_SetParameterStringA");
|
|
||||||
iVMR->VBVMR_SetParameterStringW = (T_VBVMR_SetParameterStringW)GetProcAddress(G_H_Module, "VBVMR_SetParameterStringW");
|
|
||||||
|
|
||||||
iVMR->VBVMR_Output_GetDeviceNumber = (T_VBVMR_Output_GetDeviceNumber)GetProcAddress(G_H_Module, "VBVMR_Output_GetDeviceNumber");
|
|
||||||
iVMR->VBVMR_Output_GetDeviceDescA = (T_VBVMR_Output_GetDeviceDescA)GetProcAddress(G_H_Module, "VBVMR_Output_GetDeviceDescA");
|
|
||||||
iVMR->VBVMR_Output_GetDeviceDescW = (T_VBVMR_Output_GetDeviceDescW)GetProcAddress(G_H_Module, "VBVMR_Output_GetDeviceDescW");
|
|
||||||
iVMR->VBVMR_Input_GetDeviceNumber = (T_VBVMR_Input_GetDeviceNumber)GetProcAddress(G_H_Module, "VBVMR_Input_GetDeviceNumber");
|
|
||||||
iVMR->VBVMR_Input_GetDeviceDescA = (T_VBVMR_Input_GetDeviceDescA)GetProcAddress(G_H_Module, "VBVMR_Input_GetDeviceDescA");
|
|
||||||
iVMR->VBVMR_Input_GetDeviceDescW = (T_VBVMR_Input_GetDeviceDescW)GetProcAddress(G_H_Module, "VBVMR_Input_GetDeviceDescW");
|
|
||||||
|
|
||||||
// check pointers are valid
|
|
||||||
if (iVMR->VBVMR_Login == NULL)
|
|
||||||
return -1;
|
|
||||||
if (iVMR->VBVMR_Logout == NULL)
|
|
||||||
return -2;
|
|
||||||
if (iVMR->VBVMR_RunVoicemeeter == NULL)
|
|
||||||
return -2;
|
|
||||||
if (iVMR->VBVMR_GetVoicemeeterType == NULL)
|
|
||||||
return -3;
|
|
||||||
if (iVMR->VBVMR_GetVoicemeeterVersion == NULL)
|
|
||||||
return -4;
|
|
||||||
if (iVMR->VBVMR_IsParametersDirty == NULL)
|
|
||||||
return -5;
|
|
||||||
if (iVMR->VBVMR_GetParameterFloat == NULL)
|
|
||||||
return -6;
|
|
||||||
if (iVMR->VBVMR_GetParameterStringA == NULL)
|
|
||||||
return -7;
|
|
||||||
if (iVMR->VBVMR_GetParameterStringW == NULL)
|
|
||||||
return -8;
|
|
||||||
if (iVMR->VBVMR_GetLevel == NULL)
|
|
||||||
return -9;
|
|
||||||
if (iVMR->VBVMR_SetParameterFloat == NULL)
|
|
||||||
return -10;
|
|
||||||
if (iVMR->VBVMR_SetParameters == NULL)
|
|
||||||
return -11;
|
|
||||||
if (iVMR->VBVMR_SetParametersW == NULL)
|
|
||||||
return -12;
|
|
||||||
if (iVMR->VBVMR_SetParameterStringA == NULL)
|
|
||||||
return -13;
|
|
||||||
if (iVMR->VBVMR_SetParameterStringW == NULL)
|
|
||||||
return -14;
|
|
||||||
if (iVMR->VBVMR_GetMidiMessage == NULL)
|
|
||||||
return -15;
|
|
||||||
|
|
||||||
if (iVMR->VBVMR_Output_GetDeviceNumber == NULL)
|
|
||||||
return -30;
|
|
||||||
if (iVMR->VBVMR_Output_GetDeviceDescA == NULL)
|
|
||||||
return -31;
|
|
||||||
if (iVMR->VBVMR_Output_GetDeviceDescW == NULL)
|
|
||||||
return -32;
|
|
||||||
if (iVMR->VBVMR_Input_GetDeviceNumber == NULL)
|
|
||||||
return -33;
|
|
||||||
if (iVMR->VBVMR_Input_GetDeviceDescA == NULL)
|
|
||||||
return -34;
|
|
||||||
if (iVMR->VBVMR_Input_GetDeviceDescW == NULL)
|
|
||||||
return -35;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
234
src/interface.c
Normal file
234
src/interface.c
Normal file
@ -0,0 +1,234 @@
|
|||||||
|
/**
|
||||||
|
* @file interface.c
|
||||||
|
* @author Vincent Burel, Onyx and Iris (code@onyxandiris.online)
|
||||||
|
* @brief Functions for initializing the iVMR interface.
|
||||||
|
* Defines a single public function that returns a pointer to the interface.
|
||||||
|
* @version 0.11.0
|
||||||
|
* @date 2024-07-06
|
||||||
|
*
|
||||||
|
* @copyright Vincent Burel(c)2015-2021 All Rights Reserved
|
||||||
|
* https://github.com/vburel2018/Voicemeeter-SDK/blob/main/LICENSE
|
||||||
|
*
|
||||||
|
* Copyright (c) 2024 Onyx and Iris
|
||||||
|
* https://github.com/onyx-and-iris/vmrcli/blob/main/LICENSE
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
#include "interface.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include "log.h"
|
||||||
|
|
||||||
|
#define PRAGMA_IgnoreWCastIncompatibleFuncTypes \
|
||||||
|
_Pragma("GCC diagnostic push") \
|
||||||
|
_Pragma("GCC diagnostic ignored \"-Wcast-function-type\"")
|
||||||
|
|
||||||
|
#define PRAGMA_Pop \
|
||||||
|
_Pragma("GCC diagnostic pop")
|
||||||
|
|
||||||
|
static long initialize_dll_interfaces(PT_VMR vmr);
|
||||||
|
static bool registry_get_voicemeeter_folder(char *dll_fullpath);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Create an interface object
|
||||||
|
*
|
||||||
|
* @return PT_VMR Pointer to the iVMR interface
|
||||||
|
* May return NULL if the interface fails to initialize
|
||||||
|
*/
|
||||||
|
PT_VMR create_interface()
|
||||||
|
{
|
||||||
|
PT_VMR vmr = malloc(sizeof(T_VBVMR_INTERFACE));
|
||||||
|
if (vmr == NULL)
|
||||||
|
{
|
||||||
|
log_error("malloc failed to allocate memory");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
LONG rep = initialize_dll_interfaces(vmr);
|
||||||
|
if (rep < 0)
|
||||||
|
{
|
||||||
|
if (rep == -100)
|
||||||
|
{
|
||||||
|
log_fatal("Voicemeeter is not installed");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
log_fatal("Error loading Voicemeeter dll with code %d", rep);
|
||||||
|
}
|
||||||
|
free(vmr);
|
||||||
|
vmr = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return vmr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*******************************************************************************/
|
||||||
|
/** GET DLL INTERFACE **/
|
||||||
|
/*******************************************************************************/
|
||||||
|
#define DLL_FULLPATH_SZ 1024
|
||||||
|
#define DLL64_NAME "\\VoicemeeterRemote64.dll"
|
||||||
|
#define DLL32_NAME "\\VoicemeeterRemote.dll"
|
||||||
|
|
||||||
|
static long initialize_dll_interfaces(PT_VMR vmr)
|
||||||
|
{
|
||||||
|
HMODULE G_H_Module = NULL;
|
||||||
|
char dll_fullpath[DLL_FULLPATH_SZ];
|
||||||
|
memset(vmr, 0, sizeof(T_VBVMR_INTERFACE));
|
||||||
|
|
||||||
|
// get Voicemeeter installation directory
|
||||||
|
if (!registry_get_voicemeeter_folder(dll_fullpath))
|
||||||
|
{
|
||||||
|
// Voicemeeter not installed
|
||||||
|
return -100;
|
||||||
|
}
|
||||||
|
// use right dll according to O/S type
|
||||||
|
if (IS_64_BIT)
|
||||||
|
strncat(dll_fullpath, DLL64_NAME, DLL_FULLPATH_SZ - strlen(DLL64_NAME) - 1);
|
||||||
|
else
|
||||||
|
strncat(dll_fullpath, DLL32_NAME, DLL_FULLPATH_SZ - strlen(DLL32_NAME) - 1);
|
||||||
|
|
||||||
|
// Load Dll
|
||||||
|
G_H_Module = LoadLibrary(dll_fullpath);
|
||||||
|
if (G_H_Module == NULL)
|
||||||
|
return -101;
|
||||||
|
|
||||||
|
PRAGMA_IgnoreWCastIncompatibleFuncTypes;
|
||||||
|
|
||||||
|
// Get function pointers
|
||||||
|
vmr->VBVMR_Login = (T_VBVMR_Login)GetProcAddress(G_H_Module, "VBVMR_Login");
|
||||||
|
vmr->VBVMR_Logout = (T_VBVMR_Logout)GetProcAddress(G_H_Module, "VBVMR_Logout");
|
||||||
|
vmr->VBVMR_RunVoicemeeter = (T_VBVMR_RunVoicemeeter)GetProcAddress(G_H_Module, "VBVMR_RunVoicemeeter");
|
||||||
|
vmr->VBVMR_GetVoicemeeterType = (T_VBVMR_GetVoicemeeterType)GetProcAddress(G_H_Module, "VBVMR_GetVoicemeeterType");
|
||||||
|
vmr->VBVMR_GetVoicemeeterVersion = (T_VBVMR_GetVoicemeeterVersion)GetProcAddress(G_H_Module, "VBVMR_GetVoicemeeterVersion");
|
||||||
|
|
||||||
|
vmr->VBVMR_IsParametersDirty = (T_VBVMR_IsParametersDirty)GetProcAddress(G_H_Module, "VBVMR_IsParametersDirty");
|
||||||
|
vmr->VBVMR_GetParameterFloat = (T_VBVMR_GetParameterFloat)GetProcAddress(G_H_Module, "VBVMR_GetParameterFloat");
|
||||||
|
vmr->VBVMR_GetParameterStringA = (T_VBVMR_GetParameterStringA)GetProcAddress(G_H_Module, "VBVMR_GetParameterStringA");
|
||||||
|
vmr->VBVMR_GetParameterStringW = (T_VBVMR_GetParameterStringW)GetProcAddress(G_H_Module, "VBVMR_GetParameterStringW");
|
||||||
|
vmr->VBVMR_GetLevel = (T_VBVMR_GetLevel)GetProcAddress(G_H_Module, "VBVMR_GetLevel");
|
||||||
|
vmr->VBVMR_GetMidiMessage = (T_VBVMR_GetMidiMessage)GetProcAddress(G_H_Module, "VBVMR_GetMidiMessage");
|
||||||
|
|
||||||
|
vmr->VBVMR_SetParameterFloat = (T_VBVMR_SetParameterFloat)GetProcAddress(G_H_Module, "VBVMR_SetParameterFloat");
|
||||||
|
vmr->VBVMR_SetParameters = (T_VBVMR_SetParameters)GetProcAddress(G_H_Module, "VBVMR_SetParameters");
|
||||||
|
vmr->VBVMR_SetParametersW = (T_VBVMR_SetParametersW)GetProcAddress(G_H_Module, "VBVMR_SetParametersW");
|
||||||
|
vmr->VBVMR_SetParameterStringA = (T_VBVMR_SetParameterStringA)GetProcAddress(G_H_Module, "VBVMR_SetParameterStringA");
|
||||||
|
vmr->VBVMR_SetParameterStringW = (T_VBVMR_SetParameterStringW)GetProcAddress(G_H_Module, "VBVMR_SetParameterStringW");
|
||||||
|
|
||||||
|
vmr->VBVMR_Output_GetDeviceNumber = (T_VBVMR_Output_GetDeviceNumber)GetProcAddress(G_H_Module, "VBVMR_Output_GetDeviceNumber");
|
||||||
|
vmr->VBVMR_Output_GetDeviceDescA = (T_VBVMR_Output_GetDeviceDescA)GetProcAddress(G_H_Module, "VBVMR_Output_GetDeviceDescA");
|
||||||
|
vmr->VBVMR_Output_GetDeviceDescW = (T_VBVMR_Output_GetDeviceDescW)GetProcAddress(G_H_Module, "VBVMR_Output_GetDeviceDescW");
|
||||||
|
vmr->VBVMR_Input_GetDeviceNumber = (T_VBVMR_Input_GetDeviceNumber)GetProcAddress(G_H_Module, "VBVMR_Input_GetDeviceNumber");
|
||||||
|
vmr->VBVMR_Input_GetDeviceDescA = (T_VBVMR_Input_GetDeviceDescA)GetProcAddress(G_H_Module, "VBVMR_Input_GetDeviceDescA");
|
||||||
|
vmr->VBVMR_Input_GetDeviceDescW = (T_VBVMR_Input_GetDeviceDescW)GetProcAddress(G_H_Module, "VBVMR_Input_GetDeviceDescW");
|
||||||
|
|
||||||
|
vmr->VBVMR_MacroButton_IsDirty = (T_VBVMR_MacroButton_IsDirty)GetProcAddress(G_H_Module, "VBVMR_MacroButton_IsDirty");
|
||||||
|
vmr->VBVMR_MacroButton_GetStatus = (T_VBVMR_MacroButton_GetStatus)GetProcAddress(G_H_Module, "VBVMR_MacroButton_GetStatus");
|
||||||
|
vmr->VBVMR_MacroButton_SetStatus = (T_VBVMR_MacroButton_SetStatus)GetProcAddress(G_H_Module, "VBVMR_MacroButton_SetStatus");
|
||||||
|
|
||||||
|
PRAGMA_Pop;
|
||||||
|
|
||||||
|
// check pointers are valid
|
||||||
|
if (vmr->VBVMR_Login == NULL)
|
||||||
|
return -1;
|
||||||
|
if (vmr->VBVMR_Logout == NULL)
|
||||||
|
return -2;
|
||||||
|
if (vmr->VBVMR_RunVoicemeeter == NULL)
|
||||||
|
return -3;
|
||||||
|
if (vmr->VBVMR_GetVoicemeeterType == NULL)
|
||||||
|
return -4;
|
||||||
|
if (vmr->VBVMR_GetVoicemeeterVersion == NULL)
|
||||||
|
return -5;
|
||||||
|
if (vmr->VBVMR_IsParametersDirty == NULL)
|
||||||
|
return -6;
|
||||||
|
if (vmr->VBVMR_GetParameterFloat == NULL)
|
||||||
|
return -7;
|
||||||
|
if (vmr->VBVMR_GetParameterStringA == NULL)
|
||||||
|
return -8;
|
||||||
|
if (vmr->VBVMR_GetParameterStringW == NULL)
|
||||||
|
return -9;
|
||||||
|
if (vmr->VBVMR_GetLevel == NULL)
|
||||||
|
return -10;
|
||||||
|
if (vmr->VBVMR_SetParameterFloat == NULL)
|
||||||
|
return -11;
|
||||||
|
if (vmr->VBVMR_SetParameters == NULL)
|
||||||
|
return -12;
|
||||||
|
if (vmr->VBVMR_SetParametersW == NULL)
|
||||||
|
return -13;
|
||||||
|
if (vmr->VBVMR_SetParameterStringA == NULL)
|
||||||
|
return -14;
|
||||||
|
if (vmr->VBVMR_SetParameterStringW == NULL)
|
||||||
|
return -15;
|
||||||
|
if (vmr->VBVMR_GetMidiMessage == NULL)
|
||||||
|
return -16;
|
||||||
|
|
||||||
|
if (vmr->VBVMR_Output_GetDeviceNumber == NULL)
|
||||||
|
return -30;
|
||||||
|
if (vmr->VBVMR_Output_GetDeviceDescA == NULL)
|
||||||
|
return -31;
|
||||||
|
if (vmr->VBVMR_Output_GetDeviceDescW == NULL)
|
||||||
|
return -32;
|
||||||
|
if (vmr->VBVMR_Input_GetDeviceNumber == NULL)
|
||||||
|
return -33;
|
||||||
|
if (vmr->VBVMR_Input_GetDeviceDescA == NULL)
|
||||||
|
return -34;
|
||||||
|
if (vmr->VBVMR_Input_GetDeviceDescW == NULL)
|
||||||
|
return -35;
|
||||||
|
|
||||||
|
if (vmr->VBVMR_MacroButton_IsDirty == NULL)
|
||||||
|
return -36;
|
||||||
|
if (vmr->VBVMR_MacroButton_GetStatus == NULL)
|
||||||
|
return -37;
|
||||||
|
if (vmr->VBVMR_MacroButton_SetStatus == NULL)
|
||||||
|
return -38;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*******************************************************************************/
|
||||||
|
/** GET VOICEMEETER DIRECTORY **/
|
||||||
|
/*******************************************************************************/
|
||||||
|
|
||||||
|
#define INSTALLER_DIR_KEY "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall"
|
||||||
|
#define INSTALLER_UNINST_KEY "VB:Voicemeeter {17359A74-1236-5467}"
|
||||||
|
|
||||||
|
#ifndef KEY_WOW64_32KEY
|
||||||
|
#define KEY_WOW64_32KEY 0x0200
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define UNINSTALL_KEY_SZ 256
|
||||||
|
#define UNINSTALL_PATH_SZ 1024
|
||||||
|
|
||||||
|
static bool registry_get_voicemeeter_folder(char *dll_fullpath)
|
||||||
|
{
|
||||||
|
// build Voicemeeter uninstallation key
|
||||||
|
char uninstall_key[UNINSTALL_KEY_SZ];
|
||||||
|
snprintf(uninstall_key, UNINSTALL_KEY_SZ, "%s\\%s", INSTALLER_DIR_KEY, INSTALLER_UNINST_KEY);
|
||||||
|
|
||||||
|
// open key
|
||||||
|
HKEY result;
|
||||||
|
LONG rep = RegOpenKeyEx(HKEY_LOCAL_MACHINE, uninstall_key, 0, KEY_READ, &result);
|
||||||
|
if (rep != ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
// if not present we consider running in 64bit mode and force to read 32bit registry
|
||||||
|
rep = RegOpenKeyEx(HKEY_LOCAL_MACHINE, uninstall_key, 0, KEY_READ | KEY_WOW64_32KEY, &result);
|
||||||
|
}
|
||||||
|
if (rep != ERROR_SUCCESS)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// read uninstall path from registry
|
||||||
|
DWORD pptype = REG_SZ;
|
||||||
|
DWORD len_uninstall_path = UNINSTALL_PATH_SZ;
|
||||||
|
char uninstall_path[UNINSTALL_PATH_SZ] = {0};
|
||||||
|
rep = RegQueryValueEx(result, "UninstallString", 0, &pptype, (unsigned char *)uninstall_path, &len_uninstall_path);
|
||||||
|
RegCloseKey(result);
|
||||||
|
|
||||||
|
if (pptype != REG_SZ)
|
||||||
|
return false;
|
||||||
|
if (rep != ERROR_SUCCESS)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// remove name to get the path only
|
||||||
|
remove_last_part_of_path(uninstall_path);
|
||||||
|
snprintf(dll_fullpath, DLL_FULLPATH_SZ, uninstall_path);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
@ -118,7 +118,7 @@ void log_set_quiet(bool enable)
|
|||||||
|
|
||||||
int log_add_callback(log_LogFn fn, void *udata, int level)
|
int log_add_callback(log_LogFn fn, void *udata, int level)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < MAX_CALLBACKS; i++)
|
for (int i = 0; i < MAX_CALLBACKS; ++i)
|
||||||
{
|
{
|
||||||
if (!L.callbacks[i].fn)
|
if (!L.callbacks[i].fn)
|
||||||
{
|
{
|
||||||
@ -163,7 +163,7 @@ void log_log(int level, const char *file, int line, const char *fmt, ...)
|
|||||||
va_end(ev.ap);
|
va_end(ev.ap);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < MAX_CALLBACKS && L.callbacks[i].fn; i++)
|
for (int i = 0; i < MAX_CALLBACKS && L.callbacks[i].fn; ++i)
|
||||||
{
|
{
|
||||||
Callback *cb = &L.callbacks[i];
|
Callback *cb = &L.callbacks[i];
|
||||||
if (level >= cb->level)
|
if (level >= cb->level)
|
||||||
|
|||||||
143
src/util.c
143
src/util.c
@ -1,65 +1,68 @@
|
|||||||
#include <stddef.h>
|
/**
|
||||||
|
* @file util.c
|
||||||
|
* @author Onyx and Iris (code@onyxandiris.online)
|
||||||
|
* @brief Utility functions.
|
||||||
|
* @version 0.11.0
|
||||||
|
* @date 2024-07-06
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2024
|
||||||
|
* https://github.com/onyx-and-iris/vmrcli/blob/main/LICENSE
|
||||||
|
*/
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include "vmr.h"
|
#include <string.h>
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
#include "log.h"
|
||||||
|
|
||||||
void remove_name_in_path(char *szPath)
|
/**
|
||||||
|
* @brief Removes the last part of a path
|
||||||
|
*
|
||||||
|
* @param fullpath
|
||||||
|
*/
|
||||||
|
void remove_last_part_of_path(char *fullpath)
|
||||||
{
|
{
|
||||||
char *p = szPath;
|
char *p;
|
||||||
|
|
||||||
while (*p++)
|
if ((p = strrchr(fullpath, '\\')) != NULL)
|
||||||
;
|
{
|
||||||
while (p > szPath && *p != '\\')
|
|
||||||
p--;
|
|
||||||
if (*p == '\\')
|
|
||||||
*p = '\0';
|
*p = '\0';
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief replaces multiple newlines and tabs with single spaces
|
* @brief Gets log level as int from string
|
||||||
*
|
* @param level Log level as string
|
||||||
* @param s the string to be reduced
|
* @return int Log level as int, or -1 if not found
|
||||||
* @param len current length of the string
|
|
||||||
* @return int new length of the string
|
|
||||||
*/
|
*/
|
||||||
int replace_multiple_space_with_one(char *s, size_t len)
|
int log_level_from_string(const char *level)
|
||||||
{
|
{
|
||||||
int j = 0;
|
if (strcmp(level, "TRACE") == 0)
|
||||||
int count = 0;
|
return LOG_TRACE;
|
||||||
|
else if (strcmp(level, "DEBUG") == 0)
|
||||||
if (len == 1 && (s[0] == ' ' || s[0] == '\t'))
|
return LOG_DEBUG;
|
||||||
{
|
else if (strcmp(level, "INFO") == 0)
|
||||||
s[0] = '\0';
|
return LOG_INFO;
|
||||||
return len;
|
else if (strcmp(level, "WARN") == 0)
|
||||||
|
return LOG_WARN;
|
||||||
|
else if (strcmp(level, "ERROR") == 0)
|
||||||
|
return LOG_ERROR;
|
||||||
|
else if (strcmp(level, "FATAL") == 0)
|
||||||
|
return LOG_FATAL;
|
||||||
|
else
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (len < 2)
|
/**
|
||||||
return len;
|
* @brief Converts Voicemeeter's kind into a string.
|
||||||
|
*
|
||||||
for (int i = 0; s[i] != '\0'; i++)
|
* @param s Pointer to a character buffer receiving the kind
|
||||||
|
* @param kind The kind of Voicemeeter.
|
||||||
|
* @param n Maximum number of characters to be written to the buffer
|
||||||
|
* @return char* String representation of the kind of Voicemeeter.
|
||||||
|
*/
|
||||||
|
char *kind_as_string(char *s, int kind, int n)
|
||||||
{
|
{
|
||||||
if (s[i] == ' ' || s[i] == '\t')
|
static const char *kinds[] = {
|
||||||
{
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (s[i] != ' ' && s[i] != '\t')
|
|
||||||
{
|
|
||||||
if (count >= 1)
|
|
||||||
{
|
|
||||||
count = 0;
|
|
||||||
s[j++] = ' ';
|
|
||||||
}
|
|
||||||
s[j++] = s[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
s[j] = '\0';
|
|
||||||
return j;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *kind_as_string(char *s, enum kind kind, int n)
|
|
||||||
{
|
|
||||||
char *kinds[] = {
|
|
||||||
"Basic",
|
"Basic",
|
||||||
"Banana",
|
"Banana",
|
||||||
"Potato",
|
"Potato",
|
||||||
@ -72,12 +75,12 @@ char *kind_as_string(char *s, enum kind kind, int n)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief returns Voicemeeter's version as a string
|
* @brief Converts Voicemeeter's version into a string.
|
||||||
*
|
*
|
||||||
* @param s string buffer the version will be written to
|
* @param s Pointer to a character buffer receiving the version
|
||||||
* @param v unprocessed version as a long int
|
* @param v Unprocessed version as a long int
|
||||||
* @param n maximum number of characters to be written to the buffer
|
* @param n Maximum number of characters to be written to the buffer
|
||||||
* @return char*
|
* @return char* String representation of the Voicemeeter version
|
||||||
*/
|
*/
|
||||||
char *version_as_string(char *s, long v, int n)
|
char *version_as_string(char *s, long v, int n)
|
||||||
{
|
{
|
||||||
@ -88,3 +91,37 @@ char *version_as_string(char *s, long v, int n)
|
|||||||
snprintf(s, n, "%i.%i.%i.%i", (int)v1, (int)v2, (int)v3, (int)v4);
|
snprintf(s, n, "%i.%i.%i.%i", (int)v1, (int)v2, (int)v3, (int)v4);
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Is the current input a comment
|
||||||
|
*
|
||||||
|
* @param s Pointer to the current input
|
||||||
|
* @return true
|
||||||
|
* @return false
|
||||||
|
*/
|
||||||
|
bool is_comment(char *s)
|
||||||
|
{
|
||||||
|
return s[0] == '#';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Searches the quickcommands array for a quickcommand
|
||||||
|
* corresponding to the command_key.
|
||||||
|
*
|
||||||
|
* @param command_key The key used to search for the quickcommand
|
||||||
|
* @param quickcommands Pointer to an array of quickcommands
|
||||||
|
* @param n The number of quickcommands
|
||||||
|
* @return struct quickcommand* Pointer to the found quickcommand
|
||||||
|
* May return NULL if quickcommand not found.
|
||||||
|
*/
|
||||||
|
struct quickcommand *command_in_quickcommands(const char *command_key, const struct quickcommand *quickcommands, int n)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < n; ++i)
|
||||||
|
{
|
||||||
|
if (strcmp(command_key, quickcommands[i].name) == 0)
|
||||||
|
{
|
||||||
|
return (struct quickcommand *)(quickcommands + i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|||||||
133
src/vmr.c
133
src/vmr.c
@ -1,133 +0,0 @@
|
|||||||
#include <windows.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <time.h>
|
|
||||||
#include "vmr.h"
|
|
||||||
#include "log.h"
|
|
||||||
#include "util.h"
|
|
||||||
|
|
||||||
#define VERSION_STR_LEN 128
|
|
||||||
#define KIND_STR_LEN 64
|
|
||||||
|
|
||||||
long login(T_VBVMR_INTERFACE *vmr, int kind)
|
|
||||||
{
|
|
||||||
int rep;
|
|
||||||
long v;
|
|
||||||
|
|
||||||
rep = vmr->VBVMR_Login();
|
|
||||||
if (rep == 1)
|
|
||||||
{
|
|
||||||
run_voicemeeter(vmr, kind);
|
|
||||||
char kind_s[KIND_STR_LEN];
|
|
||||||
log_info(
|
|
||||||
"Launching Voicemeeter %s GUI",
|
|
||||||
kind_as_string(kind_s, kind, KIND_STR_LEN));
|
|
||||||
|
|
||||||
time_t endwait;
|
|
||||||
int timeout = 2;
|
|
||||||
|
|
||||||
endwait = time(NULL) + timeout;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
if ((rep = version(vmr, &v)) == 0)
|
|
||||||
break;
|
|
||||||
Sleep(50);
|
|
||||||
} while (time(NULL) < endwait);
|
|
||||||
}
|
|
||||||
if (rep == 0)
|
|
||||||
{
|
|
||||||
version(vmr, &v);
|
|
||||||
char version_s[VERSION_STR_LEN];
|
|
||||||
log_info(
|
|
||||||
"Successfully logged into the Voicemeeter API v%s",
|
|
||||||
version_as_string(version_s, v, VERSION_STR_LEN));
|
|
||||||
clear_dirty(vmr);
|
|
||||||
}
|
|
||||||
return rep;
|
|
||||||
}
|
|
||||||
|
|
||||||
long logout(T_VBVMR_INTERFACE *vmr)
|
|
||||||
{
|
|
||||||
int rep;
|
|
||||||
|
|
||||||
Sleep(20); /* give time for last command */
|
|
||||||
rep = vmr->VBVMR_Logout();
|
|
||||||
if (rep == 0)
|
|
||||||
log_info("Successfully logged out of the Voicemeeter API");
|
|
||||||
return rep;
|
|
||||||
}
|
|
||||||
|
|
||||||
long run_voicemeeter(T_VBVMR_INTERFACE *vmr, int kind)
|
|
||||||
{
|
|
||||||
log_trace("VBVMR_RunVoicemeeter(%d)", kind);
|
|
||||||
return vmr->VBVMR_RunVoicemeeter((long)kind);
|
|
||||||
}
|
|
||||||
|
|
||||||
long type(T_VBVMR_INTERFACE *vmr, long *type)
|
|
||||||
{
|
|
||||||
log_trace("VBVMR_GetVoicemeeterType(<long> *t)");
|
|
||||||
return vmr->VBVMR_GetVoicemeeterType(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
long version(T_VBVMR_INTERFACE *vmr, long *version)
|
|
||||||
{
|
|
||||||
log_trace("VBVMR_GetVoicemeeterVersion(<long> *v)");
|
|
||||||
return vmr->VBVMR_GetVoicemeeterVersion(version);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_pdirty(T_VBVMR_INTERFACE *vmr)
|
|
||||||
{
|
|
||||||
log_trace("VBVMR_IsParametersDirty()");
|
|
||||||
return vmr->VBVMR_IsParametersDirty() == 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
long get_parameter_float(T_VBVMR_INTERFACE *vmr, char *param, float *f)
|
|
||||||
{
|
|
||||||
log_trace("VBVMR_GetParameterFloat(%s, <float> *f)", param);
|
|
||||||
return vmr->VBVMR_GetParameterFloat(param, f);
|
|
||||||
}
|
|
||||||
|
|
||||||
long get_parameter_string(T_VBVMR_INTERFACE *vmr, char *param, unsigned short *s)
|
|
||||||
{
|
|
||||||
log_trace("VBVMR_GetParameterStringW(%s, <unsigned short> *s)", param);
|
|
||||||
return vmr->VBVMR_GetParameterStringW(param, s);
|
|
||||||
}
|
|
||||||
|
|
||||||
long set_parameter_float(T_VBVMR_INTERFACE *vmr, char *param, float val)
|
|
||||||
{
|
|
||||||
log_trace("VBVMR_SetParameterFloat(%s, %.1f)", param, val);
|
|
||||||
return vmr->VBVMR_SetParameterFloat(param, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
long set_parameter_string(T_VBVMR_INTERFACE *vmr, char *param, char *s)
|
|
||||||
{
|
|
||||||
log_trace("VBVMR_SetParameterStringA(%s, %s)", param, s);
|
|
||||||
return vmr->VBVMR_SetParameterStringA(param, s);
|
|
||||||
}
|
|
||||||
|
|
||||||
long set_parameters(T_VBVMR_INTERFACE *vmr, char *command)
|
|
||||||
{
|
|
||||||
log_trace("VBVMR_SetParameters(%s)", command);
|
|
||||||
return vmr->VBVMR_SetParameters(command);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_mdirty(T_VBVMR_INTERFACE *vmr)
|
|
||||||
{
|
|
||||||
return vmr->VBVMR_MacroButton_IsDirty() == 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
long macrobutton_getstatus(T_VBVMR_INTERFACE *vmr, long n, float *val, long mode)
|
|
||||||
{
|
|
||||||
return vmr->VBVMR_MacroButton_GetStatus(n, val, mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
long macrobutton_setstatus(T_VBVMR_INTERFACE *vmr, long n, float val, long mode)
|
|
||||||
{
|
|
||||||
return vmr->VBVMR_MacroButton_SetStatus(n, val, mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
void clear_dirty(T_VBVMR_INTERFACE *vmr)
|
|
||||||
{
|
|
||||||
Sleep(30);
|
|
||||||
while (is_pdirty(vmr))
|
|
||||||
Sleep(1);
|
|
||||||
}
|
|
||||||
437
src/vmrcli.c
437
src/vmrcli.c
@ -1,237 +1,353 @@
|
|||||||
|
/**
|
||||||
|
* @file vmrcli.c
|
||||||
|
* @author Onyx and Iris (code@onyxandiris.online)
|
||||||
|
* @brief A Voicemeeter Remote Command Line Interface
|
||||||
|
* @version 0.11.0
|
||||||
|
* @date 2024-07-06
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2024
|
||||||
|
* https://github.com/onyx-and-iris/vmrcli/blob/main/LICENSE
|
||||||
|
*/
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdbool.h>
|
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
#include "cdll.h"
|
#include <windows.h>
|
||||||
#include "vmr.h"
|
#include "interface.h"
|
||||||
|
#include "wrapper.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
#define MAX_LINE 512
|
#define USAGE "Usage: .\\vmrcli.exe [-h] [-v] [-i|-I] [-f] [-k] [-l] [-e] [-c] [-m] [-s] <api commands>\n" \
|
||||||
|
"Where: \n" \
|
||||||
|
"\th: Print the help message\n" \
|
||||||
|
"\tv: Print the version number\n" \
|
||||||
|
"\ti: Enable interactive mode, use (-I) to disable the '>>' prompt\n" \
|
||||||
|
"\tf: Do not split input on spaces\n" \
|
||||||
|
"\tk: The kind of Voicemeeter (basic, banana, potato)\n" \
|
||||||
|
"\tl: Set log level, must be one of TRACE, DEBUG, INFO, WARN, ERROR, or FATAL\n" \
|
||||||
|
"\te: Enable extra console output (toggle, set messages)\n" \
|
||||||
|
"\tc: Load a user configuration (give the full file path)\n" \
|
||||||
|
"\tm: Launch the MacroButtons application\n" \
|
||||||
|
"\ts: Launch the StreamerView application"
|
||||||
|
#define OPTSTR ":hvk:msc:iIfl:e"
|
||||||
|
#define MAX_LINE 4096 /* Size of the input buffer */
|
||||||
|
#define RES_SZ 512 /* Size of the buffer passed to VBVMR_GetParameterStringW */
|
||||||
|
#define COUNT_OF(x) (sizeof(x) / sizeof(x[0]))
|
||||||
|
#define DELIMITERS " \t;,"
|
||||||
|
#define VERSION "0.12.0"
|
||||||
|
|
||||||
enum
|
/**
|
||||||
|
* @enum The kind of values a get call may return.
|
||||||
|
*/
|
||||||
|
enum restype : int
|
||||||
{
|
{
|
||||||
FLOAT_T,
|
FLOAT_T,
|
||||||
STRING_T,
|
STRING_T,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @struct A struct used for:
|
||||||
|
* - tracking the type of value stored
|
||||||
|
* - storing the result of a get call
|
||||||
|
*/
|
||||||
struct result
|
struct result
|
||||||
{
|
{
|
||||||
int type;
|
enum restype type;
|
||||||
union val
|
union val
|
||||||
{
|
{
|
||||||
float f;
|
float f;
|
||||||
wchar_t s[MAX_LINE];
|
wchar_t s[RES_SZ];
|
||||||
} val;
|
} val;
|
||||||
};
|
};
|
||||||
|
|
||||||
void help(void);
|
static bool eflag = false;
|
||||||
enum kind set_kind(char *kval);
|
|
||||||
int init_voicemeeter(T_VBVMR_INTERFACE *vmr, int kind);
|
static void terminate(PT_VMR vmr, char *msg);
|
||||||
void interactive(T_VBVMR_INTERFACE *vmr);
|
static void usage();
|
||||||
void parse_command(T_VBVMR_INTERFACE *vmr, char *command);
|
static enum kind set_kind(char *kval);
|
||||||
void get(T_VBVMR_INTERFACE *vmr, char *command, struct result *res);
|
static void interactive(PT_VMR vmr, bool with_prompt, char *delimiters);
|
||||||
|
static void parse_input(PT_VMR vmr, char *input, char *delimiters);
|
||||||
|
static void parse_command(PT_VMR vmr, char *command);
|
||||||
|
static void get(PT_VMR vmr, char *command, struct result *res);
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
bool iflag = false;
|
bool iflag = false,
|
||||||
|
mflag = false,
|
||||||
|
sflag = false,
|
||||||
|
cflag = false,
|
||||||
|
fflag = false,
|
||||||
|
with_prompt = true;
|
||||||
int opt;
|
int opt;
|
||||||
char *kvalue = "";
|
int log_level = LOG_WARN;
|
||||||
int dvalue;
|
char *cvalue;
|
||||||
enum kind kind = BANANAX64;
|
enum kind kind = BANANAX64;
|
||||||
|
|
||||||
if (argc == 1)
|
if (argc == 1)
|
||||||
{
|
{
|
||||||
help();
|
usage();
|
||||||
exit(EXIT_SUCCESS);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
log_set_level(LOG_WARN);
|
log_set_level(log_level);
|
||||||
|
|
||||||
while ((opt = getopt(argc, argv, "k:ihD:")) != -1)
|
opterr = 0;
|
||||||
|
while ((opt = getopt(argc, argv, OPTSTR)) != -1)
|
||||||
{
|
{
|
||||||
switch (opt)
|
switch (opt)
|
||||||
{
|
{
|
||||||
|
case 'v':
|
||||||
|
printf("vmrcli version %s\n", VERSION);
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
case 'k':
|
||||||
|
kind = set_kind(optarg);
|
||||||
|
if (kind == UNKNOWN)
|
||||||
|
{
|
||||||
|
log_fatal("Unknown Voicemeeter kind '%s'", optarg);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'm':
|
||||||
|
mflag = true;
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
sflag = true;
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
cflag = true;
|
||||||
|
cvalue = optarg;
|
||||||
|
break;
|
||||||
|
case 'I':
|
||||||
|
with_prompt = false;
|
||||||
|
[[fallthrough]];
|
||||||
case 'i':
|
case 'i':
|
||||||
iflag = true;
|
iflag = true;
|
||||||
break;
|
break;
|
||||||
case 'k':
|
case 'f':
|
||||||
kvalue = optarg;
|
fflag = true;
|
||||||
kind = set_kind(kvalue);
|
|
||||||
break;
|
break;
|
||||||
case 'h':
|
case 'l':
|
||||||
help();
|
log_level = log_level_from_string(optarg);
|
||||||
exit(EXIT_SUCCESS);
|
if (log_level != -1)
|
||||||
case 'D':
|
|
||||||
dvalue = atoi(optarg);
|
|
||||||
if (dvalue >= LOG_TRACE && dvalue <= LOG_FATAL)
|
|
||||||
{
|
{
|
||||||
log_set_level(dvalue);
|
log_set_level(log_level);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
log_error(
|
log_warn(
|
||||||
"-D arg out of range, expected value from 0 up to 5\n"
|
"-l arg out of range, expected TRACE, DEBUG, INFO, WARN, ERROR, or FATAL\n"
|
||||||
"Log level will default to LOG_WARN (3).\n");
|
"Log level will default to LOG_WARN (3).\n");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case 'e':
|
||||||
|
eflag = true;
|
||||||
|
break;
|
||||||
|
case '?':
|
||||||
|
log_fatal("unknown option -- '%c'\n"
|
||||||
|
"Try .\\vmrcli.exe -h for more information.",
|
||||||
|
optopt);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
case ':':
|
||||||
|
log_fatal("missing argument for option -- '%c'\n"
|
||||||
|
"Try .\\vmrcli.exe -h for more information.",
|
||||||
|
optopt);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
case 'h':
|
||||||
|
[[fallthrough]];
|
||||||
default:
|
default:
|
||||||
abort();
|
usage();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
T_VBVMR_INTERFACE iVMR;
|
PT_VMR vmr = create_interface();
|
||||||
T_VBVMR_INTERFACE *vmr = &iVMR;
|
if (vmr == NULL)
|
||||||
|
|
||||||
int rep = init_voicemeeter(vmr, kind);
|
|
||||||
if (rep != 0)
|
|
||||||
{
|
{
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
long rep = login(vmr, kind);
|
||||||
|
if (rep != 0)
|
||||||
|
{
|
||||||
|
if (rep == -2)
|
||||||
|
terminate(vmr, "Timeout logging into the API.");
|
||||||
|
else
|
||||||
|
terminate(vmr, "Error logging into the Voicemeeter API");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mflag)
|
||||||
|
{
|
||||||
|
run_voicemeeter(vmr, MACROBUTTONS);
|
||||||
|
log_info("MacroButtons app launched");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sflag)
|
||||||
|
{
|
||||||
|
run_voicemeeter(vmr, STREAMERVIEW);
|
||||||
|
log_info("StreamerView app launched");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cflag)
|
||||||
|
{
|
||||||
|
set_parameter_string(vmr, "command.load", cvalue);
|
||||||
|
log_info("Profile %s loaded", cvalue);
|
||||||
|
Sleep(300);
|
||||||
|
clear(vmr, is_pdirty);
|
||||||
|
}
|
||||||
|
|
||||||
|
char *delimiter_ptr = DELIMITERS;
|
||||||
|
if (fflag)
|
||||||
|
{
|
||||||
|
delimiter_ptr++; /* skip space delimiter */
|
||||||
|
}
|
||||||
|
|
||||||
if (iflag)
|
if (iflag)
|
||||||
{
|
{
|
||||||
puts("Interactive mode enabled. Enter 'Q' to exit.");
|
puts("Interactive mode enabled. Enter 'Q' to exit.");
|
||||||
interactive(vmr);
|
interactive(vmr, with_prompt, delimiter_ptr);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (int i = optind; i < argc; i++)
|
for (int i = optind; i < argc; ++i)
|
||||||
{
|
{
|
||||||
parse_command(vmr, argv[i]);
|
parse_input(vmr, argv[i], delimiter_ptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rep = logout(vmr);
|
rep = logout(vmr);
|
||||||
if (rep == 0)
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
else
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief prints the help dialogue
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
void help()
|
|
||||||
{
|
|
||||||
puts(
|
|
||||||
"Usage: ./vmrcli.exe [-h] [-i] [-k] [-D] <api commands>\n"
|
|
||||||
"Where: \n"
|
|
||||||
"\th: Prints the help dialogue\n"
|
|
||||||
"\ti: Enable interactive mode\n"
|
|
||||||
"\tk: The kind of Voicemeeter (basic, banana, potato)\n"
|
|
||||||
"\tD: Set log level 0=TRACE, 1=DEBUG, 2=INFO, 3=WARN, 4=ERROR, 5=FATAL");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Set the kind object
|
|
||||||
*
|
|
||||||
* @param kval
|
|
||||||
* @return enum kind
|
|
||||||
*/
|
|
||||||
enum kind set_kind(char *kval)
|
|
||||||
{
|
|
||||||
if (strcmp(kval, "basic") == 0)
|
|
||||||
{
|
|
||||||
if (sizeof(void *) == 8)
|
|
||||||
return BASICX64;
|
|
||||||
else
|
|
||||||
return BASIC;
|
|
||||||
}
|
|
||||||
else if (strcmp(kval, "banana") == 0)
|
|
||||||
{
|
|
||||||
if (sizeof(void *) == 8)
|
|
||||||
return BANANAX64;
|
|
||||||
else
|
|
||||||
return BANANA;
|
|
||||||
}
|
|
||||||
else if (strcmp(kval, "potato") == 0)
|
|
||||||
{
|
|
||||||
if (sizeof(void *) == 8)
|
|
||||||
return POTATOX64;
|
|
||||||
else
|
|
||||||
return POTATO;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
log_error("Unknown Voicemeeter kind '%s'\n", kval);
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int init_voicemeeter(T_VBVMR_INTERFACE *vmr, int kind)
|
|
||||||
{
|
|
||||||
int rep = initialize_dll_interfaces(vmr);
|
|
||||||
if (rep < 0)
|
|
||||||
{
|
|
||||||
if (rep == -100)
|
|
||||||
{
|
|
||||||
log_error("Voicemeeter is not installed");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
log_error("Error loading Voicemeeter dll with code %d\n", rep);
|
|
||||||
}
|
|
||||||
return rep;
|
|
||||||
}
|
|
||||||
|
|
||||||
rep = login(vmr, kind);
|
|
||||||
if (rep != 0)
|
if (rep != 0)
|
||||||
{
|
{
|
||||||
log_error("Error logging into Voicemeeter");
|
terminate(vmr, "Error logging out of the Voicemeeter API");
|
||||||
return rep;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
log_info("Successfully logged out of the Voicemeeter API");
|
||||||
|
free(vmr);
|
||||||
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
void interactive(T_VBVMR_INTERFACE *vmr)
|
/**
|
||||||
|
* @brief Write fatal error log, free dyn allocated memory then exit
|
||||||
|
*
|
||||||
|
* @param vmr Pointer to the iVMR interface
|
||||||
|
* @param msg Fatal error message
|
||||||
|
*/
|
||||||
|
static void terminate(PT_VMR vmr, char *msg)
|
||||||
{
|
{
|
||||||
char input[MAX_LINE], command[MAX_LINE];
|
log_fatal(msg);
|
||||||
char *p = input;
|
free(vmr);
|
||||||
int i;
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Prints the help message
|
||||||
|
*/
|
||||||
|
static void usage()
|
||||||
|
{
|
||||||
|
puts(USAGE);
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set the kind of Voicemeeter based on the value of -k flag.
|
||||||
|
* For 64 bit systems the value is promoted to X64.
|
||||||
|
*
|
||||||
|
* @param kval Value of the -k flag
|
||||||
|
* @return enum kind
|
||||||
|
*/
|
||||||
|
static enum kind set_kind(char *kval)
|
||||||
|
{
|
||||||
|
if (strcmp(kval, "basic") == 0)
|
||||||
|
return IS_64_BIT ? BASICX64 : BASIC;
|
||||||
|
else if (strcmp(kval, "banana") == 0)
|
||||||
|
return IS_64_BIT ? BANANAX64 : BANANA;
|
||||||
|
else if (strcmp(kval, "potato") == 0)
|
||||||
|
return IS_64_BIT ? POTATOX64 : POTATO;
|
||||||
|
else
|
||||||
|
return UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Continuously read lines from stdin.
|
||||||
|
* Break if 'Q' is entered on the interactive prompt.
|
||||||
|
* Each line is passed to parse_input()
|
||||||
|
*
|
||||||
|
* @param vmr Pointer to the iVMR interface
|
||||||
|
* @param with_prompt If true, prints the interactive prompt '>>'
|
||||||
|
* @param delimiters A string of delimiter characters to split each input line
|
||||||
|
*/
|
||||||
|
static void interactive(PT_VMR vmr, bool with_prompt, char *delimiters)
|
||||||
|
{
|
||||||
|
char input[MAX_LINE];
|
||||||
size_t len;
|
size_t len;
|
||||||
|
|
||||||
|
if (with_prompt)
|
||||||
printf(">> ");
|
printf(">> ");
|
||||||
while (fgets(input, MAX_LINE, stdin) != NULL)
|
while (fgets(input, MAX_LINE, stdin) != NULL)
|
||||||
{
|
{
|
||||||
input[strcspn(input, "\n")] = 0;
|
input[(len = strcspn(input, "\n"))] = 0;
|
||||||
len = strlen(input);
|
|
||||||
if (len == 1 && toupper(input[0]) == 'Q')
|
if (len == 1 && toupper(input[0]) == 'Q')
|
||||||
break;
|
break;
|
||||||
|
|
||||||
replace_multiple_space_with_one(input, len);
|
parse_input(vmr, input, delimiters);
|
||||||
while (*p)
|
|
||||||
{
|
|
||||||
if (isspace(*p))
|
|
||||||
{
|
|
||||||
p++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
log_trace("commands still in buffer: %s", p);
|
|
||||||
|
|
||||||
i = 0;
|
if (with_prompt)
|
||||||
while (*p && !isspace(*p))
|
|
||||||
command[i++] = *p++;
|
|
||||||
command[i] = '\0';
|
|
||||||
p++; /* shift to next char */
|
|
||||||
|
|
||||||
if (command[0] != '\0')
|
|
||||||
parse_command(vmr, command);
|
|
||||||
memset(command, '\0', MAX_LINE);
|
|
||||||
}
|
|
||||||
|
|
||||||
p = input; /* reset pointer */
|
|
||||||
memset(input, '\0', MAX_LINE); /* reset input buffer */
|
|
||||||
printf(">> ");
|
printf(">> ");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void parse_command(T_VBVMR_INTERFACE *vmr, char *command)
|
/**
|
||||||
|
* @brief Returns early if input is a comment
|
||||||
|
* Walks through each line split by " \t;," delimiters.
|
||||||
|
* Each token is passed to parse_command()
|
||||||
|
*
|
||||||
|
* @param vmr Pointer to the iVMR interface
|
||||||
|
* @param input Each input line, from stdin or CLI args
|
||||||
|
* @param delimiters A string of delimiter characters to split each input line
|
||||||
|
*/
|
||||||
|
static void parse_input(PT_VMR vmr, char *input, char *delimiters)
|
||||||
|
{
|
||||||
|
if (is_comment(input))
|
||||||
|
return;
|
||||||
|
|
||||||
|
char *token, *p;
|
||||||
|
|
||||||
|
token = strtok_r(input, delimiters, &p);
|
||||||
|
while (token != NULL)
|
||||||
|
{
|
||||||
|
parse_command(vmr, token);
|
||||||
|
token = strtok_r(NULL, delimiters, &p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Execute each command according to type.
|
||||||
|
* See command type definitions in:
|
||||||
|
* https://github.com/onyx-and-iris/vmrcli?tab=readme-ov-file#api-commands
|
||||||
|
*
|
||||||
|
* @param vmr Pointer to the iVMR interface
|
||||||
|
* @param command Each token from the input line as its own command string
|
||||||
|
*/
|
||||||
|
static void parse_command(PT_VMR vmr, char *command)
|
||||||
{
|
{
|
||||||
log_debug("Parsing %s", command);
|
log_debug("Parsing %s", command);
|
||||||
|
|
||||||
|
static const struct quickcommand quickcommands[] = {
|
||||||
|
{.name = "lock", .fullcommand = "command.lock=1"},
|
||||||
|
{.name = "unlock", .fullcommand = "command.lock=0"},
|
||||||
|
{.name = "show", .fullcommand = "command.show=1"},
|
||||||
|
{.name = "hide", .fullcommand = "command.show=0"},
|
||||||
|
{.name = "restart", .fullcommand = "command.restart=1"}};
|
||||||
|
|
||||||
|
struct quickcommand *qc_ptr = command_in_quickcommands(command, quickcommands, (int)COUNT_OF(quickcommands));
|
||||||
|
if (qc_ptr != NULL)
|
||||||
|
{
|
||||||
|
set_parameters(vmr, qc_ptr->fullcommand);
|
||||||
|
if (eflag)
|
||||||
|
{
|
||||||
|
printf("Setting %s\n", qc_ptr->fullcommand);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (command[0] == '!') /* toggle */
|
if (command[0] == '!') /* toggle */
|
||||||
{
|
{
|
||||||
command++;
|
command++;
|
||||||
@ -241,7 +357,13 @@ void parse_command(T_VBVMR_INTERFACE *vmr, char *command)
|
|||||||
if (res.type == FLOAT_T)
|
if (res.type == FLOAT_T)
|
||||||
{
|
{
|
||||||
if (res.val.f == 1 || res.val.f == 0)
|
if (res.val.f == 1 || res.val.f == 0)
|
||||||
|
{
|
||||||
set_parameter_float(vmr, command, 1 - res.val.f);
|
set_parameter_float(vmr, command, 1 - res.val.f);
|
||||||
|
if (eflag)
|
||||||
|
{
|
||||||
|
printf("Toggling %s\n", command);
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
log_warn("%s does not appear to be a boolean parameter", command);
|
log_warn("%s does not appear to be a boolean parameter", command);
|
||||||
}
|
}
|
||||||
@ -251,6 +373,10 @@ void parse_command(T_VBVMR_INTERFACE *vmr, char *command)
|
|||||||
if (strchr(command, '=') != NULL) /* set */
|
if (strchr(command, '=') != NULL) /* set */
|
||||||
{
|
{
|
||||||
set_parameters(vmr, command);
|
set_parameters(vmr, command);
|
||||||
|
if (eflag)
|
||||||
|
{
|
||||||
|
printf("Setting %s\n", command);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else /* get */
|
else /* get */
|
||||||
{
|
{
|
||||||
@ -260,10 +386,11 @@ void parse_command(T_VBVMR_INTERFACE *vmr, char *command)
|
|||||||
switch (res.type)
|
switch (res.type)
|
||||||
{
|
{
|
||||||
case FLOAT_T:
|
case FLOAT_T:
|
||||||
printf("%.1f\n", res.val.f);
|
printf("%s: %.1f\n", command, res.val.f);
|
||||||
break;
|
break;
|
||||||
case STRING_T:
|
case STRING_T:
|
||||||
printf("%ls\n", res.val.s);
|
if (res.val.s[0] != '\0')
|
||||||
|
printf("%s: %ls\n", command, res.val.s);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@ -271,9 +398,17 @@ void parse_command(T_VBVMR_INTERFACE *vmr, char *command)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void get(T_VBVMR_INTERFACE *vmr, char *command, struct result *res)
|
/**
|
||||||
|
* @brief Get the value of a float or string parameter.
|
||||||
|
* Stores its type and value into a result struct
|
||||||
|
*
|
||||||
|
* @param vmr Pointer to the iVMR interface
|
||||||
|
* @param command A parsed 'get' command as a string
|
||||||
|
* @param res Pointer to a struct holding the result of the API call.
|
||||||
|
*/
|
||||||
|
static void get(PT_VMR vmr, char *command, struct result *res)
|
||||||
{
|
{
|
||||||
clear_dirty(vmr);
|
clear(vmr, is_pdirty);
|
||||||
if (get_parameter_float(vmr, command, &res->val.f) != 0)
|
if (get_parameter_float(vmr, command, &res->val.f) != 0)
|
||||||
{
|
{
|
||||||
res->type = STRING_T;
|
res->type = STRING_T;
|
||||||
|
|||||||
268
src/wrapper.c
Normal file
268
src/wrapper.c
Normal file
@ -0,0 +1,268 @@
|
|||||||
|
/**
|
||||||
|
* @file wrapper.c
|
||||||
|
* @author Onyx and Iris (code@onyxandiris.online)
|
||||||
|
* @brief Provides public functions that wrap the iVMR calls
|
||||||
|
* @version 0.11.0
|
||||||
|
* @date 2024-07-06
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2024
|
||||||
|
* https://github.com/onyx-and-iris/vmrcli/blob/main/LICENSE
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
#include "wrapper.h"
|
||||||
|
#include "log.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#define KIND_STR_LEN 64
|
||||||
|
#define VERSION_STR_LEN 32
|
||||||
|
#define LOGIN_TIMEOUT 2
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Logs into the API.
|
||||||
|
* Tests for valid connection for up to 2 seconds.
|
||||||
|
* If successful initializes the dirty parameters.
|
||||||
|
*
|
||||||
|
* @param vmr Pointer to the iVMR interface
|
||||||
|
* @param kind The kind of Voicemeeter Gui to launch.
|
||||||
|
* @return long
|
||||||
|
* 0: OK (no error).
|
||||||
|
* -2: Login timed out.
|
||||||
|
*/
|
||||||
|
long login(PT_VMR vmr, int kind)
|
||||||
|
{
|
||||||
|
long rep;
|
||||||
|
long v;
|
||||||
|
|
||||||
|
log_trace("VBVMR_Login()");
|
||||||
|
rep = vmr->VBVMR_Login();
|
||||||
|
if (rep == 1)
|
||||||
|
{
|
||||||
|
run_voicemeeter(vmr, kind);
|
||||||
|
char kind_s[KIND_STR_LEN];
|
||||||
|
log_info(
|
||||||
|
"Launching Voicemeeter %s GUI",
|
||||||
|
kind_as_string(kind_s, kind, KIND_STR_LEN));
|
||||||
|
}
|
||||||
|
|
||||||
|
time_t start = time(NULL);
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if ((rep = version(vmr, &v)) == 0)
|
||||||
|
{
|
||||||
|
char version_s[VERSION_STR_LEN];
|
||||||
|
log_info(
|
||||||
|
"Successfully logged into the Voicemeeter API v%s",
|
||||||
|
version_as_string(version_s, v, VERSION_STR_LEN));
|
||||||
|
clear(vmr, is_pdirty);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Sleep(50);
|
||||||
|
} while (difftime(time(NULL), start) < LOGIN_TIMEOUT);
|
||||||
|
|
||||||
|
return rep;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Logs out of the API giving a short wait to allow a
|
||||||
|
* final instruction to complete.
|
||||||
|
*
|
||||||
|
* @param vmr Pointer to the iVMR interface
|
||||||
|
* @return long See:
|
||||||
|
* https://github.com/onyx-and-iris/vmrcli/blob/main/include/VoicemeeterRemote.h#L56
|
||||||
|
*/
|
||||||
|
long logout(PT_VMR vmr)
|
||||||
|
{
|
||||||
|
Sleep(20); /* give time for last command */
|
||||||
|
log_trace("VBVMR_Logout()");
|
||||||
|
return vmr->VBVMR_Logout();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Launches Voicemeeter or other utility apps
|
||||||
|
*
|
||||||
|
* @param vmr Pointer to the iVMR interface
|
||||||
|
* @param kind The kind of app to launch
|
||||||
|
* @return long See:
|
||||||
|
* https://github.com/onyx-and-iris/vmrcli/blob/main/include/VoicemeeterRemote.h#L66
|
||||||
|
*/
|
||||||
|
long run_voicemeeter(PT_VMR vmr, int kind)
|
||||||
|
{
|
||||||
|
log_trace("VBVMR_RunVoicemeeter(%d)", kind);
|
||||||
|
return vmr->VBVMR_RunVoicemeeter((long)kind);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get Voicemeeter type
|
||||||
|
*
|
||||||
|
* @param vmr Pointer to the iVMR interface
|
||||||
|
* @param type Pointer to a long object receiving the type
|
||||||
|
* @return long See:
|
||||||
|
* https://github.com/onyx-and-iris/vmrcli/blob/main/include/VoicemeeterRemote.h#L107
|
||||||
|
*/
|
||||||
|
long type(PT_VMR vmr, long *type)
|
||||||
|
{
|
||||||
|
log_trace("VBVMR_GetVoicemeeterType(<long> *t)");
|
||||||
|
return vmr->VBVMR_GetVoicemeeterType(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get Voicemeeter version
|
||||||
|
*
|
||||||
|
* @param vmr Pointer to the iVMR interface
|
||||||
|
* @param version Pointer to a long object receiving the version
|
||||||
|
* @return long See:
|
||||||
|
* https://github.com/onyx-and-iris/vmrcli/blob/main/include/VoicemeeterRemote.h#L122
|
||||||
|
*/
|
||||||
|
long version(PT_VMR vmr, long *version)
|
||||||
|
{
|
||||||
|
log_trace("VBVMR_GetVoicemeeterVersion(<long> *v)");
|
||||||
|
return vmr->VBVMR_GetVoicemeeterVersion(version);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Polling function, use it to determine if there are parameter
|
||||||
|
* states to be updated.
|
||||||
|
*
|
||||||
|
* @param vmr Pointer to the iVMR interface
|
||||||
|
* @return true New parameters yet to be updated
|
||||||
|
* @return false No new parameters, safe to make a get call
|
||||||
|
*/
|
||||||
|
bool is_pdirty(PT_VMR vmr)
|
||||||
|
{
|
||||||
|
log_trace("VBVMR_IsParametersDirty()");
|
||||||
|
return vmr->VBVMR_IsParametersDirty() == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the parameter float object
|
||||||
|
*
|
||||||
|
* @param vmr Pointer to the iVMR interface
|
||||||
|
* @param param The parameter to be queried
|
||||||
|
* @param f Pointer to a float object receiving the value
|
||||||
|
* @return long See:
|
||||||
|
* https://github.com/onyx-and-iris/vmrcli/blob/main/include/VoicemeeterRemote.h#L159
|
||||||
|
*/
|
||||||
|
long get_parameter_float(PT_VMR vmr, char *param, float *f)
|
||||||
|
{
|
||||||
|
log_trace("VBVMR_GetParameterFloat(%s, <float> *f)", param);
|
||||||
|
return vmr->VBVMR_GetParameterFloat(param, f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the parameter string object
|
||||||
|
*
|
||||||
|
* @param vmr Pointer to the iVMR interface
|
||||||
|
* @param param The parameter to be queried
|
||||||
|
* @param s Pointer to a character buffer receiving the string value
|
||||||
|
* @return long See:
|
||||||
|
* https://github.com/onyx-and-iris/vmrcli/blob/main/include/VoicemeeterRemote.h#L173
|
||||||
|
*/
|
||||||
|
long get_parameter_string(PT_VMR vmr, char *param, wchar_t *s)
|
||||||
|
{
|
||||||
|
log_trace("VBVMR_GetParameterStringW(%s, <wchar_t> *s)", param);
|
||||||
|
return vmr->VBVMR_GetParameterStringW(param, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set the parameter float object
|
||||||
|
*
|
||||||
|
* @param vmr Pointer to the iVMR interface
|
||||||
|
* @param param The parameter to be updated
|
||||||
|
* @param val The new value
|
||||||
|
* @return long See:
|
||||||
|
* https://github.com/onyx-and-iris/vmrcli/blob/main/include/VoicemeeterRemote.h#L309
|
||||||
|
*/
|
||||||
|
long set_parameter_float(PT_VMR vmr, char *param, float val)
|
||||||
|
{
|
||||||
|
log_trace("VBVMR_SetParameterFloat(%s, %.1f)", param, val);
|
||||||
|
return vmr->VBVMR_SetParameterFloat(param, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set the parameter string object
|
||||||
|
*
|
||||||
|
* @param vmr Pointer to the iVMR interface
|
||||||
|
* @param param The parameter to be updated
|
||||||
|
* @param s Pointer to a char[] object containing the new value
|
||||||
|
* @return long See:
|
||||||
|
* https://github.com/onyx-and-iris/vmrcli/blob/main/include/VoicemeeterRemote.h#L327
|
||||||
|
*/
|
||||||
|
long set_parameter_string(PT_VMR vmr, char *param, char *s)
|
||||||
|
{
|
||||||
|
log_trace("VBVMR_SetParameterStringA(%s, %s)", param, s);
|
||||||
|
return vmr->VBVMR_SetParameterStringA(param, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Run a script possibly containing multiple instructions
|
||||||
|
*
|
||||||
|
* @param vmr Pointer to the iVMR interface
|
||||||
|
* @param command Pointer to a char[] object containing the script
|
||||||
|
* @return long See:
|
||||||
|
* https://github.com/onyx-and-iris/vmrcli/blob/main/include/VoicemeeterRemote.h#L351
|
||||||
|
*/
|
||||||
|
long set_parameters(PT_VMR vmr, char *command)
|
||||||
|
{
|
||||||
|
log_trace("VBVMR_SetParameters(%s)", command);
|
||||||
|
return vmr->VBVMR_SetParameters(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Polling function, use it to determine if there are macrobutton
|
||||||
|
* states to be updated.
|
||||||
|
*
|
||||||
|
* @param vmr Pointer to the iVMR interface
|
||||||
|
* @return true Macrobutton states yet to be udpated
|
||||||
|
* @return false No new macrobutton states
|
||||||
|
*/
|
||||||
|
bool is_mdirty(PT_VMR vmr)
|
||||||
|
{
|
||||||
|
log_trace("VBVMR_MacroButton_IsDirty()");
|
||||||
|
return vmr->VBVMR_MacroButton_IsDirty() >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the current status of macrobutton[n].{mode}
|
||||||
|
*
|
||||||
|
* @param vmr Pointer to the iVMR interface
|
||||||
|
* @param n Index of the macrobutton
|
||||||
|
* @param val Pointer to a float object the current value will be stored in
|
||||||
|
* @param mode The mode (stateonly, state, trigger)
|
||||||
|
* @return long See:
|
||||||
|
* https://github.com/onyx-and-iris/vmrcli/blob/main/include/VoicemeeterRemote.h#L663
|
||||||
|
*/
|
||||||
|
long macrobutton_getstatus(PT_VMR vmr, long n, float *val, long mode)
|
||||||
|
{
|
||||||
|
log_trace("VBVMR_MacroButton_GetStatus(%ld, <float> *v, %ld)", n, mode);
|
||||||
|
return vmr->VBVMR_MacroButton_GetStatus(n, val, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set the current status of macrobutton[n].{mode}
|
||||||
|
*
|
||||||
|
* @param vmr Pointer to the iVMR interface
|
||||||
|
* @param n Index of the macrobutton
|
||||||
|
* @param val Value to be updated
|
||||||
|
* @param mode The mode (stateonly, state, trigger)
|
||||||
|
* @return long See:
|
||||||
|
* https://github.com/onyx-and-iris/vmrcli/blob/main/include/VoicemeeterRemote.h#L677
|
||||||
|
*/
|
||||||
|
long macrobutton_setstatus(PT_VMR vmr, long n, float val, long mode)
|
||||||
|
{
|
||||||
|
log_trace("VBVMR_MacroButton_SetStatus(%ld, %d, %ld)", n, (int)val, mode);
|
||||||
|
return vmr->VBVMR_MacroButton_SetStatus(n, val, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Continuously polls an is_{}dirty function until it clears.
|
||||||
|
*
|
||||||
|
* @param vmr Pointer to the iVMR interface
|
||||||
|
* @param f Pointer to a polling function
|
||||||
|
*/
|
||||||
|
void clear(PT_VMR vmr, bool (*f)(PT_VMR))
|
||||||
|
{
|
||||||
|
Sleep(30);
|
||||||
|
while (f(vmr))
|
||||||
|
Sleep(1);
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user