TRIGGERING CUES ON GRANDMA 3 WITH LAYER NAMES IN RESOLUME VIA OSC

Post your questions here and we'll all try to help.
Post Reply
Remo Rauscher
Posts: 1
Joined: Sat Feb 12, 2022 07:01

TRIGGERING CUES ON GRANDMA 3 WITH LAYER NAMES IN RESOLUME VIA OSC

Post by Remo Rauscher »

Doing theatre, need to trigger light with live video feeds.

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.

Image

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.

Post Reply