From 9fbf91db06a6d4f4b5cd8bb45389a731bb86bf22 Mon Sep 17 00:00:00 2001 From: Richard Date: Sun, 13 Apr 2025 18:48:02 +0100 Subject: initial --- site/app/twist/_unlive/twist_instance_WIP.js | 350 +++++++++++++++++++++++++++ 1 file changed, 350 insertions(+) create mode 100644 site/app/twist/_unlive/twist_instance_WIP.js (limited to 'site/app/twist/_unlive/twist_instance_WIP.js') diff --git a/site/app/twist/_unlive/twist_instance_WIP.js b/site/app/twist/_unlive/twist_instance_WIP.js new file mode 100644 index 0000000..1c56cb6 --- /dev/null +++ b/site/app/twist/_unlive/twist_instance_WIP.js @@ -0,0 +1,350 @@ +var TwistInstance = function(index, twist) { + var self = this; + this.appdata = appdata; + this.waveform = null; + var waveformFile; + var waveformTab; + this.onPlays = []; + var sr = 44100; + var undoLevel; + + function pushOperationLog(operation) { + var max = twist.storage.commitHistoryLevel; + if (!max) { + twist.storage.commitHistoryLevel = max = 16; + } + if (operationLog.length + 1 >= max) { + operationLog.shift(); + } + operationLog.push(operation); + } + + this.redraw = function() { + self.waveform.redraw(); + }; + + this.close = function() { + self.waveform.destroy(); + delete self.waveform; + }; + + this.show = function() { + self.waveform.show(); + }; + + this.movePlayhead = function() { + + }; + + function removeInstance(i) { + if (i < 0 || i > this.waveforms.length - 2) { + return; + } + self.waveform.destroy(); + if (instanceIndex == i) { + instanceIndex = i + ((i == 0) ? 1 : -1); + self.waveform.show(); + } + } + + + this.undo = function() { + if (playing) return; + self.waveform.cover(true); + operation("twst_undo", globalCallbackHandler, true, null, true); + }; + + this.cut = function() { + if (playing) return; + self.waveform.cover(true); + operation("twst_cut", globalCallbackHandler, true); + }; + + this.delete = function() { + if (playing) return; + self.waveform.cover(true); + operation("twst_delete", globalCallbackHandler, true); + }; + + this.copy = function() { + if (playing) return; + self.waveform.cover(true); + operation("twst_copy", null, true); + }; + + this.paste = function() { + if (playing) return; + self.waveform.cover(true); + operation("twst_paste", globalCallbackHandler, true); + // keep original play position / offset new + }; + + this.moveToStart = function() { + if (playing) return; + self.waveform.setSelection(0); + }; + + this.moveToEnd = function() { + if (playing) return; + self.waveform.setSelection(1); + }; + + this.selectAll = function() { + if (playing) return; + self.waveform.setSelection(0, 1); + }; + + this.selectNone = function() { + if (playing) return; + self.waveform.setSelection(0); + }; + + this.selectToEnd = function() { + if (playing) return; + self.waveform.alterSelection(null, 1); + } + + this.selectFromStart = function() { + if (playing) return; + self.waveform.alterSelection(0, null); + } + + this.play = function() { + if (playing) return; + auditioning = false; + recording = false; + operation("twst_play", playPositionHandler(), false, null, true); + }; + + this.stop = function() { + if (!playing) return; + self.waveform.cover(false); + app.insertScore("twst_stop"); + }; + + function getAutomationData(start, end) { + var calls = []; + if (!self.currentTransform) return calls; + var automations = self.currentTransform.getAutomationData(start, end); + if (automations && automations.length > 0) { + for (let i in automations) { + if (automations[i].type == "modulation") { + calls.push(automations[i].data[0] + " \\\"" + automations[i].data[1] + "\\\""); + } else if (automations[i].type == "automation") { + calls.push("chnset linseg:k(" + automations[i].data + "), \\\"" + automations[i].channel + "\\\""); + } + } + } + return calls; + } + + function handleAutomation(onready, calls) { + if (calls.length == 0) { + return onready(0); + } + var cbid = app.createCallback(function(ndata){ + if (ndata.status == 1) { + onready(1); + } else { + self.errorHandler("Cannot parse automation data"); + } + }); + + var call = [0, 1, cbid]; + for (let c of calls) { + call.push(c); + } + app.insertScore("twst_automationprepare", call); + } + + function compileVariScript(script, onComplete) { + var cbid = app.createCallback(function(ndata){ + onComplete(ndata.status == 1); + // should maybe automatically refresh + }); + } + + + function fftsizeCheck(selected, duration) { + if (self.currentTransform) { + for (var p in self.currentTransform.parameters) { + if (p.indexOf("fftsize") != -1) { + var val = self.currentTransform.parameters[p].getValue(); + var minTime = (val / sr) * 2; + if ((selected[1] - selected[0]) * duration < minTime) { + return false; + } + } + } + } + return true; + } + + this.record = async function() { + if (playing) return; + auditioning = false; + recording = true; + await app.enableAudioInput(); + errorState = "Recording error"; + self.waveform.cover(true); + var cbid = playPositionHandler(); + var s = self.waveform.selected; + var items = [0, 1, cbid, s[0], s[1], s[2]]; + app.insertScore("twst_record", items); + }; + + this.audition = function() { + if (playing) return; + if (!self.currentTransform) { + return self.play(); + } + self.currentTransform.saveState(); + var s = self.waveform.selected; + if (!fftsizeCheck(s, self.waveform.duration)) { + return self.errorHandler("Length too short for this transform"); + } + + auditioning = true; + recording = false; + errorState = "Playback error"; + handleAutomation(function(automating){ + var cbid = playPositionHandler(); + var items = [ + 0, 1, cbid, s[0], s[1], s[2], + self.currentTransform.instr, automating, + elCrossfades[0].val(), elCrossfades[1].val() + ]; + app.insertScore("twst_audition", items); + }, getAutomationData(s[0], s[1])); + + }; + + + var scriptStack = []; + function applyScript(audition) { + if (playing) return; + var lastData; + var script = scriptStack.shift(); + if (!script) { + setLoadingStatus(false); + if (lastData) { + console.log("ass", lastData); + globalCallbackHandler(lastData); + } + twist.setPlaying(false); + return; + } + + if (audition) auditioning = true; + twist.setPlaying(true); + if (script.type == "operation") { + if (audition) { + return self.errorHandler("Only transform scripts can be auditioned"); + } + self.waveform.cover(true); + onComplete = (script.instr == "twst_copy") ? null : globalCallbackHandler; + operation(script.instr, function(ndata){ + lastData = ndata; + self.setPlaying(false); + applyScript(audition); + }, true, script.selection); + } else if (script.type == "transform") { + errorState = ((audition) ? "Audition" : "Transform" ) + " commit error"; + if (!audition) { + setLoadingStatus(true, true); + } + + for (let channel in script.channels) { + app.setControlChannel(channel, script.channels[channel]); + } + handleAutomation(function(automating){ + if (audition) { + var cbid = playPositionHandler(); + } else { + var cbid = app.createCallback(function(ndata) { + lastData = ndata; + self.setPlaying(false); + applyScript(audition); + }); + } + var instr = "twst_" + ((audition) ? "audition" : "commit"); + + app.insertScore(instr, [ + 0, -1, cbid, script.selection[0], script.selection[1], script.selection[2], script.instr, automating, script.crossfades[0], script.crossfades[1] + ]); + }, script.automation); + } + } + + this.applyScript = async function(script, audition) { + if (playing) return; + scriptStack = []; + if (Array.isArray(script)) { + if (audition) { + return self.errorHandler("Only single scripts can be auditioned"); + } + scriptStack = script; + } else { + scriptStack = [script]; + } + if (self.storage.autosave && !audition) { + self.saveFile(null, function() { + applyScript(audition); + }); + } else { + applyScript(audition); + } + }; + + async function innerCommit() { + if (playing) return; + if (!self.currentTransform) return; + var s = self.waveform.selected; + if (!fftsizeCheck(s, self.waveform.duration)) { + return self.errorHandler("Length too short for this transform"); + } + watchdog.start("commit"); + self.setPlaying(true); + setLoadingStatus(true, true); + var calls = getAutomationData(s[0], s[1]); + + self.currentTransform.saveState(); + var state = await self.currentTransform.getState(); + state.type = "transform"; + state.automation = calls; + state.crossfades = [elCrossfades[0].val(), elCrossfades[1].val()]; + state.selection = [s[0], s[1], s[2]]; + state.instanceIndex = instanceIndex; + pushOperationLog(state); + + handleAutomation(function(automating){ + var cbid = app.createCallback(function(ndata) { + watchdog.stop(); + setLoadingStatus(false); + self.setPlaying(false); + if (ndata.status > 0) { + globalCallbackHandler(ndata); + } else { + var text; + if (ndata.status == -2) { + text = "Resulting file is too large"; + } + self.errorHandler(text); + } + }); + errorState = "Transform commit error"; + app.insertScore("twst_commit", [0, -1, cbid, s[0], s[1], s[2], self.currentTransform.instr, automating, state.crossfades[0],state.crossfades[1]]); + }, calls); + } + + this.commit = async function() { + if (self.storage.autosave) { + self.saveFile(null, function() { + innerCommit(); + }); + } else { + innerCommit(); + } + }; + +}; // end twist \ No newline at end of file -- cgit v1.2.3