From 9fbf91db06a6d4f4b5cd8bb45389a731bb86bf22 Mon Sep 17 00:00:00 2001 From: Richard Date: Sun, 13 Apr 2025 18:48:02 +0100 Subject: initial --- site/app/twirl/transform.js | 1212 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1212 insertions(+) create mode 100644 site/app/twirl/transform.js (limited to 'site/app/twirl/transform.js') diff --git a/site/app/twirl/transform.js b/site/app/twirl/transform.js new file mode 100644 index 0000000..1feff51 --- /dev/null +++ b/site/app/twirl/transform.js @@ -0,0 +1,1212 @@ +twirl.transform = {}; +twirl.transform.Parameter = function(options) { + var self = this; + var instr = options.instrument; + var tDefinition = options.definition; + var parent = options.parent; + var transform = options.transform; + var host = options.host; + var onChange; + var refreshable = false; + var changeFunc; + var initval = true; + var definition = {}; + var randomiseAllowed = true; + var visible = true; + var uniqueTransformID = (options.transform) ? options.transform.uniqueID : ""; + if (parent) { + Object.assign(definition, tDefinition); + } else { + definition = tDefinition; + } + + if (options.onChange || definition.onChange) { + onChange = function(val) { + if (options.onChange) options.onChange(val); + if (definition.onChange) definition.onChange(val); + } + } + + if (definition.hasOwnProperty("preset")) { + var save = {}; + for (var s of ["dfault", "name", "channel", "automatable", "description"]) { + if (definition.hasOwnProperty(s)) { + save[s] = definition[s]; + } + } + + if (definition.preset == "amp") { + Object.assign(definition, {name: "Amplitude", channel: "amp", description: "Amplitude", dfault: 1, min: 0, max: 1}); + } else if (definition.preset == "pvslock") { + Object.assign(definition, {name: "Peak lock", channel: "pvslock", description: "Lock frequencies around peaks", min: 0, max: 1, step: 1, dfault: 0}); + } else if (definition.preset == "fftsize") { + Object.assign(definition, {name: "FFT size", channel: "fftsize", description: "FFT size", options: [256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65535], dfault: 2, asvalue: true, automatable: false, lagHint: -1}); + } else if (definition.preset == "wave") { + Object.assign(definition, {name: "Wave", description: "Wave shape to use", options: ["Sine", "Square", "Saw", "Pulse", "Triangle"], dfault: 0, channel: "wave"}); + } else if (definition.preset == "wintype") { + Object.assign(definition, {name: "Window type", channel: "wintype", description: "Window shape", options: ["Hanning", "Hamming", "Half sine"], dfault: 0, automatable: false}); + + } else if (definition.preset == "instanceloop") { + Object.assign(definition, {name: "Cross instance loop type", channel: "otlooptype", description: "Loop type of other instance", options: ["None", "Forward", "Backward", "Ping-pong"], dfault: 0}); + + } else if (definition.preset == "note") { + var notes = {}; + for (var i = 21; i < 128; i++) { + var v = twirl.noteData.data.notes[i]; + notes[v[0]] = v[1]; + } + Object.assign(definition, {name: "Note", channel: "note", description: "Note to use", options: notes, dfault: 69, automatable: true}); + } else if (definition.preset == "instance") { + var c = (!definition.channel) ? "ot" : definition.channel; + initval = false; + if (transform) transform.refreshable = true; + refreshable = true; + Object.assign(definition, { + name: "Instance", description: "Other wave to use", channel: "instance", + options: options.otherInstanceNamesFunc(), + automatable: false + }); + changeFunc = function(index) { + var oif = options.instancesFunc(); + if (!oif[index]) return; + var s = oif[index].selected; + app.setControlChannel(instr + "_" + "inststart" + uniqueTransformID, s[0]); + app.setControlChannel(instr + "_" + "instend" + uniqueTransformID, s[1]); + app.setControlChannel(instr + "_" + "instchan" + uniqueTransformID, s[2]); + }; + } + if (save) { + Object.assign(definition, save); + } + } // if preset + + if (definition.channel == "applymode" || definition.noRandomisation) { + randomiseAllowed = false; + } + + var type; + + if (definition.hasOwnProperty("conditions") && !parent) { + refreshable = true; + if (transform) transform.refreshable = refreshable; + } + + var channel = ""; + if (!definition.hasOwnProperty("absolutechannel")) { + channel = (parent) ? parent.sendChannel : instr + "_"; + } + + if (definition.hasOwnProperty("channel")) { + channel += definition.channel; + } else { + channel += definition.name.toLowerCase(); + } + + this.sendChannel = channel; + if (!parent) { + this.sendChannel += uniqueTransformID; + } + var elContainer = $("
"); + var elValueLabel = $("
"); + var elValueInput; + var elModulations; + var elInput; + var elRow; + var elModSelect; + var automation = []; + + this.definition = definition; + this.modulation = null; + this.automation = null; + this.channel = channel; + this.modulationParameters = null; + + this.setPlaying = async function(state) { + if (definition.automatable || definition.hidden) return; + + if (definition.disableOnPlay) { + if (elValueInput) { + elValueInput.prop("disabled", state); + elValueInput.css("opacity", (state) ? 0.8 : 1); + } + + if (elInput) { + elInput.prop("disabled", state); + elInput.css("opacity", (state) ? 0.8 : 1); + } + } else { + if (state) { + var text = "Changes will be applied upon next run"; + elContainer.on("mouseover", function(event){ + twirl.tooltip.show(event, text); + }).on("mouseout", function(){ + twirl.tooltip.hide(); + }); + } else { + elContainer.off("mouseover").off("mouseout"); + } + } + }; + + + if (!definition.hasOwnProperty("hidden")) { + definition.hidden = false; + } + + if (!definition.step) { + definition.step = 0.0000001; + } + + if (definition.min == null) { + definition.min = 0; + } + + if (definition.max == null) { + definition.max = 1; + } + + if (!definition.hasOwnProperty("fireChanges")) { + definition.fireChanges = true; + } + + if (definition.dfault == null) { + definition.dfault = 1; + } + + if (parent) { + if (definition.hostrange) { + var items = ["step", "min", "max", "options", "conditions", "hostrange"]; + if (definition.dfault == "hostrangemin") { + definition.dfault = parent.definition.min; + } else if (definition.dfault == "hostrangemax") { + definition.dfault = parent.definition.max; + } else { + items.push("dfault"); + } + for (let o of items) { + if (parent.definition.hasOwnProperty(o)) { + definition[o] = parent.definition[o]; + } + } + } else if (definition.preset == "hostrangemin") { + definition.min = definition.max = definition.dfault = parent.definition.min; + } else if (definition.preset == "hostrangemax") { + definition.min = definition.max = definition.dfault = parent.definition.max; + } + } + + if (definition.options) { + type = "select"; + definition.min = 0; + definition.max = definition.options.length - 1; + definition.step = 1; + } else if (definition.hasOwnProperty("type")) { + type = definition.type; + } else if (definition.min == 0 && definition.max == 1 && definition.step == 1) { + type = "checkbox"; + } else { + type = "range"; + } + + if (!definition.hasOwnProperty("automatable")) { + definition.automatable = ((type == "range" || type == "checkbox") && !parent); + } + + this.getLagHint = function() { + if (!definition.lagHint || !visible) return; + var lagHint; + if (typeof(definition.lagHint) == "object") { + lagHint = "setting " + definition.name + " to " + + definition.options[definition.lagHint.option] + ""; + } else { + lagHint = ((definition.lagHint < 0) ? "reducing" : "increasing") + + " " + definition.name + ""; + } + return lagHint; + }; + + this.setRawValue = function(val) { + if (type == "checkbox") { + elInput[0].checked = (val == 0) ? false : true; + } else { + elInput.val(val); + } + elInput.trigger("change"); + } + + this.getRawValue = function() { + return elInput.val(); + } + + this.getValue = function() { + var val; + if (type == "range" || type == "string") { + val = elInput.val(); + } else if (type == "select") { + val = (definition.asvalue) ? elInput.find("option:selected").text() : elInput.val(); + } else if (type == "checkbox") { + val = (elInput[0].checked) ? 1 : 0; + } + return val; + }; + + this.reset = function() { + self.setRawValue(definition.dfault); + if (!options.unmanagedAutomation) { + if (automationActive) disableAutomation(); + if (self.automation) { + delete self.automation; + self.automation = null; + } + if (elSpline) { + elSpline.remove(); + delete elSpline; + } + } + if (modulationShown && !options.unmanagedModulation) { + hideModulations(); + } + }; + + this.randomise = function() { + if (!randomiseAllowed) return; + var val; + if (!options.unmanagedModulation && definition.automatable) { + if (Math.random() >= 0.5) { + modButton.el.click(); + } + } + + if (type == "select") { + val = Math.round(Math.random() * (definition.options.length - 1)); + } else if (type == "range") { + val = (Math.random() * (definition.max - definition.min)) + definition.min; + if (definition.step == 1) { + val = Math.round(val); + } else { + val = Math.ceil((val - definition.min) / definition.step) * definition.step + definition.min; + } + } else if (type = "checkbox") { + val = (Math.round(Math.random())); + } + self.setRawValue(val); + + if (self.modulationParameters && !options.unmanagedModulation) { + // 4 = just the non-crossadaptive ones + elModSelect.val(Math.round(Math.random() * 4)).trigger("change"); + for (let mp in self.modulationParameters) { + self.modulationParameters[mp].randomise(); + } + } + }; + + + this.refresh = function() { + if (!refreshable || !transform) { + return; + } + if (definition.preset == "instance") { + createSelectOptions(elInput, options.otherInstanceNamesFunc(), true); + } + for (var k in definition.conditions) { + var c = definition.conditions[k]; + var chan = (c.absolutechannel) ? c.channel : transform.instr + "_" + c.channel; + var val = transform.parameters[chan].getValue(); + if ( + (c.operator == "eq" && val != c.value) || + (c.operator == "neq" && val == c.value) || + (c.operator == "lt" && val >= c.value) || + (c.operator == "gt" && val <= c.value) || + (c.operator == "le" && val > c.value) || + (c.operator == "ge" && val < c.value) + ) { + visible = false; + app.setControlChannel(self.sendChannel, definition.dfault); + return elRow.hide(); + } + } + // app.setControlChannel(self.sendChannel, self.getValue()); + visible = true; + elRow.show(); + }; + + function createSelectOptions(elSelect, options, sendValue) { + var selected = elInput.val(); + elSelect.empty(); + var applied; + var firstOption; + for (var x in options) { + if (!firstOption) firstOption = x; + var opt = $("