annotate mushra.js @ 1946:1d08f8aee098

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