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.
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.
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 (astr
,bytes
orbytearray
instance containing a JSON document) to a Python object using this conversion table.
The other arguments have the same meaning as inload()
.
If the data being deserialized is not a valid JSON document, aJSONDecodeError
will be raised.
Changed in version 3.6: s can now be of typebytes
orbytearray
. 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!
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
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! 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.
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!