annotate core.js @ 656:0a401224660b

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