changeset 1048:7a8fcf04aad3

Merge
author Brecht De Man <BrechtDeMan@users.noreply.github.com>
date Mon, 29 Jun 2015 19:04:40 +0100
parents ba83143187d6 (current diff) 01fcd75067c1 (diff)
children a37ab38cf90b
files .hgignore ape.js core.js docs/SMC15/interface.png docs/SMC15/smc2015template.tex example_eval/project.xml index.html scripts/comment_parser.py scripts/score_parser.py
diffstat 3 files changed, 194 insertions(+), 105 deletions(-) [+]
line wrap: on
line diff
--- a/ape.js	Mon Jun 29 19:02:48 2015 +0100
+++ b/ape.js	Mon Jun 29 19:04:40 2015 +0100
@@ -147,6 +147,40 @@
 		return state;
 	};
 	
+	Interface.prototype.checkScaleRange = function()
+	{
+		var audioObjs = audioEngineContext.audioObjects;
+		var audioHolder = testState.stateMap[testState.stateIndex];
+		var interfaces = audioHolder.interfaces;
+		
+		var minRanking = audioObjs[0].interfaceDOM.getValue();
+		var maxRanking = minRanking;
+		
+		var minScale;
+		var maxScale;
+		for (var i=0; i<interfaces[0].options.length; i++)
+		{
+			if (interfaces[0].options[i].check == "scalerange") {
+				minScale = interfaces[0].options[i].min;
+				maxScale = interfaces[0].options[i].max;
+			}
+		}
+		
+		for (var i=1; i<audioObjs.length; i++){
+			if (audioObjs[i].specification.type != 'outsidereference') {
+				var ranking = audioObjs[i].interfaceDOM.getValue();
+				if (ranking < minRanking) { minRanking = ranking;}
+				if (ranking > maxRanking) { maxRanking = ranking;}
+			}
+		}
+		if (minRanking > minScale || maxRanking < maxScale) {
+			alert('Please use the full width of the scale');
+			return false;
+		} else {
+			return true;
+		}
+	};
+	
 	// Bindings for audioObjects
 	
 	// Create the top div for the Title element
@@ -264,11 +298,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";
@@ -323,14 +357,6 @@
 	currentTestHolder.id = audioHolderObject.id;
 	currentTestHolder.repeatCount = audioHolderObject.repeatCount;
 	
-	var randomise = audioHolderObject.randomiseOrder;
-	
-	var audioElements = audioHolderObject.audioElements;
-	currentTrackOrder = [];
-	if (randomise) {
-		audioHolderObject.audioElements = randomiseOrder(audioHolderObject.audioElements);
-	}
-	
 	// Delete any previous audioObjects associated with the audioEngine
 	audioEngineContext.audioObjects = [];
 	interfaceContext.deleteCommentBoxes();
@@ -369,6 +395,31 @@
 		feedbackHolder.appendChild(node.holder);
 	});
 	
+	// Construct outside reference;
+	if (audioHolderObject.outsideReference != null) {
+		var outsideReferenceHolder = document.createElement('div');
+		outsideReferenceHolder.id = 'outside-reference';
+		outsideReferenceHolderspan = document.createElement('span');
+		outsideReferenceHolderspan.textContent = 'Reference';
+		outsideReferenceHolder.appendChild(outsideReferenceHolderspan);
+		
+		var audioObject = audioEngineContext.newTrack(audioHolderObject.outsideReference);
+		
+		outsideReferenceHolder.onclick = function()
+		{
+			audioEngineContext.play(audioEngineContext.audioObjects.length-1);
+			$('.track-slider').removeClass('track-slider-playing');
+            $('.comment-div').removeClass('comment-box-playing');
+            if (event.srcElement.nodeName == 'DIV') {
+            	$(event.srcElement).addClass('track-slider-playing');
+            } else {
+            	$(event.srcElement.parentElement).addClass('track-slider-playing');
+            }
+		};
+		
+		document.getElementById('interface-buttons').appendChild(outsideReferenceHolder);
+	}
+	
 	
 	testWaitIndicator();
 }
@@ -408,6 +459,9 @@
             $(element).addClass('track-slider-playing');
             $('.comment-div').removeClass('comment-box-playing');
             $('#comment-div-'+id).addClass('comment-box-playing');
+            var outsideReference = document.getElementById('outside-reference');
+            if (outsideReference != undefined)
+            $(outsideReference).removeClass('track-slider-playing');
 		}
 	};
 	
