mirror of
				https://github.com/onyx-and-iris/vbantxt.git
				synced 2025-10-30 21:11:50 +00:00 
			
		
		
		
	reorganise some of the internals of the package.
functional options added.
This commit is contained in:
		
							parent
							
								
									ee781ea586
								
							
						
					
					
						commit
						6c53cfa383
					
				
							
								
								
									
										48
									
								
								client.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								client.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,48 @@ | ||||
| package vbantxt | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"net" | ||||
| 
 | ||||
| 	log "github.com/sirupsen/logrus" | ||||
| ) | ||||
| 
 | ||||
| // client represents the UDP client | ||||
| type client struct { | ||||
| 	conn *net.UDPConn | ||||
| } | ||||
| 
 | ||||
| // NewClient returns a UDP client | ||||
| func newClient(host string, port int) (client, error) { | ||||
| 	udpAddr, err := net.ResolveUDPAddr("udp4", fmt.Sprintf("%s:%d", host, port)) | ||||
| 	if err != nil { | ||||
| 		return client{}, err | ||||
| 	} | ||||
| 	conn, err := net.DialUDP("udp4", nil, udpAddr) | ||||
| 	if err != nil { | ||||
| 		return client{}, err | ||||
| 	} | ||||
| 	log.Infof("Outgoing address %s", conn.RemoteAddr()) | ||||
| 
 | ||||
| 	return client{conn: conn}, nil | ||||
| } | ||||
| 
 | ||||
| // Write implements the io.WriteCloser interface | ||||
| func (c client) Write(buf []byte) (int, error) { | ||||
| 	n, err := c.conn.Write(buf) | ||||
| 	if err != nil { | ||||
| 		return 0, err | ||||
| 	} | ||||
| 	log.Debugf("Sending '%s' to: %s", string(buf), c.conn.RemoteAddr()) | ||||
| 
 | ||||
| 	return n, nil | ||||
| } | ||||
| 
 | ||||
