changeset 2223:6c98ec540504

Merge branch 'master' of https://github.com/BrechtDeMan/WebAudioEvaluationTool
author www-data <www-data@sucuk.dcs.qmul.ac.uk>
date Thu, 14 Apr 2016 13:20:58 +0100
parents 77681b87c99d (current diff) 4d1aa94202e3 (diff)
children d61c33cd560a
files scripts/pythonServer-3.py scripts/pythonServer-legacy.py
diffstat 11 files changed, 572 insertions(+), 557 deletions(-) [+]
line wrap: on
line diff
--- a/core.js	Wed Apr 13 16:20:56 2016 +0100
+++ b/core.js	Thu Apr 14 13:20:58 2016 +0100
@@ -492,10 +492,59 @@
 	return Math.pow(10,gain/20.0);
 }
 
+function secondsToSamples(time,fs) {
+    return Math.round(time*fs);
+}
+
+function samplesToSeconds(samples,fs) {
+    return samples / fs;
+}
+
 function randomString(length) {
     return Math.round((Math.pow(36, length + 1) - Math.random() * Math.pow(36, length))).toString(36).slice(1);
 }
 
+function randomiseOrder(input)
+{
+	// This takes an array of information and randomises the order
+	var N = input.length;
+	
+	var inputSequence = []; // For safety purposes: keep track of randomisation
+	for (var counter = 0; counter < N; ++counter) 
+		inputSequence.push(counter) // Fill array
+	var inputSequenceClone = inputSequence.slice(0);
+	
+	var holdArr = [];
+	var outputSequence = [];
+	for (var n=0; n<N; n++)
+	{
+		// First pick a random number
+		var r = Math.random();
+		// Multiply and floor by the number of elements left
+		r = Math.floor(r*input.length);
+		// Pick out that element and delete from the array
+		holdArr.push(input.splice(r,1)[0]);
+		// Do the same with sequence
+		outputSequence.push(inputSequence.splice(r,1)[0]);
+	}
+	console.log(inputSequenceClone.toString()); // print original array to console
+	console.log(outputSequence.toString()); 	// print randomised array to console
+	return holdArr;
+}
+
+function randomSubArray(array,num) {
+    if (num > array.length) {
+        num = array.length;
+    }
+    var ret = [];
+    while (num > 0) {
+        var index = Math.floor(Math.random() * array.length);
+        ret.push( array.splice(index,1)[0] );
+        num--;
+    }
+    return ret;
+}
+
 function interfacePopup() {
 	// Creates an object to manage the popup
 	this.popup = null;
@@ -556,12 +605,14 @@
 	};
 	
 	this.hidePopup = function(){
-		this.popup.style.zIndex = -1;
-		this.popup.style.visibility = 'hidden';
-		var blank = document.getElementsByClassName('testHalt')[0];
-		blank.style.zIndex = -2;
-		blank.style.visibility = 'hidden';
-		this.buttonPrevious.style.visibility = 'inherit';
+        if (this.popup) {
+            this.popup.style.zIndex = -1;
+            this.popup.style.visibility = 'hidden';
+            var blank = document.getElementsByClassName('testHalt')[0];
+            blank.style.zIndex = -2;
+            blank.style.visibility = 'hidden';
+            this.buttonPrevious.style.visibility = 'inherit';
+        }
 	};
 	
 	this.postNode = function() {
@@ -850,43 +901,68 @@
 	this.currentStatePosition = null;
     this.currentStore = null;
 	this.initialise = function(){
-		
+        
 		// Get the data from Specification
-		var pageHolder = [];
+		var pagePool = [];
+        var pageInclude = [];
 		for (var page of specification.pages)
 		{
-            var repeat = page.repeatCount;
-            while(repeat >= 0)
-            {
-                pageHolder.push(page);
-                repeat--;
+            if (page.alwaysInclude) {
+                pageInclude.push(page);
+            } else {
+                pagePool.push(page);
             }
 		}
+        
+        // Find how many are left to get
+        var numPages = specification.poolSize;
+        if (numPages > pagePool.length) {
+            console.log("WARNING - You have specified more pages in <setup poolSize> than you have created!!");
+            numPages = specification.pages.length;
+        }
+        if (specification.poolSize == 0) {
+            numPages = specification.pages.length;
+        }
+        numPages -= pageInclude.length;
+        
+        if (numPages > 0) {
+            // Go find the rest of the pages from the pool
+            var subarr = null;
+            if (specification.randomiseOrder) {
+                // Append a random sub-array
+                subarr = randomSubArray(pagePool,numPages);
+            } else {
+                // Append the matching number
+                subarr = pagePool.slice(0,numPages);
+            }
+            pageInclude = pageInclude.concat(subarr);
+        }
+        
+        // We now have our selected pages in pageInclude array
 		if (specification.randomiseOrder)
 		{
-			pageHolder = randomiseOrder(pageHolder);
+			pageInclude = randomiseOrder(pageInclude);
 		}
-		for (var i=0; i<pageHolder.length; i++)
+		for (var i=0; i<pageInclude.length; i++)
 		{
-			if (specification.testPages <= i && specification.testPages != 0) {break;}
-            pageHolder[i].presentedId = i;
-			this.stateMap.push(pageHolder[i]);
-            storage.createTestPageStore(pageHolder[i]);
-            for (var element of pageHolder[i].audioElements) {
-                var URL = pageHolder[i].hostURL + element.url;
-                var buffer = null;
-                for (var buffObj of audioEngineContext.buffers) {
-                    if (URL == buffObj.url) {
-                        buffer = buffObj;
-                        break;
+            pageInclude[i].presentedId = i;
+			this.stateMap.push(pageInclude[i]);
+            // For each selected page, we must get the sub pool
+            if (pageInclude[i].poolSize != 0 && pageInclude[i].poolSize != pageInclude[i].audioElements.length) {
+                var elemInclude = [];
+                var elemPool = [];
+                for (var elem of pageInclude[i].audioElements) {
+                    if (elem.include || elem.type != "normal") {
+                        elemInclude.push(elem);
+                    } else {
+                        elemPool.push(elem);
                     }
                 }
-                if (buffer == null) {
-                    buffer = new audioEngineContext.bufferObj();
-                    buffer.getMedia(URL);
-                    audioEngineContext.buffers.push(buffer);
-                }
+                var numElems = pageInclude[i].poolSize - elemInclude.length;
+                pageInclude[i].audioElements = elemInclude.concat(randomSubArray(elemPool,numElems));
             }
+            storage.createTestPageStore(pageInclude[i]);
+            audioEngineContext.loadPageData(pageInclude[i]);
 		}
         
 		if (specification.preTest != null) {this.preTestSurvey = specification.preTest;}
@@ -1170,8 +1246,56 @@
                 // The buffer is already ready, trigger bufferLoaded
                 audioObject.bufferLoaded(this);
             }
+        };
+        
+        this.copyBuffer = function(preSilenceTime,postSilenceTime) {
+            // Copies the entire bufferObj.
+            if (preSilenceTime == undefined) {preSilenceTime = 0;}
+            if (postSilenceTime == undefined) {postSilenceTime = 0;}
+            var copy = new this.constructor();
+            copy.url = this.url;
+            var preSilenceSamples = secondsToSamples(preSilenceTime,this.buffer.sampleRate);
+            var postSilenceSamples = secondsToSamples(postSilenceTime,this.buffer.sampleRate);
+            var newLength = this.buffer.length+preSilenceSamples+postSilenceSamples;
+            copy.buffer = audioContext.createBuffer(this.buffer.numberOfChannels, newLength, this.buffer.sampleRate);
+            // Now we can use some efficient background copy schemes if we are just padding the end
+            if (preSilenceSamples == 0 && typeof copy.buffer.copyToChannel == "function") {
+                for (var c=0; c<this.buffer.numberOfChannels; c++) {
+                    copy.buffer.copyToChannel(this.buffer.getChannelData(c),c);
+                }
+            } else {
+                for (var c=0; c<this.buffer.numberOfChannels; c++) {
+                    var src = this.buffer.getChannelData(c);
+                    var dst = copy.buffer.getChannelData(c);
+                    for (var n=0; n<src.length; n++)
+                        dst[n+preSilenceSamples] = src[n];
+                }
+            }
+            // Copy in the rest of the buffer information
+            copy.buffer.lufs = this.buffer.lufs;
+            copy.buffer.playbackGain = this.buffer.playbackGain;
+            return copy;
         }
 	};
+    
+    this.loadPageData = function(page) {
+        // Load the URL from pages
+        for (var element of page.audioElements) {
+            var URL = page.hostURL + element.url;
+            var buffer = null;
+            for (var buffObj of this.buffers) {
+                if (URL == buffObj.url) {
+                    buffer = buffObj;
+                    break;
+                }
+            }
+            if (buffer == null) {
+                buffer = new this.bufferObj();
+                buffer.getMedia(URL);
+                this.buffers.push(buffer);
+            }
+        }
+    };
 	
 	this.play = function(id) {
 		// Start the timer and set the audioEngine state to playing (1)
@@ -1191,16 +1315,16 @@
 			this.timer.startTest();
 			if (id == undefined) {
 				id = -1;
-				console.log('FATAL - Passed id was undefined - AudioEngineContext.play(id)');
+				console.error('FATAL - Passed id was undefined - AudioEngineContext.play(id)');
 				return;
 			} else {
 				interfaceContext.playhead.setTimePerPixel(this.audioObjects[id]);
 			}
 			if (this.loopPlayback) {
-                var setTime = audioContext.currentTime;
+                var setTime = audioContext.currentTime+specification.crossFade;
 				for (var i=0; i<this.audioObjects.length; i++)
 				{
-					this.audioObjects[i].play(setTime);
+					this.audioObjects[i].play(audioContext.currentTime);
 					if (id == i) {
 						this.audioObjects[i].loopStart(setTime);
 					} else {
@@ -1208,7 +1332,7 @@
 					}
 				}
 			} else {
-                var setTime = audioContext.currentTime+0.1;
+                var setTime = audioContext.currentTime+specification.crossFade;
 				for (var i=0; i<this.audioObjects.length; i++)
 				{
 					if (i != id) {
@@ -1314,6 +1438,7 @@
 	
 	this.setSynchronousLoop = function() {
 		// Pads the signals so they are all exactly the same length
+        // Get the length of the longest signal.
 		var length = 0;
 		var maxId;
 		for (var i=0; i<this.audioObjects.length; i++)
@@ -1325,20 +1450,10 @@
 			}
 		}
 		// Extract the audio and zero-pad
-		for (var i=0; i<this.audioObjects.length; i++)
+		for (var ao of this.audioObjects)
 		{
-			var orig = this.audioObjects[i].buffer.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];}
-			}
-			hold.playbackGain = orig.playbackGain;
-			hold.lufs = orig.lufs;
-			this.audioObjects[i].buffer.buffer = hold;
+            var lengthDiff = length - ao.buffer.buffer.length;
+			ao.buffer = ao.buffer.copyBuffer(0,samplesToSeconds(lengthDiff,ao.buffer.buffer.sampleRate));
 		}
 	};
     
@@ -1387,26 +1502,13 @@
             this.buffer = callee;
             return;
         }
-		if (audioEngineContext.loopPlayback){
-			// First copy the buffer into this.buffer
-			this.buffer = new audioEngineContext.bufferObj();
-			this.buffer.url = callee.url;
-			this.buffer.buffer = audioContext.createBuffer(callee.buffer.numberOfChannels, callee.buffer.length, callee.buffer.sampleRate);
-			for (var c=0; c<callee.buffer.numberOfChannels; c++)
-			{
-				var src = callee.buffer.getChannelData(c);
-				var dst = this.buffer.buffer.getChannelData(c);
-				for (var n=0; n<src.length; n++)
-				{
-					dst[n] = src[n];
-				}
-			}
-		} else {
-			this.buffer = callee;
-		}
+        this.buffer = callee;
+        var preSilenceTime = this.specification.preSilence || this.specification.parent.preSilence || specification.preSilence || 0.0;
+        var postSilenceTime = this.specification.postSilence || this.specification.parent.postSilence || specification.postSilence || 0.0;
+        if (preSilenceTime != 0 || postSilenceTime != 0) {
+            this.buffer = callee.copyBuffer(preSilenceTime,postSilenceTime);    
+        }
 		this.state = 1;
-		this.buffer.buffer.playbackGain = callee.buffer.playbackGain;
-		this.buffer.buffer.lufs = callee.buffer.lufs;
 		var targetLUFS = this.specification.parent.loudness || specification.loudness;
 		if (typeof targetLUFS === "number")
 		{
@@ -1763,34 +1865,6 @@
 		return storeDOM;
 	};
 }
