Skip to content

Instantly share code, notes, and snippets.

@lebaston100
Last active April 3, 2024 16:09
Show Gist options
  • Save lebaston100/8c1963d456a425a64344f525439fb42e to your computer and use it in GitHub Desktop.
Save lebaston100/8c1963d456a425a64344f525439fb42e to your computer and use it in GitHub Desktop.
Behringer X Air 18

This example flow allows control over a Behringer XAir18 (others of the same series might work with small modifications but are not tested) thanks to the OSC protocol that the mixer uses for it's control interface. The same interface is also used by the phone app and the desktop software.

This flow depends on only a single external npm package which is "node-red-contrib-osc". Install it with "npm install node-red-contrib-osc" and restart node-red if you haven't already done that. For more information visit it's page here: https://flows.nodered.org/node/node-red-contrib-osc

The implemented functions are just a very small subset of all the available mixer functions and are meant as an example for how to use Node-Red to control the mixer. If you want to add more features read the last paragraph from the readme.

There are API's written with link nodes so you can put this into a seperate flow and call it from everywhere inside node-red. Just to give you some ideas that you can use this for:

  • Midi translation from hardware controllers via node-red-contrib-midi/node-red-contrib-midi-windows
  • Audio automation for live streaming based on data coming over websocket from OBS-Studio
  • Audio automation based on incoming mqtt data from smarthome wall switches or sensors

There are a lot of comment nodes in this flow to make most of the flow and code as understandable as possible. Most of them will have more information inside them when you double click on them. For the API endpoints what data the endpoint requires is listed in there.

If you have any problems or questions, join my dedicated Discord Server

