Skip to content

Instantly share code, notes, and snippets.

@Lokua
Last active August 29, 2015 14:06
Show Gist options
  • Save Lokua/09b6f56442864ff5f2c3 to your computer and use it in GitHub Desktop.
Save Lokua/09b6f56442864ff5f2c3 to your computer and use it in GitHub Desktop.
max for live audio input routing helper script
/*
* Copyright 2014 Joshua Kleckner, http://lokua.net
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var Logger = new Global('Logger'),
log = Logger.log,
err = Logger.err;
Logger.on = true; // set to `true` to enable debugging
log('\t\tHello from audio-input-router.js version 1.0.2 | contact: dev@lokua.net');
autowatch = 1;
inlets = 2;
outlets = 2;
var api,
routes = [],
currentRouteIndex = 0,
subRoutes = [],
OBJ_COUNT = 32,
routesMenu = patcher.getnamed('routesMenu'),
subRoutesMenu = patcher.getnamed('subRoutesMenu'),
subRoutesGate = patcher.getnamed('subRoutesGate');
/**
* Data structure to hold sub_routes and last stored index
*
* @param {String} sub_routes api path input_sub_routings
* @param {Number} index the last selected sub_route from live.menu
*/
function SubRoute(sub_routes, index) {
this.sub_routes = sub_routes;
this.index = index || 0;
}
/**
* @return {String} the last stored sub_route
*/
SubRoute.prototype.last = function() {
return this.sub_routes[this.index];
}
SubRoute.prototype.toString = function() {
return 'sub_routes: ' + this.sub_routes + ', index: ' + this.index;
}
/**
* (re)initialize and populate everything
*/
function bang() {
// set-up LiveAPI
api = new LiveAPI(_highlightCurrent, 'this_device canonical_parent');
this_name = api.get('name');
api.property = 'current_input_routing';
if (inlet === 0) {
_clear();
_configureRoutesAndSubRoutes();
_populateRoutesMenu();
_populateSubRoutesMenu();
}
// update the proxy routesMenu current item
routesMenu.message('set', api.get('current_input_routing'));
log('initialized');
}
function _clear() {
routes = [];
subRoutes = [];
for (var i = 0; i < OBJ_COUNT; i++) {
var mess = 'message_'+i,
mobj = patcher.getnamed(mess);
mobj.message('set');
mobj.message('hidden', 1);
patcher.getnamed('button_'+i).message('hidden', 1);
}
}
/**
* populate the routes and corresponding subRoutes arrays.
* This excludes the track this device is on and tracks that have their
* output set to 'None'.
*/
function _configureRoutesAndSubRoutes() {
routes.push('Ext. In');
subRoutes.push(new SubRoute(['1/2', '1', '2'], 0));
routes.push('Resampling');
subRoutes.push(new SubRoute(['none']));
_pushType('tracks');
_pushType('return_tracks');
routes.push('Master');
subRoutes.push(new SubRoute(['Pre FX', 'Post FX', 'Post Mixer'], 2));
routes.push('No Input');
subRoutes.push(new SubRoute(['none']));
api.path = 'this_device canonical_parent';
}
/**
* Internal helper used in _configureRoutesAndSubRoutes
*
* @param {String} type 'return_tracks' or 'tracks'
*/
function _pushType(type) {
api.path = 'this_device canonical_parent';
var this_name = api.get('name');
for (var i = 0, len = api.getcount(type); i < len; i++) {
api.path = 'live_set ' + type + ' ' + i;
name = api.get('name').toString();
hasOut = api.get('current_output_routing') != 'None';
if ((this_name != name) && hasOut) {
routes.push(name);
var sr = new SubRoute(['Pre FX', 'Post FX', 'Post Mixer'], 2);
subRoutes.push(sr);
}
}
}
/**
* fills the UI messages and corresponding proxy menu with input routes;
* adjusts device and panels widths to reflect visible button/message columns.
* Requires that routes have been configured.
*/
function _populateRoutesMenu() {
var deviceWidth = 222, // min width including update+about column and first routes column
i = 0, len = routes.length;
for (; i < len; i++) {
var mess = 'message_'+i,
mobj = patcher.getnamed(mess);
mobj.message('set', routes[i]);
mobj.message('hidden', 0);
routesMenu.message('append', routes[i]);
patcher.getnamed('button_'+i).message('hidden', 0);
if (i && i % 8 === 0) deviceWidth += 130;
}
// adjust widths to reflect visible columns
patcher.getnamed('this_device').message('setwidth', deviceWidth);
patcher.getnamed('panel').message('presentation_rect', 8, 6, deviceWidth-18, 158);
}
/**
* populate the subRoutesMenu with the current inputs corresponding sub_routes.
* Requires the subRoutes index to correctly match currentRouteIndex
*/
function _populateSubRoutesMenu() {
var r0 = subRoutes[_updateCurrentRouteIndex()].sub_routes[0];
// this input route does not have sub_routes ("Resampling" or "No Input")
if (r0 == 'none') {
subRoutesMenu.message('activebgcolor', 0, 0, 0, 0);
subRoutesMenu.message('textcolor', 0, 0, 0, 0);
subRoutesMenu.message('ignoreclick', 1);
return;
}
subRoutesMenu.message('_parameter_range', sr);
subRoutesMenu.message('activebgcolor', 0.05, 0.91, 0.91, 1);
subRoutesMenu.message('textcolor', 0, 0, 0, 1);
subRoutesMenu.message('ignoreclick', 0);
}
/**
* highlights the currently active route message object;
* un-highlights the rest
*/
function _highlightCurrent() {
if (api && api.type != 'Track') return;
var current = api.get('current_input_routing'),
i = 0, len = routes.length, m;
for (; i < len; i++) {
m = patcher.getnamed('message_'+i);
m.message('textcolor', 0, 0, 0, 1);
if (routes[i] == current) {
m.message('textcolor', 0.05, 0.91, 0.91, 1);
}
}
}
/**
* @return {Number} the current index of the route (as placed in proxy menu)
*/
function _updateCurrentRouteIndex() {
if (routes === undefined) return currentRouteIndex;
var current_input_route = api.get('current_input_routing'),
i = 0, len = routes.length;
for (; i < len; i++) {
if (routes[i] == current_input_route) {
currentRouteIndex = i;
return currentRouteIndex;
}
}
return currentRouteIndex;
}
/**
* Set this tracks audio input route, configure corresponding
* sub_route.
*
* @param {String} route the current input_route selected
* @param {Number} index the current item index from routesMenu
*/
function setroute(route, index) {
_closeGate();
api.path = 'this_device canonical_parent';
api.set('current_input_routing', route);
currentRouteIndex = index;
_populateSubRoutesMenu();
_openGate();
subRoutesMenu.message('symbol', subRoutes[index].last());
}
/**
* close subRoutesGate to avoid calling setsubroutes when
* populating the menu
*/
function _closeGate() {
subRoutesGate.message(0);
}
function _openGate() {
subRoutesGate.message(1);
}
/**
* This is an incoming message from the subRoutesMenu. Sets this track's sub_route.
*
* @param {String} sub the sub_route
* @param {Number} index the sub_route index (not to be confused with route index),
* which will be stored for later updating when the main route changes
* ( subRoutes[currentRouteIndex].index )
*/
function setsubroute(sub_route, index) {
api.set('current_input_sub_routing', sub_route + '');
subRoutes[currentRouteIndex].index = index;
}
function _debug() {
log('_debug >>');
log('\tapi > input_routings: {0}', api.get('input_routings'));
log('\tapi > input_sub_routings: {0}', api.get('input_sub_routings'));
log('\tapi > current_input_routing: {0}', api.get('current_input_routing'));
log('\tapi > current_input_sub_routing: {0}', api.get('current_input_sub_routing'));
log('\tcurrentRouteIndex: {0}', currentRouteIndex);
log('\troutes: {0}', routes);
subRoutes.forEach(function(r, i) {
log('\tsubRoutes[{0}].toString(): {1}', i, r.toString());
});
log('\troutes[currentRouteIndex]: {0}', routes[currentRouteIndex]);
log('\tsubRoutes[currentRouteIndex]: {0}', subRoutes[currentRouteIndex]);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment