changeset 221:9e27ca811051

Merge into default
author Nicholas Jillings <nicholas.jillings@eecs.qmul.ac.uk>
date Wed, 17 Jun 2015 16:43:13 +0100
parents 0560fe84fde6 (current diff) d8bad6edaa95 (diff)
children 49f35ece394c
files
diffstat 3 files changed, 319 insertions(+), 31 deletions(-) [+]
line wrap: on
line diff
--- a/ape.js	Wed Jun 17 15:27:11 2015 +0100
+++ b/ape.js	Wed Jun 17 16:43:13 2015 +0100
@@ -59,6 +59,94 @@
         console.log('slider ' + id + ' played (' + time + ')'); // DEBUG/SAFETY: show played slider id
 	};
 	
+	// Bindings for interfaceContext
+	Interface.prototype.checkAllPlayed = function()
+	{
+		hasBeenPlayed = audioEngineContext.checkAllPlayed();
+		if (hasBeenPlayed.length > 0) // if a fragment has not been played yet
+	    {
+	    	str = "";
+	    	if (hasBeenPlayed.length > 1) {
+		    	for (var i=0; i<hasBeenPlayed.length; i++) {
+		    		str = str + hasBeenPlayed[i];
+		    		if (i < hasBeenPlayed.length-2){
+		    			str += ", ";
+		    		} else if (i == hasBeenPlayed.length-2) {
+		    			str += " or ";
+		    		}
+		    	}
+		    	alert('You have not played fragments ' + str + ' yet. Please listen, rate and comment all samples before submitting.');
+	       } else {
+	       		alert('You have not played fragment ' + hasBeenPlayed[0] + ' yet. Please listen, rate and comment all samples before submitting.');
+	       }
+	        return false;
+	    }
+	    return true;
+	};
+	
+	Interface.prototype.checkAllMoved = function() {
+		var audioObjs = audioEngineContext.audioObjects;
+		var state = true;
+		var strNums = [];
+		for (var i=0; i<audioObjs.length; i++)
+		{
+			if (audioObjs[i].metric.wasMoved == false) {
+				state = false;
+				strNums.push(i);
+			}
+		}
+		if (state == false) {
+			if (strNums.length > 1) {
+				var str = "";
+		    	for (var i=0; i<strNums.length; i++) {
+		    		str = str + strNums[i];
+		    		if (i < strNums.length-2){
+		    			str += ", ";
+		    		} else if (i == strNums.length-2) {
+		    			str += " or ";
+		    		}
+		    	}
+		    	alert('You have not moved fragments ' + str + ' yet. Please listen, rate and comment all samples before submitting.');
+	       } else {
+	       		alert('You have not moved fragment ' + strNums[0] + ' yet. Please listen, rate and comment all samples before submitting.');
+	       }
+		}
+		return state;
+	};
+	
+	Interface.prototype.checkAllCommented = function() {
+		var audioObjs = audioEngineContext.audioObjects;
+		var audioHolder = testState.stateMap[testState.stateIndex];
+		var state = true;
+		if (audioHolder.elementComments) {
+			var strNums = [];
+			for (var i=0; i<audioObjs.length; i++)
+			{
+				if (audioObjs[i].commentDOM.trackCommentBox.value.length == 0) {
+					state = false;
+					strNums.push(i);
+				}
+			}
+			if (state == false) {
+				if (strNums.length > 1) {
+					var str = "";
+			    	for (var i=0; i<strNums.length; i++) {
+			    		str = str + strNums[i];
+			    		if (i < strNums.length-2){
+			    			str += ", ";
+			    		} else if (i == strNums.length-2) {
+			    			str += " or ";
+			    		}
+			    	}
+			    	alert('You have not commented on fragments ' + str + ' yet. Please listen, rate and comment all samples before submitting.');
+		       } else {
+		       		alert('You have not commented on fragment ' + strNums[0] + ' yet. Please listen, rate and comment all samples before submitting.');
+		       }
+			}
+		}
+		return state;
+	};
+	
 	// Bindings for audioObjects
 	
 	// Create the top div for the Title element
@@ -176,11 +264,11 @@
 	feedbackHolder.innerHTML = null;
 	canvas.innerHTML = null;
 	
