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 = $("