Mercurial > hg > webaudioevaluationtool
comparison analyse.html @ 767:d7f2912bf487
test_create: Specification Node handles complete XML to DOM and DOM to XML conversions
author | Nicholas Jillings <n.g.r.jillings@se14.qmul.ac.uk> |
---|---|
date | Fri, 04 Dec 2015 18:34:04 +0000 |
parents | |
children | babd5366db49 888292c88c33 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 767:d7f2912bf487 |
---|---|
1 <!DOCTYPE html> | |
2 <html lang="en"> | |
3 <head> | |
4 <meta charset="utf-8"> | |
5 | |
6 <!-- Always force latest IE rendering engine (even in intranet) & Chrome Frame | |
7 Remove this if you use the .htaccess --> | |
8 <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> | |
9 | |
10 <title>Analysis</title> | |
11 <meta name="description" content="Show results from subjective evaluation"> | |
12 <meta name="author" content="Brecht De Man"> | |
13 | |
14 <script type="text/javascript" src="https://www.google.com/jsapi"></script> | |
15 <script type="text/javascript"> | |
16 // To aid 'one-page set-up' all scripts and CSS must be included directly in this file! | |
17 | |
18 //google.load("visualization", "1", {packages:["corechart"]}); | |
19 | |
20 /************* | |
21 * SETUP * | |
22 *************/ | |
23 // folder where to find the XML files | |
24 xmlFileFolder = "analysis_test"; | |
25 // array of XML files | |
26 var xmlFiles = ['McG-A-2013-09.xml', 'McG-A-2014-03.xml', 'McG-A-2014-12.xml', 'McG-B-2013-09.xml', | |
27 'McG-B-2014-03.xml', 'McG-B-2014-12.xml', 'McG-C-2013-09.xml', 'McG-C-2014-03.xml', 'McG-C-2014-12.xml', | |
28 'McG-D-2013-09.xml', 'McG-D-2014-03.xml', 'McG-D-2014-12.xml', 'McG-E-2013-09.xml', 'McG-E-2014-03.xml', | |
29 'McG-E-2014-12.xml', 'McG-F-2013-09.xml', 'McG-F-2014-03.xml', 'McG-F-2014-12.xml', 'McG-G-2014-03.xml', | |
30 'McG-G-2014-12.xml', 'McG-H-2013-09.xml', 'McG-H-2014-03.xml', 'McG-H-2014-12.xml', 'McG-I-2013-09.xml', | |
31 'McG-I-2014-03.xml', 'McG-J-2013-09.xml', 'McG-J-2014-03.xml', 'McG-K-2013-09.xml', 'McG-K-2014-03.xml', | |
32 'McG-L-2013-09.xml', 'McG-L-2014-03.xml', 'McG-M-2013-09.xml', 'McG-M-2014-03.xml', 'McG-N-2013-09.xml', | |
33 'McG-N-2014-03.xml', 'McG-O-2013-09.xml', 'McG-O-2014-03.xml', 'McG-P-2013-09.xml', 'McG-P-2014-03.xml', | |
34 'McG-pro1-2013-09.xml', 'McG-pro1-2014-03.xml', 'McG-pro1-2014-12.xml', 'McG-pro2-2013-09.xml', | |
35 'McG-pro2-2014-03.xml', 'McG-pro2-2014-12.xml', 'McG-Q-2014-12.xml', 'McG-R-2014-12.xml', | |
36 'McG-S-2014-12.xml', 'McG-subA-2013-09.xml', 'McG-subA-2014-03.xml', 'McG-subB-2014-03.xml', | |
37 'McG-subB-2014-12.xml', 'McG-subC-2013-09.xml', 'McG-subC-2014-03.xml', 'McG-subC-2014-12.xml', | |
38 'McG-subD-2013-09.xml', 'McG-subD-2014-12.xml', 'McG-subE-2014-12.xml', 'McG-subG-2014-12.xml', | |
39 'McG-subH-2013-09.xml', 'McG-T-2014-12.xml', 'McG-U-2014-12.xml', 'McG-V-2014-12.xml', | |
40 'McG-W-2014-12.xml', 'McG-X-2014-12.xml', 'MG1-2013-09.xml', 'MG2-2013-09.xml', 'MG3-2013-09.xml', | |
41 'MG4-2013-09.xml', 'MG5-2013-09.xml', 'MG6-2013-09.xml', 'MG7-2013-09.xml', 'MG8-2013-09.xml', | |
42 'MG9-2013-09.xml', 'QM-1-1.xml', 'QM-1-2.xml', 'QM-10-1.xml', 'QM-11-1.xml', 'QM-11-2.xml', 'QM-12-1.xml', 'QM-12-2.xml', | |
43 'QM-13-1.xml', 'QM-14-1.xml', 'QM-15-1.xml', 'QM-16-1.xml', 'QM-17-1.xml', 'QM-18-1.xml', 'QM-18-2.xml', | |
44 'QM-18-3.xml', 'QM-19-1.xml', 'QM-2-1.xml', 'QM-2-2.xml', 'QM-2-3.xml', 'QM-20-1.xml', 'QM-20-2.xml', | |
45 'QM-20-3.xml', 'QM-21-1.xml', 'QM-21-2.xml', 'QM-3-1.xml', 'QM-3-2.xml', 'QM-3-3.xml', 'QM-4-1.xml', 'QM-5-1.xml', | |
46 'QM-5-2.xml', 'QM-6-1.xml', 'QM-6-2.xml', 'QM-7-1.xml', 'QM-7-2.xml', 'QM-8-1.xml', 'QM-9-1.xml', | |
47 'PXL-L1.xml','PXL-L2.xml','PXL-L3.xml','PXL-L4.xml','PXL-L5.xml','PXL-S1.xml','PXL-S2.xml','PXL-S3.xml', | |
48 'PXL-S4.xml','PXL-S5.xml','PXL-S6.xml','PXL-S7.xml','PXL-pro.xml','DU-A1.xml','DU-A2.xml','DU-B1.xml', | |
49 'DU-B2.xml','DU-C1.xml','DU-C2.xml','DU-D1.xml','DU-D2.xml','DU-E1.xml','DU-F1.xml','DU-F2.xml','DU-G1.xml', | |
50 'DU-G2.xml','DU-H1.xml','DU-H2.xml','DU-I2.xml','DU-J2.xml','DU-K1.xml','DU-K2.xml','DU-L1.xml','DU-L2.xml', | |
51 'DU-M1.xml','DU-M2.xml','DU-N1.xml','DU-O1.xml','DU-O2.xml','DU-P1.xml','DU-P2.xml','DU-Q1.xml','DU-Q2.xml', | |
52 'DU-R1.xml','DU-R2.xml','DU-S1.xml','DU-S2.xml','DU-T1.xml','DU-T2.xml','DU-U1.xml','DU-U2.xml','DU-U3.xml']; | |
53 //['QM-1-1.xml','QM-2-1.xml','QM-2-2.xml','QM-2-3.xml','QM-3-1.xml','QM-3-2.xml','QM-4-1.xml','QM-5-1.xml','QM-5-2.xml','QM-6-1.xml','QM-6-2.xml','QM-7-1.xml','QM-7-2.xml','QM-8-1.xml','QM-9-1.xml','QM-10-1.xml','QM-11-1.xml','QM-12-1.xml','QM-12-2.xml','QM-13-1.xml','QM-14-1.xml','QM-15-1.xml','QM-16-1.xml','QM-17-1.xml','QM-18-1.xml','QM-18-2.xml','QM-18-3.xml','QM-19-1.xml','QM-20-1.xml','QM-20-2.xml','QM-20-3.xml','QM-21-1.xml','QM-21-2.xml']; | |
54 //['McG-A-2014-03.xml','McG-B-2014-03.xml','McG-C-2014-03.xml','McG-D-2014-03.xml','McG-E-2014-03.xml','McG-F-2014-03.xml','McG-G-2014-03.xml','McG-H-2014-03.xml']; | |
55 | |
56 //TODO: make retrieval of file names automatic / drag files on here | |
57 | |
58 /**************** | |
59 * VARIABLES * | |
60 ****************/ | |
61 | |
62 // Counters | |
63 // How many files, audioholders, audioelementes and statements annotated (don't count current one) | |
64 var numberOfFiles = -1; | |
65 var numberOfaudioholders = -1; | |
66 var numberOfaudioelementes = -1; | |
67 var numberOfStatements = -1; | |
68 var numberOfSkippedComments = 0; | |
69 | |
70 // Object arrays | |
71 var fileNameArray = []; | |
72 var subjectArray = []; | |
73 var audioholderArray = []; | |
74 var audioelementArray = []; | |
75 | |
76 // End of (file, audioholder, audioelement) flags | |
77 var newFile = true; | |
78 var newAudioHolder = true; | |
79 var newAudioElement = true; | |
80 | |
81 var fileCounter = 0; // file index | |
82 var audioholderCounter=0; // audioholder index (current XML file) | |
83 var audioelementCounter=0; // audioelement index (current audioholder) | |
84 var statementNumber=0; // total number of statements | |
85 | |
86 var root; // root of XML file | |
87 var commentInFull = ''; // full comment | |
88 | |
89 var playAudio = true; // whether corresponding audio should be played back | |
90 | |
91 // // Measuring time | |
92 // var lastTimeMeasured = -1; // | |
93 // var durationLastAnnotation = -1; // duration of last annotation | |
94 // var timeArray = []; | |
95 // var MIN_TIME = 1.0; // minimum time counted as significant | |
96 // var measurementPaused = false; // whether time measurement is paused | |
97 // var timeInBuffer = 0; // | |
98 | |
99 var topLevel; | |
100 window.onload = function() { | |
101 // Initialise page | |
102 topLevel = document.getElementById('topLevelBody'); | |
103 var setup = document.createElement('div'); | |
104 setup.id = 'setupTagDiv'; | |
105 loadAllFiles(); | |
106 printSurveyData() | |
107 //makePlots(); | |
108 // measure time at this point: | |
109 lastTimeMeasured = new Date().getTime(); // in milliseconds | |
110 }; | |
111 | |
112 // Assert function | |
113 function assert(condition, message) { | |
114 if (!condition) { | |
115 message = message || "Assertion failed"; | |
116 if (typeof Error !== "undefined") { | |
117 throw new Error(message); | |
118 } | |
119 throw message; // Fallback | |
120 } | |
121 } | |
122 | |
123 function median(values) { // TODO: replace code by '50th percentile' - should be the same? | |
124 values.sort( function(a,b) {return a - b;} ); | |
125 var half = Math.floor(values.length/2); | |
126 if(values.length % 2) | |
127 return values[half]; | |
128 else | |
129 return (values[half-1] + values[half]) / 2.0; | |
130 } | |
131 | |
132 function percentile(values, n) { | |
133 values.sort( function(a,b) {return a - b;} ); | |
134 // get ordinal rank | |
135 var rank = Math.min(Math.floor(values.length*n/100), values.length-1); | |
136 return values[rank]; | |
137 } | |
138 | |
139 /*********************** | |
140 * TIME MEASUREMENT * | |
141 ************************/ | |
142 | |
143 // measure time since last time this function was called | |
144 function timeSinceLastCall() { | |
145 // current time | |
146 var currentTime = new Date().getTime(); | |
147 // calculate time difference | |
148 var timeDifference = currentTime - lastTimeMeasured + timeInBuffer; | |
149 // clear buffer (for pausing) | |
150 timeInBuffer = 0; | |
151 // remember last measured time | |
152 lastTimeMeasured = currentTime; | |
153 return timeDifference; | |
154 } | |
155 | |
156 // pause time measurement | |
157 function pauseTimeMeasurement() { | |
158 // UN-PAUSE | |
159 if (measurementPaused) { // already paused | |
160 // button shows 'pause' again | |
161 document.getElementById('pauseButton').innerHTML = 'Pause'; | |
162 // toggle state | |
163 measurementPaused = false; | |
164 // resume time measurement | |
165 lastTimeMeasured = new Date().getTime(); // reset time, discard time while paused | |
166 } else { // PAUSE | |
167 // button shows 'resume' | |
168 document.getElementById('pauseButton').innerHTML = 'Resume'; | |
169 // toggle state | |
170 measurementPaused = true; | |
171 // pause time measurement | |
172 timeInBuffer = timeSinceLastCall(); | |
173 } | |
174 } | |
175 | |
176 // show elapsed time on interface | |
177 function showTimeElapsedInSeconds() { | |
178 // if paused: un-pause | |
179 if (measurementPaused) { | |
180 pauseTimeMeasurement(); | |
181 } | |
182 | |
183 // time of last annotation | |
184 var lastAnnotationTime = timeSinceLastCall()/1000; | |
185 document.getElementById('timeDisplay').innerHTML = lastAnnotationTime.toFixed(2); | |
186 // average time over last ... annotations | |
187 var avgAnnotationTime; | |
188 var numberOfElementsToAverage = | |
189 document.getElementById('numberOfTimeAverages').value; | |
190 if (isPositiveInteger(numberOfElementsToAverage)) { | |
191 avgAnnotationTime = | |
192 calculateAverageTime(lastAnnotationTime, | |
193 Number(numberOfElementsToAverage)); | |
194 } else { | |
195 // change text field content to 'ALL' | |
196 document.getElementById('numberOfTimeAverages').value = 'ALL'; | |
197 avgAnnotationTime = calculateAverageTime(lastAnnotationTime, -1); | |
198 } | |
199 document.getElementById('timeAverageDisplay').innerHTML = avgAnnotationTime.toFixed(2); | |
200 } | |
201 | |
202 // auxiliary function: is string a positive integer? | |
203 // http://stackoverflow.com/questions/10834796/... | |
204 // validate-that-a-string-is-a-positive-integer | |
205 function isPositiveInteger(str) { | |
206 var n = ~~Number(str); | |
207 return String(n) === str && n >= 0; | |
208 } | |
209 | |
210 // calculate average time | |
211 function calculateAverageTime(newTimeMeasurementInSeconds,numberOfPoints) { | |
212 // append last measurement time to time array, if significant | |
213 if (newTimeMeasurementInSeconds > MIN_TIME) { | |
214 timeArray.push(newTimeMeasurementInSeconds); | |
215 } | |
216 // average over last N elements of this array | |
217 if (numberOfPoints < 0 || numberOfPoints>=timeArray.length) { // calculate average over all | |
218 var sum = 0; | |
219 for (var i = 0; i < timeArray.length; i++) { | |
220 sum += timeArray[i]; | |
221 } | |
222 averageOfTimes = sum/timeArray.length; | |
223 } else { // calculate average over specified number of times measured last | |
224 var sum = 0; | |
225 for (var i = timeArray.length-numberOfPoints; i < timeArray.length; i++) { | |
226 sum += timeArray[i]; | |
227 } | |
228 averageOfTimes = sum/numberOfPoints; | |
229 } | |
230 return averageOfTimes; | |
231 } | |
232 | |
233 | |
234 /******************************** | |
235 * PLAYBACK OF AUDIO * | |
236 ********************************/ | |
237 | |
238 //PLAYaudioelement | |
239 // Keep track of whether audio should be played | |
240 function playFlagChanged(){ | |
241 playAudio = playFlag.checked; // global variable | |
242 | |
243 if (!playAudio){ // if audio needs to stop | |
244 audio.pause(); // stop audio - if anything is playing | |
245 currently_playing = ''; // back to empty string so playaudioelement knows nothing's playing | |
246 } | |
247 } | |
248 | |
249 // audioholder that's currently playing | |
250 var currently_playing_audioholder = ''; // at first: empty string | |
251 var currently_playing_audioelement = ''; | |
252 var audio; | |
253 | |
254 // Play audioelement of audioholder if available, from start or from same position | |
255 function playaudioelement(audioholderName, audioelementerName){ | |
256 if (playAudio) { // if enabled | |
257 // get corresponding file from folder | |
258 var file_location = 'audio/'+audioholderName + '/' + audioelementerName + '.mp3'; // fixed path and file name format | |
259 | |
260 // if not available, show error/warning message | |
261 //TODO ... | |
262 | |
263 // if nothing playing yet, start playing | |
264 if (currently_playing_audioholder == ''){ // signal that nothing is playing | |
265 //playSound(audioBuffer); | |
266 audio = new Audio(file_location); | |
267 audio.loop = true; // loop when end is reached | |
268 audio.play(); | |
269 currently_playing_audioholder = audioholderName; | |
270 currently_playing_audioelement = audioelementerName; | |
271 } else if (currently_playing_audioholder != audioholderName) { | |
272 // if different audioholder playing, stop that and start playing | |
273 audio.pause(); // stop audio | |
274 audio = new Audio(file_location); // load new file | |
275 audio.loop = true; // loop when end is reached | |
276 audio.play(); // play audio from the start | |
277 currently_playing_audioholder = audioholderName; | |
278 currently_playing_audioelement = audioelementerName; | |
279 } else if (currently_playing_audioelement != audioelementerName) { | |
280 // if same audioholder playing, start playing from where it left off | |
281 skipTime = audio.currentTime; // time to skip to | |
282 audio.pause(); // stop audio | |
283 audio = new Audio(file_location); | |
284 audio.addEventListener('loadedmetadata', function() { | |
285 this.currentTime = skipTime; | |
286 console.log('Loaded '+audioholderName+'-'+audioelementerName+', playing from '+skipTime); | |
287 }, false); // skip to same time when audio is loaded! | |
288 audio.loop = true; // loop when end is reached | |
289 audio.play(); // play from that time | |
290 audio.currentTime = skipTime; | |
291 currently_playing_audioholder = audioholderName; | |
292 currently_playing_audioelement = audioelementerName; | |
293 } | |
294 // if same audioelement playing: keep on playing (i.e. do nothing) | |
295 } | |
296 } | |
297 | |
298 /******************** | |
299 * READING FILES * | |
300 ********************/ | |
301 | |
302 // Read necessary data from XML file | |
303 function readXML(xmlFileName){ | |
304 if (window.XMLHttpRequest) | |
305 {// code for IE7+, Firefox, Chrome, Opera, Safari | |
306 xmlhttp=new XMLHttpRequest(); | |
307 } | |
308 else | |
309 {// code for IE6, IE5 | |
310 xmlhttp=new ActiveXObject("Microsoft.XMLHTTP"); | |
311 } | |
312 xmlhttp.open("GET",xmlFileName,false); | |
313 xmlhttp.send(); | |
314 return xmlhttp.responseXML; | |
315 } | |
316 | |
317 // go over all files and compute relevant statistics | |
318 function loadAllFiles() { | |
319 // retrieve information from XMLs | |
320 | |
321 for (fileIndex = 0; fileIndex < xmlFiles.length; fileIndex++) { | |
322 xmlFileName = xmlFileFolder+"/"+xmlFiles[fileIndex]; | |
323 xml = readXML(xmlFileName); | |
324 if (xml != null) { // if file exists | |
325 // append file name to array of file names | |
326 fileNameArray.push(xmlFiles[fileIndex]); | |
327 | |
328 // get root of XML file | |
329 root = xml.getElementsByTagName('browserevaluationresult')[0]; | |
330 | |
331 // get subject ID, add to array if not already there | |
332 pretest = root.getElementsByTagName('pretest')[0]; | |
333 subjectID = pretest.getElementsByTagName('comment')[0]; | |
334 if (subjectID){ | |
335 if (subjectID.getAttribute('id')!='sessionId') { // warning in console when not available | |
336 console.log(xmlFiles[fileIndex]+': no SessionID available'); | |
337 } | |
338 if (subjectArray.indexOf(subjectID.textContent) == -1) { // if not already in array | |
339 subjectArray.push(subjectID.textContent); // append to array | |
340 } | |
341 } | |
342 | |
343 // go over all audioholders, add to array if not already there | |
344 audioholderNodes = root.getElementsByTagName('audioholder'); | |
345 // go over audioholderNodes and append audioholder name when not present yet | |
346 for (audioholderIndex = 0; audioholderIndex < audioholderNodes.length; audioholderIndex++) { | |
347 audioholderName = audioholderNodes[audioholderIndex].getAttribute('id'); | |
348 if (audioholderArray.indexOf(audioholderName) == -1) { // if not already in array | |
349 audioholderArray.push(audioholderName); // append to array | |
350 } | |
351 // within each audioholder, go over all audioelement IDs, add to array if not already there | |
352 audioelementNodes = audioholderNodes[audioholderIndex].getElementsByTagName('audioelement'); | |
353 for (audioelementIndex = 0; audioelementIndex < audioelementNodes.length; audioelementIndex++) { | |
354 audioelementName = audioelementNodes[audioelementIndex].getAttribute('id'); | |
355 if (audioelementArray.indexOf(audioelementName) == -1) { // if not already in array | |
356 audioelementArray.push(audioelementName); // append to array | |
357 } | |
358 } | |
359 } | |
360 // count occurrences of each audioholder | |
361 // ... | |
362 } | |
363 else { | |
364 console.log('XML file '+xmlFileName+' not found.'); | |
365 } | |
366 } | |
367 | |
368 // sort alphabetically | |
369 fileNameArray.sort(); | |
370 subjectArray.sort(); | |
371 audioholderArray.sort(); | |
372 audioelementArray.sort(); | |
373 | |
374 // display all information in HTML | |
375 // show XML file folder | |
376 document.getElementById('xmlFileFolder_span').innerHTML = "\""+xmlFileFolder+"/\""; | |
377 // show number of files | |
378 document.getElementById('numberOfFiles_span').innerHTML = fileNameArray.length; | |
379 // show list of subject names | |
380 document.getElementById('subjectArray_span').innerHTML = subjectArray.toString(); | |
381 // show list of audioholders | |
382 document.getElementById('audioholderArray_span').innerHTML = audioholderArray.toString(); | |
383 // show list of audioelementes | |
384 document.getElementById('audioelementArray_span').innerHTML = audioelementArray.toString(); | |
385 } | |
386 | |
387 function printSurveyData() { | |
388 // print some fields from the survey for different people | |
389 | |
390 // go over all XML files | |
391 for (fileIndex = 0; fileIndex < xmlFiles.length; fileIndex++) { | |
392 xmlFileName = xmlFileFolder+"/"+xmlFiles[fileIndex]; | |
393 xml = readXML(xmlFileName); | |
394 // make a div | |
395 var div = document.createElement('div'); | |
396 document.body.appendChild(div); | |
397 div.id = 'div_survey_'+xmlFileName; | |
398 div.style.width = '1100px'; | |
399 //div.style.height = '350px'; | |
400 | |
401 // title for that div (subject id) | |
402 document.getElementById('div_survey_'+xmlFileName).innerHTML = '<h2>'+xmlFileName+'</h2>'; | |
403 | |
404 // which songs did they do | |
405 if (xml != null) { // if file exists | |
406 // get root of XML file | |
407 root = xml.getElementsByTagName('browserevaluationresult')[0]; | |
408 // go over all audioholders | |
409 // document.getElementById('div_survey_'+xmlFileName).innerHTML += '<strong>Audioholders: </strong>'; | |
410 // audioholderNodes = root.getElementsByTagName('audioholder'); | |
411 // for (audioholderIndex = 0; audioholderIndex < audioholderNodes.length-1; audioholderIndex++) { | |
412 // document.getElementById('div_survey_'+xmlFileName).innerHTML += audioholderNodes[audioholderIndex].getAttribute('id')+', '; | |
413 // } | |
414 // document.getElementById('div_survey_'+xmlFileName).innerHTML += audioholderNodes[audioholderNodes.length-1].getAttribute('id'); | |
415 | |
416 // survey responses (each if available) | |
417 // get posttest node for total test | |
418 childNodes = root.childNodes; | |
419 posttestnode = null; | |
420 for (idx = 0; idx < childNodes.length; idx++){ | |
421 if (childNodes[childNodes.length-idx-1].tagName == 'posttest') { | |
422 posttestnode = childNodes[childNodes.length-idx-1]; | |
423 break; | |
424 } | |
425 } | |
426 | |
427 // mix experience | |
428 if (posttestnode) { | |
429 posttestcomments = posttestnode.getElementsByTagName('comment'); | |
430 for (idx=0; idx < posttestcomments.length; idx++){ | |
431 commentsToPrint = ['generalExperience', 'interfaceExperience']; | |
432 idAttribute = posttestcomments[idx].getAttribute('id'); | |
433 if (commentsToPrint.indexOf(idAttribute) >= 0) { // if exists? | |
434 document.getElementById('div_survey_'+xmlFileName).innerHTML += '<br><strong>'+idAttribute+': </strong>'+posttestcomments[idx].textContent; | |
435 } | |
436 } | |
437 } | |
438 } | |
439 } | |
440 } | |
441 | |
442 function makePlots() { //TODO: split into different functions | |
443 // TEMPORARY | |
444 makeTimeline(xmlFileFolder+"/"+xmlFiles[7]); | |
445 | |
446 // create value array | |
447 var ratings = []; // 3D matrix of ratings (audioholder, audioelement, subject) | |
448 for (audioholderIndex = 0; audioholderIndex < audioholderArray.length; audioholderIndex++) { | |
449 ratings.push([]); | |
450 for (audioelementIndex = 0; audioelementIndex < audioelementArray.length; audioelementIndex++) { | |
451 ratings[audioholderIndex].push([]); | |
452 } | |
453 } | |
454 | |
455 // go over all XML files | |
456 for (fileIndex = 0; fileIndex < xmlFiles.length; fileIndex++) { | |
457 xmlFileName = xmlFileFolder+"/"+xmlFiles[fileIndex]; | |
458 xml = readXML(xmlFileName); | |
459 if (xml != null) { // if file exists | |
460 // get root of XML file | |
461 root = xml.getElementsByTagName('browserevaluationresult')[0]; | |
462 // go over all audioholders | |
463 audioholderNodes = root.getElementsByTagName('audioholder'); | |
464 for (audioholderIndex = 0; audioholderIndex < audioholderNodes.length; audioholderIndex++) { | |
465 audioholderName = audioholderNodes[audioholderIndex].getAttribute('id'); | |
466 audioelementNodes = audioholderNodes[audioholderIndex].getElementsByTagName('audioelement'); | |
467 // go over all audioelements | |
468 for (audioelementIndex = 0; audioelementIndex < audioelementNodes.length; audioelementIndex++) { | |
469 audioelementName = audioelementNodes[audioelementIndex].getAttribute('id'); | |
470 // get value | |
471 var value = audioelementNodes[audioelementIndex].getElementsByTagName("value")[0].textContent; | |
472 if (value) { // if not empty, null, undefined... | |
473 ratingValue = parseFloat(value); | |
474 // add to matrix at proper position | |
475 aHidx = audioholderArray.indexOf(audioholderName); | |
476 aEidx = audioelementArray.indexOf(audioelementName); | |
477 ratings[aHidx][aEidx].push(ratingValue); | |
478 } | |
479 } | |
480 } | |
481 | |
482 // go over all audioholders | |
483 | |
484 // go over all audioelements within audioholder, see if present in idMatrix, add if not | |
485 // add corresponding rating to 'ratings', at position corresponding with position in idMatrix | |
486 } | |
487 } | |
488 | |
489 for (audioholderIndex = 0; audioholderIndex < audioholderArray.length; audioholderIndex++) { | |
490 audioholderName = audioholderArray[audioholderIndex]; // for this song | |
491 tickArray = [] | |
492 | |
493 raw_data = [['SubjectID', 'Rating']]; | |
494 audioElIdx = 0; | |
495 for (audioelementIndex = 0; audioelementIndex<ratings[audioholderIndex].length; audioelementIndex++){ | |
496 if (ratings[audioholderIndex][audioelementIndex].length>0) { | |
497 audioElIdx++; // increase if not empty | |
498 // make tick label | |
499 tickArray.push({v:audioElIdx, f: audioelementArray[audioelementIndex]}); | |
500 } | |
501 for (subject = 0; subject<ratings[audioholderIndex][audioelementIndex].length; subject++){ | |
502 // add subject-value pair for each subject | |
503 raw_data.push([audioElIdx, ratings[audioholderIndex][audioelementIndex][subject]]); | |
504 } | |
505 } | |
506 | |
507 // create plot (one per song) | |
508 var data = google.visualization.arrayToDataTable(raw_data); | |
509 | |
510 var options = { | |
511 title: audioholderName, | |
512 hAxis: {title: 'audioelement ID', minValue: 0, maxValue: audioElIdx+1, | |
513 ticks: tickArray}, | |
514 vAxis: {title: 'Rating', minValue: 0, maxValue: 1}, | |
515 seriesType: 'scatter', | |
516 legend: 'none' | |
517 }; | |
518 var div = document.createElement('div'); | |
519 document.body.appendChild(div); | |
520 div.id = 'div_'+audioholderName; | |
521 div.style.width = '1100px'; | |
522 div.style.height = '350px'; | |
523 var chart = new google.visualization.ComboChart(document.getElementById('div_'+audioholderName)); | |
524 chart.draw(data, options); | |
525 | |
526 // box plots | |
527 var div = document.createElement('div'); | |
528 document.body.appendChild(div); | |
529 div.id = 'div_box_'+audioholderName; | |
530 div.style.width = '1100px'; | |
531 div.style.height = '350px'; | |
532 // Get median, percentiles, maximum and minimum; outliers. | |
533 pctl25 = []; | |
534 pctl75 = []; | |
535 med = []; | |
536 min = []; | |
537 max = []; | |
538 outlierArray = []; | |
539 max_n_outliers = 0; // maximum number of outliers for one audioelement | |
540 for (audioelementIndex = 0; audioelementIndex<ratings[audioholderIndex].length; audioelementIndex++){ | |
541 med.push(median(ratings[audioholderIndex][audioelementIndex])); // median | |
542 pctl25.push(percentile(ratings[audioholderIndex][audioelementIndex], 25)); // 25th percentile | |
543 pctl75.push(percentile(ratings[audioholderIndex][audioelementIndex], 75)); // 75th percentile | |
544 IQR = pctl75[pctl75.length-1]-pctl25[pctl25.length-1]; | |
545 // outliers: range of values which is above pctl75+1.5*IQR or below pctl25-1.5*IQR | |
546 outliers = []; | |
547 rest = []; | |
548 for (idx = 0; idx<ratings[audioholderIndex][audioelementIndex].length; idx++){ | |
549 if (ratings[audioholderIndex][audioelementIndex][idx] > pctl75[pctl75.length-1]+1.5*IQR || | |
550 ratings[audioholderIndex][audioelementIndex][idx] < pctl25[pctl25.length-1]-1.5*IQR){ | |
551 outliers.push(ratings[audioholderIndex][audioelementIndex][idx]); | |
552 } | |
553 else { | |
554 rest.push(ratings[audioholderIndex][audioelementIndex][idx]); | |
555 } | |
556 } | |
557 outlierArray.push(outliers); | |
558 max_n_outliers = Math.max(max_n_outliers, outliers.length); // update max mber | |
559 // max: maximum value which is not outlier | |
560 max.push(Math.max.apply(null, rest)); | |
561 // min: minimum value which is not outlier | |
562 min.push(Math.min.apply(null, rest)); | |
563 } | |
564 | |
565 // Build data array | |
566 boxplot_data = [['ID', 'Span', '', '', '', 'Median']]; | |
567 for (idx = 0; idx < max_n_outliers; idx++) { | |
568 boxplot_data[0].push('Outlier'); | |
569 } | |
570 for (audioelementIndex = 0; audioelementIndex<ratings[audioholderIndex].length; audioelementIndex++){ | |
571 if (ratings[audioholderIndex][audioelementIndex].length>0) { // if rating array not empty for this audioelement | |
572 data_array = [ | |
573 audioelementArray[audioelementIndex], // name | |
574 min[audioelementIndex], // minimum | |
575 pctl75[audioelementIndex], | |
576 pctl25[audioelementIndex], | |
577 max[audioelementIndex], // maximum | |
578 med[audioelementIndex] | |
579 ]; | |
580 for (idx = 0; idx < max_n_outliers; idx++) { | |
581 if (idx<outlierArray[audioelementIndex].length){ | |
582 data_array.push(outlierArray[audioelementIndex][idx]); | |
583 } | |
584 else { | |
585 data_array.push(null); | |
586 } | |
587 } | |
588 boxplot_data.push(data_array); | |
589 } | |
590 } | |
591 | |
592 // Create and populate the data table. | |
593 var data = google.visualization.arrayToDataTable(boxplot_data); | |
594 // Create and draw the visualization. | |
595 var ac = new google.visualization.ComboChart(document.getElementById('div_box_'+audioholderName)); | |
596 ac.draw(data, { | |
597 title : audioholderName, | |
598 //width: 600, | |
599 //height: 400, | |
600 vAxis: {title: "Rating"}, | |
601 hAxis: {title: "audioelement ID"}, | |
602 seriesType: "line", | |
603 pointSize: 5, | |
604 lineWidth: 0, | |
605 colors: ['black'], | |
606 series: { 0: {type: "candlesticks", color: 'blue'}, // box plot shape | |
607 1: {type: "line", pointSize: 10, lineWidth: 0, color: 'red' } }, // median | |
608 legend: 'none' | |
609 }); | |
610 } | |
611 } | |
612 | |
613 function makeTimeline(xmlFileName){ // WIP | |
614 // Based on the XML file name, take time data and plot playback and marker movements | |
615 | |
616 // read XML file and check if exists | |
617 xml = readXML(xmlFileName); | |
618 if (!xml) { // if file does not exist | |
619 console.log('XML file '+xml+'does not exist. ('+xmlFileName+')') | |
620 return; // do nothing; exit function | |
621 } | |
622 // get root of XML file | |
623 root = xml.getElementsByTagName('browserevaluationresult')[0]; | |
624 | |
625 audioholder_time = 0; | |
626 previous_audioholder_time = 0; // time spent before current audioholder | |
627 time_offset = 0; // test starts at zero | |
628 | |
629 // go over all audioholders | |
630 audioholderNodes = root.getElementsByTagName('audioholder'); | |
631 for (audioholderIndex = 0; audioholderIndex < audioholderNodes.length; audioholderIndex++) { | |
632 audioholderName = audioholderNodes[audioholderIndex].getAttribute('id'); | |
633 if (!audioholderName) { | |
634 console.log('audioholder name is empty; go to next one. ('+xmlFileName+')'); | |
635 break; | |
636 } | |
637 | |
638 // subtract total audioholder length from subsequent audioholder event times | |
639 audioholder_children = audioholderNodes[audioholderIndex].childNodes; | |
640 foundIt = false; | |
641 console.log(audioholder_children[2].getElementsByTagName("metricResult")) // not working! | |
642 for (idx = 0; idx<audioholder_children.length; idx++) { // go over children | |
643 | |
644 if (audioholder_children[idx].getElementsByTagName('metricResult').length) { | |
645 console.log(audioholder_children[idx].getElementsByTagName('metricResult')[0]); | |
646 if (audioholder_children[idx].getElementsByTagName('metricResult')[0].getAttribute('id') == "testTime"){ | |
647 audioholder_time = parseFloat(audioholder_children[idx].getElementsByTagName('metricResult')[0].textContent); | |
648 console.log(audioholder_time); | |
649 foundIt = true; | |
650 } | |
651 } | |
652 } | |
653 if (!foundIt) { | |
654 console.log("Skipping audioholder without total time specified from "+xmlFileName+"."); // always hitting this | |
655 break; | |
656 } | |
657 | |
658 audioelementNodes = audioholderNodes[audioholderIndex].getElementsByTagName('audioelement'); | |
659 | |
660 // make div | |
661 | |
662 // draw chart | |
663 | |
664 // legend with audioelement names | |
665 } | |
666 } | |
667 | |
668 </script> | |
669 | |
670 | |
671 | |
672 <style> | |
673 div { | |
674 padding: 2px; | |
675 margin-top: 2px; | |
676 margin-bottom: 2px; | |
677 } | |
678 div.head{ | |
679 margin-left: 10px; | |
680 border: black; | |
681 border-width: 2px; | |
682 border-style: solid; | |
683 } | |
684 div.attrib{ | |
685 margin-left:25px; | |
686 border: black; | |
687 border-width: 2px; | |
688 border-style: dashed; | |
689 margin-bottom: 10px; | |
690 } | |
691 div#headerMatter{ | |
692 background-color: #FFFFCC; | |
693 } | |
694 div#currentStatement{ | |
695 font-size:3.0em; | |
696 font-weight: bold; | |
697 | |
698 } | |
699 div#debugDisplay { | |
700 color: #CCCCCC; | |
701 font-size:0.3em; | |
702 } | |
703 span#scoreDisplay { | |
704 font-weight: bold; | |
705 } | |
706 div#wrapper { | |
707 width: 780px; | |
708 border: 1px solid black; | |
709 overflow: hidden; /* add this to contain floated children */ | |
710 } | |
711 div#instrumentSection { | |
712 width: 250px; | |
713 border: 1px solid red; | |
714 display: inline-block; | |
715 } | |
716 div#featureSection { | |
717 width: 250px; | |
718 border: 1px solid green; | |
719 display: inline-block; | |
720 } | |
721 div#valenceSection { | |
722 width: 250px; | |
723 border: 1px solid blue; | |
724 display: inline-block; | |
725 } | |
726 button#previousComment{ | |
727 width: 120px; | |
728 height: 150px; | |
729 font-size:1.5em; | |
730 } | |
731 button#nextComment{ | |
732 width: 666px; | |
733 height: 150px; | |
734 font-size:1.5em; | |
735 } | |
736 ul | |
737 { | |
738 list-style-type: none; /* no bullet points */ | |
739 margin-left: -20px; /* less indent */ | |
740 margin-top: 0px; | |
741 margin-bottom: 5px; | |
742 } | |
743 </style> | |
744 | |
745 </head> | |
746 | |
747 <body> | |
748 <h1>Subjective evaluation results</h1> | |
749 | |
750 <div id="debugDisplay"> | |
751 XML file folder: <span id="xmlFileFolder_span"></span> | |
752 </div> | |
753 | |
754 <div id="headerMatter"> | |
755 <div> | |
756 <strong>Result XML files:</strong> <span id="numberOfFiles_span"></span> | |
757 </div> | |
758 <div> | |
759 <strong>Audioholders in dataset:</strong> <span id="audioholderArray_span"></span> | |
760 </div> | |
761 <div> | |
762 <strong>Subjects in dataset:</strong> <span id="subjectArray_span"></span> | |
763 </div> | |
764 <div> | |
765 <strong>Audioelements in dataset:</strong> <span id="audioelementArray_span"></span> | |
766 </div> | |
767 <br> | |
768 </div> | |
769 <br> | |
770 | |
771 <!-- Show time elapsed | |
772 The last annotation took <strong><span id="timeDisplay">(N/A)</span></strong> seconds. | |
773 <br>--> | |
774 | |
775 </body> | |
776 </html> |