Mercurial > hg > webaudioevaluationtool
comparison analyse.html @ 716:cbfc18a7d068
Paper: edits in various sections; Added preliminary analysis page
author | Brecht De Man <BrechtDeMan@users.noreply.github.com> |
---|---|
date | Wed, 14 Oct 2015 20:12:52 +0100 |
parents | |
children | 378726f0ac91 9da8a3e65a78 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 716:cbfc18a7d068 |
---|---|
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 var xmlFiles = ['McG-A-2014-03.xml','McG-B-2014-03.xml','McG-C-2014-03.xml']; | |
27 | |
28 //TODO: make retrieval of file names automatic / drag files on here | |
29 | |
30 /**************** | |
31 * VARIABLES * | |
32 ****************/ | |
33 | |
34 // Counters | |
35 // How many files, audioHolders, audioElementes and statements annotated (don't count current one) | |
36 var numberOfFiles = -1; | |
37 var numberOfaudioHolders = -1; | |
38 var numberOfaudioElementes = -1; | |
39 var numberOfStatements = -1; | |
40 var numberOfSkippedComments = 0; | |
41 | |
42 // Object arrays | |
43 var fileNameArray = []; | |
44 var subjectArray = []; | |
45 var audioHolderArray = []; | |
46 var audioElementArray = []; | |
47 | |
48 // End of (file, audioholder, audioelement) flags | |
49 var newFile = true; | |
50 var newAudioHolder = true; | |
51 var newAudioElement = true; | |
52 | |
53 var fileCounter = 0; // file index | |
54 var audioHolderCounter=0; // audioholder index (current XML file) | |
55 var audioElementCounter=0; // audioelement index (current audioholder) | |
56 var statementNumber=0; // total number of statements | |
57 | |
58 var root; // root of XML file | |
59 var commentInFull = ''; // full comment | |
60 | |
61 var playAudio = true; // whether corresponding audio should be played back | |
62 | |
63 // // Measuring time | |
64 // var lastTimeMeasured = -1; // | |
65 // var durationLastAnnotation = -1; // duration of last annotation | |
66 // var timeArray = []; | |
67 // var MIN_TIME = 1.0; // minimum time counted as significant | |
68 // var measurementPaused = false; // whether time measurement is paused | |
69 // var timeInBuffer = 0; // | |
70 | |
71 var topLevel; | |
72 window.onload = function() { | |
73 // Initialise page | |
74 topLevel = document.getElementById('topLevelBody'); | |
75 var setup = document.createElement('div'); | |
76 setup.id = 'setupTagDiv'; | |
77 loadAllFiles(); | |
78 | |
79 makePlots(); | |
80 // measure time at this point: | |
81 lastTimeMeasured = new Date().getTime(); // in milliseconds | |
82 }; | |
83 | |
84 // Assert function | |
85 function assert(condition, message) { | |
86 if (!condition) { | |
87 message = message || "Assertion failed"; | |
88 if (typeof Error !== "undefined") { | |
89 throw new Error(message); | |
90 } | |
91 throw message; // Fallback | |
92 } | |
93 } | |
94 | |
95 function median(values) { | |
96 values.sort( function(a,b) {return a - b;} ); | |
97 var half = Math.floor(values.length/2); | |
98 if(values.length % 2) | |
99 return values[half]; | |
100 else | |
101 return (values[half-1] + values[half]) / 2.0; | |
102 } | |
103 | |
104 /*********************** | |
105 * TIME MEASUREMENT * | |
106 ************************/ | |
107 | |
108 // measure time since last time this function was called | |
109 function timeSinceLastCall() { | |
110 // current time | |
111 var currentTime = new Date().getTime(); | |
112 // calculate time difference | |
113 var timeDifference = currentTime - lastTimeMeasured + timeInBuffer; | |
114 // clear buffer (for pausing) | |
115 timeInBuffer = 0; | |
116 // remember last measured time | |
117 lastTimeMeasured = currentTime; | |
118 return timeDifference; | |
119 } | |
120 | |
121 // pause time measurement | |
122 function pauseTimeMeasurement() { | |
123 // UN-PAUSE | |
124 if (measurementPaused) { // already paused | |
125 // button shows 'pause' again | |
126 document.getElementById('pauseButton').innerHTML = 'Pause'; | |
127 // toggle state | |
128 measurementPaused = false; | |
129 // resume time measurement | |
130 lastTimeMeasured = new Date().getTime(); // reset time, discard time while paused | |
131 } else { // PAUSE | |
132 // button shows 'resume' | |
133 document.getElementById('pauseButton').innerHTML = 'Resume'; | |
134 // toggle state | |
135 measurementPaused = true; | |
136 // pause time measurement | |
137 timeInBuffer = timeSinceLastCall(); | |
138 } | |
139 } | |
140 | |
141 // show elapsed time on interface | |
142 function showTimeElapsedInSeconds() { | |
143 // if paused: un-pause | |
144 if (measurementPaused) { | |
145 pauseTimeMeasurement(); | |
146 } | |
147 | |
148 // time of last annotation | |
149 var lastAnnotationTime = timeSinceLastCall()/1000; | |
150 document.getElementById('timeDisplay').innerHTML = lastAnnotationTime.toFixed(2); | |
151 // average time over last ... annotations | |
152 var avgAnnotationTime; | |
153 var numberOfElementsToAverage = | |
154 document.getElementById('numberOfTimeAverages').value; | |
155 if (isPositiveInteger(numberOfElementsToAverage)) { | |
156 avgAnnotationTime = | |
157 calculateAverageTime(lastAnnotationTime, | |
158 Number(numberOfElementsToAverage)); | |
159 } else { | |
160 // change text field content to 'ALL' | |
161 document.getElementById('numberOfTimeAverages').value = 'ALL'; | |
162 avgAnnotationTime = calculateAverageTime(lastAnnotationTime, -1); | |
163 } | |
164 document.getElementById('timeAverageDisplay').innerHTML = avgAnnotationTime.toFixed(2); | |
165 } | |
166 | |
167 // auxiliary function: is string a positive integer? | |
168 // http://stackoverflow.com/questions/10834796/... | |
169 // validate-that-a-string-is-a-positive-integer | |
170 function isPositiveInteger(str) { | |
171 var n = ~~Number(str); | |
172 return String(n) === str && n >= 0; | |
173 } | |
174 | |
175 // calculate average time | |
176 function calculateAverageTime(newTimeMeasurementInSeconds,numberOfPoints) { | |
177 // append last measurement time to time array, if significant | |
178 if (newTimeMeasurementInSeconds > MIN_TIME) { | |
179 timeArray.push(newTimeMeasurementInSeconds); | |
180 } | |
181 // average over last N elements of this array | |
182 if (numberOfPoints < 0 || numberOfPoints>=timeArray.length) { // calculate average over all | |
183 var sum = 0; | |
184 for (var i = 0; i < timeArray.length; i++) { | |
185 sum += timeArray[i]; | |
186 } | |
187 averageOfTimes = sum/timeArray.length; | |
188 } else { // calculate average over specified number of times measured last | |
189 var sum = 0; | |
190 for (var i = timeArray.length-numberOfPoints; i < timeArray.length; i++) { | |
191 sum += timeArray[i]; | |
192 } | |
193 averageOfTimes = sum/numberOfPoints; | |
194 } | |
195 return averageOfTimes; | |
196 } | |
197 | |
198 | |
199 /******************************** | |
200 * PLAYBACK OF AUDIO * | |
201 ********************************/ | |
202 | |
203 //PLAYaudioElement | |
204 // Keep track of whether audio should be played | |
205 function playFlagChanged(){ | |
206 playAudio = playFlag.checked; // global variable | |
207 | |
208 if (!playAudio){ // if audio needs to stop | |
209 audio.pause(); // stop audio - if anything is playing | |
210 currently_playing = ''; // back to empty string so playaudioElement knows nothing's playing | |
211 } | |
212 } | |
213 | |
214 // audioHolder that's currently playing | |
215 var currently_playing_audioHolder = ''; // at first: empty string | |
216 var currently_playing_audioElement = ''; | |
217 var audio; | |
218 | |
219 // Play audioElement of audioHolder if available, from start or from same position | |
220 function playaudioElement(audioHolderName, audioElementerName){ | |
221 if (playAudio) { // if enabled | |
222 // get corresponding file from folder | |
223 var file_location = 'audio/'+audioHolderName + '/' + audioElementerName + '.mp3'; // fixed path and file name format | |
224 | |
225 // if not available, show error/warning message | |
226 //TODO ... | |
227 | |
228 // if nothing playing yet, start playing | |
229 if (currently_playing_audioHolder == ''){ // signal that nothing is playing | |
230 //playSound(audioBuffer); | |
231 audio = new Audio(file_location); | |
232 audio.loop = true; // loop when end is reached | |
233 audio.play(); | |
234 currently_playing_audioHolder = audioHolderName; | |
235 currently_playing_audioElement = audioElementerName; | |
236 } else if (currently_playing_audioHolder != audioHolderName) { | |
237 // if different audioHolder playing, stop that and start playing | |
238 audio.pause(); // stop audio | |
239 audio = new Audio(file_location); // load new file | |
240 audio.loop = true; // loop when end is reached | |
241 audio.play(); // play audio from the start | |
242 currently_playing_audioHolder = audioHolderName; | |
243 currently_playing_audioElement = audioElementerName; | |
244 } else if (currently_playing_audioElement != audioElementerName) { | |
245 // if same audioHolder playing, start playing from where it left off | |
246 skipTime = audio.currentTime; // time to skip to | |
247 audio.pause(); // stop audio | |
248 audio = new Audio(file_location); | |
249 audio.addEventListener('loadedmetadata', function() { | |
250 this.currentTime = skipTime; | |
251 console.log('Loaded '+audioHolderName+'-'+audioElementerName+', playing from '+skipTime); | |
252 }, false); // skip to same time when audio is loaded! | |
253 audio.loop = true; // loop when end is reached | |
254 audio.play(); // play from that time | |
255 audio.currentTime = skipTime; | |
256 currently_playing_audioHolder = audioHolderName; | |
257 currently_playing_audioElement = audioElementerName; | |
258 } | |
259 // if same audioElement playing: keep on playing (i.e. do nothing) | |
260 } | |
261 } | |
262 | |
263 /******************** | |
264 * READING FILES * | |
265 ********************/ | |
266 | |
267 // Read necessary data from XML file | |
268 function readXML(xmlFileName){ | |
269 if (window.XMLHttpRequest) | |
270 {// code for IE7+, Firefox, Chrome, Opera, Safari | |
271 xmlhttp=new XMLHttpRequest(); | |
272 } | |
273 else | |
274 {// code for IE6, IE5 | |
275 xmlhttp=new ActiveXObject("Microsoft.XMLHTTP"); | |
276 } | |
277 xmlhttp.open("GET",xmlFileName,false); | |
278 xmlhttp.send(); | |
279 return xmlhttp.responseXML; | |
280 } | |
281 | |
282 // go over all files and compute relevant statistics | |
283 function loadAllFiles() { | |
284 // retrieve information from XMLs | |
285 | |
286 for (fileIndex = 0; fileIndex < xmlFiles.length; fileIndex++) { | |
287 xmlFileName = xmlFileFolder+"/"+xmlFiles[fileIndex]; | |
288 xml = readXML(xmlFileName); | |
289 if (xml != null) { // if file exists | |
290 // append file name to array of file names | |
291 fileNameArray.push(xmlFiles[fileIndex]); | |
292 | |
293 // get root of XML file | |
294 root = xml.getElementsByTagName('browserevaluationresult')[0]; | |
295 | |
296 // get subject ID, add to array if not already there | |
297 pretest = root.getElementsByTagName('pretest')[0]; | |
298 subjectID = pretest.getElementsByTagName('comment')[0]; | |
299 if (subjectID.getAttribute('id')!='sessionId') { // warning in console when not available | |
300 console.log(xmlFiles[fileIndex]+': no SessionID available'); | |
301 } | |
302 if (subjectArray.indexOf(subjectID.textContent) == -1) { // if not already in array | |
303 subjectArray.push(subjectID.textContent); // append to array | |
304 } | |
305 | |
306 // go over all audioHolders, add to array if not already there | |
307 audioHolderNodes = root.getElementsByTagName('audioholder'); | |
308 // go over audioHolderNodes and append audioHolder name when not present yet | |
309 for (audioHolderIndex = 0; audioHolderIndex < audioHolderNodes.length; audioHolderIndex++) { | |
310 audioHolderName = audioHolderNodes[audioHolderIndex].getAttribute('id'); | |
311 if (audioHolderArray.indexOf(audioHolderName) == -1) { // if not already in array | |
312 audioHolderArray.push(audioHolderName); // append to array | |
313 } | |
314 // within each audioHolder, go over all audioElement IDs, add to array if not already there | |
315 audioElementNodes = audioHolderNodes[audioHolderIndex].getElementsByTagName('audioelement'); | |
316 for (audioElementIndex = 0; audioElementIndex < audioElementNodes.length; audioElementIndex++) { | |
317 audioElementName = audioElementNodes[audioElementIndex].getAttribute('id'); | |
318 if (audioElementArray.indexOf(audioElementName) == -1) { // if not already in array | |
319 audioElementArray.push(audioElementName); // append to array | |
320 } | |
321 } | |
322 } | |
323 // count occurrences of each audioHolder | |
324 // ... | |
325 } | |
326 else { | |
327 console.log('XML file '+xmlFileName+' not found.'); | |
328 } | |
329 } | |
330 | |
331 // sort alphabetically | |
332 fileNameArray.sort(); | |
333 subjectArray.sort(); | |
334 audioHolderArray.sort(); | |
335 audioElementArray.sort(); | |
336 | |
337 // display all information in HTML | |
338 // show XML file folder | |
339 document.getElementById('xmlFileFolder_span').innerHTML = "\""+xmlFileFolder+"/\""; | |
340 // show number of files | |
341 document.getElementById('numberOfFiles_span').innerHTML = fileNameArray.length; | |
342 // show list of subject names | |
343 document.getElementById('subjectArray_span').innerHTML = subjectArray.toString(); | |
344 // show list of audioHolders | |
345 document.getElementById('audioHolderArray_span').innerHTML = audioHolderArray.toString(); | |
346 // show list of audioElementes | |
347 document.getElementById('audioElementArray_span').innerHTML = audioElementArray.toString(); | |
348 } | |
349 | |
350 function makePlots() { | |
351 // create value array | |
352 var ratings = []; // 3D matrix of ratings (audioHolder, audioElement, subject) | |
353 for (audioHolderIndex = 0; audioHolderIndex < audioHolderNodes.length; audioHolderIndex++) { | |
354 ratings.push([]); | |
355 for (audioElementIndex = 0; audioElementIndex < audioElementNodes.length; audioElementIndex++) { | |
356 ratings[audioHolderIndex].push([]); | |
357 } | |
358 } | |
359 | |
360 // go over all XML files | |
361 for (fileIndex = 0; fileIndex < xmlFiles.length; fileIndex++) { | |
362 xmlFileName = xmlFileFolder+"/"+xmlFiles[fileIndex]; | |
363 xml = readXML(xmlFileName); | |
364 if (xml != null) { // if file exists | |
365 // get root of XML file | |
366 root = xml.getElementsByTagName('browserevaluationresult')[0]; | |
367 // go over all audioHolders | |
368 audioHolderNodes = root.getElementsByTagName('audioholder'); | |
369 for (audioHolderIndex = 0; audioHolderIndex < audioHolderNodes.length; audioHolderIndex++) { | |
370 audioHolderName = audioHolderNodes[audioHolderIndex].getAttribute('id'); | |
371 audioElementNodes = audioHolderNodes[audioHolderIndex].getElementsByTagName('audioelement'); | |
372 // go over all audioelements | |
373 for (audioElementIndex = 0; audioElementIndex < audioElementNodes.length; audioElementIndex++) { | |
374 audioElementName = audioElementNodes[audioElementIndex].getAttribute('id'); | |
375 // get value | |
376 var value = audioElementNodes[audioElementIndex].getElementsByTagName("value")[0].textContent; | |
377 if (value) { // if not empty, null, undefined... | |
378 ratingValue = parseFloat(value); | |
379 // add to matrix | |
380 ratings[audioHolderIndex][audioElementIndex].push(ratingValue) | |
381 } | |
382 } | |
383 } | |
384 | |
385 // go over all audioHolders | |
386 | |
387 // go over all audioElements within audioHolder, see if present in idMatrix, add if not | |
388 // add corresponding rating to 'ratings', at position corresponding with position in idMatrix | |
389 } | |
390 } | |
391 | |
392 for (audioHolderIndex = 0; audioHolderIndex < audioHolderArray.length; audioHolderIndex++) { | |
393 audioHolderName = audioHolderArray[audioHolderIndex]; // for this song | |
394 tickArray = [] | |
395 medianOfAudioElement = [] | |
396 | |
397 raw_data = [['SubjectID', 'Rating']]; | |
398 audioElIdx = 0; | |
399 for (audioElementIndex = 0; audioElementIndex<ratings[audioHolderIndex].length; audioElementIndex++){ | |
400 if (ratings[audioHolderIndex][audioElementIndex].length>0) { | |
401 audioElIdx++; // increase if not empty | |
402 // make tick label | |
403 tickArray.push({v:audioElIdx, f: audioElementArray[audioElementIndex]}); | |
404 // add median | |
405 medianOfAudioElement.push(median(ratings[audioHolderIndex][audioElementIndex])); | |
406 } | |
407 for (subject = 0; subject<ratings[audioHolderIndex][audioElementIndex].length; subject++){ | |
408 // add subject-value pair for each subject | |
409 raw_data.push([audioElIdx, ratings[audioHolderIndex][audioElementIndex][subject]]); | |
410 } | |
411 } | |
412 | |
413 // create plot (one per song) | |
414 var data = google.visualization.arrayToDataTable(raw_data); | |
415 | |
416 var options = { | |
417 title: audioHolderName, | |
418 hAxis: {title: 'Subject', minValue: 0, maxValue: audioElIdx+1, | |
419 ticks: tickArray}, | |
420 vAxis: {title: 'Rating', minValue: 0, maxValue: 1}, | |
421 seriesType: 'scatter', | |
422 legend: 'none' | |
423 }; | |
424 var div = document.createElement('div'); | |
425 document.body.appendChild(div); | |
426 div.id = 'div_'+audioHolderName; | |
427 div.style.width = '1100px'; | |
428 div.style.height = '350px'; | |
429 var chart = new google.visualization.ComboChart(document.getElementById('div_'+audioHolderName)); | |
430 chart.draw(data, options); | |
431 | |
432 // box plots | |
433 // function drawVisualization() { | |
434 // // Create and populate the data table. | |
435 // var data = google.visualization.arrayToDataTable([ | |
436 // ['ID', 'IQR', '', '', '', 'Median', 'Average'], | |
437 // ['Serie1', 20, 28, 38, 45, 20, 25], | |
438 // ['Serie2', 31, 38, 55, 66, 30, 35], | |
439 // ['Serie3', 50, 55, 77, 80, 10, 15], | |
440 // ['Serie4', 77, 77, 66, 50, 20, 25], | |
441 // ['Serie5', 68, 66, 22, 15, 30, 35] | |
442 // // Treat first row as data as well. | |
443 // ]); | |
444 // // Create and draw the visualization. | |
445 // var ac = new google.visualization.ComboChart(document.getElementById('visualization')); | |
446 // ac.draw(data, { | |
447 // title : 'Box Plot with Median and Average', | |
448 // width: 600, | |
449 // height: 400, | |
450 // vAxis: {title: "Value"}, | |
451 // hAxis: {title: "Serie ID"}, | |
452 // series: { 0: {type: "candlesticks"}, 1: {type: "line", pointSize: 10, lineWidth: | |
453 // 0 }, 2: {type: "line", pointSize: 10, lineWidth: 0, color: 'black' } } | |
454 // }); | |
455 // } | |
456 } | |
457 } | |
458 | |
459 </script> | |
460 | |
461 | |
462 | |
463 <style> | |
464 div { | |
465 padding: 2px; | |
466 margin-top: 2px; | |
467 margin-bottom: 2px; | |
468 } | |
469 div.head{ | |
470 margin-left: 10px; | |
471 border: black; | |
472 border-width: 2px; | |
473 border-style: solid; | |
474 } | |
475 div.attrib{ | |
476 margin-left:25px; | |
477 border: black; | |
478 border-width: 2px; | |
479 border-style: dashed; | |
480 margin-bottom: 10px; | |
481 } | |
482 div#headerMatter{ | |
483 background-color: #FFFFCC; | |
484 } | |
485 div#currentStatement{ | |
486 font-size:3.0em; | |
487 font-weight: bold; | |
488 | |
489 } | |
490 div#debugDisplay { | |
491 color: #CCCCCC; | |
492 font-size:0.3em; | |
493 } | |
494 span#scoreDisplay { | |
495 font-weight: bold; | |
496 } | |
497 div#wrapper { | |
498 width: 780px; | |
499 border: 1px solid black; | |
500 overflow: hidden; /* add this to contain floated children */ | |
501 } | |
502 div#instrumentSection { | |
503 width: 250px; | |
504 border: 1px solid red; | |
505 display: inline-block; | |
506 } | |
507 div#featureSection { | |
508 width: 250px; | |
509 border: 1px solid green; | |
510 display: inline-block; | |
511 } | |
512 div#valenceSection { | |
513 width: 250px; | |
514 border: 1px solid blue; | |
515 display: inline-block; | |
516 } | |
517 button#previousComment{ | |
518 width: 120px; | |
519 height: 150px; | |
520 font-size:1.5em; | |
521 } | |
522 button#nextComment{ | |
523 width: 666px; | |
524 height: 150px; | |
525 font-size:1.5em; | |
526 } | |
527 ul | |
528 { | |
529 list-style-type: none; /* no bullet points */ | |
530 margin-left: -20px; /* less indent */ | |
531 margin-top: 0px; | |
532 margin-bottom: 5px; | |
533 } | |
534 </style> | |
535 | |
536 </head> | |
537 | |
538 <body> | |
539 <h1>Subjective evaluation results</h1> | |
540 | |
541 <div id="debugDisplay"> | |
542 XML file folder: <span id="xmlFileFolder_span"></span> | |
543 </div> | |
544 | |
545 <div id="headerMatter"> | |
546 <div> | |
547 <strong>Result XML files:</strong> <span id="numberOfFiles_span"></span> | |
548 </div> | |
549 <div> | |
550 <strong>Audioholders in dataset:</strong> <span id="audioHolderArray_span"></span> | |
551 </div> | |
552 <div> | |
553 <strong>Subjects in dataset:</strong> <span id="subjectArray_span"></span> | |
554 </div> | |
555 <div> | |
556 <strong>Audioelements in dataset:</strong> <span id="audioElementArray_span"></span> | |
557 </div> | |
558 <br> | |
559 </div> | |
560 <br> | |
561 | |
562 <!-- Show time elapsed | |
563 The last annotation took <strong><span id="timeDisplay">(N/A)</span></strong> seconds. | |
564 <br>--> | |
565 | |
566 </body> | |
567 </html> |