var Waveform = function(options) { var self = this; var elTarget; if (typeof(options.target) == "string") { elTarget = $("#" + options.target); } else { elTarget = options.target; } var elContainerOuter = $("
").css({position: "absolute", width: "100%", height: "100%"}).appendTo(elTarget); var elContainer = $("").css({cursor: "text", position: "absolute", width: "100%", bottom: "0px", top: "0px", left: "0px"}).appendTo(elContainerOuter); var elTip = $("").css({position: "fixed", "font-size": "var(--fontSizeLarge)", color: "var(--fgColor1)", "text-shadow": "0px 0px 5px var(--bgColor1)", "z-index": 12}).appendTo(elContainer); var elCanvases = []; var elTimeBar; var elPlayhead; var elCrossfades = []; var elMarkersRunner; var crossFadeRatios = []; var selected = [0, 1, -1]; var onSelects = []; var channels; var wavedata = null; var regionStart = 0; var regionEnd = 1; var duration = 1; var stereoSelectRatio = 0.2 var dragData = {}; this.markers = []; var selectionMarkers = []; var hasContent = false; this.onRegionChange = null; this.getRegion = function() { return [regionStart, regionEnd]; }; function absPosToDisplayPos(x) { var pos = (x - regionStart) / (regionEnd - regionStart); return (pos >= 0 && pos <= 1) ? pos : null; } function displayPosToAbsPos(x) { return ((regionEnd - regionStart) * x) + regionStart; } function getDisplaySelected() { var hasSelection = (selected[0] != selected[1]); var start = absPosToDisplayPos(selected[0]); //if (start == null) start = 0; var end = absPosToDisplayPos(selected[1]); //if (end == null) end = 1; if (start && !end) end = 1; if (!start && end) start = 0; if (!start && !end) { if (hasSelection) { start = 0; end = 1; } else { start = end = 0; } } return [ start, end, selected[2] ]; } if (!options) { options = {}; } if (options.hasOwnProperty("onSelect")) { onSelects.push(options.onSelect); } if (options.hasOwnProperty("duration")) { duration = options.duration; } if (!options.hasOwnProperty("latencyCorrection")) { options.latencyCorrection = 0; } if (!options.hasOwnProperty("showcrossfades")) { options.showcrossfades = false; } if (!options.hasOwnProperty("showGrid")) { options.showGrid = true; } if (!options.hasOwnProperty("drawStyle")) { options.drawStyle = "bar"; //"linebar"; } if (!options.hasOwnProperty("allowSelect")) { options.allowSelect = true; } if (options.allowSelect) { elContainer.mousedown(mouseDownHandler).dblclick(mouseDoubleClickHandler).on("mousemove", function(e) { if (channels != 2) return; var ratio = (e.clientY - elContainer.offset().top) / parseFloat(elContainer.height()); var shown = false; var text; if (ratio > 1 - stereoSelectRatio) { shown = true; text = "R"; } else if (ratio < stereoSelectRatio) { shown = true; text = "L"; } if (shown) { elTip.show().css({left: (e.clientX + 10) + "px", top: (e.clientY - 5) + "px"}).text(text); } else { elTip.hide(); } }).on("mouseleave", function(){ elTip.hide(); }); } var elCover = $("").css({position: "absolute", width: "100%", height: "100%", "background-color": "var(--waveformCoverColor)", opacity: "var(--waveformCoverOpacity)", display: "none", "z-index": 10}).appendTo(elContainerOuter); var Marker = function(data, identifier) { var mself = this; var elMarkers; var headerWidth = 14; var headerHeight = 15; var elLine; var elHeader; var drag var position; var onMarkerChange; Object.defineProperty(this, "position", { get: function() { return position; }, set: function(x) { setPosition(x); } }); Object.defineProperty(this, "identifier", { get: function() { return identifier; }, set: function(x) { identifier = x; } }); if (typeof(data) == "number") { position = data; } else if (data.preset) { if (data.preset == "selectionstart") { identifier = ""; selectionMarkers[0] = mself; onMarkerChange = function() { if (position > selectionMarkers[1].position) { self.alterSelection(selectionMarkers[1].position, position, null, true); } else { self.alterSelection(position, null, null, true); } }; onSelects.push(function(start, regionStart, end) { setPosition(start); }); } else if (data.preset == "selectionend") { identifier = ""; selectionMarkers[1] = mself; onMarkerChange = function() { if (position < selectionMarkers[0].position) { self.alterSelection(position, selectionMarkers[0].position, null, true); } else { self.alterSelection(null, position, null, true); } }; onSelects.push(function(start, regionStart, end) { setPosition(end); }); } } else { position = data.position; if (data.hasOwnProperty("identifier")) { identifier = data.identifier; } if (data.hasOwnProperty("onChange")) { onMarkerChange = data.onChange; } } function setPosition(displayPos) { if (!hasContent) return; if (displayPos != null) { position = displayPosToAbsPos(displayPos); } else { displayPos = absPosToDisplayPos(position); } if (!elLine) { elLine = $("").appendTo(elContainer).addClass("waveform_marker").css({position: "absolute", height: "100%", top: "0px", width: "2px", "background-color": "var(--waveformMarkerColor)", "z-index": 11 }); } if (!elHeader) { elHeader = $("").appendTo(elMarkersRunner).addClass("waveform_marker").css({position: "absolute", height: "100%", top: "0px", width: headerWidth + "px", "background-color": "var(--waveformMarkerColor)", "border-bottom-left-radius": headerWidth + "px", "border-bottom-right-radius": headerWidth + "px", "z-index": 8, "text-align": "center", "font-family": "sans-serif, Arial", "font-size": "8pt", "font-weight": "bold", cursor: "move", "user-select": "none" }).mousedown(handleMousedown); } if (displayPos == null) { elLine.hide(); elHeader.hide(); } else { var posx = elContainer.width() * displayPos; elLine.show().css("left", posx + "px"); posx = (posx - (headerWidth / 2)) + 1; elHeader.show().css("left", posx + "px").text(identifier); } } this.redraw = function() { if (!hasContent) return; if (position < regionStart || position > regionEnd) { if (elLine) elLine.hide(); if (elHeader) elHeader.hide(); } else { setPosition(); elLine.show(); elHeader.show(); } } function handleMousedown(e) { if (!hasContent) return; var pageX = e.pageX; var offset = elMarkersRunner.offset(); var width = elMarkersRunner.width(); function handleDrag(e) { var pos = ((e.pageX - pageX) + (pageX - offset.left)) / width; if (pos <= 1 && pos >= 0) { setPosition(pos); if (onMarkerChange) { onMarkerChange(pos); } } } function handleMouseUp(e) { $("body").off("mousemove", handleDrag).off("mouseup", handleMouseUp); } $("body").on("mouseup", handleMouseUp).on("mousemove", handleDrag); } setPosition(); }; // end marker if (options.hasOwnProperty("markers")) { elContainer.css("top", "15px"); elMarkersRunner = $("").appendTo(elContainerOuter).css({position: "absolute", width: "100%", height: "15px", top: "0px", left: "0px", "background-color": "var(--waveformMarkerRunnerColor)"}); if (typeof(options.markers) == "object") { var id = 1; for (let m of options.markers) { self.markers.push(new Marker(m, id++)); } } } if (options.timeBar) { elContainer.css({overflow: "hidden", bottom: "20px"}); var elTimeBarOuter = $("").appendTo(elContainerOuter).css({position: "absolute", width: "100%", height: "20px", bottom: "0px", left: "0px", "background-color": "var(--waveformTimeBarBgColor)"}); var elTimeBarIcons = $("").appendTo(elTimeBarOuter).css({position: "absolute", width: "80px", height: "100%", bottom: "0px", left: "0px"}); var elTimeBarContainer = $("").appendTo(elTimeBarOuter).css({position: "absolute", right: "0px", height: "100%", bottom: "0px", left: "80px", "background-color": "var(--waveformTimeBarBgColor)"}).click(handleTimeBarTrackClick); elTimeBar = $("").appendTo(elTimeBarContainer).css({position: "absolute", right: "0px", height: "16px", top: "2px", left: "0px", "background-color": "var(--waveformTimeBarFgColor)"}).mousedown(handleTimeBarMousedown); elTimeBarIcons.append(twirl.createIcon({ label: "Zoom selection", size: 20, icon: "zoomSelection", click: function() { self.zoomSelection() } }).el); elTimeBarIcons.append(twirl.createIcon({ label: "Zoom in", size: 20, icon: "zoomIn", click: function() { self.zoomIn() } }).el); elTimeBarIcons.append(twirl.createIcon({ label: "Zoom out", size: 20, icon: "zoomOut", click: function() { self.zoomOut() } }).el); elTimeBarIcons.append(twirl.createIcon({ label: "Show all", size: 20, icon: "showAll", click: function() { self.setRegion(0, 1); } }).el); function setTimeBarPosition(displayLeft, displayRight, setRegion) { if (displayLeft >= 0 && displayRight >= 0) { elTimeBar.css({left: displayLeft, right: displayRight}); var w = elTimeBarContainer.width(); if (setRegion) { regionStart = displayLeft / w; regionEnd = 1 - (displayRight / w); if (self.onRegionChange) { self.onRegionChange([regionStart, regionEnd]); } } } } function handleTimeBarTrackClick(event) { var increment = 20; var apos = event.pageX - elTimeBarContainer.offset().left; var left = parseInt(elTimeBar.css("left")); var right = parseInt(elTimeBar.css("right")); var tbWidth = parseInt(elTimeBar.css("width")); if (apos < left) { left -= increment; right += increment; } else if (apos > left + tbWidth) { left += increment; right -= increment; } else { return; } setTimeBarPosition(left, right, true); draw(); } function handleTimeBarMousedown(e) { if (!hasContent) return; var pageX = e.pageX; var offset = elTimeBarContainer.offset(); var cWidth = elTimeBarContainer.width(); var tbWidth = elTimeBar.width(); var sLeft = pageX - offset.left - parseInt(elTimeBar.css("left")); function handleDrag(e) { var left = ((e.pageX - pageX) + (pageX - offset.left)); left = left - sLeft; var end = left + tbWidth; var right = cWidth - end; setTimeBarPosition(left, cWidth - end, true); draw(4); //draw(15); } function handleMouseOut(e) { handleMouseUp(e); } function handleMouseUp(e) { $("body").off("mousemove", handleDrag).off("mouseup", handleMouseUp).off("mouseleave", handleMouseOut); function ensureDraw() { if (drawing) return setTimeout(ensureDraw, 20); draw(); } ensureDraw(); } $("body") .on("mouseup", handleMouseUp) .on("mousemove", handleDrag) .on("mouseleave", handleMouseOut); } } this.getDuration = function() { return duration; }; this.destroy = function() { elTarget.remove(); }; this.show = function() { elTarget.show(); }; this.hide = function() { elTarget.hide(); }; this.cover = function(state) { if (state) { elCover.show(); } else { elCover.hide();/* setTimeout(function() { elCover.hide(); }, options.latencyCorrection);*/ } }; this.setOptions = function(o) { Object.assign(options, o); }; function drawCrossFades() { if (!hasContent) return; if (!options.showcrossfades) return; if (elCrossfades.length == 0) { for (var x = 0; x < 2; x++) { elCrossfades.push($("").css({ position: "absolute", width: "1px", "z-index": 9, "line-width": "1px", "height": "var(--waveformCrossfadeWidth)", "background-color": "var(--waveformCrossfadeLineColor)" }).appendTo(elContainer)); } } var containerHeight = elContainer.height(); var containerWidth = elContainer.width(); var displaySelected = getDisplaySelected(); function drawCrossfade(index) { var thickness = 1; var ratio = crossFadeRatios[index]; if (ratio == 0) { elCrossfades[index].hide(); } if (index == 0) { var x1 = displaySelected[0] * containerWidth; var y1 = containerHeight; var x2 = x1 + (ratio * ((displaySelected[1] - displaySelected[0]) * containerWidth)); var y2 = 0; } else { var x1 = displaySelected[1] * containerWidth;; var y1 = containerHeight; var x2 = x1 - (ratio * ((displaySelected[1] - displaySelected[0]) * containerWidth)); var y2 = 0; } var length = Math.sqrt(((x2-x1) * (x2-x1)) + ((y2-y1) * (y2-y1))); var centrex = ((x1 + x2) / 2) - (length / 2); var centrey = ((y1 + y2) / 2) - (thickness / 2); var angle = Math.atan2((y1-y2),(x1-x2))*(180/Math.PI); elCrossfades[index].show().css({transform: "rotate(" + angle + "deg)", left: centrex, top: centrey, width: length + "px"}); } drawCrossfade(0); drawCrossfade(1); }; this.alterSelection = function(start, end, channel, noOnSelects) { if (!hasContent) return; if (start != null) { selected[0] = start; } if (end != null) { selected[1] = end; } var displaySelected = getDisplaySelected(); if (channel == null) { channel = selected[2]; } else { selected[2] = channel; } var elWidth = elContainer.width(); var left = displaySelected[0] * elWidth; var width = (displaySelected[1] * elWidth) - left; if (dragData.selection) { dragData.selection.css({ left: left, width: width }); } if (dragData.location) { dragData.location.css({ left: left }); } if (!noOnSelects && onSelects) { for (let onSelect of onSelects) { onSelect(start, regionStart, end, regionEnd, self); } } drawCrossFades(); } this.setSelection = function(start, end, channel) { if (!hasContent) return; if (!end) { end = start; } self.alterSelection(start, end, channel); }; function selectionMade() { if (!hasContent) return; var cWidth = elContainer.width(); var left = parseFloat(dragData.selection.css("left")); var width = parseFloat(dragData.selection.css("width")); var start = left / cWidth; var end = (left + width) / cWidth; selected = [ displayPosToAbsPos(start), displayPosToAbsPos(end), dragData.channel ]; if (onSelects) { for (let onSelect of onSelects) { onSelect(start, regionStart, end, regionEnd, self); } } drawCrossFades(); } function createSelectionArea(e, leftOverride, widthOverride) { var left = (leftOverride != null ) ? leftOverride : e.pageX - dragData.offset.left; var containerHeight = parseFloat(elContainer.height()); var yratio = (e.pageY - dragData.offset.top) / containerHeight; var heightmult = 1; var topaugment = 0; dragData.channel = -1; if (channels == 2) { if (yratio > 1 - stereoSelectRatio) { heightmult = 0.5; topaugment = containerHeight * 0.5; dragData.channel = 1; } else if (yratio < stereoSelectRatio) { heightmult = 0.5; dragData.channel = 0; } } var width = (widthOverride != null) ? widthOverride : "0px"; if (dragData && dragData.selection) { dragData.selection.remove(); } dragData.selection = $("") .addClass("waveformSelection") .css({ left: left, top: topaugment, position: "absolute", opacity: "var(--waveformSelectOpacity)", backgroundColor: "var(--waveformSelectColor)", height: (100 * heightmult) + "%", //containerHeight * heightmult, width: width, "pointer-events": "none", "z-index": 10 }).appendTo(elContainer); if (dragData && dragData.location) { dragData.location.remove(); } dragData.location = $("") .addClass("waveformLocation") .css({ left: left, top: 0, position: "absolute", opacity: "var(--waveformSelectOpacity)", backgroundColor: "var(--waveformLocationColor)", width: "0px", border: "1px solid black", height: "100%", //elContainer.height(), "pointer-events": "none", "z-index": 11 }).appendTo(elContainer); } function mouseDoubleClickHandler(e) { if (!hasContent) return; dragData.pageX = 0; dragData.offset.left = 0; //dragData.pageXend = elContainer.width(); // TODO redundant createSelectionArea(e, 0, elContainer.width()); selectionMade(); } function mouseDownHandler(e) { if (!hasContent) return; var tolerancePx = 4; if (!e.shiftKey) { dragData.pageX = e.pageX; dragData.pageY = e.pageY; //dragData.pageXend = 0; // TODO redundant dragData.elem = this; dragData.offset = $(this).offset(); createSelectionArea(e); dragData.shifted = false; } else { dragData.shifted = true; handleDrag(e); } var elWidth = elContainer.width(); /*if (options.showcrossfades && elCrossfades.length != 0) { elCrossfades[0].hide(); elCrossfades[1].hide(); }*/ // TODO redundant function handleDrag(e) { var origin = dragData.pageX - dragData.offset.left; var dragPos = (e.pageX - dragData.pageX); if (dragPos >= 0) { if (dragPos + origin > elWidth) { dragPos = elWidth; } if (dragPos <= tolerancePx) dragPos = 0; dragData.selection.css({ left: origin + "px", width: dragPos + "px" }); } else { var dpos = dragPos + origin; var left; var width; if (dpos <= 0) { left = 0; width = origin; } else { left = dpos; width = Math.abs(dragPos); } if (width <= tolerancePx) width = 0; dragData.selection.css({ left: left + "px", width: width + "px" }); } } function handleMouseUp(e) { $("body") .off("mousemove", handleDrag) .off("mouseup", handleMouseUp) .off("mouseleave", handleMouseOut); if (!hasContent) return; //dragData.pageXend = e.pageX; // TODO redundant selectionMade(); } function handleMouseOut(e) { if (e.clientX > $("body").width()) { var left = parseFloat(dragData.selection.css("left")); dragData.selection.css({width: (elContainer.width() - left) + "px"}); } handleMouseUp(e); } $("body") .on("mouseup", handleMouseUp) .on("mousemove", handleDrag) .on("mouseleave", handleMouseOut); } var lastDrawOneValueXpos; this.resetDrawOneValue = function() { lastDrawOneValueXpos = null; }; function drawOneValue(x, values) { var style = getComputedStyle(document.body); var bgColour = (options.hasOwnProperty("bgColor")) ? options.bgColour : style.getPropertyValue("--waveformBgColor"); var fgColour = (options.hasOwnProperty("fgColor")) ? options.fgColour : style.getPropertyValue("--waveformFgColor"); function drawCanvas(canvasIndex, val) { if (!val) return; var elCanvas = elCanvases[canvasIndex]; let width = elCanvas.width(); let height = elCanvas.height(); let ctx = elCanvas[0].getContext("2d"); var lineWidth = 1; if (lastDrawOneValueXpos) { lineWidth = x - lastDrawOneValueXpos; ctx.fillStyle = bgColour; ctx.fillRect(lastDrawOneValueXpos, 0, lineWidth, height); ctx.strokeStyle = fgColour; } lastDrawOneValueXpos = x; ctx.lineWidth = lineWidth; ctx.lineCap = "round"; ctx.fillStyle = fgColour; ctx.beginPath(); val = (val + 1) * 0.5; var posY0 = (val * height); var posY1 = (height - posY0); ctx.moveTo(x, posY0); ctx.lineTo(x, posY1); ctx.closePath(); ctx.stroke(); } drawCanvas(0, values[0]); if (values.length == 2) { drawCanvas(1, values[1]); } } this.movePlayhead = function(xratio, monitorValues) { if (!hasContent) return; setTimeout(function() { var displayPos = absPosToDisplayPos(xratio); if (!displayPos || displayPos < 0 || displayPos > 1) { if (elPlayhead) { elPlayhead.remove(); elPlayhead = null; } return; } var width = elContainer.width(); var left = Math.min(width * displayPos, width - 1); if (monitorValues) { drawOneValue(left, monitorValues); } if (!elPlayhead) { elPlayhead = $("") .addClass("waveformPlayhead") .css({ left: left, top: 0, position: "absolute", backgroundColor: "var(--waveformPlayheadColor)", width: "0px", border: "1px solid var(--waveformPlayheadColor)", height: "100%", "pointer-events": "none", "z-index": 13 }).appendTo(elContainer); } else { elPlayhead.css({left: left + "px"}); } }, options.latencyCorrection); }; var drawing = false; async function draw(efficiency) { if (!hasContent || !wavedata || wavedata.length == 0) return; if (drawing) return; drawing = true; if (!efficiency) efficiency = 1; if (elCanvases.length == 0) { for (var i in wavedata) { var height; var top; if (wavedata.length == 1) { top = "0px"; height = "100%"; } else { height = "50%"; if (i == 0) { top = "0px"; } else { top = "50%"; } } elCanvases[i] = $("").css({position: "absolute", width: "100%", height: height, top: top, left: "0px"}).addClass("waveform_canvas").appendTo(elContainer); } } for (m of self.markers) { m.redraw(); } self.alterSelection(null, null, null, true); // redraw selection and xfades async function drawCanvas(canvasIndex) { var elCanvas = elCanvases[canvasIndex]; //.empty(); elCanvas[0].width = elContainer.width(); elCanvas[0].height = elContainer.height() / wavedata.length; let width = elCanvas.width(); let height = elCanvas.height(); let ctx = elCanvas[0].getContext("2d"); var wavelength; var access; if (typeof(wavedata[canvasIndex]) == "function") { wavelength = await wavedata[canvasIndex](-1); access = wavedata[canvasIndex]; } else { wavelength = wavedata[0].length; access = async function(index) { return wavedata[0][index]; }; } var start = Math.round(regionStart * wavelength); var end = Math.round(regionEnd * wavelength); var regionLength = Math.round((regionEnd - regionStart) * wavelength); var indexStep = (regionLength / width) * efficiency; var widthStep = (indexStep < 1) ? parseInt(width / regionLength) : 1; widthStep = parseInt(Math.max(1, widthStep) * efficiency); indexStep = parseInt(indexStep); var style = getComputedStyle(document.body); var bgColour = (options.hasOwnProperty("bgColor")) ? options.bgColor : style.getPropertyValue("--waveformBgColor"); var fgColour = (options.hasOwnProperty("fgColor")) ? options.fgColor : style.getPropertyValue("--waveformFgColor"); var val; ctx.fillStyle = bgColour; ctx.fillRect(0, 0, width, height); ctx.strokeStyle = fgColour; if (options.drawStyle == "line") { ctx.lineCap = "butt"; ctx.lineWidth = 1; ctx.beginPath(); ctx.moveTo(0, height * 0.5); for (var x = 0, i = start; x < width; x+=widthStep, i+=indexStep) { val = await access(i); val = (val + 1) * 0.5; ctx.lineTo(x, (val * height) ); } ctx.closePath(); ctx.stroke(); } else if (options.drawStyle == "bar") { ctx.lineWidth = widthStep; ctx.lineCap = "round"; ctx.fillStyle = fgColour; ctx.beginPath(); for (var x = 0, i = start; x < width; x+=widthStep, i+=indexStep) { val = await access(i); val = (val + 1) * 0.5; var posY0 = (val * height); var posY1 = (height - posY0); ctx.moveTo(x, posY0); ctx.lineTo(x, posY1); } ctx.closePath(); ctx.stroke(); } else if (options.drawStyle == "linebar") { ctx.lineWidth = 1; ctx.fillStyle = fgColour; ctx.lineCap = "butt"; ctx.beginPath(); ctx.moveTo(0, height * 0.5); xindex = 0; var vals = []; for (var x = 0, i = start; x < width; x+=widthStep, i+=indexStep) { val = await access(i); val = (val + 1) * 0.5; vals[i] = val; var posY0 = (val * height); ctx.lineTo(x, posY0); } ctx.lineTo(width, (height * 0.5)); for ( ; x >= 0; x-=widthStep, i-=indexStep) { var posY1 = (height - (vals[i] * height)); ctx.lineTo(x, posY1); } ctx.lineTo(0, height * 0.5); ctx.fill(); ctx.closePath(); ctx.stroke(); } else { console.log("Invalid drawStyle"); } // end drawing waveform if (options.showGrid){ var lineSpacing = 50; var lineNum = width / lineSpacing; var position; ctx.lineCap = "butt"; ctx.lineWidth = 1; for (var x = 0; x < lineNum; x++) { ctx.beginPath(); var left = x * lineSpacing; ctx.strokeStyle = ctx.fillStyle = style.getPropertyValue("--waveformGridColor"); ctx.moveTo(left, 0); ctx.lineTo(left, height); ctx.stroke(); if ((canvasIndex == 0 && elCanvases.length == 1) || (canvasIndex == 1 && elCanvases.length == 2)) { ctx.strokeStyle = ctx.fillStyle = style.getPropertyValue("--waveformGridTextColor"); positionLabel = duration * (regionStart + (((regionEnd - regionStart) / lineNum) * x)); ctx.fillText(Math.round(positionLabel * 1000 ) / 1000, left + 2, height - 2); } } } // if showgrid if (canvasIndex == 0 && elCanvases.length == 2) { ctx.beginPath(); ctx.lineCap = "butt"; ctx.lineWidth = 1; ctx.strokeStyle = style.getPropertyValue("--waveformChannelLineColor"); ctx.fillStyle = ctx.strokeStyle; ctx.moveTo(0, height - 1); ctx.lineTo(width, height - 1); ctx.closePath(); ctx.stroke(); } } // end drawCanvas var drawCompletes = []; for (let i in elCanvases) { drawCompletes.push(false); drawCanvas(i).then(function(){ drawCompletes[i] = true; for (let c of drawCompletes) { if (!c) { return; } } drawing = false; }); } } this.redraw = function() { draw(); }; Object.defineProperty(this, "crossFadeInRatio", { get: function() { return crossFadeRatios[0]; }, set: function(v) { crossFadeRatios[0] = v; drawCrossFades(); } }); Object.defineProperty(this, "crossFadeOutRatio", { get: function() { return crossFadeRatios[1]; }, set: function(v) { crossFadeRatios[1] = v; drawCrossFades(); } }); Object.defineProperty(this, "selected", { get: function() { return selected; }, set: function(x) { } }); Object.defineProperty(this, "duration", { get: function() { return duration; }, set: function(x) { } }); Object.defineProperty(this, "channels", { get: function() { return channels; }, set: function(x) { } }); Object.defineProperty(this, "regionStart", { get: function() { return regionStart; }, set: function(x) { self.setRegion(x, regionEnd); } }); Object.defineProperty(this, "showGrid", { get: function() { return options.showGrid; }, set: function(x) { options.showGrid = x; draw(); } }); Object.defineProperty(this, "regionEnd", { get: function() { return regionEnd; }, set: function(x) { self.setRegion(regionStart, x); } }); this.zoomSelection = function() { if (!dragData || !dragData.location) return; dragData.location.css("left", "0px"); self.setRegion(selected[0], selected[1]); }; this.zoomOut = function() { self.setRegion(regionStart * 0.9, regionEnd * 1.1); }; this.zoomIn = function() { self.setRegion(regionStart * 1.1, regionEnd * 0.9); }; this.setRegion = function(start, end) { if (!hasContent) return; if (end <= start) return; if (end > 1) end = 1; if (start < 0) start = 0; regionStart = start; regionEnd = end; draw(); if (elTimeBar) { var elTbcw = elTimeBarContainer.width(); elTimeBar.css({left: (regionStart * elTbcw) + "px", right: ((1 - regionEnd) * elTbcw) + "px"}); } if (self.onRegionChange) { self.onRegionChange([regionStart, regionEnd]); } }; this.setData = function(data, nduration, noRedraw) { hasContent = true; wavedata = data; // should be array if (channels != data.length) { for (var i in elCanvases) { elCanvases[i].remove(); } delete elCanvases[i]; elCanvases.length = 0; } channels = data.length; duration = (nduration) ? nduration : 1; if (!noRedraw) { draw(); } }; var lastSize = []; function handleResize() { if (!hasContent) return; var width = elContainer.width(); var height = elContainer.height(); if (lastSize[0] = width && lastSize[1] == height) return; lastSize = [width, height]; if (dragData && dragData.selection) { selectionMade(); } draw(); } if (!options.noResizeHandler) { window.addEventListener("resize", handleResize); } }