annotate core.js @ 1655:21e887ed1e63

audioElement id's tracked from input to output for randomisation.
author Nicholas Jillings <nickjillings@users.noreply.github.com>
date Fri, 10 Apr 2015 17:18:45 +0100
parents 7911cd38f1a1
children 77c974fd7e60
rev   line source
nickjillings@1642 1 /**
nickjillings@1642 2 * core.js
nickjillings@1642 3 *
nickjillings@1642 4 * Main script to run, calls all other core functions and manages loading/store to backend.
nickjillings@1642 5 * Also contains all global variables.
nickjillings@1642 6 */
nickjillings@1642 7
nickjillings@1642 8
nickjillings@1642 9 /*
nickjillings@1642 10 *
nickjillings@1642 11 * WARNING!!!
nickjillings@1642 12 *
nickjillings@1642 13 * YOU ARE VIEWING THE DEV VERSION. THERE IS NO GUARANTEE THIS WILL BE FULLY FUNCTIONAL
nickjillings@1642 14 *
nickjillings@1642 15 * WARNING!!!
nickjillings@1642 16 *
nickjillings@1642 17 */
nickjillings@1642 18
nickjillings@1642 19
nickjillings@1642 20
nickjillings@1642 21
nickjillings@1642 22 /* create the web audio API context and store in audioContext*/
nickjillings@1643 23 var audioContext; // Hold the browser web audio API
nickjillings@1643 24 var projectXML; // Hold the parsed setup XML
nickjillings@1648 25
nickjillings@1654 26 var testXMLSetups = []; // Hold the parsed test instances
nickjillings@1654 27 var testResultsHolders =[]; // Hold the results from each test for publishing to XML
nickjillings@1655 28 var currentTrackOrder = []; // Hold the current XML tracks in their (randomised) order
nickjillings@1643 29 var currentTestHolder; // Hold an intermediate results during test - metrics
nickjillings@1643 30 var audioEngineContext; // The custome AudioEngine object
nickjillings@1643 31 var projectReturn; // Hold the URL for the return
nickjillings@1643 32 var preTestQuestions = document.createElement('PreTest'); // Store any pre-test question response
nickjillings@1643 33 var postTestQuestions = document.createElement('PostTest'); // Store any post-test question response
nickjillings@1642 34
nickjillings@1642 35 window.onload = function() {
nickjillings@1642 36 // Function called once the browser has loaded all files.
nickjillings@1642 37 // This should perform any initial commands such as structure / loading documents
nickjillings@1642 38
nickjillings@1642 39 // Create a web audio API context
nickjillings@1642 40 // Fixed for cross-browser support
nickjillings@1642 41 var AudioContext = window.AudioContext || window.webkitAudioContext;
nickjillings@1642 42 audioContext = new AudioContext;
nickjillings@1642 43
nickjillings@1642 44 // Create the audio engine object
nickjillings@1642 45 audioEngineContext = new AudioEngine();
nickjillings@1642 46 };
nickjillings@1642 47
nickjillings@1642 48 function loadProjectSpec(url) {
nickjillings@1642 49 // Load the project document from the given URL, decode the XML and instruct audioEngine to get audio data
nickjillings@1642 50 // If url is null, request client to upload project XML document
nickjillings@1642 51 var r = new XMLHttpRequest();
nickjillings@1642 52 r.open('GET',url,true);
nickjillings@1642 53 r.onload = function() {
nickjillings@1642 54 loadProjectSpecCallback(r.response);
nickjillings@1642 55 };
nickjillings@1642 56 r.send();
nickjillings@1642 57 };
nickjillings@1642 58
nickjillings@1642 59 function loadProjectSpecCallback(response) {
nickjillings@1642 60 // Function called after asynchronous download of XML project specification
nickjillings@1642 61 var decode = $.parseXML(response);
nickjillings@1642 62 projectXML = $(decode);
nickjillings@1642 63
nickjillings@1642 64 // Now extract the setup tag
nickjillings@1642 65 var xmlSetup = projectXML.find('setup');
nickjillings@1642 66 // Detect the interface to use and load the relevant javascripts.
nickjillings@1642 67 var interfaceType = xmlSetup[0].attributes['interface'];
nickjillings@1642 68 var interfaceJS = document.createElement('script');
nickjillings@1642 69 interfaceJS.setAttribute("type","text/javascript");
nickjillings@1642 70 if (interfaceType.value == 'APE') {
nickjillings@1642 71 interfaceJS.setAttribute("src","ape.js");
nickjillings@1643 72
nickjillings@1643 73 // APE comes with a css file
nickjillings@1643 74 var css = document.createElement('link');
nickjillings@1643 75 css.rel = 'stylesheet';
nickjillings@1643 76 css.type = 'text/css';
nickjillings@1643 77 css.href = 'ape.css';
nickjillings@1643 78
nickjillings@1643 79 document.getElementsByTagName("head")[0].appendChild(css);
nickjillings@1642 80 }
nickjillings@1642 81 document.getElementsByTagName("head")[0].appendChild(interfaceJS);
nickjillings@1642 82 }
nickjillings@1642 83
nickjillings@1642 84 function createProjectSave(destURL) {
nickjillings@1642 85 // Save the data from interface into XML and send to destURL
nickjillings@1642 86 // If destURL is null then download XML in client
nickjillings@1642 87 // Now time to render file locally
nickjillings@1642 88 var xmlDoc = interfaceXMLSave();
nickjillings@1642 89 if (destURL == "null" || destURL == undefined) {
nickjillings@1642 90 var parent = document.createElement("div");
nickjillings@1642 91 parent.appendChild(xmlDoc);
nickjillings@1642 92 var file = [parent.innerHTML];
nickjillings@1642 93 var bb = new Blob(file,{type : 'application/xml'});
nickjillings@1642 94 var dnlk = window.URL.createObjectURL(bb);
nickjillings@1642 95 var a = document.createElement("a");
nickjillings@1642 96 a.hidden = '';
nickjillings@1642 97 a.href = dnlk;
nickjillings@1642 98 a.download = "save.xml";
nickjillings@1642 99 a.textContent = "Save File";
nickjillings@1642 100
nickjillings@1642 101 var submitDiv = document.getElementById('download-point');
nickjillings@1642 102 submitDiv.appendChild(a);
nickjillings@1642 103 }
nickjillings@1653 104 return submitDiv;
nickjillings@1642 105 }
nickjillings@1642 106
nickjillings@1642 107 function AudioEngine() {
nickjillings@1642 108
nickjillings@1642 109 // Create two output paths, the main outputGain and fooGain.
nickjillings@1642 110 // Output gain is default to 1 and any items for playback route here
nickjillings@1642 111 // Foo gain is used for analysis to ensure paths get processed, but are not heard
nickjillings@1642 112 // because web audio will optimise and any route which does not go to the destination gets ignored.
nickjillings@1642 113 this.outputGain = audioContext.createGain();
nickjillings@1642 114 this.fooGain = audioContext.createGain();
nickjillings@1642 115 this.fooGain.gain = 0;
nickjillings@1642 116
nickjillings@1642 117 // Use this to detect playback state: 0 - stopped, 1 - playing
nickjillings@1642 118 this.status = 0;
nickjillings@1642 119
nickjillings@1642 120 // Connect both gains to output
nickjillings@1642 121 this.outputGain.connect(audioContext.destination);
nickjillings@1642 122 this.fooGain.connect(audioContext.destination);
nickjillings@1642 123
nickjillings@1642 124 // Create store for new audioObjects
nickjillings@1642 125 this.audioObjects = [];
nickjillings@1642 126
nickjillings@1642 127 this.play = function() {
nickjillings@1642 128 // Send play command to all playback buffers for synchronised start
nickjillings@1642 129 // Also start timer callbacks to detect if playback has finished
nickjillings@1642 130 if (this.status == 0) {
nickjillings@1642 131 // First get current clock
nickjillings@1642 132 var timer = audioContext.currentTime;
nickjillings@1642 133 // Add 3 seconds
nickjillings@1642 134 timer += 3.0;
nickjillings@1642 135
nickjillings@1642 136 // Send play to all tracks
nickjillings@1642 137 for (var i=0; i<this.audioObjects.length; i++)
nickjillings@1642 138 {
nickjillings@1642 139 this.audioObjects[i].play(timer);
nickjillings@1642 140 }
nickjillings@1642 141 this.status = 1;
nickjillings@1642 142 }
nickjillings@1642 143 };
nickjillings@1642 144
nickjillings@1642 145 this.stop = function() {
nickjillings@1642 146 // Send stop and reset command to all playback buffers
nickjillings@1642 147 if (this.status == 1) {
nickjillings@1642 148 for (var i=0; i<this.audioObjects.length; i++)
nickjillings@1642 149 {
nickjillings@1642 150 this.audioObjects[i].stop();
nickjillings@1642 151 }
nickjillings@1642 152 this.status = 0;
nickjillings@1642 153 }
nickjillings@1642 154 };
nickjillings@1642 155
nickjillings@1642 156 this.selectedTrack = function(id) {
nickjillings@1642 157 for (var i=0; i<this.audioObjects.length; i++)
nickjillings@1642 158 {
nickjillings@1642 159 if (id == i) {
nickjillings@1642 160 this.audioObjects[i].outputGain.gain.value = 1.0;
nickjillings@1642 161 } else {
nickjillings@1642 162 this.audioObjects[i].outputGain.gain.value = 0.0;
nickjillings@1642 163 }
nickjillings@1642 164 }
nickjillings@1642 165 };
nickjillings@1642 166
nickjillings@1642 167
nickjillings@1642 168 this.newTrack = function(url) {
nickjillings@1642 169 // Pull data from given URL into new audio buffer
nickjillings@1642 170 // URLs must either be from the same source OR be setup to 'Access-Control-Allow-Origin'
nickjillings@1642 171
nickjillings@1642 172 // Create the audioObject with ID of the new track length;
nickjillings@1642 173 audioObjectId = this.audioObjects.length
nickjillings@1642 174 this.audioObjects[audioObjectId] = new audioObject(audioObjectId);
nickjillings@1642 175
nickjillings@1642 176 // AudioObject will get track itself.
nickjillings@1642 177 this.audioObjects[audioObjectId].constructTrack(url);
nickjillings@1642 178 };
nickjillings@1642 179
nickjillings@1642 180 }
nickjillings@1642 181
nickjillings@1642 182 function audioObject(id) {
nickjillings@1642 183 // The main buffer object with common control nodes to the AudioEngine
nickjillings@1642 184
nickjillings@1642 185 this.id = id;
nickjillings@1642 186 this.state = 0; // 0 - no data, 1 - ready
nickjillings@1642 187 this.url = null; // Hold the URL given for the output back to the results.
nickjillings@1642 188
nickjillings@1642 189 // Create a buffer and external gain control to allow internal patching of effects and volume leveling.
nickjillings@1642 190 this.bufferNode = audioContext.createBufferSource();
nickjillings@1642 191 this.outputGain = audioContext.createGain();
nickjillings@1642 192
nickjillings@1642 193 // Default output gain to be zero
nickjillings@1642 194 this.outputGain.gain.value = 0.0;
nickjillings@1642 195
nickjillings@1642 196 // Connect buffer to the audio graph
nickjillings@1642 197 this.bufferNode.connect(this.outputGain);
nickjillings@1642 198 this.outputGain.connect(audioEngineContext.outputGain);
nickjillings@1642 199
nickjillings@1642 200 // the audiobuffer is not designed for multi-start playback
nickjillings@1642 201 // When stopeed, the buffer node is deleted and recreated with the stored buffer.
nickjillings@1642 202 this.buffer;
nickjillings@1642 203
nickjillings@1642 204 this.play = function(startTime) {
nickjillings@1642 205 this.bufferNode.start(startTime);
nickjillings@1642 206 };
nickjillings@1642 207
nickjillings@1642 208 this.stop = function() {
nickjillings@1642 209 this.bufferNode.stop(0);
nickjillings@1642 210 this.bufferNode = audioContext.createBufferSource();
nickjillings@1642 211 this.bufferNode.connect(this.outputGain);
nickjillings@1642 212 this.bufferNode.buffer = this.buffer;
nickjillings@1642 213 this.bufferNode.loop = true;
nickjillings@1642 214 };
nickjillings@1642 215
nickjillings@1642 216 this.constructTrack = function(url) {
nickjillings@1642 217 var request = new XMLHttpRequest();
nickjillings@1642 218 this.url = url;
nickjillings@1642 219 request.open('GET',url,true);
nickjillings@1642 220 request.responseType = 'arraybuffer';
nickjillings@1642 221
nickjillings@1642 222 var audioObj = this;
nickjillings@1642 223
nickjillings@1642 224 // Create callback to decode the data asynchronously
nickjillings@1642 225 request.onloadend = function() {
nickjillings@1642 226 audioContext.decodeAudioData(request.response, function(decodedData) {
nickjillings@1642 227 audioObj.buffer = decodedData;
nickjillings@1642 228 audioObj.bufferNode.buffer = audioObj.buffer;
nickjillings@1642 229 audioObj.bufferNode.loop = true;
nickjillings@1642 230 audioObj.state = 1;
nickjillings@1642 231 }, function(){
nickjillings@1642 232 // Should only be called if there was an error, but sometimes gets called continuously
nickjillings@1642 233 // Check here if the error is genuine
nickjillings@1642 234 if (audioObj.state == 0 || audioObj.buffer == undefined) {
nickjillings@1642 235 // Genuine error
nickjillings@1642 236 console.log('FATAL - Error loading buffer on '+audioObj.id);
nickjillings@1642 237 }
nickjillings@1642 238 });
nickjillings@1642 239 };
nickjillings@1642 240 request.send();
nickjillings@1642 241 };
nickjillings@1642 242
nickjillings@1642 243 }