var TransformParameter = function(instr, tDefinition, parent, transform, twist, onChange) { var self = this; var refreshable = false; var changeFunc; var initval = true; var definition = {}; var randomiseAllowed = true; var visible = true; if (parent) { Object.assign(definition, tDefinition); } else { definition = tDefinition; } if (definition.channel == "applymode") { randomiseAllowed = false; } 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", 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 == "applymode") { Object.assign(definition, {name: "Apply mode", channel: "applymode", absolutechannel: true, description: "Apply mode", automatable: false, options: ["Replace", "Mix", "Modulate", "Demodulate"], dfault: 0}); } else if (definition.preset == "note") { var notes = {}; for (var i = 21; i < 128; i++) { var v = twist.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: instr + "_" + "instance", options: twist.otherInstanceNames, automatable: false }); changeFunc = function(index) { var s = twist.waveforms[index].selected; app.setControlChannel(instr + "_" + "inststart", s[0]); app.setControlChannel(instr + "_" + "instend", s[1]); app.setControlChannel(instr + "_" + "instchan", s[2]); }; } if (save) { Object.assign(definition, save); } } // if preset var type; if (definition.hasOwnProperty("conditions") && !parent) { refreshable = true; if (transform) transform.refreshable = refreshable; } var channel = ""; if (!definition.hasOwnProperty("absolutechannel")) { channel = (parent) ? parent.channel : instr + "_"; } if (definition.hasOwnProperty("channel")) { channel += definition.channel; } else { channel += definition.name.toLowerCase(); } 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 (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); } }; if (!definition.hasOwnProperty("hidden")) { definition.hidden = false; } if (!definition.hasOwnProperty("step")) { definition.step = 0.0000001; } if (!definition.hasOwnProperty("min")) { definition.min = 0; } if (!definition.hasOwnProperty("max")) { definition.max = 1; } if (!definition.hasOwnProperty("fireChanges")) { definition.fireChanges = true; } if (!definition.hasOwnProperty("dfault")) { 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.hasOwnProperty("options")) { type = "select"; } 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 (automationActive) disableAutomation(); if (self.automation) { delete self.automation; self.automation = null; } if (elSpline) { elSpline.remove(); delete elSpline; } if (modulationShown) hideModulations(); }; this.randomise = function() { if (!randomiseAllowed) return; var val; if (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) { // 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, twist.otherInstanceNames); } for (var k in definition.conditions) { var c = definition.conditions[k]; var val = transform.parameters[transform.instr + "_" + c.channel].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; return elRow.hide(); } } visible = true; elRow.show(); }; function createSelectOptions(elSelect, options) { var selected = elInput.val(); elSelect.empty(); for (var x in options) { var opt = $("").text(options[x]).val(x).appendTo(elSelect); if (x == selected) { opt.attr("selected", "1"); if (changeFunc) changeFunc(x); } } 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); app.setControlChannel(channel, val); } if (onChange) { onChange(val); } }); var options = (definition.hostrange && parent) ? parent.definitions.options : definition.options; createSelectOptions(elInput, options); } else if (type == "string") { elInput = $("").change(function() { if (transform) transform.refresh(); var val = self.getValue(); if (definition.fireChanges) { app.setStringChannel(channel, val); } if (onChange) { onChange(val); } }); } else if (type == "checkbox") { elInput = $("").addClass("tp_checkbox").attr("type", "checkbox").on("change", function() { if (transform) transform.refresh(); var val = self.getValue(); if (definition.fireChanges) { app.setControlChannel(channel, 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("tp_slider").attr("type", "range").on("input", function() { updateLabel(); if (definition.fireChanges) { app.setControlChannel(channel, self.getValue()); } }).change(function() { updateLabel(); if (transform) transform.refresh(); var val = self.getValue(); if (definition.fireChanges) { app.setControlChannel(channel, 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("transparentinput").appendTo(elValueLabel).change(function() { if (updateinput) { elInput.val($(this).val()).trigger("change").trigger("input"); } }); } 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(channel, definition.dfault); }; this.remove = function() { 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 = twist.appdata.modulations[self.modulation]; return {type: "modulation", data: [m.instr, self.channel]}; } else if (automationActive && self.automation) { return {type: "automation", channel: self.channel, data: self.automation.getLinsegData(start, end, twist.waveform.getRegion())}; } }; 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", 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(); } else { showAutomation(); } } }); 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; app.setControlChannel(channel, self.getValue()); elValueLabel.show(); elInput.show(); modButton.el.show(); automationButton.setState(true); editAutomationButton.el.hide(); self.hideAutomation(); } this.redraw = function(region) { if (self.automation) { if (region && region[0] != null && region[1] != null) { self.automation.setRange(region[0], region[1]); } else { self.automation.redraw(); } } }; function showAutomation() { if (!transform) return; var colour = "rgb(" + (Math.round(Math.random() * 50) + 205) + "," + (Math.round(Math.random() * 50) + 205) + "," + (Math.round(Math.random() * 50) + 205) + ")"; 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); if (!self.automation) { self.automation = new SplineEdit( elSpline, colour, twist.waveform.getDuration, [definition.min, definition.max, self.getValue(), definition.step], definition.name ); } elValueLabel.hide(); elInput.hide(); modButton.el.hide(); elSpline.show(); editAutomationButton.el.show(); //.css("background-color", colour); automationButton.setState(false); } elModulations = $("").addClass("tfv_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(channel, 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 = twist.appdata.modulations[i]; for (let x of m.parameters) { var tp = new TransformParameter(m.instr, x, self, transform, twist); self.modulationParameters[tp.channel] = tp; tb.append(tp.getElementRow(true)); // hmm modulate the modulation with false } } var selecttb = $("").appendTo($("