annotate mushra.js @ 1490:af5eaa5d66f2

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