# HG changeset patch # User Nicholas Jillings # Date 1470232324 -3600 # Node ID dbe43b4ab7aa77393de3b55033da6daa1036b115 # Parent f6f4693a84ebd00196f811d5a470d9fa59a601ea Starting timeline.js interface diff -r f6f4693a84eb -r dbe43b4ab7aa interfaces/timeline.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/interfaces/timeline.css Wed Aug 03 14:52:04 2016 +0100 @@ -0,0 +1,43 @@ +div.timeline-element { + display: flex; + justify-content: center; +} + +div.timeline-element-content { + max-width: 800px; + min-width: 200px; + border: 1px solid black; + margin: 10px 0px; + padding: 20px; +} + +div.timeline-element-canvas-holder { + display: flex; + width: inherit; + height: 150px; + margin-left: 50px; +} + +canvas.canvas-layer1{ + position: absolute; +} +canvas.canvas-layer2 { + position: absolute; + z-index: -1; +} +canvas.canvas-layer3 { + position: absolute; + z-index: -2; +} +canvas.canvas-layer4 { + position: absolute; + z-index: -3; +} + +canvas.timeline-element-canvas { + border: 1px solid black; +} + +canvas.canvas-disabled { + background-color: gray; +} \ No newline at end of file diff -r f6f4693a84eb -r dbe43b4ab7aa interfaces/timeline.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/interfaces/timeline.js Wed Aug 03 14:52:04 2016 +0100 @@ -0,0 +1,465 @@ +/** + * WAET Timeline + * This interface plots a waveform timeline per audio fragment on a page. Clicking on the fragment will generate a comment box for processing. + */ + +// Once this is loaded and parsed, begin execution +loadInterface(); + +function loadInterface() { + // Use this to do any one-time page / element construction. For instance, placing any stationary text objects, + // holding div's, or setting up any nodes which are present for the entire test sequence + + interfaceContext.insertPoint.innerHTML = ""; // Clear the current schema + + interfaceContext.insertPoint = document.getElementById("topLevelBody"); + var testContent = document.createElement("div"); + + // Create the top div and Title element + var title = document.createElement("div"); + title.className = "title"; + title.align = "center"; + var titleSpan = document.createElement("span"); + titleSpan.id = "test-title"; + titleSpan.textContent = "Listening Test"; + title.appendChild(titleSpan); + + var pagetitle = document.createElement("div"); + pagetitle.className = "pageTitle"; + pagetitle.align = "center"; + titleSpan = document.createElement("span"); + titleSpan.id = "page-title"; + pagetitle.appendChild(titleSpan); + + // Create Interface buttons + var interfaceButtons = document.createElement("div"); + interfaceButtons.id = 'interface-buttons'; + interfaceButtons.style.height = "25px"; + + // Create playback start/stop points + var playback = document.createElement("button"); + playback.innerHTML = "Stop"; + playback.id = "playback-button"; + playback.onclick = function() { + if (audioEngineContext.status == 1) { + audioEngineContext.stop(); + this.innerHTML = "Stop"; + var time = audioEngineContext.timer.getTestTime(); + console.log("Stopped at "+time); + } + }; + // Create Submit (save) button + var submit = document.createElement("button"); + submit.innerHTML = 'Next'; + submit.onclick = buttonSubmitClick; + submit.id = 'submit-button'; + submit.style.float = 'left'; + // Append the interface buttons into the interfaceButtons object. + interfaceButtons.appendChild(playback); + interfaceButtons.appendChild(submit); + + // Create outside reference holder + var outsideRef = document.createElement("div"); + outsideRef.id = "outside-reference-holder"; + + // Create content point + var content = document.createElement("div"); + content.id = "timeline-test-content"; + + //Inject + testContent.appendChild(title); + testContent.appendChild(pagetitle); + testContent.appendChild(interfaceButtons); + testContent.appendChild(outsideRef); + testContent.appendChild(content); + interfaceContext.insertPoint.appendChild(testContent); + + // Load the full interface + testState.initialise(); + testState.advanceState(); +}; + +function loadTest(page) +{ + // Called each time a new test page is to be build. The page specification node is the only item passed in + var content = document.getElementById("timeline-test-content"); + content.innerHTML = ""; + var interfaceObj = page.interfaces; + if (interfaceObj.length > 1) + { + console.log("WARNING - This interface only supports one node per page. Using first interface node"); + } + interfaceObj = interfaceObj[0]; + + //Set the page title + if (typeof page.title == "string" && page.title.length > 0) + { + document.getElementById("test-title").textContent = page.title; + } + + if (interfaceObj.title != null) { + document.getElementById("page-title").textContent = interfaceObj.title; + } + + // Delete outside reference + var outsideReferenceHolder = document.getElementById("outside-reference-holder"); + outsideReferenceHolder.innerHTML = ""; + + var commentBoxPrefix = "Comment on track"; + if (interfaceObj.commentBoxPrefix != undefined) { + commentBoxPrefix = interfaceObj.commentBoxPrefix; + } + + $(page.audioElements).each(function(index,element){ + var audioObject = audioEngineContext.newTrack(element); + if (page.audioElements.type == 'outside-reference') { + var refNode = interfaceContext.outsideReferenceDOM(audioObject,index,outsideReferenceHolder); + audioObject.bindInterface(orNode); + } else { + switch(audioObject.specification.parent.label) { + case "none": + label = ""; + break; + case "letter": + label = String.fromCharCode(97 + index); + break; + case "capital": + label = String.fromCharCode(65 + index); + break; + default: + label = ""+index; + break; + } + var node = new interfaceObject(audioObject,label); + + content.appendChild(node.DOM); + audioObject.bindInterface(node); + } + }); + + resizeWindow(); +} + +function interfaceObject(audioObject,labelstr) +{ + // Each audio object has a waveform guide and self-generated comments + this.parent = audioObject; + this.DOM = document.createElement("div"); + this.DOM.className = "timeline-element"; + this.DOM.id = audioObject.specification.id; + + var root = document.createElement("div"); + root.className = "timeline-element-content"; + this.DOM.appendChild(root); + + var label = document.createElement("div"); + label.style.textAlign = "center"; + var labelSpan = document.createElement("span"); + labelSpan.textContent = "Fragment "+labelstr; + label.appendChild(labelSpan); + root.appendChild(label); + + var canvasHolder = document.createElement("div"); + canvasHolder.className = "timeline-element-canvas-holder"; + var buttonHolder = document.createElement("div"); + buttonHolder.className = "timeline-element-button-holder"; + var commentHolder = document.createElement("div"); + commentHolder.className = "timeline-element-comment-holder"; + + root.appendChild(canvasHolder); + root.appendChild(buttonHolder); + root.appendChild(commentHolder); + + this.comments = { + parent: this, + list: [], + Comment: function(parent,time, str) { + this.parent = parent; + this.time = time; + this.DOM = document.createElement("div"); + this.DOM.className = "comment-div"; + this.title = document.createElement("span"); + if (str != undefined) { + this.title.textContent = str; + } else { + this.title.textContent = "Time: "+time.toFixed(2)+"s"; + } + this.textarea = document.createElement("textarea"); + this.textarea.className = "trackComment"; + this.DOM.appendChild(this.title); + this.DOM.appendChild(document.createElement("br")); + this.DOM.appendChild(this.textarea); + this.resize = function() { + var w = window.innerWidth; + w = Math.min(w,800); + w = Math.max(w,200); + var elem_w = w / 2.5; + elem_w = Math.max(elem_w,190); + this.DOM.style.width = elem_w+"px"; + this.textarea.style.width = (elem_w-5)+"px"; + } + this.resize(); + }, + newComment: function(time) { + var node = new this.Comment(this,time); + this.list.push(node); + commentHolder.appendChild(node.DOM); + return node; + }, + deleteComment: function(comment) { + + }, + clearList: function() { + + } + } + + this.canvas = { + parent: this, + comments: this.comments, + layer1: document.createElement("canvas"), + layer2: document.createElement("canvas"), + layer3: document.createElement("canvas"), + layer4: document.createElement("canvas"), + resize: function(w) { + this.layer1.width = w; + this.layer2.width = w; + this.layer3.width = w; + this.layer4.width = w; + this.layer1.style.width = w+"px"; + this.layer2.style.width = w+"px"; + this.layer3.style.width = w+"px"; + this.layer4.style.width = w+"px"; + }, + handleEvent: function(event) { + switch(event.currentTarget) { + case this.layer1: + switch(event.type) { + case "mousemove": + this.drawMouse(event); + break; + case "mouseleave": + this.clearCanvas(this.layer1); + break; + case "click": + var rect = this.layer1.getBoundingClientRect(); + var pixX = event.clientX - rect.left; + var tpp = this.parent.parent.buffer.buffer.duration/this.layer1.width; + this.comments.newComment(pixX*tpp); + this.drawMarkers(); + break; + } + break; + } + }, + drawWaveform: function() { + var buffer = this.parent.parent.buffer.buffer; + var context = this.layer4.getContext("2d"); + context.lineWidth = 1; + context.strokeStyle = "#888"; + context.clearRect(0,0,this.layer4.width, this.layer4.height); + var data = buffer.getChannelData(0); + var t_per_pixel = buffer.duration/this.layer4.width; + var s_per_pixel = data.length/this.layer4.width; + var pixX = 0; + while (pixX < this.layer4.width) { + var start = Math.floor(s_per_pixel*pixX); + var end = Math.min(Math.ceil(s_per_pixel*(pixX+1)),data.length); + var frame = data.subarray(start,end); + var min = frame[0]; + var max = min; + for (var n=0; n max) {max = frame[n];} + } + // Assuming min/max normalised between [-1, 1] to map to [150, 0] + context.beginPath(); + context.moveTo(pixX+0.5,(min+1)*-75+150); + context.lineTo(pixX+0.5,(max+1)*-75+150); + context.stroke(); + pixX++; + } + }, + drawMouse: function(event) { + var context = this.layer1.getContext("2d"); + context.clearRect(0,0,this.layer1.width, this.layer1.height); + var rect = this.layer1.getBoundingClientRect(); + var pixX = event.clientX - rect.left; + pixX = Math.floor(pixX)-0.5; + context.strokeStyle = "#800"; + context.beginPath(); + context.moveTo(pixX,0); + context.lineTo(pixX,this.layer1.height); + context.stroke(); + }, + drawTicker: function() { + var context = this.layer2.getContext("2d"); + context.clearRect(0,0,this.layer2.width, this.layer2.height); + var time = this.parent.parent.getCurrentPosition(); + var ratio = time / this.parent.parent.buffer.buffer.duration; + var pixX = Math.floor(ratio*this.layer2.width)+0.5; + context.strokeStyle = "#080"; + context.beginPath(); + context.moveTo(pixX,0); + context.lineTo(pixX,this.layer2.height); + context.stroke(); + }, + drawMarkers: function() { + var context = this.layer3.getContext("2d"); + context.clearRect(0,0,this.layer3.width, this.layer3.height); + context.strokeStyle = "#008"; + var tpp = this.parent.parent.buffer.buffer.duration/this.layer1.width; + for (var i=0; i node. + // If there is no value node (such as outside reference), return null + // If there are multiple value nodes (such as multiple scale / 2D scales), return an array of nodes with each value node having an 'interfaceName' attribute + // Use storage.document.createElement('value'); to generate the XML node. + return null; + }; + this.error = function() { + // If there is an error with the audioObject, this will be called to indicate a failure + } +}; + +function resizeWindow(event) +{ + // Called on every window resize event, use this to scale your page properly + for (var i=0; i saves + // Get the current information in store (remember to appendChild your data to it) + // pageSpecification is the current page node configuration + // To create new XML nodes, use storage.document.createElement(); +} \ No newline at end of file diff -r f6f4693a84eb -r dbe43b4ab7aa js/core.js --- a/js/core.js Tue Aug 02 17:24:58 2016 +0200 +++ b/js/core.js Wed Aug 03 14:52:04 2016 +0100 @@ -419,6 +419,15 @@ document.getElementsByTagName("head")[0].appendChild(css); break; + case "timeline": + interfaceJS.setAttribute("src","interfaces/timeline.js"); + var css = document.createElement('link'); + css.rel = 'stylesheet'; + css.type = 'text/css'; + css.href = 'interfaces/timeline.css'; + + document.getElementsByTagName("head")[0].appendChild(css); + break; } document.getElementsByTagName("head")[0].appendChild(interfaceJS); diff -r f6f4693a84eb -r dbe43b4ab7aa tests/test.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test.xml Wed Aug 03 14:52:04 2016 +0100 @@ -0,0 +1,80 @@ + + + + + testTimer + elementTimer + elementInitialPosition + elementTracker + elementFlagListenedTo + elementFlagMoved + elementListenTracker + + + + + + + + + + + + My Test + + + (1) Very Annoying + (2) Annoying + (3) Slightly Annoying + (4) Audible but not Annoying + (5) Inaudible + + + + + + Please enter your overall preference + + + + + + + + Please describe the overall character + + + + + + + + My Test + + + (1) Very Annoying + (2) Annoying + (3) Slightly Annoying + (4) Audible but not Annoying + (5) Inaudible + + + + + + Please enter your overall preference + + + + + + + + Please describe the overall character + + + + + + +