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