changeset 278:8020152a36af

Pull into main
author Nicholas Jillings <nicholas.jillings@eecs.qmul.ac.uk>
date Fri, 24 Jul 2015 18:59:39 +0100
parents 4345ba8a1b6e (current diff) 724f72092fa7 (diff)
children a1c1f032ff0a
files
diffstat 4 files changed, 156 insertions(+), 24 deletions(-) [+]
line wrap: on
line diff
--- a/ape.css	Mon Jul 20 12:47:16 2015 +0100
+++ b/ape.css	Fri Jul 24 18:59:39 2015 +0100
@@ -68,6 +68,10 @@
 	background-color: rgb(100,200,100);
 }
 
+div.track-slider-disabled {
+	background-color: rgb(100,100,100);
+}
+
 div.track-slider-playing {
 	background-color: #FF0000;
 }
@@ -76,3 +80,10 @@
 	background-color: #FFDDDD;
 }
 
+div#outside-reference {
+	width:250px;
+	margin-left: 100px;
+	height:20px;
+	margin-bottom:5px;
+	background-color: rgb(100,200,100);
+}
--- a/ape.js	Mon Jul 20 12:47:16 2015 +0100
+++ b/ape.js	Fri Jul 24 18:59:39 2015 +0100
@@ -312,7 +312,17 @@
 					playbackHolder.appendChild(interfaceContext.playhead.object);
 					feedbackHolder.appendChild(playbackHolder);
 				}
-				break;
+			} else if (interfaceObj[k].options[i].type == 'option' && interfaceObj[k].options[i].name == 'page-count')
+			{
+				var pagecountHolder = document.getElementById('page-count');
+				if (pagecountHolder == null)
+				{
+					pagecountHolder = document.createElement('div');
+					pagecountHolder.id = 'page-count';
+				}
+				pagecountHolder.innerHTML = '<span>Test '+(audioHolderObject.presentedId+1)+' of '+specification.audioHolders.length+'</span>';
+				var inject = document.getElementById('interface-buttons');
+				inject.appendChild(pagecountHolder);
 			}
 		}
 	}
@@ -434,14 +444,14 @@
 	}
 	
 	
-	testWaitIndicator();
+	//testWaitIndicator();
 }
 
 function sliderObject(audioObject) {
 	// Create a new slider object;
 	this.parent = audioObject;
 	this.trackSliderObj = document.createElement('div');
-	this.trackSliderObj.className = 'track-slider';
+	this.trackSliderObj.className = 'track-slider track-slider-disabled';
 	this.trackSliderObj.id = 'track-slider-'+audioObject.id;
 
 	this.trackSliderObj.setAttribute('trackIndex',audioObject.id);
@@ -478,6 +488,13 @@
 		}
 	};
 	
+	this.enable = function() {
+		if (this.parent.state == 1)
+		{
+			$(this.trackSliderObj).removeClass('track-slider-disabled');
+		}
+	};
+	
 	this.exportXMLDOM = function(audioObject) {
 		// Called by the audioObject holding this element. Must be present
 		var node = document.createElement('value');
@@ -591,7 +608,7 @@
 	// Store the slider marker values
 	var holdValues = [];
 	$(".track-slider").each(function(index,sliderObj){
-		holdValues.push(convSliderPosToRate(index));
+		holdValues.push(convSliderPosToRate(sliderObj));
 	});
 	
 	var width = event.target.innerWidth;
--- a/core.js	Mon Jul 20 12:47:16 2015 +0100
+++ b/core.js	Fri Jul 24 18:59:39 2015 +0100
@@ -105,6 +105,19 @@
 		var blank = document.getElementsByClassName('testHalt')[0];
 		blank.style.zIndex = 2;
 		blank.style.visibility = 'visible';
+		$(window).keypress(function(e){
+			if (e.keyCode == 13 && popup.popup.style.visibility == 'visible')
+			{
+				// Enter key pressed
+				var textarea = $(popup.popupContent).find('textarea');
+				if (textarea.length != 0)
+				{
+					if (textarea[0] == document.activeElement)
+					{return;}
+				}
+				popup.buttonProceed.onclick();
+			}
+		});
 	};
 	
 	this.hidePopup = function(){
@@ -212,7 +225,11 @@
 		}
 		this.popupContent.appendChild(this.buttonProceed);
 		if(this.currentIndex+1 == this.popupOptions.length) {
-			this.buttonProceed.textContent = 'Submit';
+			if (this.responses.nodeName == "PRETEST") {
+				this.buttonProceed.textContent = 'Start';
+			} else {
+				this.buttonProceed.textContent = 'Submit';
+			}
 		} else {
 			this.buttonProceed.textContent = 'Next';
 		}