-
-function randomiseOrder(input)
-{
-	// This takes an array of information and randomises the order
-	var N = input.length;
-	
-	var inputSequence = []; // For safety purposes: keep track of randomisation
-	for (var counter = 0; counter < N; ++counter) 
-		inputSequence.push(counter) // Fill array
-	var inputSequenceClone = inputSequence.slice(0);
-	
-	var holdArr = [];
-	var outputSequence = [];
-	for (var n=0; n<N; n++)
-	{
-		// First pick a random number
-		var r = Math.random();
-		// Multiply and floor by the number of elements left
-		r = Math.floor(r*input.length);
-		// Pick out that element and delete from the array
-		holdArr.push(input.splice(r,1)[0]);
-		// Do the same with sequence
-		outputSequence.push(inputSequence.splice(r,1)[0]);
-	}
-	console.log(inputSequenceClone.toString()); // print original array to console
-	console.log(outputSequence.toString()); 	// print randomised array to console
-	return holdArr;
-}
 			
 function Interface(specificationObject) {
 	// This handles the bindings between the interface and the audioEngineContext;
--- a/example_eval/ABX_example.xml	Wed Apr 13 16:20:56 2016 +0100
+++ b/example_eval/ABX_example.xml	Thu Apr 14 13:20:58 2016 +0100
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <waet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="test-schema.xsd">
-	<setup interface="ABX" projectReturn="save.php" randomiseOrder='true' testPages="2" loudness="-23" sampleRate="44100">
+	<setup interface="ABX" projectReturn="save.php" randomiseOrder='true' poolSize="2" loudness="-23" sampleRate="44100">
 		<survey location="before">
 			<surveyentry type="question" id="sessionId" mandatory="true">
 				<statement>Please enter your name.</statement>
--- a/example_eval/AB_example.xml	Wed Apr 13 16:20:56 2016 +0100
+++ b/example_eval/AB_example.xml	Thu Apr 14 13:20:58 2016 +0100
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <waet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="test-schema.xsd">
-	<setup interface="AB" projectReturn="save.php" randomiseOrder='true' testPages="2" loudness="-23" sampleRate="44100">
+	<setup interface="AB" projectReturn="save.php" randomiseOrder='true' poolSize="2" loudness="-23" sampleRate="44100">
 		<survey location="before">
 			<surveyentry type="question" id="sessionId" mandatory="true">
 				<statement>Please enter your name.</statement>
--- a/example_eval/mushra_example.xml	Wed Apr 13 16:20:56 2016 +0100
+++ b/example_eval/mushra_example.xml	Thu Apr 14 13:20:58 2016 +0100
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <waet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="test-schema.xsd">
-	<setup interface="MUSHRA" projectReturn="save.php" randomiseOrder='true' testPages="2" loudness="-23" sampleRate="44100">
+	<setup interface="MUSHRA" projectReturn="save.php" randomiseOrder='true' poolSize="2" loudness="-23" sampleRate="44100">
         <exitText>Thank you for looking at WAET. You can modify the successful completion text as well!</exitText>
 		<survey location="before">
 			<surveyentry type="question" id="sessionId" mandatory="true">
--- a/example_eval/project.xml	Wed Apr 13 16:20:56 2016 +0100
+++ b/example_eval/project.xml	Thu Apr 14 13:20:58 2016 +0100
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <waet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="test-schema.xsd">
-	<setup interface="APE" projectReturn="save.php" randomiseOrder='true' testPages="2" loudness="-23" sampleRate="44100" calibration="true">
+	<setup interface="APE" projectReturn="save.php" randomiseOrder='true' poolSize="2" loudness="-23" sampleRate="44100" calibration="true">
 		<survey location="before">
 			<surveyentry type="question" id="sessionId" mandatory="true">
 				<statement>Please enter your name.</statement>
--- a/example_eval/radio_example.xml	Wed Apr 13 16:20:56 2016 +0100
+++ b/example_eval/radio_example.xml	Thu Apr 14 13:20:58 2016 +0100
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8" ?>
 <waet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="test-schema.xsd">
-	<setup interface="likert" projectReturn="save.php">
+	<setup interface="likert" projectReturn="save.php" crossFade="3.0">
 		<metric>
 			<metricenable>testTimer</metricenable>
 			<metricenable>elementTimer</metricenable>
@@ -18,7 +18,7 @@
 			<interfaceoption type="show" name="page-count"/>
 		</interface>
 	</setup>
-	<page id='test-0' hostURL="example_eval/" randomiseOrder='true' repeatCount='4' loop='true' showElementComments='true' loudness="-23">
+	<page id='test-0' hostURL="example_eval/" randomiseOrder='true' repeatCount='4' loop='true' showElementComments='true' loudness="-23" poolSize="3">
 		<interface>
 			<scales>
 				<scalelabel position="0">(1) Very Annoying</scalelabel>
@@ -28,8 +28,9 @@
 				<scalelabel position="100">(5) Inaudible</scalelabel>
 			</scales>
 		</interface>
-		<audioelement url="0.wav" id="track-1"/>
+		<audioelement url="0.wav" id="track-1" alwaysInclude="true"/>
 		<audioelement url="1.wav" id="track-2"/>
+        <audioelement url="3.wav" id="track-4"/>
         <audioelement url="2.wav" id="track-3" type="outside-reference"/>
 	</page>
 </waet>
--- a/scripts/pythonServer-3.py	Wed Apr 13 16:20:56 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,128 +0,0 @@
-from http.server import BaseHTTPRequestHandler, HTTPServer
-from os import walk
-from os import path
-from os import listdir
-import inspect
-import os
-import urllib as urllib2
-import pickle
-import datetime
-
-# Go to right folder. 
-scriptdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) # script directory
-os.chdir(scriptdir) # does this work?
-
-PSEUDO_PATH = 'example_eval/'
-pseudo_files = []
-for filename in listdir(PSEUDO_PATH):
-    if filename.endswith('.xml'):
-        pseudo_files.append(filename)
-
-curSaveIndex = 0;
-curFileName = 'test-0.xml'
-while(path.isfile('saves/'+curFileName)):
-	curSaveIndex += 1;
-	curFileName = 'test-'+str(curSaveIndex)+'.xml'
-
-pseudo_index = curSaveIndex % len(pseudo_files)
-
-print('URL: http://localhost:8000/index.html')
-
-def send404(s):
-	s.send_response(404)
-	s.send_header("Content-type", "text/html")
-	s.end_headers()
-	
-def processFile(s):
-	s.path = s.path.rsplit('?')
-	s.path = s.path[0]
-	s.path = s.path[1:len(s.path)]
-	st = s.path.rsplit(',')
-	lenSt = len(st)
-	fmt = st[lenSt-1].rsplit('.')
-	s.send_response(200)
-	if (fmt[1] == 'html'):
-		s.send_header("Content-type", 'text/html')
-		fileDump = open(urllib2.parse.unquote(s.path), encoding='utf-8')
-		fileBytes = bytes(fileDump.read(), "utf-8")
-		fileDump.close()
-	elif (fmt[1] == 'css'):
-		s.send_header("Content-type", 'text/css')
-		fileDump = open(urllib2.parse.unquote(s.path), encoding='utf-8')
-		fileBytes = bytes(fileDump.read(), "utf-8")
-		fileDump.close()
-	elif (fmt[1] == 'js'):
-		s.send_header("Content-type", 'application/javascript')
-		fileDump = open(urllib2.parse.unquote(s.path), encoding='utf-8')
-		fileBytes = bytes(fileDump.read(), "utf-8")
-		fileDump.close()
-	else:
-		s.send_header("Content-type", 'application/octet-stream')
-		fileDump = open(urllib2.parse.unquote(s.path), 'rb')
-		fileBytes = fileDump.read()
-		fileDump.close()
-	s.send_header("Content-Length", len(fileBytes))
-	s.end_headers()
-	s.wfile.write(fileBytes)
-	
-def saveFile(self):
-	global curFileName
-	global curSaveIndex
-	varLen = int(self.headers['Content-Length'])
-	postVars = self.rfile.read(varLen)
-	print(curFileName)
-	file = open('saves/'+curFileName,'w')
-	file.write(postVars.decode("utf-8"))
-	file.close()
-	try:
-		wbytes = os.path.getsize('saves/'+curFileName)
-	except OSError:
-		self.send_response(200)
-		self.send_header("Content-type", "text/xml")
-		self.end_headers()
-		self.wfile.write('<response state="error"><message>Could not open file</message></response>')
-	self.send_response(200)
-	self.send_header("Content-type", "text/xml")
-	self.end_headers()
-	self.wfile.write(bytes('<response state="OK"><message>OK</message><file bytes="'+str(wbytes)+'">"saves/'+curFileName+'"</file></response>','utf-8'))
-	curSaveIndex += 1
-	curFileName = 'test-'+str(curSaveIndex)+'.xml'
-
-class MyHandler(BaseHTTPRequestHandler):
-	def do_HEAD(s):
-		s.send_response(200)
-		s.send_header("Content-type", "text/html")
-		s.end_headers()
-	def do_GET(request):
-		global pseudo_index
-		global pseudo_files
-		global PSEUDO_PATH
-		if(request.client_address[0] == "127.0.0.1"):
-			if (request.path == "/favicon.ico"):
-				send404(request)
-			else:
-				if (request.path == '/'):
-					request.path = '/index.html'
-				elif (request.path == '/pseudo.xml'):
-					request.path = '/'+PSEUDO_PATH + pseudo_files[pseudo_index]
-					print(request.path)
-					pseudo_index += 1
-					pseudo_index %= len(pseudo_files)
-				processFile(request)
-		else:
-			send404(request)
-
-	def do_POST(request):
-		if(request.client_address[0] == "127.0.0.1"):
-			if (request.path == "/save" or request.path == "/save.php"):
-				saveFile(request)
-		else:
-			send404(request)
-
-def run(server_class=HTTPServer,
-        handler_class=MyHandler):
-    server_address = ('', 8000)
-    httpd = server_class(server_address, handler_class)
-    httpd.serve_forever()
-
-run()
--- a/scripts/pythonServer-legacy.py	Wed Apr 13 16:20:56 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,11 +0,0 @@
-import SimpleHTTPServer
-import SocketServer
-
-PORT = 8080
-
-Handler = SimpleHTTPServer.SimpleHTTPRequestHandler
-
-httpd = SocketServer.TCPServer(("", PORT), Handler)
-
-print "serving at port", PORT
-httpd.serve_forever()
--- a/scripts/pythonServer.py	Wed Apr 13 16:20:56 2016 +0100
+++ b/scripts/pythonServer.py	Thu Apr 14 13:20:58 2016 +0100
@@ -1,21 +1,32 @@
 #!/usr/bin/python
 
