Compare commits

...

6 Commits
v2.1.0 ... dev

Author SHA1 Message Date
0bfc1e62ba define custom usage handler. 2024-07-02 10:54:27 +01:00
6211531f87 fix expected output 2024-07-02 06:57:58 +01:00
c9eaa76f62 fix expected output 2024-07-02 06:55:30 +01:00
1b9d633217 add help dialogue
add guard for toggleCmd to prevent unsafe gain changes.

use logrus for control of log levels.

add an input prompt for interactive mode
2024-07-02 06:50:28 +01:00
359c2d61b5 reword 2024-07-01 07:32:41 +01:00
e586478729 fix WithTimeout name in README
move it above WithBits
2024-07-01 07:23:40 +01:00
4 changed files with 131 additions and 90 deletions

View File

@ -15,7 +15,7 @@ Before any major/minor/patch bump all unit tests will be run to verify they pass
### Added
- Added a configurable timeout in seconds (defaults to 2).
- Added a configurable login timeout in seconds (defaults to 2).
- Option function added for overriding the type of Voicemeeter GUI runVoicemeeter() will launch.
- Explanation of Option functions added to README.

View File

@ -81,18 +81,18 @@ Pass a delay in milliseconds to force the getters to wait for dirty parameters t
Useful if not listening for event updates.
### `voicemeeter.WithTimeout(timeout int)`
Set a login timeout, defaults to 2 seconds. For example to set it to 1s:
`voicemeeter.NewRemote("banana", 20, voicemeeter.WithTimeout(1))`
### `voicemeeter.WithBits(bits int)`
Override the type of Voicemeeter GUI to launch on 64 bit systems. For example, to force 32 bit GUI:
`voicemeeter.NewRemote("banana", 20, voicemeeter.WithBits(32))`
### `voicemeeter.WithBits(timeout int)`
Set a login timeout, defaults to 2 seconds. For example to set it to 1s:
`voicemeeter.NewRemote("banana", 20, voicemeeter.WithBits(1))`
## `Remote Type`
#### `vm.Strip`

View File

