aboutsummaryrefslogtreecommitdiff
path: root/site/app/twine/_hOLD
diff options
context:
space:
mode:
authorRichard <q@1bpm.net>2025-04-13 18:48:02 +0100
committerRichard <q@1bpm.net>2025-04-13 18:48:02 +0100
commit9fbf91db06a6d4f4b5cd8bb45389a731bb86bf22 (patch)
tree291bd79ce340e67affa755a8a6b4f6a83cce93ea /site/app/twine/_hOLD
downloadapps.csound.1bpm.net-9fbf91db06a6d4f4b5cd8bb45389a731bb86bf22.tar.gz
apps.csound.1bpm.net-9fbf91db06a6d4f4b5cd8bb45389a731bb86bf22.tar.bz2
apps.csound.1bpm.net-9fbf91db06a6d4f4b5cd8bb45389a731bb86bf22.zip
initial
Diffstat (limited to 'site/app/twine/_hOLD')
-rw-r--r--site/app/twine/_hOLD/clip.js499
-rw-r--r--site/app/twine/_hOLD/index.html863
-rw-r--r--site/app/twine/_hOLD/index_old.html102
-rw-r--r--site/app/twine/_hOLD/index_workingold.html1251
-rw-r--r--site/app/twine/_hOLD/timeline.csd154
-rw-r--r--site/app/twine/_hOLD/timeline_base.html633
6 files changed, 3502 insertions, 0 deletions
diff --git a/site/app/twine/_hOLD/clip.js b/site/app/twine/_hOLD/clip.js
new file mode 100644
index 0000000..1c4cc48
--- /dev/null
+++ b/site/app/twine/_hOLD/clip.js
@@ -0,0 +1,499 @@
+var Clip = function(twine, data, parent) {
+ var clip = this;
+ var loaded = false;
+ var waveformClip;
+ var waveformEdit;
+ var datatable;
+ this.channel = null;
+ var minWidth = 10;
+
+ if (!data) {
+ var id = twine.getNewID();
+ var data = {
+ name: "Clip " + id,
+ channel: -1,
+ id: id,
+ clipindex: null,
+ playLength: 1,
+ colour: "#" + (Math.random() * 0xFFFFFF << 0).toString(16),
+ position: 0,
+ // debugs:
+ duration: 1,
+ warp: false,
+ loop: false
+ };
+ } else {
+ loaded = true;
+ }
+ this.data = data;
+ Object.defineProperty(this, "colour", {
+ get: function() { return data.colour; },
+ set: function(x) {
+ data.colour = x;
+ clip.element.css("background-color", data.colour);
+ }
+ });
+
+ this.exportData = function() {
+ return data;
+ };
+
+ this.destroy = function(onComplete) {
+ var cbid = app.createCallback(function(ndata){
+ clip.element.remove();
+ clip.channel.removeClip(clip);
+ if (onComplete) {
+ onComplete(ndata);
+ }
+ });
+ app.insertScore("twine_removeclip", [0, 1, cbid, clip.data.clipindex]);
+ };
+
+ var dataMode = {
+ fnL: 0, fnR: 1, divisionsPerBeat: 2, duration: 3, beatsLength: 4, utilisedLength: 5,
+ warpMode: 6, pitch: 7, amp: 8, fftSize: 9, txtWinSize: 10, txtRandom: 11, txtOverlap: 12,
+ loop: 13, warp: 14, txtWinType: 15, // warp points are 16 + in table
+ position: -1, name: -2, channel: -3, clipindex: -4, playLength: -5
+ };
+
+
+ this.element = $("<div />").addClass("twine_clip").css({
+ "background-color": data.colour
+ }).click(function(e){
+ if (e.ctrlKey) {
+ twine.timeline.selectedClips.push(clip);
+ } else {
+ $(".twine_clip").css("outline", "none");
+ twine.timeline.selectedClips = [clip];
+ }
+console.log("ch", clip.data);
+ clip.element.css("outline", "1px dashed white");
+
+ var cui = twine.ui.clip;
+ cui.name.setValue(data.name);
+ cui.colour.setValue(data.colour);
+ cui.amp.setValue(data.amp);
+ cui.warp.setValue(data.warp);
+ cui.loop.setValue(data.loop);
+ cui.readType.setValue(data.warpMode);
+ cui.pitch.setValue(Math.round((Math.log(data.pitch) / Math.log(2)) * 12));
+ cui.fftSize.setValue(data.fftSize);
+ cui.winSize.setValue(data.txtWinSize);
+ cui.winRandom.setValue(data.txtRandom);
+ cui.winOverlap.setValue(data.txtOverlap);
+ cui.winType.setValue(data.txtWinType);
+
+ showEditWaveform($("#twine_clipdetailsright"));
+ twine.ui.showPane(twine.ui.pane.CLIP);
+ });
+ var elWaveClip = $("<div />").css({position: "absolute", width: "100%", height: "100%", top: "0px", left: "0px"}).appendTo(clip.element);
+ var elWaveText = $("<div />").css({position: "absolute", width: "100%", height: "100%", top: "0px", left: "0px", "font-size": "var(--fontSizeSmall)", color: "var(--fgColor1)"}).text(data.name).appendTo(clip.element);
+
+ var elResizeLeft = $("<div />").addClass("twine_clip_edge_left").appendTo(clip.element);
+ var elResizeRight = $("<div />").addClass("twine_clip_edge_right").appendTo(clip.element);
+ var elMove = $("<div />").addClass("twine_clip_centre").appendTo(clip.element);
+ var elWaveEdit = $("<div />").css({width: "100%", height: "100%", top: "0px", left: "0px"});
+
+ async function getDataFromTable(key) {
+ async function setFromKey(key) {
+ if (dataMode[key] < 0) return;
+ var value = await app.getCsound().tableGet(datatable, dataMode[key])
+ data[key] = value;
+ }
+
+ for (var k in dataMode) {
+ setFromKey(k);
+ }
+ }
+
+ function setClipAudioUnique(onComplete) {
+ var cbid = app.createCallback(async function(ndata){
+ await getDataFromTable();
+ if (onComplete) onComplete();
+ });
+ app.insertScore("twine_setclipaudiounique", [0, 1, cbid]);
+ }
+
+ function reloadAfterEdit(tables) {
+ twirl.loading.show("Loading");
+ var cbid = app.createCallback(async function(ndata){
+ datatable = ndata.datatable;
+ await getDataFromTable();
+ clip.redraw();
+ twine.setVisible(true);
+ twigs.setVisible(false);
+ twist.setVisible(false);
+ twirl.loading.hide();
+ });
+ var call = [0, 1, cbid, data.clipindex];
+ for (let t of tables) {
+ call.push(t);
+ }
+ app.insertScore("twine_clipreplacetables", call);
+ }
+
+ this.editInTwist = function(asUnique) {
+ if (!window.twist) return twirl.prompt.show("twist is unavailable in this session");
+ function edit() {
+ twist.boot(twine);
+ twist.bootAudio(twine);
+ var tables = [data.fnL];
+ if (data.fnR) tables.push(data.fnR);
+ twist.loadFileFromFtable(data.name, tables, function(ndata){
+ if (ndata.status > 0) {
+ twine.setVisible(false);
+ twist.setVisible(true);
+ }
+ }, reloadAfterEdit);
+ }
+ if (asUnique) {
+ setClipAudioUnique(edit);
+ } else {
+ edit();
+ }
+
+ };
+
+ this.editInTwigs = function(asUnique) {
+ if (!window.twigs) return twirl.prompt.show("twigs is unavailable in this session");
+ function edit() {
+ twigs.boot(twine);
+ var tables = [data.fnL];
+ if (data.fnR) tables.push(data.fnR);
+ twigs.loadFileFromFtable(data.name, tables, function(ndata){
+ if (ndata.status > 0) {
+ twine.setVisible(false);
+ twigs.setVisible(true);
+ }
+ }, reloadAfterEdit);
+ }
+ if (asUnique) {
+ setClipAudioUnique(edit);
+ } else {
+ edit();
+ }
+ };
+
+ this.setData = function(modeString, v, onComplete) {
+ data[modeString] = v;
+ if (dataMode[modeString] < 0) {
+ if (modeString == "name") {
+ elWaveText.text(data.name);
+ }
+ return;
+ }
+
+ function doSetData() {
+ if (!twirl.debug) app.getCsound().tableSet(datatable, dataMode[modeString], v);
+ }
+
+ doSetData();
+ if (onComplete) onComplete();
+ };
+
+ this.getPlaybackArgs = function(cbid, time) {
+ return [(time) ? time: 0, data.playLength, cbid, data.clipindex, data.playLength, clip.channel.getCsChannelName()];
+ };
+
+ this.play = function(onCallback) {
+ var cbid = app.createCallback(function(ndata) {
+ if (onCallback) onCallback(ndata);
+ }, true);
+ app.insertScore("ecp_playback_tst", clip.getPlaybackArgs(cbid));
+ }
+
+ async function getSourceTableData() {
+ var wavedata = [];
+ var tbL = await app.getTable(data.fnL);
+ wavedata.push(tbL);
+ if (data.hasOwnProperty("fnR") && data.fnR > 0) {
+ var tbR = await app.getTable(data.fnR);
+ wavedata.push(tbR);
+ }
+ return wavedata;
+ }
+
+ async function setClipWaveform(noRedraw) {
+ if (!waveformClip) {
+ waveformClip = new Waveform({
+ target: elWaveClip,
+ allowSelect: false,
+ showGrid: false,
+ bgColor: "rgb(255, 255, 255, 0)",
+ fgColor: "#000000"
+ });
+ setTimeout(async function(){
+ var sourceTables = await getSourceTableData();
+ waveformClip.setData(sourceTables, data.duration);
+ }, 100);
+ } else {
+ if (!noRedraw) waveformClip.redraw();
+ }
+ }
+
+ async function showEditWaveform(target) {
+ target.empty().append(elWaveEdit);
+ if (!waveformEdit) {
+ waveformEdit = new Waveform({
+ target: elWaveEdit,
+ allowSelect: true,
+ showGrid: true,
+ latencyCorrection: twirl.latencyCorrection // , markers:
+ });
+ setTimeout(async function(){
+ var sourceTables = await getSourceTableData();
+ waveformEdit.setData(sourceTables, data.duration);
+ }, 100);
+ } else {
+ waveformEdit.redraw();
+ }
+ }
+
+ this.setWarp = function(v) {
+ clip.setData("warp", v);
+ if (!data.warp && !data.loop && data.playLength > data.duration) {
+ data.playLength = data.duration;
+ clip.setSize();
+ }
+ };
+
+ this.setLoop = function(v) {
+ clip.setData("loop", v);
+ if (!data.warp && !data.loop && data.playLength > data.duration) {
+ data.playLength = data.duration;
+ clip.setSize();
+ }
+ };
+
+ this.setPitch = function(semitones) {
+ var pitchRatio = Math.pow(2, (semitones / 12));
+ clip.setData("pitch", pitchRatio);
+ if (data.warpMode == 0 && data.loop == 0 && data.warp == 0) {
+ data.playLength = data.duration / pitchRatio;
+ clip.setSize();
+ }
+ };
+
+ this.setWarpMode = function(v) {
+ var prevMode = data.warpMode;
+ clip.setData("warpMode", v);
+ if (prevMode == 0 && data.warpMode != 0 && !data.loop && !data.warp) {
+ data.playLength = data.duration;
+ clip.setSize();
+ }
+ };
+
+ this.setSize = function(noWaveRedraw) {
+ var width = data.playLength * twine.timeline.pixelsPerBeat;
+ clip.element.css("width", width + "px");
+ setClipWaveform(noWaveRedraw);
+ }
+
+ this.redraw = function(noWaveRedraw) {
+ if (!loaded) return;
+ var b = twine.timeline.beatRegion;
+ clip.setSize(noWaveRedraw);
+ if ((data.position + data.playLength) < b[0] || data.position > b[1]) {
+ return clip.element.hide();
+ }
+
+ var css = {
+ height: clip.channel.height + "px",
+ left: (data.position - b[0]) * twine.timeline.pixelsPerBeat + "px"
+ };
+ if (data.position < b[0]) {
+ css.left = "0px";
+ css.width = (data.playLength - (b[0] - data.position)) * twine.timeline.pixelsPerBeat + "px";
+ }
+
+ clip.element.show().css(css);
+ };
+
+ this.clone = function() {
+ var newData = Object.assign({}, data);
+ newData.id = twine.getNewID();
+ var c = new Clip(twine, newData, clip);
+ clip.channel.addClip(c);
+ app.insertScore("twine_cloneclip", [0, 1, app.createCallback(function(ndata) {
+ datatable = ndata.datatable;
+ c.loadFromDataTable(ndata.datatable, ndata.clipindex);
+ }), clip.data.clipindex]);
+ };
+
+
+ async function loadData(ndata, name, colour, defaultLength) {
+ twirl.loading.show("Loading");
+ if (ndata.status == -1) {
+ return twirl.errorHandler("File not valid");
+ } else if (ndata.status == -2) {
+ return twirl.errorHandler("File too large");
+ }
+ datatable = ndata.data.datatable;
+ await getDataFromTable();
+ data.clipindex = ndata.data.clipindex;
+ if (name) {
+ data.name = name;
+ }
+ if (defaultLength) {
+ data.playLength = data.duration;
+ }
+ elWaveText.text(data.name);
+ if (!colour) colour = twine.randomColour();
+ data.colour = colour;
+ loaded = true;
+ clip.redraw();
+ };
+
+ this.loadFromDataTable = async function(newDatatable, clipindex) {
+ datatable = newDatatable;
+ await getDataFromTable();
+ data.clipindex = clipindex;
+ loaded = true;
+ clip.redraw();
+ };
+
+ this.loadFromFtables = function(name, tables, colour) {
+ twirl.loading.show("Loading");
+ var cbid = app.createCallback(async function(ndata){
+ await loadData(ndata, name, colour);
+ twirl.loading.hide();
+ });
+ var call = [0, 1, cbid];
+ for (let t of tables) {
+ call.push(t);
+ }
+ app.insertScore("twine_loadftables", call);
+ };
+
+ this.loadFromPath = function(path, colour) {
+ twirl.loading.show("Loading");
+ var cbid = app.createCallback(async function(ndata){
+ await loadData(ndata, path, colour, true);
+ twirl.loading.hide();
+ });
+ app.insertScore("twine_loadpath", [0, 1, cbid, path]);
+ };
+
+ function getMaxClipWidth() {
+ var maxWidth = 9999;
+ if (!data.warp && !data.loop) {
+ maxWidth = data.duration * twine.timeline.pixelsPerBeat;
+ }
+ return maxWidth;
+ }
+
+ clip.movement = {
+ pos1: 0, pos2: 0, pos3: 0, pos4: 0, offset: 0, startX: 0, startY: 0, clipWidth: 0,
+ clipLeft: 0, clipTop: 0, lastLeft: 0, isCopying: false,
+ mouseDown: function(e, dragType) {
+ e.preventDefault();
+ twine.timeline.selectedClips.forEach(function(c){
+ c.movement.mouseDownInner(e, dragType);
+ });
+ },
+ mouseDownInner: function(e, dragType) {
+ e.preventDefault();
+ this.isCopying = false;
+ this.offset = clip.element.offset().left
+ this.clipWidth = parseFloat(clip.element.css("width"));
+ this.clipTop = parseFloat(clip.element.css("top"));
+ this.clipLeft = parseFloat(clip.element.css("left"));
+ this.startX = e.clientX - e.target.getBoundingClientRect().left;
+ this.startY = e.clientY - e.target.getBoundingClientRect().top;
+ this.pos3 = e.clientX;
+ this.pos4 = e.drag;
+ this.lastLeft = (this.pos3 - this.startX - clip.channel.offset.left);
+ $("html").on("mousemove", function(e){
+ clip.movement.doDrag(e, dragType);
+ }).on("mouseup", this.endDrag);
+ $("#container").css("cursor", "e-resize");
+ },
+ endDrag: function(e) {
+ e.preventDefault();
+ this.isCopying = false;
+ $("html").off("mouseup", this.endDrag).off("mousemove"); //, this.doDrag);
+ $("#container").css("cursor", "pointer");
+ },
+ doDrag: function(e, dragType) {
+ e.preventDefault();
+ twine.timeline.selectedClips.forEach(function(c){
+ c.movement.doDragInner(e, dragType);
+ });
+ },
+ doDragInner: function (e, dragType) {
+ e.preventDefault();
+ this.pos1 = this.pos3 - e.clientX;
+ this.pos2 = this.pos4 - e.clientY;
+ this.pos3 = e.clientX;
+ this.pos4 = e.clientY;
+
+ if (dragType == "right") {
+ var maxWidth = getMaxClipWidth();
+ var newWidth = (this.pos3 - this.startX - clip.channel.offset.left - this.clipLeft);
+ newWidth = twine.timeline.roundToGrid(newWidth);
+ if (newWidth > maxWidth) newWidth = maxWidth;
+ if (newWidth < minWidth) newWidth = minWidth;
+ data.playLength = newWidth / twine.timeline.pixelsPerBeat;
+ clip.element.css("width", newWidth + "px");
+
+ } else if (dragType == "left") {
+ var maxWidth = getMaxClipWidth();
+ var left = (this.pos3 - this.startX - clip.channel.offset.left);
+ left = twine.timeline.roundToGrid(left);
+ if (left < 0) left = 0;
+ var newWidth = (clipWidth - left) + this.clipLeft;
+ var cWidth, cLeft;
+ if (newWidth < minWidth) {
+ cWidth = minWidth, this.clipLeft + minWidth; //(minWidth - left) + clipLeft;
+ } else if (newWidth > maxWidth) {
+ cWidth = maxWidth, cLeft = this.lastLeft;
+ } else {
+ lastLeft = left;
+ cWidth = newWidth, cLeft = left;
+ }
+ clip.element.css({width: cWidth + "px", left: cLeft + "px"});
+
+ data.playLength = newWidth / twine.timeline.pixelsPerBeat;
+ data.position = Math.min(0, (left / twine.timeline.pixelsPerBeat) + twine.timeline.beatRegion[0]);
+
+ } else {
+ if (e.ctrlKey && !this.isCopying) {
+ this.isCopying = true;
+ clip.clone();
+ }
+
+ //var left = (this.pos3 - this.startX - clip.channel.offset.left);
+ var left = (this.pos3 - this.clipLeft - clip.channel.offset.left);
+ left = twine.timeline.roundToGrid(left);
+
+ //var top = (this.pos4 - this.startY - clip.channel.offset.top);
+ var top = (this.pos4 - this.clipTop - clip.channel.offset.top);
+ if (top > clip.channel.height) {
+ if (clip.channel.index + 1 < twine.timeline.channels.length) {
+ clip.channel.removeClip(clip);
+ twine.timeline.channels[clip.channel.index + 1].addClip(clip);
+ }
+ } else if (top < 0) {
+ if (clip.channel.index -1 >= 0) {
+ clip.channel.removeClip(clip);
+ twine.timeline.channels[clip.channel.index - 1].addClip(clip);
+ }
+ }
+ if (left < 0) left = 0;
+ //if (left > clip.channel.width - clipWidth) left = clip.channel.width - clipWidth;
+ if (left > clip.channel.width) left = clip.channel.width;
+ data.position = (left / twine.timeline.pixelsPerBeat) + twine.timeline.beatRegion[0];
+ clip.element.css("left", left + "px");
+ }
+ } // end doDragInner
+ };
+
+ elMove.mousedown(clip.movement.mouseDown);
+ elResizeRight.mousedown(function(e){
+ clip.movement.mouseDown(e, "right");
+ });
+ elResizeLeft.mousedown(function(e){
+ clip.movement.mouseDown(e, "left");
+ });
+
+}; \ No newline at end of file
diff --git a/site/app/twine/_hOLD/index.html b/site/app/twine/_hOLD/index.html
new file mode 100644
index 0000000..5e9e2b4
--- /dev/null
+++ b/site/app/twine/_hOLD/index.html
@@ -0,0 +1,863 @@
+<html>
+<head>
+<style type="text/css">
+
+body {
+ font-family: Arial, sans-serif;
+ font-size: 11pt;
+}
+
+#header {
+ position: absolute;
+ top: 0px;
+ height: 30px;
+ left: 0px;
+ width: 100%;
+ background-color: #545454;
+}
+
+#headertable {
+ height: 30px;
+}
+
+#clipdetails {
+ display: none;
+}
+
+
+#start {
+ z-index: 200;
+ position: fixed;
+ left: 0px;
+ top: 0px;
+ width: 100%;
+ height: 100%;
+ background-color: #343434;
+}
+
+#start_centre {
+ z-index: 201;
+ position: relative;
+ height: 200px;
+}
+
+#start_invoke {
+ z-index: 202;
+ margin: 0;
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ font-size: 72pt;
+ cursor: pointer;
+}
+
+#main {
+ position: absolute;
+ top: 30px;
+ height: 100%;
+ left: 0px;
+ width: 100%;
+}
+
+#timeline {
+ position: absolute;
+ left: 0px;
+ top: 0px;
+ width: 100%;
+ height: 70%
+}
+
+.timelinetext {
+ font-size: 8pt;
+ opacity: 0.9;
+ position: absolute;
+ color: #121212;
+ top: 2px;
+}
+
+.timelinemarker {
+ width: 1px;
+ position: absolute;
+ background-color: #bdbdbd;
+ opacity: 0.9;
+ height: 100%;
+ top: 0px;
+ z-index: 50;
+}
+
+.smbut {
+ font-size: 8pt;
+ background-color: #b5b01d;
+ border: 1px solid black;
+}
+
+
+
+#details {
+ position: absolute;
+ left: 0px;
+ top: 70%;
+ width: 100%;
+ height: 30%;
+ background-color: #dcdcdc;
+}
+
+#clipdetailsleft {
+ position: absolute;
+ left: 0px;
+ top: 0px;
+ width: 30%;
+ height: 100%;
+ background-color: #acacac;
+}
+
+#clipdetailsright {
+ position: absolute;
+ left: 30%;
+ top: 0px;
+ width: 70%;
+ height: 100%;
+ background-color: #cacaca;
+}
+
+.channel {
+ height: 30px;
+ width: 100%;
+ border-bottom: 1px solid #aaaaaa;
+ left: 0px;
+}
+
+.channeldetails {
+ position: absolute;
+ height: 30px;
+ left: 0px;
+ width: 10%;
+}
+
+.channelclips {
+ position: absolute;
+ height: 30px;
+ left: 10%;
+ width: 90%;
+ background-color: #cdcdcd;
+}
+
+.clip {
+ user-select: none;
+ position: absolute;
+ padding: 0px;
+ cursor: move;
+ z-index: 70;
+ width: 50px;
+ height: 30px;
+ color: #000000;
+ overflow: hidden;
+}
+</style>
+<script type="text/javascript" src="/code/jquery.js"></script>
+<script type="text/javascript" src="../base/base.js"></script>
+<script type="text/javascript" src="input-knobs.js"></script>
+<script type="text/javascript">
+
+window.app = new CSApplication(
+{
+ csdUrl: "timeline.csd",
+ files: ["test.mp3"],
+ csOptions: ["--omacro:ECP_NORECORDING=1"],
+ onPlay: function () {
+ $("#start").hide();
+ runTest();
+ }
+}
+);
+
+var clips = {};
+var channels = [];
+var maxID = 0;
+var chanHeight = 30;
+
+window.csseq = {};
+
+function getID() {
+ return maxID++;
+}
+
+
+var TimelineGrid = function(data) {
+ var self = this;
+
+ if (!data) {
+ var data = {
+ snapToGrid: true,
+ gridVisible: true,
+ timeSigMarker: 4,
+ resolution: null,
+ startBeat: 1,
+ endBeat: 16,
+ bpm: 120
+ };
+ }
+
+ this.getSaveData = function() {
+ return data;
+ };
+
+ function calcViewport() {
+ data.minLeft = (window.screen.width / 100) * 10;
+ var width = window.screen.width - data.minLeft;
+ var beats = data.endBeat - data.startBeat;
+ data.pixelsPerBeat = width / beats;
+ return {
+ minLeft: data.minLeft,
+ width: width,
+ beats: beats,
+ pixelsPerBeat: Math.round(data.pixelsPerBeat)
+ }
+ }
+
+ function draw() {
+ $(".timelinemarker").remove();
+ $(".timelinetext").remove();
+ if (!data.gridVisible) {
+ return;
+ }
+
+ var target = $("#timeline");
+ var geometry = calcViewport();
+
+ var beat = data.startBeat;
+
+ var width;
+ var fontWeight;
+ for (var x = geometry.minLeft; x < window.screen.width; x += geometry.pixelsPerBeat) {
+ if ((beat - 1) % data.timeSigMarker == 0) {
+ width = 2;
+ fontWeight = "bold";
+ } else {
+ width = 1;
+ fontWeight = "normal";
+ }
+ $("<div />").attr("class", "timelinemarker").appendTo(target).css("width", width).css("left", x);
+ $("<div />").attr("class", "timelinetext").appendTo(target).css("font-weight", fontWeight).css("left", x + 2).text(beat);
+ beat ++;
+ }
+ }
+
+ Object.defineProperty(this, "startBeat", {
+ get: function() { return data.startBeat; },
+ set: function(x) {
+ data.startBeat = x;
+ draw();
+ }
+ });
+
+ Object.defineProperty(this, "bpm", {
+ get: function() { return data.bpm; },
+ set: function(x) {
+ data.bpm = x;
+ app.insertScore("ecpweb_setbpm", [0, 1, data.bpm]);
+ }
+ });
+
+ Object.defineProperty(this, "endBeat", {
+ get: function() { return data.startBeat; },
+ set: function(x) {
+ data.startBeat = x;
+ draw();
+ }
+ });
+
+ Object.defineProperty(this, "minLeft", {
+ get: function() { return data.minLeft; },
+ set: function(x) {}
+ });
+
+ Object.defineProperty(this, "pixelsPerBeat", {
+ get: function() { return data.pixelsPerBeat; },
+ set: function(x) {}
+ });
+
+ Object.defineProperty(this, "snapToGrid", {
+ get: function() { return data.snapToGrid; },
+ set: function(x) {
+ data.snapToGrid = (x == 1);
+ }
+ });
+
+ Object.defineProperty(this, "gridVisible", {
+ get: function() { return data.gridVisible; },
+ set: function(x) {
+ data.gridVisible = (x == 1);
+ draw();
+ }
+ });
+
+
+
+ draw();
+};
+
+
+var Sequencer = function(data) {
+ var self = this;
+ var gridSnap = 0;
+ var timeScale = 1;
+
+ if (!data) {
+ var data = {
+ tempo: 120,
+ playing: false
+ };
+ }
+
+ this.userImportedFiles = [];
+
+ this.setTempo = function(v) {
+ insertScore("ecpweb_setglobal", [0, 1, 0, v]);
+ };
+
+ this.getSaveData = function() {
+ var sdata = {"sequencer": data, "channels": [], "clips": {}};
+ for (var x in channels) {
+ sdata.channels.push(channels[x].getSaveData());
+ }
+ for (var x in clips) {
+ sdata.clips[x] = clips[x].getSaveData();
+ }
+ return sdata;
+ };
+
+ this.loadSaveData = function(v) {
+ data = v.sequencer;
+ for (var x in v.channels) {
+ new Channel(v.channels[x]);
+ }
+ };
+
+ Object.defineProperty(this, "playing", {
+ get: function() { return data.playing; },
+ set: function(x) {
+ data.playing = x;
+ }
+ });
+};
+
+
+var Channel = function(data) {
+ var self = this;
+ channels.push(this);
+
+ if (!data) {
+ var index = channels.length;
+ var data = {
+ name: "Channel " + index,
+ index: index
+ };
+ } else {
+ var index = data.index;
+ }
+
+ var element = $("<div />").attr("id", "channel" + index).attr("class", "channel").appendTo("#timeline").append(
+ $("<div />").attr("id", "channeldetails" + index).attr("class", "channeldetails").text(data.name)
+ ).append(
+ $("<div />").attr("id", "channelclips" + index).attr("class", "channelclips")
+ );
+};
+
+var Clip = function(channel, data, parent) {
+ var self = this;
+ var loaded = false;
+ var hasTwin = (parent) ? true : false;
+ this.waveform = (parent) ? parent.waveform : null;
+
+ if (!data) {
+ var id = getID();
+ var data = {
+ name: "Clip " + id,
+ channel: channel,
+ id: id,
+ clipindex: null, // needed here?
+ colour: "#" + (Math.random() * 0xFFFFFF << 0).toString(16),
+ position: 0,
+ };
+ }
+
+ clips[data.id] = this;
+ var element = $("<div />").attr("class", "clip").attr("id", "clip" + data.id).text(data.name).click(function() {
+ csseq.selectedClip = self;
+ $("#clipdetails").show();
+ $("#clip_name").val(data.name);
+ $("#clip_colour").val(data.colour);
+ $("#clipdetailsright").empty().append(self.waveform);
+
+ }).css("top", (channel * chanHeight) + "px").css("background-color", data.colour).mousedown(handle_mousedown)
+ .css("left", ((data.position * csseq.timeline.pixelsPerBeat)) + "px")
+ .appendTo($("#channelclips" + channel));
+ // resizable()
+
+ Object.defineProperty(this, "colour", {
+ get: function() { return data.colour; },
+ set: function(x) {
+ data.colour = x;
+ element.css("background-color", data.colour);
+ }
+ });
+
+
+// debug
+this.ddata = data;
+ var dataMode = {
+ divisionsPerBeat: 2,
+ duration: 3,
+ beatsLength: 4,
+ utilisedLength: 5,
+ warpMode: 6,
+ pitch: 7,
+ amp: 8,
+ fftSize: 9,
+ txtWinSize: 10,
+ txtRandom: 11,
+ txtOverlap: 12,
+ loop: 13,
+ warp: 14,
+ txtWinType: 15,
+ // warp points are 16 + in table
+
+ position: -1,
+ name: -2,
+ soundPath: -3,
+ channel: -4
+ };
+
+ function getDataModeFromNum(v) {
+ for (var k in dataMode) {
+ if (dataMode[k] == v) {
+ return k;
+ }
+ }
+ }
+
+ for (let x in dataMode) {
+ Object.defineProperty(this, x, {
+ get: function() { return data[x] },
+ set: function(v) {
+ self.setData(x, v);
+ }
+ });
+ }
+
+ /*// v should become defunct
+ Object.defineProperty(this, "name", {
+ get: function() { return data.name; },
+ set: function(x) {
+ data.name = x;
+ element.text(data.name);
+
+ // self.setData("name", x);
+ }
+ });*/
+
+
+ /*
+ function getData() {
+ var cbid = app.createCallback(function(ndata) {
+ for (var x in ndata.data) {
+ data[x] = ndata.data[x];
+ }
+ });
+ app.insertScore("ecpweb_getdata", [0, 1, cbid, clipindex]);
+ }*/
+
+ this.setData = function(modeString, v) {
+ var cbid = app.createCallback(function(ndata) {
+ if (ndata.hasOwnProperty("clipindex")) {
+ data.clipindex = ndata.clipindex;
+ hasTwin = false;
+ }
+ data[modeString] = v;
+ if (modeString == "name") {
+ element.text(data.name);
+ }
+ });
+ app.insertScore("ecpweb_setdata",
+ [0, 1, cbid, data.id, dataMode[modeString], v, ((hasTwin) ? 1 : 0)]
+ ); // having ecp_cloneclip in if needed
+ }
+
+ this.play = function() {
+ var cbid = app.createCallback(function(ndata) {
+ console.log(ndata);
+ });
+ app.insertScore("ecpseq_playclip", [0, 1, cbid, data.id]);
+ }
+
+ this.loadTest = function() {
+ self.loadFromPath("test.mp3");
+ };
+
+ async function makeWaveform(tablenum) {
+ var width = 300;
+ var height = 150;
+ var tabledata = await app.getCsound().tableCopyOut(tablenum);
+ self.waveform = $("<canvas />", {"class": "waveform"}).width(width).height(height);
+ var ctx = self.waveform[0].getContext("2d");
+ var widthstep = tabledata.length / width;
+ var val;
+ ctx.fillStyle = "#88ff88";
+ //ctx.beginPath();
+ ctx.moveTo(0, height * 0.5);
+ for (var x = 0; x < tabledata.length; x++) {
+ val = (tabledata[x] + 1) * 0.5;
+ ctx.lineTo(x * widthstep, val * height);
+ }
+ //ctx.closePath();
+ ctx.stroke();
+ app.insertScore("ecpweb_tabdestroy", [0, 1, tablenum]);
+ }
+
+ function setSize() {
+ var beattime = 60 / csseq.timeline.bpm;
+ var width = (data.duration / beattime) * csseq.timeline.pixelsPerBeat;
+ element.css("width", width + "px");
+ }
+
+ this.loadFromPath = function(path, userImported) {
+ var cbid = app.createCallback(function(ndata) {
+ for (var x in ndata.data) {
+ var key = getDataModeFromNum(x);
+ data[key] = ndata.data[x];
+ if (key == "name") {
+ element.text(data.name);
+ }
+ }
+ data.soundPath = path;
+ makeWaveform(ndata.waveform).then(() => {
+ if (userImported) {
+ csseq.seq.userImportedFiles.push(path);
+ }
+ loaded = true;
+ setSize();
+ });
+
+ });
+ app.insertScore("ecpweb_loadsound", [0, 1, cbid, path, data.channel, data.position]);
+ };
+
+ this.clone = function() {
+ var newdata = Object.assign({}, data);
+ newdata.id = getID();
+ return new Clip(data.channel, newdata, self);
+ };
+
+ this.randomiseWarpPoints = function(mode) { // mode is -1, 0 or 1 I think..
+ if (!mode) mode = 0;
+ var cbid = app.createCallback(function(ndata) {
+ notify("OK");
+ });
+ app.insertScore("ecpweb_randomisewarppoints", [0, 1, cbid, mode]);
+ };
+
+
+ function handle_mousedown(e){
+ var originalPosition = data.position;
+ window.my_dragging = {};
+ my_dragging.pageX0 = e.pageX;
+ my_dragging.pageY0 = e.pageY;
+ my_dragging.elem = this;
+ my_dragging.offset0 = $(this).offset();
+ var isCopying = e.ctrlKey;
+
+ if (isCopying) { // && loaded
+ self.clone();
+ }
+
+ function handle_dragging(e){
+ var left = my_dragging.offset0.left + (e.pageX - my_dragging.pageX0);
+
+ var minLeft = (window.screen.width / 100) * 10;
+
+ //round
+ // left = Math.ceil(left / pixelsPerBeat) * pixelsPerBeat;
+
+ if (csseq.timeline.snapToGrid) {
+ left = (Math.ceil((left - minLeft) / csseq.timeline.pixelsPerBeat) * csseq.timeline.pixelsPerBeat) + minLeft;
+ }
+
+
+ if (left < minLeft) {
+ left = minLeft
+ }
+ data.position = csseq.timeline.startBeat + ((left - minLeft) / csseq.timeline.pixelsPerBeat);
+
+ var top = my_dragging.offset0.top + (e.pageY - my_dragging.pageY0);
+ top = Math.ceil(top / chanHeight) * chanHeight;
+ var maxTop = chanHeight * channels.length;
+ if (top > maxTop) {
+ top = maxTop;
+ } else if (top < 30) {
+ top = 30;
+ }
+
+
+ $(my_dragging.elem)
+ .offset({top: top, left: left});
+ }
+
+ function handle_mouseup(e){
+ $("body")
+ .off("mousemove", handle_dragging)
+ .off("mouseup", handle_mouseup);
+ if (data.position != originalPosition) {
+ self.setData("position", data.position);
+ }
+ }
+
+ $("body").on("mouseup", handle_mouseup).on("mousemove", handle_dragging);
+ }
+
+ /*
+ this.setLength = function(v) {
+ setData("length", v);
+ }; // now made generic above.
+ */
+};
+
+function runTest() {
+ new Channel();
+ new Channel();
+ new Channel();
+ new Channel();
+ new Channel();
+ new Channel();
+}
+
+var ClipDetailPane = function() {
+ var self = this;
+};
+
+
+
+$(function() {
+ csseq.timeline = new TimelineGrid();
+ csseq.seq = new Sequencer();
+
+
+ $(".smbut").attr("value", 0).click(function(){
+ var val;
+ var col;
+ if ($(this).attr("value") == 0) {
+ val = 1;
+ col = "#f2e30c";
+ } else {
+ val = 0;
+ col = "#b5b01d";
+ }
+ $(this).attr("value", val).css("background-color", col);
+ });
+
+ $("#head_snap").click(function() {
+ csseq.timeline.snapToGrid = $(this).attr("value");
+ });
+
+ $("#head_showgrid").click(function() {
+ csseq.timeline.gridVisible = $(this).attr("value");
+ });
+
+ $("#head_play").click(function() {
+ var bt = $(this);
+ if (csseq.seq.playing) {
+ app.insertScore("ecpseq_stop");
+ } else {
+ var cbid = app.createCallback(function(data) {
+ var text, val, col;
+ if (data.state == "playing") {
+ csseq.seq.playing = true;
+ text = "Stop";
+ val = 1;
+ col = "#f2e30c";
+ } else if (data.state == "stopped") {
+ csseq.seq.playing = false;
+ text = "Play";
+ val = 0;
+ col = "#b5b01d";
+ app.removeCallback(data.cbid);
+ }
+ bt.text(text).attr("value", val).css("background-color", col);
+ }, true);
+ app.insertScore("ecpseq_play", [0, 36000, cbid, csseq.timeline.startBeat]);
+ }
+ });
+
+
+ $("#clip_colour").change(function(){
+ csseq.selectedClip.colour = $(this).val();
+ });
+
+
+ var clip_pitchLocked = false;
+ $("#clip_pitch").change(function(){
+ if (!clip_pitchLocked) {
+ var val = $(this).val();
+ csseq.selectedClip.pitch = Math.pow(2, (val / 12));
+ $("#clip_pitchtext").val(val);
+ }
+ });
+
+ /*
+
+ var onChangeID = {
+ clip_warp: "warp",
+ clip_loop: "loop",
+ clip_amp: "amp",
+ clip_readtype: "warpMode",
+ };
+
+ for (let el in onChangeID) {
+ let obj = $("#" + el);
+ let trg = onChangeID[el];
+ var func;
+ if (typeof(trg) == "string") {
+ func = () => {
+ csseq.selectedClip[trg] = $(this).val();
+ };
+ } else {
+ func = trg;
+ }
+ obj.change(func);
+ }
+
+ */
+
+
+ $("#clip_pitchtext").change(function(){
+ clip_pitchLocked = true;
+ $("#clip_pitch").val($(this).val());
+ clip_pitchLocked = false;
+ });
+
+ $("#clip_name").change(function(){
+ csseq.selectedClip.name = $(this).val(); // setdata?
+ });
+
+ $("#clip_amp").change(function(){
+ csseq.selectedClip.amp = $(this).val();
+ });
+
+ $("#clip_warp").click(function() {
+ csseq.selectedClip.warp = $(this).val();
+ });
+
+ $("#clip_loop").click(function() {
+ csseq.selectedClip.loop = $(this).val();
+ });
+
+ $("#clip_readtype").change(function() {
+ csseq.selectedClip.warpMode = $(this).val();
+ });
+
+ $("#start_invoke").click(function() {
+ app.play();
+ });
+
+ async function handleFileDrop(e) {
+ e.preventDefault();
+ for (const item of e.originalEvent.dataTransfer.files) {
+ //item.size;
+ //item.type "audio/mpeg";
+ var content = await item.arrayBuffer();
+ const buffer = new Uint8Array(content);
+ await app.getCsound().fs.writeFile(item.name, buffer);
+ var x = new Clip(1); // determine channel
+ x.loadFromPath(item.name, true);
+ }
+ }
+
+ $("#timeline").on("dragover", function(e) {
+ e.preventDefault();
+ //e.stopPropagation();
+ e.originalEvent.dataTransfer.effectAllowed = "all";
+ e.originalEvent.dataTransfer.dropEffect = "copy";
+ //styling / clip view
+ return false;
+ }).on("dragleave", function(e) {
+ e.preventDefault();
+ //e.stopPropagation();
+ // remove styling / clip view
+ }).on("drop", function(e) {
+ handleFileDrop(e);
+ });
+
+});
+/*
+divisionsPerBeat: 2,
+ duration: 3,
+ beatsLength: 4,
+ utilisedLength: 5,
+ warpMode: 6,
+ pitch: 7,
+ amp: 8,
+ fftSize: 9,
+ txtWinSize: 10,
+ txtRandom: 11,
+ txtOverlap: 12,
+ loop: 13,
+ warp: 14,
+ txtWinType: 15,
+*/
+</script>
+</head>
+<body>
+<div id="start">
+ <div id="start_centre">
+ <p id="start_invoke">Press to begin</p>
+ </div>
+</div>
+<div id="header">
+ <table id="headertable"><tbody><tr>
+ <td><button id="head_play" class="smbut">Play</button></td>
+ <td><button id="head_snap" class="smbut">Snap</button></td>
+ <td><button id="head_showgrid" class="smbut">Grid</button></td>
+ </tr></tbody></table>
+</div>
+<div id="main">
+ <div id="timeline"></div>
+ <div id="details">
+ <div id="clipdetails">
+ <div id="clipdetailsleft">
+ <table><tbody>
+ <tr>
+ <td><input type="color" id="clip_colour"></td>
+ <td><input type="text" id="clip_name"></td>
+ </tr>
+ <tr>
+ <td>Read type</td>
+ <td><select id="clip_readtype">
+ <option value="0">Repitch</option>
+ <option value="1">Grain</option>
+ <option value="2">FFTab</option>
+ <option value="3">FFT</option>
+ </select>
+ </td>
+ </tr>
+ <tr>
+ <td><button id="clip_warp" class="smbut">Warp</button></td>
+ <td><button id="clip_loop" class="smbut">Loop</button></td>
+ </tr>
+ <tr>
+ <td>
+ <input id="clip_pitch" class="input-knob" type="range" data-src="image/knob70.png" data-sprites="100" style="width:64px; height: 64px; background-image: url('image/knob70.png'); background-size: 100%; background-position: 0px -3200px;" min="-24" max="24" step="1" value="0"/><br />
+ <input id="clip_pitchtext" type="number" min="-24" max="24" step="1" value="0" />
+ </td>
+ <td>
+ <input id="clip_amp" class="input-knob" type="range" data-src="image/knob70.png" data-sprites="100" style="width:64px; height: 64px; background-image: url('image/knob70.png'); background-size: 100%; background-position: 0px -3200px;" min="0" max="1" step="0.000001" value="1"/>
+ </td>
+ </tr>
+ </tbody></table>
+ </div>
+ <div id="clipdetailsright"></div>
+ </div>
+ </div>
+</div>
+</body>
+</html> \ No newline at end of file
diff --git a/site/app/twine/_hOLD/index_old.html b/site/app/twine/_hOLD/index_old.html
new file mode 100644
index 0000000..351e50b
--- /dev/null
+++ b/site/app/twine/_hOLD/index_old.html
@@ -0,0 +1,102 @@
+<html>
+ <head>
+ <script type="text/javascript" src="/code/jquery.js"></script>
+ <script type="text/javascript" src="../base/base.js"></script>
+ <script type="text/javascript">
+ window.app = new CSApplication(
+ {
+ csdUrl: "timeline.csd",
+ files: ["test.mp3"],
+ onPlay: function () {
+ $("#start").hide();
+ runTest();
+ }
+ }
+ );
+
+ function toggle(target, channel, multiplier) {
+ var item = new Nexus.Toggle(target);
+ item.on("change", function(v) {
+ app.setControlChannel(channel, (v ? 1 : 0) * multiplier);
+ });
+ }
+
+ function slider(target, channel, multiplier) {
+ var item = new Nexus.Slider(target);
+ item.on("change", function(v) {
+ app.setControlChannel(channel, v * multiplier);
+ });
+ }
+
+ function dial(target, channel, multiplier) {
+ var item = new Nexus.Dial(target);
+ item.on("change", function(v) {
+ app.setControlChannel(channel, v * multiplier);
+ });
+ }
+
+ function makeRow(tbody, name, text, widgetType, multiplier) {
+ var row = $("<tr />");
+ for (var x = 0; x < 4; x++) {
+ var id = name + (x + 1);
+ var cell = $("<td />").appendTo(row);
+ var widget = $("<div />").attr("id", id).appendTo(cell);
+ var label = $("<div />").text(text).appendTo(cell);
+ }
+ tbody.append(row);
+ for (var x = 0; x < 4; x++) {
+ var id = name + (x + 1);
+ widgetType("#" + id, id, multiplier);
+ }
+ }
+
+ function makeMixer() {
+ var maxeq = 2;
+ var maxsend = 2;
+ var maxamp = 1.5;
+ var items = {
+ compmodel: ["Component model", toggle, 1],
+ lowcut: ["Low cut", toggle, 1],
+ eqhigh: ["EQ high", dial, maxeq],
+ eqmid: ["EQ mid", dial, maxeq],
+ eqlow: ["EQ low", dial, maxeq],
+ auxA: ["Aux 1", dial, maxsend],
+ preA: ["Aux 1 prefade", toggle, 1],
+ auxB: ["Aux 2", dial, maxsend],
+ preB: ["Aux 2 prefade", toggle, 1],
+ auxC: ["Aux 3", dial, maxsend],
+ preC: ["Aux 3 prefade", toggle, 1],
+ auxD: ["Aux 4", dial, maxsend],
+ preD: ["Aux 4 prefade", toggle, 1],
+ gain: ["Gain", dial, 1.5],
+ };
+ var tbl = $("<table />").appendTo($("#mixer"));
+ var tbody = $("<tbody />").appendTo(tbl);
+ for (var k in items) {
+ makeRow(tbody, k, items[k][0], items[k][1], items[k][2]);
+ }
+ }
+
+ $(function() {
+ $("#start").click(function() {
+ app.play();
+ });
+
+ $("#callback").click(function() {
+ var cbid = app.createCallback(function(data) {
+ $("#callback_response").text("Response from CBID " + data.cbid);
+ });
+
+ app.insertScore("cbtest", [0, 1, cbid]);
+ });
+ });
+ </script>
+ </head>
+ <body>
+ <button id="start">Start</button>
+ <!--<button id="callback">Callback test</button>
+ <div id="callback_response"></div>
+ -->
+ <div id="mixer"></div>
+ </body>
+</html> \ No newline at end of file
diff --git a/site/app/twine/_hOLD/index_workingold.html b/site/app/twine/_hOLD/index_workingold.html
new file mode 100644
index 0000000..21812d7
--- /dev/null
+++ b/site/app/twine/_hOLD/index_workingold.html
@@ -0,0 +1,1251 @@
+<html>
+<head>
+<title>twine</title>
+<link rel="stylesheet" href="../base/theme.css">
+<link rel="stylesheet" href="../twirl/twirl.css">
+<style type="text/css">
+
+body {
+ font-family: var(--fontFace);
+ color: var(--fgColor1);
+ font-size: var(--fontSizeDefault);
+ user-select: none;
+ cursor: arrow;
+ font-size: 11pt;
+}
+
+#twine_menubar {
+ position: absolute;
+ top: 0px;
+ left: 0px;
+ width: 100%;
+ right: 0px;
+ height: 20px;
+ z-index: 6;
+}
+
+.transparentinput {
+ font-size: var(--fontSizeSmall);
+ background-color: var(--bgColor3);
+ color: var(--fgColor2);
+ border: none;
+}
+
+.slider {
+ background: var(--bgColor3);
+ accent-color: var(--fgColor2);
+}
+
+#twine_header {
+ position: absolute;
+ top: 20px;
+ height: 30px;
+ left: 0px;
+ width: 100%;
+ background-color: var(--bgColor1);
+ overflow: none;
+}
+
+.drag_selection {
+ position: fixed;
+ background-color: #323232;
+ opacity: 0.5;
+ z-index: 101;
+}
+
+#twine_headertable {
+ height: 30px;
+}
+
+#twine_clipdetails {
+ display: none;
+}
+
+#twine_channeldetailslow {
+ display: none;
+}
+
+#loading {
+ opacity: 0.7;
+ background-color: #767676;
+ position: fixed;
+ left: 0px;
+ top: 0px;
+ width: 100%;
+ height: 100%;
+ display: none;
+ z-index: 666;
+}
+
+#loading_centre {
+ z-index: 667;
+ position: relative;
+ height: 200px;
+}
+
+#loading_text {
+ font-size: 24pt;
+ margin: 0;
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ font-size: 72pt;
+ text-align: center;
+}
+
+#start {
+ z-index: 200;
+ position: fixed;
+ left: 0px;
+ top: 0px;
+ width: 100%;
+ height: 100%;
+ background-color: #343434;
+}
+
+#start_centre {
+ z-index: 201;
+ position: relative;
+ height: 200px;
+}
+
+#start_invoke {
+ z-index: 202;
+ margin: 0;
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ font-size: 72pt;
+ cursor: pointer;
+}
+
+#twine_main {
+ position: absolute;
+ top: 50px;
+ bottom: 0px;
+ left: 0px;
+ width: 100%;
+}
+
+#twine_timeline {
+ position: absolute;
+ left: 0px;
+ top: 0px;
+ width: 100%;
+ height: 70%;
+}
+
+#twine_timelineoverlay {
+ position: absolute;
+ left: 0px;
+ top: 0px;
+ width: 100%;
+ height: 100%;
+ opacity: 0.1;
+ background-color: #ffffff;
+}
+
+
+.timelinetext {
+ font-size: var(--fontSizeSmall);
+ opacity: 0.9;
+ position: absolute;
+ color: var(--fgColor3);
+ top: 2px;
+ z-index: 21;
+}
+
+.timelinemarker {
+ width: 1px;
+ position: absolute;
+ background-color: #bdbdbd;
+ opacity: 0.9;
+ height: 100%;
+ top: 0px;
+ z-index: 20;
+}
+
+.smbut {
+ font-size: 8pt;
+ background-color: var(--bgColor2);
+ color: var(--fgColor2);
+ border: 1px solid black;
+}
+
+.knoblabel {
+ font-size: 8pt;
+ text-align: center;
+}
+
+#twine_details {
+ position: absolute;
+ left: 0px;
+ bottom: 0px;
+ width: 100%;
+ height: 30%;
+ background-color: var(--bgColor1);
+}
+
+#twine_clipdetailsleft {
+ position: absolute;
+ left: 0px;
+ top: 0px;
+ width: 30%;
+ height: 100%;
+ font-size: var(--fontSizeSmall);
+ background-color: var(--bgColor2);
+}
+
+#twine_clipdetailsright {
+ position: absolute;
+ left: 30%;
+ top: 0px;
+ width: 70%;
+ height: 100%;
+ background-color: var(--bgColor3);
+}
+
+.channel {
+ height: 30px;
+ width: 100%;
+ border-bottom: 1px solid #aaaaaa;
+ left: 0px;
+}
+
+.channeldetails {
+ user-select: none;
+ position: absolute;
+ height: 30px;
+ left: 0px;
+ width: 10%;
+}
+
+.channelclips {
+ position: absolute;
+ height: 30px;
+ left: 10%;
+ width: 90%;
+ background-color: var(--bgColor2);
+}
+
+.clip {
+ user-select: none;
+ position: absolute;
+ padding: 0px;
+ cursor: move;
+ z-index: 70;
+ width: 50px;
+ height: 30px;
+ background-color: #CC3333;
+ color: #000000;
+ overflow: hidden;
+}
+</style>
+<script type="text/javascript" src="/code/jquery.js"></script>
+<script type="text/javascript" src="../base/base.js"></script>
+<script type="text/javascript" src="../twirl/twirl.js"></script>
+<script type="text/javascript" src="../base/waveform.js"></script>
+<script type="text/javascript" src="ui-elements.js"></script>
+<script type="text/javascript" src="/code/input-knobs.js"></script>
+<script type="text/javascript">
+
+
+
+var clips = {};
+var channels = [];
+var maxID = 0;
+var chanHeight = 30;
+
+
+function getID() {
+ return maxID++;
+}
+
+
+
+function setupUI(twine) {
+ var ui = {};
+ ui.clip = {
+ audition: new PlayButton({
+ target: "twine_clip_audition",
+ change: function(v, obj) {
+ if (obj.state == true) {
+ twine.selectedClip.play(function(ndata) {
+ if (ndata.status == 0) {
+ obj.setValue(false);
+ app.removeCallback(ndata.cbid);
+ }
+ });
+ } else {
+ app.insertScore("ecpseq_stop");
+ }
+ }
+ }),
+ name: new TextInput({
+ target: "twine_clip_name",
+ change: function(val) {
+ twine.selectedClip.setData("name", val);
+ }
+ }),
+ colour: new ColourInput({
+ target: "twine_clip_colour",
+ change: function(val) {
+ twine.selectedClip.colour = val;
+ }
+ }),
+ warp: new StandardToggle({
+ label: "Warp",
+ target: "twine_clip_warp",
+ change: function(val) {
+ twine.selectedClip.setData("warp", val);
+ twine.selectedClip.setSize();
+ }
+ }),
+ loop: new StandardToggle({
+ label: "Loop",
+ target: "twine_clip_loop",
+ change: function(val) {
+ twine.selectedClip.setData("loop", val);
+ twine.selectedClip.setSize();
+ }
+ }),
+ readType: new ComboBox({
+ target: "twine_clip_readtype",
+ options: [
+ "Repitch", "Grain", "FFTab", "FFT"
+ ],
+ change: function(val) {
+ twine.selectedClip.setData("warpMode", val);
+ },
+ stateAlter: function(val) {
+ twine.ui.clip.fftSize.hide();
+ twine.ui.clip.winSize.hide();
+ twine.ui.clip.winRandom.hide();
+ twine.ui.clip.winOverlap.hide();
+ twine.ui.clip.winType.hide();
+ if (val == 1) {
+ twine.ui.clip.winSize.show();
+ twine.ui.clip.winRandom.show();
+ twine.ui.clip.winOverlap.show();
+ twine.ui.clip.winType.show();
+ } else if (val > 1) {
+ twine.ui.clip.fftSize.show();
+ }
+ }
+ }),
+ amp: new Slider({
+ label: "Gain",
+ valueLabel: true,
+ value: 1,
+ size: 32,
+ target: "twine_clipparamsbottom",
+ changeOnInput: true,
+ change: function(val) {
+ twine.selectedClip.setData("amp", val);
+ }
+ }),
+ pitch: new Slider({
+ label: "Pitch",
+ valueLabel: true,
+ min: -12,
+ max: 12,
+ step: 1,
+ value: 0,
+ size: 32,
+ target: "twine_clipparamsbottom",
+ changeOnInput: true,
+ change: function(val) {
+ var c = twine.selectedClip;
+ var pitch = Math.pow(2, (val / 12));
+ c.setData("pitch", pitch);
+ if (c.warpMode == 0 && c.loop == 0 && c.warp == 0) {
+ c.setSize();
+ }
+ }
+ }),
+ fftSize: new ComboBox({
+ label: "FFT Size",
+ asRow: true,
+ target: "twine_clipparamsbottom",
+ options: [
+ "256", "512", "1024", "2048"
+ ],
+ asValue: true,
+ change: function(val) {
+ twine.selectedClip.setData("fftSize", val);
+ }
+ }),
+ winSize: new Slider({
+ label: "Window size",
+ valueLabel: true,
+ min: 44,
+ max: 4410,
+ step: 1,
+ value: 4410,
+ size: 32,
+ target: "twine_clipparamsbottom",
+ change: function(val) {
+ twine.selectedClip.setData("txtWinSize", val);
+ }
+ }),
+ winRandom: new Slider({
+ label: "Window random",
+ valueLabel: true,
+ min: 0,
+ max: 441,
+ step: 1,
+ value: 441,
+ size: 32,
+ target: "twine_clipparamsbottom",
+ change: function(val) {
+ twine.selectedClip.setData("txtRandom", val);
+ }
+ }),
+ winOverlap: new Slider({
+ label: "Window overlap",
+ valueLabel: true,
+ min: 0,
+ max: 16,
+ step: 1,
+ value: 4,
+ size: 32,
+ target: "twine_clipparamsbottom",
+ change: function(val) {
+ twine.selectedClip.setData("txtOverlap", val);
+ }
+ }),
+ winType: new ComboBox({
+ label: "Window type",
+ asRow: true,
+ target: "twine_clipparamsbottom",
+ options: [
+ "Hanning", "Hamming", "Half sine"
+ ],
+ change: function(val) {
+ twine.selectedClip.setData("txtWinType", val);
+ }
+ })
+ };
+
+ ui.head = {
+ play: new 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 StandardToggle({
+ label: "Snap",
+ target: "twine_head_snap",
+ change: function(val) {
+ twine.timeline.snapToGrid = val;
+ }
+ }),
+ grid: new StandardToggle({
+ label: "Grid",
+ target: "twine_head_showgrid",
+ change: function(val) {
+ twine.timeline.gridVisible = val;
+ }
+ })
+ };
+ return ui;
+} // end setupUI
+
+
+var TimelineGrid = function(twine, data) {
+ var self = this;
+
+ if (!data) {
+ var data = {
+ snapToGrid: true,
+ gridVisible: true,
+ timeSigMarker: 4,
+ resolution: null,
+ startBeat: 0,
+ endBeat: 16,
+ bpm: 120
+ };
+ }
+
+ this.getSaveData = function() {
+ return data;
+ };
+
+ function calcViewport() {
+ data.minLeft = (window.screen.width / 100) * 10;
+ var width = window.screen.width - data.minLeft;
+ var beats = data.endBeat - data.startBeat;
+ data.pixelsPerBeat = width / beats;
+ return {
+ minLeft: data.minLeft,
+ width: width,
+ beats: beats,
+ pixelsPerBeat: Math.round(data.pixelsPerBeat)
+ }
+ }
+
+ function draw() {
+ $(".timelinemarker").remove();
+ $(".timelinetext").remove();
+ if (!data.gridVisible) {
+ return;
+ }
+
+ var target = $("#twine_timeline");
+ var geometry = calcViewport();
+
+ var beat = data.startBeat;
+
+ var width;
+ var fontWeight;
+ for (var x = geometry.minLeft; x < window.screen.width; x += geometry.pixelsPerBeat) {
+ if ((beat - 1) % data.timeSigMarker == 0) {
+ width = 2;
+ fontWeight = "bold";
+ } else {
+ width = 1;
+ fontWeight = "normal";
+ }
+ $("<div />").attr("class", "timelinemarker").appendTo(target).css("width", width).css("left", x);
+ $("<div />").attr("class", "timelinetext").appendTo(target).css("font-weight", fontWeight).css("left", x + 2).text(beat);
+ beat ++;
+ }
+ }
+
+ Object.defineProperty(this, "startBeat", {
+ get: function() { return data.startBeat; },
+ set: function(x) {
+ data.startBeat = x;
+ draw();
+ }
+ });
+
+ Object.defineProperty(this, "bpm", {
+ get: function() { return data.bpm; },
+ set: function(x) {
+ data.bpm = x;
+ app.insertScore("ecpweb_setbpm", [0, 1, data.bpm]);
+ }
+ });
+
+ Object.defineProperty(this, "endBeat", {
+ get: function() { return data.endBeat; },
+ set: function(x) {
+ data.endBeat = x;
+ draw();
+ }
+ });
+
+ Object.defineProperty(this, "minLeft", {
+ get: function() { return data.minLeft; },
+ set: function(x) {}
+ });
+
+ Object.defineProperty(this, "pixelsPerBeat", {
+ get: function() { return data.pixelsPerBeat; },
+ set: function(x) {}
+ });
+
+ Object.defineProperty(this, "snapToGrid", {
+ get: function() { return data.snapToGrid; },
+ set: function(x) {
+ data.snapToGrid = (x == 1);
+ }
+ });
+
+ Object.defineProperty(this, "gridVisible", {
+ get: function() { return data.gridVisible; },
+ set: function(x) {
+ data.gridVisible = (x == 1);
+ draw();
+ }
+ });
+
+ var dragHandleTime;
+ function handleMousedown(e) {
+ dragHandleTime = {};
+ dragHandleTime.pageX0 = e.pageX;
+ dragHandleTime.pageY0 = e.pageY;
+ dragHandleTime.elem = this;
+ dragHandleTime.offset0 = $(this).offset();
+ dragHandleTime.selection = $("<div />").addClass("drag_selection").css("left", e.pageX).css("top", e.pageY).appendTo($("#twine_timeline"));
+
+ function handleDrag(e) {
+ var left = dragHandleTime.offset0.left + (e.pageX - dragHandleTime.pageX0);
+ var minLeft = (window.screen.width / 100) * 10;
+
+ if (twine.timeline.snapToGrid) {
+ left = (Math.floor((left - minLeft) / twine.timeline.pixelsPerBeat) * twine.timeline.pixelsPerBeat) + minLeft;
+ }
+
+ if (left < minLeft) {
+ left = minLeft
+ }
+
+ var top = dragHandleTime.offset0.top + (e.pageY - dragHandleTime.pageY0);
+ top = Math.floor(top / chanHeight) * chanHeight;
+ var maxTop = chanHeight * channels.length;
+ if (top > maxTop) {
+ top = maxTop;
+ } else if (top < 30) {
+ top = 30;
+ }
+
+ $(dragHandleTime.elem)
+ .css("height", top - dragHandleTime.pageY0)
+ .css("width", left - dragHandleTime.pageX0);
+
+ }
+
+ function handleMouseUp(e) {
+ $("body")
+ .off("mousemove", handleDrag)
+ .off("mouseup", handleMouseUp);
+ dragHandleTime.selection.remove();
+ }
+
+ $("body").on("mouseup", handleMouseUp).on("mousemove", handleDrag);
+ }
+ //$("#twine_timeline").mousedown(handleMousedown); // selection area
+ draw();
+};
+
+
+var Sequencer = function(twine, data) {
+ var self = this;
+ var gridSnap = 0;
+ var timeScale = 1;
+
+ if (!data) {
+ var data = {
+ tempo: 120,
+ playing: false
+ };
+ }
+
+ this.userImportedFiles = [];
+
+ this.setTempo = function(v) {
+ insertScore("ecpweb_setglobal", [0, 1, 0, v]);
+ };
+
+ this.getSaveData = function() {
+ var sdata = {"sequencer": data, "channels": [], "clips": {}};
+ for (var x in channels) {
+ sdata.channels.push(channels[x].getSaveData());
+ }
+ for (var x in clips) {
+ sdata.clips[x] = clips[x].getSaveData();
+ }
+ return sdata;
+ };
+
+ this.loadSaveData = function(v) {
+ data = v.sequencer;
+ for (var x in v.channels) {
+ new Channel(v.channels[x]);
+ }
+ };
+
+ Object.defineProperty(this, "playing", {
+ get: function() { return data.playing; },
+ set: function(x) {
+ data.playing = x;
+ }
+ });
+};
+
+
+var Channel = function(data) {
+ var self = this;
+ channels.push(this);
+
+ if (!data) {
+ var index = channels.length;
+ var data = {
+ name: "Channel " + index,
+ index: index
+ };
+ } else {
+ var index = data.index;
+ }
+
+ function channelClick() {
+ $("#twine_clipdetails").hide();
+ $("#twine_channeldetailslow").show();
+
+ }
+
+ var element = $("<div />").attr("id", "twine_channel" + index).attr("class", "channel").appendTo("#twine_timeline").append(
+ $("<div />").attr("id", "twine_channeldetails" + index).attr("class", "channeldetails").text(data.name).click(channelClick)
+ ).append(
+ $("<div />").attr("id", "twine_channelclips" + index).attr("class", "channelclips")
+ );
+};
+
+var Clip = function(channel, data, parent) {
+ var self = this;
+ var loaded = false;
+ var hasTwin = (parent) ? true : false;
+ var waveformClip;
+ var waveformEdit;
+ var datatable;
+
+ if (!data) {
+ var id = getID();
+ var data = {
+ name: "Clip " + id,
+ channel: channel,
+ id: id,
+ clipindex: null,
+ playLength: 1,
+ colour: "#" + (Math.random() * 0xFFFFFF << 0).toString(16),
+ position: 0,
+ };
+ }
+
+ clips[data.id] = this;
+ var elWaveEdit = $("<div />").css({width: "100%", height: "100%", top: "0px", left: "0px"});
+
+ var element = $("<div />").attr("class", "clip").attr("id", "clip" + data.id).click(function() {
+ twine.selectedClip = self;
+
+ $("#twine_channeldetailslow").hide();
+ $("#twine_clipdetails").show();
+ var cui = twine.ui.clip;
+ cui.name.setValue(data.name);
+ cui.colour.setValue(data.colour);
+ cui.amp.setValue(data.amp);
+ cui.warp.setValue(data.warp);
+ cui.loop.setValue(data.loop);
+ cui.readType.setValue(data.warpMode);
+ cui.pitch.setValue(Math.round((Math.log(data.pitch) / Math.log(2)) * 12));
+ cui.fftSize.setValue(data.fftSize);
+ cui.winSize.setValue(data.txtWinSize);
+ cui.winRandom.setValue(data.txtRandom);
+ cui.winOverlap.setValue(data.txtOverlap);
+ cui.winType.setValue(data.txtWinType);
+
+ showEditWaveform($("#twine_clipdetailsright"));
+
+ }).css("top", (channel * chanHeight) + "px").css("background-color", data.colour).mousedown(handle_mousedown)
+ .css("left", ((data.position * twine.timeline.pixelsPerBeat)) + "px")
+ .appendTo($("#twine_channelclips" + channel));
+
+ this.el = element;
+ // resizable()
+ var elWaveClip = $("<div />").css({position: "absolute", width: "100%", height: "100%", top: "0px", left: "0px"}).appendTo(element);
+ var elWaveText = $("<div />").css({position: "absolute", width: "100%", height: "100%", top: "0px", left: "0px", "font-size": "var(--fontSizeSmall)", color: "var(--fgColor1)"}).text(data.name).appendTo(element);
+
+
+ Object.defineProperty(this, "colour", {
+ get: function() { return data.colour; },
+ set: function(x) {
+ data.colour = x;
+ element.css("background-color", data.colour);
+ }
+ });
+
+ Object.defineProperty(this, "position", {
+ get: function() { return data.position; },
+ set: function(x) {
+ self.setData("position", x);
+ }
+ });
+
+
+// debug
+this.ddata = data;
+ var dataMode = {
+ fnL: 0,
+ fnR: 1,
+ divisionsPerBeat: 2,
+ duration: 3,
+ beatsLength: 4,
+ utilisedLength: 5,
+ warpMode: 6,
+ pitch: 7,
+ amp: 8,
+ fftSize: 9,
+ txtWinSize: 10,
+ txtRandom: 11,
+ txtOverlap: 12,
+ loop: 13,
+ warp: 14,
+ txtWinType: 15,
+ // warp points are 16 + in table
+
+ position: -1,
+ name: -2,
+ soundPath: -3,
+ channel: -4,
+ clipindex: -5,
+ playLength: -6
+ };
+
+ async function getDataFromTable(key) {
+ async function setFromKey(key) {
+ if (dataMode[key] < 0) return;
+ var value = await app.getCsound().tableGet(datatable, dataMode[key])
+ data[key] = value;
+ }
+
+ for (var k in dataMode) {
+ setFromKey(k);
+ }
+ }
+
+ async function getMarkers() {
+
+ }
+
+
+ this.setData = function(modeString, v, onComplete) {
+ if (dataMode[modeString] < 0) {
+ data[modeString] = v;
+ if (modeString == "name") {
+ elWaveText.text(data.name);
+ }
+ return;
+ }
+
+ function doSetData() {
+ app.getCsound().tableSet(datatable, dataMode[modeString], v);
+ data[modeString] = v;
+ }
+
+ if (hasTwin) {
+ app.insertScore("ecpweb_cloneclip", [0, 1, app.createCallback(function(ndata) {
+ hasTwin = false;
+ data.clipindex = ndata.clipindex;
+ datatable = ndata.datatable;
+ doSetData();
+ if (onComplete) onComplete();
+ })]);
+ } else {
+ doSetData();
+ if (onComplete) onComplete();
+ }
+ }
+
+ this.getPlaybackArgs = function(cbid, time) {
+ return [(time) ? time: 0, 1, cbid, data.clipindex, "mxchan" + (data.channel - 1)];
+ };
+
+ this.play = function(onCallback) {
+ var cbid = app.createCallback(function(ndata) {
+ if (onCallback) onCallback(ndata);
+ }, true);
+ app.insertScore("ecp_playback", self.getPlaybackArgs(cbid));
+ }
+
+ this.loadTest = function() {
+ self.loadFromPath("test.mp3");
+ };
+
+
+ async function getSourceTables() {
+ var wavedata = [];
+ var tbL = await app.getTable(data.fnL);
+ wavedata.push(tbL);
+ if (data.hasOwnProperty("fnR") && data.fnR > 0) {
+ var tbR = await app.getTable(data.fnR);
+ wavedata.push(tbR);
+ }
+ return wavedata;
+ }
+
+ async function setClipWaveform() {
+ if (!waveformClip) {
+ waveformClip = new Waveform({
+ target: elWaveClip,
+ allowSelect: false,
+ showGrid: false,
+ bgColor: "rgb(255, 255, 255, 0)",
+ fgColor: "#000000"
+ });
+ setTimeout(async function(){
+ var sourceTables = await getSourceTables();
+ waveformClip.setData(sourceTables, data.duration);
+ }, 100);
+ } else {
+ waveformClip.redraw();
+ }
+ }
+
+ async function showEditWaveform(target) {
+ target.empty().append(elWaveEdit);
+ if (!waveformEdit) {
+ waveformEdit = new Waveform({
+ target: elWaveEdit,
+ allowSelect: true,
+ showGrid: true,
+ latencyCorrection: twirl.latencyCorrection // , markers:
+ });
+ setTimeout(async function(){
+ var sourceTables = await getSourceTables();
+ waveformEdit.setData(sourceTables, data.duration);
+ }, 100);
+ } else {
+ waveformEdit.redraw();
+ }
+ }
+
+ this.setSize = function() {
+ var beattime = 60 / twine.timeline.bpm;
+ var beatLength;
+ if (data.loop == 0 && data.warp == 0) {
+ if (data.warpMode == 0) {
+ beatLength = data.duration / data.pitch;
+ } else {
+ beatLength = data.duration;
+ }
+ } else {
+ beatLength = data.playLength;
+ }
+ var width = (beatLength / beattime) * twine.timeline.pixelsPerBeat;
+ element.css("width", width + "px");
+ setClipWaveform();
+ }
+
+ this.redraw = function() {
+ self.setSize();
+ };
+
+ this.loadFromPath = function(path, userImported) {
+ var cbid = app.createCallback(async function(ndata) {
+ //await app.unlinkFile(path);
+ if (ndata.status == -1) {
+ return self.errorHandler("File not valid");
+ } else if (ndata.status == -2) {
+ return self.errorHandler("File too large");
+ }
+ datatable = ndata.data.datatable;
+ await getDataFromTable();
+ data.clipindex = ndata.data.clipindex;
+ data.channel = ndata.data.channel;
+ data.name = ndata.data.name;
+ elWaveText.text(data.name);
+ data.soundPath = path;
+
+ if (userImported) {
+ twine.seq.userImportedFiles.push(path);
+ }
+ loaded = true;
+ twirl.loading.hide();
+ self.setSize();
+ });
+ app.insertScore("ecpweb_loadsound", [0, 1, cbid, path, data.channel, data.position]);
+ };
+
+ this.clone = function() {
+ var newdata = Object.assign({}, data);
+ newdata.id = getID();
+ return new Clip(data.channel, newdata, self);
+ };
+
+ this.randomiseWarpPoints = function(mode) { // mode is -1, 0 or 1 I think..
+ if (!mode) mode = 0;
+ var cbid = app.createCallback(function(ndata) {
+ notify("OK");
+ });
+ app.insertScore("ecpweb_randomisewarppoints", [0, 1, cbid, mode]);
+ };
+
+ var dragHandleClip;
+ function handle_mousedown(e){
+ var originalPosition = data.position;
+ dragHandleClip = {};
+ dragHandleClip.pageX0 = e.pageX;
+ dragHandleClip.pageY0 = e.pageY;
+ dragHandleClip.elem = this;
+ dragHandleClip.offset0 = $(this).offset();
+ var isCopying = e.ctrlKey;
+
+ if (isCopying) { // && loaded
+ self.clone();
+ }
+
+ function handle_dragging(e){
+ var left = dragHandleClip.offset0.left + (e.pageX - dragHandleClip.pageX0);
+ var minLeft = (window.screen.width / 100) * 10;
+
+ //round
+ // left = Math.ceil(left / pixelsPerBeat) * pixelsPerBeat;
+
+ if (twine.timeline.snapToGrid) {
+ left = (Math.ceil((left - minLeft) / twine.timeline.pixelsPerBeat) * twine.timeline.pixelsPerBeat) + minLeft;
+ }
+
+
+ if (left < minLeft) {
+ left = minLeft
+ }
+ data.position = twine.timeline.startBeat + ((left - minLeft) / twine.timeline.pixelsPerBeat);
+
+ var header = 50;
+ var top = dragHandleClip.offset0.top + (e.pageY - dragHandleClip.pageY0);
+ top = (Math.ceil(top / chanHeight) * chanHeight) + header;
+ var maxTop = (chanHeight * channels.length) + header;
+
+ if (top > maxTop) {
+ top = maxTop;
+ } else if (top < 30) {
+ top = 30;
+ }
+
+
+ $(dragHandleClip.elem).offset({top: top, left: left});
+ }
+
+ function handle_mouseup(e){
+ $("body")
+ .off("mousemove", handle_dragging)
+ .off("mouseup", handle_mouseup);
+ if (data.position != originalPosition) {
+ self.setData("position", data.position);
+ }
+ }
+
+ $("body").on("mouseup", handle_mouseup).on("mousemove", handle_dragging);
+ }
+
+ if (parent) {
+ self.redraw();
+ }
+};
+
+function runTest() {
+ new Channel();
+ new Channel();
+ new Channel();
+ new Channel();
+ new Channel();
+ new Channel();
+}
+
+var topMenuData = [
+ {name: "File", contents: [
+ {name: "New", disableOnPlay: true, shortcut: {name: "Ctrl N", ctrlKey: true, key: "n"}, click: function(twine) {
+ twine.createNewInstance();
+ }},
+ {name: "Save", disableOnPlay: true, shortcut: {name: "Ctrl S", ctrlKey: true, key: "s"}, click: function(twine) {
+ twine.saveFile();
+ }},
+ {name: "Close", disableOnPlay: true, shortcut: {name: "Ctrl W", ctrlKey: true, key: "w"}, click: function(twine) {
+ twine.closeInstance();
+ }},
+ ]},
+ {name: "Help", contents: [
+ {name: "Help", click: function(twine){
+ $("#twist_documentation")[0].click();
+ }},
+ {name: "About", click: function(twine) {
+ twine.ui.showAbout();
+ }},
+ ]}
+];
+
+
+var Twine = function() {
+ twirl.init();
+ var twine = this;
+ var playing = false;
+ this.onPlays = [];
+ this.selectedClip = null;
+ this.clipUpdates = true;
+ this.timeline = new TimelineGrid(twine);
+ this.seq = new Sequencer(twine);
+ this.topMenu = new twirl.TopMenu(twine, topMenuData, $("#twine_menubar"));
+ this.ui = setupUI(twine);
+
+ twine.ui.head.grid.setValue(1, true);
+ twine.ui.head.snap.setValue(1, true);
+
+ this.setPlaying = function(state) {
+ playing = state;
+ };
+
+ this.play = function() {
+ if (playing) return;
+ twine.setPlaying(true);
+ var time;
+ var reltime;
+ var maxtime = 0;
+ var beatTime = 60 / twine.timeline.bpm;
+ for (var c in clips) {
+ time = clips[c].position;
+ if (time >= twine.timeline.startBeat && time <= twine.timeline.endBeat) {
+ reltime = time - twine.timeline.startBeat;
+ if (reltime + clips[c].duration > maxtime) {
+ maxtime = reltime + clips[c].duration;
+ }
+ app.insertScore("ecp_playback", clips[c].getPlaybackArgs(-1, reltime * beatTime));
+ }
+ }
+ var cbid = app.createCallback(function() {
+ twine.setPlaying(false);
+ });
+ app.insertScore("ecpweb_playbackwatchdog", [0, maxtime, cbid]);
+ };
+
+ this.stop = function() {
+ if (!playing) return;
+ app.insertScore("ecpweb_stopplayback");
+ };
+
+ async function handleFileDrop(e) {
+ e.preventDefault();
+ twirl.loading.show();
+ for (const item of e.originalEvent.dataTransfer.files) {
+ if (!twirl.audioTypes.includes(item.type)) {
+ return self.errorHandler("Unsupported file type", self.ui.showLoadNewPrompt);
+ }
+ if (item.size > twirl.maxFileSize) {
+ return self.errorHandler("File too large", self.ui.showLoadNewPrompt);
+ }
+
+ errorState = "File loading error";
+ var content = await item.arrayBuffer();
+ const buffer = new Uint8Array(content);
+ await app.getCsound().fs.writeFile(item.name, buffer);
+ var x = new Clip(1); // determine channel
+ x.loadFromPath(item.name, true);
+ twirl.loading.hide();
+ }
+ }
+
+ var tempclip = null;
+ $("body").on("dragover", function(e) {
+ e.preventDefault();
+ e.originalEvent.dataTransfer.effectAllowed = "all";
+ e.originalEvent.dataTransfer.dropEffect = "copy";
+
+ if (!tempclip) {
+ tempclip = $("<div />").addClass("clip").text("New Clip");
+ $("#twine_timelineoverlay").show().append(tempclip);
+ }
+
+ tempclip.css("top", e.pageY + "px").css("left", e.pageX + "px");
+
+ return false;
+ }).on("dragleave", function(e) {
+ e.preventDefault();
+ $("#twine_timelineoverlay").hide();
+ if (tempclip) tempclip.remove();
+ }).on("drop", function(e) {
+ $("#twine_timelineoverlay").hide();
+ if (tempclip) tempclip.remove();
+ handleFileDrop(e);
+ });
+};
+
+
+$(function() {
+ window.twine = new Twine();
+
+ window.app = new CSApplication({
+ csdUrl: "twine.csd",
+ files: ["test.mp3"],
+ csOptions: ["--omacro:ECP_NORECORDING=1"],
+ onPlay: function () {
+ runTest();
+ twirl.loading.hide();
+ },
+ errorHandler: twirl.errorHandler,
+ ioReceivers: {percent: twirl.loading.setPercent}
+ });
+
+ $("#start_invoke").click(function() {
+ $("#start").hide();
+ twirl.loading.show();
+ app.play(function(text){
+ twirl.loading.show(text);
+ });
+ });
+
+
+
+
+
+});
+/*
+divisionsPerBeat: 2,
+ duration: 3,
+ beatsLength: 4,
+ utilisedLength: 5,
+ warpMode: 6,
+ pitch: 7,
+ amp: 8,
+ fftSize: 9,
+ txtWinSize: 10,
+ txtRandom: 11,
+ txtOverlap: 12,
+ loop: 13,
+ warp: 14,
+ txtWinType: 15,
+*/
+</script>
+</head>
+<body>
+<div id="start">
+ <div id="start_centre">
+ <p id="start_invoke">Press to begin</p>
+ </div>
+</div>
+
+<div id="twine_header">
+ <table id="twine_headertable"><tbody><tr>
+ <td id="twine_head_play"></td>
+ <td id="twine_head_snap"></td>
+ <td id="twine_head_showgrid"></td>
+ </tr></tbody></table>
+</div>
+<div id="twine_menubar"></div>
+<div id="twine_main">
+ <div id="twine_timeline"></div>
+ <div id="twine_timelineoverlay"></div>
+ <div id="twine_details">
+ <div id="twine_channeldetailslow"></div>
+ <div id="twine_clipdetails">
+ <div id="twine_clipdetailsleft">
+ <table><tbody>
+ <tr>
+ <td id="twine_clip_audition"></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ </tr>
+ <tr>
+ <td id="twine_clip_colour"></td>
+ <td id="twine_clip_name"></td>
+ <td></td>
+ <td></td>
+ </tr>
+ </tbody></table>
+ <table><tbody>
+ <tr>
+ <td>Read type</td>
+ <td id="twine_clip_readtype"></td>
+ <td id="twine_clip_warp"></td>
+ <td id="twine_clip_loop"></td>
+ </tr>
+ </tbody></table>
+ <table><tbody id="twine_clipparamsbottom">
+ </tbody></table>
+ </div>
+ <div id="twine_clipdetailsright"></div>
+ </div>
+ </div>
+</div>
+</body>
+</html> \ No newline at end of file
diff --git a/site/app/twine/_hOLD/timeline.csd b/site/app/twine/_hOLD/timeline.csd
new file mode 100644
index 0000000..72e03ae
--- /dev/null
+++ b/site/app/twine/_hOLD/timeline.csd
@@ -0,0 +1,154 @@
+<CsoundSynthesizer>
+<CsOptions>
+-odac
+</CsOptions>
+<CsInstruments>
+sr = 44100
+ksmps = 64
+nchnls = 2
+0dbfs = 1
+seed 0
+
+#include "/scss/elasticlip_sequencer.udo"
+;#include "/scss/mixer/base.udo"
+#include "/interop.udo"
+#include "/bussing.udo"
+#include "/table_tools.udo"
+
+opcode ecpweb_getdata, S, iii
+ ichannel, iclipindex, itime xin
+ Sname = gSecp_clipnames[iclipindex]
+ Sresponse = sprintf("{\"-1\":%f,\"-2\":\"%s\",\"-4\":%d", itime, Sname, ichannel)
+
+ index = 2
+ while (index < giecp_controlitemnum) do
+ ival = tab_i(index, giecp_fnclips[iclipindex])
+ Sformat = strcat(",\"%d\":%", (frac(ival) == 0) ? "d" : "f")
+ Sresponse = strcat(Sresponse, sprintf(Sformat, index, ival))
+ index += 1
+ od
+ Sresponse = strcat(Sresponse, "}")
+ xout Sresponse
+endop
+
+opcode ecpweb_getdata, S, i
+ iseqindex xin
+ ichannel, iclipindex, itime ecpseq_get iseqindex
+ Sresponse ecpweb_getdata ichannel, iclipindex, itime
+ xout Sresponse
+endop
+
+instr ecpweb_getdata
+ icbid = p4
+ iseqindex = p5
+endin
+
+instr ecpweb_tabdestroy
+ ifn = p4
+ tab_destroy ifn
+ turnoff
+endin
+
+instr ecpweb_setbpm
+ ibpm = p4
+ gkseq_tempo init ibpm
+ turnoff
+endin
+
+instr ecpweb_loadsound
+ icbid = p4
+ Spath = strget(p5)
+ ichannel = p6
+ itime = p7
+ iforcemono = p8
+
+ iclipindex ecp_loadsound Spath, 4, iforcemono ;beats contentious, analyse and also set warp mode
+ iseqindex ecpseq_getnewindex
+
+ ecpseq_set iseqindex, ichannel, iclipindex, itime
+ Sdata ecpweb_getdata ichannel, iclipindex, itime
+
+ ifnwave ecp_getwaveform iclipindex
+
+ Sresponse = sprintf("{\"cbid\":%d,\"data\":%s,\"waveform\":%d}", icbid, Sdata, ifnwave)
+ io_sendstring("callback", Sresponse)
+ turnoff
+endin
+
+
+instr ecpweb_copyclip
+ icbid = p4
+ iseqindex = p5
+ ichannel = p6
+ itime = p7
+
+ i_, iclipindex, i_ ecpseq_get iseqindex
+
+ iseqindex ecpseq_getnewindex
+ ecpseq_set iseqindex, ichannel, iclipindex, itime
+ Sresponse = sprintf("{\"cbid\":%d}", icbid)
+ io_sendstring("callback", Sresponse)
+endin
+
+
+instr ecpweb_setdata
+ icbid = p4
+ iseqindex = p5
+ idataindex = p6
+ ;p7 is value
+ ihastwin = p8
+
+ Sresponse = sprintf("{\"cbid\":%d", icbid)
+ iapplyupdate = 0
+ ichannel, iclipindex, itime ecpseq_get iseqindex
+
+ if (ihastwin == 1 && idataindex != -1) then
+ iclipindex ecp_cloneclip iclipindex
+ Sresponse = strcat(Sresponse, sprintf(",\"clipindex\":%d", iclipindex)) ; needed?
+ endif
+
+ if (idataindex < 0) then
+ if (idataindex == -2) then ;name
+ Sname = strget(p7)
+ ecp_set_name iclipindex, Sname
+ Sresponse = strcat(Sresponse, sprintf(",\"name\":\"%s\"", Sname))
+ elseif (idataindex == -1) then ;position
+ itime = p7
+ iapplyupdate = 1
+ elseif (idataindex == -4) then ;channel
+ ichannel = p7
+ iapplyupdate = 1
+ endif
+
+ elseif (idataindex >= 2) then
+ ivalue = p7
+ tabw_i ivalue, idataindex, giecp_fnclips[iclipindex]
+ endif
+
+ if (iapplyupdate == 1) then
+ ecpseq_set iseqindex, ichannel, iclipindex, itime
+ endif
+
+ io_sendstring("callback", strcat(Sresponse, "}"))
+ turnoff
+endin
+
+instr boot
+ schedule("mx_boot", 0, 1)
+
+ aL0, aR0 bus_read "mxchan0"
+ aL1, aR1 bus_read "mxchan1"
+ aL2, aR2 bus_read "mxchan2"
+ aL3, aR3 bus_read "mxchan3"
+
+ aoutL = aL0 + aL1 + aL2 + aL3
+ aoutR = aR0 + aR1 + aR2 + aR3
+ outs aoutL, aoutR
+endin
+
+</CsInstruments>
+<CsScore>
+i"boot" 0 36000
+f0 z
+</CsScore>
+</CsoundSynthesizer> \ No newline at end of file
diff --git a/site/app/twine/_hOLD/timeline_base.html b/site/app/twine/_hOLD/timeline_base.html
new file mode 100644
index 0000000..fc460bd
--- /dev/null
+++ b/site/app/twine/_hOLD/timeline_base.html
@@ -0,0 +1,633 @@
+<html>
+<head>
+<style type="text/css">
+
+body {
+ font-family: Arial, sans-serif;
+ font-size: 11pt;
+}
+
+#header {
+ position: absolute;
+ top: 0px;
+ height: 30px;
+ left: 0px;
+ width: 100%;
+ background-color: #545454;
+}
+
+#headertable {
+ height: 30px;
+}
+
+#clipdetails {
+ display: none;
+}
+
+
+#main {
+ position: absolute;
+ top: 30px;
+ height: 100%;
+ left: 0px;
+ width: 100%;
+}
+
+#timeline {
+ position: absolute;
+ left: 0px;
+ top: 0px;
+ width: 100%;
+ height: 70%
+}
+
+.timelinetext {
+ font-size: 8pt;
+ opacity: 0.9;
+ position: absolute;
+ color: #121212;
+ top: 2px;
+}
+
+.timelinemarker {
+ width: 1px;
+ position: absolute;
+ background-color: #bdbdbd;
+ opacity: 0.9;
+ height: 100%;
+ top: 0px;
+ z-index: 50;
+}
+
+.smbut {
+ font-size: 8pt;
+ background-color: #b5b01d;
+ border: 1px solid black;
+}
+
+
+
+#details {
+ position: absolute;
+ left: 0px;
+ top: 70%;
+ width: 100%;
+ height: 30%;
+ background-color: #dcdcdc;
+}
+
+#clipdetailsleft {
+ position: absolute;
+ left: 0px;
+ top: 0px;
+ width: 30%;
+ height: 100%;
+ background-color: #acacac;
+}
+
+#clipdetailsright {
+ position: absolute;
+ left: 30%;
+ top: 0px;
+ width: 70%;
+ height: 100%;
+ background-color: #cacaca;
+}
+
+.channel {
+ height: 30px;
+ width: 100%;
+ border-bottom: 1px solid #aaaaaa;
+ left: 0px;
+}
+
+.channeldetails {
+ position: absolute;
+ height: 30px;
+ left: 0px;
+ width: 10%;
+}
+
+.channelclips {
+ position: absolute;
+ height: 30px;
+ left: 10%;
+ width: 90%;
+ background-color: #cdcdcd;
+}
+
+.clip {
+ user-select: none;
+ position: absolute;
+ padding: 0px;
+ cursor: move;
+ z-index: 70;
+ width: 100px;
+ height: 30px;
+ color: #000000;
+ overflow: hidden;
+}
+</style>
+<script type="text/javascript" src="https://apps.csound.1bpm.net/code/jquery.js"></script>
+<script type="text/javascript">
+
+var clips = {};
+var channels = [];
+var maxID = 0;
+var chanHeight = 30;
+
+window.csseq = {};
+
+function getID() {
+ return maxID++;
+}
+
+var TimelineGrid = function(data) {
+ var self = this;
+
+ if (!data) {
+ var data = {
+ snapToGrid: true,
+ gridVisible: true,
+ timeSigMarker: 4,
+ resolution: null,
+ startBeat: 1,
+ endBeat: 16,
+ bpm: null
+ };
+ }
+
+ this.getSaveData = function() {
+ return data;
+ };
+
+ function calcViewport() {
+ data.minLeft = (window.screen.width / 100) * 10;
+ var width = window.screen.width - data.minLeft;
+ var beats = data.endBeat - data.startBeat;
+ data.pixelsPerBeat = width / beats;
+ return {
+ minLeft: data.minLeft,
+ width: width,
+ beats: beats,
+ pixelsPerBeat: Math.round(data.pixelsPerBeat)
+ }
+ }
+
+ function draw() {
+ $(".timelinemarker").remove();
+ $(".timelinetext").remove();
+ if (!data.gridVisible) {
+ return;
+ }
+
+ var target = $("#timeline");
+ var geometry = calcViewport();
+
+ var beat = data.startBeat;
+
+ var width;
+ var fontWeight;
+ for (var x = geometry.minLeft; x < window.screen.width; x += geometry.pixelsPerBeat) {
+ if ((beat - 1) % data.timeSigMarker == 0) {
+ width = 2;
+ fontWeight = "bold";
+ } else {
+ width = 1;
+ fontWeight = "normal";
+ }
+ $("<div />").attr("class", "timelinemarker").appendTo(target).css("width", width).css("left", x);
+ $("<div />").attr("class", "timelinetext").appendTo(target).css("font-weight", fontWeight).css("left", x + 2).text(beat);
+ beat ++;
+ }
+ }
+
+ Object.defineProperty(this, "startBeat", {
+ get: function() { return data.startBeat; },
+ set: function(x) {
+ data.startBeat = x;
+ draw();
+ }
+ });
+
+ Object.defineProperty(this, "endBeat", {
+ get: function() { return data.startBeat; },
+ set: function(x) {
+ data.startBeat = x;
+ draw();
+ }
+ });
+
+ Object.defineProperty(this, "minLeft", {
+ get: function() { return data.minLeft; },
+ set: function(x) {}
+ });
+
+ Object.defineProperty(this, "pixelsPerBeat", {
+ get: function() { return data.pixelsPerBeat; },
+ set: function(x) {}
+ });
+
+ Object.defineProperty(this, "snapToGrid", {
+ get: function() { return data.snapToGrid; },
+ set: function(x) {
+ data.snapToGrid = (x == 1);
+ }
+ });
+
+ Object.defineProperty(this, "gridVisible", {
+ get: function() { return data.gridVisible; },
+ set: function(x) {
+ data.gridVisible = (x == 1);
+ draw();
+ }
+ });
+
+
+
+ draw();
+};
+
+
+var Sequencer = function(data) {
+ var self = this;
+ var gridSnap = 0;
+ var timeScale = 1;
+
+ if (!data) {
+ var data = {
+ tempo: 120,
+ playing: false
+ };
+ }
+
+ this.setTempo = function(v) {
+ insertScore("ecpweb_setglobal", [0, 1, 0, v]);
+ };
+
+ this.getSaveData = function() {
+ var sdata = {"sequencer": data, "channels": [], "clips": {}};
+ for (var x in channels) {
+ sdata.channels.push(channels[x].getSaveData());
+ }
+ for (var x in clips) {
+ sdata.clips[x] = clips[x].getSaveData();
+ }
+ return sdata;
+ };
+
+ this.loadSaveData = function(v) {
+ data = v.sequencer;
+ for (var x in v.channels) {
+ new Channel(v.channels[x]);
+ }
+ };
+
+ Object.defineProperty(this, "playing", {
+ get: function() { return data.playing; },
+ set: function(x) {
+ data.playing = x;
+ }
+ });
+};
+
+
+var Channel = function(data) {
+ var self = this;
+ channels.push(this);
+
+ if (!data) {
+ var index = channels.length;
+ var data = {
+ name: "Channel " + index,
+ index: index
+ };
+ } else {
+ var index = data.index;
+ }
+
+ var element = $("<div />").attr("id", "channel" + index).attr("class", "channel").appendTo("#timeline").append(
+ $("<div />").attr("id", "channeldetails" + index).attr("class", "channeldetails").text(data.name)
+ ).append(
+ $("<div />").attr("id", "channelclips" + index).attr("class", "channelclips")
+ );
+};
+
+var Clip = function(channel, data, parent) {
+ var self = this;
+ var loaded = false;
+
+ if (!data) {
+ var id = getID();
+ var data = {
+ name: "Clip " + id,
+ channel: channel,
+ id: id,
+ clipindex: null,
+ beatTime: 0,
+ colour: "#" + (Math.random() * 0xFFFFFF << 0).toString(16)
+ };
+ } else {
+ var id = data.id;
+ }
+ clips[id] = this;
+ var element = $("<div />").attr("class", "clip").attr("id", "clip" + id).text(data.name).click(function() {
+ csseq.selectedClip = self;
+ $("#clipdetails").show();
+ $("#clip_name").val(data.name);
+ $("#clip_colour").val(data.colour);
+
+ }).css("top", (channel * chanHeight) + "px").css("background-color", data.colour).mousedown(handle_mousedown)
+ .css("left", ((data.beatTime * csseq.timeline.pixelsPerBeat)) + "px")
+ .appendTo($("#channelclips" + channel));
+ // resizable()
+
+ Object.defineProperty(this, "colour", {
+ get: function() { return data.colour; },
+ set: function(x) {
+ data.colour = x;
+ element.css("background-color", data.colour);
+ }
+ });
+
+ var dataMode = {
+ divisionsPerBeat: 2,
+ duration: 3,
+ beatsLength: 4,
+ utilisedLength: 5,
+ warpMode: 6,
+ pitch: 7,
+ amp: 8,
+ fftSize: 9,
+ txtWinSize: 10,
+ txtRandom: 11,
+ txtOverlap: 12,
+ loop: 13,
+ warp: 14,
+ txtWinType: 15,
+ // warp points are 16 + in table
+
+ position: -1,
+ name: -2,
+ soundPath: -3,
+ channel: -4
+ }
+
+ for (let x in dataMode) {
+ Object.defineProperty(this, x, {
+ get: function() { return data[x] },
+ set: function(v) {
+ self.setData(x, v);
+ }
+ });
+ }
+
+ /*// v should become defunct
+ Object.defineProperty(this, "name", {
+ get: function() { return data.name; },
+ set: function(x) {
+ data.name = x;
+ element.text(data.name);
+
+ // self.setData("name", x);
+ }
+ });*/
+
+
+ /*
+ function getData() {
+ var cbid = app.createCallback(function(ndata) {
+ for (var x in ndata.data) {
+ data[x] = ndata.data[x];
+ }
+ });
+ app.insertScore("ecpweb_getdata", [0, 1, cbid, clipindex]);
+ }*/
+
+ this.setData = function(modeString, v) {
+ var cbid = app.createCallback(function(ndata) {
+ if (ndata.hasOwnProperty("clipindex")) {
+ data.clipindex = ndata.clipindex;
+ }
+ data[modeString] = v;
+ if (modeString == "name") {
+ element.text(data.name);
+ }
+ });
+ app.insertScore("ecpweb_setdata", [0, 1, cbid, id, dataMode[modeString], v, (parent ? 1 : 0)]); // having ecp_cloneclip in if needed
+ }
+
+ this.loadTest = function() {
+ self.loadFromPath("test.mp3");
+ };
+
+ this.loadFromPath = function(path) {
+ var cbid = app.createCallback(function(ndata) {
+ for (var x in ndata.data) {
+ data[x] = ndata.data[x];
+ }
+ data.soundPath = path;
+ loaded = true;
+ });
+ app.insertScore("ecpweb_loadsound", [0, 1, cbid, path]);
+ };
+
+ this.clone = function() {
+ var newdata = Object.assign({}, data);
+ data.id = getID();
+ return new Clip(data.channel, newdata, self);
+ };
+
+ this.randomiseWarpPoints = function(mode) { // mode is -1, 0 or 1 I think..
+ if (!mode) mode = 0;
+ var cbid = app.createCallback(function(ndata) {
+ notify("OK");
+ });
+ app.insertScore("ecpweb_randomisewarppoints", [0, 1, cbid, mode]);
+ };
+
+
+ function handle_mousedown(e){
+
+ window.my_dragging = {};
+ my_dragging.pageX0 = e.pageX;
+ my_dragging.pageY0 = e.pageY;
+ my_dragging.elem = this;
+ my_dragging.offset0 = $(this).offset();
+ var isCopying = e.ctrlKey;
+
+ if (isCopying) { // && loaded
+ self.clone();
+ }
+
+ function handle_dragging(e){
+ var left = my_dragging.offset0.left + (e.pageX - my_dragging.pageX0);
+
+ var minLeft = (window.screen.width / 100) * 10;
+
+ //round
+ // left = Math.ceil(left / pixelsPerBeat) * pixelsPerBeat;
+
+ if (csseq.timeline.snapToGrid) {
+ left = (Math.ceil((left - minLeft) / csseq.timeline.pixelsPerBeat) * csseq.timeline.pixelsPerBeat) + minLeft;
+ }
+
+
+ if (left < minLeft) {
+ left = minLeft
+ }
+ data.beatTime = csseq.timeline.startBeat + ((left - minLeft) / csseq.timeline.pixelsPerBeat);
+
+ var top = my_dragging.offset0.top + (e.pageY - my_dragging.pageY0);
+ top = Math.ceil(top / chanHeight) * chanHeight;
+ var maxTop = chanHeight * channels.length;
+ if (top > maxTop) {
+ top = maxTop;
+ } else if (top < 30) {
+ top = 30;
+ }
+
+
+ $(my_dragging.elem)
+ .offset({top: top, left: left});
+ }
+
+ function handle_mouseup(e){
+ $("body")
+ .off("mousemove", handle_dragging)
+ .off("mouseup", handle_mouseup);
+ }
+
+ $("body").on("mouseup", handle_mouseup).on("mousemove", handle_dragging);
+ }
+
+ /*
+ this.setLength = function(v) {
+ setData("length", v);
+ }; // now made generic above.
+ */
+};
+
+
+
+
+$(function() {
+ csseq.timeline = new TimelineGrid();
+ csseq.seq = new Sequencer();
+
+
+ $(".smbut").attr("value", 0).click(function(){
+ var val;
+ var col;
+ if ($(this).attr("value") == 0) {
+ val = 1;
+ col = "#f2e30c";
+ } else {
+ val = 0;
+ col = "#b5b01d";
+ }
+ $(this).attr("value", val).css("background-color", col);
+ });
+
+ $("#head_snap").click(function() {
+ csseq.timeline.snapToGrid = $(this).attr("value");
+ });
+
+ $("#head_showgrid").click(function() {
+ csseq.timeline.gridVisible = $(this).attr("value");
+ });
+
+ $("#head_play").click(function() {
+ if (csseq.seq.playing) {
+ app.insertScore("ecpseq_stop");
+ } else {
+ var cbid = app.createCallback(function(data) {
+ if (data.state == "playing") {
+ csseq.seq.playing = true;
+ $(this).text("Stop");
+ } else if (data.state == "stopped") {
+ csseq.seq.playing = false;
+ $(this).text("Play");
+ app.removeCallback(data.cbid);
+ }
+ }, true);
+ app.insertScore("ecpseq_play", [0, 36000, cbid, csseq.timeline.startBeat]);
+ }
+ });
+
+ new Channel();
+ new Channel();
+ new Channel();
+ new Clip(0);
+ new Clip(1);
+ new Clip(2);
+
+
+ $("#clip_colour").change(function(){
+ csseq.selectedClip.colour = $(this).val();
+ });
+
+ $("#clip_name").change(function(){
+ csseq.selectedClip.name = $(this).val();
+ });
+
+});
+/*
+divisionsPerBeat: 2,
+ duration: 3,
+ beatsLength: 4,
+ utilisedLength: 5,
+ warpMode: 6,
+ pitch: 7,
+ amp: 8,
+ fftSize: 9,
+ txtWinSize: 10,
+ txtRandom: 11,
+ txtOverlap: 12,
+ loop: 13,
+ warp: 14,
+ txtWinType: 15,
+*/
+</script>
+</head>
+<body>
+<div id="header">
+ <table id="headertable"><tbody><tr>
+ <td><button id="play" class="smbut">Play</button></td>
+ <td><button id="head_snap" class="smbut">Snap</button></td>
+ <td><button id="head_showgrid" class="smbut">Grid</button></td>
+ </tr></tbody></table>
+</div>
+<div id="main">
+ <div id="timeline"></div>
+ <div id="details">
+ <div id="clipdetails">
+ <div id="clipdetailsleft">
+ <table><tbody>
+ <tr>
+ <td><input type="color" id="clip_colour"></td><td><input type="text" id="clip_name"></td>
+ </tr>
+ <tr>
+ <td>Read type</td>
+ <td><select id="clip_readtype">
+ <option value="0">Repitch</option>
+ <option value="1">Grain</option>
+ <option value="2">FFT</option>
+ <option value="3">FFTab</option>
+ </select>
+ </td>
+ </tr>
+ <tr><td><button id="clip_warp" class="smbut">Warp</button></td><td><button class="smbut">Loop</button></td></tr>
+ <tr><td>
+ <x-knob divisions="12" min="-12" max="12">
+ </td><td></td></tr>
+ </tbody></table>
+ </div>
+ <div id="clipdetailsright">
+
+ </div>
+ </div>
+ </div>
+</div>
+</body>
+</html> \ No newline at end of file