Last active
December 6, 2019 12:32
-
-
Save mollerse/474f4ce3be131cdf7f7bf92c49e37db6 to your computer and use it in GitHub Desktop.
Simple interface for controlling various parameters with MIDI and/or dat.gui
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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