me@1942: /** me@1942: * mushra.js me@1942: * Create the MUSHRA interface me@1942: */ me@1942: me@1942: // Once this is loaded and parsed, begin execution me@1942: loadInterface(); me@1942: me@1942: function loadInterface() { me@1942: // Get the dimensions of the screen available to the page me@1942: var width = window.innerWidth; me@1942: var height = window.innerHeight; me@1942: me@1942: // The injection point into the HTML page me@1942: interfaceContext.insertPoint = document.getElementById("topLevelBody"); me@1942: var testContent = document.createElement('div'); me@1942: testContent.id = 'testContent'; me@1942: me@1942: // Create the top div for the Title element me@1942: var titleAttr = specification.title; me@1942: var title = document.createElement('div'); me@1942: title.className = "title"; me@1942: title.align = "center"; me@1942: var titleSpan = document.createElement('span'); me@1942: me@1942: // Set title to that defined in XML, else set to default me@1942: if (titleAttr != undefined) { me@1942: titleSpan.textContent = titleAttr; me@1942: } else { me@1942: titleSpan.textContent = 'Listening test'; me@1942: } me@1942: // Insert the titleSpan element into the title div element. me@1942: title.appendChild(titleSpan); me@1942: me@1942: var pagetitle = document.createElement('div'); me@1942: pagetitle.className = "pageTitle"; me@1942: pagetitle.align = "center"; me@1942: var titleSpan = document.createElement('span'); me@1942: titleSpan.id = "pageTitle"; me@1942: pagetitle.appendChild(titleSpan); me@1942: me@1942: // Create Interface buttons! me@1942: var interfaceButtons = document.createElement('div'); me@1942: interfaceButtons.id = 'interface-buttons'; me@1942: me@1942: // Create playback start/stop points me@1942: var playback = document.createElement("button"); me@1942: playback.innerHTML = 'Stop'; me@1942: playback.id = 'playback-button'; me@1942: // onclick function. Check if it is playing or not, call the correct function in the me@1942: // audioEngine, change the button text to reflect the next state. me@1942: playback.onclick = function() { me@1942: if (audioEngineContext.status == 1) { me@1942: audioEngineContext.stop(); me@1942: this.innerHTML = 'Stop'; me@1942: var time = audioEngineContext.timer.getTestTime(); me@1942: console.log('Stopped at ' + time); // DEBUG/SAFETY me@1942: } me@1942: }; me@1942: // Create Submit (save) button me@1942: var submit = document.createElement("button"); me@1942: submit.innerHTML = 'Submit'; me@1942: submit.onclick = buttonSubmitClick; me@1942: submit.id = 'submit-button'; me@1942: // Append the interface buttons into the interfaceButtons object. me@1942: interfaceButtons.appendChild(playback); me@1942: interfaceButtons.appendChild(submit); me@1942: me@1942: // Create a slider box me@1942: var sliderBox = document.createElement('div'); me@1942: sliderBox.style.width = "100%"; me@1942: sliderBox.style.height = window.innerHeight - 180 + 'px'; me@1942: sliderBox.id = 'slider'; me@1942: sliderBox.align = "center"; me@1942: me@1942: // Global parent for the comment boxes on the page me@1942: var feedbackHolder = document.createElement('div'); me@1942: feedbackHolder.id = 'feedbackHolder'; me@1942: me@1942: testContent.style.zIndex = 1; me@1942: interfaceContext.insertPoint.innerHTML = null; // Clear the current schema me@1942: me@1942: // Inject into HTML me@1942: testContent.appendChild(title); // Insert the title me@1942: testContent.appendChild(pagetitle); me@1942: testContent.appendChild(interfaceButtons); me@1942: testContent.appendChild(sliderBox); me@1942: testContent.appendChild(feedbackHolder); me@1942: interfaceContext.insertPoint.appendChild(testContent); me@1942: me@1942: // Load the full interface me@1942: testState.initialise(); me@1942: testState.advanceState(); me@1942: } me@1942: me@1942: function loadTest(audioHolderObject) me@1942: { me@1942: // Reset audioEngineContext.Metric globals for new test me@1942: audioEngineContext.newTestPage(); me@1942: me@1942: // Delete any previous audioObjects associated with the audioEngine me@1942: audioEngineContext.audioObjects = []; me@1942: interfaceContext.deleteCommentBoxes(); me@1942: interfaceContext.deleteCommentQuestions(); me@1942: me@1942: var id = audioHolderObject.id; me@1942: me@1942: var feedbackHolder = document.getElementById('feedbackHolder'); me@1942: var interfaceObj = audioHolderObject.interfaces; me@1942: me@1942: var sliderBox = document.getElementById('slider'); me@1942: feedbackHolder.innerHTML = null; me@1942: sliderBox.innerHTML = null; me@1942: me@1942: var commentBoxPrefix = "Comment on track"; me@1942: if (interfaceObj.commentBoxPrefix != undefined) { me@1942: commentBoxPrefix = interfaceObj.commentBoxPrefix; me@1942: } me@1942: me@1942: /// CHECK FOR SAMPLE RATE COMPATIBILITY me@1942: if (audioHolderObject.sampleRate != undefined) { me@1942: if (Number(audioHolderObject.sampleRate) != audioContext.sampleRate) { me@1942: 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: alert(errStr); me@1942: return; me@1942: } me@1942: } me@1942: me@1942: var loopPlayback = audioHolderObject.loop; me@1942: me@1942: audioEngineContext.loopPlayback = loopPlayback; me@1942: me@1942: currentTestHolder = document.createElement('audioHolder'); me@1942: currentTestHolder.id = audioHolderObject.id; me@1942: currentTestHolder.repeatCount = audioHolderObject.repeatCount; me@1942: me@1942: $(audioHolderObject.commentQuestions).each(function(index,element) { me@1942: var node = interfaceContext.createCommentQuestion(element); me@1942: feedbackHolder.appendChild(node.holder); me@1942: }); me@1942: me@1942: // Find all the audioElements from the audioHolder me@1942: $(audioHolderObject.audioElements).each(function(index,element){ me@1942: // Find URL of track me@1942: // In this jQuery loop, variable 'this' holds the current audioElement. me@1942: me@1942: // Now load each audio sample. First create the new track by passing the full URL me@1942: var trackURL = audioHolderObject.hostURL + element.url; me@1942: var audioObject = audioEngineContext.newTrack(element); me@1942: me@1942: var node = interfaceContext.createCommentBox(audioObject); me@1942: me@1942: // Create a slider per track me@1942: audioObject.interfaceDOM = new sliderObject(audioObject); me@1942: me@1942: // Distribute it randomnly me@1942: audioObject.interfaceDOM.slider.value = Math.random(); me@1942: me@1942: sliderBox.appendChild(audioObject.interfaceDOM.holder); me@1942: audioObject.metric.initialised(audioObject.interfaceDOM.slider.value); me@1942: me@1942: }); me@1942: me@1942: // Auto-align me@1942: var numObj = audioHolderObject.audioElements.length; me@1942: var totalWidth = (numObj-1)*150+100; me@1942: var diff = (window.innerWidth - totalWidth)/2; me@1942: audioEngineContext.audioObjects[0].interfaceDOM.holder.style.marginLeft = diff + 'px'; me@1942: } me@1942: me@1942: function sliderObject(audioObject) me@1942: { me@1942: // Constructs the slider object. We use the HTML5 slider object me@1942: this.parent = audioObject; me@1942: this.holder = document.createElement('div'); me@1942: this.title = document.createElement('span'); me@1942: this.slider = document.createElement('input'); me@1942: this.play = document.createElement('button'); me@1942: me@1942: this.holder.className = 'track-slider'; me@1942: this.holder.style.height = window.innerHeight-200 + 'px'; me@1942: this.holder.appendChild(this.title); me@1942: this.holder.appendChild(this.slider); me@1942: this.holder.appendChild(this.play); me@1942: this.holder.align = "center"; me@1942: this.holder.style.marginLeft = "50px"; me@1942: this.holder.setAttribute('trackIndex',audioObject.id); me@1942: me@1942: this.title.textContent = audioObject.id; me@1942: this.title.style.width = "100%"; me@1942: this.title.style.float = "left"; me@1942: me@1942: this.slider.type = "range"; me@1942: this.slider.min = "0"; me@1942: this.slider.max = "1"; me@1942: this.slider.step = "0.01"; me@1942: this.slider.setAttribute('orient','vertical'); me@1942: this.slider.style.float = "left"; me@1942: this.slider.style.width = "100%"; me@1942: this.slider.style.height = window.innerHeight-250 + 'px'; me@1942: this.slider.onchange = function() me@1942: { me@1942: var time = audioEngineContext.timer.getTestTime(); me@1942: var id = Number(this.parentNode.getAttribute('trackIndex')); me@1942: audioEngineContext.audioObjects[id].metric.moved(time,this.value); me@1942: console.log('slider '+id+' moved to '+this.value+' ('+time+')'); me@1942: }; me@1942: me@1942: this.play.textContent = "Play"; me@1942: this.play.value = audioObject.id; me@1942: this.play.style.float = "left"; me@1942: this.play.style.width = "100%"; me@1942: this.play.onclick = function() me@1942: { me@1942: audioEngineContext.play(); me@1942: if (audioEngineContext.audioObjectsReady) { me@1942: var id = Number(event.srcElement.value); me@1942: //audioEngineContext.metric.sliderPlayed(id); me@1942: audioEngineContext.play(id); me@1942: } me@1942: }; me@1942: me@1942: this.enable = function() { me@1942: if (this.parent.state == 1) me@1942: { me@1942: $(this.slider).removeClass('track-slider-disabled'); me@1942: } me@1942: }; me@1942: me@1942: this.exportXMLDOM = function(audioObject) { me@1942: // Called by the audioObject holding this element. Must be present me@1942: var node = document.createElement('value'); me@1942: node.textContent = this.slider.value; me@1942: return node; me@1942: }; me@1942: this.getValue = function() { me@1942: return this.slider.value; me@1942: }; me@1942: } me@1942: me@1942: me@1942: function buttonSubmitClick() // TODO: Only when all songs have been played! me@1942: { me@1942: var checks = testState.currentStateMap[testState.currentIndex].interfaces[0].options; me@1942: var canContinue = true; me@1942: me@1942: // Check that the anchor and reference objects are correctly placed me@1942: if (interfaceContext.checkHiddenAnchor() == false) {return;} me@1942: if (interfaceContext.checkHiddenReference() == false) {return;} me@1942: /* me@1942: for (var i=0; i