-import BaseHTTPServer
+# Detect the Python version to switch code between 2.x and 3.x
+# http://stackoverflow.com/questions/9079036/detect-python-version-at-runtime
+import sys
+
 from os import walk
 from os import path
 from os import listdir
 import inspect
 import os
-import urllib2
-import urlparse
 import pickle
 import datetime
 
+if sys.version_info[0] == 2:
+    # Version 2.x
+    import BaseHTTPServer
+    import urllib2
+    import urlparse
+elif sys.version_info[0] == 3:
+    # Version 3.x
+    from http.server import BaseHTTPRequestHandler, HTTPServer
+    import urllib as urllib2
+
 # Go to right folder. 
 scriptdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) # script directory
 os.chdir(scriptdir) # does this work?
 
-PSEUDO_PATH = 'example_eval/'
+PSEUDO_PATH = '../example_eval/'
 pseudo_files = []
 for filename in listdir(PSEUDO_PATH):
     if filename.endswith('.xml'):
@@ -23,42 +34,79 @@
 
 curSaveIndex = 0;
 curFileName = 'test-0.xml'
-while(path.isfile('saves/'+curFileName)):
-	curSaveIndex += 1;
-	curFileName = 'test-'+str(curSaveIndex)+'.xml'
+while(path.isfile('../saves/'+curFileName)):
+    curSaveIndex += 1;
+    curFileName = 'test-'+str(curSaveIndex)+'.xml'
 
 pseudo_index = curSaveIndex % len(pseudo_files)
 