-	var playbackHolder = document.createElement('div');
-	playbackHolder.style.width = "100%";
-	playbackHolder.align = 'center';
-	playbackHolder.appendChild(interfaceContext.playhead.object);
-	feedbackHolder.appendChild(playbackHolder);
+	//var playbackHolder = document.createElement('div');
+	//playbackHolder.style.width = "100%";
+	//playbackHolder.align = 'center';
+	//playbackHolder.appendChild(interfaceContext.playhead.object);
+	//feedbackHolder.appendChild(playbackHolder);
 	// Setup question title
 	var interfaceObj = audioHolderObject.interfaces;
 	var commentBoxPrefix = "Comment on track";
@@ -322,12 +410,15 @@
 		}
 	};
 	
-	this.exportXMLDOM = function() {
+	this.exportXMLDOM = function(audioObject) {
 		// Called by the audioObject holding this element. Must be present
 		var node = document.createElement('value');
 		node.textContent = convSliderPosToRate(this.trackSliderObj);
 		return node;
 	};
+	this.getValue = function() {
+		return convSliderPosToRate(this.trackSliderObj);
+	};
 }
 
 function dragEnd(ev) {
@@ -353,8 +444,98 @@
 
 function buttonSubmitClick() // TODO: Only when all songs have been played!
 {
-    hasBeenPlayed = audioEngineContext.checkAllPlayed();
-    if (hasBeenPlayed.length == 0) {
+	var checks = specification.commonInterface.options;
+	var canContinue = true;
+	
+	// Check that the anchor and reference objects are correctly placed
+	var audioObjs = audioEngineContext.audioObjects;
+	var audioHolder = testState.stateMap[testState.stateIndex];
+	var anchorId = null;
+	var referenceId = null;
+	for (var i=0; i<audioObjs.length; i++) {
+		if (audioObjs[i].specification.anchor == true && anchorId == null) {anchorId = i;}
+		if (audioObjs[i].specification.reference == true && referenceId == null) {referenceId = i;}
+	}
+	if (anchorId != null) {
+		if (audioObjs[anchorId].specification.marker != null) {
+			if (audioObjs[anchorId].interfaceDOM.getValue() > audioObjs[anchorId].specification.marker)
+			{
+				// Anchor is not set below
+				console.log('Anchor node not below marker value');
+				alert('Please keep listening');
+				return;
+			}
+		} else {
+			// No marker value given, ensure it is the minimum value
+			var anchorVal = audioObjs[anchorId].interfaceDOM.getValue();
+			for (var i=0; i<audioObjs.length; i++) {
+				if (i != anchorId) {
+					if (anchorVal > audioObjs[i].interfaceDOM.getValue()) {
+						// Anchor not the minimum
+						console.log('No marker set, anchor node not the minimum');
+						alert('Please keep listening');
+						return;
+					}
+				}
+			}
+		}
+	}
+	if (referenceId != null) {
+		if (audioObjs[referenceId].specification.marker != null) {
+			if (audioObjs[referenceId].interfaceDOM.getValue() < audioObjs[referenceId].specification.marker)
+			{
+				// Anchor is not set below
+				console.log('Reference node not above marker value');
+				alert('Please keep listening');
+				return;
+			}
+		} else {
+			// No marker value given, ensure it is the minimum value
+			var referenceVal = audioObjs[referenceId].interfaceDOM.getValue();
+			for (var i=0; i<audioObjs.length; i++) {
+				if (i != referenceId) {
+					if (referenceVal > audioObjs[i].interfaceDOM.getValue()) {
+						// Anchor not the minimum
+						console.log('No marker set, reference node not the maximum');
+						alert('Please keep listening');
+						return;
+					}
+				}
+			}
+		}
+	}
+	
+	for (var i=0; i<checks.length; i++) {
+		if (checks[i].type == 'check')
+		{
+			switch(checks[i].check) {
+			case 'fragmentPlayed':
+				// Check if all fragments have been played
+				var checkState = interfaceContext.checkAllPlayed();
+				if (checkState == false) {canContinue = false;}
+				break;
+			case  'fragmentFullPlayback':
+				// Check all fragments have been played to their full length
+				var checkState = interfaceContext.checkAllPlayed();
+				if (checkState == false) {canContinue = false;}
+				console.log('NOTE: fragmentFullPlayback not currently implemented, performing check fragmentPlayed instead');
+				break;
+			case 'fragmentMoved':
+				// Check all fragment sliders have been moved.
+				var checkState = interfaceContext.checkAllMoved();
+				if (checkState == false) {canContinue = false;}
+				break;
+			case 'fragmentComments':
+				// Check all fragment sliders have been moved.
+				var checkState = interfaceContext.checkAllCommented();
+				if (checkState == false) {canContinue = false;}
+				break;
+			}
+
+		}
+	}
+   
+    if (canContinue) {
 	    if (audioEngineContext.status == 1) {
 	        var playback = document.getElementById('playback-button');
 	        playback.click();
@@ -368,24 +549,7 @@
 	        }
 	    }
 	    testState.advanceState();
-    } else // if a fragment has not been played yet
-    {
-    	str = "";
-    	if (hasBeenPlayed.length > 1) {
-	    	for (var i=0; i<hasBeenPlayed.length; i++) {
-	    		str = str + hasBeenPlayed[i];
-	    		if (i < hasBeenPlayed.length-2){
-	    			str += ", ";
-	    		} else if (i == hasBeenPlayed.length-2) {
-	    			str += " or ";
-	    		}
-	    	}
-	    	alert('You have not played fragments ' + str + ' yet. Please listen, rate and comment all samples before submitting.');
-       } else {
-       		alert('You have not played fragment ' + hasBeenPlayed[0] + ' yet. Please listen, rate and comment all samples before submitting.');
-       }
-        return;
-    }
+    } 
 }
 
 function convSliderPosToRate(slider)