@ -1,6 +1,6 @@
## About
A simple voicemeeter-cli program. Offers ability to toggle, get and set parameters.
A Voicemeeter CLI, offers ability to toggle, get and set parameters.
## Install
@ -8,28 +8,32 @@ First build and install it with `go install` (skip this step if using binary fro
## Use
Toggle with `!` prefix, get by excluding `=` and set by including `=`. Mix and match arguments.
Commands that begin with `!` will toggle a parameter, commands that contain `=` will set a parameter, all other commands will get a value.
You may pass the following optional flags:
- -v: (-verbose) to toggle console output.
- -i: (-interactive) to toggle interactive mode.
- -k: (-kind) to set the kind of Voicemeeter. Defaults to banana.
- -d: (-delay) to set a delay on the getters. Defaults to 20ms.
- -h: Print the help message
- -i: Enable interactive mode
- -k: The kind of Voicemeeter GUI to launch, defaults to Banana
- -l: Log level (0 up to 6), defaults to 3, Warn Level
- -d: Set the delay between commands, defaults to 20ms
- -v: Enable extra console output (toggle and set messages).
for example:
`vm-cli.exe -v -k=potato -d=25 strip[0].mute=0 strip[0].mute !strip[0].mute strip[0].mute bus[0].gain=-8.8 command.lock=1`
`vm-cli.exe -v -l=4 -k=potato strip[0].mute=0 strip[0].mute !strip[0].mute strip[0].mute bus[0].gain=-8.8 command.lock=1`
Expected output:
```
Running command strip[0].mute=0
Value of strip[0].mute is: 0
time="<timestamp>" level=info msg="Logged into Voicemeeter Potato v3.1.1.1"
Setting strip[0].mute=0
strip[0].mute: 0.00
Toggling strip[0].mute
Value of strip[0].mute is: 1
Running command bus[0].gain=-8.8
Running command command.lock=1
strip[0].mute: 1.00
Setting bus[0].gain=-8.8
Setting command.lock=1
time="<timestamp>" level=info msg="Logged out of Voicemeeter Potato"
```
If running in interactive mode enter `q`, `quit` or `<Enter>` to exit.
If running in interactive mode enter `Q`, to exit.

View File

@ -4,26 +4,36 @@ import (
"bufio"
"flag"
"fmt"
"log"
"os"
"strings"
log "github.com/sirupsen/logrus"
"github.com/onyx-and-iris/voicemeeter/v2"
)
type (
verbosePrinter struct {
verbose bool
}
const (
FLOAT = iota
STRING
)
type result struct {
kind int
stringParam string
floatParam float64
}
type verbosePrinter struct {
verbose bool
}
func newVerbosePrinter() *verbosePrinter {
return &verbosePrinter{}
}
func (v *verbosePrinter) printf(format string, a ...interface{}) {
if v.verbose {
fmt.Printf(format+"\n", a...)
fmt.Printf(format, a...)
}
}
@ -40,28 +50,68 @@ func main() {
kind string
delay int
interactive bool
loglevel int
help bool
)
flag.Usage = usage
flag.BoolVar(&help, "help", false, "print the help message")
flag.BoolVar(&help, "h", false, "print the help message (shorthand)")
flag.StringVar(&kind, "kind", "banana", "kind of voicemeeter")
flag.StringVar(&kind, "k", "banana", "kind of voicemeeter (shorthand)")
flag.IntVar(&delay, "delay", 20, "delay between commands")
flag.IntVar(&delay, "d", 20, "delay between commands (shorthand)")
flag.BoolVar(&vPrinter.verbose, "verbose", false, "toggle console output")
flag.BoolVar(&vPrinter.verbose, "v", false, "toggle console output (shorthand)")
flag.BoolVar(&interactive, "interactive", false, "toggle interactive mode")
flag.BoolVar(&interactive, "i", false, "toggle interactive mode (shorthand)")
flag.IntVar(&loglevel, "loglevel", int(log.WarnLevel), "set the log level")
flag.IntVar(&loglevel, "l", int(log.WarnLevel), "set the log level (shorthand)")
flag.BoolVar(&vPrinter.verbose, "verbose", false, "enable extra console output (toggle and set messages)")
flag.BoolVar(&vPrinter.verbose, "v", false, "enable extra console output (toggle and set messages) (shorthand)")
flag.Parse()
if help {
flag.Usage()
return
}
if loglevel >= int(log.PanicLevel) && loglevel <= int(log.TraceLevel) {
log.SetLevel(log.Level(loglevel))
}
vm, err := vmConnect(kind, delay)
if err != nil {
log.Fatal(err)
}
defer vm.Logout()
err = runCommands(vm, interactive)
if err != nil {
fmt.Println(err)
if interactive {
interactiveMode(vm)
return
}
args := flag.Args()
if len(args) == 0 {
flag.Usage()
return
}
for _, arg := range args {
if err := parse(vm, arg); err != nil {
log.Error(err.Error())
}
}
}
func usage() {
fmt.Println("usage: ./vm-cli.exe [-h] [-i] [-k] [-l] [-d] [-v]\n" +
"Where:\n" +
"\th: Print the help message\n" +
"\ti: Enable interactive mode\n" +
"\tk: The kind of Voicemeeter GUI to launch, defaults to Banana\n" +
"\tl: Log level 0 up to 6, (defaults to 3, Warn Level)\n" +
"\td: Set the delay between commands (defaults to 20ms)\n" +
"\tv: Enable extra console output (toggle and set messages).")
}
func vmConnect(kind string, delay int) (*voicemeeter.Remote, error) {
@ -78,38 +128,23 @@ func vmConnect(kind string, delay int) (*voicemeeter.Remote, error) {
return vm, nil
}
func runCommands(vm *voicemeeter.Remote, interactive bool) error {
if interactive {
return interactiveMode(vm)
}
args := flag.Args()
if len(args) == 0 {
err := fmt.Errorf("must provide some commands to run")
return err
}
for _, arg := range args {
err := parse(vm, arg)
if err != nil {
vPrinter.printf(err.Error())
}
}
return nil
}
func interactiveMode(vm *voicemeeter.Remote) error {
vPrinter.printf("running in interactive mode... waiting for input")
fmt.Println("Interactive mode enabled. Enter 'Q' to exit.")
scanner := bufio.NewScanner(os.Stdin)
fmt.Printf(">> ")
for scanner.Scan() {
input := scanner.Text()
if input == "q" || input == "quit" || input == "" {
return nil
if strings.ToUpper(input) == "Q" {
break
}
for _, cmd := range strings.Split(input, " ") {
err := parse(vm, cmd)
if err != nil {
vPrinter.printf(err.Error())
if err := parse(vm, cmd); err != nil {
log.Error(err.Error())
}
}
fmt.Printf(">> ")
}
if scanner.Err() != nil {
return scanner.Err()
@ -119,58 +154,60 @@ func interactiveMode(vm *voicemeeter.Remote) error {
func parse(vm *voicemeeter.Remote, cmd string) error {
if cmd[0] == '!' {
err := toggleCmd(vm, cmd[1:])
if err != nil {
if err := toggleCmd(vm, cmd[1:]); err != nil {
return err
}
} else if strings.Contains(cmd, "=") {
if err := setCmd(vm, cmd); err != nil {
return err
}
} else {
if strings.Contains(cmd, "=") {
err := setCmd(vm, cmd)
if err != nil {
return err
}
} else {
err := getCmd(vm, cmd)
if err != nil {
return err
}
r := result{kind: FLOAT}
if err := getCmd(vm, cmd, &r); err != nil {
return err
}
switch r.kind {
case FLOAT:
fmt.Printf("%s: %.2f\n", cmd, r.floatParam)
case STRING:
fmt.Printf("%s: %s\n", cmd, r.stringParam)
}
}
return nil
}
func toggleCmd(vm *voicemeeter.Remote, cmd string) error {
val, err := vm.GetFloat(cmd)
if err != nil {
err = fmt.Errorf("unable to toggle %s", cmd)
r := result{kind: FLOAT}
if err := getCmd(vm, cmd, &r); err != nil {
return err
}
vm.SetFloat(cmd, 1-val)
vPrinter.printf("Toggling %s", cmd)
if r.kind == FLOAT && (r.floatParam == 0 || r.floatParam == 1) {
vPrinter.printf("Toggling %s\n", cmd)
vm.SetFloat(cmd, 1-r.floatParam)
} else {
log.Warnf("%s does not appear to be a boolean parameter", cmd)
}
return nil
}
func setCmd(vm *voicemeeter.Remote, cmd string) error {
vPrinter.printf("Running command %s", cmd)
err := vm.SendText(cmd)
if err != nil {
if err := vm.SendText(cmd); err != nil {
err = fmt.Errorf("unable to set %s", cmd)
return err
}
vPrinter.printf("Setting %s\n", cmd)
return nil
}
func getCmd(vm *voicemeeter.Remote, cmd string, r *result) error {
if val, err := vm.GetFloat(cmd); err == nil {
r.floatParam = val
} else if val, err := vm.GetString(cmd); err == nil {
r.kind = STRING
r.stringParam = val
} else {
err := fmt.Errorf("unknown parameter '%s'", cmd)
return err
}
return nil
}
func getCmd(vm *voicemeeter.Remote, cmd string) error {
valF, err := vm.GetFloat(cmd)
if err != nil {
valS, err := vm.GetString(cmd)
if err != nil {
err = fmt.Errorf("unable to get %s", cmd)
return err
}
fmt.Printf("Value of %s is: %s", cmd, valS)
} else {
fmt.Printf("Value of %s is: %v", cmd, valF)
}
return nil
}