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 = $("").text(options[x]).val(x).appendTo(elSelect); if (x == selected) { opt.attr("selected", "1"); if (changeFunc) changeFunc(x); applied = true; } } if (!applied) { elInput.val(firstOption); if (changeFunc) changeFunc(firstOption); } if (sendValue) { app.setControlChannel(self.sendChannel, self.getValue()); } definition.min = 0; definition.max = (Array.isArray(options)) ? options.length - 1 : Object.keys(options).length - 1; } function updateLabel() { if (elValueInput) { var val = self.getValue(); updateinput = false; rounding = 10000; val = Math.round(val * rounding) / rounding; elValueInput.val(val); updateinput = true; } } if (type == "select") { elInput = $(""); elInput.change(function(){ var val = self.getValue(); if (transform) transform.refresh(); if (definition.fireChanges) { if (changeFunc) changeFunc(val); if (!host.offline) app.setControlChannel(self.sendChannel, val); } if (onChange) { onChange(val); } }); var selectOptions = (definition.hostrange && parent) ? parent.definitions.options : definition.options; createSelectOptions(elInput, selectOptions); } else if (type == "string") { elInput = $("").change(function() { if (transform) transform.refresh(); var val = self.getValue(); if (definition.fireChanges) { app.setStringChannel(self.sendChannel, val); } if (onChange) { onChange(val); } }); } else if (type == "checkbox") { elInput = $("").addClass("twirl_checkbox").attr("type", "checkbox").on("change", function() { if (transform) transform.refresh(); var val = self.getValue(); if (definition.fireChanges) { app.setControlChannel(self.sendChannel, val); } if (onChange) { onChange(val); } }); } else if (type == "range") { var updateinput = true; var max = definition.max; var min = definition.min; var step = definition.step; var dfault = definition.dfault; elInput = $("").addClass("twirl_slider").attr("type", "range").on("input", function() { updateLabel(); if (definition.fireChanges) { app.setControlChannel(self.sendChannel, self.getValue()); } }).change(function() { updateLabel(); if (transform) transform.refresh(); var val = self.getValue(); if (definition.fireChanges && !host.offline) { app.setControlChannel(self.sendChannel, val); } if (onChange) { onChange(val); } }).attr("min", min).attr("max", max).attr("step", step).val(dfault); elValueInput = $("").attr("type", "number").attr("min", min).attr("max", max).attr("step", step).addClass("twirl_transparentinput").appendTo(elValueLabel).change(function() { if (updateinput) { elInput.val($(this).val()).trigger("change").trigger("input"); } }); } /* elInput.on("contextmenu", function(e){ var items = [{name: "Reset", click: function(){ self.reset(); }}]; if (definition.automatable) { items.push({ name: "Automate", click: function(){ if (!options.unmanagedAutomation) { transform.showAutomation(definition.name, elSpline); } } }); } items.push({ name: "Randomise", click: function(){ self.randomise(); } }); items.push({ name: ((randomiseAllowed) ? "Exclude from" : "Include in") + " randomisation", click: randomiseButton.click }); twirl.contextMenu.show(e, items); });*/ elContainer.append(elInput); if (initval) { self.setRawValue(definition.dfault); if (definition.fireChanges) { elInput.trigger("change"); } } this.setDefault = function() { elInput.val(definition.dfault).trigger("change"); //app.setControlChannel(sendChannel, definition.dfault); }; this.remove = function() { disableAutomation(); elRow.remove(); if (elSpline) { elSpline.remove(); } if (self.modulation) { self.modulation = null; } if (self.automation) { self.automation = null; } }; this.getAutomationData = function(start, end) { if (self.modulation) { var m = twirl.appdata.modulations[self.modulation]; return {type: "modulation", data: [m.instr, self.sendChannel]}; } else if (automationActive && self.automation) { return {type: "automation", channel: self.sendChannel, data: self.automation.getLinsegData(start, end, options.getRegionFunc())}; } }; var resetButton = twirl.createIcon({ label: "Reset parameter", icon: "reset", click: function() { self.reset(); } }); var randomiseButton = twirl.createIcon({ label: "Include in randomisation", icon: "randomise", click: function(obj) { randomiseAllowed = !randomiseAllowed; var opacity = (randomiseAllowed) ? 1 : 0.4; obj.el.css("opacity", opacity); } }); if (!randomiseAllowed) { randomiseButton.el.css("opacity", 0.4); } var elSpline; var editAutomationButton = twirl.createIcon({ label: "Select automation", icon: "show", click: function() { if (!transform) return; if (elSpline) { automationShown = true; transform.showAutomation(definition.name, elSpline); } } }); editAutomationButton.el.hide(); var automationButton = twirl.createIcon({ label: "Automate", label2: "Close automation", icon: "automate", icon2: "close", click: function() { if (elSpline && automationActive) { disableAutomation(); if (options.onAutomationClick) options.onAutomationClick(false); } else { showAutomation(); if (options.onAutomationClick) options.onAutomationClick(true); } } }); var automationActive = false; var automationShown = false; this.hideAutomation = function() { if (!transform) return; automationShown = false; if (elSpline) { transform.hideAutomation(definition.name); } } function disableAutomation() { if (!transform) return; automationActive = false; automationShown = false; if (!host.offline) app.setControlChannel(self.sendChannel, self.getValue()); elValueLabel.show(); elInput.show(); modButton.el.show(); automationButton.setState(true); editAutomationButton.el.hide(); self.hideAutomation(); } this.redraw = function(region) { if (self.automation && !options.unmanagedAutomation) { if (region && region[0] != null && region[1] != null) { self.automation.setRange(region[0], region[1]); } else { self.automation.redraw(); } } }; this.createAutomationSpline = function(elTarget, colour) { if (!colour) colour = twirl.random.rgbColour(); if (!self.automation) { self.automation = new SplineEdit( elTarget, colour, options.getDurationFunc, [definition.min, definition.max, self.getValue(), definition.step], definition.name ); } }; function showAutomation() { if (!transform) return; var colour = twirl.random.rgbColour(); automationShown = true; automationActive = true; if (!elSpline) { elSpline = $("").attr("id", "spl_" + channel).css({ position: "absolute", width: "100%", height: "100%", overflow: "hidden" }); } transform.showAutomation(definition.name, elSpline); self.createAutomationSpline(elSpline, colour); elValueLabel.hide(); elInput.hide(); modButton.el.hide(); elSpline.show(); editAutomationButton.el.show(); //.css("background-color", colour); automationButton.setState(false); } elModulations = $("").addClass("twirl_tf_container").hide().appendTo(elContainer); var modulationShown = false; var modButton = twirl.createIcon({ label: "Modulate", label2: "Close modulation", icon: "modulate", icon2: "close", click: function() { if (elModulations && modulationShown) { hideModulations(); } else { showModulations(); } } }); function hideModulations() { app.setControlChannel(self.sendChannel, self.getValue()); modulationShown = false; elValueLabel.show(); elInput.show(); automationButton.el.show(); self.modulation = null; modButton.setState(true); if (elModulations) { elModulations.hide(); } } function showModulations() { if (!transform) return; modulationShown = true; elValueLabel.hide(); elInput.hide(); automationButton.el.hide(); elModulations.show(); modButton.setState(false); if (elModulations.children().length != 0) { elModSelect.val(0).trigger("change"); return; } var tb = $(""); function buildModulation(i) { tb.empty(); self.modulationParameters = {}; self.modulation = i; let m = twirl.appdata.modulations[i]; for (let x of m.parameters) { var tp = new twirl.transform.Parameter({ instrument: m.instr, definition: x, transform: transform, parent: self, onAutomationClick: options.onAutomationClick, getDurationFunc: options.getDurationFunc, getRegionFunc: options.getRegionFunc, otherInstanceNamesFunc: options.otherInstanceNamesFunc, instancesFunc: options.instancesFunc, host: options.host }); self.modulationParameters[tp.channel] = tp; tb.append(tp.getElementRow(true)); // hmm modulate the modulation with false } } var selecttb = $("").appendTo($("[new]
"; } } li.html(content).css("cursor", "pointer").click(function() { options.click(items[k], thisPath); }); } ul.append(li); } options.element.append(ul); return ul; } options.element.append(recurse()); };