var Insert = function(channel, options) { var insert = this; }; var Channel = function(timeline, index) { var channel = this; this.clips = {}; this.width = null; this.offset = null; var heights = [25, 100]; this.height = heights[0]; this.index = index; this.name = "Channel " + (index + 1); var elChannel = $("
").addClass("twine_channel").css({ height: channel.height + "px" }).appendTo(timeline.element); this.inserts = []; var splines = {}; var heightState = 0; var elChannelControl = $("").addClass("twine_channelcontrol").text(channel.name).appendTo(elChannel).dblclick(function(){ heightState = 1 - heightState; channel.setHeight(heights[heightState]); }).click(function(){ channel.showDetails(); }).on("contextmenu", function(e){ return twirl.contextMenu.show(e, [ {name: "Rename", click: function(){ var el = $(""); $("").text("Rename channel").appendTo(el); var ti = $("").val(channel.name).appendTo(el); twirl.prompt.show(el, function(){ channel.setName(ti.val()); }); }} ]); }); var elChannelClips = $("").addClass("twine_channelclips").appendTo(elChannel).mousedown(twine.timeline.dragSelection); var elChannelSpline = $("").css({ position: "absolute", width: "100%", height: "75%", left: "0px", top: "25%", "z-index": 31, opacity: 0.57, "background-color": "var(--bgColor1)", display: "none" }).appendTo(elChannelClips); channel.defaultChannels = { amp: "twine_camp" + channel.index, pan: "twine_cpan" + channel.index, mute: "twine_cmute" + channel.index, solo: twine.mixer.soloChannel }; this.setName = function(name) { channel.name = name; elChannelControl.text(channel.name); }; this.exportData = async function() { var saveData = { clips: [], inserts: [], name: channel.name, amp: (await app.getControlChannel(channel.defaultChannels.amp)), pan: (await app.getControlChannel(channel.defaultChannels.pan)) }; for (var ins in channel.inserts) { } for (var i in channel.clips) { if (channel.clips[i]) { saveData.clips.push(await channel.clips[i].exportData()); } } return saveData; }; this.remove = function(noredraw) { elChannel.remove(); console.log("remove channel"); for (let c in channel.clips) { if (channel.clips[c]) channel.clips[c].destroy(); } for (let c in timeline.channels) { if (timeline.channels[c] == channel) { timeline.channels.splice(c, 1); } } if (!noredraw) timeline.drawGrid(); }; this.importData = async function(loadData, ftMap) { await app.setControlChannel(channel.defaultChannels.amp, loadData.amp); await app.setControlChannel(channel.defaultChannels.pan, loadData.pan); channel.setName(loadData.name); for (var i in channel.clips) { channel.clips[i].remove(); delete channel.clips[i]; } for (let cl of loadData.clips) { if (cl.table[0]) { cl.table[0] = ftMap[cl.table[0]]; } if (cl.table[1]) { cl.table[1] = ftMap[cl.table[1]]; } var c = new Clip(twine); c.importData(cl); channel.addClip(c); } }; this.addClip = function(clip) { channel.clips[clip.data.id] = clip; clip.channel = channel; elChannelClips.append(clip.element); clip.redraw(); }; this.removeClip = function(clip) { //clip.element.detach(); //clip.channel = null; channel.clips[clip.data.id] = null; //delete channel.clips[clip.data.id]; }; this.changeBeatEnd = function(original, newBeatEnd, noredraw) { var ratio = original / newBeatEnd; for (let s in splines) { splines[s].spline.resize(ratio, noredraw); } }; this.getCsChannelName = function() { return "mxchan" + channel.index; }; var elAddInsert; var elInsertsArea; var insertID = 0; function removeInsert(id, noCompile) { var index; for (let ci in channel.inserts) { if (channel.inserts[ci].id == id) { index = ci; break; } } if (index == null) return; var i = channel.inserts[index]; i.element.remove(); i.transform.remove(); channel.inserts.splice(index, 1); refreshInserts(); if (!noCompile) channel.compileInstr(); } async function createDefaultSplines() { var splineColour = getComputedStyle(document.body).getPropertyValue("--fgColor2"); var ampVal, panVal; if (twine.offline) { ampVal = 1; panVal = 0; } else { ampVal = await app.getControlChannel(channel.defaultChannels.amp); panVal = await app.getControlChannel(channel.defaultChannels.pan); } var def = [ {name: "default_amp", dfault: 1, constraints: [0, 2, ampVal, 0.00001], channel: channel.defaultChannels.amp}, {name: "default_pan", dfault: 0, constraints: [-1, 1, panVal, 0.00001], channel: channel.defaultChannels.pan} ]; for (let d of def) { splines[d.name] = { element: $("").addClass("twca" + channel.index).addClass("twine_spline").attr("id", "twca" + channel.index + d.name).hide().appendTo(elChannelSpline), show: function() { $(".twca" + channel.index).hide(); splines[d.name].element.show(); splines[d.name].spline.redraw(); }, hide: function() { splines[d.name].element.hide(); }, channel: d.channel }; } for (let d of def) { splines[d.name].spline = new SplineEdit( splines[d.name].element, splineColour, twine.timeline.getTotalBeatDuration, d.constraints, d.name ) } } function addInsert(definition, noCompile) { var container = $("").addClass("twine_channeldetails_insert").appendTo(elInsertsArea); container.css("left", (channel.inserts.length * 500) + "px"); var id = insertID ++; var uniqueID = parseInt(channel.index.toString() + id.toString()); var t = new twirl.transform.Transform({ uniqueID: uniqueID, element: container, definition: definition, splineElement: elChannelSpline, getRegionFunc: function() { return [0, 1]; }, getDurationFunc: timeline.getTotalBeatDuration, onClose: function() { removeInsert(id); }, onAutomationClick: function(state) { if (state && channel.height == heights[0]) { channel.expand(); // TODO: set selected thing in dropdown } }, unmanagedAutomation: true, unmanagedModulation: true, host: twine }); channel.inserts.push({ transform: t, element: container, id: id }); for (let i in t.parameters) { let paramID = (channel.inserts.length - 1) + "_" + i; let tp = t.parameters[i]; splines[paramID] = { element: $("").addClass("twca" + channel.index).addClass("twine_spline").attr("id", "twca" + channel.index + paramID).hide().appendTo(elChannelSpline), show: function() { $(".twca" + channel.index).hide(); splines[paramID].element.show(); splines[paramID].spline.redraw(); }, hide: function() { splines[paramID].element.hide(); }, channel: tp.sendChannel }; tp.createAutomationSpline(splines[paramID].element); splines[paramID].spline = tp.automation; } refreshAutomationSelectors(); if (!noCompile) channel.compileInstr(); } this.hasOverlap = function(clip, proposedBeat, proposedLength) { if (!proposedBeat) proposedBeat = clip.data.position; if (!proposedLength) proposedLength = clip.data.playLength; var clipEnd; var proposedEnd; for (var i in channel.clips) { var c = channel.clips[i]; if (c && c != clip) { clipEnd = c.data.position + c.data.playLength; proposedEnd = proposedBeat + proposedLength; if ((proposedBeat < clipEnd && proposedEnd > c.data.position) || (c.data.position > proposedBeat && clipEnd < proposedEnd)) { return true; } } } return false; }; function refreshInserts() { elInsertsArea.empty(); for (let i in channel.inserts) { var el = channel.inserts[i].element; el.css("left", (i * 500) + "px"); elInsertsArea.append(el); } } this.showDetails = function() { if (!elAddInsert) { elAddInsert = $("").addClass("twine_channeldetails_insertnew"); elInsertsArea = $("").addClass("twine_channeldetails_inserts"); var data = {}; for (var c of twirl.appdata.transforms) { var catName = c.name; for (var t of c.contents) { if (t.twine) { if (!data[c.name]) { data[c.name] = { name: c.name, description: c.description, contents: [] }; } data[c.name].contents.push(t); } } } var ttv = new twirl.transform.TreeView({ element: elAddInsert, items: Object.values(data), click: addInsert }); } if (twine.timeline.selectedChannel != channel) { twine.timeline.selectedChannel = channel; $(".twine_channelcontrol").css("background-color", "var(--bgColor2)"); elChannelControl.css("background-color", "var(--bgColor1)"); $("#twine_channeldetails").append(elAddInsert).append(elInsertsArea).show(); } twine.ui.showPane(twine.ui.pane.CHANNEL); }; this.compileInstr = async function() { if (twine.offline) return; var instrName = "twine_channel" + channel.index; var instr = "instr " + instrName + "\n" + "aL, aR bus_read \"" + channel.getCsChannelName() + "\"\n"; for (let i of channel.inserts) { instr += "chnset aL, \"twstfeedL\"\n" + "chnset aR, \"twstfeedR\"\n" + "aoutL, aoutR subinstr \"" + i.transform.instr + "\", " + i.transform.uniqueID + "\n" + "aL, aR twst_setapplymode chnget:i(\"applymode" + i.transform.uniqueID + "\"), aL, aR, aoutL, aoutR\n" } instr += "kamp chnget \"" + channel.defaultChannels.amp + "\"\n" + "kpan chnget \"" + channel.defaultChannels.pan + "\"\n" + "kmute chnget \"" + channel.defaultChannels.mute + "\"\n" + "ksolo chnget \"" + channel.defaultChannels.solo + "\"\n" + "kaudible = (1 - kmute) * ((ksolo == -1 || ksolo == " + channel.index + ") ? 1 : 0)\n" + "aL *= kamp * (1 - kpan) * kaudible\naR *= kamp * kpan * kaudible\n" instr += "bus_mix \"twine_master\", aL, aR\n"; instr += "endin"; await app.compileOrc(instr); }; this.automationChanged = function() { var changed = false; for (let i in splines) { if (splines[i].spline && splines[i].spline.changed) { console.log("automation changed"); return true; } } return changed; }; this.getAutomationData = function(start, end) { var data = []; for (let i in splines) { var linsegData = splines[i].spline.getLinsegData(start, end, true); if (linsegData) { data.push("chnset linseg:k(" + linsegData + "), \"" + splines[i].channel + "\""); } } return data; }; this.refreshOffset = function() { channel.offset = elChannelClips.offset(); }; this.redraw = function(noClipWaveRedraw) { channel.width = parseFloat(elChannelClips.css("width")); channel.refreshOffset(); refreshAutomationSelectors(); for (let c in channel.clips) { if (channel.clips[c]) channel.clips[c].redraw(noClipWaveRedraw); } if (channel.height > heights[0]) { for (var d in splines) { console.log(splines[d]); splines[d].spline.setRange(timeline.data.regionStart, timeline.data.regionEnd); } } }; this.showAutomation = function(groupIndex, nameIndex) { elAutomationSelectGroup.val(groupIndex).change(); elAutomationSelector.val(nameIndex).change(); }; var elAutomationSelectGroup; var elAutomationSelector; function refreshAutomationSelectors() { if (channel.height <= heights[0]) { if (elAutomationSelectGroup && elAutomationSelector) { elAutomationSelectGroup.hide(); elAutomationSelector.hide(); } return; } var valGroup = (elAutomationSelectGroup) ? elAutomationSelectGroup.val() : 0; var valItem = (elAutomationSelector) ? elAutomationSelector.val() : 0; var items = [ {name: "Mixer", items: [ {name: "Gain", onselect: function(){ splines.default_amp.show(); }}, {name: "Pan", onselect: function(){ splines.default_pan.show(); }} ]} ]; for (let i in channel.inserts) { let ci = channel.inserts[i]; var obj = {name: ci.transform.name, items: []}; items.push(obj); for (let it in ci.transform.parameters) obj.items.push({ name: ci.transform.parameters[it].definition.name, onselect: function() { splines[i + "_" + it].show(); } }); } if (!elAutomationSelectGroup && !elAutomationSelector) { var el = $("").addClass("twine_automationselectors").appendTo(elChannelControl); elAutomationSelectGroup = $("").addClass("twine_automationselect").appendTo(el).on("change", changeAutomationGroup); el.append("