@@ -445,66 +499,12 @@
 
 function buttonSubmitClick() // TODO: Only when all songs have been played!
 {
-	var checks = specification.commonInterface.options;
+	var checks = testState.currentStateMap[testState.currentIndex].interfaces[0].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;
-					}
-				}
-			}
-		}
-	}
+	if (interfaceContext.checkHiddenAnchor() == false) {return;}
+	if (interfaceContext.checkHiddenReference() == false) {return;}
 	
 	for (var i=0; i<checks.length; i++) {
 		if (checks[i].type == 'check')
@@ -531,9 +531,15 @@
 				var checkState = interfaceContext.checkAllCommented();
 				if (checkState == false) {canContinue = false;}
 				break;
+			case 'scalerange':
+				// Check the scale is used to its full width outlined by the node
+				var checkState = interfaceContext.checkScaleRange();
+				if (checkState == false) {canContinue = false;}
+				break;
 			}
 
 		}
+		if (!canContinue) {break;}
 	}
    
     if (canContinue) {
@@ -618,6 +624,7 @@
 	for (var i=0; i<audioObjects.length; i++) 
 	{
 		var audioElement = audioEngineContext.audioObjects[i].exportXMLDOM();
+		audioElement.setAttribute('presentedId',i);
 		xmlDoc.appendChild(audioElement);
 	}
 	
--- a/core.js	Mon Jun 29 19:02:48 2015 +0100
+++ b/core.js	Mon Jun 29 19:04:40 2015 +0100
@@ -204,7 +204,7 @@
 			this.popupContent.appendChild(span);
 			this.popupContent.appendChild(document.createElement('br'));
 			var input = document.createElement('input');
-			input.type = 'number';
+			input.type = 'textarea';
 			if (node.min != null) {input.min = node.min;}
 			if (node.max != null) {input.max = node.max;}
 			if (node.step != null) {input.step = node.step;}
@@ -298,7 +298,7 @@
 				return;
 			}
 			var enteredNumber = Number(input.value);
