changeset 57:086a10f85fde Dev_main

Added Loop
author Nicholas Jillings <n.g.r.jillings@se14.qmul.ac.uk>
date Sat, 18 Apr 2015 15:36:10 +0100
parents 83198a86b3bc
children c23050ad945a
files ape.js core.js docs/ProjectSpecificationDocument.tex example_eval/project.xml
diffstat 4 files changed, 108 insertions(+), 48 deletions(-) [+]
line wrap: on
line diff
--- a/ape.js	Fri Apr 17 10:06:20 2015 +0100
+++ b/ape.js	Sat Apr 18 15:36:10 2015 +0100
@@ -322,6 +322,99 @@
 		}
 	}
 	
+	var loopPlayback = textXML.attributes['loop'];
+	if (loopPlayback != undefined)
+	{
+		loopPlayback = loopPlayback.value;
+		if (loopPlayback == 'true') {
+			loopPlayback = true;
+		} else {
+			loopPlayback = false;
+		}
+	} else {
+		loopPlayback = false;
+	}
+	audioEngineContext.loopPlayback = loopPlayback;
+	
+	// Create AudioEngine bindings for playback
+	if (loopPlayback) {
+		audioEngineContext.play = function() {
+			// Send play command to all playback buffers for synchronised start
+			// Also start timer callbacks to detect if playback has finished
+			if (this.status == 0) {
+				this.timer.startTest();
+				// First get current clock
+				var timer = audioContext.currentTime;
+				// Add 3 seconds
+				timer += 3.0;
+				// Send play to all tracks
+				for (var i=0; i<this.audioObjects.length; i++)
+				{
+					this.audioObjects[i].play(timer);
+				}
+				this.status = 1;
+			}
+		};
+		
+		audioEngineContext.stop = function() {
+			// Send stop and reset command to all playback buffers
+			if (this.status == 1) {
+				if (this.loopPlayback) {
+					for (var i=0; i<this.audioObjects.length; i++)
+					{
+						this.audioObjects[i].stop();
+					}
+				}
+				this.status = 0;
+			}
+		};
+		
+		audioEngineContext.selectedTrack = function(id) {
+			for (var i=0; i<this.audioObjects.length; i++)
+			{
+				if (id == i) {
+					this.audioObjects[i].outputGain.gain.value = 1.0;
+				} else {
+					this.audioObjects[i].outputGain.gain.value = 0.0;
+				}
+			}
+		};
+	} else {
+		audioEngineContext.play = function() {
+			// Send play command to all playback buffers for synchronised start
+			// Also start timer callbacks to detect if playback has finished
+			if (this.status == 0) {
+				this.timer.startTest();
+				this.status = 1;
+			}
+		};
+		
+		audioEngineContext.stop = function() {
+			// Send stop and reset command to all playback buffers
+			if (this.status == 1) {
+				if (this.loopPlayback) {
+					for (var i=0; i<this.audioObjects.length; i++)
+					{
+						this.audioObjects[i].stop();
+					}
+				}
+				this.status = 0;
+			}
+		};
+		
+		audioEngineContext.selectedTrack = function(id) {
+			for (var i=0; i<this.audioObjects.length; i++)
+			{
+				if (id == i) {
+					this.audioObjects[i].outputGain.gain.value = 1.0;
+					this.audioObjects[i].play(audioContext.currentTime+0.01);
+				} else {
+					this.audioObjects[i].outputGain.gain.value = 0.0;
+				}
+			}
+		};
+	}
+	
 	currentTestHolder = document.createElement('audioHolder');
 	currentTestHolder.id = textXML.id;
 	currentTestHolder.repeatCount = textXML.attributes['repeatCount'].value;
--- a/core.js	Fri Apr 17 10:06:20 2015 +0100
+++ b/core.js	Sat Apr 18 15:36:10 2015 +0100
@@ -32,6 +32,9 @@
 var preTestQuestions = document.createElement('PreTest'); // Store any pre-test question response
 var postTestQuestions = document.createElement('PostTest'); // Store any post-test question response
 
