Description
Sunday, August 13, 2023
Control of Digico desks with OSC -
Sunday, August 6, 2023
Control of Digico desks with OSC - Part 3B – Using TouchOSC
This turned out to be a rather long article so I divided into two parts 3A and 3B. The section below is part 3B and continues on directly from part 3A.
Building Pages
Want to have multiple tabbed pages for different but related controls, it’s easy to do in TouchOSC. Start a new file, right click on the working surface and select Pager. It will launch a default outline with 3 page tabs along the top. If you examine the Pager properties you will have some selections available to change tab sizes and Font size.
Clicking the Pages button shows the existing pages already assigned to the container (called a Pager) and a plus sign on the right of the Pages line allows you to add more pages. In this example I built a 4 page pager. Clicking on the tabs moves you from page to page.
Each page can have any number of controls on it and the properties of the Pager and the pages can be changed from the Control properties drop down. The currently selected page is shown with a highlighted tick mark. Under the pager1 drop down is a list of pages and the controls nested within them.
Examples of Working Controls
Here is a simple page that controls playback functions of the Reaper DAW, shown as part of a 4 page pager.
Our 4 pages are arranged as tabs along the top and this page shows simple playback controls for a Reaper session on our local host computer. In this example the connections page has been used to assign different port numbers to communicate with the Reaper software which has also been setup to receive OSC commands on the corresponding ports. We used Device Connection #5 in our connections menu for these communication channels and assigned ports 8003 and 9003 as send and receive respectively.
Like most DAW’s Reaper will accept OSC commands in a very simple format e.g “/play” and “/stop”. However there are a much more extensive set of control commands that are called Actions in Reaper and have specific Action numbers assigned to them. The play command is the same as the pause command in Reaper (which can complicate matters) so even though we built a toggle button for Play/Pause, there is only one OSC command
“/play” or, using Reaper actions, “/action/40073”. The Local commands set the text of the button to indicate a Paused or Play condition and change the colour of the text so it stands out.
The Local commands are triggered seperately by the Rise and Fall of the toggle button which gives two Text labels (Play and Pause) and two colours for the text (white and black). If you are a Reaper user please check out the OSC documentation on the Reaper web site and some discussions in the Reaper Forum. If you use Cubase or Ableton you can set up the DAW for OSC control in a similar way. If you use a cue manager such as QLab or Multiplay then they both have extensive OSC control sections. Qlab has a well documented set of OSC controls specifically for use with TouchOSC.
Scripting
A unique feature of TouchOSC is the ability to write scripts to direct the way that controls operate in many different and singular ways. The scripting is based on a limited sub-set of the LUA language. LUA is similar to other object oriented programming languages so if you have some familiarity with JavaScript or Python then you will be able to find your way around. Once again the use of scripting in TouchOSC is not well documented and most users seem to rely on examples and scripts published by other users. I do not explain the Lua syntax in the examples below, I leave that to you.
At the end of this article is a list of helpful script resources.
Here is an example of a script
This script is taken from one of the examples that are included on a very useful GitHub site called https://github.com/F-l-i-x/TouchOSC/tree/main/modules . I suggest you download the file and run it to see the way that the buttons are programmed to interact with each other. Here is what the controls look like.
It looks simple but is more complicated than it looks in order to demonstrate the way that scripting works. The yellow outlined button and label are actually a group delineated by the blue line, the green button and numbers are inside pages within a pager delineated by the grey line. The blue and red buttons and the red label are part of the “root” page. The script below contains comments, coloured in green, inserted by the original author using the standard commenting prefixes “--“. This script is added to the red button control. Each of the other buttons (yellow, green and blue) have their own scripts that are basically the same script except the “self” text properties define the different colours and the amount of page changing needed.
-------------------------------------------------------------------------------------------------------
-- "self" is the red button
function onValueChanged(key)
if (key == "x" and self.values.x == 1) then -- only on rise of x
self.parent.children.label.values.text = "red button"
-- back to --> root -- forward to --> label
self.parent.children.group1.children.label_group.values.text = "red button"
-- back to --> root
-- forward to --> group1 --> label_group
self.parent.children.pager1.children.page2.children.label_pager.values.text = "red button"
-- back to --> root
-- forward to --> pager1 --> page2 --> label_pager
-- (page2 is named like this in the settings)
-- if it has original name ("2") use children["2"]
-- or the index of the page children[2]
-------------------------------------------------------------------------------------------------------
Here are my notes on this script that hopefully will help explain how it works.
function onValueChanged(key)
Standard function call which defines the whole script
if (key == "x" and self.values.x == 1) then
Capture of the button press value (x) for a momentary button.
self.parent.children.label.values.text = "red button"
Write the text to the label in the root page (there is only one label on the root)
self.parent.children.group1.children.label_group.values.text = "red button"
Write the text to the label in the group, this is where the yellow label lives.
self.parent.children.pager1.children.page2.children.label_pager.values.text = "red button"
Write the text to the label in pager1, this is where the green label lives.
end
end
Seperate end commands for if statement and function call.
-------------------------------------------------------------------------------------------------------
If you have been following this whole article along closely you are probably thinking why not use a Local command to do the labelling instead of this opaque kind of script?
That’s a good point because in an earlier example we used a Local command to display the current value of a fader. The reason for the scripting is that Local commands can’t address other containers directly, they can only address their local page. The whole TouchOSC interface is based on objects that “contain” other objects and can be nested inside each other many layers deep.
So in the example above you could use a local command for the red label but nothing else would work.
If you downloaded this example and are running it then please take a close look at the scripts for the other buttons, in particular the green button. Here the script has to back out of the page and pager that the button is in to get to root before being able to write the text to the label on the root page. That is a great example of how the nested objects are referenced by each other.
Example script with Digico controls
This is something that most desk users will be familiar with, solos, aux send levels and aux send on/off controls. This page is part of a more extensive set of tabbed pages but is self contained to these functions and so does not need extensive “root” or “parent” page references. It makes script writing much easier if you keep you controls functionally organised by pages. (The unlabelled controls on the right hand side are not fully defined or developed and can be used for script experimentation if you would like to try your hand out).
Unfortunately there is no apparent OSC command for the “Select” function on Digico desks (i.e. when you touch a fader or press a fader channel on screen). Using the solo seems the only way to do this from an OSC controller. A double click of the solo button would still assign the auxiliary control if solo is not required. The controls stay set to the most recently pressed solo button.
The Solos block is a group containing 12 buttons which are simply labelled 1 - 12. Each time a button is pressed a short script tells the group which button has been pressed.
The Solos group has a much more extensive script which does the following:-
- Defines a Digico Solo OSC command to be run that is specific to whichever button has been pressed.
- Manages the naming/renaming of the group name and clears the current button values whenever a button is pressed e.g. clearing the current solo’d button when a different one is pressed.
- Sets
a variable with the button number as a value for the “tag” property of the
group. This is then inserted into the aux control tag property and used in
the aux OSC commands.
- The aux controls follow the solo buttons so if, for example, input 10 is solo’d the aux controls will control the send levels of channel 10
Let’s take a look at the messages that these controls send to the Digico desk in Protokol.
Solo 1 was selected and the OSC message sent to the desk.
Watch what happens on Protokol when we select a different solo, we’ll use Channel 5.
Line 1 is our original solo command for Channel 1. When we pressed Channel 5 the script sent an OSC command, value FLOAT(0), to channel 1 to clear that solo (see Line 2) and then a solo command to channel 5 (see Line 3).
Now lets look at what happens when we switch off and on our auxiliary send.
The selected channel 5 is followed by the aux send on/off control as well as the rotary control as shown below:-
Here solo 5 was selected and the
level changed on Aux send 1 with the appropriate OSC command issued to adjust
the aux send level to approximately -6db.
In the next example we adjusted Aux send 4 level to approximately -4db and then turned on the send.
We’ll take a look at the scripts in sections, first up will be the solo buttons section.
Solo Buttons Script
function onValueChanged(key)
if (key == "x") then
if self.values.x == 1 then
self.parent:notify("ON", self.name)
elseif self.values.x == 0 then
self.parent:notify("OFF", self.name)
end
end
end
This is a very similar function call to the one that we discussed above in the buttons and labels example. It notifies the group container that if the solo button is pressed (value=1) then set the button property to ON in the group container. That’s all we need right now.
Solo Group Script
Before we look closely at this script take a look at the Control properties for the group and see that the tag has been assigned the value of the currently solo’s channel. This is what allows us to issue the correct OSC commands from the Aux send controls.
Here is the code which I have broken out into sections to help explain what each block does.
Section A
This initialises the variables to blank or zero values, and then defines the OSC messages that are to be sent when a button is pressed, released or superseded by a new button press (these actions are called “super”). The OSC commands are sent complete with the appropriate Float value corresponding to the rise or fall of the button press. (Commented sections are by the author).
-----------------------------------------------------------------------------------------------------------------
-- Put this code in your Group
-- NOTE: Buttons in the Group have to be named 1, 2, 3, ...
-- NOTE: All buttons have to run the same short script to communicate with the Group
----- messages to be sent ------------------------------
-- init
old_button = 0
on = {}
super = {}
off = {}
-- 'on' messages.
-- Sent by ANY button that is pushed and turned on:
on["any"] = {}
-- 'on' messages for individual buttons (starting at 1):
on[1] = {'/Input_Channels/1/solo', 1} ---Note that the Float value is included in
on[2] = {'/Input_Channels/2/solo', 1} ---the OSC command.
on[3] = {'/Input_Channels/3/solo', 1}
on[4] = {'/Input_Channels/4/solo', 1}
on[5] = {'/Input_Channels/5/solo', 1}
on[6] = {'/Input_Channels/6/solo', 1}
on[7] = {'/Input_Channels/7/solo', 1}
on[8] = {'/Input_Channels/8/solo', 1}
on[9] = {'/Input_Channels/9/solo', 1}
on[10] = {'/Input_Channels/10/solo', 1}
on[11] = {'/Input_Channels/11/solo', 1}
on[12] = {'/Input_Channels/12/solo', 1}
-- 'super' messages.
-- Sent by button that gets superseded by another one, to turn the solo button value to 0
-- in case there is no previously active button (all buttons off)
super[0] = {}
-- 'super' messages for individual buttons (starting at 1)
super[1] = {'/Input_Channels/1/solo', 0}
super[2] = {'/Input_Channels/2/solo', 0}
super[3] = {'/Input_Channels/3/solo', 0}
super[4] = {'/Input_Channels/4/solo', 0}
super[5] = {'/Input_Channels/5/solo', 0}
super[6] = {'/Input_Channels/6/solo', 0}
super[7] = {'/Input_Channels/7/solo', 0}
super[8] = {'/Input_Channels/8/solo', 0}
super[9] = {'/Input_Channels/9/solo', 0}
super[10] = {'/Input_Channels/10/solo', 0}
super[11] = {'/Input_Channels/11/solo', 0}
super[12] = {'/Input_Channels/12/solo', 0}
-- 'off' messages.
-- Sent when the only active button is pushed and turned off, not on button change
-- message sent at ANY 'off' event
off["any"] = {}
-- 'off' messages for individual buttons (for Grid starting at 1)
off[1] = {'/Input_Channels/1/solo', 0}
off[2] = {'/Input_Channels/2/solo', 0}
off[3] = {'/Input_Channels/3/solo', 0}
off[4] = {'/Input_Channels/4/solo', 0}
off[5] = {'/Input_Channels/5/solo', 0}
off[6] = {'/Input_Channels/6/solo', 0}
off[7] = {'/Input_Channels/7/solo', 0}
off[8] = {'/Input_Channels/8/solo', 0}
off[9] = {'/Input_Channels/9/solo', 0}
off[10] = {'/Input_Channels/10/solo', 0}
off[11] = {'/Input_Channels/11/solo', 0}
off[12] = {'/Input_Channels/12/solo', 0}
-----------------------------------------------------------------------------------------------------------------
Section B
The “If” part of the function defines what to do when a button is turned on; first any existing button in an “on” state is turned off; the new “ON” button is saved as the next button to be superseded and then the appropriate OSC message is sent as defined in Section A
-----------------------------------------------------------------------------------------------------------------
function onReceiveNotify(command, button)
if old_button ~= 0 then -- if other button is still 'on' then turn it 'off'
self.children[tostring(old_button)].values.x = 0
end
if command == "ON" then -- if a button was turned 'on'
handle_button(super[tonumber(old_button)]) -- handle individual 'super' msg of -- button that gets superseded
old_button = button -- save the now active button for next 'super' action
handle_button(on["any"]) -- handle the generic 'on' msg
handle_button(on[tonumber(button)]) -- handle the individual 'on' msg
-----------------------------------------------------------------------------------------------------------------
Section C
This section defines a variable “ch” which is used to define the tag property of the radial controls (aux send level) and the Aux on/off button.
-----------------------------------------------------------------------------------------------------------------
--- CA added tag id to enable rotary control --- and on/off to follow solo.
self.parent.children.radial72.properties.tag=ch --- radial control names are out of
---numerical order !! It depends how you build your page, these were moved around!
self.parent.children.radial77.properties.tag=ch
self.parent.children.radial76.properties.tag=ch
self.parent.children.radial75.properties.tag=ch
self.parent.children.radial74.properties.tag=ch
self.parent.children.radial73.properties.tag=ch
--- now for on/off button addressing
--- same out of order group addresses!
self.parent.children.group86.children.button47.properties.tag=ch
self.parent.children.group87.children.button48.properties.tag=ch
self.parent.children.group90.children.button49.properties.tag=ch
self.parent.children.group88.children.button50.properties.tag=ch
self.parent.children.group89.children.button51.properties.tag=ch
self.parent.children.group91.children.button52.properties.tag=ch
-------------------------------------------------------------------------------------------------- end CA
-----------------------------------------------------------------------------------------------------------------
Section D
The “elseif” part of the function that issues the OSC off commands for the buttons
-----------------------------------------------------------------------------------------------------------------
elseif command == "OFF" then -- if a button was turned 'off'handle_button(off["any"]) -- handle the generic 'off' msg
handle_button(off[tonumber(button)]) -- handle the individual 'off' msg
old_button = 0 -- set now active button to 'none'
end
end
function handle_button(osc_msg)
if #osc_msg == 0 then -- if no OSC message is set: do nothing
return
else
sendOSC(unpack(osc_msg)) -- if OSC message is set: unpack and send
end
end
-----------------------------------------------------------------------------------------------------------------
Once assigned a channel number, the on/off and rotary level controls issue standard OSC commands and do not need any scripting. The OSC commands contain a variable “tag” which is evaluated from the tag line in the Control Properties and we defined in the script above. The OSC command becomes:-“/Input_Channels/tag/Aux_Send/1/send_level”
Note that the range of the rotary has been changed to -60 to +6 in exactly the same way as we did in the earlier fader example.
The on/off button has a similarly constructed OSC command
“Input_Channels/tag/Aux_Send/1/send_on”
-----------------------------------------------------------------------------------------------------------------
Summary
- We have explained in detail how TouchOSC works and why it is such a valuable resource for generating more complex and interactive control pages.
- We examined the Properties of several controls and how to issue OSC commands and how to change the state of each control for various purposes.
- We explained how to develop tabbed pages that functionally seperate different sets of commands.
-
Lastly we looked at how the Lua
scripting module works and discussed some examples in detail. TouchOSC is
extremely flexible without having to leap into scripting and you can develop
very nice and useful control pages without any scripting at all. But it’s all
there if you want to use it
Hopefully these articles have been helpful, feel free to leave comments.
© Clive Alcock, 2023
TouchOSC Script Examples and Resources
A couple of sample control pages have been added to the sidebar on this blog. They are not necessarily complete and at least one of them has unfinished controls that you can use for script experiments. This lets you copy and paste working commands and then modifying them for different purposes.
https://github.com/F-l-i-x/TouchOSC/tree/main
A series of script samples for a wide variety of useful functions
https://github.com/attaccapublishing/TouchOSC
A Tim Corpus resource with a variety of script samples and other examples
https://drive.google.com/file/d/1Snbwx3m6us6L1qeP1_pD6s8hbJpIpD0a/view
For those of you using the X32 platform this is the best resource for OSC control commands.
https://hexler.net/touchosc/manual/script-examples#sending-osc-messages
Some script examples from the developer of TouchOSC
https://qlab.app/docs/v5/networking/using-osc/
Useful short form explanation of how to use OSC commands with Qlab
https://qlab.app/docs/v3/scripting/osc-dictionary-v3/
A complete definition of the Qlab OSC API
Useful TouchOSC YouTube Channels
https://timcorpus.net/index.php/touchosc/
Tim Corpus has published a whole series of useful OSC related videos including extensive instructions on implementing the OSC client on IOS phones and tablets.
https://www.youtube.com/results?search_query=touchosc
If you want to get lost in a whole world of TouchOSC videos just click here. There are videos about using TouchOSC to run Ableton, Reaper, Logic Pro, Midi over ethernet, Arduino and robotics and a whole host of other quite improbable scenarios.
Stream Deck
This is a popular device that has a number of programmable buttons which can control a wide range of AV devices. There is an implementation for Stream Deck with some basic Digico commands. Unfortunately there are several challenges with this and I don’t recommend it for the following reasons.
a) There is no fader implementation using Stream Deck, nudge buttons can be made to increment/decrement fader values by a defined amount, which lacks useability.
b) The Stream Deck Digico implementation is very limited and not easily updated. Stream Deck requires a configuration file written in Javascript and they don’t publish the structure of it. There is an enthusiastic user group so if you really want to use Stream Deck then you might be able to get help writing a more extensive config file.
----------------------------------------------------------------------------------------------------------------