@@ -537,6 +554,10 @@
 	if (specification.randomiseOrder)
 	{
  		specification.audioHolders = randomiseOrder(specification.audioHolders);
+ 		for (var i=0; i<specification.audioHolders.length; i++)
+ 		{
+ 			specification.audioHolders[i].presentedId = i;
+ 		}
 	}
 	
 	$(specification.audioHolders).each(function(index,elem){
@@ -631,6 +652,10 @@
 			console.log(xmlhttp.status);
 			if (xmlhttp.status != 200 && xmlhttp.readyState == 4) {
 				createProjectSave(null);
+			} else {
+				popup.showPopup();
+				popup.popupContent.innerHTML = null;
+				popup.popupContent.textContent = "Thank you for performing this listening test";
 			}
 		};
 		xmlhttp.send(file);
@@ -713,6 +738,8 @@
 			}
 			if (this.audioObjectsReady == true) {
 				this.timer.startTest();
+				if (this.loopPlayback)
+					{this.setSynchronousLoop();}
 				this.status = 1;
 			}
 		}
@@ -803,6 +830,45 @@
 		return ready;
 	};
 	
+	this.setSynchronousLoop = function() {
+		// Pads the signals so they are all exactly the same length
+		if (this.audioObjectsReady)
+		{
+			var length = 0;
+			var lens = [];
+			var maxId;
+			for (var i=0; i<this.audioObjects.length; i++)
+			{
+				lens.push(this.audioObjects[i].buffer.length);
+				if (length < this.audioObjects[i].buffer.length)
+				{
+					length = this.audioObjects[i].buffer.length;
+					maxId = i;
+				}
+			}
+			// Perform difference
+			for (var i=0; i<lens.length; i++)
+			{
+				lens[i] = length - lens[i];
+			}
+			// Extract the audio and zero-pad
+			for (var i=0; i<lens.length; i++)
+			{
+				var orig = this.audioObjects[i].buffer;
+				var hold = audioContext.createBuffer(orig.numberOfChannels,length,orig.sampleRate);
+				for (var c=0; c<orig.numberOfChannels; c++)
+				{
+					var inData = hold.getChannelData(c);
+					var outData = orig.getChannelData(c);
+					for (var n=0; n<orig.length; n++)
+					{inData[n] = outData[n];}
+				}
+				this.audioObjects[i].buffer = hold;
+				delete orig;
+			}
+		}
+	};
+	
 }
 
 function audioObject(id) {
@@ -905,6 +971,8 @@
 			audioContext.decodeAudioData(request.response, function(decodedData) {
 				audioObj.buffer = decodedData;
 				audioObj.state = 1;
+				if (audioObj.specification.type != 'outsidereference')
+					{audioObj.interfaceDOM.enable();}
 			}, function(){
 				// Should only be called if there was an error, but sometimes gets called continuously
 				// Check here if the error is genuine
@@ -936,6 +1004,11 @@
 		if (this.specification.type != 'outsidereference') {
 			root.appendChild(this.interfaceDOM.exportXMLDOM(this));
 			root.appendChild(this.commentDOM.exportXMLDOM(this));
+			if(this.specification.type == 'anchor') {
+				root.setAttribute('anchor',true);
+			} else if(this.specification.type == 'reference') {
+				root.setAttribute('reference',true);
+			}
 		}
 		root.appendChild(this.metric.exportXMLDOM());
 		return root;
@@ -1312,6 +1385,9 @@
 					}
 				} else if (this.type == 'anchor' || this.type == 'reference') {
 					this.value = Number(child.textContent);
+					this.enforce = child.getAttribute('enforce');
+					if (this.enforce == 'true') {this.enforce = true;}
+					else {this.enforce = false;}
 				}
 			};
 			this.options = [];
