changeset 1342:397e96ee781a

Better loudness calculation. Buffer ready not called until after loudness calculation to avoid NaNs on gain. <survey> nodes do not need to be present, no survey then no node. Added example boilerplate interface with all required functions and brief descriptions.
author Nicholas Jillings <nickjillings@users.noreply.github.com>
date Wed, 13 Jan 2016 10:31:31 +0000
parents bbb6462cf446
children 9af89ccadb59
files core.js interfaces/blank.js interfaces/mushra.js loudness.js test-schema.xsd
diffstat 5 files changed, 98 insertions(+), 24 deletions(-) [+]
line wrap: on
line diff
--- a/core.js	Wed Jan 13 09:34:46 2016 +0000
+++ b/core.js	Wed Jan 13 10:31:31 2016 +0000
@@ -730,12 +730,14 @@
 			this.initialise();
 		}
 		if (this.stateIndex == -1) {
+			this.stateIndex++;
 			console.log('Starting test...');
 			if (this.preTestSurvey != null)
 			{
 				popup.initState(this.preTestSurvey,storage.globalPreTest);
+			} else {
+				this.advanceState();
 			}
-			this.stateIndex++;
 		} else if (this.stateIndex == this.stateMap.length)
 		{
 			// All test pages complete, post test
@@ -860,6 +862,17 @@
 		this.xmlRequest = new XMLHttpRequest();
 		this.xmlRequest.parent = this;
 		this.users = [];
+		this.ready = function()
+		{
+			for (var i=0; i<this.users.length; i++)
+			{
+				this.users[i].state = 1;
+				if (this.users[i].interfaceDOM != null)
+				{
+					this.users[i].bufferLoaded(this);
+				}
+			}
+		};
 		this.getMedia = function(url) {
 			this.url = url;
 			this.xmlRequest.open('GET',this.url,true);
@@ -871,15 +884,8 @@
 			this.xmlRequest.onloadend = function() {
 				audioContext.decodeAudioData(bufferObj.xmlRequest.response, function(decodedData) {
 					bufferObj.buffer = decodedData;
-					for (var i=0; i<bufferObj.users.length; i++)
-					{
-						bufferObj.users[i].state = 1;
-						if (bufferObj.users[i].interfaceDOM != null)
-						{
-							bufferObj.users[i].bufferLoaded(bufferObj);
-						}
-					}
-					calculateLoudness(bufferObj.buffer,"I");
+					calculateLoudness(bufferObj,"I");
+					
 				}, function(){
 					// Should only be called if there was an error, but sometimes gets called continuously
 					// Check here if the error is genuine
@@ -2827,8 +2833,8 @@
 	
 	this.initialise = function()
 	{
-		this.globalPreTest = new this.surveyNode(this,this.root,specification.preTest);
-		this.globalPostTest = new this.surveyNode(this,this.root,specification.postTest);
+		if (specification.preTest != undefined){this.globalPreTest = new this.surveyNode(this,this.root,specification.preTest);}
+		if (specification.postTest != undefined){this.globalPostTest = new this.surveyNode(this,this.root,specification.postTest);}
 	};
 	
 	this.createTestPageStore = function(specification)
@@ -2897,8 +2903,8 @@
 		this.XMLDOM = this.parent.document.createElement('page');
 		this.XMLDOM.setAttribute('id',specification.id);
 		this.XMLDOM.setAttribute('presentedId',specification.presentedId);
-		this.preTest = new this.parent.surveyNode(parent,this.XMLDOM,specification.preTest);
-		this.postTest = new this.parent.surveyNode(parent,this.XMLDOM,specification.postTest);
+		if (specification.preTest != undefined){this.preTest = new this.surveyNode(this,this.root,this.specification.preTest);}
+		if (specification.postTest != undefined){this.postTest = new this.surveyNode(this,this.root,this.specification.postTest);}
 		
 		// Add any page metrics
 		var page_metric = this.parent.document.createElement('metric');
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/interfaces/blank.js	Wed Jan 13 10:31:31 2016 +0000
@@ -0,0 +1,67 @@
+/**
+ * WAET Blank Template
+ * Use this to start building your custom interface
+ */
+
+// 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
+};
+
+function loadTest(page)
+{
+	// Called each time a new test page is to be build. The page specification node is the only item passed in
+}
+
+function interfaceObject()
+{
+	// An example node, you can make this however you want for each audioElement.
+	// However, every audioObject (audioEngineContext.audioObject) MUST have an interface object with the following
+	// You attach them by calling audioObject.bindInterface( )
+	this.enable = function()
+	{
+		// This is used to tell the interface object that playback of this node is ready
+	};
+	this.updateLoading = function(progress)
+	{
+		// progress is a value from 0 to 100 indicating the current download state of media files
+	};
+	this.getValue = function()
+	{
+		// Return the current value of the object. If there is no value, return 0
+	};
+	this.getPresentedId = function()
+	{
+		// Return the presented ID of the object. For instance, the APE has sliders starting from 0. Whilst AB has alphabetical scale
+	};
+	this.canMove = function()
+	{
+		// Return either true or false if the interface object can be moved. AB / Reference cannot, whilst sliders can and therefore have a continuous scale.
+		// These are checked primarily if the interface check option 'fragmentMoved' is enabled.
+	};
+	this.exportXMLDOM = function(audioObject) {
+		// Called by the audioObject holding this element to export the interface <value> 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.
+		
+	};
+};
+
+function resizeWindow(event)
+{
+	// Called on every window resize event, use this to scale your page properly
+}
+
+function pageXMLSave(store, pageSpecification)
+{
+	// MANDATORY
+	// Saves a specific test page
+	// You can use this space to add any extra nodes to your XML <audioHolder> saves
+	// Get the current <page> 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
--- a/interfaces/mushra.js	Wed Jan 13 09:34:46 2016 +0000
+++ b/interfaces/mushra.js	Wed Jan 13 10:31:31 2016 +0000
@@ -166,18 +166,18 @@
 			var node = interfaceContext.createCommentBox(audioObject);
 		
 			// Create a slider per track
-			audioObject.bindInterface(new sliderObject(audioObject,label));
+			var sliderObj = new sliderObject(audioObject,label);
 			
 			if (typeof audioHolderObject.initialPosition === "number")
 			{
 				// Set the values
-				audioObject.interfaceDOM.slider.value = audioHolderObject.initalPosition;
+				sliderObj.slider.value = audioHolderObject.initalPosition;
 			} else {
 				// Distribute it randomnly
-				audioObject.interfaceDOM.slider.value = Math.random();
+				sliderObj.slider.value = Math.random();
 			}
-			sliderBox.appendChild(audioObject.interfaceDOM.holder);
-			audioObject.metric.initialise(audioObject.interfaceDOM.slider.value);
+			sliderBox.appendChild(sliderObj.holder);
+			audioObject.bindInterface(sliderObj);
 			label += 1;
 		}
         
--- a/loudness.js	Wed Jan 13 09:34:46 2016 +0000
+++ b/loudness.js	Wed Jan 13 10:31:31 2016 +0000
@@ -34,7 +34,7 @@
 	}
 	if (offlineContext == undefined)
 	{
-		offlineContext = new OfflineAudioContext(buffer.numberOfChannels, buffer.length, buffer.sampleRate);
+		offlineContext = new OfflineAudioContext(buffer.buffer.numberOfChannels, buffer.buffer.length, buffer.buffer.sampleRate);
 	}
 	// Create the required filters
 	var KFilter = offlineContext.createBiquadFilter();
@@ -48,7 +48,7 @@
 	HPFilter.frequency.value = 60;
 	// copy Data into the process buffer
 	var processSource = offlineContext.createBufferSource();
-	processSource.buffer = buffer;
+	processSource.buffer = buffer.buffer;
 	
 	processSource.connect(KFilter);
 	KFilter.connect(HPFilter);
@@ -100,7 +100,8 @@
 				}
 			}
 			var overallRelLoudness = calculateOverallLoudnessFromChannelBlocks(relgateEnergy);
-			buffer.lufs =  overallRelLoudness;
+			buffer.buffer.lufs =  overallRelLoudness;
+			buffer.ready();
 		}
 	};
 	offlineContext.startRendering();
--- a/test-schema.xsd	Wed Jan 13 09:34:46 2016 +0000
+++ b/test-schema.xsd	Wed Jan 13 10:31:31 2016 +0000
@@ -23,7 +23,7 @@
   <xs:element name="setup">
     <xs:complexType>
       <xs:sequence>
-        <xs:element ref="survey" maxOccurs="2"/>
+        <xs:element ref="survey" minOccurs="0" maxOccurs="2"/>
         <xs:element ref="metric" maxOccurs="1"/>
         <xs:element ref="interface" maxOccurs="1"/>
       </xs:sequence>
@@ -44,7 +44,7 @@
         <xs:element ref="interface" minOccurs="1" maxOccurs="unbounded"/>
         <xs:element ref="audioelement" minOccurs="1" maxOccurs="unbounded"/>
         <xs:element ref="commentquestion" minOccurs="0" maxOccurs="unbounded"/>
-        <xs:element ref="survey" maxOccurs="2"/>
+        <xs:element ref="survey" minOccurs="0" maxOccurs="2"/>
       </xs:sequence>
       <xs:attribute ref="id" use="required"/>
       <xs:attribute name="hostURL" type="xs:anyURI" use="required"/>