Skip to content

Instantly share code, notes, and snippets.

@mollerse
Last active December 6, 2019 12:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mollerse/474f4ce3be131cdf7f7bf92c49e37db6 to your computer and use it in GitHub Desktop.
Save mollerse/474f4ce3be131cdf7f7bf92c49e37db6 to your computer and use it in GitHub Desktop.
Simple interface for controlling various parameters with MIDI and/or dat.gui
import midiControl from "./midiControl.js";
const controls = midiControl("Launch Control MIDI 1");
function setProp(name, unit, v) {
document.documentElement.style.setProperty(name, `${v}${unit}`);
}
let setMarginXXS = setProp.bind(null, "--tv-margins-xxs", "px");
let setMarginXS = setProp.bind(null, "--tv-margins-xs", "px");
let setMarginS = setProp.bind(null, "--tv-margins-s", "px");
let setMarginM = setProp.bind(null, "--tv-margins-m", "px");
let setMarginL = setProp.bind(null, "--tv-margins-l", "px");
let setMarginXL = setProp.bind(null, "--tv-margins-xl", "px");
let setPlugPeek = setProp.bind(null, "--plug-peek", "px");
let setPlugShadowTop = setProp.bind(null, "--plug-shadow-top", "px");
let setPlugShadowBottom = setProp.bind(null, "--plug-shadow-bottom", "px");
let setPlugTitleHeight = setProp.bind(null, "--plug-title-height", "px");
let setPlugPerspective = setProp.bind(null, "--plug-perspective", "px");
let setPlugHoverDistance = setProp.bind(null, "--plug-hover-distance", "px");
let setPlugActiveDistance = setProp.bind(null, "--plug-active-distance", "px");
controls
.addScheme("DEMO")
.addNumberValue("margin xxs", [4, -4, 8, 0.5], {
triggerId: 21,
onChange: setMarginXXS
})
.addNumberValue("margin xs", [8, -8, 16, 0.5], {
triggerId: 22,
onChange: setMarginXS
})
.addNumberValue("margin s", [16, -16, 32, 0.5], {
triggerId: 23,
onChange: setMarginS
})
.addNumberValue("margin m", [24, -24, 48, 0.5], {
triggerId: 24,
onChange: setMarginM
})
.addNumberValue("margin l", [48, -48, 96, 1], {
triggerId: 25,
onChange: setMarginL
})
.addNumberValue("margin xl", [96, -96, 192, 1], {
triggerId: 26,
onChange: setMarginXL
})
.addNumberValue("Plug Peek", [24, -12, 48, 1], {
triggerId: 41,
onChange: setPlugPeek
})
.addNumberValue("Plug Shadow Top", [12, -12, 24, 1], {
triggerId: 42,
onChange: setPlugShadowTop
})
.addNumberValue("Plug Shadow Bottom", [12, -12, 48, 1], {
triggerId: 43,
onChange: setPlugShadowBottom
})
.addNumberValue("Plug Title Height", [20, -10, 50, 1], {
triggerId: 44,
onChange: setPlugTitleHeight
})
.addNumberValue("Plug Perspective", [1000, -1000, 2000, 50], {
triggerId: 45,
onChange: setPlugPerspective
})
.addNumberValue("Hover Distance", [20, -10, 40, 1], {
triggerId: 46,
onChange: setPlugHoverDistance
})
.addNumberValue("Active Distance", [-10, -30, 20, 1], {
triggerId: 47,
onChange: setPlugActiveDistance
});
import dat from "dat.gui";
function normalize(min, max, v) {
return (v - min) / (max - min);
}
class MidiControl {
constructor(name) {
dat.GUI.prototype.removeFolder = function(name) {
var folder = this.__folders[name];
if (!folder) {
return;
}
folder.close();
this.__ul.removeChild(folder.domElement.parentNode);
delete this.__folders[name];
this.onResize();
};
this.gui = new dat.GUI({ closed: false });
this.device = { in: null, out: null };
if (window.navigator.requestMIDIAccess) {
navigator
.requestMIDIAccess()
.then(access => {
let entries = access.inputs.entries();
let outries = access.outputs.entries();
let entry = entries.next();
while (!entry.done) {
let [_, d] = entry.value;
if (d.name === name) {
this.device.in = d;
break;
}
entry = entries.next();
}
let outry = outries.next();
while (!outry.done) {
let [_, d] = outry.value;
if (d.name === name) {
this.device.out = d;
break;
}
outry = outries.next();
}
if (this.device.in) {
this.device.in.onmidimessage = ({ data }) => {
let [eventId, keyId, value] = data;
if (eventId === 144 || eventId === 176) {
this.trigger(keyId, normalize(0, 127, value));
}
};
} else {
console.warn(`No MIDI Input named ${name} found.`);
}
if (!this.device.out) {
console.warn(`No MIDI Output named ${name} found.`);
} else {
window.__device = this;
}
})
.catch(e => {
console.warn(e);
});
} else {
console.warn("Midi not available, not enabling midi controls.");
}
this.schemes = {};
this.activeScheme = null;
}
// Builder methods
addScheme(name) {
if (this.getSchemes().indexOf(name) > -1) {
throw new Error(
`Scheme ${name} already exists. Remove existing before adding.`
);
}
this.schemes[name] = {
values: {},
triggers: {},
gui: this.gui.addFolder(name)
};
this.activeScheme = name;
return this;
}
addNumberValue(
key,
[value, min = 0, max = value, step = 1],
{ onChange, triggerId }
) {
let scheme = this.getScheme();
scheme.values[key] = value;
let control = scheme.gui.add(scheme.values, key, min, max, step);
if (typeof onChange === "function") {
control.onChange(onChange);
}
if (typeof triggerId === "string" || typeof triggerId === "number") {
scheme.triggers[triggerId] = v => control.setValue(min + v * (max - min));
}
return this;
}
addBooleanValue(key, [value], { onChange, triggerId }) {
let scheme = this.getScheme();
scheme.values[key] = value;
let control = scheme.gui.add(scheme.values, key);
if (typeof onChange === "function") {
control.onChange(onChange);
}
if (typeof triggerId === "string" || typeof triggerId === "number") {
scheme.triggers[triggerId] = () => {
control.setValue(!control.getValue());
if (this.device.out) {
this.device.out.send([144, triggerId, control.getValue() ? 100 : 10]);
}
};
}
return this;
}
// Runtime methods
loadScheme(name) {
if (this.getSchemes().indexOf(name) < 0) {
throw new Error(`Scheme ${name} not found. Please initialize.`);
}
if (this.activeScheme === name) {
console.warn(`Scheme ${name} already active. Skipping.`);
} else if (this.activeScheme != null) {
console.warn(`Unloading active scheme ${name}.`);
this.unloadScheme(this.activeScheme);
}
this.activeScheme = name;
}
unloadScheme(name) {
if (this.getSchemes().indexOf(name) < 0) {
console.warn(`Scheme ${name} not found. Skipping.`);
}
if (this.activeScheme === name) {
this.activeScheme = null;
}
}
removeScheme(name) {
if (this.getSchemes().indexOf(name) < 0) {
console.warn(`Scheme ${name} not found. Skipping.`);
} else {
this.unloadScheme(name);
this.gui.removeFolder(name);
delete this.schemes[name];
}
}
getValue(key) {
let scheme = this.getScheme();
return scheme.values[key];
}
// Internal stuff
getScheme(name = this.activeScheme) {
return this.schemes[name];
}
trigger(triggerId, v) {
let scheme = this.getScheme();
if (!scheme) return;
let trigger = scheme.triggers[triggerId];
trigger && trigger(v);
}
// Debug-stuff
getSchemes() {
return Object.keys(this.schemes);
}
}
export default function midiControlFactory(name) {
return new MidiControl(name);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment