/**
* Getiing and sending device parameters from/to Ableton Live through a ControlP5 knob
* To adjust a device parameter, the parameter needs to be changed (not only selected) in Ableton Live
*
* to be installed within Ableton
*
* GPL Licensed
* by Hartmut habu Wöhlbier * MAD Lab - Mannheim Digital Lab / Hochschule Mannheim
* Please do not remove this header.
*/
import oscP5.*;
import netP5.*;
import controlP5.*;
ControlP5 cP5;
Knob knob;
OscP5 oscP5;
NetAddress myRemoteLocation;
int track, device, dial, rangeMinInt, rangeMaxInt;
float dialValue, rangeMinFloat, rangeMaxFloat;
String dialName = "none", trackName = "none", deviceName = "none";
Boolean paramIsFloat;
void setup() {
size(300, 200);
// start oscP5, listening for incoming messages at port 9001 from Ableton */
oscP5 = new OscP5(this,9001);
myRemoteLocation = new NetAddress("127.0.0.1",9000); // loopback to Ableton port 9000
// some graphical stuff
fill(0, 102, 153);
smooth();
cP5 = new ControlP5(this);
knob = cP5.addKnob("knob",100,100,128,100,160,40)
.setRange(0,1)
.setValue(60)
.setPosition(40,80)
.setRadius(25)
.setViewStyle(Knob.ARC)
.setDragDirection(Knob.VERTICAL)
.setLabel("")
;
handleDial();
}
void draw() {
background(128, 128, 128);
text( "Track: " + trackName + " (" + track + ")\n" +
"Device: " + deviceName + " (" + device + ")\n" +
"Parameter: " + dialName + " (" + dial + ")\n" +
"Value: " + dialValue,
15, 20);
}
///////////////// oscEvent /////////////////////
void oscEvent(OscMessage theOscMessage) {
// what is comming in?
displayMessage(theOscMessage);
// what are we missing?
Boolean missMess = true;
// Checking for track selection
if(theOscMessage.checkAddrPattern("/live/track") == true)
{
print("Caught " + theOscMessage.addrPattern()); missMess = false;
track = theOscMessage.get(0).intValue() - 1; // different numering in live
getTrackName();
//oscP5.send("/live/track/view", new Object[] { new Integer(track) }, myRemoteLocation);
oscP5.send("/live/track/device/view", new Object[] { new Integer(track), new Integer(0) }, myRemoteLocation);
getDeviceName(); //deviceName = "none";
handleDial();
}
// Checking for device selection
if(theOscMessage.checkAddrPattern("/live/device/selected") == true)
{
print("Caught " + theOscMessage.addrPattern()); missMess = false;
track = theOscMessage.get(0).intValue();
device = theOscMessage.get(1).intValue();
getTrackName();
getDeviceName();
handleDial();
}
// checking the params of the modified device
if(theOscMessage.checkAddrPattern("/live/device/param") == true)
{
print("Caught " + theOscMessage.addrPattern()); missMess = false;
track = theOscMessage.get(0).intValue();
device = theOscMessage.get(1).intValue();
dial = theOscMessage.get(2).intValue();
dialValue = theOscMessage.get(3).floatValue();
dialName = theOscMessage.get(4).stringValue();
knob.setValue(dialValue).setLabel(dialName);
getParamRange();
getTrackName();
getDeviceName();
}
// checking for track name
if(theOscMessage.checkAddrPattern("/live/name/track") == true)
{
print("Caught " + theOscMessage.addrPattern() ); missMess = false;
track = theOscMessage.get(0).intValue();
trackName = theOscMessage.get(1).stringValue();
}
// checking for device name
// we only get this through the divicelist with names of all devices
if(theOscMessage.checkAddrPattern("/live/devicelist") == true)
{
print("Caught " + theOscMessage.addrPattern()); missMess = false;
track = theOscMessage.get(0).intValue();
// multiply device by 2 and add 2, since the first two element of the message are track and device
// and then every second element ist the desired string:
deviceName = theOscMessage.get(2*device+2).stringValue();
}
// checking for parameter range
if(theOscMessage.checkAddrPattern("/live/device/range") == true)
{
print("Caught " + theOscMessage.addrPattern()); missMess = false;
track = theOscMessage.get(0).intValue();
device = theOscMessage.get(1).intValue();
dial = theOscMessage.get(2).intValue();
// we need to find out whether the params are int or float
switch(theOscMessage.typetag().charAt(3))
{ case 'i': rangeMinInt = theOscMessage.get(3).intValue();
paramIsFloat = false;
break;
case 'f': rangeMinFloat = theOscMessage.get(3).floatValue();
paramIsFloat = true;
break;
}
switch(theOscMessage.typetag().charAt(4))
{ case 'i': rangeMaxInt = theOscMessage.get(4).intValue();
paramIsFloat = false;
knob.setRange(rangeMinInt, rangeMaxInt);
println("Range: " + rangeMinInt + " " + rangeMaxInt);
break;
case 'f': rangeMaxFloat = theOscMessage.get(4).floatValue();
paramIsFloat = true;
knob.setRange(rangeMinFloat, rangeMaxFloat);
println("Range: " + rangeMinFloat + " " + rangeMaxFloat);
break;
}
}
// checking the params of the modified device
if(theOscMessage.checkAddrPattern("/live/device/param") == true)
{
print("Caught " + theOscMessage.addrPattern()); missMess = false;
track = theOscMessage.get(0).intValue();
device = theOscMessage.get(1).intValue();
dial = theOscMessage.get(2).intValue();
dialValue = theOscMessage.get(3).floatValue();
dialName = theOscMessage.get(4).stringValue();
knob.setValue(dialValue).setLabel(dialName);
getParamRange();
getTrackName();
getDeviceName();
}
if(missMess)
print("^^^ MISSED MSG ^^^");
print("\n");
return;
}
///////////////// knob /////////////////////
void knob(float theValue) {
// because we do not get an OSCEvent on switching from a selected parameter to a device, which can be conciderd as bug on behalf of AL,
// we here have to make the decision to either live with the inconsitency of a parameter being remotly changed whil in AL different
// device is selected, or we select the device from p5, which leads to an unselected parameter if we change it from within AL
// so, uncomment the next line or leave it in: it somewhat wierd any way.
oscP5.send("/live/track/device/param/view", new Object[] { new Integer(track), new Integer(device) }, myRemoteLocation);
oscP5.send("/live/device", new Object[] { new Integer(track) // Devicelist query on Track 1
, new Integer(device)
, new Integer(dial)
, new Float(theValue)
}
, myRemoteLocation
);
println("Send /live/device with " + theValue);
}
///////////////// div Getters /////////////////////
void getTrackName(){
oscP5.send("/live/name/track", new Object[] { new Integer(track) }, myRemoteLocation);
println("Send /live/name/track with " + track);
}
void getParamRange(){
oscP5.send("/live/device/range", new Object[] { new Integer(track) // Devicelist query on Track 1
, new Integer(device)
, new Integer(dial)
}
, myRemoteLocation
);
println("Send /live/device/range with ");
}
void getDeviceName(){
knob.show();
oscP5.send("/live/devicelist", new Object[] { new Integer(track) }, myRemoteLocation);
println("Send /live/devicelist");
}
void handleDial(){
dialName = "none";
knob.hide();
dial = 0;
dialValue = 0;
}
///////////////// displayMessage /////////////////////
void displayMessage(OscMessage theOscMessage){
print("### received an osc message with typtag: " + theOscMessage.typetag() +
", Length: " + theOscMessage.typetag().length() + " ### " + theOscMessage.addrPattern() + " with values: ");
for(int i=0; i<theOscMessage.typetag().length(); i++)
{ switch(theOscMessage.typetag().charAt(i))
{ case 'i': //int firstValue = theOscMessage.get(0).intValue();
print(theOscMessage.get(i).intValue() + " ");
break;
case 'f': print(theOscMessage.get(i).floatValue() + " ");
break;
case 's': print(theOscMessage.get(i).stringValue() + " ");
break;
}
}
print("\n");
}