mirror of
https://github.com/onyx-and-iris/vbantxt.git
synced 2026-04-07 17:53:30 +00:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e36af2c059 | |||
| 5a5a6fa893 | |||
| be11239d39 | |||
| d72c6a2d17 | |||
| c063feb919 | |||
| ae170ca572 | |||
| 7a844e3624 |
@@ -11,12 +11,19 @@ Before any major/minor/patch bump all unit tests will be run to verify they pass
|
|||||||
|
|
||||||
- [x]
|
- [x]
|
||||||
|
|
||||||
|
# [0.2.1] - 2024-11-07
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- {packet}.header() now uses a reusable buffer.
|
||||||
|
|
||||||
# [0.2.0] - 2024-10-27
|
# [0.2.0] - 2024-10-27
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- `config` flag (shorthand `C`), you may now specify a custom config directory. It defaults to `home directory / .config / vbantxt_cli /`.
|
- `config` flag (shorthand `C`), you may now specify a custom config directory. It defaults to `home directory / .config / vbantxt_cli /`.
|
||||||
- please note, the default directory has changed from v0.1.0
|
- please note, the default directory has changed from v0.1.0
|
||||||
|
- Functional options `WithRateLimit` and `WithBPSOpt` and `WithChannel` added. Use them to configure the vbantxt client. See the [included vbantxt cli][vbantxt-cli] for an example of usage.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
@@ -41,3 +48,5 @@ Before any major/minor/patch bump all unit tests will be run to verify they pass
|
|||||||
|
|
||||||
- Initial release, package implements VBAN PROTOCOL TXT with a basic CLI for configuring options.
|
- Initial release, package implements VBAN PROTOCOL TXT with a basic CLI for configuring options.
|
||||||
- Ability to load configuration settings from a config.toml.
|
- Ability to load configuration settings from a config.toml.
|
||||||
|
|
||||||
|
[vbantxt-cli]: https://github.com/onyx-and-iris/vbantxt/blob/main/cmd/vbantxt/main.go
|
||||||
|
|||||||
20
README.md
20
README.md
@@ -41,13 +41,13 @@ func main() {
|
|||||||
streamname string = "onyx"
|
streamname string = "onyx"
|
||||||
)
|
)
|
||||||
|
|
||||||
vbantxtClient, err := vbantxt.New(host, port, streamname)
|
client, err := vbantxt.New(host, port, streamname)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
defer vbantxtClient.Close()
|
defer client.Close()
|
||||||
|
|
||||||
err = vbantxtClient.Send("strip[0].mute=0")
|
err = client.Send("strip[0].mute=0")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_, _ = fmt.Fprintf(os.Stderr, "Error: %s", err)
|
_, _ = fmt.Fprintf(os.Stderr, "Error: %s", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
@@ -60,18 +60,18 @@ func main() {
|
|||||||
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 -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 / .config / vbantxt_cli /`
|
You may also store them in a `config.toml` located in `home directory / .config / vbantxt /`
|
||||||
|
|
||||||
A valid `config.toml` might look like this:
|
A valid `config.toml` might look like this:
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[connection]
|
[connection]
|
||||||
Host="gamepc.local"
|
host="gamepc.local"
|
||||||
Port=6980
|
port=6980
|
||||||
Streamname="Command1"
|
streamname="Command1"
|
||||||
```
|
```
|
||||||
|
|
||||||
- `host` defaults to "localhost"
|
- `host` defaults to "localhost"
|
||||||
@@ -88,7 +88,7 @@ The vbantxt-cli utility accepts a single string request or an array of string re
|
|||||||
|
|
||||||
For example, in Windows with Powershell you could:
|
For example, in Windows with Powershell you could:
|
||||||
|
|
||||||
`vbantxt-cli $(Get-Content .\script.txt)`
|
`vbantxt $(Get-Content .\script.txt)`
|
||||||
|
|
||||||
Or with Bash:
|
Or with Bash:
|
||||||
|
|
||||||
@@ -109,7 +109,7 @@ bus[3].eq.On=0
|
|||||||
Sending commands to VB-Audio Matrix is also possible, for example:
|
Sending commands to VB-Audio Matrix is also possible, for example:
|
||||||
|
|
||||||
```
|
```
|
||||||
vbantxt-cli -s=streamname "Point(ASIO128.IN[2],ASIO128.OUT[1]).dBGain = -8"
|
vbantxt -s=streamname "Point(ASIO128.IN[2],ASIO128.OUT[1]).dBGain = -8"
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -82,12 +82,12 @@ func main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func createClient(host string, port int, streamname string, bps int, channel, ratelimit int) (*vbantxt.VbanTxt, error) {
|
func createClient(host string, port int, streamname string, bps, channel, ratelimit int) (*vbantxt.VbanTxt, error) {
|
||||||
client, err := vbantxt.New(
|
client, err := vbantxt.New(
|
||||||
host,
|
host,
|
||||||
port,
|
port,
|
||||||
streamname,
|
streamname,
|
||||||
vbantxt.WithBPSOpt(indexOf(vbantxt.BpsOpts, bps)),
|
vbantxt.WithBPSOpt(bps),
|
||||||
vbantxt.WithChannel(channel),
|
vbantxt.WithChannel(channel),
|
||||||
vbantxt.WithRateLimit(time.Duration(ratelimit)*time.Millisecond))
|
vbantxt.WithRateLimit(time.Duration(ratelimit)*time.Millisecond))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -15,12 +15,3 @@ func flagsPassed(flags []string) bool {
|
|||||||
})
|
})
|
||||||
return found
|
return found
|
||||||
}
|
}
|
||||||
|
|
||||||
func indexOf[T comparable](collection []T, e T) int {
|
|
||||||
for i, x := range collection {
|
|
||||||
if x == e {
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|||||||
11
option.go
11
option.go
@@ -16,18 +16,19 @@ func WithRateLimit(ratelimit time.Duration) Option {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithBPSOpt is a functional option to set the bps index for {VbanTx}.{Packet}.bpsIndex
|
// WithBPSOpt is a functional option to set the bps index for {VbanTxt}.packet
|
||||||
func WithBPSOpt(bpsIndex int) Option {
|
func WithBPSOpt(bps int) Option {
|
||||||
return func(vt *VbanTxt) {
|
return func(vt *VbanTxt) {
|
||||||
if bpsIndex < 0 || bpsIndex >= len(BpsOpts) {
|
bpsIndex := indexOf(BpsOpts, bps)
|
||||||
log.Warnf("invalid bpsIndex %d, defaulting to 0", bpsIndex)
|
if bpsIndex == -1 {
|
||||||
|
log.Warnf("invalid bps value %d, expected one of %v, defaulting to 0", bps, BpsOpts)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
vt.packet.bpsIndex = bpsIndex
|
vt.packet.bpsIndex = bpsIndex
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithChannel is a functional option to set the bps index for {VbanTx}.{Packet}.channel
|
// WithChannel is a functional option to set the channel for {VbanTxt}.packet
|
||||||
func WithChannel(channel int) Option {
|
func WithChannel(channel int) Option {
|
||||||
return func(vt *VbanTxt) {
|
return func(vt *VbanTxt) {
|
||||||
vt.packet.channel = channel
|
vt.packet.channel = channel
|
||||||
|
|||||||
35
packet.go
35
packet.go
@@ -1,6 +1,7 @@
|
|||||||
package vbantxt
|
package vbantxt
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
@@ -17,19 +18,24 @@ var BpsOpts = []int{0, 110, 150, 300, 600, 1200, 2400, 4800, 9600, 14400, 19200,
|
|||||||
1000000, 1500000, 2000000, 3000000}
|
1000000, 1500000, 2000000, 3000000}
|
||||||
|
|
||||||
type packet struct {
|
type packet struct {
|
||||||
name string
|
streamname []byte
|
||||||
bpsIndex int
|
bpsIndex int
|
||||||
channel int
|
channel int
|
||||||
framecounter []byte
|
framecounter []byte
|
||||||
|
hbuf *bytes.Buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
// newPacket returns a packet struct with default values, framecounter at 0.
|
// newPacket returns a packet struct with default values, framecounter at 0.
|
||||||
func newPacket(streamname string) packet {
|
func newPacket(streamname string) packet {
|
||||||
|
streamnameBuf := make([]byte, streamNameSz)
|
||||||
|
copy(streamnameBuf, streamname)
|
||||||
|
|
||||||
return packet{
|
return packet{
|
||||||
name: streamname,
|
streamname: streamnameBuf,
|
||||||
bpsIndex: 0,
|
bpsIndex: 0,
|
||||||
channel: 0,
|
channel: 0,
|
||||||
framecounter: make([]byte, 4),
|
framecounter: make([]byte, 4),
|
||||||
|
hbuf: bytes.NewBuffer(make([]byte, headerSz)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,24 +49,17 @@ func (p *packet) nbc() byte {
|
|||||||
return byte(p.channel)
|
return byte(p.channel)
|
||||||
}
|
}
|
||||||
|
|
||||||
// streamname defines the stream name of the text request
|
|
||||||
func (p *packet) streamname() []byte {
|
|
||||||
b := make([]byte, streamNameSz)
|
|
||||||
copy(b, p.name)
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
// header returns a fully formed packet header
|
// header returns a fully formed packet header
|
||||||
func (p *packet) header() []byte {
|
func (p *packet) header() []byte {
|
||||||
h := make([]byte, 0, headerSz)
|
p.hbuf.Reset()
|
||||||
h = append(h, []byte("VBAN")...)
|
p.hbuf.WriteString("VBAN")
|
||||||
h = append(h, p.sr())
|
p.hbuf.WriteByte(p.sr())
|
||||||
h = append(h, byte(0))
|
p.hbuf.WriteByte(byte(0))
|
||||||
h = append(h, p.nbc())
|
p.hbuf.WriteByte(p.nbc())
|
||||||
h = append(h, byte(0x10))
|
p.hbuf.WriteByte(byte(0x10))
|
||||||
h = append(h, p.streamname()...)
|
p.hbuf.Write(p.streamname)
|
||||||
h = append(h, p.framecounter...)
|
p.hbuf.Write(p.framecounter)
|
||||||
return h
|
return p.hbuf.Bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
// bumpFrameCounter increments the frame counter by 1
|
// bumpFrameCounter increments the frame counter by 1
|
||||||
|
|||||||
@@ -7,40 +7,40 @@ import (
|
|||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
// client represents the UDP client
|
// udpConn represents the UDP client
|
||||||
type client struct {
|
type udpConn struct {
|
||||||
conn *net.UDPConn
|
conn *net.UDPConn
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewClient returns a UDP client
|
// newUDPConn returns a UDP client
|
||||||
func newClient(host string, port int) (client, error) {
|
func newUDPConn(host string, port int) (udpConn, error) {
|
||||||
udpAddr, err := net.ResolveUDPAddr("udp4", fmt.Sprintf("%s:%d", host, port))
|
udpAddr, err := net.ResolveUDPAddr("udp4", fmt.Sprintf("%s:%d", host, port))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return client{}, err
|
return udpConn{}, err
|
||||||
}
|
}
|
||||||
conn, err := net.DialUDP("udp4", nil, udpAddr)
|
conn, err := net.DialUDP("udp4", nil, udpAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return client{}, err
|
return udpConn{}, err
|
||||||
}
|
}
|
||||||
log.Infof("Outgoing address %s", conn.RemoteAddr())
|
log.Infof("Outgoing address %s", conn.RemoteAddr())
|
||||||
|
|
||||||
return client{conn: conn}, nil
|
return udpConn{conn: conn}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write implements the io.WriteCloser interface
|
// Write implements the io.WriteCloser interface
|
||||||
func (c client) Write(buf []byte) (int, error) {
|
func (u udpConn) Write(buf []byte) (int, error) {
|
||||||
n, err := c.conn.Write(buf)
|
n, err := u.conn.Write(buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
log.Debugf("Sending '%s' to: %s", string(buf), c.conn.RemoteAddr())
|
log.Debugf("Sending '%s' to: %s", string(buf), u.conn.RemoteAddr())
|
||||||
|
|
||||||
return n, nil
|
return n, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close implements the io.WriteCloser interface
|
// Close implements the io.WriteCloser interface
|
||||||
func (c client) Close() error {
|
func (u udpConn) Close() error {
|
||||||
err := c.conn.Close()
|
err := u.conn.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
10
util.go
Normal file
10
util.go
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
package vbantxt
|
||||||
|
|
||||||
|
func indexOf[T comparable](collection []T, e T) int {
|
||||||
|
for i, x := range collection {
|
||||||
|
if x == e {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
10
vbantxt.go
10
vbantxt.go
@@ -8,7 +8,7 @@ import (
|
|||||||
|
|
||||||
// VbanTxt is used to send VBAN-TXT requests to a distant Voicemeeter/Matrix.
|
// VbanTxt is used to send VBAN-TXT requests to a distant Voicemeeter/Matrix.
|
||||||
type VbanTxt struct {
|
type VbanTxt struct {
|
||||||
client io.WriteCloser
|
udpConn io.WriteCloser
|
||||||
packet packet
|
packet packet
|
||||||
ratelimit time.Duration
|
ratelimit time.Duration
|
||||||
}
|
}
|
||||||
@@ -16,13 +16,13 @@ type VbanTxt struct {
|
|||||||
// New constructs a fully formed VbanTxt instance. This is the package's entry point.
|
// New constructs a fully formed VbanTxt instance. This is the package's entry point.
|
||||||
// It sets default values for it's fields and then runs the option functions.
|
// It sets default values for it's fields and then runs the option functions.
|
||||||
func New(host string, port int, streamname string, options ...Option) (*VbanTxt, error) {
|
func New(host string, port int, streamname string, options ...Option) (*VbanTxt, error) {
|
||||||
client, err := newClient(host, port)
|
udpConn, err := newUDPConn(host, port)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error creating UDP client for (%s:%d): %w", host, port, err)
|
return nil, fmt.Errorf("error creating UDP client for (%s:%d): %w", host, port, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
vt := &VbanTxt{
|
vt := &VbanTxt{
|
||||||
client: client,
|
udpConn: udpConn,
|
||||||
packet: newPacket(streamname),
|
packet: newPacket(streamname),
|
||||||
ratelimit: time.Duration(20) * time.Millisecond,
|
ratelimit: time.Duration(20) * time.Millisecond,
|
||||||
}
|
}
|
||||||
@@ -37,7 +37,7 @@ func New(host string, port int, streamname string, options ...Option) (*VbanTxt,
|
|||||||
// Send is resonsible for firing each VBAN-TXT request.
|
// Send is resonsible for firing each VBAN-TXT request.
|
||||||
// It waits for {vt.ratelimit} time before returning.
|
// It waits for {vt.ratelimit} time before returning.
|
||||||
func (vt VbanTxt) Send(cmd string) error {
|
func (vt VbanTxt) Send(cmd string) error {
|
||||||
_, err := vt.client.Write(append(vt.packet.header(), []byte(cmd)...))
|
_, err := vt.udpConn.Write(append(vt.packet.header(), []byte(cmd)...))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error sending command (%s): %w", cmd, err)
|
return fmt.Errorf("error sending command (%s): %w", cmd, err)
|
||||||
}
|
}
|
||||||
@@ -51,7 +51,7 @@ func (vt VbanTxt) Send(cmd string) error {
|
|||||||
|
|
||||||
// Close is responsible for closing the UDP Client connection
|
// Close is responsible for closing the UDP Client connection
|
||||||
func (vt VbanTxt) Close() error {
|
func (vt VbanTxt) Close() error {
|
||||||
err := vt.client.Close()
|
err := vt.udpConn.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error attempting to close UDP Client: %w", err)
|
return fmt.Errorf("error attempting to close UDP Client: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user