annotate Perceptual Evaluation/webaudioevaluationtool/mushra.js @ 0:55c282f01a30 tip

Adding files to Repo. Initial Commit
author Dave <d.j.moffat@qmul.ac.uk>
date Fri, 16 Oct 2015 18:04:00 +0100
parents
children
rev   line source
d@0 1 /**
d@0 2 * mushra.js
d@0 3 * Create the MUSHRA interface
d@0 4 */
d@0 5
d@0 6 // Once this is loaded and parsed, begin execution
d@0 7 loadInterface();
d@0 8
d@0 9 function loadInterface() {
d@0 10 // Get the dimensions of the screen available to the page
d@0 11 var width = window.innerWidth;
d@0 12 var height = window.innerHeight;
d@0 13
d@0 14 // The injection point into the HTML page
d@0 15 interfaceContext.insertPoint = document.getElementById("topLevelBody");
d@0 16 var testContent = document.createElement('div');
d@0 17 testContent.id = 'testContent';
d@0 18
d@0 19 // Create the top div for the Title element
d@0 20 var titleAttr = specification.title;
d@0 21 var title = document.createElement('div');
d@0 22 title.className = "title";
d@0 23 title.align = "center";
d@0 24 var titleSpan = document.createElement('span');
d@0 25
d@0 26 // Set title to that defined in XML, else set to default
d@0 27 if (titleAttr != undefined) {
d@0 28 titleSpan.textContent = titleAttr;
d@0 29 } else {
d@0 30 titleSpan.textContent = 'Listening test';
d@0 31 }
d@0 32 // Insert the titleSpan element into the title div element.
d@0 33 title.appendChild(titleSpan);
d@0 34
d@0 35 var pagetitle = document.createElement('div');
d@0 36 pagetitle.className = "pageTitle";
d@0 37 pagetitle.align = "center";
d@0 38 var titleSpan = document.createElement('span');
d@0 39 titleSpan.id = "pageTitle";
d@0 40 pagetitle.appendChild(titleSpan);
d@0 41
d@0 42 // Create Interface buttons!
d@0 43 var interfaceButtons = document.createElement('div');
d@0 44 interfaceButtons.id = 'interface-buttons';
d@0 45
d@0 46 // Create playback start/stop points
d@0 47 var playback = document.createElement("button");
d@0 48 playback.innerHTML = 'Stop';
d@0 49 playback.id = 'playback-button';
d@0 50 // onclick function. Check if it is playing or not, call the correct function in the
d@0 51 // audioEngine, change the button text to reflect the next state.
d@0 52 playback.onclick = function() {
d@0 53 if (audioEngineContext.status == 1) {
d@0 54 audioEngineContext.stop();
d@0 55 this.innerHTML = 'Stop';
d@0 56 var time = audioEngineContext.timer.getTestTime();
d@0 57 console.log('Stopped at ' + time); // DEBUG/SAFETY
d@0 58 }
d@0 59 };
d@0 60 // Create Submit (save) button
d@0 61 var submit = document.createElement("button");
d@0 62 submit.innerHTML = 'Submit';
d@0 63 submit.onclick = buttonSubmitClick;
d@0 64 submit.id = 'submit-button';
d@0 65 // Append the interface buttons into the interfaceButtons object.
d@0 66 interfaceButtons.appendChild(playback);
d@0 67 interfaceButtons.appendChild(submit);
d@0 68
d@0 69 // Create a slider box
d@0 70 var sliderBox = document.createElement('div');
d@0 71 sliderBox.style.width = "100%";
d@0 72 sliderBox.style.height = window.innerHeight - 180 + 'px';
d@0 73 sliderBox.id = 'slider';
d@0 74 sliderBox.align = "center";
d@0 75
d@0 76 // Global parent for the comment boxes on the page
d@0 77 var feedbackHolder = document.createElement('div');
d@0 78 feedbackHolder.id = 'feedbackHolder';
d@0 79
d@0 80 testContent.style.zIndex = 1;
d@0 81 interfaceContext.insertPoint.innerHTML = null; // Clear the current schema
d@0 82
d@0 83 // Inject into HTML
d@0 84 testContent.appendChild(title); // Insert the title
d@0 85 testContent.appendChild(pagetitle);
d@0 86 testContent.appendChild(interfaceButtons);
d@0 87 testContent.appendChild(sliderBox);
d@0 88 testContent.appendChild(feedbackHolder);
d@0 89 interfaceContext.insertPoint.appendChild(testContent);
d@0 90
d@0 91 // Load the full interface
d@0 92 testState.initialise();
d@0 93 testState.advanceState();
d@0 94 }
d@0 95
d@0 96 function loadTest(audioHolderObject)
d@0 97 {
d@0 98 // Reset audioEngineContext.Metric globals for new test
d@0 99 audioEngineContext.newTestPage();
d@0 100
d@0 101 // Delete any previous audioObjects associated with the audioEngine
d@0 102 audioEngineContext.audioObjects = [];
d@0 103 interfaceContext.deleteCommentBoxes();
d@0 104 interfaceContext.deleteCommentQuestions();
d@0 105
d@0 106 var id = audioHolderObject.id;
d@0 107
d@0 108 var feedbackHolder = document.getElementById('feedbackHolder');
d@0 109 var interfaceObj = audioHolderObject.interfaces;
d@0 110
d@0 111 var sliderBox = document.getElementById('slider');
d@0 112 feedbackHolder.innerHTML = null;
d@0 113 sliderBox.innerHTML = null;
d@0 114
d@0 115 var commentBoxPrefix = "Comment on track";
d@0 116 if (interfaceObj.commentBoxPrefix != undefined) {
d@0 117 commentBoxPrefix = interfaceObj.commentBoxPrefix;
d@0 118 }
d@0 119
d@0 120 /// CHECK FOR SAMPLE RATE COMPATIBILITY
d@0 121 if (audioHolderObject.sampleRate != undefined) {
d@0 122 if (Number(audioHolderObject.sampleRate) != audioContext.sampleRate) {
d@0 123 var errStr = 'Sample rates do not match! Requested '+Number(audioHolderObject.sampleRate)+', got '+audioContext.sampleRate+'. Please set the sample rate to match before completing this test.';
d@0 124 alert(errStr);
d@0 125 return;
d@0 126 }
d@0 127 }
d@0 128
d@0 129 var loopPlayback = audioHolderObject.loop;
d@0 130
d@0 131 audioEngineContext.loopPlayback = loopPlayback;
d@0 132
d@0 133 currentTestHolder = document.createElement('audioHolder');
d@0 134 currentTestHolder.id = audioHolderObject.id;
d@0 135 currentTestHolder.repeatCount = audioHolderObject.repeatCount;
d@0 136
d@0 137 $(audioHolderObject.commentQuestions).each(function(index,element) {
d@0 138 var node = interfaceContext.createCommentQuestion(element);
d@0 139 feedbackHolder.appendChild(node.holder);
d@0 140 });
d@0 141
d@0 142 // Find all the audioElements from the audioHolder
d@0 143 $(audioHolderObject.audioElements).each(function(index,element){
d@0 144 // Find URL of track
d@0 145 // In this jQuery loop, variable 'this' holds the current audioElement.
d@0 146
d@0 147 // Now load each audio sample. First create the new track by passing the full URL
d@0 148 var trackURL = audioHolderObject.hostURL + element.url;
d@0 149 var audioObject = audioEngineContext.newTrack(element);
d@0 150
d@0 151 var node = interfaceContext.createCommentBox(audioObject);
d@0 152
d@0 153 // Create a slider per track
d@0 154 audioObject.interfaceDOM = new sliderObject(audioObject);
d@0 155
d@0 156 // Distribute it randomnly
d@0 157 audioObject.interfaceDOM.slider.value = Math.random();
d@0 158
d@0 159 sliderBox.appendChild(audioObject.interfaceDOM.holder);
d@0 160 audioObject.metric.initialised(audioObject.interfaceDOM.slider.value);
d@0 161
d@0 162 });
d@0 163
d@0 164 // Auto-align
d@0 165 var numObj = audioHolderObject.audioElements.length;
d@0 166 var totalWidth = (numObj-1)*150+100;
d@0 167 var diff = (window.innerWidth - totalWidth)/2;
d@0 168 audioEngineContext.audioObjects[0].interfaceDOM.holder.style.marginLeft = diff + 'px';
d@0 169 }
d@0 170
d@0 171 function sliderObject(audioObject)
d@0 172 {
d@0 173 // Constructs the slider object. We use the HTML5 slider object
d@0 174 this.parent = audioObject;
d@0 175 this.holder = document.createElement('div');
d@0 176 this.title = document.createElement('span');
d@0 177 this.slider = document.createElement('input');
d@0 178 this.play = document.createElement('button');
d@0 179
d@0 180 this.holder.className = 'track-slider';
d@0 181 this.holder.style.height = window.innerHeight-200 + 'px';
d@0 182 this.holder.appendChild(this.title);
d@0 183 this.holder.appendChild(this.slider);
d@0 184 this.holder.appendChild(this.play);
d@0 185 this.holder.align = "center";
d@0 186 this.holder.style.marginLeft = "50px";
d@0 187 this.holder.setAttribute('trackIndex',audioObject.id);
d@0 188
d@0 189 this.title.textContent = audioObject.id;
d@0 190 this.title.style.width = "100%";
d@0 191 this.title.style.float = "left";
d@0 192
d@0 193 this.slider.type = "range";
d@0 194 this.slider.min = "0";
d@0 195 this.slider.max = "1";
d@0 196 this.slider.step = "0.01";
d@0 197 this.slider.setAttribute('orient','vertical');
d@0 198 this.slider.style.float = "left";
d@0 199 this.slider.style.width = "100%";
d@0 200 this.slider.style.height = window.innerHeight-250 + 'px';
d@0 201 this.slider.onchange = function()
d@0 202 {
d@0 203 var time = audioEngineContext.timer.getTestTime();
d@0 204 var id = Number(this.parentNode.getAttribute('trackIndex'));
d@0 205 audioEngineContext.audioObjects[id].metric.moved(time,this.value);
d@0 206 console.log('slider '+id+' moved to '+this.value+' ('+time+')');
d@0 207 };
d@0 208
d@0 209 this.play.textContent = "Play";
d@0 210 this.play.value = audioObject.id;
d@0 211 this.play.style.float = "left";
d@0 212 this.play.style.width = "100%";
d@0 213 this.play.onclick = function()
d@0 214 {
d@0 215 audioEngineContext.play();
d@0 216 if (audioEngineContext.audioObjectsReady) {
d@0 217 var id = Number(event.srcElement.value);
d@0 218 //audioEngineContext.metric.sliderPlayed(id);
d@0 219 audioEngineContext.play(id);
d@0 220 }
d@0 221 };
d@0 222
d@0 223 this.enable = function() {
d@0 224 if (this.parent.state == 1)
d@0 225 {
d@0 226 $(this.slider).removeClass('track-slider-disabled');
d@0 227 }
d@0 228 };
d@0 229
d@0 230 this.exportXMLDOM = function(audioObject) {
d@0 231 // Called by the audioObject holding this element. Must be present
d@0 232 var node = document.createElement('value');
d@0 233 node.textContent = this.slider.value;
d@0 234 return node;
d@0 235 };
d@0 236 this.getValue = function() {
d@0 237 return this.slider.value;
d@0 238 };
d@0 239 }
d@0 240
d@0 241
d@0 242 function buttonSubmitClick() // TODO: Only when all songs have been played!
d@0 243 {
d@0 244 var checks = testState.currentStateMap[testState.currentIndex].interfaces[0].options;
d@0 245 var canContinue = true;
d@0 246
d@0 247 // Check that the anchor and reference objects are correctly placed
d@0 248 if (interfaceContext.checkHiddenAnchor() == false) {return;}
d@0 249 if (interfaceContext.checkHiddenReference() == false) {return;}
d@0 250 /*
d@0 251 for (var i=0; i<checks.length; i++) {
d@0 252 if (checks[i].type == 'check')
d@0 253 {
d@0 254 switch(checks[i].check) {
d@0 255 case 'fragmentPlayed':
d@0 256 // Check if all fragments have been played
d@0 257 var checkState = interfaceContext.checkAllPlayed();
d@0 258 if (checkState == false) {canContinue = false;}
d@0 259 break;
d@0 260 case 'fragmentFullPlayback':
d@0 261 // Check all fragments have been played to their full length
d@0 262 var checkState = interfaceContext.checkAllPlayed();
d@0 263 if (checkState == false) {canContinue = false;}
d@0 264 console.log('NOTE: fragmentFullPlayback not currently implemented, performing check fragmentPlayed instead');
d@0 265 break;
d@0 266 case 'fragmentMoved':
d@0 267 // Check all fragment sliders have been moved.
d@0 268 var checkState = interfaceContext.checkAllMoved();
d@0 269 if (checkState == false) {canContinue = false;}
d@0 270 break;
d@0 271 case 'fragmentComments':
d@0 272 // Check all fragment sliders have been moved.
d@0 273 var checkState = interfaceContext.checkAllCommented();
d@0 274 if (checkState == false) {canContinue = false;}
d@0 275 break;
d@0 276 case 'scalerange':
d@0 277 // Check the scale is used to its full width outlined by the node
d@0 278 var checkState = interfaceContext.checkScaleRange();
d@0 279 if (checkState == false) {canContinue = false;}
d@0 280 break;
d@0 281 }
d@0 282
d@0 283 }
d@0 284 if (!canContinue) {break;}
d@0 285 }
d@0 286 */
d@0 287 if (canContinue) {
d@0 288 if (audioEngineContext.status == 1) {
d@0 289 var playback = document.getElementById('playback-button');
d@0 290 playback.click();
d@0 291 // This function is called when the submit button is clicked. Will check for any further tests to perform, or any post-test options
d@0 292 } else
d@0 293 {
d@0 294 if (audioEngineContext.timer.testStarted == false)
d@0 295 {
d@0 296 alert('You have not started the test! Please press start to begin the test!');
d@0 297 return;
d@0 298 }
d@0 299 }
d@0 300 testState.advanceState();
d@0 301 }
d@0 302 }
d@0 303
d@0 304 function pageXMLSave(store, testXML)
d@0 305 {
d@0 306 // Saves a specific test page
d@0 307 var xmlDoc = store;
d@0 308 // Check if any session wide metrics are enabled
d@0 309
d@0 310 var commentShow = testXML.elementComments;
d@0 311
d@0 312 var metric = document.createElement('metric');
d@0 313 if (audioEngineContext.metric.enableTestTimer)
d@0 314 {
d@0 315 var testTime = document.createElement('metricResult');
d@0 316 testTime.id = 'testTime';
d@0 317 testTime.textContent = audioEngineContext.timer.testDuration;
d@0 318 metric.appendChild(testTime);
d@0 319 }
d@0 320 xmlDoc.appendChild(metric);
d@0 321 var audioObjects = audioEngineContext.audioObjects;
d@0 322 for (var i=0; i<audioObjects.length; i++)
d@0 323 {
d@0 324 var audioElement = audioEngineContext.audioObjects[i].exportXMLDOM();
d@0 325 audioElement.setAttribute('presentedId',i);
d@0 326 xmlDoc.appendChild(audioElement);
d@0 327 }
d@0 328
d@0 329 $(interfaceContext.commentQuestions).each(function(index,element){
d@0 330 var node = element.exportXMLDOM();
d@0 331 xmlDoc.appendChild(node);
d@0 332 });
d@0 333 store = xmlDoc;
d@0 334 }