Has anyone figured out how to send text from max to the display of the VSN1? A simple step by step guide on how such solution is spread across the various sections of the grid editor would be highly apreciated. Is there anything out already for someone fit in MaxMsp but totaly dummy on lua and its strcuture in the editor? Thanks in advance.
VSN1 display & MaxMsp
Replies (34)
this, for example, could be on the max side as a simple test using cc 20-23.
Working on it, making a sysex example right now, almost done
sounds good. in the end i will have to figure out a way to send to the grid controller directly from within the maxforlive device without the track-midi-out … as the final device, currently, is sitting in the master track. i hope to find a way without having to write a control script - which is another rabbit hole entirely.
many thanks. my main problem however is the grid side. is this supposed to work with your widget WIP (which here only shows a red screen with a colorchanging square bottom right and a 0 in the middle)?
grid-editor://?config-link=3aQggwzkqFAH2e00rVN5
Or look up the name "MaxMSP Test Widget (wip)", and load it onto a VSN1 module.
By default after loading the profile, 4 colorful rectangles should be seen with 0,1,2,3 as values.
Disclaimer: This is a showcase of the widget system, which will be supported on firmware level, once we settled on the behaviour. This means, the code and example working here might be different after weeks / months, looking back to this thread. The config is in lua though, so it should work, but hopefully in some time as this moves to the firmware, we will have cleaner configs and more script length at our disposal!
The backbone of this config is the Layout. This code is on the system event setup in the profile I mentioned above. It's plain lua. The goal of the Layout, is to add widgets to it, where the widgets get automatic sizing benefits as all widget is a cell within a grid layout, like they will know their x (start X coord), y (start Y coord), h (height) and w (width) to draw out things.
Layout = {
new = function(self)
local instance = setmetatable({}, { __index = self })
return instance
end,
addWidget = function(self, cx, cy, wg)
wg.w = self.width / self.r // 1
wg.h = self.height / self.c // 1
wg.x = self.x + cx * wg.w // 1
wg.y = self.y + cy * wg.h // 1
wg.parent = self
self.cells[cy * self.r + cx] = wg
return wg
end,
initialize = function(self, lcd, hcc, vcc, x, y, w, h)
self.lcd = lcd
self.r = hcc
self.c = vcc
self.width = w
self.height = h
self.x = x
self.y = y
self.cells = {}
for i = 0, hcc * vcc - 1 do
self.cells[i] = {}
end
end,
render = function(self)
for i = 0, self.r * self.c - 1 do
local cell = self.cells[i]
if type(cell.render) == "function" then
cell:render()
end
end
self.lcd:draw_swap()
end,
sysexrx_cb = function(self, hdr, ev)
for i = 0, self.r * self.c - 1 do
local cell = self.cells[i]
if type(cell.sysexrx_cb) == "function" then
cell:sysexrx_cb(hdr, ev)
end
end
end,
}This code is for the brave, some parts even I have to spend an hour to figure out, but fortunately LLM tools, specially Claude can help with it. Plan is to move this to firmwre level.
great. works, thanks. what would be a simple mod to get a different layout ?
oh. i think you just answerd my question with this Layout code. thanks.
I try to explain bits in this thread, hang on
My needed layout is a simple text list however with options for size and style per line. i could solve that with the background color possibly but need a bigger font.
The widget, which we render looks like this and resides on the second small button's setup event under the screen:
MAX_TEXT = {
new = function(self, ci, wclr)
local instance = setmetatable({}, { __index = self })
instance.ci = ci
instance.text = ci
instance.tclr = { 255, 255, 255 }
instance.wclr = wclr ~= nil and wclr or { 40, 40, 40 }
instance.tsize = 32
return instance
end,
render = function(self)
self.parent.lcd:draw_area_filled(self.x, self.y, self.x + self.w, self.y + self.h, self.wclr)
self.parent.lcd:draw_text(self.text, self.x + 4, self.y + 4, self.tsize, self.tclr)
end,
sysexrx_cb = function(self, hrd, ev)
local ci = tonumber(ev:match("^%x%x%x%x(%x%x)"), 16)
if ev ~= nil and self.ci == ci then
self.text = extractSysexString(ev)
end
end,
}To show sysex data received on the widget, the sysexrx_cb, is passed to the Layout with coding wizardry. The received sysex data is parsed with a helper function written in lua, found under the first button.
In sysexrx_cb, we use the ci value which stands for cell index. This helps determine, which cell should be written.
In the new part, the widget is initialised with sensible defaults. Right now just ci - cell index, wclr widget color are passed in as parameters during initialization, but this can be extended to have tsize and other params too.
Two more key details are:
1.) LCD Setup
On the LCD, under setup event we glue this together like this:
lcd_set_backlight(255)
canvas = Layout:new()
canvas:initialize(self, 2, 2, 0, 0, 320, 240)
canvas:addWidget(0, 0, MAX_TEXT:new(0, { 150, 190, 0 }))
canvas:addWidget(0, 1, MAX_TEXT:new(1, { 0, 120, 120 }))
canvas:addWidget(1, 0, MAX_TEXT:new(2, { 170, 50, 40 }))
canvas:addWidget(1, 1, MAX_TEXT:new(3, { 70, 120, 255 }))
canvas:render()
self.sysexrx_cb = function(self, hdr, ev)
canvas:sysexrx_cb(hdr, ev)
endThis is the place, where the layout is initialized for the full screen, with 2 row and 2 cell -> canvas:initialize(self, 2, 2 ...).
Then the widget is registered in the 4 cell, with their cell index and default colors.
After registration, we call canvas:render() to render after initialization.
The sysexrx_cb is also passed here for the canvas (instance of Layout).
2.) LCD Draw
The changes passed to the widgets are continously rendered with the canvas:render() function on the LCD draw event.
wait a minute, I'll try to adjust what we have now
1.) To get differen text size upon widget creation:
Add tsize as a paremeter for the widget code:
new = function(self, ci, tsize, wclr)
local instance = setmetatable({}, { __index = self })
instance.ci = ci
instance.text = ci
instance.tclr = { 255, 255, 255 }
instance.wclr = wclr ~= nil and wclr or { 40, 40, 40 }
instance.tsize = tsize ~= nil and tsize or 32
return instance
end,I do a check for nil value, could be omitted as well. Default is 32. The widget uses draw_text, which can receive arbitrary text size. If you use draw_text_fast, it can get text size in the increment of 8, so 8, 16, 24 and so on (but might be 8,16,32? I'm not sure on this one).
2.) Update the LCD's setup event
We pass the text size as the second parameter for widget creation.
I have also changed the layout, creation a 1 col, 4 row canvas.
The widgets are rendered then under one and the other.
The last parameter in the new right now is the widget background color, could be omitted or set to something else.
lcd_set_backlight(255)
canvas = Layout:new()
canvas:initialize(self, 1, 4, 0, 0, 320, 240)
canvas:addWidget(0, 0, MAX_TEXT:new(0, 20, { 150, 190, 0 }))
canvas:addWidget(0, 1, MAX_TEXT:new(1, 30, { 0, 120, 120 }))
canvas:addWidget(0, 2, MAX_TEXT:new(2, 40, { 170, 50, 40 }))
canvas:addWidget(0, 3, MAX_TEXT:new(3, 50, { 70, 120, 255 }))
canvas:render()
self.sysexrx_cb = function(self, hdr, ev)
canvas:sysexrx_cb(hdr, ev)
endIf you want to change the text color and size from sysex, you can do that too.
In the widget code (MAX_TEXT on the second small button), you have to somehow extract the other data and pass it to the widget..
if ev ~= nil and self.ci == ci then
self.text = extractSysexString(ev)
-- here you could add / call other helper functions to parse text color (tclr variable in the widget) or text size (tsize variable)
--self.tclr = MY_TEXT_COLOR_SYSEX_EXTRACTOR(ev)
--self.tclr = MY_TEXT_SIZE_SYSEX_EXTRACTOR(ev)
endhey kristof, your help is highly apreciated, really. thanks so much. i have to admit, that still, i dont get where the various code sippets go in the editor and why (like why with the smal buttons? this a workaround?) but hey… here is a first working approach thanks to your guide. awsome. thanks.
Yes, to split the code to functions and put them at various places is a workaround. We lifted the character limit from 400 characters to 900, but it is still a limiting factor.
I have shared the altered profile as "MaxMSP Test Widget (wip) - Variant 2", so you can use it right away. If you need just more horizontal lines, you would have to:
1.) extend the canvas size, like changing it to 5:canvas:initialize(self, 1, 5, 0, 0, 320, 240)
2.) add a new widget at row 5:
canvas:addWidget(0, 4, MAX_TEXT:new(3, 50, { 70, 120, 100 }))
Thanks again kristof. I now have a working version of what I tried to do (getting the scene list of "ya_Next" device onto the VSN1 screen) thanks to you.
Great, happy exploring. Later on if you wish new additons or have other features on your mind, let us know, it might inspire new ways for configurations.
a quick follow up: how would I tackle the fact that the solution currently does not display umlaut (ä ü ö) and and some more special characters and quirks ("…" becomes "&" while "&" remains "&")?
… trying to figure out how to decode special characters (like umlauts ä, ö, ü) from the SysEx data. Do you know if the default font used by draw_text actually contains the glyphs for these characters? If it doesn't, is there a way to specify or load a different font that supports UTF-8 or extended character sets?
I think it does not contain those characters. I will have to ask @user on this. Right now it's not possible to load different character set.
This probably needs to be something high up on the list to get VSN1 display speak multiple language fonts, right? If you get any feedback from @user please let me know. Highly apreciated, thanks.
View on Discord
This post is from the Intech Studio Discord community.




