2022-09-17 03:11:53 +01:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2022-10-04 20:56:38 +01:00
|
|
|
"bufio"
|
2022-09-18 05:39:42 +01:00
|
|
|
"flag"
|
2022-09-17 03:11:53 +01:00
|
|
|
"fmt"
|
2022-10-04 20:56:38 +01:00
|
|
|
"os"
|
2022-09-17 03:11:53 +01:00
|
|
|
"strings"
|
|
|
|
|
2024-07-02 06:50:28 +01:00
|
|
|
log "github.com/sirupsen/logrus"
|
|
|
|
|
2022-12-08 10:52:15 +00:00
|
|
|
"github.com/onyx-and-iris/voicemeeter/v2"
|
2022-09-17 03:11:53 +01:00
|
|
|
)
|
|
|
|
|
2024-07-02 06:50:28 +01:00
|
|
|
const (
|
|
|
|
FLOAT = iota
|
|
|
|
STRING
|
2022-10-05 21:34:59 +01:00
|
|
|
)
|
|
|
|
|
2024-07-02 06:50:28 +01:00
|
|
|
type result struct {
|
|
|
|
kind int
|
|
|
|
stringParam string
|
|
|
|
floatParam float64
|
|
|
|
}
|
|
|
|
|
|
|
|
type verbosePrinter struct {
|
|
|
|
verbose bool
|
|
|
|
}
|
|
|
|
|
2022-10-05 21:34:59 +01:00
|
|
|
func newVerbosePrinter() *verbosePrinter {
|
|
|
|
return &verbosePrinter{}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v *verbosePrinter) printf(format string, a ...interface{}) {
|
|
|
|
if v.verbose {
|
2024-07-02 06:50:28 +01:00
|
|
|
fmt.Printf(format, a...)
|
2022-10-05 21:34:59 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
|
|
|
vPrinter *verbosePrinter
|
|
|
|
)
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
vPrinter = newVerbosePrinter()
|
|
|
|
}
|
|
|
|
|
2022-09-17 03:11:53 +01:00
|
|
|
func main() {
|
2022-10-04 20:56:38 +01:00
|
|
|
var (
|
|
|
|
kind string
|
|
|
|
delay int
|
|
|
|
interactive bool
|
2024-07-02 06:50:28 +01:00
|
|
|
loglevel int
|
|
|
|
help bool
|
2022-10-04 20:56:38 +01:00
|
|
|
)
|
|
|
|
|
2024-07-02 06:50:28 +01:00
|
|
|
flag.BoolVar(&help, "help", false, "print the help dialogue")
|
|
|
|
flag.BoolVar(&help, "h", false, "print the help dialogue (shorthand)")
|
2022-09-29 18:05:21 +01:00
|
|
|
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)")
|
2022-10-04 20:56:38 +01:00
|
|
|
flag.BoolVar(&interactive, "interactive", false, "toggle interactive mode")
|
|
|
|
flag.BoolVar(&interactive, "i", false, "toggle interactive mode (shorthand)")
|
2024-07-02 06:50:28 +01:00
|
|
|
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, "toggle console output")
|
|
|
|
flag.BoolVar(&vPrinter.verbose, "v", false, "toggle console output (shorthand)")
|
2022-09-18 05:39:42 +01:00
|
|
|
flag.Parse()
|
|
|
|
|
2024-07-02 06:50:28 +01:00
|
|
|
if help {
|
|
|
|
help_dialogue()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if loglevel >= int(log.PanicLevel) && loglevel <= int(log.TraceLevel) {
|
|
|
|
log.SetLevel(log.Level(loglevel))
|
|
|
|
}
|
|
|
|
|
2022-09-29 18:05:21 +01:00
|
|
|
vm, err := vmConnect(kind, delay)
|
2022-09-17 03:11:53 +01:00
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
defer vm.Logout()
|
|
|
|
|
2024-07-02 06:50:28 +01:00
|
|
|
if interactive {
|
|
|
|
interactiveMode(vm)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
args := flag.Args()
|
|
|
|
if len(args) == 0 {
|
|
|
|
help_dialogue()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, arg := range args {
|
|
|
|
if err := parse(vm, arg); err != nil {
|
|
|
|
log.Error(err.Error())
|
|
|
|
}
|
2022-09-17 03:11:53 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-07-02 06:50:28 +01:00
|
|
|
func help_dialogue() {
|
|
|
|
fmt.Printf("usage: ./vm-cli [-h] [-i] [-k] [-l] [-d] [-v]\n" +
|
|
|
|
"Where:\n" +
|
|
|
|
"\th: Print the help dialogue\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).")
|
|
|
|
}
|
|
|
|
|
2022-09-29 18:05:21 +01:00
|
|
|
func vmConnect(kind string, delay int) (*voicemeeter.Remote, error) {
|
|
|
|
vm, err := voicemeeter.NewRemote(kind, delay)
|
2022-09-17 03:11:53 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = vm.Login()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return vm, nil
|
|
|
|
}
|
2022-09-18 05:39:42 +01:00
|
|
|
|
2022-10-05 21:34:59 +01:00
|
|
|
func interactiveMode(vm *voicemeeter.Remote) error {
|
2024-07-02 06:50:28 +01:00
|
|
|
fmt.Println("Interactive mode enabled. Enter 'Q' to exit.")
|
|
|
|
|
2022-10-04 20:56:38 +01:00
|
|
|
scanner := bufio.NewScanner(os.Stdin)
|
2024-07-02 06:50:28 +01:00
|
|
|
fmt.Printf(">> ")
|
2022-10-04 20:56:38 +01:00
|
|
|
for scanner.Scan() {
|
|
|
|
input := scanner.Text()
|
2024-07-02 06:50:28 +01:00
|
|
|
if strings.ToUpper(input) == "Q" {
|
|
|
|
break
|
2022-10-04 20:56:38 +01:00
|
|
|
}
|
2024-07-02 06:50:28 +01:00
|
|
|
|
2022-10-04 20:56:38 +01:00
|
|
|
for _, cmd := range strings.Split(input, " ") {
|
2024-07-02 06:50:28 +01:00
|
|
|
if err := parse(vm, cmd); err != nil {
|
|
|
|
log.Error(err.Error())
|
2022-09-18 05:39:42 +01:00
|
|
|
}
|
2022-10-04 20:56:38 +01:00
|
|
|
}
|
2024-07-02 06:50:28 +01:00
|
|
|
fmt.Printf(">> ")
|
2022-10-04 20:56:38 +01:00
|
|
|
}
|
2022-10-06 11:31:39 +01:00
|
|
|
if scanner.Err() != nil {
|
|
|
|
return scanner.Err()
|
|
|
|
}
|
2022-10-04 20:56:38 +01:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-10-05 21:34:59 +01:00
|
|
|
func parse(vm *voicemeeter.Remote, cmd string) error {
|
2022-10-04 20:56:38 +01:00
|
|
|
if cmd[0] == '!' {
|
2024-07-02 06:50:28 +01:00
|
|
|
if err := toggleCmd(vm, cmd[1:]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
} else if strings.Contains(cmd, "=") {
|
|
|
|
if err := setCmd(vm, cmd); err != nil {
|
2022-10-04 20:56:38 +01:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
} else {
|
2024-07-02 06:50:28 +01:00
|
|
|
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)
|
2022-09-18 05:39:42 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2022-10-04 20:56:38 +01:00
|
|
|
|
2022-10-05 21:34:59 +01:00
|
|
|
func toggleCmd(vm *voicemeeter.Remote, cmd string) error {
|
2024-07-02 06:50:28 +01:00
|
|
|
r := result{kind: FLOAT}
|
|
|
|
if err := getCmd(vm, cmd, &r); err != nil {
|
2022-10-04 20:56:38 +01:00
|
|
|
return err
|
|
|
|
}
|
2024-07-02 06:50:28 +01:00
|
|
|
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)
|
|
|
|
}
|
2022-10-04 20:56:38 +01:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-10-05 21:34:59 +01:00
|
|
|
func setCmd(vm *voicemeeter.Remote, cmd string) error {
|
2024-07-02 06:50:28 +01:00
|
|
|
if err := vm.SendText(cmd); err != nil {
|
2022-10-04 20:56:38 +01:00
|
|
|
err = fmt.Errorf("unable to set %s", cmd)
|
|
|
|
return err
|
|
|
|
}
|
2024-07-02 06:50:28 +01:00
|
|
|
vPrinter.printf("Setting %s\n", cmd)
|
2022-10-04 20:56:38 +01:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2024-07-02 06:50:28 +01:00
|
|
|
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
|
2022-10-04 20:56:38 +01:00
|
|
|
} else {
|
2024-07-02 06:50:28 +01:00
|
|
|
err := fmt.Errorf("unknown parameter '%s'", cmd)
|
|
|
|
return err
|
2022-10-04 20:56:38 +01:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|