Passing JSON from UdpinDAT to JsonDAT

Greetings! I have a user who is trying to pass information from my Beat Link Trigger to Touch Designer, so it can respond to beats and song structure information about tracks playing on Pioneer pro DJ gear. I think this is a great idea, so I have been trying to help. He wants me to pass JSON data that he can parse, using UDP. I have placed the bytes of the JSON data followed by a newline into a UDP packet and sent it to the address he has configured in TouchDesigner. It seems to be received by the UdpinDAT, but the JsonDAT is complaining it needs text. I am afraid I know nothing about TouchDesigner, and little Python, but I am guessing he may somehow need to add a script that parses the bytes from the UdpinDAT to a Python string before passing that on to the JsonDAT, much as I had to do the reverse operation in my Clojure code before sending the packet. Does this make sense? Is his goal even plausibly possible, and can someone help us with the last steps? Or should we consider an alternate approach? I can send data in many ways. Iā€™m not sure if I can add links in these postings, but will investigate that later. I am between flights right now, but wanted to get this discussion started. Thanks for any help anyone can offer, I think this would be a very useful integration for many people if we can get it working.

1 Like

Hey @deepsymmetry,

just testing this a bit it seem to work as expected. The screenshot here showing how I tested this with a UDPOut DAT as the sender and then selecting the incoming data with a Select DAT from the UDPIn DAT.

Now for practical reasons and to make sure all messages are processed, it would make sense to deal with the incoming data in the UDPInā€™s callback. There is a real chance that multiple messages are received per frame which would make it hard to process them with the JSON DAT - instead parsing and dealing with the data right in the callback would circumvent this issue.

Hope this helps a bit
Markus

Thank you so much for responding. One immediate difference I see between your screen shot and what I received from the lighting designer in China who is trying to make this work is the select3 node you added in between the udpin and json blocks. He doesnā€™t have one of those. Can you explain what it does? I will also hunt for documentation while waiting for my next flight. (Were I not traveling, I would download TouchDesigner for myself, and experiment with this stuff. I will have to do that when I get home and have some time in any case, it looks like a very interesting environment.)

Also, the way you are constructing the data for the udpout block looks identical to what I am doing in my Clojure code, which is promising. I feel like we are very close. :smile:

One last question: I am extracting the bytes of the JSON string using UTF-8 encoding, is that what TouchDesigner would expect to find? And in case it helps, hereā€™s the error screen shot he sent me. Iā€™ll link him to this discussion as well, so I can stop trying to play interpreter, as someone who has never run TouchDesigner.

Hmm, it looks like the forum prevents links in posts, how strangely irritating. How do you cross-reference relevant outside resources?

Hey @deepsymmetry,

the JSON DAT is intended to only parse JSON. So when it does not receive valid JSON data it will go into an error state. The header row of the UDPIn DAT not being JSON will cause this error. In a more recent build of TouchDesigner the error message of the JSON DAT also reflects that better.
I was using the Select DAT to only select out the second row from the table data that is coming from the UDPIn DAT.

Generally I would still say that the JSON DAT is not the correct approach for what you are trying to do. The received message should be parsed in the UDPInā€™s onReceive callback function. The callbacks are attached to the DAT.
If sending a message like the one in my previous example {"hello":456, "something":"ā˜ŗ"} and i want to parse out the values, the callback would look like this:

import json
def onReceive(dat, rowIndex, message, bytes, peer):
	jDict = json.loads(message)
	helloVal = jDict.get('hello', None)
	somethingVal = jDict.get('something', None)
	debug(helloVal, somethingVal)
	return

In the TouchDesigner Textport (Alt+t) you would then get this output:

456 ā˜ŗ (Debug - DAT:/project1/udpin1_callbacks fn:onReceive line:21)

It really then depends what you want to do with this data. Maybe there is some strings that come across that should be passed on to a Table DAT or some operator that can render text. Perhaps there is numerical data that should be passed on to other parametersā€¦

As you would be using the python json library to decode it, json can be encoded in utf-8, utf-16 or utf-32. From the documentation:

json.loads(s, ***, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, **kw )
Deserialize s (a str, bytes or bytearray instance containing a JSON document) to a Python object using this conversion table.
The other arguments have the same meaning as in load().
If the data being deserialized is not a valid JSON document, a JSONDecodeError will be raised.
Changed in version 3.6: s can now be of type bytes or bytearray. The input encoding should be UTF-8, UTF-16 or UTF-32.

Regarding posting links, to prevent spam there might be an inital number of posts requirement before you can include links. Iā€™m not sure but checking.

Hope this helps
cheers
Markus

Thank you, Markus, that is tremendously helpful. I agree that this looks like a much better approach. Hopefully my user will see it when he wakes up, since I linked to this discussion from my own support community. And I can download TouchDesigner and experiment for myself once Iā€™m not traveling.

Also, what you are saying about a minimum post requirement to cut down on spam is very reasonable. Thanks again!

1 Like

Iā€™m coming back to this because I am in the process of writing up the integration example for the Beat Link Trigger user guide, showing people how to get phrase structure information from Pioneer DJ hardware fed into TouchDesigner so that their designs can react to the nature of the music being played. My user was able to achieve this without coding, he said ā€œI used a combination of function nodes convert2, select3, result, and table in TouchDesigner to implement JSON tables without writing any code because I donā€™t know how to.ā€

