observe devices inside rack chain
Hi,
Im trying to observe the devices inside a rack, when theyre deleted or added. I have the following code, which works most of the time, but it seems to get stuck listening to the last added chain for some reason. If there is a device inside one of the other chains, this is not observed.
Would be grateful if anyone has any pointers.
Cheers
N
ive just tested this in ableton live 9 and found that this works as expected. Something must have changed between live 8 and 9 that has resulted in this functionality now working. Is there anyone that can confirm this?
The code basically bangs when something is added or deleted in a rack chain, or track.
Also, would be really grateful to find out if there is a way of doing this in js. ie to observe all levels of chains, rather than the 1st level.
newtfish, I posted a JS in answer to your last post about iterating devices in chains which demonstrates how to find all of the devices. You should be able to install observers along the way and get what you want.
Best, Jeremy
cool thx for this jeremy, your js solution is amazing! :)
Im not that great with js, so i try the following code, with callbacks, but I dont get any output when observed (device is deleted or added). Is there something im doing drastically wrong? grateful for any help.
autowatch = 1;
outlets = 4;
function bang()
{
var api = getTrackPath();
if (!api) return;
iterateDevices(api);
}
function iterateDevices(api)
{
var count = api.getcount("devices");
var apiPath = dequotePath(api);
for (var i = 0; i < count; i++) {
var deviceApi = new LiveAPI(callback,apiPath + " devices " + i);
if (deviceApi) {
var deviceName = deviceApi.get("name");
var deviceId = deviceApi.id;
var deviceApiPath = dequotePath(deviceApi);
var chainsCount;
var chainApi;
var j;
// post("found device " + deviceName + " at path '" + deviceApiPath + "'n");
outlet(0, "append", deviceName);
outlet(1, "append", deviceId);
if (deviceApi.get("can_have_chains") == 1) {
chainsCount = deviceApi.getcount("chains"); // only racks have chains
for (j = 0; j < chainsCount; j++) {
chainApi = new LiveAPI(callback,deviceApiPath + " chains " + j);
iterateDevices(chainApi);
}
chainsCount = deviceApi.getcount("return_chains"); // only racks have chains
for (j = 0; j < chainsCount; j++) {
chainApi = new LiveAPI(callback,deviceApiPath + " return_chains " + j);
iterateDevices(chainApi);
}
}
}
}
}
iterateDevices.local = 1;
function getTrackPath()
{
var api = new LiveAPI("this_device");
if (api) {
trackPath = dequotePath(api);
trackPath = trackPath.replace(/s+devices.*?$/g, ""); // remove 'devices N' at the end
return new LiveAPI(trackPath);
}
return null;
}
getTrackPath.local = 1;
function dequotePath(api)
{
return api.path.replace(/"/g, ""); // remove quotes
}
dequotePath.local = 1;
function callback(args)
{
outlet(0,args[1]);
outlet(1,this.id);
post(args);
}
Try this. I might have mangled your additions a little, but I think it's right. There may be a couple of things going wrong in your version:
- in addition to the callback function, you need to assign the LiveAPI object's "property" attribute to the property you want to observe. In the case of the track, this is "devices", in the case of a rack device, this is "chains", in the case of a chain, this is "devices" and so on.
- once a JavaScript object goes out of scope it can be garbage collected if it's not in use elsewhere. So I'm stuffing the objects that we're observing into a global array to be certain that they can't disappear.
Note that to observe both chains and return chains, you actually need 2 LiveAPI objects (each object can only observe 1 property). I left this as an exercise to the reader.
Best, Jeremy
thanks Jeremy,
There seems to be a bit of a problem with the unquotedpath in this script? Im wondering what unquotedpath is? Is this something built into the object? The first error I get is when using unquotedpath, which results in trackPath throwing an undefined error.
Oh sorry, I was testing something here and forgot to change it back. Remove that line and uncomment the one below it.
Best, Jeremy
This is really amazing, didnt realise js was so useful. Thanks so much Jeremy.
The one thing I have noticed is that when a device is added in an existing chain, the device id is output. However, if I drag a device to create a new chain (automatically creating a new chain by dragging), then it only outputs the chains and not the new device id. I also noticed this behaviour when using the max objects, it doesnt seem to notice the new device id. Maybe this is a feature that im missing. Ideally, I would really like to just refresh the individual id that is added/deleted, but this works great for now, so I can just check if something has changed within a rack and refresh all device ids on the track.
Cheers Jeremy :)
yes, as Im looking deeper into this, im finding that when a new chain is added, the array needs refreshing, but live cant seem to tell me when this happens. I thought that maybe changing:
for (j = 0; j < chainsCount; j++) {
to
for (j = 0; j
might work, so the array refreshes on the first chain. However, this doesnt seem to be the case. Im afraid this "chain 0" thing is becoming a bit elusive for me, it seems to work the same in max objects as it does in js and the liveapi cannot seem to see the past the first chain.
Hi,
Ive been using the following code to output a bang when any device is added or deleted whether it is inside a rack chain, or not. This works ok most of the time. However, when you add a new chain, the devices inside the "new chain" are not observed. Its strange, because it is observing the chain and therefore must be calling the function "iterateDevices()". However, it seems not to be triggering this function inside a chain. Would be grateful for some pointers as to why new devices are not being observed inside a new chain.
autowatch = 1;
outlets = 1;
var apiArray = new Array();
function iterate(x){
var a = "id " + x;
//delete api.cb_private;
var api = new LiveAPI( a);
iterateDevices(api);
}
function iterateDevices(api)
api.property = "devices"; // we want to observe the devices tuple
apiArray.push(api); // push it onto our array so it doesn't get GC'd
var count = api.getcount("devices");
var apiPath = dequotePath(api);
for (var i = 0; i < count; i++) {
var deviceApi = new LiveAPI(callback, apiPath + " devices " + i);
if (deviceApi) {
var deviceName = deviceApi.get("name");
var deviceId = deviceApi.id;
var deviceApiPath = dequotePath(deviceApi);
var chainsCount;
var chainApi;
var j;
if (deviceApi.get("can_have_chains") == 1) {
deviceApi.property = "chains"; // we want to observe the chains tuple
apiArray.push(deviceApi); // push it onto our array so it doesn't get GC'd
chainsCount = deviceApi.getcount("chains"); // only racks have chains
for (j = 0; j < chainsCount; j++) {
chainApi = new LiveAPI(callback, deviceApiPath + " chains " + j);
if (chainApi) {
iterateDevices(chainApi);
}
}
chainsCount = deviceApi.getcount("return_chains"); // only racks have chains
for (j = 0; j < chainsCount; j++) {
chainApi = new LiveAPI(callback, deviceApiPath + " return_chains " + j);
if (chainApi) {
iterateDevices(chainApi);
}
}
}
}
}
}
iterateDevices.local = 1;
function dequotePath(api)
{
//return api.unquotedpath;
return api.path.replace(/\"/g, ""); // remove quotes
}
dequotePath.local = 1;
function callback(args)
{
//outlet(0,args[1]);
//outlet(1,this.id);
//post(args[1]);
if(args){
//outlet(0,'bang');
}
//post();
outlet(0,'bang');
}
Hi Newtfish,
Have you had any luck with observing tracks/chains for device changes using Javascript?
I'm looking into it again and was just wondering how you got on, if you have any tips etc..
Cheers
Chris Gough
Hi Chris,
A little luck. The process I built involves using the above script 2 times (one time for observing whether any devices changed (moved, inserted, deleted) on a track, and another time for refreshing all devices on a track) - The first script triggers the second.
There is a bug which C74 know about, where if you drag a device to the 1st position on a track - no change will be recognised
Hi Newtfish,
I got my code working to get all the devices in tracks and the first chain of racks.
It uses a few tricks including checking for already observed ids to eliminate duplications, suppressing the first callback when setting the observer and using the second output into a deferlow object and back into the input to trigger a re-test of the script.
Here's a copy of the script...
autowatch = 1;
outlets = 2;
var DevCallbackArray = new Array();
var callbackIDArray = new Array();
var devListArray = new Array(); //list of devices in tracks and first chain of rack
//----------------------------------------------------------
function bang()
{
var ignoreId = 0;
devListArray.length = 0;
iterTracks();
post(callbackIDArray, "\n");
outlet(0,devListArray, "\n");
}
//----------------------------------------------------------
function iterTracks()
{
var Song = new LiveAPI("live_set");
observe_this(Song.id, "tracks"); //observe for new/deleted/moved tracks
var Tracks = Song.getcount("tracks");
for (var Track = 0; Track < Tracks; Track++)
{
var api = new LiveAPI("live_set tracks " + Track);
if (!api) {post("Break!\n"); return;}
iterateDevices(api);
}
}
iterTracks.local = 1;
//----------------------------------------------------------
function iterateDevices(api)
{
observe_this(api.id, "devices"); //observe for new/deleted/moved devices in path
var count = api.getcount("devices");
var apiPath = dequotePath(api);
for (var i = 0; i < count; i++)
{
var deviceApi = new LiveAPI(apiPath + " devices " + i);
deviceApi.property = "devices";
if (!deviceApi) {post("Break!\n"); return;}
var deviceId = deviceApi.id;
var deviceApiPath = dequotePath(deviceApi);
var chainsCount;
var j;
if (deviceApi.get("can_have_chains") == 1) //if rack
{
observe_this(deviceApi.id, "chains"); //observe for new/deleted/moved chains in rack
chainsCount = deviceApi.getcount("chains");
if(chainsCount > 0)
{
var chainApi = new LiveAPI(deviceApiPath + " chains 0"); //if there is a chain in rack send chain to iterateDevices()
if (!chainApi) {post("Break!\n"); return;}
iterateDevices(chainApi);
}
}
else
{
devListArray.push(deviceApi.id); //add device id to list
}
}
}
iterateDevices.local = 1;
//----------------------------------------------------------
function observe_this(ID, observeProperty)
{
if (callbackIDArray.indexOf(ID) == -1) //If id not observed...
{
callbackIDArray.push(ID); // ...Add id to observed list
ignoreId = 1;
var DevCallback = new LiveAPI(callback, "id " + ID);
DevCallback.property = observeProperty;
DevCallbackArray.push(DevCallback);}
}
observe_this.local = 1;
//----------------------------------------------------------
function callback(args)
{
if (args[0] != "id")
{
if (ignoreId == 1)
{
ignoreId = 0;
}
else
{
outlet(1,"bang");
}
}
}
callback.local = 1;
//----------------------------------------------------------
function dequotePath(api)
{
return api.path.replace(/\"/g, ""); // remove quotes
}
dequotePath.local = 1;
//----------------------------------------------------------
Hi Chris,
Nice script - I will try this out over the next few days. Just wondering, have you tried moving a device, out of a rack and making it the first device on the track? I noticed that there were significant problems with this. I think it might be down to the LiveAPI in general but it would be great to know for sure.
Cheers
N
Think it's working for me, I'm using Max 7.0.1 at the moment.