--- a/core.js	Wed Jun 17 15:27:11 2015 +0100
+++ b/core.js	Wed Jun 17 16:43:13 2015 +0100
@@ -892,8 +892,8 @@
 		var root = document.createElement('audioElement');
 		root.id = this.specification.id;
 		root.setAttribute('url',this.url);
-		root.appendChild(this.interfaceDOM.exportXMLDOM());
-		root.appendChild(this.commentDOM.exportXMLDOM());
+		root.appendChild(this.interfaceDOM.exportXMLDOM(this));
+		root.appendChild(this.commentDOM.exportXMLDOM(this));
 		root.appendChild(this.metric.exportXMLDOM());
 		return root;
 	};
@@ -1201,6 +1201,7 @@
 	// Handles the decoding of the project specification XML into a simple JavaScript Object.
 	
 	this.interfaceType;
+	this.commonInterface;
 	this.projectReturn;
 	this.randomiseOrder;
 	this.collectMetrics;
@@ -1233,6 +1234,32 @@
 			}
 		}
 		
+		var commonInterfaceNode = setupNode.getElementsByTagName('interface');
+		if (commonInterfaceNode.length > 0) {
+			commonInterfaceNode = commonInterfaceNode[0];
+		} else {
+			commonInterfaceNode = undefined;
+		}
+		
+		this.commonInterface = new function() {
+			this.OptionNode = function(child) {
+				this.type = child.nodeName;
+				if (this.type == 'check') {
+					this.check = child.getAttribute('name');
+				} else if (this.type == 'anchor' || this.type == 'reference') {
+					this.value = Number(child.textContent);
+				}
+			};
+			this.options = [];
+			if (commonInterfaceNode != undefined) {
+				var child = commonInterfaceNode.firstElementChild;
+				while (child != undefined) {
+					this.options.push(new this.OptionNode(child));
+					child = child.nextElementSibling;
+				}
+			}
+		};
+		
 		var audioHolders = projectXML.getElementsByTagName('audioHolder');
 		for (var i=0; i<audioHolders.length; i++) {
 			this.audioHolders.push(new this.audioHolderNode(this,audioHolders[i]));
@@ -1333,6 +1360,27 @@
 			this.url = xml.getAttribute('url');
 			this.id = xml.id;
 			this.parent = parent;
+			this.anchor = xml.getAttribute('anchor');
+			if (this.anchor == 'true') {this.anchor = true;}
+			else {this.anchor = false;}
+			
+			this.reference = xml.getAttribute('reference');
+			if (this.reference == 'true') {this.reference = true;}
+			else {this.reference = false;}
+			
+			if (this.anchor == true && this.reference == true) {
+				console.log('ERROR - Cannot have one audioElement be both the reference and anchor!');
+				console.log(this);
+				console.log('Neither reference nor anchor will be enabled on this fragment');
+				this.anchor = false;
+				this.reference = false;
+			}
+			if (this.anchor == true) {
+				this.marker = anchor;
+			}
+			if (this.reference == true) {
+				this.marker = reference;
+			}
 		};
 		
 		this.commentQuestionNode = function(xml) {
@@ -1388,6 +1436,46 @@
 		if (xml.getAttribute('elementComments') == "true") {this.elementComments = true;}
 		else {this.elementComments = false;}
 		
+		var anchor = xml.getElementsByTagName('anchor');
+		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;
+					break;
+				}
+			}
+			if (typeof(anchor) == "object") {
+				anchor = null;
+			}
+		} else {
+			anchor = anchor[0].textContent;
+		}
+		
+		var reference = xml.getElementsByTagName('anchor');
+		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;
+					break;
+				}
+			}
+			if (typeof(reference) == "object") {
+				reference = null;
+			}
+		} else {
+			reference = reference[0].textContent;
+		}
+		
+		if (typeof(anchor) == 'number') {
+			if (anchor > 1 && anchor < 100) {anchor /= 100.0;}
+		}
+		
+		if (typeof(reference) == 'number') {
+			if (reference > 1 && reference < 100) {reference /= 100.0;}
+		}
+		
 		this.preTest = new parent.prepostNode('pretest',xml.getElementsByTagName('PreTest'));
 		this.postTest = new parent.prepostNode('posttest',xml.getElementsByTagName('PostTest'));
 		
