aboutsummaryrefslogtreecommitdiff
path: root/site/app/twine/_hOLD/index.html
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/index.html
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/index.html')
-rw-r--r--site/app/twine/_hOLD/index.html863
1 files changed, 863 insertions, 0 deletions
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