| // Close implements the io.WriteCloser interface | ||||
| func (c client) Close() error { | ||||
| 	err := c.conn.Close() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										9
									
								
								errors.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								errors.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,9 @@ | ||||
| package vbantxt | ||||
| 
 | ||||
| // Error is used to define sentinel errors. | ||||
| type Error string | ||||
| 
 | ||||
| // Error implements the error interface. | ||||
| func (r Error) Error() string { | ||||
| 	return string(r) | ||||
| } | ||||
							
								
								
									
										35
									
								
								option.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								option.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,35 @@ | ||||
| package vbantxt | ||||
| 
 | ||||
| import ( | ||||
| 	"time" | ||||
| 
 | ||||
| 	log "github.com/sirupsen/logrus" | ||||
| ) | ||||
| 
 | ||||
| // Option is a functional option type that allows us to configure the VbanTxt. | ||||
| type Option func(*VbanTxt) | ||||
| 
 | ||||
| // WithRateLimit is a functional option to set the ratelimit for requests | ||||
| func WithRateLimit(ratelimit time.Duration) Option { | ||||
| 	return func(vt *VbanTxt) { | ||||
| 		vt.ratelimit = ratelimit | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // WithBPSOpt is a functional option to set the bps index for {VbanTx}.{Packet}.bpsIndex | ||||
| func WithBPSOpt(bpsIndex int) Option { | ||||
| 	return func(vt *VbanTxt) { | ||||
| 		if bpsIndex < 0 || bpsIndex >= len(BpsOpts) { | ||||
| 			log.Warnf("invalid bpsIndex %d, defaulting to 0", bpsIndex) | ||||
| 			return | ||||
| 		} | ||||
| 		vt.packet.bpsIndex = bpsIndex | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // WithChannel is a functional option to set the bps index for {VbanTx}.{Packet}.channel | ||||
| func WithChannel(channel int) Option { | ||||
| 	return func(vt *VbanTxt) { | ||||
| 		vt.packet.channel = channel | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										70
									
								
								packet.go
									
									
									
									
									
								
							
							
						
						
									
										70
									
								
								packet.go
									
									
									
									
									
								
							| @ -1,50 +1,72 @@ | ||||
| package main | ||||
| package vbantxt | ||||
| 
 | ||||
| var r *requestHeader | ||||
| import ( | ||||
| 	"encoding/binary" | ||||
| 
 | ||||
| const VBAN_PROTOCOL_TXT = 0x40 | ||||
| 	log "github.com/sirupsen/logrus" | ||||
| ) | ||||
| 
 | ||||
| // requestHeader represents a single request header | ||||
| type requestHeader struct { | ||||
| const ( | ||||
| 	vbanProtocolTxt = 0x40 | ||||
| 	streamNameSz    = 16 | ||||
| 	headerSz        = 4 + 1 + 1 + 1 + 1 + 16 + 4 | ||||
| ) | ||||
| 
 | ||||
| var 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 packet 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 | ||||
| // newPacket returns a packet struct with default values, framecounter at 0. | ||||
| func newPacket(streamname string) packet { | ||||
| 	return packet{ | ||||
| 		name:         streamname, | ||||
| 		bpsIndex:     0, | ||||
| 		channel:      0, | ||||
| 		framecounter: make([]byte, 4), | ||||
| 	} | ||||
| 	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) | ||||
| func (p *packet) sr() byte { | ||||
| 	return byte(vbanProtocolTxt + p.bpsIndex) | ||||
| } | ||||
| 
 | ||||
| // nbc defines the channel of the request | ||||
| func (r *requestHeader) nbc() byte { | ||||
| 	return byte(r.channel) | ||||
| func (p *packet) nbc() byte { | ||||
| 	return byte(p.channel) | ||||
| } | ||||
| 
 | ||||
| // streamname defines the stream name of the text request | ||||
| func (r *requestHeader) streamname() []byte { | ||||
| 	b := make([]byte, 16) | ||||
| 	copy(b, r.name) | ||||
| func (p *packet) streamname() []byte { | ||||
| 	b := make([]byte, streamNameSz) | ||||
| 	copy(b, p.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()) | ||||
| // header returns a fully formed packet header | ||||
| func (p *packet) header() []byte { | ||||
| 	h := make([]byte, 0, headerSz) | ||||
| 	h = append(h, []byte("VBAN")...) | ||||
| 	h = append(h, p.sr()) | ||||
| 	h = append(h, byte(0)) | ||||
| 	h = append(h, t.nbc()) | ||||
| 	h = append(h, p.nbc()) | ||||
| 	h = append(h, byte(0x10)) | ||||
| 	h = append(h, t.streamname()...) | ||||
| 	h = append(h, t.framecounter...) | ||||
| 	h = append(h, p.streamname()...) | ||||
| 	h = append(h, p.framecounter...) | ||||
| 	return h | ||||
| } | ||||
| 
 | ||||
| // bumpFrameCounter increments the frame counter by 1 | ||||
| func (p *packet) bumpFrameCounter() { | ||||
| 	x := binary.LittleEndian.Uint32(p.framecounter) | ||||
| 	binary.LittleEndian.PutUint32(p.framecounter, x+1) | ||||
| 
 | ||||
| 	log.Tracef("framecounter: %d", x) | ||||
| } | ||||
|  | ||||
							
								
								
									
										59
									
								
								vbantxt.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								vbantxt.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,59 @@ | ||||
| package vbantxt | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
| // VbanTxt is used to send VBAN-TXT requests to a distant Voicemeeter/Matrix. | ||||
| type VbanTxt struct { | ||||
| 	client    io.WriteCloser | ||||
| 	packet    packet | ||||
| 	ratelimit time.Duration | ||||
| } | ||||
| 
 | ||||
| // 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. | ||||
| func New(host string, port int, streamname string, options ...Option) (*VbanTxt, error) { | ||||
| 	client, err := newClient(host, port) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("error creating UDP client for (%s:%d): %w", host, port, err) | ||||
| 	} | ||||
| 
 | ||||
| 	vt := &VbanTxt{ | ||||
| 		client:    client, | ||||
| 		packet:    newPacket(streamname), | ||||
| 		ratelimit: time.Duration(20) * time.Millisecond, | ||||
| 	} | ||||
| 
 | ||||
| 	for _, o := range options { | ||||
| 		o(vt) | ||||
| 	} | ||||
| 
 | ||||
| 	return vt, nil | ||||
| } | ||||
| 
 | ||||
| // Send is resonsible for firing each VBAN-TXT request. | ||||
| // It waits for {vt.ratelimit} time before returning. | ||||
| func (vt VbanTxt) Send(cmd string) error { | ||||
| 	_, err := vt.client.Write(append(vt.packet.header(), []byte(cmd)...)) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("error sending command (%s): %w", cmd, err) | ||||
| 	} | ||||
| 
 | ||||
| 	vt.packet.bumpFrameCounter() | ||||
| 
 | ||||
| 	time.Sleep(vt.ratelimit) | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // Close is responsible for closing the UDP Client connection | ||||
| func (vt VbanTxt) Close() error { | ||||
| 	err := vt.client.Close() | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("error attempting to close UDP Client: %w", err) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user