annotate analyse.html @ 1116:c44fbf72f7f2

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