-print 'URL: http://localhost:8000/index.html'
+if sys.version_info[0] == 2:
+    print 'URL: http://localhost:8000/index.html'
+elif sys.version_info[0] == 3:
+    print('URL: http://localhost:8000/index.html')
 
 def send404(s):
-	s.send_response(404)
-	s.send_header("Content-type", "text/html")
-	s.end_headers()
+    s.send_response(404)
+    s.send_header("Content-type", "text/html")
+    s.end_headers()
 	
 def processFile(s):
-	s.path = s.path.rsplit('?')
-	s.path = s.path[0]
-	s.path = s.path[1:len(s.path)]
-	st = s.path.rsplit(',')
-	lenSt = len(st)
-	fmt = st[lenSt-1].rsplit('.')
-	size = path.getsize(urllib2.unquote(s.path))
-	fileDump = open(urllib2.unquote(s.path))
-	s.send_response(200)
-	
-	if (fmt[1] == 'html'):
-		s.send_header("Content-type", 'text/html')
-	elif (fmt[1] == 'css'):
-		s.send_header("Content-type", 'text/css')
-	elif (fmt[1] == 'js'):
-		s.send_header("Content-type", 'application/javascript')
-	else:
-		s.send_header("Content-type", 'application/octet-stream')
-	s.send_header("Content-Length", size)
-	s.end_headers()
-	s.wfile.write(fileDump.read())
-	fileDump.close()
+    if sys.version_info[0] == 2:
+        s.path = s.path.rsplit('?')
+        s.path = s.path[0]
+        s.path = s.path[1:len(s.path)]
+        st = s.path.rsplit(',')
+        lenSt = len(st)
+        fmt = st[lenSt-1].rsplit('.')
+        fpath = "../"+urllib2.unquote(s.path)
+        size = path.getsize(fpath)
+        fileDump = open(fpath)
+        s.send_response(200)
+
+        if (fmt[1] == 'html'):
+            s.send_header("Content-type", 'text/html')
+        elif (fmt[1] == 'css'):
+            s.send_header("Content-type", 'text/css')
+        elif (fmt[1] == 'js'):
+            s.send_header("Content-type", 'application/javascript')
+        else:
+            s.send_header("Content-type", 'application/octet-stream')
+        s.send_header("Content-Length", size)
+        s.end_headers()
+        s.wfile.write(fileDump.read())
+        fileDump.close()
+    elif sys.version_info[0] == 3:
+        s.path = s.path.rsplit('?')
+        s.path = s.path[0]
+        s.path = s.path[1:len(s.path)]
+        st = s.path.rsplit(',')
+        lenSt = len(st)
+        fmt = st[lenSt-1].rsplit('.')
+        fpath = "../"+urllib2.unquote(s.path)
+        s.send_response(200)
+        if (fmt[1] == 'html'):
+            s.send_header("Content-type", 'text/html')
+            fileDump = open(fpath, encoding='utf-8')
+            fileBytes = bytes(fileDump.read(), "utf-8")
+            fileDump.close()
+        elif (fmt[1] == 'css'):
+            s.send_header("Content-type", 'text/css')
+            fileDump = open(fpath, encoding='utf-8')
+            fileBytes = bytes(fileDump.read(), "utf-8")
+            fileDump.close()
+        elif (fmt[1] == 'js'):
+            s.send_header("Content-type", 'application/javascript')
+            fileDump = open(fpath, encoding='utf-8')
+            fileBytes = bytes(fileDump.read(), "utf-8")
+            fileDump.close()
+        else:
+            s.send_header("Content-type", 'application/octet-stream')
+            fileDump = open(fpath, 'rb')
+            fileBytes = fileDump.read()
+            fileDump.close()
+        s.send_header("Content-Length", len(fileBytes))
+        s.end_headers()
+        s.wfile.write(fileBytes)
 
 def keygen(s):
 	reply = ""
