Infinite audio matrix/routing with Dante audio network

Nice little packet sniffing project...

Dante is a propriety, high-quality, low-latency (<5ms), high density (32x32, 64x64, 128x128) audio over IP solution that was created by Audinate. They make some of their own hardware and license the tech to other companies in the commercial/pro-audio space.

Inspired by this gearspace thread
https://gearspace.com/board/music-computers/1221989-dante-routing-without-dante-controller-possible-2.html

Typically the way to control routing is through the dante controller software.

dante sample setup in controller
subscribers on left <-> sources on top

but I wanted to be able to automate routing

flow

[
    {
        "id": "6dbc8f871796a620",
        "type": "change",
        "z": "28c43d512158d500",
        "name": "IP and channel to cancel subscriptions",
        "rules": [
            {
                "t": "set",
                "p": "subscriber",
                "pt": "msg",
                "to": "192.168.1.100",
                "tot": "str"
            },
            {
                "t": "set",
                "p": "subscriberChannel",
                "pt": "msg",
                "to": "1",
                "tot": "num"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 250,
        "y": 1720,
        "wires": [
            [
                "b33febe2cba43d33"
            ]
        ]
    },
    {
        "id": "c478d2eb03af1693",
        "type": "change",
        "z": "28c43d512158d500",
        "name": "IP and channel to subscribe to",
        "rules": [
            {
                "t": "set",
                "p": "subscriber",
                "pt": "msg",
                "to": "192.168.1.100",
                "tot": "str"
            },
            {
                "t": "set",
                "p": "subscriberChannel",
                "pt": "msg",
                "to": "1",
                "tot": "num"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 230,
        "y": 1660,
        "wires": [
            [
                "20acc0317404d6b8"
            ]
        ]
    },
    {
        "id": "20acc0317404d6b8",
        "type": "change",
        "z": "28c43d512158d500",
        "name": "Device and channel to source from",
        "rules": [
            {
                "t": "set",
                "p": "sourceChannel",
                "pt": "msg",
                "to": "CH1",
                "tot": "str"
            },
            {
                "t": "set",
                "p": "sourceDevice",
                "pt": "msg",
                "to": "MXWANI8-72d2",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 560,
        "y": 1660,
        "wires": [
            [
                "42c5dea1d8985710"
            ]
        ]
    },
    {
        "id": "42c5dea1d8985710",
        "type": "function",
        "z": "28c43d512158d500",
        "name": "Dante Controller Subscribe",
        "func": "//Dante Controller subscribe function\n\n//subscriber config -- set to any subscriber\nvar Subscriber = msg.subscriber //ip address of subscriber\nvar Port = 4440 //port 4440 for subscription requests\nvar SubscriberChannel = msg.subscriberChannel //subscriber channel number - numerical value, not name \n\n//source config -- set to any source\nvar SourceChannel = msg.sourceChannel  //name of channel in dante controller\nvar SourceDevice = msg.sourceDevice  //name of device in dante controller\n\n\n\n\n//buffer constants\nconst BufHeader = Buffer.from('272900', 'hex');\nconst Buf1 = Buffer.from('000030100000040100', 'hex');\nconst Buf2 = Buffer.from('005c00', 'hex');\nconst Buf3 = Buffer.from('0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', 'hex');\nconst BufNull = Buffer.from('00', 'hex');\n\n//buffer variables\nvar BufSubChannel = Buffer.from([SubscriberChannel]);\nvar BufSorChannel = Buffer.from(SourceChannel, 'ascii');\nvar BufSorDevice = Buffer.from(SourceDevice, 'ascii');\nvar SorChLength = BufSorChannel.length;  //used to calculate other sections of stream\nvar SorDevLength = BufSorDevice.length\nvar BufSorNameLength = Buffer.from([SorChLength + 93]);\nvar BufTotalLength = Buffer.from([SorChLength + 94 + SorDevLength]);\n\n\n//concatinated buffer -- all inputs must be buffers\nvar BufComplete = Buffer.concat([BufHeader, BufTotalLength, Buf1, BufSubChannel, Buf2, BufSorNameLength, Buf3, BufSorChannel, BufNull, BufSorDevice, BufNull])\n\n//UDP output config\nmsg.ip = Subscriber\nmsg.port = Port\n\n//complete buffer output\nmsg.payload = BufComplete\n\n//debug messages\nmsg.inNameLength = SorChLength\nmsg.totalLength = BufComplete.length\n \nreturn msg;",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 860,
        "y": 1660,
        "wires": [
            [
                "8b58d47178f038d8"
            ]
        ]
    },
    {
        "id": "b33febe2cba43d33",
        "type": "function",
        "z": "28c43d512158d500",
        "name": "Dante Controller Unsubscribe",
        "func": "//Dante Controller subscribe function\n\n//subscriber config -- set to any subscriber\nvar Subscriber = msg.subscriber //ip address of subscriber\nvar Port = 4440 //port 4440 for subscription requests\nvar SubscriberChannel = msg.subscriberChannel //subscriber channel number - numerical value, not name \n\n\n\n//buffer constants\nconst BufHeader = Buffer.from('272900', 'hex');\nconst Buf1 = Buffer.from('000030100000040100', 'hex');\nconst Buf3 = Buffer.from('000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', 'hex');\nconst BufNull = Buffer.from('00', 'hex');\n\n//buffer variables\nvar BufSubChannel = Buffer.from([SubscriberChannel]);\nvar BufTotalLength = Buffer.from([92]);\n\n//concatinated buffer -- all inputs must be buffers\nvar BufComplete = Buffer.concat([BufHeader, BufTotalLength, Buf1, BufSubChannel, Buf3])\n\n//UDP output config\nmsg.ip = Subscriber\nmsg.port = Port\n\n//complete buffer output\nmsg.payload = BufComplete\n\n//debug messages\nmsg.totalLength = BufComplete.length\n \nreturn msg;",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 870,
        "y": 1720,
        "wires": [
            [
                "8b58d47178f038d8"
            ]
        ]
    },
    {
        "id": "8b58d47178f038d8",
        "type": "udp out",
        "z": "28c43d512158d500",
        "name": "",
        "addr": "",
        "iface": "",
        "port": "",
        "ipv": "udp4",
        "outport": "",
        "base64": false,
        "multicast": "false",
        "x": 1090,
        "y": 1680,
        "wires": []
    }
]

functions

//Dante Controller subscribe function

//subscriber config -- set to any subscriber
var Subscriber = msg.subscriber //ip address of subscriber
var Port = 4440 //port 4440 for subscription requests
var SubscriberChannel = msg.subscriberChannel //subscriber channel number - numerical value, not name 

//source config -- set to any source
var SourceChannel = msg.sourceChannel  //name of channel in dante controller
var SourceDevice = msg.sourceDevice  //name of device in dante controller




//buffer constants
const BufHeader = Buffer.from('272900', 'hex');
const Buf1 = Buffer.from('000030100000040100', 'hex');
const Buf2 = Buffer.from('005c00', 'hex');
const Buf3 = Buffer.from('0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', 'hex');
const BufNull = Buffer.from('00', 'hex');

//buffer variables
var BufSubChannel = Buffer.from([SubscriberChannel]);
var BufSorChannel = Buffer.from(SourceChannel, 'ascii');
var BufSorDevice = Buffer.from(SourceDevice, 'ascii');
var SorChLength = BufSorChannel.length;  //used to calculate other sections of stream
var SorDevLength = BufSorDevice.length
var BufSorNameLength = Buffer.from([SorChLength + 93]);
var BufTotalLength = Buffer.from([SorChLength + 94 + SorDevLength]);


//concatinated buffer -- all inputs must be buffers
var BufComplete = Buffer.concat([BufHeader, BufTotalLength, Buf1, BufSubChannel, Buf2, BufSorNameLength, Buf3, BufSorChannel, BufNull, BufSorDevice, BufNull])

//UDP output config
msg.ip = Subscriber
msg.port = Port

//complete buffer output
msg.payload = BufComplete

//debug messages
msg.inNameLength = SorChLength
msg.totalLength = BufComplete.length
 
return msg;
//Dante Controller unsubscribe function

//subscriber config -- set to any subscriber
var Subscriber = msg.subscriber //ip address of subscriber
var Port = 4440 //port 4440 for subscription requests
var SubscriberChannel = msg.subscriberChannel //subscriber channel number - numerical value, not name 



//buffer constants
const BufHeader = Buffer.from('272900', 'hex');
const Buf1 = Buffer.from('000030100000040100', 'hex');
const Buf3 = Buffer.from('000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', 'hex');
const BufNull = Buffer.from('00', 'hex');

//buffer variables
var BufSubChannel = Buffer.from([SubscriberChannel]);
var BufTotalLength = Buffer.from([92]);

//concatinated buffer -- all inputs must be buffers
var BufComplete = Buffer.concat([BufHeader, BufTotalLength, Buf1, BufSubChannel, Buf3])

//UDP output config
msg.ip = Subscriber
msg.port = Port

//complete buffer output
msg.payload = BufComplete

//debug messages
msg.totalLength = BufComplete.length
 
return msg;
1 Like

Although I don't have a use for this, thank you for linking the write up on how it was done. It's fascinating to see people reverse engineer stuff without a public API.

Oh, since they do have a pay-to-access API to do this, that reminds me... if this ever catches on, Audinate may just decide to push some firmware that breaks everything we've worked on so here is the version I'm on...
image