@@ -1405,6 +1481,7 @@
 	
 	this.audioHolderNode = function(parent,xml) {
 		this.type = 'audioHolder';
+		this.presentedId = parent.audioHolders.length;
 		this.interfaceNode = function(DOM) {
 			var title = DOM.getElementsByTagName('title');
 			if (title.length == 0) {this.title = null;}
@@ -1434,10 +1511,14 @@
 			this.marker = xml.getAttribute('marker');
 			if (this.marker == null) {this.marker = undefined;}
 			
-			if (this.anchor == true && this.marker == undefined) {
+			if (this.anchor == true) {
+				if (this.marker != undefined) {this.enforce = true;}
+				else {this.enforce = enforceAnchor;}
 				this.marker = anchor;
 			}
-			else if (this.reference == true && this.marker == undefined) {
+			else if (this.reference == true) {
+				if (this.marker != undefined) {this.enforce = true;}
+				else {this.enforce = enforceReference;}
 				this.marker = reference;
 			}
 			
@@ -1501,11 +1582,13 @@
 		else {this.elementComments = false;}
 		
 		var anchor = xml.getElementsByTagName('anchor');
+		var enforceAnchor = false;
 		if (anchor.length == 0) {
 			// Find anchor in commonInterface;
 			for (var i=0; i<parent.commonInterface.options.length; i++) {
 				if(parent.commonInterface.options[i].type == 'anchor') {
 					anchor = parent.commonInterface.options[i].value;
+					enforceAnchor = parent.commonInterface.options[i].enforce;
 					break;
 				}
 			}
@@ -1517,11 +1600,13 @@
 		}
 		
 		var reference = xml.getElementsByTagName('anchor');
+		var enforceReference = false;
 		if (reference.length == 0) {
 			// Find anchor in commonInterface;
 			for (var i=0; i<parent.commonInterface.options.length; i++) {
 				if(parent.commonInterface.options[i].type == 'reference') {
 					reference = parent.commonInterface.options[i].value;
+					enforceReference = parent.commonInterface.options[i].enforce;
 					break;
 				}
 			}
@@ -1943,19 +2028,28 @@
 			// Update the playhead position, startPlay must be called
 			if (this.timePerPixel > 0) {
 				var time = this.playbackObject.getCurrentPosition();
-				var width = 490;
-				var pix = Math.floor(time/this.timePerPixel);
-				this.scrubberHead.style.left = pix+'px';
-				if (this.maxTime > 60.0) {
-					var secs = time%60;
-					var mins = Math.floor((time-secs)/60);
-					secs = secs.toString();
-					secs = secs.substr(0,2);
-					mins = mins.toString();
-					this.curTimeSpan.textContent = mins+':'+secs;
+				if (time > 0) {
+					var width = 490;
+					var pix = Math.floor(time/this.timePerPixel);
+					this.scrubberHead.style.left = pix+'px';
+					if (this.maxTime > 60.0) {
+						var secs = time%60;
+						var mins = Math.floor((time-secs)/60);
+						secs = secs.toString();
+						secs = secs.substr(0,2);
+						mins = mins.toString();
+						this.curTimeSpan.textContent = mins+':'+secs;
+					} else {
+						time = time.toString();
+						this.curTimeSpan.textContent = time.substr(0,4);
+					}
 				} else {
-					time = time.toString();
-					this.curTimeSpan.textContent = time.substr(0,4);
+					this.scrubberHead.style.left = '0px';
+					if (this.maxTime < 60) {
+						this.curTimeSpan.textContent = '0.00';
+					} else {
+						this.curTimeSpan.textContent = '00:00';
+					}
 				}
 			}
 		};
@@ -1964,12 +2058,21 @@
 		
 		this.start = function() {
 			if (this.playbackObject != undefined && this.interval == undefined) {
-				this.interval = setInterval(function(){interfaceContext.playhead.update();},100);
+				if (this.maxTime < 60) {
+					this.interval = setInterval(function(){interfaceContext.playhead.update();},10);
+				} else {
+					this.interval = setInterval(function(){interfaceContext.playhead.update();},100);
+				}
 			}
 		};
 		this.stop = function() {
 			clearInterval(this.interval);
 			this.interval = undefined;
+			if (this.maxTime < 60) {
+				this.curTimeSpan.textContent = '0.00';
+			} else {
+				this.curTimeSpan.textContent = '00:00';
+			}
 		};
 	};
 	
@@ -1981,7 +2084,7 @@
 		if (audioHolder.anchorId != null)
 		{
 			var audioObject = audioEngineContext.audioObjects[audioHolder.anchorId];
-			if (audioObject.interfaceDOM.getValue() > audioObject.specification.marker)
+			if (audioObject.interfaceDOM.getValue() > audioObject.specification.marker && audioObject.interfaceDOM.enforce == true)
 			{
 				// Anchor is not set below
 				console.log('Anchor node not below marker value');
@@ -1998,7 +2101,7 @@
 		if (audioHolder.referenceId != null)
 		{
 			var audioObject = audioEngineContext.audioObjects[audioHolder.referenceId];
-			if (audioObject.interfaceDOM.getValue() < audioObject.specification.marker)
+			if (audioObject.interfaceDOM.getValue() < audioObject.specification.marker && audioObject.interfaceDOM.enforce == true)
 			{
 				// Anchor is not set below
 				console.log('Reference node not above marker value');
--- a/example_eval/project.xml	Mon Jul 20 12:47:16 2015 +0100
+++ b/example_eval/project.xml	Fri Jul 24 18:59:39 2015 +0100
@@ -41,6 +41,8 @@
 			<check name="fragmentComments"/>-->
 			<check name="scalerange" min="25" max="75"/>
 			<option name='playhead'/>
+			<option name="page-count"/>
+			<anchor enforce="false">40</anchor>
 		</interface>
 	</setup>
 	<audioHolder id='test-0' hostURL="example_eval/" sampleRate="44100" randomiseOrder='true' repeatCount='0' loop='true' elementComments='true'>
@@ -51,7 +53,6 @@
 			<scale position="50">Middle</scale>
 			<scale position="20">20</scale>
 			<commentBoxPrefix>Comment on fragment</commentBoxPrefix>
-			<anchor>40</anchor>
 		</interface>
 		<audioElements url="0.wav" id="0" type="anchor"/>
 		<audioElements url="1.wav" id="1"/>
@@ -86,7 +87,7 @@
 			<question id="genre" mandatory="true">Please enter the genre.</question>
 		</PostTest>
 	</audioHolder>
-    <audioHolder id='test-1' hostURL="example_eval/" sampleRate="44100" randomiseOrder='true' repeatCount='0' loop='true' elementComments='true'>
+    <audioHolder id='test-1' hostURL="example_eval/" sampleRate="44100" randomiseOrder='true' repeatCount='0' loop='false' elementComments='true'>
         <interface>
             <title>Example Test Question</title>
             <scale position="0">Min</scale>