Mercurial > hg > webaudioevaluationtool
comparison analysis/analyse.html @ 2203:b332dcc65b8c
Instructions update (folder contents); moved legacy analysis page to analysis folder; removed README.txt in favour of markdown
author | Brecht De Man <b.deman@qmul.ac.uk> |
---|---|
date | Sat, 09 Apr 2016 10:26:29 +0100 |
parents | |
children | 185232d01324 |
comparison
equal
deleted
inserted
replaced
2201:41577a11f12e | 2203:b332dcc65b8c |
---|---|
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-2.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.overrideMimeType('text/xml'); | |
288 xmlhttp.send(); | |
289 return xmlhttp.responseXML; | |
290 } | |
291 | |
292 // go over all files and compute relevant statistics | |
293 function loadAllFiles() { | |
294 // retrieve information from XMLs | |
295 | |
296 for (fileIndex = 0; fileIndex < xmlFiles.length; fileIndex++) { | |
297 xmlFileName = xmlFileFolder+"/"+xmlFiles[fileIndex]; | |
298 xml = readXML(xmlFileName); | |
299 if (xml != null) { // if file exists | |
300 // append file name to array of file names | |
301 fileNameArray.push(xmlFiles[fileIndex]); | |
302 | |
303 // get root of XML file | |
304 root = xml.getElementsByTagName('waetresult')[0]; | |
305 | |
306 // get subject ID, add to array if not already there | |
307 pretestSurveyResult = root.getElementsByTagName('surveyresult')[0]; | |
308 subjectID = pretestSurveyResult.getElementsByTagName('comment')[0]; | |
309 if (subjectID){ | |
310 if (subjectID.getAttribute('id')!='sessionId') { // warning in console when not available | |
311 console.log(xmlFiles[fileIndex]+': no SessionID available'); | |
312 } | |
313 if (subjectArray.indexOf(subjectID.textContent) == -1) { // if not already in array | |
314 subjectArray.push(subjectID.textContent); // append to array | |
315 } | |
316 } | |
317 | |
318 // go over all audioholders, add to array if not already there | |
319 audioholderNodes = root.getElementsByTagName('audioholder'); | |
320 // go over audioholderNodes and append audioholder name when not present yet | |
321 for (audioholderIndex = 0; audioholderIndex < audioholderNodes.length; audioholderIndex++) { | |
322 audioholderName = audioholderNodes[audioholderIndex].getAttribute('id'); | |
323 if (audioholderArray.indexOf(audioholderName) == -1) { // if not already in array | |
324 audioholderArray.push(audioholderName); // append to array | |
325 } | |
326 // within each audioholder, go over all audioelement IDs, add to array if not already there | |
327 audioelementNodes = audioholderNodes[audioholderIndex].getElementsByTagName('audioelement'); | |
328 for (audioelementIndex = 0; audioelementIndex < audioelementNodes.length; audioelementIndex++) { | |
329 audioelementName = audioelementNodes[audioelementIndex].getAttribute('id'); | |
330 if (audioelementArray.indexOf(audioelementName) == -1) { // if not already in array | |
331 audioelementArray.push(audioelementName); // append to array | |
332 } | |
333 } | |
334 } | |
335 // count occurrences of each audioholder | |
336 // ... | |
337 } | |
338 else { | |
339 console.log('XML file '+xmlFileName+' not found.'); | |
340 } | |
341 } | |
342 | |
343 // sort alphabetically | |
344 fileNameArray.sort(); | |
345 subjectArray.sort(); | |
346 audioholderArray.sort(); | |
347 audioelementArray.sort(); | |
348 | |
349 // display all information in HTML | |
350 // show XML file folder | |
351 document.getElementById('xmlFileFolder_span').innerHTML = "\""+xmlFileFolder+"/\""; | |
352 // show number of files | |
353 document.getElementById('numberOfFiles_span').innerHTML = fileNameArray.length; | |
354 // show list of subject names | |
355 document.getElementById('subjectArray_span').innerHTML = subjectArray.toString(); | |
356 // show list of audioholders | |
357 document.getElementById('audioholderArray_span').innerHTML = audioholderArray.toString(); | |
358 // show list of audioelementes | |
359 document.getElementById('audioelementArray_span').innerHTML = audioelementArray.toString(); | |
360 } | |
361 | |
362 function printSurveyData() { | |
363 // print some fields from the survey for different people | |
364 | |
365 // go over all XML files | |
366 for (fileIndex = 0; fileIndex < xmlFiles.length; fileIndex++) { | |
367 xmlFileName = xmlFileFolder+"/"+xmlFiles[fileIndex]; | |
368 xml = readXML(xmlFileName); | |
369 // make a div | |
370 var div = document.createElement('div'); | |
371 document.body.appendChild(div); | |
372 div.id = 'div_survey_'+xmlFileName; | |
373 div.style.width = '1100px'; | |
374 //div.style.height = '350px'; | |
375 | |
376 // title for that div (subject id) | |
377 document.getElementById('div_survey_'+xmlFileName).innerHTML = '<h2>'+xmlFileName+'</h2>'; | |
378 | |
379 // which songs did they do | |
380 if (xml != null) { // if file exists | |
381 // get root of XML file | |
382 root = xml.getElementsByTagName('waetresult')[0]; | |
383 // go over all audioholders | |
384 // document.getElementById('div_survey_'+xmlFileName).innerHTML += '<strong>Audioholders: </strong>'; | |
385 // audioholderNodes = root.getElementsByTagName('audioholder'); | |
386 // for (audioholderIndex = 0; audioholderIndex < audioholderNodes.length-1; audioholderIndex++) { | |
387 // document.getElementById('div_survey_'+xmlFileName).innerHTML += audioholderNodes[audioholderIndex].getAttribute('id')+', '; | |
388 // } | |
389 // document.getElementById('div_survey_'+xmlFileName).innerHTML += audioholderNodes[audioholderNodes.length-1].getAttribute('id'); | |
390 | |
391 // survey responses (each if available) | |
392 // get posttest node for total test | |
393 childNodes = root.childNodes; | |
394 posttestnode = null; | |
395 for (idx = 0; idx < childNodes.length; idx++){ | |
396 if (childNodes[childNodes.length-idx-1].tagName == 'posttest') { | |
397 posttestnode = childNodes[childNodes.length-idx-1]; | |
398 break; | |
399 } | |
400 } | |
401 | |
402 // post-test info | |
403 if (posttestnode) { | |
404 posttestcomments = posttestnode.getElementsByTagName('comment'); | |
405 for (idx=0; idx < posttestcomments.length; idx++){ | |
406 commentsToPrint = ['age', 'location']; // CHANGE WHAT TO PRINT | |
407 idAttribute = posttestcomments[idx].getAttribute('id'); | |
408 if (commentsToPrint.indexOf(idAttribute) >= 0) { // if exists? | |
409 document.getElementById('div_survey_'+xmlFileName).innerHTML += '<br><strong>'+idAttribute+': </strong>'+posttestcomments[idx].textContent; | |
410 } | |
411 } | |
412 } | |
413 } | |
414 } | |
415 } | |
416 | |
417 function makePlots() { //TODO: split into different functions | |
418 // TEMPORARY | |
419 makeTimeline(xmlFileFolder+"/"+xmlFiles[0]); | |
420 | |
421 // create value array | |
422 var ratings = []; // 3D matrix of ratings (audioholder, audioelement, subject) | |
423 for (audioholderIndex = 0; audioholderIndex < audioholderArray.length; audioholderIndex++) { | |
424 ratings.push([]); | |
425 for (audioelementIndex = 0; audioelementIndex < audioelementArray.length; audioelementIndex++) { | |
426 ratings[audioholderIndex].push([]); | |
427 } | |
428 } | |
429 | |
430 // go over all XML files | |
431 for (fileIndex = 0; fileIndex < xmlFiles.length; fileIndex++) { | |
432 xmlFileName = xmlFileFolder+"/"+xmlFiles[fileIndex]; | |
433 xml = readXML(xmlFileName); | |
434 if (xml != null) { // if file exists | |
435 // get root of XML file | |
436 root = xml.getElementsByTagName('waetresult')[0]; | |
437 // go over all audioholders | |
438 audioholderNodes = root.getElementsByTagName('audioholder'); | |
439 for (audioholderIndex = 0; audioholderIndex < audioholderNodes.length; audioholderIndex++) { | |
440 audioholderName = audioholderNodes[audioholderIndex].getAttribute('id'); | |
441 audioelementNodes = audioholderNodes[audioholderIndex].getElementsByTagName('audioelement'); | |
442 // go over all audioelements | |
443 for (audioelementIndex = 0; audioelementIndex < audioelementNodes.length; audioelementIndex++) { | |
444 audioelementName = audioelementNodes[audioelementIndex].getAttribute('id'); | |
445 // get value | |
446 var value = audioelementNodes[audioelementIndex].getElementsByTagName("value")[0].textContent; | |
447 if (value) { // if not empty, null, undefined... | |
448 ratingValue = parseFloat(value); | |
449 // add to matrix at proper position | |
450 aHidx = audioholderArray.indexOf(audioholderName); | |
451 aEidx = audioelementArray.indexOf(audioelementName); | |
452 ratings[aHidx][aEidx].push(ratingValue); | |
453 } | |
454 } | |
455 } | |
456 | |
457 // go over all audioholders | |
458 | |
459 // go over all audioelements within audioholder, see if present in idMatrix, add if not | |
460 // add corresponding rating to 'ratings', at position corresponding with position in idMatrix | |
461 } | |
462 } | |
463 | |
464 for (audioholderIndex = 0; audioholderIndex < audioholderArray.length; audioholderIndex++) { | |
465 audioholderName = audioholderArray[audioholderIndex]; // for this song | |
466 tickArray = [] | |
467 | |
468 raw_data = [['SubjectID', 'Rating']]; | |
469 audioElIdx = 0; | |
470 for (audioelementIndex = 0; audioelementIndex<ratings[audioholderIndex].length; audioelementIndex++){ | |
471 if (ratings[audioholderIndex][audioelementIndex].length>0) { | |
472 audioElIdx++; // increase if not empty | |
473 // make tick label | |
474 tickArray.push({v:audioElIdx, f: audioelementArray[audioelementIndex]}); | |
475 } | |
476 for (subject = 0; subject<ratings[audioholderIndex][audioelementIndex].length; subject++){ | |
477 // add subject-value pair for each subject | |
478 raw_data.push([audioElIdx, ratings[audioholderIndex][audioelementIndex][subject]]); | |
479 } | |
480 } | |
481 | |
482 // create plot (one per song) | |
483 var data = google.visualization.arrayToDataTable(raw_data); | |
484 | |
485 var options = { | |
486 title: audioholderName, | |
487 hAxis: {title: 'audioelement ID', minValue: 0, maxValue: audioElIdx+1, | |
488 ticks: tickArray}, | |
489 vAxis: {title: 'Rating', minValue: 0, maxValue: 1}, | |
490 seriesType: 'scatter', | |
491 legend: 'none' | |
492 }; | |
493 var div = document.createElement('div'); | |
494 document.body.appendChild(div); | |
495 div.id = 'div_'+audioholderName; | |
496 div.style.width = '1100px'; | |
497 div.style.height = '350px'; | |
498 var chart = new google.visualization.ComboChart(document.getElementById('div_'+audioholderName)); | |
499 chart.draw(data, options); | |
500 | |
501 // box plots | |
502 var div = document.createElement('div'); | |
503 document.body.appendChild(div); | |
504 div.id = 'div_box_'+audioholderName; | |
505 div.style.width = '1100px'; | |
506 div.style.height = '350px'; | |
507 // Get median, percentiles, maximum and minimum; outliers. | |
508 pctl25 = []; | |
509 pctl75 = []; | |
510 med = []; | |
511 min = []; | |
512 max = []; | |
513 outlierArray = []; | |
514 max_n_outliers = 0; // maximum number of outliers for one audioelement | |
515 for (audioelementIndex = 0; audioelementIndex<ratings[audioholderIndex].length; audioelementIndex++){ | |
516 med.push(median(ratings[audioholderIndex][audioelementIndex])); // median | |
517 pctl25.push(percentile(ratings[audioholderIndex][audioelementIndex], 25)); // 25th percentile | |
518 pctl75.push(percentile(ratings[audioholderIndex][audioelementIndex], 75)); // 75th percentile | |
519 IQR = pctl75[pctl75.length-1]-pctl25[pctl25.length-1]; | |
520 // outliers: range of values which is above pctl75+1.5*IQR or below pctl25-1.5*IQR | |
521 outliers = []; | |
522 rest = []; | |
523 for (idx = 0; idx<ratings[audioholderIndex][audioelementIndex].length; idx++){ | |
524 if (ratings[audioholderIndex][audioelementIndex][idx] > pctl75[pctl75.length-1]+1.5*IQR || | |
525 ratings[audioholderIndex][audioelementIndex][idx] < pctl25[pctl25.length-1]-1.5*IQR){ | |
526 outliers.push(ratings[audioholderIndex][audioelementIndex][idx]); | |
527 } | |
528 else { | |
529 rest.push(ratings[audioholderIndex][audioelementIndex][idx]); | |
530 } | |
531 } | |
532 outlierArray.push(outliers); | |
533 max_n_outliers = Math.max(max_n_outliers, outliers.length); // update max mber | |
534 // max: maximum value which is not outlier | |
535 max.push(Math.max.apply(null, rest)); | |
536 // min: minimum value which is not outlier | |
537 min.push(Math.min.apply(null, rest)); | |
538 } | |
539 | |
540 // Build data array | |
541 boxplot_data = [['ID', 'Span', '', '', '', 'Median']]; | |
542 for (idx = 0; idx < max_n_outliers; idx++) { | |
543 boxplot_data[0].push('Outlier'); | |
544 } | |
545 for (audioelementIndex = 0; audioelementIndex<ratings[audioholderIndex].length; audioelementIndex++){ | |
546 if (ratings[audioholderIndex][audioelementIndex].length>0) { // if rating array not empty for this audioelement | |
547 data_array = [ | |
548 audioelementArray[audioelementIndex], // name | |
549 min[audioelementIndex], // minimum | |
550 pctl75[audioelementIndex], | |
551 pctl25[audioelementIndex], | |
552 max[audioelementIndex], // maximum | |
553 med[audioelementIndex] | |
554 ]; | |
555 for (idx = 0; idx < max_n_outliers; idx++) { | |
556 if (idx<outlierArray[audioelementIndex].length){ | |
557 data_array.push(outlierArray[audioelementIndex][idx]); | |
558 } | |
559 else { | |
560 data_array.push(null); | |
561 } | |
562 } | |
563 boxplot_data.push(data_array); | |
564 } | |
565 } | |
566 | |
567 // Create and populate the data table. | |
568 var data = google.visualization.arrayToDataTable(boxplot_data); | |
569 // Create and draw the visualization. | |
570 var ac = new google.visualization.ComboChart(document.getElementById('div_box_'+audioholderName)); | |
571 ac.draw(data, { | |
572 title : audioholderName, | |
573 //width: 600, | |
574 //height: 400, | |
575 vAxis: {title: "Rating"}, | |
576 hAxis: {title: "audioelement ID"}, | |
577 seriesType: "line", | |
578 pointSize: 5, | |
579 lineWidth: 0, | |
580 colors: ['black'], | |
581 series: { 0: {type: "candlesticks", color: 'blue'}, // box plot shape | |
582 1: {type: "line", pointSize: 10, lineWidth: 0, color: 'red' } }, // median | |
583 legend: 'none' | |
584 }); | |
585 } | |
586 } | |
587 | |
588 function makeTimeline(xmlFileName){ // WIP | |
589 // Based on the XML file name, take time data and plot playback and marker movements | |
590 | |
591 // read XML file and check if exists | |
592 xml = readXML(xmlFileName); | |
593 if (!xml) { // if file does not exist | |
594 console.log('XML file '+xml+'does not exist. ('+xmlFileName+')') | |
595 return; // do nothing; exit function | |
596 } | |
597 // get root of XML file | |
598 root = xml.getElementsByTagName('waetresult')[0]; | |
599 | |
600 audioholder_time = 0; | |
601 previous_audioholder_time = 0; // time spent before current audioholder | |
602 time_offset = 0; // test starts at zero | |
603 | |
604 // go over all audioholders | |
605 audioholderNodes = root.getElementsByTagName('audioholder'); | |
606 for (audioholderIndex = 0; audioholderIndex < audioholderNodes.length; audioholderIndex++) { | |
607 audioholderName = audioholderNodes[audioholderIndex].getAttribute('id'); | |
608 if (!audioholderName) { | |
609 console.log('audioholder name is empty; go to next one. ('+xmlFileName+')'); | |
610 break; | |
611 } | |
612 | |
613 // subtract total audioholder length from subsequent audioholder event times | |
614 audioholder_children = audioholderNodes[audioholderIndex].childNodes; | |
615 foundIt = false; | |
616 console.log(audioholder_children[2].getElementsByTagName("metricResult")) // not working! | |
617 for (idx = 0; idx<audioholder_children.length; idx++) { // go over children | |
618 | |
619 if (audioholder_children[idx].getElementsByTagName('metricResult').length) { | |
620 console.log(audioholder_children[idx].getElementsByTagName('metricResult')[0]); | |
621 if (audioholder_children[idx].getElementsByTagName('metricResult')[0].getAttribute('id') == "testTime"){ | |
622 audioholder_time = parseFloat(audioholder_children[idx].getElementsByTagName('metricResult')[0].textContent); | |
623 console.log(audioholder_time); | |
624 foundIt = true; | |
625 } | |
626 } | |
627 } | |
628 if (!foundIt) { | |
629 console.log("Skipping audioholder without total time specified from "+xmlFileName+"."); // always hitting this | |
630 break; | |
631 } | |
632 | |
633 audioelementNodes = audioholderNodes[audioholderIndex].getElementsByTagName('audioelement'); | |
634 | |
635 // make div | |
636 | |
637 // draw chart | |
638 | |
639 // legend with audioelement names | |
640 } | |
641 } | |
642 | |
643 </script> | |
644 | |
645 | |
646 | |
647 <style> | |
648 div { | |
649 padding: 2px; | |
650 margin-top: 2px; | |
651 margin-bottom: 2px; | |
652 } | |
653 div.head{ | |
654 margin-left: 10px; | |
655 border: black; | |
656 border-width: 2px; | |
657 border-style: solid; | |
658 } | |
659 div.attrib{ | |
660 margin-left:25px; | |
661 border: black; | |
662 border-width: 2px; | |
663 border-style: dashed; | |
664 margin-bottom: 10px; | |
665 } | |
666 div#headerMatter{ | |
667 background-color: #FFFFCC; | |
668 } | |
669 div#currentStatement{ | |
670 font-size:3.0em; | |
671 font-weight: bold; | |
672 | |
673 } | |
674 div#debugDisplay { | |
675 color: #CCCCCC; | |
676 font-size:0.3em; | |
677 } | |
678 span#scoreDisplay { | |
679 font-weight: bold; | |
680 } | |
681 div#wrapper { | |
682 width: 780px; | |
683 border: 1px solid black; | |
684 overflow: hidden; /* add this to contain floated children */ | |
685 } | |
686 div#instrumentSection { | |
687 width: 250px; | |
688 border: 1px solid red; | |
689 display: inline-block; | |
690 } | |
691 div#featureSection { | |
692 width: 250px; | |
693 border: 1px solid green; | |
694 display: inline-block; | |
695 } | |
696 div#valenceSection { | |
697 width: 250px; | |
698 border: 1px solid blue; | |
699 display: inline-block; | |
700 } | |
701 button#previousComment{ | |
702 width: 120px; | |
703 height: 150px; | |
704 font-size:1.5em; | |
705 } | |
706 button#nextComment{ | |
707 width: 666px; | |
708 height: 150px; | |
709 font-size:1.5em; | |
710 } | |
711 ul | |
712 { | |
713 list-style-type: none; /* no bullet points */ | |
714 margin-left: -20px; /* less indent */ | |
715 margin-top: 0px; | |
716 margin-bottom: 5px; | |
717 } | |
718 </style> | |
719 | |
720 </head> | |
721 | |
722 <body> | |
723 <h1>Subjective evaluation results</h1> | |
724 | |
725 <div id="debugDisplay"> | |
726 XML file folder: <span id="xmlFileFolder_span"></span> | |
727 </div> | |
728 | |
729 <div id="headerMatter"> | |
730 <div> | |
731 <strong>Result XML files:</strong> <span id="numberOfFiles_span"></span> | |
732 </div> | |
733 <div> | |
734 <strong>Audioholders in dataset:</strong> <span id="audioholderArray_span"></span> | |
735 </div> | |
736 <div> | |
737 <strong>Subjects in dataset:</strong> <span id="subjectArray_span"></span> | |
738 </div> | |
739 <div> | |
740 <strong>Audioelements in dataset:</strong> <span id="audioelementArray_span"></span> | |
741 </div> | |
742 <br> | |
743 </div> | |
744 <br> | |
745 | |
746 <!-- Show time elapsed | |
747 The last annotation took <strong><span id="timeDisplay">(N/A)</span></strong> seconds. | |
748 <br>--> | |
749 | |
750 </body> | |
751 </html> |