Last active
September 16, 2022 13:31
-
-
Save onyx-and-iris/7da2591a3f4edc8db784c0da5e56e2b3 to your computer and use it in GitHub Desktop.
Send a Voicemeeter text request using VBAN (sendtext)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import socket | |
import time | |
from dataclasses import dataclass | |
from pathlib import Path | |
try: | |
import tomllib | |
except ModuleNotFoundError: | |
import tomli as tomllib | |
HEADER_SIZE = 4 + 1 + 1 + 1 + 1 + 16 + 4 | |
@dataclass | |
class TextRequestHeader: | |
"""Header for a string request packet""" | |
name: str | |
bps_index: int | |
channel: int | |
vban: bytes = "VBAN".encode() | |
nbs: bytes = (0).to_bytes(1, "little") | |
bit: bytes = (0x10).to_bytes(1, "little") | |
framecounter: bytes = (0).to_bytes(4, "little") | |
@property | |
def sr(self): | |
return (0x40 + self.bps_index).to_bytes(1, "little") | |
@property | |
def nbc(self): | |
return (self.channel).to_bytes(1, "little") | |
@property | |
def streamname(self): | |
return self.name.encode() + bytes(16 - len(self.name)) | |
@property | |
def header(self): | |
header = self.vban | |
header += self.sr | |
header += self.nbs | |
header += self.nbc | |
header += self.bit | |
header += self.streamname | |
header += self.framecounter | |
assert len(header) == HEADER_SIZE, f"Header expected {HEADER_SIZE} bytes" | |
return header | |
class SendText: | |
# fmt: off | |
BPS_OPTS = [ | |
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, | |
] | |
# fmt: on | |
def __init__(self, **kwargs): | |
defaultkwargs = { | |
"ip": None, | |
"port": 6980, | |
"streamname": "Command1", | |
"bps": 0, | |
"channel": 0, | |
"delay": 0.02, | |
} | |
kwargs = defaultkwargs | kwargs | |
for attr, val in kwargs.items(): | |
setattr(self, attr, val) | |
# no ip? assume it's in a config.toml file | |
if not self.ip: | |
conn = self._conn_from_toml() | |
for attr, val in conn.items(): | |
setattr(self, attr, val) | |
self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) | |
self.header = TextRequestHeader( | |
name=self.streamname, | |
bps_index=self.BPS_OPTS.index(self.bps), | |
channel=self.channel, | |
) | |
def __enter__(self): | |
return self | |
def _conn_from_toml(self): | |
filepath = Path.cwd() / "config.toml" | |
with open(filepath, "rb") as f: | |
conn = tomllib.load(f) | |
return conn["connection"] | |
def send(self, cmd): | |
"""Sends a Voicemeeter string request over a network""" | |
self.sock.sendto( | |
self.header.header + cmd.encode(), | |
(socket.gethostbyname(self.ip), self.port), | |
) | |
count = int.from_bytes(self.header.framecounter, "little") + 1 | |
self.header.framecounter = count.to_bytes(4, "little") | |
time.sleep(self.delay) | |
def __exit__(self, exc_t, exc_v, exc_tr): | |
self.sock.close() | |
if __name__ == "__main__": | |
cmd_off = "Strip[0].mute=0;Strip[1].mute=0;Strip[2].mute=0" | |
cmd_on = "Strip[0].mute=1;Strip[1].mute=1;Strip[2].mute=1" | |
with SendText() as vban: | |
for _ in range(30): | |
vban.send(cmd_off) | |
vban.send(cmd_on) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
You can set ip, port, streamname etc as keyword arguments, or load them from a config.toml file. This example assumes existence of config file.
If you prefer to code more complex interactions or get and store parameter values check:
https://github.com/onyx-and-iris/vban-cmd-python
example config.toml:
config.toml
should be placed next to your__main__.py