Mercurial > hg > webaudioevaluationtool
diff analyse.html @ 336:378726f0ac91 WAC2016
Paper: Added interface screenshot and box plot example
author | Brecht De Man <b.deman@qmul.ac.uk> |
---|---|
date | Thu, 15 Oct 2015 20:10:00 +0100 |
parents | 5f27d3eb93fe |
children | de9a0a740b6e |
line wrap: on
line diff
--- a/analyse.html Thu Oct 15 19:09:00 2015 +0100 +++ b/analyse.html Thu Oct 15 20:10:00 2015 +0100 @@ -18,32 +18,33 @@ google.load("visualization", "1", {packages:["corechart"]}); /************* - * SETUP * + * SETUP * *************/ // folder where to find the XML files - xmlFileFolder = "saves"; + xmlFileFolder = "analysis_test"; // array of XML files - var xmlFiles = ['McG-A-2014-03.xml','McG-B-2014-03.xml','McG-C-2014-03.xml']; + var xmlFiles = ['QM-1-1.xml','QM-2-1.xml','QM-2-2.xml','QM-2-3.xml','QM-3-1.xml','QM-3-2.xml','QM-4-1.xml','QM-5-1.xml','QM-5-2.xml','QM-6-1.xml','QM-6-2.xml','QM-7-1.xml','QM-7-2.xml','QM-8-1.xml','QM-9-1.xml','QM-10-1.xml','QM-11-1.xml','QM-12-1.xml','QM-12-2.xml','QM-13-1.xml','QM-14-1.xml','QM-15-1.xml','QM-16-1.xml','QM-17-1.xml','QM-18-1.xml','QM-18-2.xml','QM-18-3.xml','QM-19-1.xml','QM-20-1.xml','QM-20-2.xml','QM-20-3.xml','QM-21-1.xml','QM-21-2.xml']; + //['McG-A-2014-03.xml','McG-B-2014-03.xml','McG-C-2014-03.xml','McG-D-2014-03.xml','McG-E-2014-03.xml','McG-F-2014-03.xml','McG-G-2014-03.xml','McG-H-2014-03.xml']; //TODO: make retrieval of file names automatic / drag files on here /**************** - * VARIABLES * + * VARIABLES * ****************/ // Counters - // How many files, audioHolders, audioElementes and statements annotated (don't count current one) + // How many files, audioholders, audioelementes and statements annotated (don't count current one) var numberOfFiles = -1; - var numberOfaudioHolders = -1; - var numberOfaudioElementes = -1; + var numberOfaudioholders = -1; + var numberOfaudioelementes = -1; var numberOfStatements = -1; var numberOfSkippedComments = 0; // Object arrays var fileNameArray = []; var subjectArray = []; - var audioHolderArray = []; - var audioElementArray = []; + var audioholderArray = []; + var audioelementArray = []; // End of (file, audioholder, audioelement) flags var newFile = true; @@ -51,8 +52,8 @@ var newAudioElement = true; var fileCounter = 0; // file index - var audioHolderCounter=0; // audioholder index (current XML file) - var audioElementCounter=0; // audioelement index (current audioholder) + var audioholderCounter=0; // audioholder index (current XML file) + var audioelementCounter=0; // audioelement index (current audioholder) var statementNumber=0; // total number of statements var root; // root of XML file @@ -92,7 +93,7 @@ } } - function median(values) { + function median(values) { // TODO: replace code by '50th percentile' - should be the same? values.sort( function(a,b) {return a - b;} ); var half = Math.floor(values.length/2); if(values.length % 2) @@ -100,9 +101,16 @@ else return (values[half-1] + values[half]) / 2.0; } + + function percentile(values, n) { + values.sort( function(a,b) {return a - b;} ); + // get ordinal rank + var rank = Math.min(Math.floor(values.length*n/100), values.length-1); + return values[rank]; + } /*********************** - * TIME MEASUREMENT * + * TIME MEASUREMENT * ************************/ // measure time since last time this function was called @@ -197,71 +205,71 @@ /******************************** - * PLAYBACK OF AUDIO * + * PLAYBACK OF AUDIO * ********************************/ - //PLAYaudioElement + //PLAYaudioelement // Keep track of whether audio should be played function playFlagChanged(){ playAudio = playFlag.checked; // global variable if (!playAudio){ // if audio needs to stop audio.pause(); // stop audio - if anything is playing - currently_playing = ''; // back to empty string so playaudioElement knows nothing's playing + currently_playing = ''; // back to empty string so playaudioelement knows nothing's playing } } - // audioHolder that's currently playing - var currently_playing_audioHolder = ''; // at first: empty string - var currently_playing_audioElement = ''; + // audioholder that's currently playing + var currently_playing_audioholder = ''; // at first: empty string + var currently_playing_audioelement = ''; var audio; - // Play audioElement of audioHolder if available, from start or from same position - function playaudioElement(audioHolderName, audioElementerName){ + // Play audioelement of audioholder if available, from start or from same position + function playaudioelement(audioholderName, audioelementerName){ if (playAudio) { // if enabled // get corresponding file from folder - var file_location = 'audio/'+audioHolderName + '/' + audioElementerName + '.mp3'; // fixed path and file name format + var file_location = 'audio/'+audioholderName + '/' + audioelementerName + '.mp3'; // fixed path and file name format // if not available, show error/warning message //TODO ... // if nothing playing yet, start playing - if (currently_playing_audioHolder == ''){ // signal that nothing is playing + if (currently_playing_audioholder == ''){ // signal that nothing is playing //playSound(audioBuffer); audio = new Audio(file_location); audio.loop = true; // loop when end is reached audio.play(); - currently_playing_audioHolder = audioHolderName; - currently_playing_audioElement = audioElementerName; - } else if (currently_playing_audioHolder != audioHolderName) { - // if different audioHolder playing, stop that and start playing + currently_playing_audioholder = audioholderName; + currently_playing_audioelement = audioelementerName; + } else if (currently_playing_audioholder != audioholderName) { + // if different audioholder playing, stop that and start playing audio.pause(); // stop audio audio = new Audio(file_location); // load new file audio.loop = true; // loop when end is reached audio.play(); // play audio from the start - currently_playing_audioHolder = audioHolderName; - currently_playing_audioElement = audioElementerName; - } else if (currently_playing_audioElement != audioElementerName) { - // if same audioHolder playing, start playing from where it left off + currently_playing_audioholder = audioholderName; + currently_playing_audioelement = audioelementerName; + } else if (currently_playing_audioelement != audioelementerName) { + // if same audioholder playing, start playing from where it left off skipTime = audio.currentTime; // time to skip to audio.pause(); // stop audio audio = new Audio(file_location); audio.addEventListener('loadedmetadata', function() { this.currentTime = skipTime; - console.log('Loaded '+audioHolderName+'-'+audioElementerName+', playing from '+skipTime); + console.log('Loaded '+audioholderName+'-'+audioelementerName+', playing from '+skipTime); }, false); // skip to same time when audio is loaded! audio.loop = true; // loop when end is reached audio.play(); // play from that time audio.currentTime = skipTime; - currently_playing_audioHolder = audioHolderName; - currently_playing_audioElement = audioElementerName; + currently_playing_audioholder = audioholderName; + currently_playing_audioelement = audioelementerName; } - // if same audioElement playing: keep on playing (i.e. do nothing) + // if same audioelement playing: keep on playing (i.e. do nothing) } } /******************** - * READING FILES * + * READING FILES * ********************/ // Read necessary data from XML file @@ -303,24 +311,24 @@ subjectArray.push(subjectID.textContent); // append to array } - // go over all audioHolders, add to array if not already there - audioHolderNodes = root.getElementsByTagName('audioholder'); - // go over audioHolderNodes and append audioHolder name when not present yet - for (audioHolderIndex = 0; audioHolderIndex < audioHolderNodes.length; audioHolderIndex++) { - audioHolderName = audioHolderNodes[audioHolderIndex].getAttribute('id'); - if (audioHolderArray.indexOf(audioHolderName) == -1) { // if not already in array - audioHolderArray.push(audioHolderName); // append to array + // go over all audioholders, add to array if not already there + audioholderNodes = root.getElementsByTagName('audioholder'); + // go over audioholderNodes and append audioholder name when not present yet + for (audioholderIndex = 0; audioholderIndex < audioholderNodes.length; audioholderIndex++) { + audioholderName = audioholderNodes[audioholderIndex].getAttribute('id'); + if (audioholderArray.indexOf(audioholderName) == -1) { // if not already in array + audioholderArray.push(audioholderName); // append to array } - // within each audioHolder, go over all audioElement IDs, add to array if not already there - audioElementNodes = audioHolderNodes[audioHolderIndex].getElementsByTagName('audioelement'); - for (audioElementIndex = 0; audioElementIndex < audioElementNodes.length; audioElementIndex++) { - audioElementName = audioElementNodes[audioElementIndex].getAttribute('id'); - if (audioElementArray.indexOf(audioElementName) == -1) { // if not already in array - audioElementArray.push(audioElementName); // append to array + // within each audioholder, go over all audioelement IDs, add to array if not already there + audioelementNodes = audioholderNodes[audioholderIndex].getElementsByTagName('audioelement'); + for (audioelementIndex = 0; audioelementIndex < audioelementNodes.length; audioelementIndex++) { + audioelementName = audioelementNodes[audioelementIndex].getAttribute('id'); + if (audioelementArray.indexOf(audioelementName) == -1) { // if not already in array + audioelementArray.push(audioelementName); // append to array } } } - // count occurrences of each audioHolder + // count occurrences of each audioholder // ... } else { @@ -331,8 +339,8 @@ // sort alphabetically fileNameArray.sort(); subjectArray.sort(); - audioHolderArray.sort(); - audioElementArray.sort(); + audioholderArray.sort(); + audioelementArray.sort(); // display all information in HTML // show XML file folder @@ -341,19 +349,22 @@ document.getElementById('numberOfFiles_span').innerHTML = fileNameArray.length; // show list of subject names document.getElementById('subjectArray_span').innerHTML = subjectArray.toString(); - // show list of audioHolders - document.getElementById('audioHolderArray_span').innerHTML = audioHolderArray.toString(); - // show list of audioElementes - document.getElementById('audioElementArray_span').innerHTML = audioElementArray.toString(); + // show list of audioholders + document.getElementById('audioholderArray_span').innerHTML = audioholderArray.toString(); + // show list of audioelementes + document.getElementById('audioelementArray_span').innerHTML = audioelementArray.toString(); } - function makePlots() { + function makePlots() { //TODO: split into different functions + // TEMPORARY + makeTimeline(xmlFileFolder+"/"+xmlFiles[7]); + // create value array - var ratings = []; // 3D matrix of ratings (audioHolder, audioElement, subject) - for (audioHolderIndex = 0; audioHolderIndex < audioHolderNodes.length; audioHolderIndex++) { + var ratings = []; // 3D matrix of ratings (audioholder, audioelement, subject) + for (audioholderIndex = 0; audioholderIndex < audioholderArray.length; audioholderIndex++) { ratings.push([]); - for (audioElementIndex = 0; audioElementIndex < audioElementNodes.length; audioElementIndex++) { - ratings[audioHolderIndex].push([]); + for (audioelementIndex = 0; audioelementIndex < audioelementArray.length; audioelementIndex++) { + ratings[audioholderIndex].push([]); } } @@ -364,49 +375,48 @@ if (xml != null) { // if file exists // get root of XML file root = xml.getElementsByTagName('browserevaluationresult')[0]; - // go over all audioHolders - audioHolderNodes = root.getElementsByTagName('audioholder'); - for (audioHolderIndex = 0; audioHolderIndex < audioHolderNodes.length; audioHolderIndex++) { - audioHolderName = audioHolderNodes[audioHolderIndex].getAttribute('id'); - audioElementNodes = audioHolderNodes[audioHolderIndex].getElementsByTagName('audioelement'); + // go over all audioholders + audioholderNodes = root.getElementsByTagName('audioholder'); + for (audioholderIndex = 0; audioholderIndex < audioholderNodes.length; audioholderIndex++) { + audioholderName = audioholderNodes[audioholderIndex].getAttribute('id'); + audioelementNodes = audioholderNodes[audioholderIndex].getElementsByTagName('audioelement'); // go over all audioelements - for (audioElementIndex = 0; audioElementIndex < audioElementNodes.length; audioElementIndex++) { - audioElementName = audioElementNodes[audioElementIndex].getAttribute('id'); + for (audioelementIndex = 0; audioelementIndex < audioelementNodes.length; audioelementIndex++) { + audioelementName = audioelementNodes[audioelementIndex].getAttribute('id'); // get value - var value = audioElementNodes[audioElementIndex].getElementsByTagName("value")[0].textContent; + var value = audioelementNodes[audioelementIndex].getElementsByTagName("value")[0].textContent; if (value) { // if not empty, null, undefined... ratingValue = parseFloat(value); - // add to matrix - ratings[audioHolderIndex][audioElementIndex].push(ratingValue) + // add to matrix at proper position + aHidx = audioholderArray.indexOf(audioholderName); + aEidx = audioelementArray.indexOf(audioelementName); + ratings[aHidx][aEidx].push(ratingValue); } } } - // go over all audioHolders + // go over all audioholders - // go over all audioElements within audioHolder, see if present in idMatrix, add if not + // go over all audioelements within audioholder, see if present in idMatrix, add if not // add corresponding rating to 'ratings', at position corresponding with position in idMatrix } } - for (audioHolderIndex = 0; audioHolderIndex < audioHolderArray.length; audioHolderIndex++) { - audioHolderName = audioHolderArray[audioHolderIndex]; // for this song + for (audioholderIndex = 0; audioholderIndex < audioholderArray.length; audioholderIndex++) { + audioholderName = audioholderArray[audioholderIndex]; // for this song tickArray = [] - medianOfAudioElement = [] raw_data = [['SubjectID', 'Rating']]; audioElIdx = 0; - for (audioElementIndex = 0; audioElementIndex<ratings[audioHolderIndex].length; audioElementIndex++){ - if (ratings[audioHolderIndex][audioElementIndex].length>0) { + for (audioelementIndex = 0; audioelementIndex<ratings[audioholderIndex].length; audioelementIndex++){ + if (ratings[audioholderIndex][audioelementIndex].length>0) { audioElIdx++; // increase if not empty // make tick label - tickArray.push({v:audioElIdx, f: audioElementArray[audioElementIndex]}); - // add median - medianOfAudioElement.push(median(ratings[audioHolderIndex][audioElementIndex])); + tickArray.push({v:audioElIdx, f: audioelementArray[audioelementIndex]}); } - for (subject = 0; subject<ratings[audioHolderIndex][audioElementIndex].length; subject++){ + for (subject = 0; subject<ratings[audioholderIndex][audioelementIndex].length; subject++){ // add subject-value pair for each subject - raw_data.push([audioElIdx, ratings[audioHolderIndex][audioElementIndex][subject]]); + raw_data.push([audioElIdx, ratings[audioholderIndex][audioelementIndex][subject]]); } } @@ -414,8 +424,8 @@ var data = google.visualization.arrayToDataTable(raw_data); var options = { - title: audioHolderName, - hAxis: {title: 'Subject', minValue: 0, maxValue: audioElIdx+1, + title: audioholderName, + hAxis: {title: 'audioelement ID', minValue: 0, maxValue: audioElIdx+1, ticks: tickArray}, vAxis: {title: 'Rating', minValue: 0, maxValue: 1}, seriesType: 'scatter', @@ -423,36 +433,151 @@ }; var div = document.createElement('div'); document.body.appendChild(div); - div.id = 'div_'+audioHolderName; + div.id = 'div_'+audioholderName; div.style.width = '1100px'; div.style.height = '350px'; - var chart = new google.visualization.ComboChart(document.getElementById('div_'+audioHolderName)); + var chart = new google.visualization.ComboChart(document.getElementById('div_'+audioholderName)); chart.draw(data, options); // box plots - // function drawVisualization() { - // // Create and populate the data table. - // var data = google.visualization.arrayToDataTable([ - // ['ID', 'IQR', '', '', '', 'Median', 'Average'], - // ['Serie1', 20, 28, 38, 45, 20, 25], - // ['Serie2', 31, 38, 55, 66, 30, 35], - // ['Serie3', 50, 55, 77, 80, 10, 15], - // ['Serie4', 77, 77, 66, 50, 20, 25], - // ['Serie5', 68, 66, 22, 15, 30, 35] - // // Treat first row as data as well. - // ]); - // // Create and draw the visualization. - // var ac = new google.visualization.ComboChart(document.getElementById('visualization')); - // ac.draw(data, { - // title : 'Box Plot with Median and Average', - // width: 600, - // height: 400, - // vAxis: {title: "Value"}, - // hAxis: {title: "Serie ID"}, - // series: { 0: {type: "candlesticks"}, 1: {type: "line", pointSize: 10, lineWidth: - // 0 }, 2: {type: "line", pointSize: 10, lineWidth: 0, color: 'black' } } - // }); - // } + var div = document.createElement('div'); + document.body.appendChild(div); + div.id = 'div_box_'+audioholderName; + div.style.width = '1100px'; + div.style.height = '350px'; + // Get median, percentiles, maximum and minimum; outliers. + pctl25 = []; + pctl75 = []; + med = []; + min = []; + max = []; + outlierArray = []; + max_n_outliers = 0; // maximum number of outliers for one audioelement + for (audioelementIndex = 0; audioelementIndex<ratings[audioholderIndex].length; audioelementIndex++){ + med.push(median(ratings[audioholderIndex][audioelementIndex])); // median + pctl25.push(percentile(ratings[audioholderIndex][audioelementIndex], 25)); // 25th percentile + pctl75.push(percentile(ratings[audioholderIndex][audioelementIndex], 75)); // 75th percentile + IQR = pctl75[pctl75.length-1]-pctl25[pctl25.length-1]; + // outliers: range of values which is above pctl75+1.5*IQR or below pctl25-1.5*IQR + outliers = []; + rest = []; + for (idx = 0; idx<ratings[audioholderIndex][audioelementIndex].length; idx++){ + if (ratings[audioholderIndex][audioelementIndex][idx] > pctl75[pctl75.length-1]+1.5*IQR || + ratings[audioholderIndex][audioelementIndex][idx] < pctl25[pctl25.length-1]-1.5*IQR){ + outliers.push(ratings[audioholderIndex][audioelementIndex][idx]); + } + else { + rest.push(ratings[audioholderIndex][audioelementIndex][idx]); + } + } + outlierArray.push(outliers); + max_n_outliers = Math.max(max_n_outliers, outliers.length); // update max mber + // max: maximum value which is not outlier + max.push(Math.max.apply(null, rest)); + // min: minimum value which is not outlier + min.push(Math.min.apply(null, rest)); + } + + // Build data array + boxplot_data = [['ID', 'Span', '', '', '', 'Median']]; + for (idx = 0; idx < max_n_outliers; idx++) { + boxplot_data[0].push('Outlier'); + } + for (audioelementIndex = 0; audioelementIndex<ratings[audioholderIndex].length; audioelementIndex++){ + if (ratings[audioholderIndex][audioelementIndex].length>0) { // if rating array not empty for this audioelement + data_array = [ + audioelementArray[audioelementIndex], // name + min[audioelementIndex], // minimum + pctl75[audioelementIndex], + pctl25[audioelementIndex], + max[audioelementIndex], // maximum + med[audioelementIndex] + ]; + for (idx = 0; idx < max_n_outliers; idx++) { + if (idx<outlierArray[audioelementIndex].length){ + data_array.push(outlierArray[audioelementIndex][idx]); + } + else { + data_array.push(null); + } + } + boxplot_data.push(data_array); + } + } + + // Create and populate the data table. + var data = google.visualization.arrayToDataTable(boxplot_data); + // Create and draw the visualization. + var ac = new google.visualization.ComboChart(document.getElementById('div_box_'+audioholderName)); + ac.draw(data, { + title : audioholderName, + //width: 600, + //height: 400, + vAxis: {title: "Rating"}, + hAxis: {title: "audioelement ID"}, + seriesType: "line", + pointSize: 5, + lineWidth: 0, + colors: ['black'], + series: { 0: {type: "candlesticks", color: 'blue'}, // box plot shape + 1: {type: "line", pointSize: 10, lineWidth: 0, color: 'red' } }, // median + legend: 'none' + }); + } + } + + function makeTimeline(xmlFileName){ // WIP + // Based on the XML file name, take time data and plot playback and marker movements + + // read XML file and check if exists + xml = readXML(xmlFileName); + if (!xml) { // if file does not exist + console.log('XML file '+xml+'does not exist. ('+xmlFileName+')') + return; // do nothing; exit function + } + // get root of XML file + root = xml.getElementsByTagName('browserevaluationresult')[0]; + + audioholder_time = 0; + previous_audioholder_time = 0; // time spent before current audioholder + time_offset = 0; // test starts at zero + + // go over all audioholders + audioholderNodes = root.getElementsByTagName('audioholder'); + for (audioholderIndex = 0; audioholderIndex < audioholderNodes.length; audioholderIndex++) { + audioholderName = audioholderNodes[audioholderIndex].getAttribute('id'); + if (!audioholderName) { + console.log('audioholder name is empty; go to next one. ('+xmlFileName+')'); + break; + } + + // subtract total audioholder length from subsequent audioholder event times + audioholder_children = audioholderNodes[audioholderIndex].childNodes; + foundIt = false; + console.log(audioholder_children[2].getElementsByTagName("metricResult")) // not working! + for (idx = 0; idx<audioholder_children.length; idx++) { // go over children + + if (audioholder_children[idx].getElementsByTagName('metricResult').length) { + console.log(audioholder_children[idx].getElementsByTagName('metricResult')[0]); + if (audioholder_children[idx].getElementsByTagName('metricResult')[0].getAttribute('id') == "testTime"){ + audioholder_time = parseFloat(audioholder_children[idx].getElementsByTagName('metricResult')[0].textContent); + console.log(audioholder_time); + foundIt = true; + } + } + } + if (!foundIt) { + console.log("Skipping audioholder without total time specified from "+xmlFileName+"."); // always hitting this + break; + } + + audioelementNodes = audioholderNodes[audioholderIndex].getElementsByTagName('audioelement'); + + // make div + + // draw chart + + // legend with audioelement names } } @@ -500,19 +625,19 @@ overflow: hidden; /* add this to contain floated children */ } div#instrumentSection { - width: 250px; - border: 1px solid red; - display: inline-block; + width: 250px; + border: 1px solid red; + display: inline-block; } div#featureSection { width: 250px; - border: 1px solid green; - display: inline-block; + border: 1px solid green; + display: inline-block; } div#valenceSection { width: 250px; - border: 1px solid blue; - display: inline-block; + border: 1px solid blue; + display: inline-block; } button#previousComment{ width: 120px; @@ -547,13 +672,13 @@ <strong>Result XML files:</strong> <span id="numberOfFiles_span"></span> </div> <div> - <strong>Audioholders in dataset:</strong> <span id="audioHolderArray_span"></span> + <strong>Audioholders in dataset:</strong> <span id="audioholderArray_span"></span> </div> <div> <strong>Subjects in dataset:</strong> <span id="subjectArray_span"></span> </div> <div> - <strong>Audioelements in dataset:</strong> <span id="audioElementArray_span"></span> + <strong>Audioelements in dataset:</strong> <span id="audioelementArray_span"></span> </div> <br> </div>