Mercurial > hg > webaudioevaluationtool
comparison analyse.html @ 1936:94e57a566167
First draft of AES poster
author | Dave Moffat <me@davemoffat.com> |
---|---|
date | Tue, 23 Feb 2016 15:19:31 +0000 |
parents | |
children | b5bf2f57187c 9ee921c8cdd3 eef2d4ea18fb |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 1936:94e57a566167 |
---|---|
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> |