diff options
author | Richard <q@1bpm.net> | 2025-04-13 18:48:02 +0100 |
---|---|---|
committer | Richard <q@1bpm.net> | 2025-04-13 18:48:02 +0100 |
commit | 9fbf91db06a6d4f4b5cd8bb45389a731bb86bf22 (patch) | |
tree | 291bd79ce340e67affa755a8a6b4f6a83cce93ea /site/app/twine/timeline.js | |
download | apps.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/timeline.js')
-rw-r--r-- | site/app/twine/timeline.js | 736 |
1 files changed, 736 insertions, 0 deletions
diff --git a/site/app/twine/timeline.js b/site/app/twine/timeline.js new file mode 100644 index 0000000..9e33a4c --- /dev/null +++ b/site/app/twine/timeline.js @@ -0,0 +1,736 @@ +var Locators = function(timeline, elTimebar, elChannelOverlay) {
+ var locators = this;
+ var items = {
+ start: {
+ elMain: $("<div />").addClass("twine_timeline_timebar_locatorhead").appendTo(elTimebar),
+ elLine: $("<div />").addClass("twine_timeline_timebar_locatorline").appendTo(elChannelOverlay),
+ setLeft: function(px){
+ items.start.elMain.show().css("left", px - (items.start.elMain.width() / 2));
+ items.start.elLine.show().css("left", px);
+ },
+ hide: function() {
+ items.start.elMain.hide();
+ items.start.elLine.hide();
+ }
+ },
+ regionStart: {
+ elMain: $("<div />").addClass("twine_timeline_timebar_regionstart").appendTo(elTimebar),
+ elLine: $("<div />").addClass("twine_timeline_timebar_regionstart").appendTo(elChannelOverlay),
+ setLeft: function(px){
+ items.regionStart.elMain.show().css("left", px - (items.regionStart.elMain.width() / 2));
+ items.regionStart.elLine.show().css("left", px);
+ },
+ hide: function() {
+ items.regionStart.elMain.hide();
+ items.regionStart.elLine.hide();
+ }
+ },
+ regionEnd: {
+ elMain: $("<div />").addClass("twine_timeline_timebar_regionstart").appendTo(elTimebar),
+ elLine: $("<div />").addClass("twine_timeline_timebar_regionstart").appendTo(elChannelOverlay),
+ setLeft: function(px){
+ items.regionEnd.elMain.show().css("left", px - (items.regionEnd.elMain.width() / 2));
+ items.regionEnd.elLine.show().css("left", px);
+ },
+ hide: function() {
+ items.regionEnd.elMain.hide();
+ items.regionEnd.elLine.hide();
+ }
+ }
+ };
+
+
+ items.regionStart.elMain.on("mousedown", function(es) {
+ es.preventDefault();
+ es.stopPropagation();
+ var offset = elTimebar.offset().left;
+ var max = elTimebar.width();
+ var startLeft = parseFloat(items.regionStart.elLine.css("left"));
+ function mouseup() {
+ $("body").off("mousemove", mousemove).off("mouseup", mouseup);
+ }
+
+ function mousemove(e) {
+ var newPos = (e.clientX - es.clientX) + startLeft;
+ newPos = timeline.roundToGrid(newPos);
+ if (newPos < 0) {
+ newPos = 0;
+ } else if (newPos > max) {
+ newPos = max;
+ }
+
+ var offset = elTimebar.offset().left;
+ var beats = timeline.beatRegion[1] - timeline.beatRegion[0];
+ var beat = timeline.beatRegion[0] + (Math.round((newPos / (max - offset)) * beats * 100) / 100);
+ if (beat > timeline.data.beatEnd - 1) {
+ return;
+ }
+ items.regionStart.setLeft(newPos);
+ timeline.data.beatStart = beat;
+ }
+ $("body").on("mousemove", mousemove).on("mouseup", mouseup);
+ });
+
+ items.regionEnd.elMain.on("mousedown", function(es) {
+ es.preventDefault();
+ es.stopPropagation();
+ var offset = elTimebar.offset().left;
+ var max = elTimebar.width();
+ var startLeft = parseFloat(items.regionEnd.elLine.css("left"));
+ function mouseup() {
+ $("body").off("mousemove", mousemove).off("mouseup", mouseup);
+ }
+
+ function mousemove(e) {
+ var newPos = (e.clientX - es.clientX) + startLeft;
+ newPos = timeline.roundToGrid(newPos);
+ if (newPos < 0) {
+ newPos = 0;
+ } else if (newPos > max) {
+ newPos = max;
+ }
+
+ var offset = elTimebar.offset().left;
+ var beats = timeline.beatRegion[1] - timeline.beatRegion[0];
+ var beat = timeline.beatRegion[0] + (Math.round((newPos / (max - offset)) * beats * 100) / 100);
+ if (beat < timeline.data.beatStart + 1) {
+ return;
+ }
+ items.regionEnd.setLeft(newPos);
+ timeline.data.beatEnd = beat;
+ }
+ $("body").on("mousemove", mousemove).on("mouseup", mouseup);
+ });
+
+
+ items.start.elMain.on("mousedown", function(es) {
+ es.preventDefault();
+ es.stopPropagation();
+ var offset = elTimebar.offset().left;
+ var max = elTimebar.width();
+ var startLeft = parseFloat(items.start.elLine.css("left"));
+ function mouseup() {
+ $("body").off("mousemove", mousemove).off("mouseup", mouseup);
+ }
+
+ function mousemove(e) {
+ var newPos = (e.clientX - es.clientX) + startLeft;
+ newPos = timeline.roundToGrid(newPos);
+ if (newPos < 0) {
+ newPos = 0;
+ } else if (newPos > max) {
+ newPos = max;
+ }
+ items.start.setLeft(newPos);
+ var offset = elTimebar.offset().left;
+ var beats = timeline.beatRegion[1] - timeline.beatRegion[0];
+ var beat = timeline.beatRegion[0] + (Math.round((newPos / (max - offset)) * beats * 100) / 100);
+ timeline.startLocation = beat;
+ }
+ $("body").on("mousemove", mousemove).on("mouseup", mouseup);
+ });
+
+ this.redrawStart = function() {
+ if (timeline.startLocation >= timeline.beatRegion[1] || timeline.startLocation < timeline.beatRegion[0]) {
+ items.start.hide();
+ return;
+ }
+ var ratio = (timeline.startLocation - timeline.beatRegion[0]) / (timeline.beatRegion[1] - timeline.beatRegion[0]);
+ var eltWidth = elTimebar.width() - elTimebar.offset().left;
+ var px = ratio * eltWidth;
+ items.start.setLeft(px);
+ }
+}; // end locators
+
+
+
+var Timeline = function(twine, data) {
+ var timeline = this;
+ var elDragSelection;
+ timeline.selectedClips = [];
+ timeline.selectedChannel = null;
+ timeline.startLocation = 0;
+
+ Object.defineProperty(timeline, "selectedClip", {
+ get: function() {
+ return timeline.selectedClips[timeline.selectedClips.length - 1];
+ },
+ set: function(c) {
+ timeline.selectedClips = [c];
+ }
+ });
+
+ var container = $("#twine_timeline");
+ var elTimebarContainer = $("<div />").attr("id", "twine_timeline_timebar").appendTo(container);
+ var elTimebar = $("<div />").attr("id", "twine_timeline_timebar_inner").appendTo(elTimebarContainer);
+ this.element = $("<div />").attr("id", "twine_timeline_inner").appendTo(container);
+ $("<div />").attr("id", "twine_timelineoverlay").appendTo(timeline.element);
+ var elChannelOverlay = $("<div />").attr("id", "twine_timeline_channeloverlay").appendTo(container);
+ var elScrollOuter = $("<div />").attr("id", "twine_timeline_scroll_outer").appendTo(container);
+ var elScrollInner = $("<div />").attr("id", "twine_timeline_scroll_inner").appendTo(elScrollOuter);
+ var elIcons = $("<div />").attr("id", "twine_timeline_scroll_filler").appendTo(container);
+ var elPlayhead = $("<div />").attr("id", "twine_timeline_playposition").appendTo(elChannelOverlay);
+
+ var locators = new Locators(timeline, elTimebar, elChannelOverlay);
+
+ elIcons.append(twirl.createIcon({
+ label: "Zoom in",
+ size: 20,
+ icon: "zoomIn",
+ click: function() {
+ timeline.zoomIn();
+ }
+ }).el);
+
+ elIcons.append(twirl.createIcon({
+ label: "Zoom out",
+ size: 20,
+ icon: "zoomOut",
+ click: function() {
+ timeline.zoomOut();
+ }
+ }).el);
+
+ elIcons.append(twirl.createIcon({
+ label: "Show all",
+ size: 20,
+ icon: "showAll",
+ click: function() {
+ timeline.showAll();
+ }
+ }).el);
+
+ var channelIndex = 0;
+ this.channels = [];
+ this.gridPixels = 40;
+ this.pixelsPerBeat = 10;
+ this.beatRegion = [];
+ this.playbackBeatStart = 0;
+
+ if (data) {
+ this.data = data;
+ } else {
+ this.data = {
+ name: "New arrangement",
+ snapToGrid: 4,
+ gridVisible: true,
+ beatStart: 0,
+ beatEnd: 16,
+ bpm: 120,
+ timeSigMarker: 4,
+ regionStart: 0,
+ regionEnd: 1
+ };
+ }
+
+ elTimebar.click(function(e) {
+ var offset = elTimebar.offset().left;
+ var beats = timeline.beatRegion[1] - timeline.beatRegion[0];
+ var px = timeline.roundToGrid(e.clientX - offset);
+ var width = elTimebar.width() - offset; // ????
+ var beat = timeline.beatRegion[0] + (Math.round((px / width) * beats * 100) / 100);
+ timeline.startLocation = beat;
+ locators.redrawStart();
+ twine.stopAndPlay(beat);
+ });
+
+
+ this.getTotalBeatDuration = function() {
+ return timeline.data.beatEnd * (60 / timeline.data.bpm);
+ };
+
+ this.changeBeatEnd = function(newBeatEnd, noredraw) {
+ var original = timeline.data.beatEnd;
+ timeline.data.beatEnd = newBeatEnd;
+ for (let c of timeline.channels) {
+ c.changeBeatEnd(original, newBeatEnd, noredraw);
+ }
+ };
+
+ this.extend = function(newBeatEnd, noredraw) {
+ if (newBeatEnd > timeline.data.beatEnd) {
+ timeline.changeBeatEnd(newBeatEnd, noredraw);
+ }
+ };
+
+ this.reduce = function() {
+ var end = 0;
+ for (let ch of timeline.channels) {
+ for (let i in ch.clips) {
+ if (ch.clips[i]) {
+ var e = ch.clips[i].data.position + ch.clips[i].data.playLength;
+ if (e > end) {
+ end = e;
+ }
+ }
+ }
+ }
+ end += 8;
+ timeline.data.beatEnd = end;
+ };
+
+ this.exportData = async function() {
+ var saveData = {
+ channels: [],
+ data: timeline.data,
+ masterAmp: (await app.getControlChannel(twine.mixer.masterAmpChannel)),
+ ftables: {}
+ };
+ for (let c of timeline.channels) {
+ saveData.channels.push(await c.exportData());
+ }
+ for (let ch of saveData.channels) {
+ for (let cl of ch.clips) {
+ var fnL = cl.table[0];
+ var fnR = cl.table[1];
+ if (!saveData.ftables[fnL] && fnL > 0) {
+ saveData.ftables[fnL] = {
+ length: await app.getCsound().tableLength(fnL),
+ data: (await app.getCsound().tableCopyOut(fnL)).values().toArray()
+ };
+ }
+ if (!saveData.ftables[fnR] && fnR > 0) {
+ saveData.ftables[fnR] = {
+ length: await app.getCsound().tableLength(fnR),
+ data: (await app.getCsound().tableCopyOut(fnR)).values().toArray()
+ };
+ }
+ }
+ }
+ return saveData;
+ };
+
+ this.createFtable = async function(length) {
+ return new Promise(function(resolve, reject) {
+ var cbid = app.createCallback(function(ndata){
+ resolve(ndata.table);
+ });
+ app.insertScore("twine_createtable", [0, 1, cbid, length]);
+ });
+ };
+
+ this.copyNewTableIn = async function(data) {
+ var ftable = await timeline.createFtable(data.length);
+ await app.getCsound().tableCopyIn(ftable, data);
+ return ftable;
+ };
+
+ this.importData = async function(loadData) {
+ timeline.data = loadData.data;
+ await app.setControlChannel(twine.mixer.masterAmpChannel, loadData.masterAmp);
+ var ftMap = {};
+ console.log("load ftables", loadData.ftables);
+ for (let i in loadData.ftables) {
+ var fn = await timeline.copyNewTableIn(loadData.ftables[i].data);
+ console.log("copy into", fn, loadData.ftables[i]);
+ window.fn = loadData.ftables[i];
+ ftMap[i] = fn;
+ }
+
+ while (timeline.channels.length > 0) {
+ timeline.channels[0].remove();
+ }
+
+ timeline.channels = [];
+ var channelIndex = 0;
+ for (let c of loadData.channels) {
+ var channel = new Channel(timeline, channelIndex ++);
+ timeline.channels.push(channel);
+ await channel.importData(c, ftMap);
+ }
+ timeline.redraw();
+ };
+
+ var playheadInterval;
+ this.setPlaying = function(state) {
+ twine.ui.head.play.setValue(state);
+ if (playheadInterval) {
+ clearInterval(playheadInterval);
+ }
+ if (state) {
+ playheadInterval = setInterval(async function(){
+ var val = await app.getControlChannel("twine_playpos");
+ var beat = (val * (timeline.data.bpm / 60)) + timeline.playbackBeatStart;
+ if (beat > timeline.beatRegion[1]) {
+ clearInterval(playheadInterval);
+ elPlayhead.hide();
+ }
+ var pos = beat * timeline.pixelsPerBeat;
+ elPlayhead.css("left", pos + "px");
+ }, 50);
+ elPlayhead.show();
+ } else {
+ elPlayhead.hide();
+ }
+ };
+
+ this.zoomIn = function() {
+ timeline.setRegion(
+ timeline.data.regionStart * 1.1,
+ timeline.data.regionEnd * 0.9
+ );
+ };
+
+ this.zoomOut = function() {
+ timeline.setRegion(
+ timeline.data.regionStart * 0.9,
+ timeline.data.regionEnd * 1.1
+ );
+ timeline.redraw();
+ };
+
+ this.showAll = function() {
+ timeline.setRegion(0, 1);
+ };
+
+ this.compileAutomationData = function(onready) {
+ var changed = false;
+ for (let c of timeline.channels) {
+ if (c.automationChanged()) {
+ changed = true;
+ break;
+ }
+ }
+ if (!changed) {
+ return onready(1);
+ }
+
+ var start = timeline.data.beatStart / (timeline.data.beatEnd - timeline.data.beatStart);
+ var instr = "instr twine_automaterun\n";
+ for (let c of timeline.channels) {
+ for (let a of c.getAutomationData(start, 1)) {
+ instr += a + "\n"
+ }
+ }
+ instr += "a_ init 0\nout a_\nendin\n";
+ console.log(instr);
+ app.compileOrc(instr).then(function(status){
+ if (status < 0) {
+ self.errorHandler("Cannot parse automation data");
+ } else {
+ onready(1);
+ }
+ });
+ };
+
+ function determineChannelForAdd() {
+ var channel;
+ if (!timeline.selectedChannel) {
+ if (timeline.channels.length == 0) {
+ channel = timeline.addChannel();
+ } else {
+ channel = timeline.channels[0];
+ }
+ } else {
+ channel = timeline.selectedChannel;
+ }
+ return channel;
+ }
+
+ this.addScriptClip = function() {
+ var channel = determineChannelForAdd();
+ var clip = new Clip(twine);
+ clip.data.position = timeline.playbackBeatStart; //timeline.beatRegion[0];
+ channel.addClip(clip);
+ clip.initScript();
+ clip.redraw();
+ };
+
+ this.addBlankClip = function() {
+ var el = $("<div />");
+ el.append($("<h4 />").text("New blank clip"));
+ let pStereo = new twirl.transform.Parameter({
+ definition: {
+ name: "Stereo",
+ description: "Whether the clip should be stereo or mono",
+ fireChanges: false, automatable: false,
+ min: 0, max: 1, step: 1, dfault: 1
+ },
+ host: twine
+ });
+ let pDuration = new twirl.transform.Parameter({
+ definition: {
+ name: "Duration",
+ description: "Duration of the clip in seconds",
+ fireChanges: false, automatable: false,
+ min: 0.1, max: 30, step: 0.01, dfault: 10
+ },
+ host: twine
+ });
+ var tb = $("<tbody />").appendTo($("<table />").appendTo(el));
+ tb.append(pStereo.getElementRow(true));
+ tb.append(pDuration.getElementRow(true));
+ twirl.prompt.show(el, function(){
+ var channel = determineChannelForAdd();
+ var clip = new Clip(twine);
+ clip.data.position = timeline.playbackBeatStart; //timeline.beatRegion[0];
+ channel.addClip(clip);
+ clip.createSilence(pStereo.getValue(), pDuration.getValue(), name);
+ });
+ };
+
+ this.addChannel = function() {
+ var channel = new Channel(timeline, channelIndex ++);
+ timeline.channels.push(channel);
+ timeline.drawGrid();
+ return channel;
+ };
+
+
+ this.contractChannels = function() {
+ for (let c of timeline.channels) {
+ c.contract();
+ }
+ };
+
+ this.roundToGrid = function(px) {
+ if (timeline.data.snapToGrid) {
+ return twine.roundToNearest(px, twine.timeline.gridPixels, twine.timeline.gridOffset);
+ } else {
+ return px;
+ }
+ };
+
+
+
+ this.roundToChannel = function(px) {
+ var total = 0;
+ var rounded = px;
+ for (let i in timeline.channels) {
+ var c = timeline.channels[i];
+ if (i == 0) rounded = c.height;
+ total += c.height + 1;
+ if (px > total) {
+ rounded = total;
+ }
+ }
+ return rounded;
+ };
+
+ this.determineChannelFromTop = function(posTop) {
+ var top = 0;
+ for (var c of timeline.channels) {
+ top += c.height;
+ if (posTop < top) {
+ return c;
+ }
+ }
+ return c;
+ };
+
+ function calcViewport() {
+ var cc = $(".twine_channelclips");
+ var width = $("#twine_timeline_channeloverlay").width(); //cc.width();
+ var d = timeline.data;
+ timeline.beatRegion = [
+ d.regionStart * d.beatEnd,
+ d.regionEnd * d.beatEnd
+ ];
+ var gridScale = 1;
+ var beats = timeline.beatRegion[1] - timeline.beatRegion[0];
+ timeline.pixelsPerBeat = width / beats;
+ timeline.gridPixels = timeline.pixelsPerBeat / parseInt(d.snapToGrid);
+ var beat = timeline.beatRegion[0];
+ timeline.gridOffset = (parseInt(beat) - beat) * timeline.pixelsPerBeat;
+ return {
+ minLeft: 0, //cc.offset().left,
+ width: width,
+ beat: beat
+ }
+ }
+
+
+ this.drawGrid = function() {
+ if (timeline.channels.length == 0) return;
+ $(".twine_timelinemarker").remove();
+ $(".twine_timelinetext").remove();
+ var vp = calcViewport();
+ if (!timeline.data.gridVisible) return;
+
+ var width;
+ var fontWeight;
+ beat = Math.floor(vp.beat);
+ for (var x = vp.minLeft + timeline.gridOffset; x < vp.width; x += timeline.pixelsPerBeat) {
+ if ((beat - 1) % timeline.data.timeSigMarker == 0) {
+ width = 2;
+ fontWeight = "bold";
+ } else {
+ width = 1;
+ fontWeight = "normal";
+ }
+ if (x >= 0) {
+ $("<div />").attr("class", "twine_vline twine_timelinemarker").appendTo(elChannelOverlay).css("width", width).css("left", x).css("top", "0px");
+ $("<div />").attr("class", "twine_timelinetext").appendTo(elChannelOverlay).css("font-weight", fontWeight).css("left", x + 2).text(beat);
+ }
+ beat ++;
+ }
+ locators.redrawStart();
+ }
+
+ var drawing;
+ this.redraw = function(noClipWaveRedraw) {
+ if (drawing) return;
+ drawing = true;
+ timeline.drawGrid();
+ for (let c of timeline.channels) {
+ c.redraw(noClipWaveRedraw);
+ }
+ drawing = false;
+ };
+
+ this.setRegion = function(start, end, noClipWaveRedraw) {
+ timeline.reduce();
+ if (end <= start) return;
+ if (end > 1) end = 1;
+ if (start < 0) start = 0;
+ timeline.data.regionStart = start;
+ timeline.data.regionEnd = end;
+ timeline.redraw(noClipWaveRedraw);
+ var elTbcw = elScrollOuter.width();
+ elScrollInner.css({left: (timeline.data.regionStart * elTbcw) + "px", right: ((1 - timeline.data.regionEnd) * elTbcw) + "px"});
+ if (self.onRegionChange) {
+ self.onRegionChange([timeline.data.regionStart, timeline.data.regionEnd]);
+ }
+ };
+
+ this.onRegionChange = function(region) {
+
+ };
+
+
+ function setScrollBarPosition(displayLeft, displayRight, setRegion) {
+ if (displayLeft >= 0 && displayRight >= 0) {
+ elScrollInner.css({left: displayLeft, right: displayRight});
+ var w = elScrollOuter.width();
+ if (setRegion) {
+ timeline.data.regionStart = displayLeft / w;
+ timeline.data.regionEnd = 1 - (displayRight / w);
+ if (self.onRegionChange) {
+ self.onRegionChange([timeline.data.regionStart, timeline.data.regionEnd]);
+ }
+ }
+ }
+ }
+
+ elScrollOuter.mousedown(function() {
+ var increment = 20;
+ var apos = event.pageX - elScrollOuter.offset().left;
+ var left = parseInt(elScrollInner.css("left"));
+ var right = parseInt(elScrollInner.css("right"));
+ var tbWidth = parseInt(elScrollInner.css("width"));
+ if (apos < left) {
+ left -= increment;
+ right += increment;
+ } else if (apos > left + tbWidth) {
+ left += increment;
+ right -= increment;
+ } else {
+ return;
+ }
+ setScrollBarPosition(left, right, true);
+ timeline.redraw();
+ });
+
+ elScrollInner.mousedown(function(e){
+ var pageX = e.pageX;
+ var offset = elScrollOuter.offset();
+ var cWidth = elScrollOuter.width();
+ var tbWidth = elScrollInner.width();
+ var sLeft = pageX - offset.left - parseInt(elScrollInner.css("left"));
+
+ function handleDrag(e) {
+ var left = ((e.pageX - pageX) + (pageX - offset.left));
+ left = left - sLeft;
+ var end = left + tbWidth;
+ var right = cWidth - end;
+ setScrollBarPosition(left, cWidth - end, true);
+ timeline.redraw(true);
+
+ }
+ function handleMouseUp(e) {
+ $("body").off("mousemove", handleDrag).off("mouseup", handleMouseUp);
+ function ensureDraw() {
+ if (drawing) return setTimeout(ensureDraw, 20);
+ timeline.redraw();
+ }
+ ensureDraw();
+ }
+ $("body").on("mouseup", handleMouseUp).on("mousemove", handleDrag);
+ });
+
+ this.deselectClips = function() {
+ timeline.selectedClips = [];
+ $(".twine_clip").css("outline", "none");
+ };
+
+ this.dragSelection = function(e){
+ var elChannelOverlay = $("#twine_timeline_channeloverlay");
+ var pageX = e.pageX;
+ var pageY = e.pageY;
+ var offset = elChannelOverlay.offset();
+ var width = elChannelOverlay.width();
+ var height = elChannelOverlay.height();
+ var left = (pageX - offset.left);
+ var top = (pageY - offset.top);
+ if (!elDragSelection) {
+ elDragSelection = $("<div />").addClass("drag_selection").appendTo(elChannelOverlay);
+ }
+ elDragSelection.hide().css({left: left + "px", top: top + "px"});
+
+ function handleDrag(e) {
+ elDragSelection.show();
+ var xMovement = e.pageX - pageX;
+ var yMovement = e.pageY - pageY;
+ if (xMovement < 0) {
+ elDragSelection.css({
+ left: (left + xMovement) + "px"
+ });
+ }
+ elDragSelection.css({
+ width: Math.abs(xMovement) + "px"
+ });
+
+ if (yMovement < 0) {
+ elDragSelection.css({
+ top: (top + yMovement) + "px"
+ });
+ }
+ elDragSelection.css({
+ height: Math.abs(yMovement) + "px"
+ });
+
+ }
+
+ function handleMouseUp(e) {
+ elDragSelection.hide();
+ var left = parseFloat(elDragSelection.css("left"));
+ var width = parseFloat(elDragSelection.css("width"));
+ var top = parseFloat(elDragSelection.css("top"));
+ var height = parseFloat(elDragSelection.css("height")) - 10;
+ var channelStart = timeline.determineChannelFromTop(top).index;
+ var channelEnd = timeline.determineChannelFromTop(top + height).index;
+
+ var beatStart = ((left / timeline.pixelsPerBeat) + timeline.beatRegion[0]) - 0.01;
+ var beatEnd = (((left + width) / timeline.pixelsPerBeat) + timeline.beatRegion[0]) + 0.01;
+ timeline.deselectClips();
+ for (let ch of timeline.channels) {
+ if (ch.index >= channelStart && ch.index <= channelEnd) {
+ for (let ci in ch.clips) {
+ var cl = ch.clips[ci];
+ if (cl) {
+ if (cl.data.position >= beatStart && cl.data.position + cl.data.playLength <= beatEnd) {
+ cl.markSelected();
+ timeline.selectedClips.push(cl);
+
+ }
+ }
+ }
+ }
+ }
+ $("body").off("mousemove", handleDrag).off("mouseup", handleMouseUp);
+ }
+ $("body").on("mouseup", handleMouseUp).on("mousemove", handleDrag);
+ };
+
+ timeline.drawGrid();
+};
|