@@ -74,36 +122,39 @@
 	s.send_header("Content-type", "application/xml")
 	s.end_headers()
 	s.wfile.write(reply)
-	file = open("saves/save-"+key+".xml",'w')
+	file = open("../saves/save-"+key+".xml",'w')
 	file.write("<waetresult key="+key+"/>")
 	file.close();
 
 def saveFile(self):
-	global curFileName
-	global curSaveIndex
-	options = self.path.rsplit('?')
-        options = options[1].rsplit('=')
-        key = options[1]
-        print key
-	varLen = int(self.headers['Content-Length'])
-	postVars = self.rfile.read(varLen)
-	print "Saving file key "+key
-	file = open('saves/save-'+key+'.xml','w')
-	file.write(postVars)
-	file.close()
-	try:
-		wbytes = os.path.getsize('saves/save-'+key+'.xml')
-	except OSError:
-		self.send_response(200)
-		self.send_header("Content-type", "text/xml")
-		self.end_headers()
-		self.wfile.write('<response state="error"><message>Could not open file</message></response>')
-	self.send_response(200)
-	self.send_header("Content-type", "text/xml")
-	self.end_headers()
-	self.wfile.write('<response state="OK"><message>OK</message><file bytes="'+str(wbytes)+'">"saves/'+curFileName+'"</file></response>')
-	curSaveIndex += 1
-	curFileName = 'test-'+str(curSaveIndex)+'.xml'
+    global curFileName
+    global curSaveIndex
+    options = self.path.rsplit('?')
+    options = options[1].rsplit('=')
+    key = options[1]
+    print key
+    varLen = int(self.headers['Content-Length'])
+    postVars = self.rfile.read(varLen)
+    if sys.version_info[0] == 2:
+        print "Saving file key "+key
+    elif sys.version_info[0] == 3:
+        print("Saving file key "+key)
+    file = open('../saves/save-'+key+'.xml','w')
+    file.write(postVars)
+    file.close()
+    try:
+        wbytes = os.path.getsize('../saves/save-'+key+'.xml')
+    except OSError:
+        self.send_response(200)
+        self.send_header("Content-type", "text/xml")
+        self.end_headers()
+        self.wfile.write('<response state="error"><message>Could not open file</message></response>')
+    self.send_response(200)
+    self.send_header("Content-type", "text/xml")
+    self.end_headers()
+    self.wfile.write('<response state="OK"><message>OK</message><file bytes="'+str(wbytes)+'">"saves/'+curFileName+'"</file></response>')
+    curSaveIndex += 1
+    curFileName = 'test-'+str(curSaveIndex)+'.xml'
 
 class MyHandler(BaseHTTPServer.BaseHTTPRequestHandler):
 	def do_HEAD(s):
@@ -137,11 +188,15 @@
 				saveFile(request)
 		else:
 			send404(request)
-
-def run(server_class=BaseHTTPServer.HTTPServer,
-        handler_class=MyHandler):
-    server_address = ('', 8000)
-    httpd = server_class(server_address, handler_class)
-    httpd.serve_forever()
+if sys.version_info[0] == 2:
+    def run(server_class=BaseHTTPServer.HTTPServer,handler_class=MyHandler):
+        server_address = ('', 8000)
+        httpd = server_class(server_address, handler_class)
+        httpd.serve_forever()
+elif sys.version_info[0] == 3:
+    def run(server_class=HTTPServer,handler_class=MyHandler):
+        server_address = ('', 8000)
+        httpd = server_class(server_address, handler_class)
+        httpd.serve_forever()
 
 run()
--- a/specification.js	Wed Apr 13 16:20:56 2016 +0100
+++ b/specification.js	Thu Apr 14 13:20:58 2016 +0100
@@ -27,7 +27,19 @@
 		}
 		var dataType = schema.getAttribute('type');
 		if (typeof dataType == "string") { dataType = dataType.substr(3);}
