mirror of
https://github.com/onyx-and-iris/vbantxt.git
synced 2026-04-08 02:03:30 +00:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c73614133d | |||
| 2bdabdae03 | |||
| 31b188280d | |||
| ccf34e492d | |||
|
|
56ce415d6d | ||
|
|
cfd89fb1ed | ||
|
|
73a99e5059 | ||
|
|
6221f6a167 | ||
|
|
826df820fc |
31
CHANGELOG.md
Normal file
31
CHANGELOG.md
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
Before any major/minor/patch bump all unit tests will be run to verify they pass.
|
||||||
|
|
||||||
|
## [Unreleased]
|
||||||
|
|
||||||
|
- [x]
|
||||||
|
|
||||||
|
# [0.1.0] - 2024-06-28
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Matrix and Logging sections to README.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- `host` flag now defaults to "localhost". Useful if sending VBAN-Text to Matrix
|
||||||
|
- `loglevel` flag now expects values that correspond to the logrus package loglevels (0 up to 6). See README.
|
||||||
|
- Config values are only applied if the corresponding flag was not passed on the command line.
|
||||||
|
|
||||||
|
# [0.0.1] - 2022-09-23
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Initial release, package implements VBAN PROTOCOL TXT with a basic CLI for configuring options.
|
||||||
|
- Ability to load configuration settings from a config.toml.
|
||||||
69
README.md
69
README.md
@@ -1,25 +1,33 @@
|
|||||||
# vbantxt
|

|
||||||
|

