mirror of
https://github.com/onyx-and-iris/vbantxt.git
synced 2024-11-21 17:30:49 +00:00
initial commit
This commit is contained in:
parent
5dcf3fbc28
commit
4fd0fdbc56
2
.gitignore
vendored
2
.gitignore
vendored
@ -13,3 +13,5 @@
|
|||||||
|
|
||||||
# Dependency directories (remove the comment below to include it)
|
# Dependency directories (remove the comment below to include it)
|
||||||
# vendor/
|
# vendor/
|
||||||
|
|
||||||
|
*.txt
|
50
README.md
50
README.md
@ -1,2 +1,48 @@
|
|||||||
# vban-cli
|
# vbantxt
|
||||||
VBAN cli utility for sending parameter requests
|
|
||||||
|
VBAN sendtext cli utility for sending Voicemeeter string requests over a network.
|
||||||
|
|
||||||
|
## Tested against
|
||||||
|
|
||||||
|
- Basic 1.0.8.4
|
||||||
|
- Banana 2.0.6.4
|
||||||
|
- Potato 3.0.2.4
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- [Voicemeeter](https://voicemeeter.com/)
|
||||||
|
- Go 1.18 or greater
|
||||||
|
|
||||||
|
## `Use`
|
||||||
|
|
||||||
|
#### Command Line
|
||||||
|
|
||||||
|
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"`
|
||||||
|
|
||||||
|
You may also store them in a `config.toml` located in `home directory / .vbantxt_cli /`
|
||||||
|
|
||||||
|
A valid `config.toml` might look like this:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[connection]
|
||||||
|
Host="gamepc.local"
|
||||||
|
Port=6990
|
||||||
|
Streamname="Command1"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 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.
|
||||||
|
|
||||||
|
For example, in Windows with Powershell you could:
|
||||||
|
|
||||||
|
`vbantxt-cli $(Get-Content .\script.txt)`
|
||||||
|
|
||||||
|
to load commands from a file:
|
||||||
|
|
||||||
|
```
|
||||||
|
strip[0].mute=0;strip[0].mute=0
|
||||||
|
strip[1].mono=0;strip[1].mono=0
|
||||||
|
```
|
||||||
|
10
go.mod
Normal file
10
go.mod
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
module github.com/onyx-and-iris/vbantxt-cli
|
||||||
|
|
||||||
|
go 1.19
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/BurntSushi/toml v1.2.1
|
||||||
|
github.com/sirupsen/logrus v1.9.0
|
||||||
|
)
|
||||||
|
|
||||||
|
require golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect
|
17
go.sum
Normal file
17
go.sum
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak=
|
||||||
|
github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
|
||||||
|
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||||
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
|
||||||
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
155
main.go
Normal file
155
main.go
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/BurntSushi/toml"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
host string
|
||||||
|
port int
|
||||||
|
streamname string
|
||||||
|
bps int
|
||||||
|
channel int
|
||||||
|
delay int
|
||||||
|
loglevel int
|
||||||
|
|
||||||
|
bpsOpts = []int{0, 110, 150, 300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 31250,
|
||||||
|
38400, 57600, 115200, 128000, 230400, 250000, 256000, 460800, 921600,
|
||||||
|
1000000, 1500000, 2000000, 3000000}
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
// connection represents the configurable fields of a config.toml
|
||||||
|
connection struct {
|
||||||
|
Host string
|
||||||
|
Port int
|
||||||
|
Streamname string
|
||||||
|
}
|
||||||
|
|
||||||
|
// config maps toml headers
|
||||||
|
config struct {
|
||||||
|
Connection map[string]connection
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
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() {
|
||||||
|
flag.StringVar(&host, "host", "", "vban host")
|
||||||
|
flag.StringVar(&host, "h", "", "vban host (shorthand)")
|
||||||
|
flag.IntVar(&port, "port", 6980, "vban server port")
|
||||||
|
flag.IntVar(&port, "p", 6980, "vban server port (shorthand)")
|
||||||
|
flag.StringVar(&streamname, "streamname", "Command1", "stream name for text requests")
|
||||||
|
flag.StringVar(&streamname, "s", "Command1", "stream name for text requests (shorthand)")
|
||||||
|
flag.IntVar(&bps, "bps", 0, "vban bps")
|
||||||
|
flag.IntVar(&bps, "b", 0, "vban bps (shorthand)")
|
||||||
|
flag.IntVar(&channel, "channel", 0, "vban channel")
|
||||||
|
flag.IntVar(&channel, "c", 0, "vban channel (shorthand)")
|
||||||
|
flag.IntVar(&delay, "delay", 20, "delay between requests")
|
||||||
|
flag.IntVar(&delay, "d", 20, "delay between requests (shorthand)")
|
||||||
|
flag.IntVar(&loglevel, "loglevel", 0, "log level")
|
||||||
|
flag.IntVar(&loglevel, "l", 0, "log level (shorthand)")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
setLogLevel(loglevel)
|
||||||
|
|
||||||
|
c, err := vbanConnect()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
defer c.Close()
|
||||||
|
|
||||||
|
header := newRequestHeader(streamname, indexOf(bpsOpts, bps), channel)
|
||||||
|
for _, arg := range flag.Args() {
|
||||||
|
err := send(c, header, arg)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// vbanConnect establishes a VBAN connection to remote host
|
||||||
|
func vbanConnect() (*net.UDPConn, error) {
|
||||||
|
if host == "" {
|
||||||
|
conn, err := connFromToml()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
host = conn.Host
|
||||||
|
port = conn.Port
|
||||||
|
streamname = conn.Streamname
|
||||||
|
if host == "" {
|
||||||
|
err := errors.New("must provide a host with --host flag or config.toml")
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CONNECT := fmt.Sprintf("%s:%d", host, port)
|
||||||
|
|
||||||
|
s, _ := net.ResolveUDPAddr("udp4", CONNECT)
|
||||||
|
c, err := net.DialUDP("udp4", nil, s)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
log.Info("Connected to ", c.RemoteAddr().String())
|
||||||
|
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// connFromToml parses connection info from config.toml
|
||||||
|
func connFromToml() (*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
|
||||||
|
_, err = toml.DecodeFile(f, &c.Connection)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
conn := c.Connection["connection"]
|
||||||
|
return &conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// send sends a VBAN text request over UDP to remote host
|
||||||
|
func send(c *net.UDPConn, h *requestHeader, msg string) error {
|
||||||
|
log.Debug("Sending '", msg, "' to: ", c.RemoteAddr().String())
|
||||||
|
data := []byte(msg)
|
||||||
|
_, err := c.Write(append(h.header(), data...))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var a uint32
|
||||||
|
_ = binary.Read(bytes.NewReader(h.framecounter[:]), binary.LittleEndian, &a)
|
||||||
|
binary.LittleEndian.PutUint32(h.framecounter[:], a+1)
|
||||||
|
|
||||||
|
time.Sleep(time.Duration(delay) * time.Millisecond)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
50
packet.go
Normal file
50
packet.go
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
var r *requestHeader
|
||||||
|
|
||||||
|
const VBAN_PROTOCOL_TXT = 0x40
|
||||||
|
|
||||||
|
// requestHeader represents a single request header
|
||||||
|
type requestHeader struct {
|
||||||
|
name string
|
||||||
|
bpsIndex int
|
||||||
|
channel int
|
||||||
|
framecounter []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// newRequestHeader returns a pointer to a requestHeader struct as a singleton
|
||||||
|
func newRequestHeader(streamname string, bpsI, channel int) *requestHeader {
|
||||||
|
if r != nil {
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
return &requestHeader{streamname, bpsI, channel, make([]byte, 4)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// sr defines the samplerate for the request
|
||||||
|
func (r *requestHeader) sr() byte {
|
||||||
|
return byte(VBAN_PROTOCOL_TXT + r.bpsIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
// nbc defines the channel of the request
|
||||||
|
func (r *requestHeader) nbc() byte {
|
||||||
|
return byte(r.channel)
|
||||||
|
}
|
||||||
|
|
||||||
|
// streamname defines the stream name of the text request
|
||||||
|
func (r *requestHeader) streamname() []byte {
|
||||||
|
b := make([]byte, 16)
|
||||||
|
copy(b, r.name)
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// header returns a fully formed text request packet header
|
||||||
|
func (t *requestHeader) header() []byte {
|
||||||
|
h := []byte("VBAN")
|
||||||
|
h = append(h, t.sr())
|
||||||
|
h = append(h, byte(0))
|
||||||
|
h = append(h, t.nbc())
|
||||||
|
h = append(h, byte(0x10))
|
||||||
|
h = append(h, t.streamname()...)
|
||||||
|
h = append(h, t.framecounter...)
|
||||||
|
return h
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user