-			if (enteredNumber == undefined) {
+			if (isNaN(enteredNumber)) {
 				alert('Please enter a valid number');
 				return;
 			}
@@ -638,6 +638,9 @@
 function interfaceXMLSave(){
 	// Create the XML string to be exported with results
 	var xmlDoc = document.createElement("BrowserEvaluationResult");
+	var projectDocument = specification.projectXML;
+	projectDocument.setAttribute('file-name',url);
+	xmlDoc.appendChild(projectDocument);
 	xmlDoc.appendChild(returnDateNode());
 	for (var i=0; i<testState.stateResults.length; i++)
 	{
@@ -892,8 +895,16 @@
 		var root = document.createElement('audioElement');
 		root.id = this.specification.id;
 		root.setAttribute('url',this.url);
-		root.appendChild(this.interfaceDOM.exportXMLDOM(this));
-		root.appendChild(this.commentDOM.exportXMLDOM(this));
+		var file = document.createElement('file');
+		file.setAttribute('sampleRate',this.buffer.sampleRate);
+		file.setAttribute('channels',this.buffer.numberOfChannels);
+		file.setAttribute('sampleCount',this.buffer.length);
+		file.setAttribute('duration',this.buffer.duration);
+		root.appendChild(file);
+		if (this.specification.type != 'outsidereference') {
+			root.appendChild(this.interfaceDOM.exportXMLDOM(this));
+			root.appendChild(this.commentDOM.exportXMLDOM(this));
+		}
 		root.appendChild(this.metric.exportXMLDOM());
 		return root;
 	};
@@ -1213,6 +1224,7 @@
 	
 	this.decode = function() {
 		// projectXML - DOM Parsed document
+		this.projectXML = projectXML.childNodes[0];
 		var setupNode = projectXML.getElementsByTagName('setup')[0];
 		this.interfaceType = setupNode.getAttribute('interface');
 		this.projectReturn = setupNode.getAttribute('projectReturn');
@@ -1246,6 +1258,22 @@
 				this.type = child.nodeName;
 				if (this.type == 'check') {
 					this.check = child.getAttribute('name');
+					if (this.check == 'scalerange') {
+						this.min = child.getAttribute('min');
+						this.max = child.getAttribute('max');
+						if (this.min == null) {this.min = 1;}
+						else if (Number(this.min) > 1 && this.min != null) {
+							this.min = Number(this.min)/100;
+						} else {
+							this.min = Number(this.min);
+						}
+						if (this.max == null) {this.max = 0;}
+						else if (Number(this.max) > 1 && this.max != null) {
+							this.max = Number(this.max)/100;
+						} else {
+							this.max = Number(this.max);
+						}
+					}
 				} else if (this.type == 'anchor' || this.type == 'reference') {
 					this.value = Number(child.textContent);
 				}
@@ -1345,7 +1373,7 @@
 			var title = DOM.getElementsByTagName('title');
 			if (title.length == 0) {this.title = null;}
 			else {this.title = title[0].textContent;}
-			
+			this.options = parent.commonInterface.options;
 			var scale = DOM.getElementsByTagName('scale');
 			this.scale = [];
 			for (var i=0; i<scale.length; i++) {
@@ -1360,27 +1388,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;}
+			this.type = xml.getAttribute('type');
+			if (this.type == null) {this.type = "normal";}
+			if (this.type == 'anchor') {this.anchor = true;}
 			else {this.anchor = false;}
-			
-			this.reference = xml.getAttribute('reference');
-			if (this.reference == 'true') {this.reference = true;}
+			if (this.type == 'reference') {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 = xml.getAttribute('marker');
+			if (this.marker == null) {this.marker = undefined;}
+			
+			if (this.anchor == true && this.marker == undefined) {
 				this.marker = anchor;
 			}
-			if (this.reference == true) {
+			else if (this.reference == true && this.marker == undefined) {
 				this.marker = reference;
 			}
+			
+			if (this.marker != undefined) {
+				this.marker = Number(this.marker);
+				if (this.marker > 1) {this.marker /= 100;}
+			}
 		};
 		
 		this.commentQuestionNode = function(xml) {
@@ -1494,13 +1522,30 @@
 		
 		this.audioElements  =[];
 		var audioElementsDOM = xml.getElementsByTagName('audioElements');
+		this.outsideReference = null;
 		for (var i=0; i<audioElementsDOM.length; i++) {
-			this.audioElements.push(new this.audioElementNode(this,audioElementsDOM[i]));
+			if (audioElementsDOM[i].getAttribute('type') == 'outsidereference') {
+				if (this.outsideReference == null) {
+					this.outsideReference = new this.audioElementNode(this,audioElementsDOM[i]);
+				} else {
+					console.log('Error only one audioelement can be of type outsidereference per audioholder');
+					this.audioElements.push(new this.audioElementNode(this,audioElementsDOM[i]));
+					console.log('Element id '+audioElementsDOM[i].id+' made into normal node');
+				}
+			} else {
+				this.audioElements.push(new this.audioElementNode(this,audioElementsDOM[i]));
+			}
+		}
+		
+		if (this.randomiseOrder) {
+			this.audioElements = randomiseOrder(this.audioElements);
 		}
 		
 		// Check only one anchor and one reference per audioNode
 		var anchor = [];
 		var reference = [];
+		this.anchorId = null;
+		this.referenceId = null;
 		for (var i=0; i<this.audioElements.length; i++)
 		{
 			if (this.audioElements[i].anchor == true) {anchor.push(i);}
@@ -1515,7 +1560,7 @@
 				this.audioElements[anchor[i]].anchor = false;
 				this.audioElements[anchor[i]].value = undefined;
 			}
-		}
+		} else {this.anchorId = anchor[0];}
 		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');
@@ -1524,7 +1569,7 @@
 				this.audioElements[reference[i]].reference = false;
 				this.audioElements[reference[i]].value = undefined;
 			}
-		}
+		} else {this.referenceId = reference[0];}
 		
 		this.commentQuestions = [];
 		var commentQuestionsDOM = xml.getElementsByTagName('CommentQuestion');
@@ -1798,7 +1843,7 @@
 	
 	this.deleteCommentBoxes = function() {
 		this.commentBoxes = [];
-	}
+	};
 	
 	this.createCommentQuestion = function(element) {
 		var node;
@@ -1888,5 +1933,41 @@
 			this.interval = undefined;
 		};
 	};