+// Add a prototype to the bufferSourceNode to reference to the audioObject holding it
+AudioBufferSourceNode.prototype.owner = undefined;
+
 window.onload = function() {
 	// Function called once the browser has loaded all files.
 	// This should perform any initial commands such as structure / loading documents
@@ -126,49 +129,14 @@
 	// Create session metrics
 	this.metric = new sessionMetrics(this);
 	
+	this.loopPlayback = false;
+	
 	// Create store for new audioObjects
 	this.audioObjects = [];
 	
-	this.play = function() {
-		// Send play command to all playback buffers for synchronised start
-		// Also start timer callbacks to detect if playback has finished
-		if (this.status == 0) {
-			this.timer.startTest();
-			// First get current clock
-			var timer = audioContext.currentTime;
-			// Add 3 seconds
-			timer += 3.0;
-			
-			// Send play to all tracks
-			for (var i=0; i<this.audioObjects.length; i++)
-			{
-				this.audioObjects[i].play(timer);
-			}
-			this.status = 1;
-		}
-	};
+	this.play = function(){};
 	
-	this.stop = function() {
-		// Send stop and reset command to all playback buffers
-		if (this.status == 1) {
-			for (var i=0; i<this.audioObjects.length; i++)
-			{
-				this.audioObjects[i].stop();
-			}
-			this.status = 0;
-		}
-	};
-	
-	this.selectedTrack = function(id) {
-		for (var i=0; i<this.audioObjects.length; i++)
-		{
-			if (id == i) {
-				this.audioObjects[i].outputGain.gain.value = 1.0;
-			} else {
-				this.audioObjects[i].outputGain.gain.value = 0.0;
-			}
-		}
-	};
+	this.stop = function(){};
 	
 	
 	this.newTrack = function(url) {
@@ -194,14 +162,13 @@
 	this.metric = new metricTracker();
 	
 	// Create a buffer and external gain control to allow internal patching of effects and volume leveling.
-	this.bufferNode = audioContext.createBufferSource();
+	this.bufferNode = undefined;
 	this.outputGain = audioContext.createGain();
 	
 	// Default output gain to be zero
 	this.outputGain.gain.value = 0.0;
 	
 	// Connect buffer to the audio graph
-	this.bufferNode.connect(this.outputGain);
 	this.outputGain.connect(audioEngineContext.outputGain);
 	
 	// the audiobuffer is not designed for multi-start playback
@@ -209,15 +176,16 @@
 	this.buffer;
 	
 	this.play = function(startTime) {
+		this.bufferNode = audioContext.createBufferSource();
+		this.bufferNode.connect(this.outputGain);
+		this.bufferNode.buffer = this.buffer;
+		this.bufferNode.loop = audioEngineContext.loopPlayback;
 		this.bufferNode.start(startTime);
 	};
 	
 	this.stop = function() {
 		this.bufferNode.stop(0);
-		this.bufferNode = audioContext.createBufferSource();
-		this.bufferNode.connect(this.outputGain);
-		this.bufferNode.buffer = this.buffer;
-		this.bufferNode.loop = true;
+		this.bufferNode = undefined;
 	};
 
 	this.constructTrack = function(url) {
@@ -232,8 +200,6 @@
 		request.onloadend = function() {
 			audioContext.decodeAudioData(request.response, function(decodedData) {
 				audioObj.buffer = decodedData;
-				audioObj.bufferNode.buffer = audioObj.buffer;
-				audioObj.bufferNode.loop = true;
 				audioObj.state = 1;
 			}, function(){
 				// Should only be called if there was an error, but sometimes gets called continuously
--- a/docs/ProjectSpecificationDocument.tex	Fri Apr 17 10:06:20 2015 +0100
+++ b/docs/ProjectSpecificationDocument.tex	Sat Apr 18 15:36:10 2015 +0100
@@ -46,6 +46,7 @@
 \item \texttt{sampleRate} - Optional, Number. If your test requires a specific sample rate, this should be set to the desired sample rate in Hertz. This does not set the browser to the correct sample rate, but forces the browser to check the sample rate matches. If this is undefined, no sample rate matching will occur.
 \item \texttt{randomiseOrder} - Optional, Boolean String. Defaults to false. Determine if the track order should be randomised. Must be true or false.
 \item \texttt{repeatCount} - Optional, Number. Defaults to 0 (ie: no repeats). The number of times a test should be repeated.
+\item \texttt{loop} - Optional, Boolean String. Defaults to false. Enable if audioElements should loop their playback or not.
 \end{itemize}
 
 \subsection{Elements}
--- a/example_eval/project.xml	Fri Apr 17 10:06:20 2015 +0100
+++ b/example_eval/project.xml	Sat Apr 18 15:36:10 2015 +0100
@@ -18,7 +18,7 @@
 			<metricEnable>elementFlagMoved</metricEnable>
 		</Metric>
 	</setup>
-	<audioHolder id='0' hostURL="example_eval/" sampleRate="44100" randomiseOrder='true' repeatCount='1'>
+	<audioHolder id='0' hostURL="example_eval/" sampleRate="44100" randomiseOrder='true' repeatCount='1' loop='true'>
 		<interface>
 			<title>Example Test Question</title>
 			<scale position="0">Min</scale>