[{"id":"4fb50fa4.554aa","type":"tab","label":"Node-Red XAIR18","disabled":false,"info":""},{"id":"c31afd89.4fd67","type":"osc","z":"4fb50fa4.554aa","name":"OSC Encoder","path":"","metadata":false,"x":420,"y":1960,"wires":[[]]},{"id":"74fc7ff1.329af","type":"inject","z":"4fb50fa4.554aa","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"/ch/01/mix/on","payload":"1","payloadType":"num","x":140,"y":1960,"wires":[["c31afd89.4fd67"]]},{"id":"fe328093.aa7f8","type":"udp out","z":"4fb50fa4.554aa","name":"","addr":"10.10.12.30","iface":"","port":"10024","ipv":"udp4","outport":"11220","base64":false,"multicast":"false","x":610,"y":1060,"wires":[]},{"id":"b969f667.952eb8","type":"inject","z":"4fb50fa4.554aa","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"/ch/01/mix/on","payload":"0","payloadType":"num","x":140,"y":2000,"wires":[["c31afd89.4fd67"]]},{"id":"d4166326.c6564","type":"inject","z":"4fb50fa4.554aa","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"/ch/01/mix/fader","payload":"0.0","payloadType":"num","x":150,"y":2040,"wires":[["c31afd89.4fd67"]]},{"id":"dc092763.110028","type":"inject","z":"4fb50fa4.554aa","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"/ch/01/mix/fader","payload":"1.0","payloadType":"num","x":150,"y":2080,"wires":[["c31afd89.4fd67"]]},{"id":"a6c9c95e.064878","type":"link in","z":"4fb50fa4.554aa","name":"XAir mute/unmute channel [msg.channel, msg.mute]","links":["863370b9.3b28b"],"x":315,"y":240,"wires":[["8f0cb4d5.b5ddb8"]]},{"id":"43677a74.0bea84","type":"comment","z":"4fb50fa4.554aa","name":"mute/unmute channel","info":"This mutes or unmutes a channel.\n\nExpects the channel to be muted in msg.channel as either a Number or a String.\nThe channel will be automatically padded if it's below 10 (5 -> 05).\n\nExpects the action (mute/unmute) in msg.mute as a Number:\n1 = Channel on (Unmuted)\n0 = Channel off (Muted)","x":140,"y":240,"wires":[]},{"id":"8f0cb4d5.b5ddb8","type":"change","z":"4fb50fa4.554aa","name":"set OSC options","rules":[{"t":"set","p":"topic","pt":"msg","to":"\"/ch/\" & $formatNumber($number(msg.channel), \"00\") & \"/mix/on\"","tot":"jsonata"},{"t":"set","p":"payload","pt":"msg","to":"$number(msg.mute)","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":440,"y":240,"wires":[["c64d8e14.85ffb"]]},{"id":"c64d8e14.85ffb","type":"osc","z":"4fb50fa4.554aa","name":"OSC Encoder","path":"","metadata":false,"x":860,"y":200,"wires":[["4eb5a810.9ab708"]]},{"id":"4eb5a810.9ab708","type":"udp out","z":"4fb50fa4.554aa","name":"Send OSC via UDP","addr":"10.10.12.30","iface":"","port":"10024","ipv":"udp4","outport":"","base64":false,"multicast":"false","x":1050,"y":200,"wires":[]},{"id":"c8e2da80.6c8f08","type":"link in","z":"4fb50fa4.554aa","name":"XAir set channel volume [msg.channel, msg.volume]","links":["1b4e040f.367fac"],"x":315,"y":320,"wires":[["7f1e81f3.14424"]]},{"id":"7f1e81f3.14424","type":"change","z":"4fb50fa4.554aa","name":"set OSC options","rules":[{"t":"set","p":"topic","pt":"msg","to":"\"/ch/\" & $formatNumber($number(msg.channel), \"00\") & \"/mix/fader\"","tot":"jsonata"},{"t":"set","p":"payload","pt":"msg","to":"volume","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":440,"y":320,"wires":[["c64d8e14.85ffb"]]},{"id":"c52b7ce5.e9cd6","type":"comment","z":"4fb50fa4.554aa","name":"set channel volume","info":"This set's the volume for a given channel.\n\nExpects the channel to be adjusted in msg.channel as either a Number or a String.\nThe channel will be automatically padded if it's below 10 (5 -> 05).\n\nExpects the target volume in msg.volume as a number(float).\n0.0 = -∞ db\n1.0 = +10 db\n\nZero db is around 0.7497556209564209","x":130,"y":320,"wires":[]},{"id":"f7c7cc9b.ed117","type":"link in","z":"4fb50fa4.554aa","name":"XAir send raw MSG","links":["9a559c79.1dcf5"],"x":315,"y":200,"wires":[["c64d8e14.85ffb"]]},{"id":"b48a0d0d.18be6","type":"comment","z":"4fb50fa4.554aa","name":"raw message","info":"This api endpoint can be used to send raw osc messages to the mixer\nThe osc node expects the osc path to be in the msg.topic and the osc data in the mgs.payload","x":110,"y":200,"wires":[]},{"id":"4dafee1e.766c1","type":"link in","z":"4fb50fa4.554aa","name":"XAir fade in channel [msg.channel,msg.time,msg.endVolume]","links":["10ca0b07.fb3ab5"],"x":315,"y":520,"wires":[["f8c7a497.45ef08"]]},{"id":"d90d2c40.785a7","type":"comment","z":"4fb50fa4.554aa","name":"fade in channel","info":"This fades in (from 0.0 volume to your specified volume) a channel.\nThis works by asnc sending multiple messages from node-red in the specific time.\n\nExpects the channel to be faded in msg.channel as either a Number or a String.\nThe channel will be automatically padded if it's below 10 (5 -> 05).\n\nExpects the time for the fade in msg.time as a Number in seconds.\n\nExpects the volume to land on in msg.endVolume as a Number (from 0.0-1.0 like the set channel volume function)","x":120,"y":520,"wires":[]},{"id":"f74a9ec7.d8e35","type":"link in","z":"4fb50fa4.554aa","name":"XAir set DCA volume [msg.channel, msg.volume]","links":["67f7f1f5.ada"],"x":315,"y":360,"wires":[["a801350c.e51fe8"]]},{"id":"a801350c.e51fe8","type":"change","z":"4fb50fa4.554aa","name":"set OSC options","rules":[{"t":"set","p":"topic","pt":"msg","to":"\"/dca/\" & channel & \"/fader\"","tot":"jsonata"},{"t":"set","p":"payload","pt":"msg","to":"volume","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":440,"y":360,"wires":[["c64d8e14.85ffb"]]},{"id":"8a07b5ab.0e5c18","type":"comment","z":"4fb50fa4.554aa","name":"set DCA volume","info":"This set's the volume for a given dca.\n\nExpects the channel to be adjusted in msg.channel as either a Number or a String.\nThe channel will be automatically padded if it's below 10 (5 -> 05).\n\nExpects the target volume in msg.volume as a number(float).\n0.0 = -∞ db\n1.0 = +10 db\n\nZero db is around 0.7497556209564209","x":120,"y":360,"wires":[]},{"id":"fd4927be.a22348","type":"comment","z":"4fb50fa4.554aa","name":"fade in DCA","info":"This fades in (from 0.0 volume to your specified volume) a dca.\nThis works by asnc sending multiple messages from node-red in the specific time.\n\nExpects the channel to be faded in msg.channel as either a Number or a String.\n\nExpects the time for the fade in msg.time as a Number in seconds.\n\nExpects the volume to land on in msg.endVolume as a Number (from 0.0-1.0 like the set channel volume function)","x":110,"y":560,"wires":[]},{"id":"d41095b2.d0b3d8","type":"link in","z":"4fb50fa4.554aa","name":"XAir fade in DCA [msg.channel,msg.time,msg.endVolume]","links":["47d9ae41.ea686"],"x":315,"y":560,"wires":[["a724d185.a19b4"]]},{"id":"7419fe06.24eb6","type":"udp in","z":"4fb50fa4.554aa","name":"XAIR18 Data Receiver","iface":"","port":"11220","ipv":"udp4","multicast":"false","group":"","datatype":"buffer","x":140,"y":920,"wires":[["450cf49.af98f0c"]]},{"id":"450cf49.af98f0c","type":"osc","z":"4fb50fa4.554aa","name":"OSC Decoder","path":"","metadata":false,"x":380,"y":920,"wires":[["d93bcfc.a7d763","1fd96a59.4ee4d6"]]},{"id":"d93bcfc.a7d763","type":"debug","z":"4fb50fa4.554aa","name":"debug out of all XAIR18 incoming messages","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":670,"y":920,"wires":[]},{"id":"ef2115cd.d5a418","type":"inject","z":"4fb50fa4.554aa","name":"Trigger","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payloadType":"date","x":370,"y":1580,"wires":[["893578d7.860b88"]]},{"id":"893578d7.860b88","type":"change","z":"4fb50fa4.554aa","name":"","rules":[{"t":"set","p":"time","pt":"msg","to":"5","tot":"num"},{"t":"set","p":"channel","pt":"msg","to":"1","tot":"str"},{"t":"set","p":"endVolume","pt":"msg","to":"0.75","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":540,"y":1580,"wires":[["10ca0b07.fb3ab5"]]},{"id":"a8ebff9e.56575","type":"osc","z":"4fb50fa4.554aa","name":"OSC Encoder","path":"","metadata":false,"x":380,"y":1060,"wires":[["fe328093.aa7f8"]]},{"id":"79a08ab1.8299e4","type":"inject","z":"4fb50fa4.554aa","name":"Update Sub Request","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"9","crontab":"","once":true,"onceDelay":"1","topic":"/xremote","payloadType":"date","x":160,"y":1060,"wires":[["a8ebff9e.56575"]]},{"id":"2591d5ea.8fff1a","type":"comment","z":"4fb50fa4.554aa","name":"simple osc test messages","info":"This is the most simple form of testing if a command will do that you want it to do.","x":150,"y":1920,"wires":[]},{"id":"14594c91.bc2263","type":"comment","z":"4fb50fa4.554aa","name":"handle incoming data","info":"This part receives all the data that is coming from the mixer.\nThis includes every interaction with the mixer that happens outside of the Node-Red connection. For example with the phone app or X-Air-Edit.","x":140,"y":840,"wires":[]},{"id":"e1383ba2.003c38","type":"comment","z":"4fb50fa4.554aa","name":"request data updates from mixer","info":"This tells the mixer to send updated data back to Node-Red and listen for commands.\n\nBasically a \"please-keep-connection-alive\".","x":170,"y":1020,"wires":[]},{"id":"5c73e352.c969ec","type":"link in","z":"4fb50fa4.554aa","name":"XAir fade out channel [msg.channel,msg.time,msg.startVolume]","links":["aac0effb.fc015"],"x":315,"y":600,"wires":[["c91b092e.278cb8"]]},{"id":"e28d7ebf.ab647","type":"comment","z":"4fb50fa4.554aa","name":"fade out channel","info":"This fades out (from your specified volume to 0.0 volume) a channel.\nThis works by asnc sending multiple messages from node-red in the specific time.\n\nExpects the channel to be faded in msg.channel as either a Number or a String.\nThe channel will be automatically padded if it's below 10 (5 -> 05).\n\nExpects the time for the fade in msg.time as a Number in seconds.\n\nExpects the volume to start on in msg.startVolume as a Number (from 0.0-1.0 like the set channel volume function)","x":120,"y":600,"wires":[]},{"id":"47d291f3.10d42","type":"comment","z":"4fb50fa4.554aa","name":"fade out DCA","info":"This fades out (from your specified volume to 0.0 volume) a dca.\nThis works by asnc sending multiple messages from node-red in the specific time.\n\nExpects the channel to be faded in msg.channel as either a Number or a String.\nThe channel will be automatically padded if it's below 10 (5 -> 05).\n\nExpects the time for the fade in msg.time as a Number in seconds.\n\nExpects the volume to start on in msg.startVolume as a Number (from 0.0-1.0 like the set channel volume function)","x":110,"y":640,"wires":[]},{"id":"ef36f894.e353c8","type":"link in","z":"4fb50fa4.554aa","name":"XAir fade out DCA [msg.channel,msg.time,msg.startVolume]","links":["1c50ede3.776502"],"x":315,"y":640,"wires":[["6bd1dc0c.709534"]]},{"id":"42aabfd5.5a5b3","type":"inject","z":"4fb50fa4.554aa","name":"Trigger","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payloadType":"date","x":370,"y":1660,"wires":[["8c6a2644.600588"]]},{"id":"8c6a2644.600588","type":"change","z":"4fb50fa4.554aa","name":"","rules":[{"t":"set","p":"time","pt":"msg","to":"5","tot":"num"},{"t":"set","p":"channel","pt":"msg","to":"1","tot":"str"},{"t":"set","p":"startVolume","pt":"msg","to":"0.75","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":540,"y":1660,"wires":[["aac0effb.fc015"]]},{"id":"d107ba3f.bd8218","type":"comment","z":"4fb50fa4.554aa","name":"API test/demo nodes","info":"","x":130,"y":1200,"wires":[]},{"id":"c5eb4931.480c48","type":"inject","z":"4fb50fa4.554aa","name":"Trigger","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payloadType":"date","x":370,"y":1620,"wires":[["81a97d2b.095f4"]]},{"id":"81a97d2b.095f4","type":"change","z":"4fb50fa4.554aa","name":"","rules":[{"t":"set","p":"time","pt":"msg","to":"5","tot":"num"},{"t":"set","p":"channel","pt":"msg","to":"1","tot":"str"},{"t":"set","p":"endVolume","pt":"msg","to":"0.75","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":540,"y":1620,"wires":[["47d9ae41.ea686"]]},{"id":"111000ab.931d5f","type":"inject","z":"4fb50fa4.554aa","name":"Trigger","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payloadType":"date","x":370,"y":1700,"wires":[["1f45123.98d59ee"]]},{"id":"1f45123.98d59ee","type":"change","z":"4fb50fa4.554aa","name":"","rules":[{"t":"set","p":"time","pt":"msg","to":"5","tot":"num"},{"t":"set","p":"channel","pt":"msg","to":"1","tot":"str"},{"t":"set","p":"startVolume","pt":"msg","to":"0.75","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":540,"y":1700,"wires":[["1c50ede3.776502"]]},{"id":"970f9f10.407e4","type":"link in","z":"4fb50fa4.554aa","name":"XAir flex fade channel [msg.channel,msg.time,msg.startVolume,msg.endVolume]","links":["efc1eab2.b3a5d8"],"x":315,"y":680,"wires":[["497289d8.26a888"]]},{"id":"72ca09c8.f04f68","type":"comment","z":"4fb50fa4.554aa","name":"flex fade channel","info":"What i call a \"flex fade\" works the same way as the normal fade in and outs except you can give it both a start and end value.\n\nExpects the channel to be faded in msg.channel as either a Number or a String.\nThe channel will be automatically padded if it's below 10 (5 -> 05).\n\nExpects the time for the fade in msg.time as a Number in seconds.\n\nExpects the volume to start on in msg.startVolume as a Number (from 0.0-1.0 like the set channel volume function)\n\nExpects the volume to land on in msg.endVolume as a Number (from 0.0-1.0 like the set channel volume function)","x":120,"y":680,"wires":[]},{"id":"7f736135.ed96b","type":"inject","z":"4fb50fa4.554aa","name":"Trigger","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payloadType":"date","x":370,"y":1740,"wires":[["15bcc430.f5974c"]]},{"id":"15bcc430.f5974c","type":"change","z":"4fb50fa4.554aa","name":"","rules":[{"t":"set","p":"time","pt":"msg","to":"5","tot":"num"},{"t":"set","p":"channel","pt":"msg","to":"1","tot":"str"},{"t":"set","p":"endVolume","pt":"msg","to":"0.75","tot":"num"},{"t":"set","p":"startVolume","pt":"msg","to":"0.2","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":540,"y":1740,"wires":[["efc1eab2.b3a5d8"]]},{"id":"637ffe13.fd5ce","type":"link in","z":"4fb50fa4.554aa","name":"XAir flex fade DCA [msg.channel,msg.time,msg.startVolume,msg.endVolume]","links":["ebba8230.6c3ea"],"x":315,"y":720,"wires":[["1a30cc72.0ce104"]]},{"id":"4113ac70.6046b4","type":"comment","z":"4fb50fa4.554aa","name":"flex fade DCA","info":"What i call a \"flex fade\" works the same way as the normal fade in and outs except you can give it both a start and end value.\n\nExpects the dca to be faded in msg.channel as either a Number or a String.\n\nExpects the time for the fade in msg.time as a Number in seconds.\n\nExpects the volume to start on in msg.startVolume as a Number (from 0.0-1.0 like the set channel volume function)\n\nExpects the volume to land on in msg.endVolume as a Number (from 0.0-1.0 like the set channel volume function)","x":110,"y":720,"wires":[]},{"id":"bdc6233f.96faa","type":"inject","z":"4fb50fa4.554aa","name":"Trigger","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payloadType":"date","x":370,"y":1780,"wires":[["423f3d7e.935994"]]},{"id":"423f3d7e.935994","type":"change","z":"4fb50fa4.554aa","name":"","rules":[{"t":"set","p":"time","pt":"msg","to":"5","tot":"num"},{"t":"set","p":"channel","pt":"msg","to":"1","tot":"str"},{"t":"set","p":"endVolume","pt":"msg","to":"0.75","tot":"num"},{"t":"set","p":"startVolume","pt":"msg","to":"0.2","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":540,"y":1780,"wires":[["ebba8230.6c3ea"]]},{"id":"2e1f81a4.9fa08e","type":"comment","z":"4fb50fa4.554aa","name":"Set the XAIR18 IP here","info":"Do not change the port or any of the other settings","x":1060,"y":160,"wires":[]},{"id":"9d4727e3.321318","type":"comment","z":"4fb50fa4.554aa","name":"API","info":"","x":90,"y":140,"wires":[]},{"id":"f8c7a497.45ef08","type":"function","z":"4fb50fa4.554aa","name":"Generate Fade","func":"//get variables from msg object\nvar time = msg.time;\nvar channel = parseFloat(msg.channel);\nvar endVolume = msg.endVolume;\nvar outMSG = {topic: \"/ch/\" + ((channel < 10) ? (\"0\" + channel) : channel) + \"/mix/fader\"};\nvar resolution = 1023;\nvar stepSize = endVolume/resolution;\nvar timesteps = time/resolution*1000;\nvar currentVol = 0;\nvar currentStep = 0;\n\nwhile (timesteps < 20.0000000) {\n resolution--;\n stepSize = endVolume/resolution;\n timesteps = time/resolution*1000;\n}\n\nfunction generate() {\n currentStep++;\n if (currentStep >= resolution) {\n clearTimeout(timer);\n return null;\n } else {\n timer = setTimeout(function(){\n generate();\n }, timesteps);\n }\n currentVol += stepSize;\n outMSG.payload = currentVol;\n node.send(outMSG);\n}\n\nvar timer = setTimeout(function(){\n generate();\n}, timesteps);\n\nreturn null;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":440,"y":520,"wires":[["c64d8e14.85ffb"]]},{"id":"a724d185.a19b4","type":"function","z":"4fb50fa4.554aa","name":"Generate Fade","func":"var time = msg.time;\nvar channel = msg.channel;\nvar endVolume = msg.endVolume;\nvar outMSG = {topic: \"/dca/\" + channel + \"/fader\"};\nvar resolution = 1023;\nvar stepSize = endVolume/resolution;\nvar timesteps = time/resolution*1000;\nvar currentVol = 0;\nvar currentStep = 0;\n\nwhile (timesteps < 20.0000000) {\n resolution--;\n stepSize = endVolume/resolution;\n timesteps = time/resolution*1000;\n}\n\nfunction generate() {\n currentStep++;\n if (currentStep >= resolution) {\n clearTimeout(timer);\n return null;\n } else {\n timer = setTimeout(function(){\n generate();\n }, timesteps);\n }\n currentVol += stepSize;\n outMSG.payload = currentVol;\n node.send(outMSG);\n}\n\nvar timer = setTimeout(function(){\n generate();\n}, timesteps);\n\nreturn null;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":440,"y":560,"wires":[["c64d8e14.85ffb"]]},{"id":"c91b092e.278cb8","type":"function","z":"4fb50fa4.554aa","name":"Generate Fade","func":"var time = msg.time;\nvar channel = parseFloat(msg.channel);\nvar startVolume = msg.startVolume;\nvar outMSG = {topic: \"/ch/\" + ((channel < 10) ? (\"0\" + channel) : channel) + \"/mix/fader\"};\nvar resolution = 1023;\nvar stepSize = startVolume/resolution;\nvar timesteps = time/resolution*1000;\nvar currentVol = startVolume;\nvar currentStep = 0;\n\nwhile (timesteps < 20.0000000) {\n resolution--;\n stepSize = startVolume/resolution;\n timesteps = time/resolution*1000;\n}\n\nfunction generate() {\n currentStep++;\n if (currentStep >= resolution) {\n clearTimeout(timer);\n return null;\n } else {\n timer = setTimeout(function(){\n generate();\n }, timesteps);\n }\n currentVol -= stepSize;\n outMSG.payload = currentVol;\n node.send(outMSG);\n}\n\nvar timer = setTimeout(function(){\n generate();\n}, timesteps);\n\nreturn null;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":440,"y":600,"wires":[["c64d8e14.85ffb"]]},{"id":"6bd1dc0c.709534","type":"function","z":"4fb50fa4.554aa","name":"Generate Fade","func":"var time = msg.time;\nvar channel = msg.channel;\nvar startVolume = msg.startVolume;\nvar outMSG = {topic: \"/dca/\" + channel + \"/fader\"};\nvar resolution = 1023;\nvar stepSize = startVolume/resolution;\nvar timesteps = time/resolution*1000;\nvar currentVol = startVolume;\nvar currentStep = 0;\n\nwhile (timesteps < 20.0000000) {\n resolution--;\n stepSize = startVolume/resolution;\n timesteps = time/resolution*1000;\n}\n\nfunction generate() {\n currentStep++;\n if (currentStep >= resolution) {\n clearTimeout(timer);\n return null;\n } else {\n timer = setTimeout(function(){\n generate();\n }, timesteps);\n }\n currentVol -= stepSize;\n outMSG.payload = currentVol;\n node.send(outMSG);\n}\n\nvar timer = setTimeout(function(){\n generate();\n}, timesteps);\n\nreturn null;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":440,"y":640,"wires":[["c64d8e14.85ffb"]]},{"id":"497289d8.26a888","type":"function","z":"4fb50fa4.554aa","name":"Generate Flex Fade","func":"var time = msg.time;\nvar channel = parseFloat(msg.channel);\nvar startVolume = msg.startVolume;\nvar targetVolume = msg.endVolume;\nvar currentVol = startVolume;\nvar outMSG = {topic: \"/ch/\" + ((channel < 10) ? (\"0\" + channel) : channel) + \"/mix/fader\"};\nvar resolution = 1023;\nvar currentStep = 0;\nvar timesteps = time/resolution*1000;\nvar direction = (targetVolume - startVolume > 0) ? 1 : 0; //Up:Down\nvar stepSize = Math.abs(targetVolume - startVolume)/resolution;\n\nwhile (timesteps < 20.0000000) {\n resolution--;\n stepSize = Math.abs(targetVolume - startVolume)/resolution;\n timesteps = time/resolution*1000;\n}\n\nfunction generate() {\n currentStep++;\n if (currentStep >= resolution) {\n clearTimeout(timer);\n } else {\n timer = setTimeout(function(){\n generate();\n }, timesteps);\n }\n (direction) ? currentVol += stepSize : currentVol -= stepSize;\n outMSG.payload = currentVol;\n node.send(outMSG);\n}\n\nvar timer = setTimeout(function(){\n generate();\n}, timesteps);\n\nreturn null;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":460,"y":680,"wires":[["c64d8e14.85ffb"]]},{"id":"1a30cc72.0ce104","type":"function","z":"4fb50fa4.554aa","name":"Generate Flex Fade","func":"var time = msg.time;\nvar channel = parseFloat(msg.channel);\nvar startVolume = msg.startVolume;\nvar targetVolume = msg.endVolume;\nvar currentVol = startVolume;\nvar outMSG = {topic: \"/dca/\" + channel + \"/fader\"};\nvar resolution = 1023;\nvar currentStep = 0;\nvar timesteps = time/resolution*1000;\nvar direction = (targetVolume - startVolume > 0) ? 1 : 0; //Up:Down\nvar stepSize = Math.abs(targetVolume - startVolume)/resolution;\n\nwhile (timesteps < 20.0000000) {\n resolution--;\n stepSize = Math.abs(targetVolume - startVolume)/resolution;\n timesteps = time/resolution*1000;\n}\n\nfunction generate() {\n currentStep++;\n if (currentStep >= resolution) {\n clearTimeout(timer);\n } else {\n timer = setTimeout(function(){\n generate();\n }, timesteps);\n }\n (direction) ? currentVol += stepSize : currentVol -= stepSize;\n outMSG.payload = currentVol;\n node.send(outMSG);\n}\n\nvar timer = setTimeout(function(){\n generate();\n}, timesteps);\n\nreturn null;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":460,"y":720,"wires":[["c64d8e14.85ffb"]]},{"id":"194e3094.ff6aef","type":"comment","z":"4fb50fa4.554aa","name":"README (open this)","info":"# This contains important information. Plase take the time and read it.\n\nThis flow allows control over a Behringer XAir18 (others of the same series might work but are not tested) thanks to the OSC Protocol that the mixer uses for it's control interface. The same interface is also used by the phone app and the desktop software.\n\nThe implemented functions are just a very small subset of all the available mixer functions and are meant as an example for how to use Node-Red so control the mixer. If you want to add more features read the last paragraph from this readme.\n\nThere are API's written with link nodes so you can put this into a seperate flow and call it from everywhere inside node-red.\nJust to give you some ideas that you can use this for:\n- Midi translation from hardware controllers via node-red-contrib-midi/node-red-contrib-midi-windows\n- Audio automation for live streaming based on data coming over websocket from OBS-Studio\n- Audio automation based on incoming mqtt data from smarthome wall switches or sensors\n\nThere are a lot of comment nodes in this flow to make most of the code as understandable as possible.\nMost of them will have more information inside them when you double click on them (just like you did with this one).\nFor the API endpoints what data the endpoint requires is listed in there.\n\nThe fade generator code is not really the best and could be improved. But it works for now.\n\n# Getting started\n\nThis flow depends on only a single external npm package which is \"node-red-contrib-osc\".\nInstall it with \"npm install node-red-contrib-osc\" and restart node-red if you haven't already done that.\nFor more information visit it's page here: https://flows.nodered.org/node/node-red-contrib-osc\n\nChange the IP address in the 3 nodes that are marked \"Set the XAIR18 IP here\" to the address of your mixer.\nNow go and explore.\n\nYou can find the full documentation for the osc protocol inside the firmware zip file when you download it from the behringer website. It's inside the \"parameters.txt\".\n\nIf you have any problems or questions, join my dedicated [Discord Server](https://discord.gg/PCYQJwX) ","x":130,"y":60,"wires":[]},{"id":"1ba77832.976358","type":"comment","z":"4fb50fa4.554aa","name":"Set the XAIR18 IP here","info":"Do not change the port or any of the other settings","x":600,"y":1020,"wires":[]},{"id":"6afbcad0.ba0a34","type":"comment","z":"4fb50fa4.554aa","name":"Set the XAIR18 IP here","info":"Do not change the port or any of the other settings","x":140,"y":880,"wires":[]},{"id":"6e63a03f.0ff13","type":"comment","z":"4fb50fa4.554aa","name":"IMPORTANT! (read)","info":"This will result in a lot of data output if you move a fader for example.\nUse with caution as it can slow things down.\nFor the best result filter out everything you don't need or disable the debug node.","x":590,"y":880,"wires":[]},{"id":"d3109baf.3ed588","type":"link in","z":"4fb50fa4.554aa","name":"XAir set LR volume [msg.volume]","links":["e4c6f731.19b218"],"x":315,"y":400,"wires":[["b4576812.93c2a8"]]},{"id":"b4576812.93c2a8","type":"change","z":"4fb50fa4.554aa","name":"set OSC options","rules":[{"t":"set","p":"topic","pt":"msg","to":"/lr/mix/fader","tot":"str"},{"t":"set","p":"payload","pt":"msg","to":"volume","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":440,"y":400,"wires":[["c64d8e14.85ffb"]]},{"id":"86691fc2.d756c","type":"comment","z":"4fb50fa4.554aa","name":"set LR volume","info":"This set's the volume for the main LR out.\n\nExpects the target volume in msg.volume as a number(float).\n0.0 = -∞ db\n1.0 = +10 db\n\nZero db is around 0.7497556209564209","x":110,"y":400,"wires":[]},{"id":"11852448.65102c","type":"link in","z":"4fb50fa4.554aa","name":"XAir flex fade LR [msg.time,msg.startVolume,msg.endVolume]","links":["6b8554e0.90f75c"],"x":315,"y":760,"wires":[["f2a86867.185dc8"]]},{"id":"aa76dc7b.8ae13","type":"comment","z":"4fb50fa4.554aa","name":"flex fade LR","info":"What i call a \"flex fade\" works the same way as the normal fade in and outs except you can give it both a start and end value.\n\nExpects the time for the fade in msg.time as a Number in seconds.\n\nExpects the volume to start on in msg.startVolume as a Number (from 0.0-1.0 like the set channel volume function)\n\nExpects the volume to land on in msg.endVolume as a Number (from 0.0-1.0 like the set channel volume function)","x":110,"y":760,"wires":[]},{"id":"3893a307.877a2c","type":"inject","z":"4fb50fa4.554aa","name":"Trigger","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payloadType":"date","x":370,"y":1820,"wires":[["f9be28d6.bc7f78"]]},{"id":"f9be28d6.bc7f78","type":"change","z":"4fb50fa4.554aa","name":"","rules":[{"t":"set","p":"time","pt":"msg","to":"5","tot":"num"},{"t":"set","p":"endVolume","pt":"msg","to":"0.75","tot":"num"},{"t":"set","p":"startVolume","pt":"msg","to":"0.2","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":540,"y":1820,"wires":[["6b8554e0.90f75c"]]},{"id":"f2a86867.185dc8","type":"function","z":"4fb50fa4.554aa","name":"Generate Flex Fade","func":"var time = msg.time;\nvar startVolume = msg.startVolume;\nvar targetVolume = msg.endVolume;\nvar currentVol = startVolume;\nvar outMSG = {topic: \"/lr/mix/fader\"};\nvar resolution = 1023;\nvar currentStep = 0;\nvar timesteps = time/resolution*1000;\nvar direction = (targetVolume - startVolume > 0) ? 1 : 0; //Up:Down\nvar stepSize = Math.abs(targetVolume - startVolume)/resolution;\n\nwhile (timesteps < 20.0000000) {\n resolution--;\n stepSize = Math.abs(targetVolume - startVolume)/resolution;\n timesteps = time/resolution*1000;\n}\n\nfunction generate() {\n currentStep++;\n if (currentStep >= resolution) {\n clearTimeout(timer);\n } else {\n timer = setTimeout(function(){\n generate();\n }, timesteps);\n }\n (direction) ? currentVol += stepSize : currentVol -= stepSize;\n outMSG.payload = currentVol;\n node.send(outMSG);\n}\n\nvar timer = setTimeout(function(){\n generate();\n}, timesteps);\n\nreturn null;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":460,"y":760,"wires":[["c64d8e14.85ffb"]]},{"id":"2cc59450.7adcfc","type":"link in","z":"4fb50fa4.554aa","name":"XAir set bus send [msg.channel, msg.busChannel, msg.volume]","links":["778ab7ea.005658"],"x":315,"y":440,"wires":[["918e36a0.35bcd8"]]},{"id":"918e36a0.35bcd8","type":"change","z":"4fb50fa4.554aa","name":"set OSC options","rules":[{"t":"set","p":"topic","pt":"msg","to":"\"/ch/\" &\t$formatNumber($number(msg.channel), \"00\") &\t\"/mix/\" &\t$formatNumber($number(msg.bus), \"00\") &\t\"/level\"","tot":"jsonata"},{"t":"set","p":"payload","pt":"msg","to":"volume","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":440,"y":440,"wires":[["c64d8e14.85ffb"]]},{"id":"a2601a3e.b56cb8","type":"comment","z":"4fb50fa4.554aa","name":"set channel to bus send level","info":"This set's the bus send volume for a given channel.\n\nExpects the target volume in msg.volume as a number(float).\n0.0 = -∞ db\n1.0 = +10 db\n\nExpects the channel to be adjusted in msg.channel as either a Number or a String.\nThe channel will be automatically padded if it's below 10 (5 -> 05).\n\nExpects the bus in msg.bus as either a Number or a String.\nCan be 1-6.","x":160,"y":440,"wires":[]},{"id":"1fd96a59.4ee4d6","type":"function","z":"4fb50fa4.554aa","name":"example filter","func":"if (msg.topic.match(/\\/ch\\/\\d{2}\\/mix\\/fader/)) { //fader values\n //channel number = msg.topic.split(\"/\")[2]\n //fader value = msg.payload\n} else if (msg.topic.match(/\\/dca\\/\\d{1}\\/fader/)) { //DCA fader values\n //dca number = msg.topic.split(\"/\")[2]\n //fader value = msg.payload\n} else if (msg.topic.match(/\\/lr\\/mix\\/fader/)) { //LR values\n //fader value = msg.payload\n} else if (msg.topic.match(/\\/ch\\/\\d{1}\\/mix\\/on/)) { //Channel mute update\n //channel number = msg.topic.split(\"/\")[2]\n //mute status = !msg.payload\n}\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":570,"y":960,"wires":[[]]},{"id":"497615ea.55008c","type":"comment","z":"4fb50fa4.554aa","name":"fade in channel","info":"","x":120,"y":1580,"wires":[]},{"id":"cb8e8f3a.7829","type":"comment","z":"4fb50fa4.554aa","name":"fade in DCA","info":"","x":110,"y":1620,"wires":[]},{"id":"58c1cdb5.196a84","type":"comment","z":"4fb50fa4.554aa","name":"fade out channel","info":"","x":120,"y":1660,"wires":[]},{"id":"d4f6708f.9e00f","type":"comment","z":"4fb50fa4.554aa","name":"fade out DCA","info":"","x":110,"y":1700,"wires":[]},{"id":"7ad29dee.4ffb54","type":"comment","z":"4fb50fa4.554aa","name":"flex fade channel","info":"","x":120,"y":1740,"wires":[]},{"id":"299bef10.550ed","type":"comment","z":"4fb50fa4.554aa","name":"flex fade DCA","info":"","x":110,"y":1780,"wires":[]},{"id":"46f4c62c.3445f8","type":"comment","z":"4fb50fa4.554aa","name":"flex fade LR","info":"","x":110,"y":1820,"wires":[]},{"id":"92aeecd.82b5a1","type":"comment","z":"4fb50fa4.554aa","name":"mute/unmute channel","info":"","x":140,"y":1300,"wires":[]},{"id":"e528fd56.08bbb","type":"comment","z":"4fb50fa4.554aa","name":"set channel volume","info":"","x":130,"y":1380,"wires":[]},{"id":"c2f8905e.32f36","type":"comment","z":"4fb50fa4.554aa","name":"raw message","info":"","x":110,"y":1260,"wires":[]},{"id":"e96d9028.4b994","type":"comment","z":"4fb50fa4.554aa","name":"set DCA volume","info":"","x":120,"y":1420,"wires":[]},{"id":"e09ff243.d6c1","type":"comment","z":"4fb50fa4.554aa","name":"set LR volume","info":"","x":110,"y":1460,"wires":[]},{"id":"a2f38156.60dc7","type":"comment","z":"4fb50fa4.554aa","name":"set channel to bus send level","info":"","x":160,"y":1500,"wires":[]},{"id":"f496776d.ffb448","type":"inject","z":"4fb50fa4.554aa","name":"Trigger","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payloadType":"date","x":370,"y":1260,"wires":[["70149c4d.726e54"]]},{"id":"70149c4d.726e54","type":"change","z":"4fb50fa4.554aa","name":"","rules":[{"t":"set","p":"topic","pt":"msg","to":"/ch/01/mix/fader","tot":"str"},{"t":"set","p":"payload","pt":"msg","to":"0.7","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":540,"y":1260,"wires":[["9a559c79.1dcf5"]]},{"id":"798081fa.8ad08","type":"inject","z":"4fb50fa4.554aa","name":"Trigger","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payloadType":"date","x":370,"y":1300,"wires":[["977b802.bdcb08"]]},{"id":"977b802.bdcb08","type":"change","z":"4fb50fa4.554aa","name":"","rules":[{"t":"set","p":"channel","pt":"msg","to":"1","tot":"num"},{"t":"set","p":"mute","pt":"msg","to":"0","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":540,"y":1300,"wires":[["863370b9.3b28b"]]},{"id":"80a4dfe1.2717e","type":"inject","z":"4fb50fa4.554aa","name":"Trigger","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payloadType":"date","x":370,"y":1380,"wires":[["78448de3.b71904"]]},{"id":"78448de3.b71904","type":"change","z":"4fb50fa4.554aa","name":"","rules":[{"t":"set","p":"channel","pt":"msg","to":"2","tot":"num"},{"t":"set","p":"volume","pt":"msg","to":"0.5","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":540,"y":1380,"wires":[["1b4e040f.367fac"]]},{"id":"f56c541b.238888","type":"inject","z":"4fb50fa4.554aa","name":"Trigger","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payloadType":"date","x":370,"y":1420,"wires":[["1eda9528.c53a4b"]]},{"id":"1eda9528.c53a4b","type":"change","z":"4fb50fa4.554aa","name":"","rules":[{"t":"set","p":"channel","pt":"msg","to":"1","tot":"num"},{"t":"set","p":"volume","pt":"msg","to":"0.5","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":540,"y":1420,"wires":[["67f7f1f5.ada"]]},{"id":"cbeeee40.e4c65","type":"inject","z":"4fb50fa4.554aa","name":"Trigger","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payloadType":"date","x":370,"y":1460,"wires":[["4920af38.e05d3"]]},{"id":"4920af38.e05d3","type":"change","z":"4fb50fa4.554aa","name":"","rules":[{"t":"set","p":"volume","pt":"msg","to":"0.5","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":540,"y":1460,"wires":[["e4c6f731.19b218"]]},{"id":"e60ff2c9.9dd59","type":"inject","z":"4fb50fa4.554aa","name":"Trigger","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payloadType":"date","x":370,"y":1500,"wires":[["40062fdf.1244d"]]},{"id":"40062fdf.1244d","type":"change","z":"4fb50fa4.554aa","name":"","rules":[{"t":"set","p":"volume","pt":"msg","to":"0.5","tot":"num"},{"t":"set","p":"channel","pt":"msg","to":"3","tot":"num"},{"t":"set","p":"bus","pt":"msg","to":"1","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":540,"y":1500,"wires":[["778ab7ea.005658"]]},{"id":"9a559c79.1dcf5","type":"link out","z":"4fb50fa4.554aa","name":"","mode":"link","links":["f7c7cc9b.ed117"],"x":695,"y":1260,"wires":[]},{"id":"863370b9.3b28b","type":"link out","z":"4fb50fa4.554aa","name":"","mode":"link","links":["a6c9c95e.064878"],"x":695,"y":1300,"wires":[]},{"id":"1b4e040f.367fac","type":"link out","z":"4fb50fa4.554aa","name":"","mode":"link","links":["c8e2da80.6c8f08"],"x":695,"y":1380,"wires":[]},{"id":"67f7f1f5.ada","type":"link out","z":"4fb50fa4.554aa","name":"","mode":"link","links":["f74a9ec7.d8e35"],"x":695,"y":1420,"wires":[]},{"id":"e4c6f731.19b218","type":"link out","z":"4fb50fa4.554aa","name":"","mode":"link","links":["d3109baf.3ed588"],"x":695,"y":1460,"wires":[]},{"id":"778ab7ea.005658","type":"link out","z":"4fb50fa4.554aa","name":"","mode":"link","links":["2cc59450.7adcfc"],"x":695,"y":1500,"wires":[]},{"id":"10ca0b07.fb3ab5","type":"link out","z":"4fb50fa4.554aa","name":"","mode":"link","links":["4dafee1e.766c1"],"x":695,"y":1580,"wires":[]},{"id":"47d9ae41.ea686","type":"link out","z":"4fb50fa4.554aa","name":"","mode":"link","links":["d41095b2.d0b3d8"],"x":695,"y":1620,"wires":[]},{"id":"aac0effb.fc015","type":"link out","z":"4fb50fa4.554aa","name":"","mode":"link","links":["5c73e352.c969ec"],"x":695,"y":1660,"wires":[]},{"id":"1c50ede3.776502","type":"link out","z":"4fb50fa4.554aa","name":"","mode":"link","links":["ef36f894.e353c8"],"x":695,"y":1700,"wires":[]},{"id":"efc1eab2.b3a5d8","type":"link out","z":"4fb50fa4.554aa","name":"","mode":"link","links":["970f9f10.407e4"],"x":695,"y":1740,"wires":[]},{"id":"ebba8230.6c3ea","type":"link out","z":"4fb50fa4.554aa","name":"","mode":"link","links":["637ffe13.fd5ce"],"x":695,"y":1780,"wires":[]},{"id":"6b8554e0.90f75c","type":"link out","z":"4fb50fa4.554aa","name":"","mode":"link","links":["11852448.65102c"],"x":695,"y":1820,"wires":[]},{"id":"8c098418.e01c58","type":"comment","z":"4fb50fa4.554aa","name":"Why this port?","info":"The mixer returns the data to the ip and port that the \"request data updates\" request came from.\nSo in order to actually receive the data from the mixer this node listens to a non-random port that is the exact same as the one below.","x":110,"y":960,"wires":[]},{"id":"3b41f7ba.5f6738","type":"function","z":"4fb50fa4.554aa","name":"set OSC options","func":"//get variables from msg object\nvar channel = parseFloat(msg.channel);\nvar eqchannel = parseFloat(msg.eqchannel);\nvar f = msg.frequency;\nvar g = msg.gain;\nvar q = msg.width;\n\n//Generate the osc path for the channel and eq channel\nvar oscBaseString = \"/ch/\" + ((channel < 10) ? (\"0\" + channel) : channel) + \"/eq/\" + eqchannel + \"/\";\n\n//If msg.type is set and valid, send type message\nif (msg.hasOwnProperty(\"type\") && msg.type >= 0 && msg.type <=5) {\n //Sometimes you have to specify the osc data type by hand. This works by seting the payload like this:\n node.send({topic: oscBaseString + \"type\", payload: [{type: \"i\", value: msg.type}]});\n}\n\n//If msg.frequency is set and valid, send f message\nif (f >= 20 && f <= 20000) {\n node.send({topic: oscBaseString + \"f\", payload: (Math.log(f/20)) / (3*Math.log(10))});\n}\n\n//If msg.gain is set and valid, send g message\nif (g >= -15 && g <= 15) {\n node.send({topic: oscBaseString + \"g\", payload: (g+15) / (15+15)});\n}\n\n//If msg.width is set and valid, send q message\nif (q >= 0.3 && q <= 10) {\n node.send({topic: oscBaseString + \"q\", payload: (Math.log(q/10)) / (Math.log(0.3/10))});\n}\n\n//Do not need to return anything here because the messages have already been sent.\nreturn null;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":440,"y":480,"wires":[["c64d8e14.85ffb"]]},{"id":"9bcc1259.6ca89","type":"comment","z":"4fb50fa4.554aa","name":"adjust eq","info":"This can adjust the eq for a channel.\n\nMost of the values will not be exactly what you set them to, that's because the mixer as an internal \"step size\" that you can't influence. In case the value you set is between one of these steps it will automatically go to the next fitting step.\n\nExpects the channel where the eq should be changed in msg.channel as either a Number or a String\nThe channel will be automatically padded if it's below 10 (5 -> 05).\n\n(Optional) Expects the EQ type in msg.type as a number. This is optional and does not have to be set if you don't want to change it.\n0 = LCut\n1 = LShv\n2 = PEQ\n3 = VEQ\n4 = HShv\n5 = HCut\n\nExpects the eq channel (so the different points that you can edit inside the eq) in msg.eqchannel as a Number or String(1-4).\n1 = Low\n2 = LoMid\n3 = HiMid\n4 = High\n\nExpects the frequency for the point in msg.frequency as a Number (20-20000) in Hz.\n\nExpects the gain for the point in msg.gain as a Number (-15-15) in dB.\n\nExpects the Q factor (the \"width\") in msg.width as a Number (0.3-10).","x":100,"y":480,"wires":[]},{"id":"3692792d.0906d6","type":"link in","z":"4fb50fa4.554aa","name":"XAir adjust eq [msg.channel, (msg.type), msg.eqchannel, msg.frequency, msg.gain, msg.width]","links":["b56ad23f.9a778"],"x":315,"y":480,"wires":[["3b41f7ba.5f6738"]]},{"id":"5ab731f0.77614","type":"change","z":"4fb50fa4.554aa","name":"","rules":[{"t":"set","p":"frequency","pt":"msg","to":"5000","tot":"num"},{"t":"set","p":"gain","pt":"msg","to":"4","tot":"num"},{"t":"set","p":"width","pt":"msg","to":"3","tot":"msg"},{"t":"set","p":"eqchannel","pt":"msg","to":"2","tot":"num"},{"t":"set","p":"channel","pt":"msg","to":"2","tot":"num"},{"t":"set","p":"type","pt":"msg","to":"3","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":540,"y":1540,"wires":[["b56ad23f.9a778"]]},{"id":"a4afa991.70fef8","type":"comment","z":"4fb50fa4.554aa","name":"Changelog","info":"Version 1.2 (21.11.2021)\n- Updated nodes data\n- Small code improvements\n- No other changes besides that\n\nVersion 1.1 (26.01.2020)\n- Add eq type control\n- Add mutegroup control\n\nVersion 1.0 (23.01.2020)\n- Initial Version","x":360,"y":60,"wires":[]},{"id":"e62dfe1.e60e9","type":"inject","z":"4fb50fa4.554aa","name":"Trigger","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payloadType":"date","x":370,"y":1540,"wires":[["5ab731f0.77614"]]},{"id":"b56ad23f.9a778","type":"link out","z":"4fb50fa4.554aa","name":"","mode":"link","links":["3692792d.0906d6"],"x":695,"y":1540,"wires":[]},{"id":"b2ddde13.f83ee","type":"comment","z":"4fb50fa4.554aa","name":"adjust eq","info":"","x":100,"y":1540,"wires":[]},{"id":"bdf3371.55995c8","type":"link in","z":"4fb50fa4.554aa","name":"XAir mute/unmute mutegroup [msg.channel, msg.mute]","links":["cda330fd.7b22b"],"x":315,"y":280,"wires":[["8ad8c98d.5fd818"]]},{"id":"477284d0.34a2bc","type":"comment","z":"4fb50fa4.554aa","name":"mute/unmute mutegroup","info":"This mutes or unmutes a mutegroup.\n\nExpects the mutegroup to be muted in msg.channel as either a Number or a String.\nCan be 1-4.\n\nExpects the action (mute/unmute) in msg.mute as a Number:\n0 = Mutegroup off (Sources unmuted)\n1 = Mutegroup on (Sources muted)","x":150,"y":280,"wires":[]},{"id":"8ad8c98d.5fd818","type":"change","z":"4fb50fa4.554aa","name":"set OSC options","rules":[{"t":"set","p":"topic","pt":"msg","to":"\"/config/mute/\" & $number(msg.channel)","tot":"jsonata"},{"t":"set","p":"payload","pt":"msg","to":"$number(msg.mute)","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":440,"y":280,"wires":[["c64d8e14.85ffb"]]},{"id":"3ba6fc1f.f308f4","type":"inject","z":"4fb50fa4.554aa","name":"Trigger","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payloadType":"date","x":370,"y":1340,"wires":[["88a1f7b6.a371b8"]]},{"id":"88a1f7b6.a371b8","type":"change","z":"4fb50fa4.554aa","name":"","rules":[{"t":"set","p":"channel","pt":"msg","to":"1","tot":"num"},{"t":"set","p":"mute","pt":"msg","to":"0","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":540,"y":1340,"wires":[["cda330fd.7b22b"]]},{"id":"cda330fd.7b22b","type":"link out","z":"4fb50fa4.554aa","name":"","mode":"link","links":["bdf3371.55995c8"],"x":695,"y":1340,"wires":[]},{"id":"d525691.9414798","type":"comment","z":"4fb50fa4.554aa","name":"mute/unmute mutegroup","info":"This mutes or unmutes a channel.\n\nExpects the channel to be muted in msg.channel as either a Number or a String.\nThe channel will be automatically padded if it's below 10 (5 -> 05).\n\nExpects the action (mute/unmute) in msg.mute as a Number:\n1 = Channel on (Unmuted)\n0 = Channel off (Muted)","x":150,"y":1340,"wires":[]}]
@colinvv2
Copy link

Love it! Just want to add a way to get current values when the node starts up.

@tasteebeverage
Copy link

Dude, this is absolutely awesome. Thank you for creating it. I'm able to turn on my jam room sound system and set values via home automation in home assistant. Thanks again!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment