annotate analyse.html @ 1316:279930a008ca

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