How to get a bang when Push 2 is turned on?

Sébastien's icon

How can I get a bang when Push 2 is turned on in M4L? I tried sysexin. It works while patching but not when nested in Live. Is it possible with live.observer?

Capture-d’écran-2016-03-30-à-15.30.42.png
png
Sébastien's icon
Sébastien's icon

Well actually it works only when the Push 2 is loaded before you drop a device in a track.

patrickkidd's icon
Max Patch
Copy patch and select New From Clipboard in Max.

I periodically poll the control_surfaces object for changes and this works very well. This object will do that on a bang, or if you set the @auto part it will do it on a timer. It will output the id of Push or 0 if it doesn't exist. That way you get connect and disconnect:

And here is a javascript version of the same concept. You can pass callback functions in as options when you create the object like onPushConnected and onPushDisconnected:

var pushAPI = null; // global ref

// called once by [live.thisdevice]
function live_api_init() {
    if(pushAPI.PKLiveAPI.bInit) {
        return;
    }
    log('live_api_init()');
    pushAPI.PKLiveAPI.bInit = true;
    pushAPI.live_api_init();
}

function Push(options) {

    pushAPI = this; // retain reference at least for timers, but also for glboal access

    options = typeof options !== 'undefined' ? options : {};

    this.bLiveAPIInit = false;
    this.live_api_init = function() {
        if(this.bLiveAPIInit) {
            return;
        }
        if(options.onLiveAPIInit) {
            options.onLiveAPIInit();
        }
        this.bLiveAPIInit = true;
        this.poll();
    };

    this.poll = function() {
        if(this.bLiveAPIInit == false) {
            return;
        }
        var exists = false;
          for(var i=0; i < 6; i++) {
                var api = new LiveAPI("control_surfaces " + i);
            if(api.type.indexOf('Push') == 0) { // 'Push' || 'Push2'
                var thisId = this.api != null ? this.api.id : null;
                if(this.api && this.api.id == api.id) {
                    exists = true;
                } else if(!this.api || this.api.id != api.id) {
                    this.api = api;
                    options.api = api;
                    exists = true;
                    if(options.onPushConnected) {
                        options.onPushConnected();
                    }
                }
            }
          }
        if(!exists && this.api) {
            this.api = null;
            if(options.onPushDisconnected) {
                options.onPushDisconnected();
            }
        }
    },

    this.init = function() {
        this.api = null;
        this.dead = false;

        // just check a little time after the live api might be initialized
        // in case we are in js dev mode and no [live.thisdevice] bang is coming.
        // if a [live.thisdevice] bang already came then running it again won't hurt.
        // this.pollCheck = new Task(function() {
        //     this.poll();
        //     if(this.bPolling == false && this.pollRepeat) {
        //         this.pollRepeat.repeat();
        //         this.bPolling = true;
        //     }
        // }, this);
        // this.pollCheck.schedule(1500);
        
        // the regular polling task, started above
        this.pollRepeat = new Task(this.poll, this);
        this.pollRepeat.interval = 1000;
        this.pollRepeat.repeat();

        // store in a global to recall if [live.thisdevice] sent bang
        // before script is re-loaded with autowatch.
        if(this.PKLiveAPI.bInit === undefined) {
            this.PKLiveAPI.bInit = false;
        } else if(this.PKLiveAPI.bInit) {
            log('Push.init: calling live_api_init');
            this.live_api_init();
        }

        this.bPolling = false;
    },
    
    this.PKLiveAPI = new Global('PKLiveAPI');

    this.initTask = new Task(function() {
        this.init();
    }, this);
    this.initTask.schedule(0); // defer to avoid null 'app' references in api callbacks

    return options;
}

var push = new Push({
onPushConnected: function() {},
onPushDisconnected: function() {}
});

Sébastien's icon

Hi PATRICKKIDD,

The polling with metro method sends out the control_surfaces $1 path id to quickly after a bang to grab_control $1 when turning on the Push 2. Which results in errors. That's why I was looking into the sysexin message to match 240 0 33 29 1 1 10 0 247.

But in conjunction with the polling what finally works for me is to live.observe the implicit_arm property (use only by Push for now) of a selected_track and gate the Push 2 path id and thus the call grab_control $1 until the value of the implicit_arm turns to 1. That way I can grab_contol $1 automatically after Push 2 is turn on and loaded.

patrickkidd's icon

Ah, I'm familiar with that type of problem. The python midi remote scripts don't create all of the objects in the hierarchy when th device is created (I.e. Connected) and it waits for a particular event to initialize them. At least this was true for push 1, probably for performance reasons. For example the SessionController wasn't created until you actually entered session mode on push. Quite annoying for programmers. So you have to find and wait for a particular event to create the object, as you have done.