aboutsummaryrefslogtreecommitdiff
path: root/site/app/twine/clip.js
diff options
context:
space:
mode:
Diffstat (limited to 'site/app/twine/clip.js')
-rw-r--r--site/app/twine/clip.js769
1 files changed, 769 insertions, 0 deletions
diff --git a/site/app/twine/clip.js b/site/app/twine/clip.js
new file mode 100644
index 0000000..4705d95
--- /dev/null
+++ b/site/app/twine/clip.js
@@ -0,0 +1,769 @@
+var Clip = function(twine, data, parent) {
+ var clip = this;
+ var loaded = false;
+ var waveformClip;
+ var waveformEdit;
+ var datatable;
+ this.channel = null;
+ var minWidth = 10;
+ this.types = {AUDIO: 0, SCRIPT: 1};
+
+
+ if (!data) {
+ var id = twine.getNewID();
+ var data = {
+ name: "Clip " + id,
+ type: (parent) ? parent.type : null,
+ id: id,
+ clipindex: null,
+ playLength: 1,
+ pitch: 1,
+ colour: "#" + (Math.random() * 0xFFFFFF << 0).toString(16),
+ position: 0,
+ // debugs:
+ duration: 1,
+ warp: 0,
+ loop: 0,
+ script: ""
+ };
+ } else {
+ data.id = twine.getNewID();
+ 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);
+ }
+ });
+
+ Object.defineProperty(this, "isAudio", {
+ get: function() { return (clip.data.type == clip.types.AUDIO); },
+ set: function(x) {}
+ });
+
+ twine.undo.add("add clip", function(){
+ clip.destroy();
+ twine.timeline.redraw();
+ });
+
+ this.exportData = async function() {
+ // tablecopyout messes first few values. so loop...
+ var len = await app.getCsound().tableLength(datatable);
+ var items = [];
+ for (var i = 0; i < len; i ++) {
+ items.push(await app.getCsound().tableGet(datatable, i));
+ }
+ var local = {};
+ for (var d in dataMode) {
+ if (dataMode[d] < 0) {
+ local[d] = data[d];
+ }
+ }
+ return {table: items, local: local};
+ };
+
+ this.importData = async function(loadData) {
+
+ if (datatable && clip.data.clipindex) {
+ await app.insertScoreAsync("twine_removeclip", [clip.data.clipindex]);
+ }
+ datatable = await twine.timeline.copyNewTableIn(loadData.table);
+ var ndata = await app.insertScoreAsync("twine_importclip", [datatable]);
+ for (var d in loadData.local) {
+ data[d] = loadData.local[d];
+ }
+ data.clipindex = ndata.data.clipindex;
+ await getDataFromTable();
+ console.log("import", loadData, data);
+ loaded = true;
+ clip.redraw(); // race here?
+ };
+
+ this.destroy = function(onComplete) {
+ function done() {
+ clip.element.remove();
+ clip.channel.removeClip(clip);
+ if (onComplete) {
+ onComplete();
+ }
+ }
+ if (clip.isAudio) {
+ app.insertScore("twine_removeclip", [0, 1, app.createCallback(done), clip.data.clipindex]);
+ } else {
+ done();
+ }
+ };
+
+ 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, utilisedStart: 16, phaseLock: 17, sr: 18,
+ // warp points are after this+ in table
+ position: -1, name: -2, clipindex: -3, playLength: -4, type: -5
+ };
+
+
+ this.element = $("<div />").addClass("twine_clip").css({
+ "background-color": data.colour
+ }).on("contextmenu", function(e){
+ return twirl.contextMenu.show(e, [
+ {name: "Delete", click: function(){
+ clip.destroy();
+ }},
+ {name: "Audition", click: function(){
+ clip.play();
+ }}
+ ]);
+ }).click(function(e){
+ if (e.ctrlKey) {
+ twine.timeline.selectedClips.push(clip);
+ } else {
+ $(".twine_clip").css("outline", "none");
+ twine.timeline.selectedClips = [clip];
+ }
+ var uiType;
+ var cui = twine.ui.clip;
+ clip.markSelected();
+ if (clip.isAudio) {
+ uiType = twine.ui.pane.CLIPAUDIO;
+ var items = [
+ "name", "colour", "amp", "warp", "warpMode", "pitch",
+ "fftSize", "txtWinSize", "txtRandom", "txtOverlap",
+ "txtWinType", "phaseLock"
+ // , "loop"
+ ];
+ for (let i of items) {
+ cui[i].setValue(data[i]);
+ }
+ showEditWaveform($("#twine_clipdetailsrightaudio"));
+ } else {
+ uiType = twine.ui.pane.CLIPSCRIPT;
+ cui.scriptEdit.setValue(data.script);
+ }
+ twine.ui.showPane(uiType);
+ });
+
+ 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() {
+ if (!clip.isAudio) return;
+ 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) {
+ await setFromKey(k);
+ }
+ }
+
+ function setClipAudioUnique(onComplete) {
+ if (!clip.isAudio) return onComplete();
+ twirl.loading.show();
+ var cbid = app.createCallback(async function(ndata){
+ await getDataFromTable();
+ twirl.loading.hide();
+ if (onComplete) onComplete();
+ });
+ app.insertScore("twine_setclipaudiounique", [0, 1, cbid]);
+ }
+
+ function getScriptInstrName() {
+ return "twinescript" + id;
+ }
+
+ this.setScript = function(script, onready) {
+ var originalScript = clip.data.script;
+ if (script) {
+ clip.data.script = script;
+ }
+ var instr = "instr " + getScriptInstrName() + "\n" +
+ "iduration = p5\nichannel = p6\n" +
+ clip.data.script + "\nendin";
+ app.compileOrc(instr).then(function(status){
+ if (status >= 0 && onready) { // errors will be caught by app and shown
+ twine.undo.add("set script", function(){
+ clip.data.script = originalScript;
+ if (twine.timeline.selectedClip == clip) {
+ cui.scriptEdit.setValue(data.script);
+ }
+ });
+ onready();
+ }
+ });
+ };
+
+ this.initScript = function() {
+ data.warp = 1;
+ data.duration = 1;
+ clip.data.playLength = 4;
+ clip.data.type = clip.types.SCRIPT;
+ clip.data.script = "; your Csound instrument here";
+ loaded = true;
+ };
+
+ 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);
+ }
+
+ function checksr() { // twist uses tfi transforms that may require sample sr to be running sr..
+ if (twine.sr != data.sr) {
+ twirl.loading.show();
+ var cbid = app.createCallback(async function(ndata){
+ await getDataFromTable();
+ twirl.loading.hide();
+ edit();
+ });
+ app.insertScore("twine_convertsr", [0, 1, cbid, data.clipindex, twine.sr]);
+ } else {
+ edit();
+ }
+ }
+ if (asUnique) {
+ setClipAudioUnique(checksr);
+ } else {
+ checksr();
+ }
+
+ };
+
+ 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);
+ setClipWaveform();
+ }
+ return;
+ }
+
+ if (!twine.offline || !clip.isAudio) app.getCsound().tableSet(datatable, dataMode[modeString], v);
+
+ if (onComplete) onComplete();
+ };
+
+ var playbackcbid;
+ this.play = function(onCallback) {
+ var instr;
+ var args;
+ var channel = clip.channel.getCsChannelName();
+ var cbid = app.createCallback(function(ndata) {
+ if (ndata.status == 0) {
+ app.removeCallback(ndata.cbid);
+ playbackcbid = null;
+ } else {
+ playbackcbid = ndata.cbid;
+ }
+ if (onCallback) {
+ onCallback(ndata);
+ }
+ }, true);
+ if (clip.isAudio) {
+ instr = "ecp_playaudition";
+ args = [0, data.playLength, cbid, data.clipindex, data.playLength, channel];
+ } else {
+ instr = getScriptInstrName();
+ args = [0, data.playLength, cbid, data.playLength, channel];
+ }
+ app.insertScore(instr, args);
+ };
+
+ this.stop = function(onCallback) {
+ if (!playbackcbid) return;
+ app.insertScore("ecp_stopaudition", [0, 1, playbackcbid]);
+ };
+
+ async function getSourceTableData() {
+ if (!clip.isAudio) return;
+ 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 (twine.offline || !clip.isAudio || !twine.storage.showClipWaveforms) return;
+ 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) {
+ console.log("redraw wave");
+ waveformClip.redraw();
+ }
+ }
+
+ async function showEditWaveform(target) {
+ if (twine.offline || !clip.isAudio) return;
+ 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", semitones);
+ 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);
+ var endPos = data.position + data.playLength;
+ if (endPos < 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"
+ };
+ elWaveText.text(data.name);
+ clip.element.show().css(css);
+ if (endPos > twine.timeline.beatRegion[1] - 8) {
+ var extension = endPos + 8;
+ twine.timeline.extend(twine.timeline.beatRegion[1] + extension);
+ }
+ };
+
+ this.clone = async function() {
+ var newData = Object.assign({}, data);
+ newData.id = twine.getNewID();
+ var c = new Clip(twine, newData, clip);
+ clip.channel.addClip(c);
+ if (!twine.offline && clip.isAudio) {
+ var ndata = await app.insertScoreAsync("twine_cloneclip", [clip.data.clipindex]);
+ await c.loadFromDataTable(ndata.datatable, ndata.clipindex);
+
+ } else {
+ loaded = true;
+ c.setScript();
+ c.redraw();
+ }
+ return c;
+ };
+
+
+ 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;
+ }
+ setTimeout(function(){
+ if (defaultLength) {
+ data.playLength = data.duration / (60 / twine.timeline.data.bpm);
+ console.log("deflength", data.playLength);
+ }
+ if (!colour) colour = twine.randomColour();
+ data.colour = colour;
+ loaded = true;
+ clip.redraw();
+ }, 50); // csound race
+ };
+
+ this.loadFromDataTable = async function(newDatatable, clipindex) {
+ datatable = newDatatable;
+ await getDataFromTable();
+ data.clipindex = clipindex;
+ clip.data.type = clip.types.AUDIO;
+ loaded = true;
+ setTimeout(clip.redraw, 20);
+ //clip.redraw();
+ };
+
+ this.loadFromFtables = function(name, tables, colour) {
+ clip.data.type = clip.types.AUDIO;
+ 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) {
+ clip.data.type = clip.types.AUDIO;
+ if (twine.offline) {
+ loaded = true;
+ clip.redraw();
+ return;
+ }
+ 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]);
+ };
+
+ this.createSilence = function(stereo, duration, name, colour) {
+ clip.data.type = clip.types.AUDIO;
+ twirl.loading.show("Creating");
+ var cbid = app.createCallback(async function(ndata){
+ await loadData(ndata, name, colour);
+ twirl.loading.hide();
+ });
+ app.insertScore("twine_createblankclip", [0, 1, cbid, stereo, duration]);
+ };
+
+ function getMaxClipWidth() {
+ var maxWidth = 9999;
+ if (!data.warp && !data.loop) {
+ maxWidth = data.duration * twine.timeline.pixelsPerBeat;
+ }
+ return maxWidth;
+ }
+
+ this.markSelected = function (unselected) {
+ if (unselected) {
+ clip.element.css("outline", "none");
+ } else {
+ clip.element.css("outline", "1px dashed white");
+ }
+ };
+
+ clip.movement = {
+ startX: 0, startY: 0, startXabs: 0, startYabs: 0, clipWidth: 0,
+ clipLeft: 0, clipTop: 0, lastLeft: 0, isCopying: false, lastYabs: 0, extendHold: false, doClipRedraw: false, originalClipData: null, mouseMoveInnerFunc: null, clonedClips: null, masterClip: null, moved: false, channel: null,
+ mouseDown: function(e, dragType) {
+ e.preventDefault();
+ e.stopPropagation();
+ var cm = clip.movement;
+ cm.moved = false;
+ cm.originalClipData = [];
+ cm.clonedClips = [];
+ cm.mouseMoveInnerFunc = function(e) {
+ clip.movement.doDragInner(e, dragType);
+ };
+ for (let c of twine.timeline.selectedClips) {
+ cm.originalClipData.push({
+ clip: c,
+ position: c.data.position,
+ playLength: c.data.playLength
+ });
+ c.movement.mouseDownInner(e, dragType);
+ }
+ $("html").on("mouseup", cm.endDragMaster);
+ },
+ mouseDownInner: function(e, dragType) {
+ var cm = clip.movement;
+ $("html").on("mousemove", cm.mouseMoveInnerFunc).on("mouseup", cm.endDrag);
+ cm.masterClip = clip; // for cloning
+ cm.channel = clip.channel;
+ cm.originalPlaylength = data.playlength;
+ cm.originalPosition = data.position;
+ cm.isCopying = false;
+ cm.clipWidth = parseFloat(clip.element.css("width"));
+ cm.clipTop = parseFloat(clip.element.css("top"));
+ cm.clipLeft = parseFloat(clip.element.css("left"));
+ cm.startXabs = e.clientX;
+ cm.startYabs = e.clientY;
+ cm.startX = cm.startXabs - e.target.getBoundingClientRect().left;
+ cm.startY = cm.startYabs - e.target.getBoundingClientRect().top;
+ cm.lastLeft = (e.clientX - cm.startX - clip.channel.offset.left);
+ cm.lastYabs = cm.startYabs;
+ //$("#container").css("cursor", "e-resize");
+ },
+ endDragMaster: function(e) {
+ var cm = clip.movement;
+ $("html").off("mouseup", cm.endDragMaster);
+ if (!cm.moved) return;
+ var clipData = [...cm.originalClipData];
+ var clonedClips = [...cm.clonedClips];
+ console.log("add move undo");
+ twine.undo.add("move/resize clip", function(){
+ clipData.forEach(function(d){
+ d.clip.data.position = d.position;
+ d.clip.data.playLength = d.playLength;
+ d.clip.redraw();
+ });
+ clonedClips.forEach(function(c){
+ c.destroy();
+ });
+ });
+ },
+ endDrag: function(e) {
+ e.preventDefault();
+ var cm = clip.movement;
+ cm.isCopying = false;
+ $("html").off("mouseup", cm.endDrag).off("mousemove", cm.mouseMoveInnerFunc);
+ $("#container").css("cursor", "pointer");
+ if (cm.doClipRedraw) {
+ setClipWaveform();
+ }
+ },
+ doDrag: function(e, dragType) {
+ e.preventDefault();
+ //$("html").off("mouseup", this.initialMouseUp);
+ twine.timeline.selectedClips.forEach(function(c){
+ c.movement.doDragInner(e, dragType);
+ });
+ },
+ setExtendHold: function() {
+ var cm = clip.movement;
+ cm.extendHold = true;
+ setTimeout(function() {
+ cm.extendHold = false;
+ }, 250);
+ },
+ doDragInner: async function (e, dragType) {
+ var cm = clip.movement;
+ if (dragType == "right") {
+ var maxWidth = getMaxClipWidth();
+ var xMovement = e.clientX - cm.startXabs;
+ var newWidth = xMovement + cm.clipWidth;
+ newWidth = twine.timeline.roundToGrid(newWidth);
+ if (newWidth > maxWidth) newWidth = maxWidth;
+ if (newWidth < minWidth) newWidth = minWidth;
+ if (newWidth != parseFloat(clip.element.css("width"))) {
+ cm.moved = true;
+ cm.doClipRedraw = true;
+ }
+ var playLength = newWidth / twine.timeline.pixelsPerBeat;
+ if (!cm.masterClip.channel.hasOverlap(cm.masterClip, null, playLength)) {
+ data.playLength = playLength;
+ clip.element.css("width", newWidth + "px");
+ }
+ } else if (dragType == "left") {
+ var maxWidth = getMaxClipWidth();
+ var xMovement = e.clientX - cm.startXabs;
+ var left = cm.clipLeft + xMovement;
+ //var left = (e.clientX - cm.startX - clip.channel.offset.left);
+ left = twine.timeline.roundToGrid(left);
+ if (left < 0) left = 0;
+ var newWidth = (cm.clipWidth - left) + cm.clipLeft;
+ var cWidth, cLeft;
+ if (newWidth < minWidth) {
+ cWidth = minWidth, cm.clipLeft + minWidth; //(minWidth - left) + clipLeft;
+ } else if (newWidth > maxWidth) {
+ cWidth = maxWidth, cLeft = cm.lastLeft;
+ } else {
+ lastLeft = left;
+ cWidth = newWidth, cLeft = left;
+ }
+ if (cWidth != parseFloat(clip.element.css("width"))) {
+ cm.moved = true;
+ cm.doClipRedraw = true;
+ }
+ var position = Math.min(0, (left / twine.timeline.pixelsPerBeat) + twine.timeline.beatRegion[0]);
+ var playLength = newWidth / twine.timeline.pixelsPerBeat;
+ if (!cm.masterClip.channel.hasOverlap(cm.masterClip, position, playLength)) {
+ data.position = position;
+ data.playLength = playLength
+ clip.element.css({width: cWidth + "px", left: cLeft + "px"});
+ }
+ } else {
+ if (cm.extendHold) return;
+ if (e.ctrlKey && !cm.isCopying) {
+ cm.isCopying = true;
+ cm.masterClip = await clip.clone();
+ }
+ var xMovement = e.clientX - cm.startXabs;
+ var left = xMovement + cm.clipLeft;
+ left = twine.timeline.roundToGrid(left);
+
+ var yMovement = e.clientY - cm.lastYabs;
+ var top = (e.clientY - cm.lastYabs) + cm.clipTop;
+ //console.log(top);
+ var ttop = (e.clientY - cm.startY) + cm.clipTop;
+ var tshift = ttop / cm.channel.height; //cm.masterClip.channel.height;
+ tshift = (tshift > 0) ? Math.floor(tshift) : Math.ceil(tshift);
+ console.log(tshift);
+
+ var channelShift = top / cm.masterClip.channel.height;
+ channelShift = (channelShift > 0) ? Math.floor(channelShift) : Math.ceil(channelShift);
+
+ if (channelShift != 0 || left != parseFloat(clip.element.css("left"))) {
+ cm.moved = true;
+ }
+ if (channelShift != 0) {
+ cm.lastYabs = e.clientY;
+ var newChannel = cm.masterClip.channel.index + channelShift;
+ if (newChannel < twine.timeline.channels.length && newChannel >= 0) {
+ if (!twine.timeline.channels[newChannel].hasOverlap(cm.masterClip)) {
+ cm.masterClip.channel.removeClip(cm.masterClip);
+ twine.timeline.channels[newChannel].addClip(cm.masterClip);
+ }
+ }
+ }
+
+ var doRedraw = false;
+ var extension = cm.masterClip.data.playLength + 8;
+ var newBeats;
+ var d = twine.timeline.data;
+ if (left < 0) {
+ cm.setExtendHold();
+ extension = -extension;
+ left = 0;
+ newBeats = twine.timeline.beatRegion[1];
+ doRedraw = true;
+ }
+
+ var position = (left / twine.timeline.pixelsPerBeat) + twine.timeline.beatRegion[0];
+
+ if (!cm.masterClip.channel.hasOverlap(cm.masterClip, position)) {
+ data.position = position;
+ cm.masterClip.element.css("left", left + "px");
+ }
+
+ if (left + cm.clipWidth > cm.masterClip.channel.width) {
+ cm.setExtendHold();
+ doRedraw = true;
+ newBeats = twine.timeline.beatRegion[1] + extension;
+ twine.timeline.extend(newBeats, true);
+ }
+
+ if (doRedraw) {
+ var extensionRatio = (extension / newBeats);
+ var newStart = d.regionStart + ((d.regionEnd - d.regionStart) * (extensionRatio));
+ var newEnd = d.regionEnd + extensionRatio;
+ newStart = Math.max(0, newStart);
+ newEnd = Math.max(0, newEnd);
+ twine.timeline.setRegion(newStart, newEnd, true);
+ }
+ } // move type
+ } // end doDragInner
+ };
+
+ elMove.mousedown(function(e){
+ clip.movement.mouseDown(e, "mid");
+ });
+ elResizeRight.mousedown(function(e){
+ clip.movement.mouseDown(e, "right");
+ });
+ elResizeLeft.mousedown(function(e){
+ clip.movement.mouseDown(e, "left");
+ });
+
+}; \ No newline at end of file