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