|
||||||
|
|
||||||
VBAN sendtext cli utility for sending Voicemeeter string requests over a network.
|
# VBAN Sendtext CLI Utility
|
||||||
|
|
||||||
|
Send Voicemeeter string requests over a network or to Matrix
|
||||||
|
|
||||||
|
For an outline of past/future changes refer to: [CHANGELOG](CHANGELOG.md)
|
||||||
|
|
||||||
## Tested against
|
## Tested against
|
||||||
|
|
||||||
- Basic 1.0.8.4
|
- Basic 1.0.8.4
|
||||||
- Banana 2.0.6.4
|
- Banana 2.0.6.4
|
||||||
- Potato 3.0.2.4
|
- Potato 3.0.2.4
|
||||||
|
- Matrix 1.0.0.3
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
- [Voicemeeter](https://voicemeeter.com/)
|
- [Voicemeeter](https://voicemeeter.com/) or [Matrix](https://vb-audio.com/Matrix/)
|
||||||
- Go 1.18 or greater
|
- Go 1.18 or greater (if you want to compile yourself, otherwise check `Releases`)
|
||||||
|
|
||||||
## `Use`
|
---
|
||||||
|
|
||||||
#### Command Line
|
## `Command Line`
|
||||||
|
|
||||||
Pass `host`, `port` and `streamname` as flags, for example:
|
Pass `host`, `port` and `streamname` as flags, for example:
|
||||||
|
|
||||||
`vbantxt-cli -h="gamepc.local" -p=6980 -s=Command1 "strip[0].mute=1 strip[1].mono=1"`
|
```
|
||||||
|
vbantxt-cli -h="gamepc.local" -p=6980 -s=Command1 "strip[0].mute=1 strip[1].mono=1"
|
||||||
|
```
|
||||||
|
|
||||||
You may also store them in a `config.toml` located in `home directory / .vbantxt_cli /`
|
You may also store them in a `config.toml` located in `home directory / .vbantxt_cli /`
|
||||||
|
|
||||||
@@ -28,11 +36,19 @@ A valid `config.toml` might look like this:
|
|||||||
```toml
|
```toml
|
||||||
[connection]
|
[connection]
|
||||||
Host="gamepc.local"
|
Host="gamepc.local"
|
||||||
Port=6990
|
Port=6980
|
||||||
Streamname="Command1"
|
Streamname="Command1"
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Script files
|
- `host` defaults to "localhost"
|
||||||
|
- `port` defaults to 6980
|
||||||
|
- `streamname` defaults to "Command1"
|
||||||
|
|
||||||
|
Command line flags will override values in a config.toml.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## `Script files`
|
||||||
|
|
||||||
The vbantxt-cli utility accepts a single string request or an array of string requests. This means you can pass scripts stored in files.
|
The vbantxt-cli utility accepts a single string request or an array of string requests. This means you can pass scripts stored in files.
|
||||||
|
|
||||||
@@ -40,9 +56,34 @@ For example, in Windows with Powershell you could:
|
|||||||
|
|
||||||
`vbantxt-cli $(Get-Content .\script.txt)`
|
`vbantxt-cli $(Get-Content .\script.txt)`
|
||||||
|
|
||||||
|
Or with Bash:
|
||||||
|
|
||||||
|
`cat script.txt | xargs vbantxt-cli`
|
||||||
|
|
||||||
to load commands from a file:
|
to load commands from a file:
|
||||||
|
|
||||||
```
|
```
|
||||||
strip[0].mute=0;strip[0].mute=0
|
strip[0].mute=1;strip[0].mono=0
|
||||||
strip[1].mono=0;strip[1].mono=0
|
strip[1].mute=0;strip[1].mono=1
|
||||||
|
bus[3].eq.On=0
|
||||||
```
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## `Matrix`
|
||||||
|
|
||||||
|
Sending commands to VB-Audio Matrix is also possible, for example:
|
||||||
|
|
||||||
|
```
|
||||||
|
vbantxt-cli -s=streamname "Point(ASIO128.IN[2],ASIO128.OUT[1]).dBGain = -8"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## `Logging`
|
||||||
|
|
||||||
|
Log level may be set by passing the `-l` flag with a number from 0 up to 6 where
|
||||||
|
|
||||||
|
0 = Panic, 1 = Fatal, 2 = Error, 3 = Warning, 4 = Info, 5 = Debug, 6 = Trace
|
||||||
|
|
||||||
|
Log level defaults to Warning level.
|
||||||
|
|||||||
71
main.go
71
main.go
@@ -3,7 +3,6 @@ package main
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
@@ -44,20 +43,9 @@ type (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func setLogLevel(loglevel int) {
|
|
||||||
switch loglevel {
|
|
||||||
case 0:
|
|
||||||
log.SetLevel(log.WarnLevel)
|
|
||||||
case 1:
|
|
||||||
log.SetLevel(log.InfoLevel)
|
|
||||||
case 2:
|
|
||||||
log.SetLevel(log.DebugLevel)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
flag.StringVar(&host, "host", "", "vban host")
|
flag.StringVar(&host, "host", "localhost", "vban host")
|
||||||
flag.StringVar(&host, "h", "", "vban host (shorthand)")
|
flag.StringVar(&host, "h", "localhost", "vban host (shorthand)")
|
||||||
flag.IntVar(&port, "port", 6980, "vban server port")
|
flag.IntVar(&port, "port", 6980, "vban server port")
|
||||||
flag.IntVar(&port, "p", 6980, "vban server port (shorthand)")
|
flag.IntVar(&port, "p", 6980, "vban server port (shorthand)")
|
||||||
flag.StringVar(&streamname, "streamname", "Command1", "stream name for text requests")
|
flag.StringVar(&streamname, "streamname", "Command1", "stream name for text requests")
|
||||||
@@ -68,11 +56,13 @@ func main() {
|
|||||||
flag.IntVar(&channel, "c", 0, "vban channel (shorthand)")
|
flag.IntVar(&channel, "c", 0, "vban channel (shorthand)")
|
||||||
flag.IntVar(&delay, "delay", 20, "delay between requests")
|
flag.IntVar(&delay, "delay", 20, "delay between requests")
|
||||||
flag.IntVar(&delay, "d", 20, "delay between requests (shorthand)")
|
flag.IntVar(&delay, "d", 20, "delay between requests (shorthand)")
|
||||||
flag.IntVar(&loglevel, "loglevel", 0, "log level")
|
flag.IntVar(&loglevel, "loglevel", int(log.WarnLevel), "log level")
|
||||||
flag.IntVar(&loglevel, "l", 0, "log level (shorthand)")
|
flag.IntVar(&loglevel, "l", int(log.WarnLevel), "log level (shorthand)")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
setLogLevel(loglevel)
|
if loglevel >= int(log.PanicLevel) && loglevel <= int(log.TraceLevel) {
|
||||||
|
log.SetLevel(log.Level(loglevel))
|
||||||
|
}
|
||||||
|
|
||||||
c, err := vbanConnect()
|
c, err := vbanConnect()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -84,52 +74,49 @@ func main() {
|
|||||||
for _, arg := range flag.Args() {
|
for _, arg := range flag.Args() {
|
||||||
err := send(c, header, arg)
|
err := send(c, header, arg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err)
|
log.Error(err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// vbanConnect establishes a VBAN connection to remote host
|
// vbanConnect establishes a VBAN connection to remote host
|
||||||
func vbanConnect() (*net.UDPConn, error) {
|
func vbanConnect() (*net.UDPConn, error) {
|
||||||
if host == "" {
|
homeDir, err := os.UserHomeDir()
|
||||||
conn, err := connFromToml()
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
f := filepath.Join(homeDir, ".vbantxt_cli", "config.toml")
|
||||||
|
if _, err := os.Stat(f); err == nil {
|
||||||
|
conn, err := connFromToml(f)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
host = conn.Host
|
if !isFlagPassed("h") && !isFlagPassed("host") {
|
||||||
port = conn.Port
|
host = conn.Host
|
||||||
streamname = conn.Streamname
|
}
|
||||||
if host == "" {
|
if !isFlagPassed("p") && !isFlagPassed("port") {
|
||||||
err := errors.New("must provide a host with --host flag or config.toml")
|
port = conn.Port
|
||||||
return nil, err
|
}
|
||||||
|
if !isFlagPassed("s") && !isFlagPassed("streamname") {
|
||||||
|
streamname = conn.Streamname
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CONNECT := fmt.Sprintf("%s:%d", host, port)
|
log.Debugf("Using values host: %s port: %d streamname: %s", host, port, streamname)
|
||||||
|
|
||||||
s, _ := net.ResolveUDPAddr("udp4", CONNECT)
|
s, _ := net.ResolveUDPAddr("udp4", fmt.Sprintf("%s:%d", host, port))
|
||||||
c, err := net.DialUDP("udp4", nil, s)
|
c, err := net.DialUDP("udp4", nil, s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
log.Info("Connected to ", c.RemoteAddr().String())
|
log.Infof("Connected to %s", c.RemoteAddr())
|
||||||
|
|
||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// connFromToml parses connection info from config.toml
|
// connFromToml parses connection info from config.toml
|
||||||
func connFromToml() (*connection, error) {
|
func connFromToml(f string) (*connection, error) {
|
||||||
homeDir, err := os.UserHomeDir()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
f := filepath.Join(homeDir, ".vbantxt_cli", "config.toml")
|
|
||||||
if _, err := os.Stat(f); err != nil {
|
|
||||||
err := fmt.Errorf("unable to locate %s", f)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var c config
|
var c config
|
||||||
_, err = toml.DecodeFile(f, &c.Connection)
|
_, err := toml.DecodeFile(f, &c.Connection)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -139,7 +126,7 @@ func connFromToml() (*connection, error) {
|
|||||||
|
|
||||||
// send sends a VBAN text request over UDP to remote host
|
// send sends a VBAN text request over UDP to remote host
|
||||||
func send(c *net.UDPConn, h *requestHeader, msg string) error {
|
func send(c *net.UDPConn, h *requestHeader, msg string) error {
|
||||||
log.Debug("Sending '", msg, "' to: ", c.RemoteAddr().String())
|
log.Debugf("Sending '%s' to: %s", msg, c.RemoteAddr())
|
||||||
data := []byte(msg)
|
data := []byte(msg)
|
||||||
_, err := c.Write(append(h.header(), data...))
|
_, err := c.Write(append(h.header(), data...))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
12
util.go
12
util.go
@@ -1,5 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
|
import "flag"
|
||||||
|
|
||||||
// indexOf returns the index of an element in an array
|
// indexOf returns the index of an element in an array
|
||||||
func indexOf[T comparable](collection []T, e T) int {
|
func indexOf[T comparable](collection []T, e T) int {
|
||||||
for i, x := range collection {
|
for i, x := range collection {
|
||||||
@@ -9,3 +11,13 @@ func indexOf[T comparable](collection []T, e T) int {
|
|||||||
}
|
}
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isFlagPassed(name string) bool {
|
||||||
|
found := false
|
||||||
|
flag.Visit(func(f *flag.Flag) {
|
||||||
|
if f.Name == name {
|
||||||
|
found = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return found
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user