769 lines
28 KiB
JavaScript
769 lines
28 KiB
JavaScript
|
'use strict'
|
||
|
|
||
|
import {
|
||
|
mix,
|
||
|
getMix,
|
||
|
mixObject,
|
||
|
} from './utils.js'
|
||
|
|
||
|
const PhysicalMidiMapping = {
|
||
|
"Launch Control MIDI 1": {
|
||
|
"knobs": [
|
||
|
21, 22, 23, 24, 25, 26, 27, 28, // first row
|
||
|
41, 42, 43, 44, 45, 46, 47, 48, // second row
|
||
|
],
|
||
|
"buttons": [
|
||
|
9, 10, 11, 12, 25, 26, 27, 28,
|
||
|
],
|
||
|
"arrows": [
|
||
|
114, 115, 116, 117, // up, down, left, right
|
||
|
]
|
||
|
}
|
||
|
};
|
||
|
|
||
|
const generalControl = {
|
||
|
"Launch Control MIDI 1": {}
|
||
|
};
|
||
|
|
||
|
const MidiController = function() {
|
||
|
window.mixObject = mixObject;
|
||
|
|
||
|
const element = document.querySelector('#midiController');
|
||
|
const openCloseButton = document.querySelector('#midi_open');
|
||
|
const buttons = element.querySelector(".buttons");
|
||
|
|
||
|
let inputs;
|
||
|
let outputs;
|
||
|
|
||
|
let isPanelOpen = false;
|
||
|
let layers = [];
|
||
|
|
||
|
let debugLog = false;
|
||
|
let isInitialized = false;
|
||
|
|
||
|
let activeLayer = 0;
|
||
|
let activePropSet = 0;
|
||
|
window.activeLayer = activeLayer;
|
||
|
let artboardWidth = 1920;
|
||
|
let lastSelectionPoint = -1;
|
||
|
let playbackSpeed = 1;
|
||
|
|
||
|
let setFontVariation = (layer, layerIndex, button, midiValue) => {
|
||
|
if (layer.theatreObject.value.hasOwnProperty('fontVariationAxes') &&
|
||
|
typeof layer.theatreObject.value.fontVariationAxes === 'object') {
|
||
|
const axes = layer.theatreObject.value.fontVariationAxes;
|
||
|
const index = button - 46;
|
||
|
const keys = Object.keys(axes);
|
||
|
if (index < keys.length) {
|
||
|
const key = keys[index];
|
||
|
const axesProps = layer.props.fontVariationAxes.props[key];
|
||
|
const min = axesProps.range[0];
|
||
|
const max = axesProps.range[1];
|
||
|
const v = (midiValue / 127.0) * (max - min) + min;
|
||
|
if (!currentValues[layerIndex].hasOwnProperty('fontVariationAxes')) {
|
||
|
currentValues[layerIndex].fontVariationAxes = {};
|
||
|
}
|
||
|
currentValues[layerIndex].fontVariationAxes[key] = v;
|
||
|
//tp.studio.transaction(({
|
||
|
//set
|
||
|
//}) => {
|
||
|
//set(layer.theatreObject.props.fontVariationAxes[key], v);
|
||
|
//});
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
let setLetterDelays = (layer, layerIndex, button, midiValue) => {
|
||
|
if (layer.theatreObject.value.hasOwnProperty('letterDelays')) {
|
||
|
const letterDelays = layer.theatreObject.value.letterDelays;
|
||
|
const keys = Object.keys(letterDelays);
|
||
|
const min = 0;
|
||
|
const max = 2000;
|
||
|
const v = (midiValue / 127.0) * (max - min) + min;
|
||
|
//console.log('MidiController::setLetterDelays - font has letterDelays', JSON.parse(JSON.stringify(letterDelays)));
|
||
|
|
||
|
for (let i = 0; i < keys.length; i++) {
|
||
|
const key = keys[i];
|
||
|
if (typeof letterDelays[key] === 'object') {
|
||
|
const subKeys = Object.keys(letterDelays[key]);
|
||
|
for (let si = 0; si < subKeys.length; si++) {
|
||
|
const subKey = subKeys[si];
|
||
|
if (!currentValues[layerIndex].hasOwnProperty('letterDelays')) {
|
||
|
currentValues[layerIndex].letterDelays = {};
|
||
|
}
|
||
|
if (!currentValues[layerIndex].letterDelays.hasOwnProperty(key)) {
|
||
|
currentValues[layerIndex].letterDelays[key] = {};
|
||
|
}
|
||
|
currentValues[layerIndex].letterDelays[key][subKey] = v;
|
||
|
//tp.studio.transaction(({
|
||
|
//set
|
||
|
//}) => {
|
||
|
//set(layer.theatreObject.props.letterDelays[key][subKey], v);
|
||
|
//});
|
||
|
}
|
||
|
} else {
|
||
|
if (!currentValues[layerIndex].hasOwnProperty('letterDelays')) {
|
||
|
currentValues[layerIndex].letterDelays = {};
|
||
|
}
|
||
|
currentValues[layerIndex].letterDelays[key] = v;
|
||
|
//tp.studio.transaction(({
|
||
|
//set
|
||
|
//}) => {
|
||
|
//set(layer.theatreObject.props.letterDelays[key], v);
|
||
|
//});
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
//console.log('MidiController::setLetterDelays - font has no letterDelays');
|
||
|
}
|
||
|
};
|
||
|
let mirror_x = false;
|
||
|
let mirror_y = false;
|
||
|
let mirror_xy = false;
|
||
|
let setMirror = (button, midiValue) => {
|
||
|
}
|
||
|
|
||
|
let setLayer = (button, midiValue) => {
|
||
|
const layers = getLayers();
|
||
|
if (button === 116 && midiValue === 127) {
|
||
|
activeLayer = (activeLayer + 1) % layers.length;
|
||
|
} else if (button === 117 && midiValue === 127) {
|
||
|
activeLayer = (activeLayer - 1 + layers.length) % layers.length;
|
||
|
}
|
||
|
layers[activeLayer].showBoundingBoxDiv();
|
||
|
setTimeout(() => {
|
||
|
layers[activeLayer].hideBoundingBoxDiv();
|
||
|
}, 100);
|
||
|
};
|
||
|
|
||
|
let setProject = (button, midiValue) => {
|
||
|
const projects = tp.listProjects();
|
||
|
const activeProject = projects.indexOf(tp.sheet.project.address.projectId);
|
||
|
let direction;
|
||
|
if (button === 114 && midiValue === 127) {
|
||
|
direction = 1;
|
||
|
} else if (button === 115 && midiValue === 127) {
|
||
|
direction = -1;
|
||
|
}
|
||
|
const nextProjectIndex = (activeProject + direction + projects.length) % projects.length;
|
||
|
const nextProject = projects[nextProjectIndex];
|
||
|
tp.reloadToProject(nextProject, true);
|
||
|
};
|
||
|
|
||
|
let setBackgroundOpacity = (knob, midiValue) => {
|
||
|
let prop = 'backgroundColor.a';
|
||
|
setValue(getArtboard(), layers.length, prop, midiValue, [0, 1]);
|
||
|
};
|
||
|
|
||
|
let setBackgroundColor = (button, midiValue) => {
|
||
|
let pr = 'backgroundColor.r';
|
||
|
let pg = 'backgroundColor.g';
|
||
|
let pb = 'backgroundColor.b';
|
||
|
let r = Math.random() * 127.0;
|
||
|
let g = Math.random() * 127.0;
|
||
|
let b = Math.random() * 127.0;
|
||
|
setValue(getArtboard(), layers.length, pr, r, [0, 1]);
|
||
|
setValue(getArtboard(), layers.length, pg, g, [0, 1]);
|
||
|
setValue(getArtboard(), layers.length, pb, b, [0, 1]);
|
||
|
};
|
||
|
|
||
|
let setSpeed = (knob, midiValue) => {
|
||
|
if (midiValue >= 62 && midiValue <= 64) {
|
||
|
tp.sheet.sequence.pause();
|
||
|
} else {
|
||
|
const min = -6;
|
||
|
const max = 6;
|
||
|
let v = (midiValue / 127.0) * (max - min) + min;
|
||
|
if (v > 0) {
|
||
|
tp.sheet.sequence.play({
|
||
|
iterationCount: Infinity,
|
||
|
rate: v
|
||
|
});
|
||
|
} else if (v < 0) {
|
||
|
tp.sheet.sequence.play({
|
||
|
direction: 'reverse',
|
||
|
iterationCount: Infinity,
|
||
|
rate: Math.abs(v)
|
||
|
});
|
||
|
}
|
||
|
playbackSpeed = Math.abs(v);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
const sentValues = [];
|
||
|
const sentMidiValues = [];
|
||
|
const currentValues = [];
|
||
|
const valueBuffer = [];
|
||
|
const populateValueBuffer = (_layers) => {
|
||
|
_layers.forEach((layer) => {
|
||
|
currentValues.push({});
|
||
|
sentValues.push({});
|
||
|
sentMidiValues.push({});
|
||
|
valueBuffer.push(new Map());
|
||
|
|
||
|
//console.log('pushed -------------------> ', _layers.length, JSON.parse(JSON.stringify(layer.theatreObject.value)));
|
||
|
});
|
||
|
// artboard
|
||
|
currentValues.push({});
|
||
|
sentValues.push({});
|
||
|
sentMidiValues.push({});
|
||
|
valueBuffer.push(new Map());
|
||
|
};
|
||
|
const addValuesToBuffer = (layerIndex, values, time_s, start_time_s) => {
|
||
|
const layerValueBuffer = valueBuffer[layerIndex];
|
||
|
const copiedValues = JSON.parse(JSON.stringify(values));
|
||
|
if (start_time_s !== -1) {
|
||
|
layerValueBuffer.forEach((value, value_time_s) => {
|
||
|
if (value_time_s > start_time_s && value_time_s <= time_s) {
|
||
|
const keys = Object.keys(values);
|
||
|
for (let k = 0; k < keys.length; k++) {
|
||
|
delete layerValueBuffer.get(value_time_s)[keys[k]];
|
||
|
if (Object.keys(layerValueBuffer.get(value_time_s)).length === 0) {
|
||
|
layerValueBuffer.delete(value_time_s);
|
||
|
break;
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
if (layerValueBuffer.has(time_s)) {
|
||
|
layerValueBuffer.set(time_s, {...layerValueBuffer.get(time_s), ...copiedValues});
|
||
|
} else {
|
||
|
layerValueBuffer.set(time_s, copiedValues);
|
||
|
}
|
||
|
};
|
||
|
const getValuesFromBuffer = (layerIndex, time_s) => {
|
||
|
if (valueBuffer[layerIndex].size === 0) {
|
||
|
return {};
|
||
|
} else {
|
||
|
valueBuffer[layerIndex] = new Map([...valueBuffer[layerIndex].entries()].sort());
|
||
|
let mergedValues = {};
|
||
|
let didMergeValues = {};
|
||
|
valueBuffer[layerIndex].forEach((value, value_time_s) => {
|
||
|
if (value_time_s < time_s) {
|
||
|
mergedValues = {...mergedValues, ...value};
|
||
|
} else {
|
||
|
if (Object.keys(didMergeValues).length === 0) {
|
||
|
didMergeValues = JSON.parse(JSON.stringify(mergedValues));
|
||
|
}
|
||
|
Object.keys(value).forEach((key) => {
|
||
|
if(!didMergeValues.hasOwnProperty(key)) {
|
||
|
mergedValues[key] = value[key];
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
});
|
||
|
return mergedValues;
|
||
|
}
|
||
|
};
|
||
|
this.getValuesFromBuffer = getValuesFromBuffer;
|
||
|
|
||
|
this.currentValue = currentValues;
|
||
|
this.valueBuffer = valueBuffer;
|
||
|
|
||
|
const smoothed = {
|
||
|
184: [21, 22, 23, 24, 26, 27, 28, 41, 42, 43, 44, 45, 46, 47, 48]
|
||
|
};
|
||
|
const ledButtonRowStatus = [0, 0, 0, 0, 0, 0, 0, 0];
|
||
|
const ledButtonRowMapping = [9, 10, 11, 12, 25, 26, 27, 28];
|
||
|
const ledColors = [ 0, 12, 13, 15, 29, 63, 62, 28, 60 ];
|
||
|
const setLed = (button, color, statusCode = 152) => {
|
||
|
outputs.forEach((midiOutput) => {
|
||
|
if (midiOutput.name === "Launch Control MIDI 1") {
|
||
|
midiOutput.send([statusCode, button, color]);
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
this.ledTimeColor = 2;
|
||
|
this.setLed = setLed;
|
||
|
this.ledMapping = ledButtonRowMapping;
|
||
|
this.ledColors = ledColors;
|
||
|
const mapping = {
|
||
|
"Launch Control MIDI 1": {
|
||
|
'general': {
|
||
|
184: { // status code
|
||
|
114: setProject,
|
||
|
115: setProject,
|
||
|
116: setLayer,
|
||
|
117: setLayer,
|
||
|
28: setSpeed,
|
||
|
25: setBackgroundOpacity,
|
||
|
},
|
||
|
// buttons
|
||
|
152: { // down
|
||
|
9: setBackgroundColor,
|
||
|
10: setBackgroundColor,
|
||
|
11: setBackgroundColor,
|
||
|
12: setBackgroundColor,
|
||
|
25: setBackgroundColor,
|
||
|
26: setBackgroundColor,
|
||
|
27: setBackgroundColor,
|
||
|
28: setBackgroundColor,
|
||
|
},
|
||
|
},
|
||
|
'props': [{
|
||
|
// knobs
|
||
|
184: {
|
||
|
// first row
|
||
|
21: ['x', [0, 1920]],
|
||
|
22: ['y', [0, 1080]],
|
||
|
23: ['fontSize_px', [-1000, 1000]],
|
||
|
24: ['rotation', [-360, 360]],
|
||
|
25: ['transformOrigin', ['top_left', 'top_right', 'center', 'bottom_left', 'bottom_right']],
|
||
|
26: ['letterSpacing', [-3, 3]],
|
||
|
27: ['lineHeight', [0, 10]],
|
||
|
// 28 free
|
||
|
// second row
|
||
|
41: ['color.r', [0, 1]],
|
||
|
42: ['color.g', [0, 1]],
|
||
|
43: ['color.b', [0, 1]],
|
||
|
44: ['color.a', [0, 1]],
|
||
|
45: setLetterDelays,
|
||
|
46: setFontVariation,
|
||
|
47: setFontVariation,
|
||
|
48: setFontVariation,
|
||
|
},
|
||
|
152: { // down
|
||
|
//9: setPropsSet,
|
||
|
//10: setPropsSet,
|
||
|
//11:
|
||
|
//12:
|
||
|
//25:
|
||
|
//26:
|
||
|
//27:
|
||
|
//28:
|
||
|
},
|
||
|
136: { // up
|
||
|
//9:
|
||
|
//10:
|
||
|
//11:
|
||
|
//12:
|
||
|
//25:
|
||
|
//26:
|
||
|
//27:
|
||
|
//28:
|
||
|
}
|
||
|
}]
|
||
|
}
|
||
|
};
|
||
|
this.mapping = mapping;
|
||
|
let updatingMidiValues = {};
|
||
|
let appliedMidiValues = {};
|
||
|
let doApplyMidiValues = true;
|
||
|
window.applyMidiTimeoutMs = 30;
|
||
|
|
||
|
this.mix = mix;
|
||
|
const applyMidiValuesInterval = () => {
|
||
|
if (doApplyMidiValues) {
|
||
|
// apply midi values
|
||
|
const device = "Launch Control MIDI 1";
|
||
|
const mkeys = Object.keys(updatingMidiValues);
|
||
|
if (updatingMidiValues.hasOwnProperty(device)) {
|
||
|
const keys = Object.keys(updatingMidiValues[device]);
|
||
|
for (let i = 0; i < keys.length; i++) {
|
||
|
const key = parseInt(keys[i]);
|
||
|
const statusCode = updatingMidiValues[device][key][0];
|
||
|
const midiValue = updatingMidiValues[device][key][1];
|
||
|
if (!appliedMidiValues[device].hasOwnProperty(keys[i]) ||
|
||
|
appliedMidiValues[device][keys[i]] !== midiValue) {
|
||
|
if (typeof mapping[device].general[statusCode][key] === 'function') {
|
||
|
mapping[device].general[statusCode][key](key, midiValue);
|
||
|
} else if (mapping[device].props[activePropSet][statusCode].hasOwnProperty(key)) {
|
||
|
const pm = mapping[device].props[activePropSet][statusCode][key];
|
||
|
if (typeof pm === 'function') {
|
||
|
pm(getLayers()[activeLayer], activeLayer, key, midiValue);
|
||
|
} else {
|
||
|
setValue(getLayers()[activeLayer], activeLayer, pm[0], midiValue, pm[1]);
|
||
|
}
|
||
|
}
|
||
|
appliedMidiValues[device][key] = updatingMidiValues[device][key];
|
||
|
}
|
||
|
delete updatingMidiValues[device][key];
|
||
|
}
|
||
|
}
|
||
|
setTimeout(() => {
|
||
|
if (doApplyMidiValues) {
|
||
|
requestAnimationFrame(applyMidiValuesInterval);
|
||
|
}
|
||
|
}, window.applyMidiTimeoutMs);
|
||
|
}
|
||
|
};
|
||
|
const directlyApplyMidiValues = (device, key, statusCode, midiValue) => {
|
||
|
if (typeof mapping[device].general[statusCode][key] === 'function') {
|
||
|
mapping[device].general[statusCode][key](key, midiValue);
|
||
|
} else if (mapping[device].props[activePropSet][statusCode].hasOwnProperty(key)) {
|
||
|
const pm = mapping[device].props[activePropSet][statusCode][key];
|
||
|
if (typeof pm === 'function') {
|
||
|
pm(getLayers()[activeLayer], activeLayer, key, midiValue);
|
||
|
} else {
|
||
|
setValue(getLayers()[activeLayer], activeLayer, pm[0], midiValue, pm[1]);
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
window.mapping = mapping;
|
||
|
|
||
|
if (!("requestMIDIAccess" in navigator)) {
|
||
|
element.innerHTML = `<h1>:-/</h1><p>I'm sorry, but your browser does not support the WebMIDI API ☹️🚫🎹</p>`;
|
||
|
}
|
||
|
|
||
|
const registerEvents = () => {
|
||
|
openCloseButton.addEventListener('click', () => {
|
||
|
if (!isPanelOpen) {
|
||
|
isPanelOpen = true;
|
||
|
element.style.display = 'flex';
|
||
|
} else {
|
||
|
isPanelOpen = false;
|
||
|
element.style.display = 'none';
|
||
|
}
|
||
|
});
|
||
|
|
||
|
const buttonOn = document.createElement('div');
|
||
|
buttonOn.innerHTML = "light on";
|
||
|
buttonOn.addEventListener('click', () => {
|
||
|
outputs.forEach((midiOutput) => {
|
||
|
midiOutput.send([152, 9, 2]);
|
||
|
});
|
||
|
});
|
||
|
const buttonOff = document.createElement('div');
|
||
|
buttonOff.innerHTML = "light off";
|
||
|
buttonOff.addEventListener('click', () => {
|
||
|
outputs.forEach((midiOutput) => {
|
||
|
midiOutput.send([152, 9, 0]);
|
||
|
});
|
||
|
});
|
||
|
const buttonDebug = document.createElement('div');
|
||
|
buttonDebug.innerHTML = "debug on";
|
||
|
buttonDebug.addEventListener('click', () => {
|
||
|
if (debugLog) {
|
||
|
debugLog = false;
|
||
|
buttonDebug.innerHTML = "debug on";
|
||
|
} else {
|
||
|
debugLog = true;
|
||
|
buttonDebug.innerHTML = "debug off";
|
||
|
}
|
||
|
});
|
||
|
buttons.append(buttonOn);
|
||
|
buttons.append(buttonOff);
|
||
|
buttons.append(buttonDebug);
|
||
|
};
|
||
|
|
||
|
window.debugCallTimes = [];
|
||
|
|
||
|
const tryGetLayers = (resolve) => {
|
||
|
if (getLayers().length > 0 && tp.isProjectLoaded) {
|
||
|
resolve();
|
||
|
} else {
|
||
|
setTimeout(() => {
|
||
|
tryGetLayers(resolve);
|
||
|
}, 10);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
const tryGetLayersP = () => {
|
||
|
return new Promise((resolve) => {
|
||
|
tryGetLayers(resolve);
|
||
|
});
|
||
|
};
|
||
|
|
||
|
const selectLayers = () => {
|
||
|
return new Promise((resolve) => {
|
||
|
let delay = 500;//parseInt(localStorage.getItem('debugdelay'));
|
||
|
for (let i = 0; i <= layers.length; i++) {
|
||
|
setTimeout(() => {
|
||
|
if (i < layers.length) {
|
||
|
tp.studio.setSelection([layers[i].theatreObject]);
|
||
|
} else {
|
||
|
tp.studio.setSelection([]);
|
||
|
resolve();
|
||
|
}
|
||
|
}, i * delay);
|
||
|
}
|
||
|
});
|
||
|
};
|
||
|
|
||
|
window.ofRA = true;
|
||
|
window.ofUpdateMS = 1000 / 30;
|
||
|
const timeline_head = document.querySelector('#timeline_head');
|
||
|
const timeline = document.querySelector('#timeline');
|
||
|
let last_realtime_s = -999999;
|
||
|
let last_time_s = -999999;
|
||
|
let last_touchtime_s = -999999;
|
||
|
let last_processed_touchtime_s = -999999;
|
||
|
|
||
|
const ofUpdater = () => {
|
||
|
const realtime_s = performance.now() / 1000.0;
|
||
|
const time_s = tp.sheet.sequence.position;
|
||
|
const percent = time_s / tp.duration * 100;
|
||
|
|
||
|
{
|
||
|
const led = 114;
|
||
|
const statusCode = 184;
|
||
|
const color = [3,2,1,0][Math.floor(realtime_s * 4.0) % 4];
|
||
|
setLed(led, color, statusCode);
|
||
|
}
|
||
|
{
|
||
|
const led = 115;
|
||
|
const statusCode = 184;
|
||
|
const color = [0,1,2,3][Math.floor(realtime_s * 4.0) % 4];
|
||
|
setLed(led, color, statusCode);
|
||
|
}
|
||
|
{
|
||
|
const led = 117;
|
||
|
const statusCode = 184;
|
||
|
const color = [3,2,1,2][Math.floor(realtime_s * 6.0) % 4];
|
||
|
setLed(led, color, statusCode);
|
||
|
}
|
||
|
{
|
||
|
const led = 116;
|
||
|
const statusCode = 184;
|
||
|
const color = [1,2,3,2][Math.floor(realtime_s * 6.0) % 4];
|
||
|
setLed(led, color, statusCode);
|
||
|
}
|
||
|
|
||
|
for (let b = 0; b < ledButtonRowMapping.length; b++) {
|
||
|
const percentIndex = Math.floor((percent * 0.01) * ledButtonRowMapping.length);
|
||
|
if (b === percentIndex) {
|
||
|
ledButtonRowStatus[b] = Math.floor(Math.random() * 127.0);
|
||
|
}
|
||
|
setLed(ledButtonRowMapping[b], ledButtonRowStatus[b]);
|
||
|
}
|
||
|
|
||
|
let currentlyTouching = false;
|
||
|
if (realtime_s - last_touchtime_s < config.midi.touchTimeThreshold_s) {
|
||
|
currentlyTouching = true;
|
||
|
}
|
||
|
if (Object.keys(currentValues[activeLayer]).length > 0 && last_touchtime_s !== last_processed_touchtime_s) {
|
||
|
let starttime_s = -1;
|
||
|
if (realtime_s - last_realtime_s < config.midi.touchTimeThreshold_s) {
|
||
|
starttime_s = last_time_s; // fires first time prematurely, but this is okay (=> -1)
|
||
|
}
|
||
|
addValuesToBuffer(activeLayer, currentValues[activeLayer], time_s, starttime_s);
|
||
|
last_processed_touchtime_s = last_touchtime_s;
|
||
|
last_realtime_s = realtime_s;
|
||
|
last_time_s = time_s;
|
||
|
}
|
||
|
timeline_head.style.left = `calc(${percent}% - 10px)`;
|
||
|
timeline.style.background = currentlyTouching ? 'red' : 'grey';
|
||
|
|
||
|
for (let i = 0; i <= layers.length; i++) {
|
||
|
let bufferValues = JSON.parse(JSON.stringify(getValuesFromBuffer(i, time_s)));
|
||
|
|
||
|
bufferValues = {...bufferValues, ...currentValues[i]};
|
||
|
|
||
|
sentMidiValues[i] = mixObject(sentMidiValues[i], bufferValues, config.midi.smoothingMix);
|
||
|
|
||
|
if (i < layers.length) {
|
||
|
const values = {...layers[i].theatreObject.value, ...sentMidiValues[i]};
|
||
|
sentValues[i] = mixObject(sentValues[i], values, config.midi.smoothingMix);
|
||
|
|
||
|
let p = layers[i].values2cppProps(values);
|
||
|
if (p !== false) {
|
||
|
Module.setProps(p, layers[i].id());
|
||
|
}
|
||
|
} else {
|
||
|
const artboardValues = {...getArtboard().theatreObject.value, ...sentMidiValues[i]}
|
||
|
sentValues[i] = mixObject(sentValues[i], artboardValues, config.midi.smoothingMix);
|
||
|
let cppProps = getArtboard().values2cppProps(artboardValues);
|
||
|
Module.setArtboardProps(cppProps);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!currentlyTouching) {
|
||
|
for (let i = 0; i < currentValues.length; i++) {
|
||
|
currentValues[i] = {};
|
||
|
}
|
||
|
}
|
||
|
if (window.ofRA) {
|
||
|
requestAnimationFrame(ofUpdater);
|
||
|
} else {
|
||
|
setTimeout(() => {
|
||
|
ofUpdater();
|
||
|
}, window.ofUpdateMS);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const init = () => {
|
||
|
tryGetLayersP().then(() => {
|
||
|
layers = getLayers();
|
||
|
//console.log('what... this is layers' , layers);
|
||
|
const promises = [];
|
||
|
layers.forEach((layer) => {
|
||
|
promises.push(layer.updateFonts());
|
||
|
});
|
||
|
if (tp.sheet.project.address.projectId === 'rudi-midi') {
|
||
|
mapping["Launch Control MIDI 1"].props[0][184][23] = ['fontSize_px', [-128, 128]];
|
||
|
}
|
||
|
if (tp.sheet.project.address.projectId === 'sam-midi') {
|
||
|
mapping["Launch Control MIDI 1"].props[0][184][23] = ['fontSize_px', [-256, 256]];
|
||
|
}
|
||
|
Promise.all(promises).then(() => {
|
||
|
layers.forEach((layer, layerI) => {
|
||
|
const letterDelayProps = [
|
||
|
{
|
||
|
sequenced: true,
|
||
|
prop: ['color'],
|
||
|
},
|
||
|
{
|
||
|
sequenced: true,
|
||
|
prop: ['letterSpacing']
|
||
|
},
|
||
|
{
|
||
|
sequenced: true,
|
||
|
prop: ['fontSize_px']
|
||
|
},
|
||
|
];
|
||
|
if (layer.props.hasOwnProperty('fontVariationAxes')) {
|
||
|
const keys = Object.keys(layer.props.fontVariationAxes.props);
|
||
|
keys.forEach((key) => {
|
||
|
const detail = {
|
||
|
sequenced: true,
|
||
|
prop: ['fontVariationAxes', key],
|
||
|
};
|
||
|
letterDelayProps.push(detail);
|
||
|
});
|
||
|
}
|
||
|
letterDelayProps.forEach((detail, i) => {
|
||
|
// only update theatre for the last one
|
||
|
const updateTheatre = i === letterDelayProps.length - 1;
|
||
|
layer.handleSequenceEvent(detail, updateTheatre)
|
||
|
.then((updatedTheatre) => {
|
||
|
if (updatedTheatre && layerI === layers.length - 1) {
|
||
|
populateValueBuffer(layers);
|
||
|
ofUpdater();
|
||
|
isInitialized = true;
|
||
|
}
|
||
|
});
|
||
|
});
|
||
|
});
|
||
|
});
|
||
|
//selectLayers().then(() => {
|
||
|
//});
|
||
|
});
|
||
|
registerEvents();
|
||
|
navigator.requestMIDIAccess()
|
||
|
.then((access) => {
|
||
|
|
||
|
// Get lists of available MIDI controllers
|
||
|
inputs = access.inputs;
|
||
|
outputs = access.outputs;
|
||
|
|
||
|
const inputText = [];
|
||
|
const outputText = [];
|
||
|
|
||
|
inputs.forEach((midiInput) => {
|
||
|
inputText.push(`FOUND: ${midiInput.name}\n`);
|
||
|
updatingMidiValues[midiInput.name] = {};
|
||
|
appliedMidiValues[midiInput.name] = {};
|
||
|
midiInput.onmidimessage = function(message) {
|
||
|
//window.debugCallTimes.push(performance.now());
|
||
|
if (midiInput.name === "Launch Control MIDI 1") {
|
||
|
const isGeneral =
|
||
|
mapping[midiInput.name]
|
||
|
.general.hasOwnProperty(message.data[0]) &&
|
||
|
mapping[midiInput.name]
|
||
|
.general[message.data[0]].hasOwnProperty(message.data[1]);
|
||
|
const isProp =
|
||
|
mapping[midiInput.name]
|
||
|
.props[activePropSet].hasOwnProperty(message.data[0]) &&
|
||
|
mapping[midiInput.name]
|
||
|
.props[activePropSet][message.data[0]].hasOwnProperty(message.data[1]);
|
||
|
if (isInitialized && (isGeneral || isProp)) {
|
||
|
last_touchtime_s = performance.now() / 1000.0;
|
||
|
updatingMidiValues[midiInput.name][message.data[1]] = [message.data[0], message.data[2]];
|
||
|
//directlyApplyMidiValues(midiInput.name, message.data[1], message.data[0], message.data[2]);
|
||
|
}
|
||
|
autoSwitchPerhaps();
|
||
|
}
|
||
|
if (debugLog) {
|
||
|
element.querySelector(".midiMessages").innerText += `# ${midiInput.name}
|
||
|
${new Date()}
|
||
|
==================================
|
||
|
- Status: ${message.data[0]}
|
||
|
- Data 1: ${message.data[1]}
|
||
|
- Data 2: ${message.data[2]}
|
||
|
==================================\n\n`;
|
||
|
}
|
||
|
};
|
||
|
});
|
||
|
|
||
|
outputs.forEach((midiOutput) => {
|
||
|
outputText.push(`FOUND: ${midiOutput.name}\n`);
|
||
|
});
|
||
|
|
||
|
element.querySelector(".inputs").innerText = inputText.join('');
|
||
|
element.querySelector(".outputs").innerText = outputText.join('');
|
||
|
|
||
|
applyMidiValuesInterval();
|
||
|
|
||
|
// lalalaload another project
|
||
|
//autoSwitchPerhaps();
|
||
|
|
||
|
});
|
||
|
};
|
||
|
|
||
|
let autoSwitchTimeout = false;
|
||
|
|
||
|
const autoSwitchPerhaps = () => {
|
||
|
clearTimeout(autoSwitchTimeout);
|
||
|
autoSwitchTimeout = setTimeout(() => {
|
||
|
setProject(114, 127);
|
||
|
}, 5 * 60 * 1000);
|
||
|
};
|
||
|
|
||
|
const setValue = (layer, layerIndex, prop, value, minMax) => {
|
||
|
let v;
|
||
|
let propName = prop;
|
||
|
if (minMax.length > 2) {
|
||
|
const index = Math.floor((value / 128.0) * minMax.length);
|
||
|
v = minMax[index];
|
||
|
} else {
|
||
|
const min = minMax[0];
|
||
|
const max = minMax[1];
|
||
|
v = (value / 127.0) * (max - min) + min;
|
||
|
if (propName.indexOf('color') === 0) {
|
||
|
propName = propName.split('.')[1];
|
||
|
let color;
|
||
|
if (currentValues[layerIndex].hasOwnProperty('color')) {
|
||
|
color = {...layer.theatreObject.value.color, ...currentValues[layerIndex].color};
|
||
|
} else {
|
||
|
color = layer.theatreObject.value.color;
|
||
|
}
|
||
|
color[propName] = v;
|
||
|
propName = 'color';
|
||
|
v = color;
|
||
|
}
|
||
|
if (propName.indexOf('backgroundColor') === 0) {
|
||
|
propName = propName.split('.')[1];
|
||
|
let backgroundColor;
|
||
|
if (currentValues[layerIndex].hasOwnProperty('backgroundColor')) {
|
||
|
backgroundColor = {...layer.theatreObject.value.backgroundColor, ...currentValues[layerIndex].backgroundColor};
|
||
|
} else {
|
||
|
backgroundColor = layer.theatreObject.value.backgroundColor;
|
||
|
}
|
||
|
backgroundColor[propName] = v;
|
||
|
propName = 'backgroundColor';
|
||
|
v = backgroundColor;
|
||
|
}
|
||
|
}
|
||
|
currentValues[layerIndex][propName] = v;
|
||
|
//tp.studio.transaction(({
|
||
|
//set
|
||
|
//}) => {
|
||
|
//set(layer.theatreObject.props[propName], v);
|
||
|
//});
|
||
|
};
|
||
|
|
||
|
|
||
|
this.init = init;
|
||
|
this.getInputs = () => {
|
||
|
return inputs;
|
||
|
}
|
||
|
this.getOutputs = () => {
|
||
|
return outputs;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
export {
|
||
|
MidiController
|
||
|
};
|