@@ -1410,6 +1498,34 @@
 			this.audioElements.push(new this.audioElementNode(this,audioElementsDOM[i]));
 		}
 		
+		// Check only one anchor and one reference per audioNode
+		var anchor = [];
+		var reference = [];
+		for (var i=0; i<this.audioElements.length; i++)
+		{
+			if (this.audioElements[i].anchor == true) {anchor.push(i);}
+			if (this.audioElements[i].reference == true) {reference.push(i);}
+		}
+		
+		if (anchor.length > 1) {
+			console.log('Error - cannot have more than one anchor!');
+			console.log('Each anchor node will be a normal mode to continue the test');
+			for (var i=0; i<anchor.length; i++)
+			{
+				this.audioElements[anchor[i]].anchor = false;
+				this.audioElements[anchor[i]].value = undefined;
+			}
+		}
+		if (reference.length > 1) {
+			console.log('Error - cannot have more than one anchor!');
+			console.log('Each anchor node will be a normal mode to continue the test');
+			for (var i=0; i<reference.length; i++)
+			{
+				this.audioElements[reference[i]].reference = false;
+				this.audioElements[reference[i]].value = undefined;
+			}
+		}
+		
 		this.commentQuestions = [];
 		var commentQuestionsDOM = xml.getElementsByTagName('CommentQuestion');
 		for (var i=0; i<commentQuestionsDOM.length; i++) {
--- a/example_eval/project.xml	Wed Jun 17 15:27:11 2015 +0100
+++ b/example_eval/project.xml	Wed Jun 17 16:43:13 2015 +0100
@@ -34,6 +34,14 @@
 			<metricEnable>elementFlagMoved</metricEnable>
 			<metricEnable>elementListenTracker</metricEnable>
 		</Metric>
+		<interface>
+			<!--<check name="fragmentPlayed"/>
+			<check name="fragmentFullPlayback"/>
+			<check name="fragmentMoved"/>
+			<check name="fragmentComments"/>-->
+			<anchor>20</anchor>
+			<reference>80</reference>
+		</interface>
 	</setup>
 	<audioHolder id='test-0' hostURL="example_eval/" sampleRate="44100" randomiseOrder='true' repeatCount='0' loop='true' elementComments='true'>
 		<interface>
@@ -44,7 +52,7 @@
 			<scale position="20">20</scale>
 			<commentBoxPrefix>Comment on fragment</commentBoxPrefix>
 		</interface>
-		<audioElements url="0.wav" id="0"/>
+		<audioElements url="0.wav" id="0" anchor='true'/>
 		<audioElements url="1.wav" id="1"/>
 		<audioElements url="2.wav" id="2"/>
 		<audioElements url="3.wav" id="3"/>
@@ -86,8 +94,8 @@
             <scale position="75">75</scale>
             <commentBoxPrefix>Comment on fragment</commentBoxPrefix>
         </interface>
-        <audioElements url="0.wav" id="0"/>
-        <audioElements url="1.wav" id="1"/>
+        <audioElements url="0.wav" id="0" reference="true"/>
+        <audioElements url="1.wav" id="1" anchor="true"/>
         <audioElements url="2.wav" id="2"/>
         <audioElements url="3.wav" id="3"/>
         <audioElements url="4.wav" id="4"/>