From 9fbf91db06a6d4f4b5cd8bb45389a731bb86bf22 Mon Sep 17 00:00:00 2001 From: Richard Date: Sun, 13 Apr 2025 18:48:02 +0100 Subject: initial --- site/app/twine/twine_ui.js | 506 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 506 insertions(+) create mode 100644 site/app/twine/twine_ui.js (limited to 'site/app/twine/twine_ui.js') diff --git a/site/app/twine/twine_ui.js b/site/app/twine/twine_ui.js new file mode 100644 index 0000000..3a98c75 --- /dev/null +++ b/site/app/twine/twine_ui.js @@ -0,0 +1,506 @@ +var twineTopMenuData = [ + {name: "File", contents: [ + {name: "Save", disableOnPlay: true, shortcut: {name: "Ctrl S", ctrlKey: true, key: "s"}, click: function(twine) { + twine.downloadExportData(); + }}, + ]}, + {name: "Edit", contents: [ + {shortcut: {name: "Ctrl Z", ctrlKey: true, key: "z"}, click: function(twine) { + twine.undo.apply(); + }, condition: function(twine) { + return twine.undo.has(); + }, name: function(twine) { + return "Undo " + twine.undo.lastName(); + }}, + {name: "Copy", shortcut: {name: "Ctrl C", ctrlKey: true, key: "c"}, click: function(twine) { + twine.copySelected(); + }, condition: function(twine) { + return twine.timeline.selectedClips.length > 0; + }} + ]}, + {name: "View", contents: [ + {name: "Zoom in", shortcut: {name: ",", key: ","}, click: function(twine){ + twine.timeline.zoomIn(); + }}, + {name: "Zoom out", shortcut: {name: ".", key: "."}, click: function(twine){ + twine.timeline.zoomOut(); + }}, + {name: "Show all", shortcut: {name: "/", key: "/"}, click: function(twine){ + twine.timeline.showAll(); + }}, + {preset: "divider"}, + {name: "Mixer", shortcut: {name: "M", key: "m"}, click: function(twine){ + twine.showMixer(); + }}, + {name: "Contract channels", shortcut: {name: "C", key: "c"}, click: function(twine) { + twine.timeline.contractChannels(); + }} + ]}, + {name: "Action", contents: [ + {name: "Play/stop", shortcut: {name: "Space", key: "space"}, click: function(twine) { + twine.ui.head.play.element.click(); + }}, + {preset: "divider"}, + {name: "Add channel", shortcut: {name: "A", key: "a"}, click: function(twine) { + twine.timeline.addChannel(); + }}, + {name: "Add script clip", click: function(twine) { + twine.timeline.addScriptClip(); + }}, + {name: "Add blank clip", click: function(twine) { + twine.timeline.addBlankClip(); + }}, + {preset: "divider"}, + {name: "Delete clip(s)", shortcut: {name: "Del", key: "delete"}, click: function(twine) { + twine.timeline.selectedClips.forEach(function(clip){ + clip.destroy(); + }); + twine.ui.showPane(twine.ui.pane.NONE); + }, condition: function(twine){ + return twine.timeline.selectedClips.length > 0; + }}, + {preset: "divider"}, + {name: "Render to file", click: function(twine) { + twine.renderToFile(); + }}, + {name: "Bounce", shortcut: {name: "A", key: "a"}, click: function(twine) { + twine.renderToClip(); + }} + ]}, + {name: "Options", contents: [ + {name: "Settings", click: function(twine) { + twine.ui.showSettings(); + }} + ]}, + {name: "Help", contents: [ + {name: "Help", click: function(twine){ + $("#twist_documentation")[0].click(); + }}, + {name: "About", click: function(twine) { + twine.ui.showAbout(); + }}, + ]} +]; + + +var TwineUI = function(twine) { + var ui = this; + ui.topMenu = new twirl.TopMenu(twine, twineTopMenuData, $("#twine_menubar")); + + ui.showSettings = function() { + var settings = [ + { + name: "Show master VU meter", + description: "Show the master VU mixer in the mixer view", + bool: true, + storageKey: "showMasterVu" + }, + { + name: "Show clip waveforms", + description: "Show waveforms in clips", + bool: true, + storageKey: "showClipWaveforms" + } + ]; + twirl.showSettings(twine, settings); + }; + + this.pane = {NONE: -1, MIXER: 0, CHANNEL: 1, CLIPAUDIO: 2, CLIPSCRIPT: 3}; + this.showPane = function(pane) { + var chd = $("#twine_channeldetails"); + var cld = $("#twine_clipdetails"); + if (pane == ui.pane.MIXER) { + twine.mixer.show(); + chd.hide(); + cld.hide(); + + } else if (pane == ui.pane.CHANNEL) { + twine.mixer.hide(); + chd.show(); + cld.hide(); + + } else if (pane >= 2) { + twine.mixer.hide(); + chd.hide(); + cld.show(); + var cda = $("#twine_clipdetailsaudio"); + var cds = $("#twine_clipdetailsscript"); + if (pane == ui.pane.CLIPAUDIO) { + cda.show(); + cds.hide(); + } else { + cda.hide(); + cds.show(); + } + } else if (pane == ui.pane.NONE) { + twine.mixer.hide(); + chd.hide(); + cld.hide(); + }; + }; + + ui.showAbout = function() { + var el = $("
"); + var x = $("
").appendTo(el); + var string = "twine"; + var intervals = []; + + function addChar(c, left) { + left = Math.min(Math.max(left, 30), 70); + var elC = $("

").text(c).css({position: "absolute", left: left + "%"}).appendTo(x); + var rate = (Math.random() * 0.1) + 0.15; + var leftDirection = Boolean(Math.round(Math.random())); + setTimeout(function(){ + intervals.push(setInterval(function(){ + if (leftDirection) { + if (left < 70) { + left += rate; + } else { + leftDirection = false; + } + } else { + if (left > 30) { + left -= rate; + } else { + leftDirection = true; + } + } + //console.log(left, rate, leftDirection); + elC.css("left", left + "%"); + }, (Math.random() * 20) + 20)); + }, (Math.random() * 1000) + 500); + } + var widthPercent = (40 / (string.length)); + for (let c in string) { + intervals.push(addChar(string[c], (widthPercent * c) + 30)); + } + + $("
").appendTo(el); + $("
").appendTo(el); + $("

").text("Version " + twine.version.toFixed(1)).appendTo(el); + $("

").css("font-size", "12px").text("By Richard Knight 2024").appendTo(el); + + twirl.prompt.show(el, function(){ + for (let i of intervals) clearInterval(i); + }); + }; + + function refreshWarpParams() { + var warp = ui.clip.warp.element.val(); + var warpMode = ui.clip.warpMode.element.val(); + + ui.clip.warpMode.hide(); + ui.clip.fftSize.hide(); + ui.clip.phaseLock.hide(); + ui.clip.txtWinSize.hide(); + ui.clip.txtRandom.hide(); + ui.clip.txtOverlap.hide(); + ui.clip.txtWinType.hide(); + + if (!warp) return; + ui.clip.warpMode.show(); + + if (warpMode == 1) { + ui.clip.txtWinSize.show(); + ui.clip.txtRandom.show(); + ui.clip.txtOverlap.show(); + ui.clip.txtWinType.show(); + } else if (warpMode > 1) { + ui.clip.fftSize.show(); + ui.clip.phaseLock.show(); + } + } + + function applyValToSelectedFunc(dataKey, applyFunc, undoName, noApplyUndo) { + if (!undoName) undoName = dataKey; + var func = function(val) { + if (!noApplyUndo) { + var selected = [...twine.timeline.selectedClips]; + var values = []; + } + twine.timeline.selectedClips.forEach(async function(clip){ + if (!noApplyUndo) { + values.push(clip.data[dataKey]); + } + applyFunc(clip, val); + }); + if (!noApplyUndo) { + twine.undo.add("clip " + undoName, function(){ + for (var i in selected) { + applyFunc(selected[i], values[i]); + if (twine.timeline.selectedClip == selected[i]) { + ui.clip[dataKey].setValue(values[i]); + } + } + }); + } + }; + return func; + } + + ui.clip = { + scriptEdit: new twirl.stdui.TextArea({ + target: "twine_clip_scriptedit", + height: "100%", + width: "100%" + + }), + scriptAudition: new twirl.stdui.PlayButton({ + target: "twine_clip_scriptaudition", + change: function(v, obj) { + if (obj.state == true) { + + } else { + app.insertScore("twine_scriptstop"); + } + } + }), + scriptApply: new twirl.stdui.StandardButton({ + target: "twine_clip_scriptapply", + label: "Apply script", + change: function() { + twine.timeline.selectedClip.setScript( + ui.clip.scriptEdit.element.val(), + function() { + twirl.prompt.show("Script successfully compiled"); + } + ); + } + }), + audition: new twirl.stdui.PlayButton({ + target: "twine_clip_audition", + tooltip: "Audition clip", + change: function(v, obj) { + if (obj.state == true) { + twine.timeline.selectedClip.play(function(ndata) { + if (ndata.status == 0) { + obj.setValue(false); + } + }); + } else { + twine.timeline.selectedClip.stop(); + } + } + }), + name: new twirl.stdui.TextInput({ + target: "twine_clip_name", + change: applyValToSelectedFunc("name", function(clip, val){ + clip.setData("name", val); + }), + css: { + border: "none" + } + }), + colour: new twirl.stdui.ColourInput({ + target: "twine_clip_colour", + change: applyValToSelectedFunc("colour", function(clip, val){ + clip.colour = val; + }), + css: { + border: "none" + } + }), + editTwist: new twirl.stdui.StandardButton({ + label: "Twist", + target: "twine_clip_edittwist", + change: function(e) { + twirl.contextMenu.show(e, [ + {name: "Edit all references", click: function(){ + twine.timeline.selectedClip.editInTwist(); + }}, + {name: "Edit as unique", click: function(){ + twine.timeline.selectedClip.editInTwist(true); + }} + ]); + } + }), + editTwigs: new twirl.stdui.StandardButton({ + label: "Twigs", + target: "twine_clip_edittwigs", + change: function(e) { + twirl.contextMenu.show(e, [ + {name: "Edit all references", click: function(){ + twine.timeline.selectedClip.editInTwigs(); + }}, + {name: "Edit as unique", click: function(){ + twine.timeline.selectedClip.editInTwigs(true); + }} + ]); + } + }), + warp: new twirl.stdui.StandardToggle({ + label: "Warp", + target: "twine_clip_warp", + change: applyValToSelectedFunc("warp", function(clip, val){ + clip.setWarp(val); + }), + stateAlter: function(val) { + refreshWarpParams(); + } + }),/* + loop: new twirl.stdui.StandardToggle({ + label: "Loop", + target: "twine_clip_loop", + change: function(val) { + twine.timeline.selectedClip.setLoop(val); + } + }),*/ + warpMode: new twirl.stdui.ComboBox({ + target: "twine_clip_warpmode", + options: [ + "Repitch", "Grain", "Mince" //, "FFTab" + ], + change: applyValToSelectedFunc("warpMode", function(clip, val){ + clip.setWarpMode(val); + }, "warp mode"), + stateAlter: function(val) { + refreshWarpParams(); + } + }), + amp: new twirl.stdui.Slider({ + label: "Gain", + valueLabel: true, + value: 1, + min: 0, + max: 2, + size: 32, + target: "twine_clipparamsbottom", + input: applyValToSelectedFunc("amp", function(clip, val){ + clip.setData("amp", val); + }, "gain", true), + change: applyValToSelectedFunc("amp", function(clip, val){ + clip.setData("amp", val); + }, "gain") + }), + pitch: new twirl.stdui.Slider({ + label: "Pitch", + valueLabel: true, + min: -12, + max: 12, + step: 1, + value: 0, + size: 32, + target: "twine_clipparamsbottom", + input: applyValToSelectedFunc("pitch", function(clip, val){ + clip.setPitch(val); + }, null, true), + change: applyValToSelectedFunc("pitch", function(clip, val){ + clip.setPitch(val); + }) + }), + fftSize: new twirl.stdui.ComboBox({ + label: "FFT Size", + asRow: true, + target: "twine_clipparamsbottom", + options: [ + "256", "512", "1024", "2048" + ], + asValue: true, + change: applyValToSelectedFunc("fftSize", function(clip, val){ + clip.setData("fftSize", val); + }, "FFT size") + }), + phaseLock: new twirl.stdui.StandardToggle({ + label: "Phase lock", + target: "twine_clipparamsbottom", + change: applyValToSelectedFunc("phaseLock", function(clip, val){ + clip.setData("phaseLock", val); + }, "phase lock"), + stateAlter: function(val) { + refreshWarpParams(); + } + }), + txtWinSize: new twirl.stdui.Slider({ + label: "Window size", + valueLabel: true, + min: 44, + max: 4410, + step: 1, + value: 4410, + size: 32, + target: "twine_clipparamsbottom", + change: applyValToSelectedFunc("txtWinSize", function(clip, val){ + clip.setData("txtWinSize", val); + }, "window size") + }), + txtRandom: new twirl.stdui.Slider({ + label: "Window random", + valueLabel: true, + min: 0, + max: 441, + step: 1, + value: 441, + size: 32, + target: "twine_clipparamsbottom", + change: applyValToSelectedFunc("txtRandom", function(clip, val){ + clip.setData("txtRandom", val); + }, "window random") + }), + txtOverlap: new twirl.stdui.Slider({ + label: "Window overlap", + valueLabel: true, + min: 0, + max: 16, + step: 1, + value: 4, + size: 32, + target: "twine_clipparamsbottom", + change: applyValToSelectedFunc("txtOverlap", function(clip, val){ + clip.setData("txtOverlap", val); + }, "window overlap") + }), + txtWinType: new twirl.stdui.ComboBox({ + label: "Window type", + asRow: true, + target: "twine_clipparamsbottom", + options: [ + "Hanning", "Hamming", "Half sine" + ], + change: applyValToSelectedFunc("txtWinType", function(clip, val){ + clip.setData("txtWinType", val); + }, "window type") + }) + }; + + ui.head = { + play: new twirl.stdui.PlayButton({ + target: "twine_head_play", + fontsize: "14pt", + change: function(v, obj) { + if (obj.state == true) { + twine.play(); + } else { + twine.stop(); + obj.setValue(false); + } + } + }), + snap: new twirl.stdui.StandardToggle({ + label: "Snap", + target: "twine_head_snap", + change: function(val) { + val = (val) ? 4 : 0; + twine.timeline.data.snapToGrid = val; + } + }), + grid: new twirl.stdui.StandardToggle({ + label: "Grid", + target: "twine_head_showgrid", + change: function(val) { + twine.timeline.data.gridVisible = val; + twine.timeline.redraw(); + } + }), + name: new twirl.stdui.TextInput({ + target: "twine_head_name", + css: { + border: "none", + "font-family": "var(--fontFace)", + "font-size": "var(--fontSizeLarge)", + }, + change: function(val) { + twine.timeline.arrangementName = val; + } + }) + }; +}; \ No newline at end of file -- cgit v1.2.3