Iā€™d like to try a variation that is closer to what you propose above, using the onReceive callback to parse the JSON dictionary, and then feed that to a table DAT directly. Since I am going to be encouraging people to experiment with TouchDesigner, I would like the example to follow best practices as you see them.

The JSON I am sending has seven values in it: masterPlayerNumber, an integer; bpm, a floating-point number; trackBank, phraseType, and trackTitle are all strings, beat is an integer from 1 to 4 telling you the part of a measure that has just started playing, and fill which is a boolean, indicating whether the phrase is currently filling time (ā€œvampingā€) before the next phrase begins. I am hoping there is a way to simply create a table row from this information in a more direct way than using all the nodes my user did. I have installed TouchDesigner and am experimenting a bit, but it is a large and rich environment that will take me a while to learn, so any help would be welcome!

Letā€™s see if I can share links yetā€¦ if so, here is the discussion in my own user forum as we converged on what is working for now: (Alas, no, I am still not allowed to share links here, I would very much appreciate guidance on how I can get that restriction removed; I do not impose it in my own forum. If you find Beat Link Trigger on GitHub you can find links to the Zulip community forum on the main project page, and once you are there, the thread from the person who wanted my help sending information to TouchDesigner is titled ā€œA Chinese lighting designer seeking help.ā€)

On a forum of this size this is needed to head of spammers.
See Understanding Discourse Trust Levels, scroll to Trust Level 1 ā€“ Basic

Get to trust level 1 byā€¦

  • Entering at least 5 topics
  • Reading at least 30 posts
  • Spend a total of 10 minutes reading posts

so if you browse the forum outside of this topic for a few minutes, youā€™re good to go!

so here is an example which writes some received JSON to a Table DAT.
Because Iā€™m lazy I used a Webclient DAT to pull some demo JSON from the internet, but the same Python code applies to the onReceive callback of a UDPInDAT.
When you click the request pulse parameter of the webclient DAT, it will pull this JSON data from the web:

{
  "userId": 1,
  "id": 1,
  "title": "delectus aut autem",
  "completed": false
}

An in its onResponse callback are some lines of Python which will write those items to a Table DAT:

import json
def onResponse(webClientDAT, statusCode, headerDict, data, id):
	data = json.loads(data)
	op("table1").clear()
	for key, value in data.items():
		op("table1").appendRow((key,value))
	return

image

Download example: fill_table_with_JSON_received_from_web.tox (7.7 KB)

So how to find this info as a new user?
if you click the yellow/blue Python logo question mark on the topleft of a nodeā€™s parameter window, you end up in itā€™s Python class documentation. So ff you do that on a Table DAT youā€™ll find this documentation on how to modify table content:

Thanks for the responses! I didnā€™t see them until just now because I was busy exploring and experimenting for myself, although I may adjust the approach to work the way you suggest in my user guide. I was trying to recreate the table structure my user had come up with, which has the first row of the table hold the column labels, and the second row has the data for each column. I was able to do that, and learned a little bit about programming in TouchDesigner, despite my very limited Python exposure. It seems like a wonderful system, I hope to have an excuse to try something real in it for myself someday!

Here is the solution that I came up with in my UDPIn DAT:

import json
def onReceive(dat, rowIndex, message, bytes, peer):
	jDict = json.loads(message)
	table = op('table1')
	player = jDict.get('masterPlayerNumber')
	bpm = jDict.get('bpm');
	trackBank = jDict.get('trackBank')
	phraseType = jDict.get('phraseType')
	title = jDict.get('trackTitle')
	beat = jDict.get('beat')
	fill = jDict.get('fill')
	table.replaceRow(1, [player, bpm, trackBank, phraseType, title, beat, fill], entireRow=True)
	return

I had set up a table with the column headers and an empty second row before starting to send the UDP messages.

Since I did not like all the boilerplate I had to use to pull out the JSON elements and then manually set up the column headers, I am now going to try switching it up to do it the way you did, with the row labels coming from the JSON keys, and the values being the second element in each row. I assume that a table like that is as easy to consume as the version I created in a more inelegant way?

Since Iā€™m only here to try to set up an example in my user guide that will hopefully interest people in the idea of working with TouchDesigner (and since working on that is holding up some of my own feature development that I am eager to get back to), I wonā€™t be spending time browsing other forum topics just to gain the ability to post links here. Hopefully eventually Discourse will notice I have spent enough time here. Zulip must have a less intrusive way of fending off spammers; none of the communities I am part of there seem to have trouble, and some of them are much larger than my own.

Thanks again for the great help!

Ah! I did earn the Basic trust badge just now, so I can post links! :smile_cat: Once I publish the user guide revision with this integration example, I will post a link to it here.

In case anyone reads these forums who can take feedback about the beginner experience in TouchDesigner, the hardest problem I faced was figuring out how to see output from debug. I eventually discovered that I had to open the Textport and DATs window using the Dialogs menu, but that was not something that was easy to figure out through the help searches I performed, and was not something I would have guessed.

The impressive thing, though, is that by just clicking around with things and experimenting I was able to make progress fairly steadily, and all the more quickly once I had that dialog open. Nice work!

Oh, nice, by just swapping appendRow in your code for appendCol I can get the same format my user wanted, with your more flexible approach which derives both the headers and data values from the JSON payload.

1 Like

And I have published version 7.1.0 of Beat Link Trigger, which includes the TouchDesigner integration example in the user guide. You can find that example here. Thanks again to everyone who helped me figure out how to do this!

1 Like