mirror of
https://github.com/onyx-and-iris/vmrcli.git
synced 2026-03-12 12:49:15 +00:00
Compare commits
No commits in common. "9bb7b32f7bc6d6d79a0c06ac13f5a9319fd79960" and "4e5801541101df6c05d7ce5d9d4c4f80c1ec61ab" have entirely different histories.
9bb7b32f7b
...
4e58015411
102
.github/workflows/release.yml
vendored
102
.github/workflows/release.yml
vendored
@ -1,102 +0,0 @@
|
|||||||
name: Build vmrcli
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
tags:
|
|
||||||
- 'v*.*.*'
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build-windows:
|
|
||||||
runs-on: windows-latest
|
|
||||||
permissions:
|
|
||||||
contents: write # Required to create releases
|
|
||||||
actions: read # Required to download artifacts
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Setup MSYS2
|
|
||||||
uses: msys2/setup-msys2@v2
|
|
||||||
with:
|
|
||||||
msystem: MINGW64
|
|
||||||
update: true
|
|
||||||
install: >-
|
|
||||||
mingw-w64-x86_64-gcc
|
|
||||||
mingw-w64-x86_64-make
|
|
||||||
make
|
|
||||||
|
|
||||||
- name: Add MSYS2 to PATH
|
|
||||||
run: |
|
|
||||||
echo "${{ runner.temp }}\msys64\mingw64\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
|
|
||||||
echo "${{ runner.temp }}\msys64\usr\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
|
|
||||||
shell: pwsh
|
|
||||||
|
|
||||||
- name: Verify GCC installation
|
|
||||||
run: |
|
|
||||||
gcc --version
|
|
||||||
make --version
|
|
||||||
shell: pwsh
|
|
||||||
|
|
||||||
- name: Clean previous builds
|
|
||||||
run: make clean
|
|
||||||
shell: pwsh
|
|
||||||
continue-on-error: true
|
|
||||||
|
|
||||||
- name: Build vmrcli
|
|
||||||
run: make all
|
|
||||||
shell: pwsh
|
|
||||||
env:
|
|
||||||
LOG_USE_COLOR: yes
|
|
||||||
|
|
||||||
- name: Verify build output
|
|
||||||
run: |
|
|
||||||
if (Test-Path "bin/vmrcli.exe") {
|
|
||||||
Write-Host "✅ Build successful - vmrcli.exe created"
|
|
||||||
Get-Item "bin/vmrcli.exe" | Format-List Name, Length, LastWriteTime
|
|
||||||
} else {
|
|
||||||
Write-Host "❌ Build failed - vmrcli.exe not found"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
shell: pwsh
|
|
||||||
|
|
||||||
- name: Test executable
|
|
||||||
run: |
|
|
||||||
if (Test-Path "bin/vmrcli.exe") {
|
|
||||||
Write-Host "Testing vmrcli.exe..."
|
|
||||||
& ".\bin\vmrcli.exe" -h
|
|
||||||
}
|
|
||||||
shell: pwsh
|
|
||||||
continue-on-error: true
|
|
||||||
|
|
||||||
- name: Upload build artifact
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: vmrcli-windows
|
|
||||||
path: |
|
|
||||||
bin/vmrcli.exe
|
|
||||||
retention-days: 30
|
|
||||||
|
|
||||||
- name: Upload build logs (on failure)
|
|
||||||
if: failure()
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: build-logs
|
|
||||||
path: |
|
|
||||||
obj/
|
|
||||||
*.log
|
|
||||||
retention-days: 7
|
|
||||||
|
|
||||||
- name: Create GitHub Release
|
|
||||||
if: startsWith(github.ref, 'refs/tags/')
|
|
||||||
uses: softprops/action-gh-release@v1
|
|
||||||
with:
|
|
||||||
files: |
|
|
||||||
bin/vmrcli.exe
|
|
||||||
tag_name: ${{ github.ref_name }}
|
|
||||||
name: vmrcli ${{ github.ref_name }}
|
|
||||||
draft: false
|
|
||||||
prerelease: false
|
|
||||||
generate_release_notes: true
|
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
4
.gitignore
vendored
4
.gitignore
vendored
@ -56,6 +56,4 @@ dkms.conf
|
|||||||
|
|
||||||
.vscode/
|
.vscode/
|
||||||
|
|
||||||
test/
|
test*
|
||||||
|
|
||||||
test-*
|
|
||||||
182
README.md
182
README.md
@ -1,189 +1,127 @@
|
|||||||
# Voicemeeter Remote Command Line Utility
|
# Voicemeeter Remote Command Line Utility
|
||||||
|
|
||||||
[](LICENSE)
|
## `Tested against`
|
||||||
[](#requirements)
|
|
||||||
|
|
||||||
> A command-line interface for controlling Voicemeeter
|
- Basic 1.1.1.9
|
||||||
|
- Banana 2.1.1.9
|
||||||
|
- Potato 3.1.1.9
|
||||||
|
|
||||||
## Compatibility
|
## `Requirements`
|
||||||
|
|
||||||
| Voicemeeter Version | Status |
|
- [Voicemeeter](https://voicemeeter.com/)
|
||||||
|-------------------|--------|
|
|
||||||
| Basic 1.1.2.2 | ✅ Tested |
|
|
||||||
| Banana 2.1.2.2 | ✅ Tested |
|
|
||||||
| Potato 3.1.2.2 | ✅ Tested |
|
|
||||||
|
|
||||||
## Requirements
|
## `Use`
|
||||||
|
|
||||||
- **[Voicemeeter](https://voicemeeter.com/)** - Any version (Basic, Banana, or Potato)
|
|
||||||
- **Windows** operating system
|
|
||||||
- **Command line environment** (PowerShell, CMD, or Terminal)
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
.\vmrcli.exe [OPTIONS] <api_commands>
|
.\vmrcli.exe [-h] [-v] [-i|-I] [-f] [-k] [-l] [-e] [-c] [-m] [-s] <api commands>
|
||||||
```
|
```
|
||||||
|
|
||||||
### Command Line Options
|
Where:
|
||||||
|
|
||||||
| Option | Description | Example |
|
- `h`: Print the help message.
|
||||||
|--------|-------------|----------|
|
- `v`: Print the version of vmrcli.
|
||||||
| `-h` | Print help message | `vmrcli.exe -h` |
|
- `i`: Enable interactive mode, use (-I) to disable the '>>' prompt.
|
||||||
| `-v` | Show version information | `vmrcli.exe -v` |
|
- If set, any api commands passed on the command line will be ignored.
|
||||||
| `-i` | Enable interactive mode | `vmrcli.exe -i` |
|
- `f`: Do not split input on spaces.
|
||||||
| `-I` | Interactive mode without prompt | `vmrcli.exe -I` |
|
- `k`: The kind of Voicemeeter (basic, banana or potato). Use this to launch the GUI.
|
||||||
| `-f` | Don't split input on spaces | `vmrcli.exe -f` |
|
- `l`: Set log level, must be one of TRACE, DEBUG, INFO, WARN, ERROR, or FATAL
|
||||||
| `-k <type>` | Launch Voicemeeter GUI | `-kbasic`, `-kbanana`, `-kpotato` |
|
- `e`: Enable extra console output (toggle, set messages)
|
||||||
| `-l <level>` | Set log level | `-lDEBUG`, `-lINFO`, `-lWARN` |
|
- `c`: Load a user configuration (give the full file path)
|
||||||
| `-e` | Enable extra console output | `vmrcli.exe -e` |
|
- `m`: Launch the MacroButtons application
|
||||||
| `-c <path>` | Load user configuration | `-c "C:\config.txt"` |
|
- `s`: Launch the StreamerView application
|
||||||
| `-m` | Launch MacroButtons app | `vmrcli.exe -m` |
|
|
||||||
| `-s` | Launch StreamerView app | `vmrcli.exe -s` |
|
|
||||||
|
|
||||||
> **Note:** When using interactive mode (`-i`), command line API commands are ignored.
|
|
||||||
|
|
||||||
## `API Commands`
|
## `API Commands`
|
||||||
|
|
||||||
### Command Types
|
- Commands starting with `!` will be toggled, use it with boolean parameters.
|
||||||
|
- Commands containing `=` will set a value. (use `+=` and `-=` to increment/decrement)
|
||||||
|
- All other commands with get a value.
|
||||||
|
|
||||||
| Syntax | Action | Example |
|
Examples:
|
||||||
|--------|---------|---------|
|
|
||||||
| `!command` | **Toggle** boolean values | `!strip[0].mute` |
|
|
||||||
| `command=value` | **Set** a parameter | `strip[0].gain=2.5` |
|
|
||||||
| `command+=value` | **Increment** a parameter | `bus[0].gain+=1.2` |
|
|
||||||
| `command-=value` | **Decrement** a parameter | `bus[0].gain-=3.8` |
|
|
||||||
| `command` | **Get** current value | `strip[0].label` |
|
|
||||||
|
|
||||||
> **Tip:** Use quotes around values containing spaces: `'strip[0].label="my device"'`
|
Launch basic GUI, set log level to INFO, Toggle Strip 0 Mute, print its new value, then decrease Bus 0 Gain by 3.8
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Examples
|
|
||||||
|
|
||||||
#### **Basic Operations**
|
|
||||||
*Toggle mute, get values, and adjust gain*
|
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
.\vmrcli.exe -kbasic -lINFO !strip[0].mute strip[0].mute bus[0].gain-=3.8
|
.\vmrcli.exe -kbasic -lINFO !strip[0].mute strip[0].mute bus[0].gain-=3.8
|
||||||
```
|
```
|
||||||
|
|
||||||
#### **Setting Labels with Spaces**
|
Launch banana GUI, set log level to DEBUG, set Strip 0 label to podmic then print Strip 2 label
|
||||||
*Set labels and print them back*
|
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
.\vmrcli.exe -kbanana -lDEBUG 'strip[0].label="my podmic"' strip[0].label
|
.\vmrcli.exe -kbanana -lDEBUG strip[0].label=podmic strip[2].label
|
||||||
```
|
```
|
||||||
|
|
||||||
#### **Device Configuration**
|
#### `String Commands With Spaces`
|
||||||
*Configure hardware devices with complex names*
|
|
||||||
|
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
|
```powershell
|
||||||
.\vmrcli.exe -lDEBUG bus[2].mute=1 bus[2].mute 'bus[2].device.wdm="Realtek Digital Output (Realtek(R) Audio)"'
|
.\vmrcli.exe -lDEBUG -f bus[1].device.wdm='"Realtek Digital Output (Realtek(R) Audio)"'
|
||||||
|
|
||||||
|
.\vmrcli.exe -lDEBUG -f strip[0].label='"My Podmic"'
|
||||||
```
|
```
|
||||||
|
|
||||||
#### **Batch Operations**
|
#### `Quick Commands`
|
||||||
*Multiple strip configurations in one command*
|
|
||||||
|
|
||||||
```powershell
|
A short list of quick commands are available:
|
||||||
.\vmrcli.exe `
|
|
||||||
'strip[0].label="my podmic"' strip[0].label !strip[0].mute `
|
|
||||||
'strip[1].label="my wavemic"' strip[1].label !strip[1].mute
|
|
||||||
```
|
|
||||||
|
|
||||||
### Quick Commands
|
- `lock`: command.lock=1
|
||||||
|
- `unlock`: command.lock=0
|
||||||
|
- `show`: command.show=1
|
||||||
|
- `hide`: command.show=0
|
||||||
|
- `restart`: command.restart=1
|
||||||
|
|
||||||
*Convenient shortcuts for common Voicemeeter operations*
|
They may be used in direct or interactive mode.
|
||||||
|
|
||||||
| Command | API Equivalent | Description |
|
## `Interactive Mode`
|
||||||
|---------|----------------|-------------|
|
|
||||||
| `lock` | `command.lock=1` | 🔒 Lock Voicemeeter parameters |
|
|
||||||
| `unlock` | `command.lock=0` | 🔓 Unlock Voicemeeter parameters |
|
|
||||||
| `show` | `command.show=1` | 👁️ Show Voicemeeter interface |
|
|
||||||
| `hide` | `command.show=0` | 🙈 Hide Voicemeeter interface |
|
|
||||||
| `restart` | `command.restart=1` | 🔄 Restart Voicemeeter engine |
|
|
||||||
|
|
||||||
> **Available in both direct and interactive modes**
|
Running the following command in Powershell:
|
||||||
|
|
||||||
## Interactive Mode
|
|
||||||
|
|
||||||
*Real-time command interface for live audio control*
|
|
||||||
|
|
||||||
**Start interactive session:**
|
|
||||||
```powershell
|
```powershell
|
||||||
.\vmrcli.exe -i
|
.\vmrcli.exe -i
|
||||||
```
|
```
|
||||||
|
|
||||||
**Interactive prompt:**
|
Will open an interactive prompt:
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
Interactive mode enabled. Enter 'Q' to exit.
|
Interactive mode enabled. Enter 'Q' to exit.
|
||||||
>>
|
>>
|
||||||
```
|
```
|
||||||
|
|
||||||
> **Important:** Command line API arguments are ignored when using `-i`
|
API commands follow the same rules as listed above. Entering `Q` or `q` will exit the program.
|
||||||
|
|
||||||
## Script Files
|
## `Script files`
|
||||||
|
|
||||||
*Automate complex audio setups with script files*
|
Scripts can be loaded from text files, for example in Powershell:
|
||||||
|
|
||||||
### Loading Scripts
|
|
||||||
|
|
||||||
**From file content:**
|
|
||||||
```powershell
|
```powershell
|
||||||
.\vmrcli.exe -lDEBUG $(Get-Content .\example_commands.txt)
|
.\vmrcli.exe -lDEBUG $(Get-Content .\example_commands.txt)
|
||||||
```
|
```
|
||||||
|
|
||||||
**Via pipeline:**
|
You may also pipe a scripts contents to the CLI:
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
$(Get-Content .\example_commands.txt) | .\vmrcli.exe -lDEBUG -I
|
$(Get-Content .\example_commands.txt) | .\vmrcli.exe -lDEBUG -I
|
||||||
```
|
```
|
||||||
|
|
||||||
### Script Format Rules
|
Multiple API commands can be in a single line, they may be separated by space, `;` or `,`.
|
||||||
|
|
||||||
| Feature | Syntax | Example |
|
Lines starting with `#` will be interpreted as comments.
|
||||||
|---------|--------|----------|
|
|
||||||
| **Multiple commands per line** | Space, `;`, or `,` separated | `strip[0].mute=1;bus[0].gain+=2` |
|
|
||||||
| **Comments** | Lines starting with `#` | `# This is a comment` |
|
|
||||||
|
|
||||||
## Build Instructions
|
## `Build`
|
||||||
|
|
||||||
*Compile from source using GNU Make*
|
Run the included `makefile` with [GNU Make](https://www.gnu.org/software/make/).
|
||||||
|
|
||||||
### Prerequisites
|
The binary in [Releases][releases] is compiled with coloured logging enabled. To disable this you can override the `LOG_USE_COLOR` variable, for example:
|
||||||
- [GNU Make](https://www.gnu.org/software/make/)
|
|
||||||
- GCC compiler (recommended)
|
|
||||||
- Windows development environment
|
|
||||||
|
|
||||||
### Build Commands
|
`make LOG_USE_COLOR=no`
|
||||||
|
|
||||||
```bash
|
## `Official Documentation`
|
||||||
# Standard build
|
|
||||||
make
|
|
||||||
|
|
||||||
# Disable colored logging
|
- [Voicemeeter Remote C API][remoteapi-docs]
|
||||||
make LOG_USE_COLOR=no
|
|
||||||
|
|
||||||
# Clean build artifacts
|
## `Special Thanks`
|
||||||
make clean
|
|
||||||
```
|
|
||||||
|
|
||||||
> **Pre-built binaries** are available in [Releases][releases] with coloured logging enabled
|
- [rxi][rxi-user] for writing the [log.c][log-c] package
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Resources
|
|
||||||
|
|
||||||
### Official Documentation
|
|
||||||
- [Voicemeeter Remote C API][remoteapi-docs] - Complete API reference
|
|
||||||
|
|
||||||
### Acknowledgments
|
|
||||||
- **[rxi][rxi-user]** - Creator of the excellent [log.c][log-c] logging library
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
||||||
|
|
||||||
[releases]: https://github.com/onyx-and-iris/vmrcli/releases
|
[releases]: https://github.com/onyx-and-iris/vmrcli/releases
|
||||||
[remoteapi-docs]: https://github.com/onyx-and-iris/Voicemeeter-SDK/blob/main/VoicemeeterRemoteAPI.pdf
|
[remoteapi-docs]: https://github.com/onyx-and-iris/Voicemeeter-SDK/blob/main/VoicemeeterRemoteAPI.pdf
|
||||||
|
|||||||
@ -81,5 +81,4 @@ tasks:
|
|||||||
pwsh -c "bump show -f src/vmrcli.c -p \"#define VERSION .(\d+\.\d+\.\d+).\""
|
pwsh -c "bump show -f src/vmrcli.c -p \"#define VERSION .(\d+\.\d+\.\d+).\""
|
||||||
{{else}}
|
{{else}}
|
||||||
pwsh -c "bump {{.CLI_ARGS}} -w -f src/vmrcli.c -p \"#define VERSION .(\d+\.\d+\.\d+).\""
|
pwsh -c "bump {{.CLI_ARGS}} -w -f src/vmrcli.c -p \"#define VERSION .(\d+\.\d+\.\d+).\""
|
||||||
pwsh -c "bump {{.CLI_ARGS}} -w -f src/interface.c -f src/util.c -f src/vmrcli.c -f src/wrapper.c -p \"@version (\d+\.\d+\.\d+)\""
|
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
# Strip 0
|
# Strip 0
|
||||||
strip[0].mute !strip[0].mute strip[0].mute strip[0].gain strip[0].label="my podmic" strip[0].label
|
strip[0].mute !strip[0].mute strip[0].mute strip[0].gain strip[0].label=podmic strip[0].label
|
||||||
|
|
||||||
# Strip 1
|
# Strip 1
|
||||||
strip[1].mute=1 strip[1].mute strip[1].limit-=8
|
strip[1].mute=1 strip[1].mute strip[1].limit-=8
|
||||||
|
|||||||
@ -20,6 +20,5 @@ char *kind_as_string(char *s, int kind, int n);
|
|||||||
char *version_as_string(char *s, long v, int n);
|
char *version_as_string(char *s, long v, int n);
|
||||||
bool is_comment(char *s);
|
bool is_comment(char *s);
|
||||||
struct quickcommand *command_in_quickcommands(const char *command, const struct quickcommand *quickcommands, int n);
|
struct quickcommand *command_in_quickcommands(const char *command, const struct quickcommand *quickcommands, int n);
|
||||||
bool add_quotes_if_needed(const char *command, char *output, size_t max_len);
|
|
||||||
|
|
||||||
#endif /* __UTIL_H__ */
|
#endif /* __UTIL_H__ */
|
||||||
@ -3,7 +3,7 @@
|
|||||||
* @author Vincent Burel, Onyx and Iris (code@onyxandiris.online)
|
* @author Vincent Burel, Onyx and Iris (code@onyxandiris.online)
|
||||||
* @brief Functions for initializing the iVMR interface.
|
* @brief Functions for initializing the iVMR interface.
|
||||||
* Defines a single public function that returns a pointer to the interface.
|
* Defines a single public function that returns a pointer to the interface.
|
||||||
* @version 0.13.0
|
* @version 0.11.0
|
||||||
* @date 2024-07-06
|
* @date 2024-07-06
|
||||||
*
|
*
|
||||||
* @copyright Vincent Burel(c)2015-2021 All Rights Reserved
|
* @copyright Vincent Burel(c)2015-2021 All Rights Reserved
|
||||||
|
|||||||
66
src/util.c
66
src/util.c
@ -2,7 +2,7 @@
|
|||||||
* @file util.c
|
* @file util.c
|
||||||
* @author Onyx and Iris (code@onyxandiris.online)
|
* @author Onyx and Iris (code@onyxandiris.online)
|
||||||
* @brief Utility functions.
|
* @brief Utility functions.
|
||||||
* @version 0.13.0
|
* @version 0.11.0
|
||||||
* @date 2024-07-06
|
* @date 2024-07-06
|
||||||
*
|
*
|
||||||
* @copyright Copyright (c) 2024
|
* @copyright Copyright (c) 2024
|
||||||
@ -125,67 +125,3 @@ struct quickcommand *command_in_quickcommands(const char *command_key, const str
|
|||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Adds quotes around the value part of a command if it contains spaces or tabs
|
|
||||||
*
|
|
||||||
* @param command The input command string (parameter=value format)
|
|
||||||
* @param output Buffer to store the result
|
|
||||||
* @param max_len Maximum length of the output buffer
|
|
||||||
* @return true if quotes were added or command was copied successfully
|
|
||||||
* @return false if the command is too long or invalid
|
|
||||||
*/
|
|
||||||
bool add_quotes_if_needed(const char *command, char *output, size_t max_len)
|
|
||||||
{
|
|
||||||
const char *equals_pos = strchr(command, '=');
|
|
||||||
|
|
||||||
// No '=' found, copy command as-is
|
|
||||||
if (equals_pos == NULL) {
|
|
||||||
if (strlen(command) >= max_len)
|
|
||||||
return false;
|
|
||||||
strcpy(output, command);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *value = equals_pos + 1;
|
|
||||||
|
|
||||||
// Value doesn't contain spaces or tabs, copy command as-is
|
|
||||||
if (strchr(value, ' ') == NULL && strchr(value, '\t') == NULL) {
|
|
||||||
if (strlen(command) >= max_len)
|
|
||||||
return false;
|
|
||||||
strcpy(output, command);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Value needs quotes - calculate required buffer size
|
|
||||||
size_t param_len = equals_pos - command;
|
|
||||||
size_t value_len = strlen(value);
|
|
||||||
size_t quotes_len = 2;
|
|
||||||
size_t required_len = param_len + 1 + quotes_len + value_len + 1; // param + '=' + '"' + value + '"' + '\0'
|
|
||||||
|
|
||||||
if (required_len > max_len)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Construct the output string in the format: parameter="value"
|
|
||||||
* - Copy the parameter part (up to the '=')
|
|
||||||
* - Append '=' and opening quote
|
|
||||||
* - Append the value
|
|
||||||
* - Append closing quote and null terminator
|
|
||||||
*/
|
|
||||||
char *pos = output;
|
|
||||||
|
|
||||||
strncpy(pos, command, param_len);
|
|
||||||
pos += param_len;
|
|
||||||
|
|
||||||
*pos++ = '=';
|
|
||||||
*pos++ = '"';
|
|
||||||
|
|
||||||
strcpy(pos, value);
|
|
||||||
pos += value_len;
|
|
||||||
|
|
||||||
*pos++ = '"';
|
|
||||||
*pos = '\0';
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|||||||
107
src/vmrcli.c
107
src/vmrcli.c
@ -2,7 +2,7 @@
|
|||||||
* @file vmrcli.c
|
* @file vmrcli.c
|
||||||
* @author Onyx and Iris (code@onyxandiris.online)
|
* @author Onyx and Iris (code@onyxandiris.online)
|
||||||
* @brief A Voicemeeter Remote Command Line Interface
|
* @brief A Voicemeeter Remote Command Line Interface
|
||||||
* @version 0.13.0
|
* @version 0.11.0
|
||||||
* @date 2024-07-06
|
* @date 2024-07-06
|
||||||
*
|
*
|
||||||
* @copyright Copyright (c) 2024
|
* @copyright Copyright (c) 2024
|
||||||
@ -11,8 +11,6 @@
|
|||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
|
||||||
#include <ctype.h>
|
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include "interface.h"
|
#include "interface.h"
|
||||||
@ -37,7 +35,7 @@
|
|||||||
#define RES_SZ 512 /* Size of the buffer passed to VBVMR_GetParameterStringW */
|
#define RES_SZ 512 /* Size of the buffer passed to VBVMR_GetParameterStringW */
|
||||||
#define COUNT_OF(x) (sizeof(x) / sizeof(x[0]))
|
#define COUNT_OF(x) (sizeof(x) / sizeof(x[0]))
|
||||||
#define DELIMITERS " \t;,"
|
#define DELIMITERS " \t;,"
|
||||||
#define VERSION "0.13.0"
|
#define VERSION "0.12.0"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @enum The kind of values a get call may return.
|
* @enum The kind of values a get call may return.
|
||||||
@ -296,35 +294,11 @@ static void interactive(PT_VMR vmr, bool with_prompt, char *delimiters)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Helper functions for parse_input */
|
|
||||||
static inline bool is_quote_char(char c) {
|
|
||||||
return (c == '"' || c == '\'');
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool is_delimiter_char(char c, const char *delimiters) {
|
|
||||||
return strchr(delimiters, c) != NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static char* skip_consecutive_delimiters(char *p, const char *delimiters) {
|
|
||||||
while (*p != '\0' && is_delimiter_char(*p, delimiters)) {
|
|
||||||
p++;
|
|
||||||
}
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool add_char_to_token(char *token, size_t *token_len, char c, size_t max_len) {
|
|
||||||
if (*token_len < max_len - 1) {
|
|
||||||
token[(*token_len)++] = c;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false; // Buffer would overflow
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Parse each input line into separate commands and execute them.
|
* @brief Returns early if input is a comment
|
||||||
* Commands are split based on the delimiters argument, but quoted strings are preserved as single commands.
|
* Walks through each line split by " \t;," delimiters.
|
||||||
* See the test cases for examples of how input lines are parsed:
|
* Each token is passed to parse_command()
|
||||||
* https://github.com/onyx-and-iris/vmrcli?tab=readme-ov-file#api-commands
|
*
|
||||||
* @param vmr Pointer to the iVMR interface
|
* @param vmr Pointer to the iVMR interface
|
||||||
* @param input Each input line, from stdin or CLI args
|
* @param input Each input line, from stdin or CLI args
|
||||||
* @param delimiters A string of delimiter characters to split each input line
|
* @param delimiters A string of delimiter characters to split each input line
|
||||||
@ -334,54 +308,13 @@ static void parse_input(PT_VMR vmr, char *input, char *delimiters)
|
|||||||
if (is_comment(input))
|
if (is_comment(input))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
char *current = input;
|
char *token, *p;
|
||||||
char token[MAX_LINE];
|
|
||||||
size_t token_length = 0;
|
|
||||||
bool inside_quotes = false;
|
|
||||||
char quote_char = '\0';
|
|
||||||
|
|
||||||
while (*current != '\0')
|
token = strtok_r(input, delimiters, &p);
|
||||||
|
while (token != NULL)
|
||||||
{
|
{
|
||||||
if (!inside_quotes && is_quote_char(*current))
|
|
||||||
{
|
|
||||||
inside_quotes = true;
|
|
||||||
quote_char = *current;
|
|
||||||
current++;
|
|
||||||
log_trace("Entering quotes with char '%c'", quote_char);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
else if (inside_quotes && *current == quote_char)
|
|
||||||
{
|
|
||||||
inside_quotes = false;
|
|
||||||
quote_char = '\0';
|
|
||||||
current++;
|
|
||||||
log_trace("Exiting quotes");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
else if (!inside_quotes && is_delimiter_char(*current, delimiters))
|
|
||||||
{
|
|
||||||
if (token_length > 0)
|
|
||||||
{
|
|
||||||
token[token_length] = '\0';
|
|
||||||
parse_command(vmr, token);
|
|
||||||
token_length = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
current = skip_consecutive_delimiters(current, delimiters);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
add_char_to_token(token, &token_length, *current, MAX_LINE);
|
|
||||||
log_trace("Added char '%c' to token, current token: '%s'", *current, token);
|
|
||||||
}
|
|
||||||
current++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (token_length > 0)
|
|
||||||
{
|
|
||||||
token[token_length] = '\0';
|
|
||||||
parse_command(vmr, token);
|
parse_command(vmr, token);
|
||||||
|
token = strtok_r(NULL, delimiters, &p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -408,7 +341,8 @@ static void parse_command(PT_VMR vmr, char *command)
|
|||||||
if (qc_ptr != NULL)
|
if (qc_ptr != NULL)
|
||||||
{
|
{
|
||||||
set_parameters(vmr, qc_ptr->fullcommand);
|
set_parameters(vmr, qc_ptr->fullcommand);
|
||||||
if (eflag) {
|
if (eflag)
|
||||||
|
{
|
||||||
printf("Setting %s\n", qc_ptr->fullcommand);
|
printf("Setting %s\n", qc_ptr->fullcommand);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@ -425,7 +359,8 @@ static void parse_command(PT_VMR vmr, char *command)
|
|||||||
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) {
|
if (eflag)
|
||||||
|
{
|
||||||
printf("Toggling %s\n", command);
|
printf("Toggling %s\n", command);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -437,18 +372,10 @@ static void parse_command(PT_VMR vmr, char *command)
|
|||||||
|
|
||||||
if (strchr(command, '=') != NULL) /* set */
|
if (strchr(command, '=') != NULL) /* set */
|
||||||
{
|
{
|
||||||
char quoted_command[MAX_LINE];
|
set_parameters(vmr, command);
|
||||||
|
if (eflag)
|
||||||
if (add_quotes_if_needed(command, quoted_command, MAX_LINE))
|
|
||||||
{
|
{
|
||||||
set_parameters(vmr, quoted_command);
|
printf("Setting %s\n", command);
|
||||||
if (eflag) {
|
|
||||||
printf("Setting %s\n", command);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
log_error("Command too long after adding quotes");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else /* get */
|
else /* get */
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
* @file wrapper.c
|
* @file wrapper.c
|
||||||
* @author Onyx and Iris (code@onyxandiris.online)
|
* @author Onyx and Iris (code@onyxandiris.online)
|
||||||
* @brief Provides public functions that wrap the iVMR calls
|
* @brief Provides public functions that wrap the iVMR calls
|
||||||
* @version 0.13.0
|
* @version 0.11.0
|
||||||
* @date 2024-07-06
|
* @date 2024-07-06
|
||||||
*
|
*
|
||||||
* @copyright Copyright (c) 2024
|
* @copyright Copyright (c) 2024
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user