Stumbled upon grandMA3 and needed to trigger Light-Cues via OSC. Found many fragments for answers and assembled them to a script in Processing to act as a bridge application that uses CLIP-NAMES in Resolume (where the cue number is stored) to send grandMA3 customised OSC-Messages.

Code: Select all
/*
-----------------------------------------------------------------
TRIGGERING CUES ON GRANDMA 3 WITH LAYER NAMES IN RESOLUME VIA OSC
-----------------------------------------------------------------
THIS PROCESSING APPLICATION USES AN OPEN SOURCE OSC-SERVER TO LISTEN
TO RESOLUME FOR CLIP CHANGES ON A SPECIFIC LAYER THAT CONTAINS CLIP NAMES
THAT REFER TO AND TRIGGER CUE NUMBERS IN GRANDMA 3 SEQUENCES
THIS WAY YOU DON'T NEED TO SET CUSTOM OSC ADDRESSES IN RESOLUME
TO TRIGGER LIGHT CUES. ALSO A BLANK CLIP ACTS AS A 'WAIT' AND KEEPS
THE CURRENT LIGHT CUE UNTIL A NEW NUMBER IS BEING TRIGGERED
THIS IS POWERFUL IN MANY WAYS. ALSO FADES AND OTHER RESOLUME PARAMETERS
CAN BE CUSTOMIZED FOR YOUR PLEASURE BY QUICKLY GIVING CLIP NAMES.
---
THE FOLLOWING CODE EXPECTS RESOLUME TO HAVE BLANK (OR ANY) CONTENT ON A SPECIFIC LAYER.
IMPORT IS THE LAYER NUMBER THAT IS BEING SET IN VARIABLE:
*/
int lightLayer = 3; // (NOTE THAT RESOLUME ALWAYS COUNTS FROM BOTTOM UP)
/*
--------------------
// HOW TO GET THERE:
--------------------
0: BE IN THE SAME NETWORK AS GRANDMA.
1: PROCESSING
https://processing.org/download
2: INSTALL LIBRARIES FOR PROCESSING
In order to make OSC running in Processing
download and install oscP5 by Andreas Schlegel:
https://sojamo.de/libraries/oscP5/
On MAC save Library folder to:
> User/Documents/Processing/libraries/
*/
// The following 2 libraries will available by then:
import oscP5.*; // OSC Server
import netP5.*; // Network
OscP5 OscP5; // Initiate server
// GUI and FONTS
int canvasWidth = 300;
int canvasHeight = 130;
int bigFont = 20;
int smallFont = 13;
String upper = "GRANDMA";
String middle = "...";
String lower = "PLEASE";
// Port for THIS Processing application
int incoming = 11000; // Incoming Port for THIS application = outgoing port for Resolume
// GRANDMA
int outgoingToGrandMA = 8000; // = Incoming port in grandMA3 Software
String IPGrandma = "10.0.1.33";
NetAddress GrandmaOscLocation; // Network object will be initated in setup();
// Resolume
int outgoingToResolume = 12000; // = OSC input port in Resolume
String IPResolume = "127.0.0.1"; // = OSC output port in Resolume
NetAddress ResolumeOscLocation; // Network object for sending OSC Messages
// COMMUNICATION VARIABLES
int lightColumn; // To store column once your navigating in Resolume
String lightClipName = ""; // To receive Clip-Name from Resolume
// INITIATION
void setup() {
size(300, 130); // Canvas width and height. freakingly it doesn't accept variables o.O
frameRate(25);
// Initiating OSC Server and Network Locations
OscP5 = new OscP5(this, incoming);
GrandmaOscLocation = new NetAddress(IPGrandma, outgoingToGrandMA);
ResolumeOscLocation = new NetAddress(IPResolume, outgoingToResolume);
PFont vulf;
vulf = createFont("vulf.ttf", bigFont); // ttf in data folder im sketch
textFont(vulf);
textAlign(CENTER);
}
// R E F R E S H ... every 25 frames
void draw() {
background(0);
// GUI STUFF
textSize(bigFont);
text(middle, canvasWidth/2, canvasHeight/1.95);
textSize(smallFont);
text(lower, canvasWidth/2, canvasHeight/2+bigFont*1.4);
text(upper, canvasWidth/2, canvasHeight/2-bigFont*1.5);
}
// -------------------------
// O S C - L I S T E N E R
// -------------------------
void oscEvent(OscMessage theOscMessage) {
// OSC-Messages mostly consist of 2 parts
// 1: The Pattern: which is a customised String coming from Resolume for a specific parameter
// 2: A Value: this can be an Integer, Float or String or more.
String addrPattern = theOscMessage.addrPattern(); // We only need the pattern to extract the Clips Position and Name
// Printing ALL OSC-Messages coming from Resolume EXCEPT position information
if(addrPattern.indexOf("position") <0 ){
println(theOscMessage.addrPattern() + " " +theOscMessage.typetag());
}
// LISTENING TO LIGHT LAYER
String[] m1 = match(addrPattern, "/composition/layers/"+lightLayer+"/clips/(.*?)/connect"); // If not null, then a match was found
if (m1 != null) { // If regular Expression is null, then it doesn't contain the pattern
if(theOscMessage.get(0).intValue() == 3){ // Integer-Value contains connection information. If '3' = New Connection
println(">> Connected to: " + m1[1]);
// ------
// IMPORTANT: Question to Resolume to ask for the name of the layer (containing the Cue-Number as a String)
// ------
askResolume("?/composition/layers/"+lightLayer+"/clips/"+m1[1]+"/name"); // REQUESTING NAME OF CONNECTED CLIP => function "askResolume();"
lightColumn = int(m1[1]); // Updating current column for navigation
}
}
// WAIT FOR THE CLIP-NAME
if(addrPattern.indexOf("/composition/layers/"+lightLayer+"/clips/"+lightColumn+"/name") >=0 ){
lightClipName = trim( theOscMessage.get(0).stringValue() ); // store and trim blanks (=spaces)
if(int(lightClipName) != 0){ // if String isn't an integer number, then don't send it to grandMA
sendToGrandMa(int(lightClipName)); // If yes: trigger OSC-Message to GrandMA
}
}
}
// -------------------------
// ASK RESOLUME
// Funny enough: EVERY OSC Message can be used to send a request to Resolume by putting a '?' just in front of the message
// eg: ?/composition/layers/3/clips/1/name"
// this way Resolume will 'answer' by sending back the name of the clip to this application
// see: usage of this function 'askResolume' in OSC-Listener
// -------------------------
public void askResolume(String question){
OscMessage Message = new OscMessage(question);
OscP5.send(Message, ResolumeOscLocation);
println("> askResolume: " + question);
}
// -------------------------
// SEND TO GRANDMA
// after having resolved the Clip Name coming from Resolume, a String will be merged to serve grandMA's needs
// which will trigger the Cue-Number in the Sequence already programmed there.
// -------------------------
public void sendToGrandMa(int cue){
OscMessage Message = new OscMessage("/cmd"); // "cmd" shall be added if there is NO 'Prefix' setted in GrandMA's Software
Message.add( "Goto Cue " + cue ); // grandMA needs only a String containg the function ('Goto Cue') plus a space ' ' plus the number of the Cue.
OscP5.send(Message, GrandmaOscLocation);
println(">> sendToGrandMa: " + cue);
middle = "Goto Cue "+cue; // variable for the GUI
}
>>>
Download Processing Script and Libraries: www.theaterdermitte.at/labor/GrandMA3-OSC.zip
>>>
Hope it helps.
Curious about your thoughts.