-		else {dataType = "string";}
+		else {
+            var rest = schema.getAllElementsByTagName("xs:restriction").concat(schema.getAllElementsByTagName("xs:enumeration"));
+            if (rest.length > 0) {
+                dataType = rest[0].getAttribute("base");
+                if (typeof dataType == "string") {
+                    dataType = dataType.substr(3);
+                } else {
+                    dataType = "string";
+                }
+            } else {
+                dataType = "string";
+            }
+        }
 		if (attribute == null)
 		{
 			return attribute;
--- a/test-schema.xsd	Wed Apr 13 16:20:56 2016 +0100
+++ b/test-schema.xsd	Thu Apr 14 13:20:58 2016 +0100
@@ -1,263 +1,275 @@
 <?xml version="1.0"?>
-<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
-  <!-- define simple elements-->
-  <xs:element name="statement" type="xs:string"/>
-  <xs:element name="metricenable" type="xs:string"/>
-  <xs:element name="title" type="xs:string"/>
-  
-  <!-- define simple attributes-->
-  <xs:attribute name="id" type="xs:ID"/>
-  <xs:attribute name="mandatory" type="xs:boolean"/>
-  <xs:attribute name="name" type="xs:string"/>
+    <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
+        <!-- define simple elements-->
+        <xs:element name="statement" type="xs:string" />
+        <xs:element name="metricenable" type="xs:string" />
+        <xs:element name="title" type="xs:string" />
 
-  <xs:attribute name="preSilence" default="0">
-      <xs:simpleType>
-          <xs:restriction base="xs:decimal">
-              <xs:minInclusive value="0.0"/>
-          </xs:restriction>
-      </xs:simpleType>
-  </xs:attribute>
-  <xs:attribute name="postSilence" default="0">
-      <xs:simpleType>
-          <xs:restriction base="xs:decimal">
-              <xs:minInclusive value="0.0"/>
-          </xs:restriction>
-      </xs:simpleType>
-  </xs:attribute>
-  
-  <!-- define complex elements-->
-  <xs:element name="waet">
-    <xs:complexType>
-      <xs:sequence>
-        <xs:element ref="setup" minOccurs="1" maxOccurs="1"/>
-        <xs:element ref="page" minOccurs="1" maxOccurs="unbounded"/>
-      </xs:sequence>
-    </xs:complexType>
-  </xs:element>
+        <!-- define simple attributes-->
+        <xs:attribute name="id" type="xs:ID" />
+        <xs:attribute name="mandatory" type="xs:boolean" />
+        <xs:attribute name="name" type="xs:string" />
+        <xs:attribute name="poolSize" type="xs:nonNegativeInteger" default="0" />
+        <xs:attribute name="alwaysInclude" type="xs:boolean" default="false" />
 
-  <xs:element name="setup">
-    <xs:complexType>
-      <xs:sequence>
-        <xs:element name="exitText" type="xs:string" minOccurs="0" maxOccurs="1"/>
-        <xs:element ref="survey" minOccurs="0" maxOccurs="2"/>
-        <xs:element ref="metric" maxOccurs="1"/>
-        <xs:element ref="interface" maxOccurs="1"/>
-      </xs:sequence>
-      <xs:attribute name="interface" type="xs:string" use="required"/>
-      <xs:attribute name="projectReturn" type="xs:string" use="optional" default=""/>
-      <xs:attribute name="randomiseOrder" type="xs:boolean" default="false"/>
-      <xs:attribute name="testPages" type="xs:nonNegativeInteger" default="0"/>
-      <xs:attribute name="loudness" type="xs:nonPositiveInteger" use="optional"/>
-      <xs:attribute name="sampleRate" type="xs:positiveInteger" use="optional"/>
-      <xs:attribute name="calibration" type="xs:boolean" default="false"/>
-      <xs:attribute ref="preSilence"/>
-      <xs:attribute ref="postSilence"/>
-    </xs:complexType>
-  </xs:element>
+        <xs:attribute name="preSilence">
+            <xs:simpleType>
+                <xs:restriction base="xs:decimal">
+                    <xs:minInclusive value="0.0" />
+                </xs:restriction>
+            </xs:simpleType>
+        </xs:attribute>
+        <xs:attribute name="postSilence">
+            <xs:simpleType>
+                <xs:restriction base="xs:decimal">
+                    <xs:minInclusive value="0.0" />
+                </xs:restriction>
+            </xs:simpleType>
+        </xs:attribute>
 
-  <xs:element name="page">
-    <xs:complexType>
-      <xs:sequence>
-        <xs:element ref="title" minOccurs="0" maxOccurs="1"/>
-        <xs:element name="commentboxprefix" type="xs:string" minOccurs="0" maxOccurs="1"/>
-        <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" minOccurs="0" maxOccurs="2"/>
-      </xs:sequence>
-      <xs:attribute ref="id" use="required"/>
-      <xs:attribute name="hostURL" type="xs:anyURI" default=""/>
-      <xs:attribute name="randomiseOrder" type="xs:boolean" default="false"/>
-      <xs:attribute name="repeatCount" type="xs:nonNegativeInteger" default="0"/>
-      <xs:attribute name="loop" type="xs:boolean" default="false"/>
-      <xs:attribute name="showElementComments" type="xs:boolean" default="false"/>
-      <xs:attribute name="loudness" type="xs:nonPositiveInteger" use="optional"/>
-      <xs:attribute name="label" use="optional" default="default">
-          <xs:simpleType>
-              <xs:restriction base="xs:string">
-                  <xs:enumeration value="default"/>
-                  <xs:enumeration value="none"/>
-                  <xs:enumeration value="number"/>
-                  <xs:enumeration value="letter"/>
-                  <xs:enumeration value="capital"/>
-              </xs:restriction>
-          </xs:simpleType>
-      </xs:attribute>
-      <xs:attribute ref="preSilence"/>
-      <xs:attribute ref="postSilence"/>
-    </xs:complexType>
-  </xs:element>
+        <!-- define complex elements-->
+        <xs:element name="waet">
+            <xs:complexType>
+                <xs:sequence>
+                    <xs:element ref="setup" minOccurs="1" maxOccurs="1" />
+                    <xs:element ref="page" minOccurs="1" maxOccurs="unbounded" />
+                </xs:sequence>
+            </xs:complexType>
+        </xs:element>
 
-  <xs:element name="metric">
-    <xs:complexType>
-      <xs:sequence>
-        <xs:element name="metricenable" type="xs:string" minOccurs="0" maxOccurs="unbounded"/>
-      </xs:sequence>
-    </xs:complexType>
-  </xs:element>
+        <xs:element name="setup">
+            <xs:complexType>
+                <xs:sequence>
+                    <xs:element name="exitText" type="xs:string" minOccurs="0" maxOccurs="1" />
+                    <xs:element ref="survey" minOccurs="0" maxOccurs="2" />
+                    <xs:element ref="metric" maxOccurs="1" />
+                    <xs:element ref="interface" maxOccurs="1" />
+                </xs:sequence>
+                <xs:attribute name="interface" type="xs:string" use="required" />
+                <xs:attribute name="projectReturn" type="xs:string" use="optional" default="" />
+                <xs:attribute name="randomiseOrder" type="xs:boolean" default="false" />
+                <xs:attribute ref="poolSize" />
+                <xs:attribute name="loudness" type="xs:nonPositiveInteger" use="optional" />
+                <xs:attribute name="sampleRate" type="xs:positiveInteger" use="optional" />
+                <xs:attribute name="calibration" type="xs:boolean" default="false" />
+                <xs:attribute name="crossFade" default="0.0">
+                    <xs:simpleType>
+                        <xs:restriction base="xs:decimal">
+                            <xs:minInclusive value="0.0" />
+                        </xs:restriction>
+                    </xs:simpleType>
+                </xs:attribute>
+                <xs:attribute ref="preSilence" />
+                <xs:attribute ref="postSilence" />
+            </xs:complexType>
+        </xs:element>
 
-  <xs:element name="interface">
-    <xs:complexType>
-      <xs:sequence>
-        <xs:element ref="title" minOccurs="0" maxOccurs="1"/>
-        <xs:element name="interfaceoption" minOccurs="0" maxOccurs="unbounded">
-          <xs:complexType>
-            <xs:attribute name="type" use="required">
-              <xs:simpleType>
-                <xs:restriction base="xs:string">
-                  <xs:enumeration value="check"/>
-                  <xs:enumeration value="show"/>
-                  <xs:enumeration value="option"/>
-                </xs:restriction>
-              </xs:simpleType>
-            </xs:attribute>
-            <xs:attribute ref="name" use="required"/>
-            <xs:attribute name="min" type="xs:decimal" use="optional"/>
-            <xs:attribute name="max" type="xs:decimal" use="optional"/>
-          </xs:complexType>
+        <xs:element name="page">
+            <xs:complexType>
+                <xs:sequence>
+                    <xs:element ref="title" minOccurs="0" maxOccurs="1" />
+                    <xs:element name="commentboxprefix" type="xs:string" minOccurs="0" maxOccurs="1" />
+                    <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" minOccurs="0" maxOccurs="2" />
+                </xs:sequence>
+                <xs:attribute ref="id" use="required" />
+                <xs:attribute name="hostURL" type="xs:anyURI" default="" />
+                <xs:attribute name="randomiseOrder" type="xs:boolean" default="false" />
+                <xs:attribute name="repeatCount" type="xs:nonNegativeInteger" default="0" />
+                <xs:attribute name="loop" type="xs:boolean" default="false" />
+                <xs:attribute name="showElementComments" type="xs:boolean" default="false" />
+                <xs:attribute name="loudness" type="xs:nonPositiveInteger" use="optional" />
+                <xs:attribute name="label" use="optional" default="default">
+                    <xs:simpleType>
+                        <xs:restriction base="xs:string">
+                            <xs:enumeration value="default" />
+                            <xs:enumeration value="none" />
+                            <xs:enumeration value="number" />
+                            <xs:enumeration value="letter" />
+                            <xs:enumeration value="capital" />
+                        </xs:restriction>
+                    </xs:simpleType>
+                </xs:attribute>
+                <xs:attribute ref="poolSize" />
+                <xs:attribute ref="alwaysInclude" />
+                <xs:attribute ref="preSilence" />
+                <xs:attribute ref="postSilence" />
+            </xs:complexType>
         </xs:element>
-        <xs:element name="scales" minOccurs="0" maxOccurs="1">
-          <xs:complexType>
-            <xs:sequence>
-              <xs:element name="scalelabel" minOccurs="0" maxOccurs="unbounded">
-                <xs:complexType>
-                  <xs:simpleContent>
-                    <xs:extension base="xs:string">
-                      <xs:attribute name="position" use="required">
-                        <xs:simpleType>
-                          <xs:restriction base="xs:nonNegativeInteger">
-                            <xs:minInclusive value="0"/>
-                            <xs:maxInclusive value="100"/>
-                          </xs:restriction>
-                        </xs:simpleType>
-                      </xs:attribute>
-                    </xs:extension>
-                  </xs:simpleContent>
-                </xs:complexType>
-              </xs:element>
-            </xs:sequence>
-          </xs:complexType>
+
+        <xs:element name="metric">
+            <xs:complexType>
+                <xs:sequence>
+                    <xs:element name="metricenable" type="xs:string" minOccurs="0" maxOccurs="unbounded" />
+                </xs:sequence>
+            </xs:complexType>
         </xs:element>
-      </xs:sequence>
-      <xs:attribute ref="name" use="optional"/>
-    </xs:complexType>
-  </xs:element>
 
-  <xs:element name="audioelement">
-    <xs:complexType>
-      <xs:attribute ref="id" use="required"/>
-      <xs:attribute name="url" type="xs:anyURI" use="required"/>
-      <xs:attribute name="gain" type="xs:decimal" default="0"/>
-      <xs:attribute ref="name"/>
-      <xs:attribute name="type" default="normal">
-        <xs:simpleType>
-          <xs:restriction base="xs:string">
-            <xs:enumeration value="normal"/>
-            <xs:enumeration value="anchor"/>
-            <xs:enumeration value="reference"/>
-            <xs:enumeration value="outside-reference"/>
-          </xs:restriction>
-        </xs:simpleType>
-      </xs:attribute>
-      <xs:attribute name="marker" use="optional">
-        <xs:simpleType>
-          <xs:restriction base="xs:nonNegativeInteger">
-            <xs:minInclusive value="0"/>
-            <xs:maxInclusive value="100"/>
-          </xs:restriction>
-        </xs:simpleType>
-      </xs:attribute>
-      <xs:attribute name="loudness" type="xs:nonPositiveInteger" use="optional"/>
-      <xs:attribute ref="preSilence"/>
-      <xs:attribute ref="postSilence"/>
-    </xs:complexType>
-  </xs:element>
+        <xs:element name="interface">
+            <xs:complexType>
+                <xs:sequence>
+                    <xs:element ref="title" minOccurs="0" maxOccurs="1" />
+                    <xs:element name="interfaceoption" minOccurs="0" maxOccurs="unbounded">
+                        <xs:complexType>
+                            <xs:attribute name="type" use="required">
+                                <xs:simpleType>
+                                    <xs:restriction base="xs:string">
+                                        <xs:enumeration value="check" />
+                                        <xs:enumeration value="show" />
+                                        <xs:enumeration value="option" />
+                                    </xs:restriction>
+                                </xs:simpleType>
+                            </xs:attribute>
+                            <xs:attribute ref="name" use="required" />
+                            <xs:attribute name="min" type="xs:decimal" use="optional" />
+                            <xs:attribute name="max" type="xs:decimal" use="optional" />
+                        </xs:complexType>
+                    </xs:element>
+                    <xs:element name="scales" minOccurs="0" maxOccurs="1">
+                        <xs:complexType>
+                            <xs:sequence>
+                                <xs:element name="scalelabel" minOccurs="0" maxOccurs="unbounded">
+                                    <xs:complexType>
+                                        <xs:simpleContent>
+                                            <xs:extension base="xs:string">
+                                                <xs:attribute name="position" use="required">
+                                                    <xs:simpleType>
+                                                        <xs:restriction base="xs:nonNegativeInteger">
+                                                            <xs:minInclusive value="0" />
+                                                            <xs:maxInclusive value="100" />
+                                                        </xs:restriction>
+                                                    </xs:simpleType>
+                                                </xs:attribute>
+                                            </xs:extension>
+                                        </xs:simpleContent>
+                                    </xs:complexType>
+                                </xs:element>
+                            </xs:sequence>
+                        </xs:complexType>
+                    </xs:element>
+                </xs:sequence>
+                <xs:attribute ref="name" use="optional" />
+            </xs:complexType>
+        </xs:element>
 
-  <xs:element name="commentquestion">
-    <xs:complexType>
-      <xs:sequence>
-        <xs:element ref="statement" minOccurs="0" maxOccurs="1"/>
-        <xs:element name="option" minOccurs="0" maxOccurs="unbounded">
-          <xs:complexType>
-            <xs:simpleContent>
-              <xs:extension base="xs:string">
-                <xs:attribute ref="name"/>
-              </xs:extension>
-            </xs:simpleContent>
-          </xs:complexType>
+        <xs:element name="audioelement">
+            <xs:complexType>
+                <xs:attribute ref="id" use="required" />
+                <xs:attribute name="url" type="xs:anyURI" use="required" />
+                <xs:attribute name="gain" type="xs:decimal" default="0" />
+                <xs:attribute ref="name" />
+                <xs:attribute name="type" default="normal">
+                    <xs:simpleType>
+                        <xs:restriction base="xs:string">
+                            <xs:enumeration value="normal" />
+                            <xs:enumeration value="anchor" />
+                            <xs:enumeration value="reference" />
+                            <xs:enumeration value="outside-reference" />
+                        </xs:restriction>
+                    </xs:simpleType>
+                </xs:attribute>
+                <xs:attribute name="marker" use="optional">
+                    <xs:simpleType>
+                        <xs:restriction base="xs:nonNegativeInteger">
+                            <xs:minInclusive value="0" />
+                            <xs:maxInclusive value="100" />
+                        </xs:restriction>
+                    </xs:simpleType>
+                </xs:attribute>
+                <xs:attribute name="loudness" type="xs:nonPositiveInteger" use="optional" />
+                <xs:attribute ref="alwaysInclude" />
+                <xs:attribute ref="preSilence" />
+                <xs:attribute ref="postSilence" />
+            </xs:complexType>
         </xs:element>
-      </xs:sequence>
-      <xs:attribute ref="id" use="optional"/>
-      <xs:attribute ref="name" use="optional"/>
-      <xs:attribute name="type" default="question">
-        <xs:simpleType>
-          <xs:restriction base="xs:string">
-            <xs:enumeration value="question"/>
-            <xs:enumeration value="radio"/>
-            <xs:enumeration value="checkbox"/>
-          </xs:restriction>
-        </xs:simpleType>
-      </xs:attribute>
-    </xs:complexType>
-  </xs:element>
 
-  <xs:element name="survey">
-    <xs:complexType>
-      <xs:sequence>
-        <xs:element name="surveyentry" minOccurs="0" maxOccurs="unbounded">
-          <xs:complexType>
-            <xs:sequence>
-              <xs:element ref="statement" minOccurs="1" maxOccurs="1"/>
-              <xs:element name="option" minOccurs="0" maxOccurs="unbounded">
-                <xs:complexType>
-                  <xs:simpleContent>
-                    <xs:extension base="xs:string">
-                      <xs:attribute ref="name"/>
-                    </xs:extension>
-                  </xs:simpleContent>
-                </xs:complexType>
-              </xs:element>
-            </xs:sequence>
-            <xs:attribute ref="id" use="required"/>
-            <xs:attribute ref="name"/>
-            <xs:attribute ref="mandatory"/>
-            <xs:attribute name="min" type="xs:decimal"/>
-            <xs:attribute name="max" type="xs:decimal"/>
-            <xs:attribute name="type" use="required">
-              <xs:simpleType>
-                <xs:restriction base="xs:string">
-                  <xs:enumeration value="statement"/>
-                  <xs:enumeration value="question"/>
-                  <xs:enumeration value="number"/>
-                  <xs:enumeration value="radio"/>
-                  <xs:enumeration value="checkbox"/>
-                </xs:restriction>
-              </xs:simpleType>
-            </xs:attribute>
-            <xs:attribute name="boxsize" default="normal">
-              <xs:simpleType>
-                <xs:restriction base="xs:string">
-                  <xs:enumeration value="normal"/>
-                  <xs:enumeration value="large"/>
-                  <xs:enumeration value="small"/>
-                  <xs:enumeration value="huge"/>
-                </xs:restriction>
-              </xs:simpleType>
-            </xs:attribute>
-          </xs:complexType>
+        <xs:element name="commentquestion">
+            <xs:complexType>
+                <xs:sequence>
+                    <xs:element ref="statement" minOccurs="0" maxOccurs="1" />
+                    <xs:element name="option" minOccurs="0" maxOccurs="unbounded">
+                        <xs:complexType>
+                            <xs:simpleContent>
+                                <xs:extension base="xs:string">
+                                    <xs:attribute ref="name" />
+                                </xs:extension>
+                            </xs:simpleContent>
+                        </xs:complexType>
+                    </xs:element>
+                </xs:sequence>
+                <xs:attribute ref="id" use="optional" />
+                <xs:attribute ref="name" use="optional" />
+                <xs:attribute name="type" default="question">
+                    <xs:simpleType>
+                        <xs:restriction base="xs:string">
+                            <xs:enumeration value="question" />
+                            <xs:enumeration value="radio" />
+                            <xs:enumeration value="checkbox" />
+                        </xs:restriction>
+                    </xs:simpleType>
+                </xs:attribute>
+            </xs:complexType>
         </xs:element>
-      </xs:sequence>
-      <xs:attribute name="location">
-        <xs:simpleType>
-          <xs:restriction base="xs:string">
-            <xs:enumeration value="before"/>
-            <xs:enumeration value="pre"/>
-            <xs:enumeration value="after"/>
-            <xs:enumeration value="post"/>
-          </xs:restriction>
-        </xs:simpleType>
-      </xs:attribute>
-    </xs:complexType>
-  </xs:element>
-  
-</xs:schema>
\ No newline at end of file
+
+        <xs:element name="survey">
+            <xs:complexType>
+                <xs:sequence>
+                    <xs:element name="surveyentry" minOccurs="0" maxOccurs="unbounded">
+                        <xs:complexType>
+                            <xs:sequence>
+                                <xs:element ref="statement" minOccurs="1" maxOccurs="1" />
+                                <xs:element name="option" minOccurs="0" maxOccurs="unbounded">
+                                    <xs:complexType>
+                                        <xs:simpleContent>
+                                            <xs:extension base="xs:string">
+                                                <xs:attribute ref="name" />
+                                            </xs:extension>
+                                        </xs:simpleContent>
+                                    </xs:complexType>
+                                </xs:element>
+                            </xs:sequence>
+                            <xs:attribute ref="id" use="required" />
+                            <xs:attribute ref="name" />
+                            <xs:attribute ref="mandatory" />
+                            <xs:attribute name="min" type="xs:decimal" />
+                            <xs:attribute name="max" type="xs:decimal" />
+                            <xs:attribute name="type" use="required">
+                                <xs:simpleType>
+                                    <xs:restriction base="xs:string">
+                                        <xs:enumeration value="statement" />
+                                        <xs:enumeration value="question" />
+                                        <xs:enumeration value="number" />
+                                        <xs:enumeration value="radio" />
+                                        <xs:enumeration value="checkbox" />
+                                    </xs:restriction>
+                                </xs:simpleType>
+                            </xs:attribute>
+                            <xs:attribute name="boxsize" default="normal">
+                                <xs:simpleType>
+                                    <xs:restriction base="xs:string">
+                                        <xs:enumeration value="normal" />
+                                        <xs:enumeration value="large" />
+                                        <xs:enumeration value="small" />
+                                        <xs:enumeration value="huge" />
+                                    </xs:restriction>
+                                </xs:simpleType>
+                            </xs:attribute>
+                        </xs:complexType>
+                    </xs:element>
+                </xs:sequence>
+                <xs:attribute name="location">
+                    <xs:simpleType>
+                        <xs:restriction base="xs:string">
+                            <xs:enumeration value="before" />
+                            <xs:enumeration value="pre" />
+                            <xs:enumeration value="after" />
+                            <xs:enumeration value="post" />
+                        </xs:restriction>
+                    </xs:simpleType>
+                </xs:attribute>
+            </xs:complexType>
+        </xs:element>
+
+    </xs:schema>