+	
+	// Global Checkers
+	// These functions will help enforce the checkers
+	this.checkHiddenAnchor = function()
+	{
+		var audioHolder = testState.currentStateMap[testState.currentIndex];
+		if (audioHolder.anchorId != null)
+		{
+			var audioObject = audioEngineContext.audioObjects[audioHolder.anchorId];
+			if (audioObject.interfaceDOM.getValue() > audioObject.specification.marker)
+			{
+				// Anchor is not set below
+				console.log('Anchor node not below marker value');
+				alert('Please keep listening');
+				return false;
+			}
+		}
+		return true;
+	};
+	
+	this.checkHiddenReference = function()
+	{
+		var audioHolder = testState.currentStateMap[testState.currentIndex];
+		if (audioHolder.referenceId != null)
+		{
+			var audioObject = audioEngineContext.audioObjects[audioHolder.referenceId];
+			if (audioObject.interfaceDOM.getValue() < audioObject.specification.marker)
+			{
+				// Anchor is not set below
+				console.log('Reference node not above marker value');
+				alert('Please keep listening');
+				return false;
+			}
+		}
+		return true;
+	};
 }
 
--- a/example_eval/project.xml	Mon Jun 29 19:02:48 2015 +0100
+++ b/example_eval/project.xml	Mon Jun 29 19:04:40 2015 +0100
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <BrowserEvalProjectDocument>
-	<setup interface="APE" projectReturn="/save" randomiseOrder='false' collectMetrics='true'>
+	<setup interface="APE" projectReturn="/save" randomiseOrder='true' collectMetrics='true'>
 		<PreTest>
 			<question id="Location" mandatory="true" boxsize="large">Please enter your location.</question>
 			<checkbox id="experience">
@@ -39,8 +39,7 @@
 			<check name="fragmentFullPlayback"/>
 			<check name="fragmentMoved"/>
 			<check name="fragmentComments"/>-->
-			<anchor>20</anchor>
-			<reference>80</reference>
+			<check name="scalerange" min="25" max="75"/>
 		</interface>
 	</setup>
 	<audioHolder id='test-0' hostURL="example_eval/" sampleRate="44100" randomiseOrder='true' repeatCount='0' loop='true' elementComments='true'>
@@ -51,21 +50,22 @@
 			<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"/>
+		<audioElements url="0.wav" id="0" type="anchor"/>
 		<audioElements url="1.wav" id="1"/>
 		<audioElements url="2.wav" id="2"/>
 		<audioElements url="3.wav" id="3"/>
-		<audioElements url="4.wav" id="4"/>
-		<audioElements url="5.wav" id="5"/>
+		<audioElements url="4.wav" id="4" type="outsidereference"/>
+		<!--<audioElements url="5.wav" id="5"/>
 		<audioElements url="6.wav" id="6"/>
-		<!--<audioElements url="7.wav" id="7"/>
+		<audioElements url="7.wav" id="7"/>
 		<audioElements url="8.wav" id="8"/>
 		<audioElements url="9.wav" id="9"/>
 		<audioElements url="10.wav" id="10"/>-->
-		<CommentQuestion id='generalExperience' type="text">General Comments</CommentQuestion>
+		<CommentQuestion id='mixingExperience' type="text">What is your mixing experience</CommentQuestion>
 		<CommentQuestion id="preference" type="radio">
-			<statement>What do you think of the song?</statement>
+			<statement>Please enter your ranking preference on this song</statement>
 			<option name="worst">Very Bad</option>
 			<option name="bad"></option>
 			<option name="OK">OK</option>
@@ -73,7 +73,7 @@
 			<option name="Great">Great</option>
 		</CommentQuestion>
 		<CommentQuestion id="preference" type="checkbox">
-			<statement>This song is... </statement>
+			<statement>Describe this song</statement>
 			<option name="bright">Bright</option>
 			<option name="punchy">Punchy</option>
 			<option name="dark">Dark</option>
@@ -92,10 +92,11 @@
             <scale position="100">Max</scale>
             <scale position="50">Middle</scale>
             <scale position="75">75</scale>
+            <scalerange min="25" max="75"/>
             <commentBoxPrefix>Comment on fragment</commentBoxPrefix>
         </interface>
-        <audioElements url="0.wav" id="0"/>
-        <audioElements url="1.wav" id="1"/>
+        <audioElements url="0.wav" id="0" type="reference" marker="80"/>
+        <audioElements url="1.wav" id="1" type="anchor" marker="20"/>
         <audioElements url="2.wav" id="2"/>
         <audioElements url="3.wav" id="3"/>
         <audioElements url="4.wav" id="4"/>