diff --git a/LICENSE b/LICENSE index c760ebf..1d03f1c 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,7 @@ MIT License -Copyright (c) 2019-2021 lebaston100 +Copyright (c) 2019-2022 lebaston100 +Copyright (c) 2022 Onyx-and-Iris Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 1509a2f..05e6460 100644 --- a/README.md +++ b/README.md @@ -1,50 +1,58 @@ # OBS-to-XAir + This is a small script that mutes and unmutes channels on Behringer XAir Mixers depending on the current scene. ## Requirements -- The [obs-websocket plugin](https://github.com/Palakis/obs-websocket/releases) (Version 4.5.1 - 4.9.1) -- A fairly recent version of Python 3 -- [websocket-client](https://github.com/websocket-client/websocket-client) -- [python-osc](https://github.com/attwad/python-osc) +- The [obs-websocket-v5 plugin](https://github.com/obsproject/obs-websocket/releases) (Version 5.0.0+) + - Note, this now comes included with OBS 28+ +- [obsws-python](https://github.com/aatikturk/obsws-python) +- [xair-api](https://github.com/onyx-and-iris/xair-api-python) +- Python 3.10 or greater -## General Setup +## Installation -- Install Python 3.x.x - - On Windows: Make sure you trick "Add Python 3.x to PATH" in the setup -- Make sure you also install pip -- Open up a command line and execute these commands to install the required pip modules: - - pip install python-osc - - pip install websocket-client -- OBS Websocket: - - Download the installer from the link above and run it - - Start OBS, open the "Tools" menu and select "websocket server settings" - - Make sure that "Enable Websocket server" is checked, "Server Port" is 4444 and "Enable authentification" is unchecked +- First install [latest version of Python](https://www.python.org/downloads/). + + - Ensure you tick `Add Python 3.x to PATH` during setup. + +- Download the repository files with git or the green `Code` button. Then in command prompt: + +``` +cd OBS-to-XAir +pip install . +``` ## Configuration -You have to configure your scene-to-channel mapping and the IP settings. Open up the .py file with a text editor. +- Configure websocket settings within `OBS->Tools->obs-websocket Settings` -- The mapping: -This python dict resolves the OBS scene names to the XAir channels. 3 Channels come pre-set as a template. -The format follows the rule "scene name": "mixer channel". -"scene name" is the scene name in OBS which you want to pair to a mixer channel. "mixer channel" is the channel on the XAir mixer. Important: If you use the lower channels 1-9 you have to add zero-padding so "1" becomes "01" and so on. You can find all available channel names besides the normal 01-16 in the "parameters.txt" when you download the latest mixer firmware zip. +- Open the included `config.toml` and set OBS host, port and password as well as the xair mixers ip. -- OBS IP and Port: -In line 9 and 10 you can set the IP and port from the machine that runs OBS. If you are running the script locally you don't need to change anything. + - You may also set the kind of mixer in the script. (`XR12, XR16, XR18, MR18`) -- XAir Mixer IP: -In line 11 you need to set the IP address of your XAir Mixer. The OSC Port can't be changed so it's hardcoded. +- Set the scene to channel mutes mapping in the script. ## Usage -Just run the script, either from the command line or with a double-click. There will be no further output besides "Websocket open" when it's running. If the connection to OBS is broken, you will get an error. +Simply run the script, there will be confirmation of mixer connection and OBS connection if everything is successful. Switch between the defined scenes. + +Closing OBS will stop the script. + +## Further notes + +Since this script relies upon two interfaces, `obsws-python` and `xair-api` this code can be readily modified to interact with any OBS events and set any xair parameters. Check the README files for each interface for further details. ## Compatibility This script was developed and tested with: -- OBS 23.1.0 -- obs-websocket 4.5.1 -- A Behringer XR18 -It should theoretically also work with the XR12, XR16 and X32 but i cannot validate this myself. Feel free to let me know if it worked for you. \ No newline at end of file +- OBS 28.01 +- obs-websocket 5.0.1 +- A Behringer XR18 and Midas MR18 + +## Special Thanks + +- OBS team and the obs-websocket developers + Behringer/Midas for the OSC protocol. +- [Adem](https://github.com/aatikturk) for contributions towards the obsws-python wrapper. +- [Onyx-and-Iris](https://github.com/onyx-and-iris) for contributions towards the obsws-python and xair-api wrappers. diff --git a/__main__.py b/__main__.py new file mode 100644 index 0000000..0580557 --- /dev/null +++ b/__main__.py @@ -0,0 +1,53 @@ +import time + +import obsws_python as obs +import xair_api + +mapping = { + "START": 1, + "BRB": 2, + "END": 3, + "LIVE": 4, +} # set the mapping for the scene to channel mapping here. "scenename": "channel" + + +class Observer: + def __init__(self, mixer): + self._mixer = mixer + self._request = obs.ReqClient() + self._event = obs.EventClient() + self._event.callback.register( + (self.on_current_program_scene_changed, self.on_exit_started) + ) + self.running = True + resp = self._request.get_version() + info = ( + f"Connected to OBS version:{resp.obs_version}", + f"with websocket version:{resp.obs_web_socket_version}", + ) + print(" ".join(info)) + + def on_current_program_scene_changed(self, data): + scene = data.scene_name + print(f"Switched to scene {scene}") + for k, v in mapping.items(): + self._mixer.strip[v - 1].mix.on = k == scene + + def on_exit_started(self, _): + print("OBS closing") + self._event.unsubscribe() + self.running = False + + +def main(): + with xair_api.connect(kind_mixer) as mixer: + o = Observer(mixer) + + while o.running: + time.sleep(0.5) + + +if __name__ == "__main__": + kind_mixer = "XR18" + + main() diff --git a/config.toml b/config.toml new file mode 100644 index 0000000..ae2bbb4 --- /dev/null +++ b/config.toml @@ -0,0 +1,8 @@ +[connection] +# OBS connection info +host = "localhost" +port = 4455 +password = "strongpassword" + +# xair mixer ip +ip = "mixer.local" \ No newline at end of file diff --git a/obs-to-xair.py b/obs-to-xair.py deleted file mode 100644 index a952057..0000000 --- a/obs-to-xair.py +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/env python - -import websocket, json -from time import sleep -from pythonosc import osc_message_builder -from pythonosc import udp_client - -mapping = {"scene1": "01", "scene2": "02", "scene3": "03"} #set the mapping for the scene to channel mapping here. "scenename": "channel" -obsip = "localhost" #set the obs machine ip here. localhost works if you run this script on the same machine. -obsport = "4444" #set the ob websocket port here. 4444 is defult -xairip = "192.168.1.10" #set the xairip here - -def ws1_on_message(ws, message): - jsn = json.loads(message) - if jsn["update-type"] == "SwitchScenes": - for scene in mapping: - if scene == jsn["scene-name"]: - client.send_message("/ch/" + mapping[scene] + "/mix/on", 1) - else: - client.send_message("/ch/" + mapping[scene] + "/mix/on", 0) - elif jsn["update-type"] == "TransitionBegin": - for scene in mapping: - if scene == jsn["to-scene"]: - client.send_message("/ch/" + mapping[scene] + "/mix/on", 1) - elif scene == jsn["from-scene"] and not jsn["name"] == "Cut": - client.send_message("/ch/" + mapping[scene] + "/mix/on", 1) - else: - client.send_message("/ch/" + mapping[scene] + "/mix/on", 0) - -def ws1_on_error(ws, error): - print(error) - -def ws1_on_close(ws): - print("Websocket close") - -def ws1_on_open(ws): - print("Websocket open") - -def ws1_start(): - while True: - ws1.run_forever() - print("Websocket restart") - print("This most likely means that OBS is not open or you lost network connection.") - sleep(1) - -if __name__ == "__main__": - client = udp_client.SimpleUDPClient(xairip, 10024) - ws1 = websocket.WebSocketApp("ws://" + obsip + ":" + obsport, on_message = ws1_on_message, on_error = ws1_on_error, on_close = ws1_on_close) - ws1.on_open = ws1_on_open - ws1_start() diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..c9189d2 --- /dev/null +++ b/setup.py @@ -0,0 +1,9 @@ +from setuptools import setup + +setup( + name="xair-obs", + version="0.0.1", + description="Syncs Xair states to OBS scenes", + install_requires=["obsws-python", "xair-api"], + python_requires=">=3.10", +)