Mercurial > hg > webaudioevaluationtool
comparison js/core.js @ 2681:95f614a82762
#180. Tidying up of audioEngineContext
author | Nicholas Jillings <nicholas.jillings@mail.bcu.ac.uk> |
---|---|
date | Wed, 01 Mar 2017 15:13:01 +0000 |
parents | 470bbfd78a96 |
children | 0a72855de078 |
comparison
equal
deleted
inserted
replaced
2680:470bbfd78a96 | 2681:95f614a82762 |
---|---|
1222 this.initialise = function () { | 1222 this.initialise = function () { |
1223 | 1223 |
1224 // Get the data from Specification | 1224 // Get the data from Specification |
1225 var pagePool = []; | 1225 var pagePool = []; |
1226 var pageInclude = []; | 1226 var pageInclude = []; |
1227 for (var i = 0; i < specification.pages.length; i++) { | 1227 var i; |
1228 for (i = 0; i < specification.pages.length; i++) { | |
1228 var page = specification.pages[i]; | 1229 var page = specification.pages[i]; |
1229 if (page.alwaysInclude) { | 1230 if (page.alwaysInclude) { |
1230 pageInclude.push(page); | 1231 pageInclude.push(page); |
1231 } else { | 1232 } else { |
1232 pagePool.push(page); | 1233 pagePool.push(page); |
1259 | 1260 |
1260 // We now have our selected pages in pageInclude array | 1261 // We now have our selected pages in pageInclude array |
1261 if (specification.randomiseOrder) { | 1262 if (specification.randomiseOrder) { |
1262 pageInclude = randomiseOrder(pageInclude); | 1263 pageInclude = randomiseOrder(pageInclude); |
1263 } | 1264 } |
1264 for (var i = 0; i < pageInclude.length; i++) { | 1265 for (i = 0; i < pageInclude.length; i++) { |
1265 pageInclude[i].presentedId = i; | 1266 pageInclude[i].presentedId = i; |
1266 this.stateMap.push(pageInclude[i]); | 1267 this.stateMap.push(pageInclude[i]); |
1267 // For each selected page, we must get the sub pool | 1268 // For each selected page, we must get the sub pool |
1268 if (pageInclude[i].poolSize !== 0 && pageInclude[i].poolSize !== pageInclude[i].audioElements.length) { | 1269 if (pageInclude[i].poolSize !== 0 && pageInclude[i].poolSize !== pageInclude[i].audioElements.length) { |
1269 var elemInclude = []; | 1270 var elemInclude = []; |
1270 var elemPool = []; | 1271 var elemPool = []; |
1271 for (var elem of pageInclude[i].audioElements) { | 1272 for (var j = 0; j < pageInclude[i].audioElements.length; j++) { |
1273 var elem = pageInclude[i].audioElements[j]; | |
1272 if (elem.alwaysInclude || elem.type != "normal") { | 1274 if (elem.alwaysInclude || elem.type != "normal") { |
1273 elemInclude.push(elem); | 1275 elemInclude.push(elem); |
1274 } else { | 1276 } else { |
1275 elemPool.push(elem); | 1277 elemPool.push(elem); |
1276 } | 1278 } |
1340 if (this.currentStateMap === null) { | 1342 if (this.currentStateMap === null) { |
1341 this.currentStateMap = this.stateMap[this.stateIndex]; | 1343 this.currentStateMap = this.stateMap[this.stateIndex]; |
1342 // Find and extract the outside reference | 1344 // Find and extract the outside reference |
1343 var elements = [], | 1345 var elements = [], |
1344 ref = []; | 1346 ref = []; |
1345 var elem; | 1347 var elem = this.currentStateMap.audioElements.pop(); |
1346 while (elem = this.currentStateMap.audioElements.pop()) { | 1348 while (elem) { |
1347 if (elem.type == "outside-reference") { | 1349 if (elem.type == "outside-reference") { |
1348 ref.push(elem); | 1350 ref.push(elem); |
1349 } else { | 1351 } else { |
1350 elements.push(elem); | 1352 elements.push(elem); |
1351 } | 1353 } |
1354 elem = this.currentStateMap.audioElements.pop(); | |
1352 } | 1355 } |
1353 elements = elements.reverse(); | 1356 elements = elements.reverse(); |
1354 if (this.currentStateMap.randomiseOrder) { | 1357 if (this.currentStateMap.randomiseOrder) { |
1355 elements = randomiseOrder(elements); | 1358 elements = randomiseOrder(elements); |
1356 } | 1359 } |
1402 testTime.textContent = audioEngineContext.timer.testDuration; | 1405 testTime.textContent = audioEngineContext.timer.testDuration; |
1403 metric.appendChild(testTime); | 1406 metric.appendChild(testTime); |
1404 } | 1407 } |
1405 | 1408 |
1406 var audioObjects = audioEngineContext.audioObjects; | 1409 var audioObjects = audioEngineContext.audioObjects; |
1407 for (var ao of audioEngineContext.audioObjects) { | 1410 audioEngineContext.audioObjects.forEach(function (ao) { |
1408 ao.exportXMLDOM(); | 1411 ao.exportXMLDOM(); |
1409 } | 1412 }); |
1410 for (var element of interfaceContext.commentQuestions) { | 1413 interfaceContext.commentQuestions.forEach(function (element) { |
1411 element.exportXMLDOM(storePoint); | 1414 element.exportXMLDOM(storePoint); |
1412 } | 1415 }); |
1413 pageXMLSave(storePoint.XMLDOM, this.currentStateMap); | 1416 pageXMLSave(storePoint.XMLDOM, this.currentStateMap); |
1414 storePoint.complete(); | 1417 storePoint.complete(); |
1415 }; | 1418 }; |
1416 | 1419 |
1417 this.getCurrentTestPage = function () { | 1420 this.getCurrentTestPage = function () { |
1594 this.users[i].interfaceDOM.updateLoading(this.progress * 100); | 1597 this.users[i].interfaceDOM.updateLoading(this.progress * 100); |
1595 } | 1598 } |
1596 } | 1599 } |
1597 } | 1600 } |
1598 } | 1601 } |
1599 }; | 1602 } |
1600 | 1603 |
1601 this.progress = 0; | 1604 this.progress = 0; |
1602 this.status = 1; | 1605 this.status = 1; |
1603 currentUrlIndex = 0; | 1606 currentUrlIndex = 0; |
1604 get(urls[0].url).then(processAudio.bind(self)).catch(getNextURL.bind(self)); | 1607 get(urls[0].url).then(processAudio.bind(self)).catch(getNextURL.bind(self)); |
1605 }; | 1608 }; |
1606 | 1609 |
1607 this.registerAudioObject = function (audioObject) { | 1610 this.registerAudioObject = function (audioObject) { |
1608 // Called by an audioObject to register to the buffer for use | 1611 // Called by an audioObject to register to the buffer for use |
1609 // First check if already in the register pool | 1612 // First check if already in the register pool |
1610 for (var objects of this.users) { | 1613 this.users.forEach(function (object) { |
1611 if (audioObject.id == objects.id) { | 1614 if (audioObject.id == objects.id) { |
1612 return 0; | 1615 return 0; |
1613 } | 1616 } |
1614 } | 1617 }); |
1615 this.users.push(audioObject); | 1618 this.users.push(audioObject); |
1616 if (this.status == 3 || this.status == -1) { | 1619 if (this.status == 3 || this.status == -1) { |
1617 // The buffer is already ready, trigger bufferLoaded | 1620 // The buffer is already ready, trigger bufferLoaded |
1618 audioObject.bufferLoaded(this); | 1621 audioObject.bufferLoaded(this); |
1619 } | 1622 } |
1629 } | 1632 } |
1630 var preSilenceSamples = secondsToSamples(preSilenceTime, this.buffer.sampleRate); | 1633 var preSilenceSamples = secondsToSamples(preSilenceTime, this.buffer.sampleRate); |
1631 var postSilenceSamples = secondsToSamples(postSilenceTime, this.buffer.sampleRate); | 1634 var postSilenceSamples = secondsToSamples(postSilenceTime, this.buffer.sampleRate); |
1632 var newLength = this.buffer.length + preSilenceSamples + postSilenceSamples; | 1635 var newLength = this.buffer.length + preSilenceSamples + postSilenceSamples; |
1633 var copybuffer = audioContext.createBuffer(this.buffer.numberOfChannels, newLength, this.buffer.sampleRate); | 1636 var copybuffer = audioContext.createBuffer(this.buffer.numberOfChannels, newLength, this.buffer.sampleRate); |
1637 var c; | |
1634 // Now we can use some efficient background copy schemes if we are just padding the end | 1638 // Now we can use some efficient background copy schemes if we are just padding the end |
1635 if (preSilenceSamples === 0 && typeof copybuffer.copyToChannel === "function") { | 1639 if (preSilenceSamples === 0 && typeof copybuffer.copyToChannel === "function") { |
1636 for (var c = 0; c < this.buffer.numberOfChannels; c++) { | 1640 for (c = 0; c < this.buffer.numberOfChannels; c++) { |
1637 copybuffer.copyToChannel(this.buffer.getChannelData(c), c); | 1641 copybuffer.copyToChannel(this.buffer.getChannelData(c), c); |
1638 } | 1642 } |
1639 } else { | 1643 } else { |
1640 for (var c = 0; c < this.buffer.numberOfChannels; c++) { | 1644 for (c = 0; c < this.buffer.numberOfChannels; c++) { |
1641 var src = this.buffer.getChannelData(c); | 1645 var src = this.buffer.getChannelData(c); |
1642 var dst = copybuffer.getChannelData(c); | 1646 var dst = copybuffer.getChannelData(c); |
1643 for (var n = 0; n < src.length; n++) | 1647 for (var n = 0; n < src.length; n++) |
1644 dst[n + preSilenceSamples] = src[n]; | 1648 dst[n + preSilenceSamples] = src[n]; |
1645 } | 1649 } |
1672 }; | 1676 }; |
1673 }; | 1677 }; |
1674 | 1678 |
1675 this.loadPageData = function (page) { | 1679 this.loadPageData = function (page) { |
1676 // Load the URL from pages | 1680 // Load the URL from pages |
1677 for (var element of page.audioElements) { | 1681 function loadAudioElementData(element) { |
1678 var URL = page.hostURL + element.url; | 1682 var URL = page.hostURL + element.url; |
1679 var buffer = null; | 1683 var buffer = this.buffers.find(function (buffObj) { |
1680 for (var buffObj of this.buffers) { | 1684 return buffObj.hasUrl(URL); |
1681 if (buffObj.hasUrl(URL)) { | 1685 }); |
1682 buffer = buffObj; | 1686 if (buffer === undefined) { |
1683 break; | |
1684 } | |
1685 } | |
1686 if (buffer === null) { | |
1687 buffer = new this.bufferObj(); | 1687 buffer = new this.bufferObj(); |
1688 var urls = [{ | 1688 var urls = [{ |
1689 url: URL, | 1689 url: URL, |
1690 sampleRate: element.sampleRate | 1690 sampleRate: element.sampleRate |
1691 }]; | 1691 }]; |
1698 buffer.setUrls(urls); | 1698 buffer.setUrls(urls); |
1699 buffer.getMedia(); | 1699 buffer.getMedia(); |
1700 this.buffers.push(buffer); | 1700 this.buffers.push(buffer); |
1701 } | 1701 } |
1702 } | 1702 } |
1703 }; | 1703 page.audioElements.forEach(loadAudioElementData, this); |
1704 }; | |
1705 | |
1706 function playNormal(id) { | |
1707 var playTime = audioContext.currentTime + 0.1; | |
1708 var stopTime = playTime + specification.crossFade; | |
1709 this.audioObjects.forEach(function (ao) { | |
1710 if (ao.id === id) { | |
1711 ao.play(playTime); | |
1712 } else { | |
1713 ao.stop(stopTime); | |
1714 } | |
1715 }); | |
1716 } | |
1717 | |
1718 function playLoopSync(id) { | |
1719 var playTime = audioContext.currentTime + 0.1; | |
1720 var stopTime = playTime + specification.crossFade; | |
1721 this.audioObjects.forEach(function (ao) { | |
1722 ao.play(playTime); | |
1723 if (ao.id === id) { | |
1724 ao.loopStart(playTime); | |
1725 } else { | |
1726 ao.loopStop(stopTime); | |
1727 } | |
1728 }); | |
1729 } | |
1704 | 1730 |
1705 this.play = function (id) { | 1731 this.play = function (id) { |
1706 // Start the timer and set the audioEngine state to playing (1) | 1732 // Start the timer and set the audioEngine state to playing (1) |
1707 if (this.status === 0) { | 1733 if (typeof id !== "number" || id < 0 || id > this.audioObjects.length) { |
1708 // Check if all audioObjects are ready | 1734 throw ('FATAL - Passed id was undefined - AudioEngineContext.play(id)'); |
1709 this.bufferReady(id); | |
1710 } else { | |
1711 this.status = 1; | |
1712 } | 1735 } |
1713 if (this.status === 1) { | 1736 if (this.status === 1) { |
1714 this.timer.startTest(); | 1737 this.timer.startTest(); |
1715 if (id === undefined) { | 1738 interfaceContext.playhead.setTimePerPixel(this.audioObjects[id]); |
1716 id = -1; | |
1717 console.error('FATAL - Passed id was undefined - AudioEngineContext.play(id)'); | |
1718 return; | |
1719 } else { | |
1720 interfaceContext.playhead.setTimePerPixel(this.audioObjects[id]); | |
1721 } | |
1722 var setTime = audioContext.currentTime; | |
1723 if (this.synchPlayback && this.loopPlayback) { | 1739 if (this.synchPlayback && this.loopPlayback) { |
1724 // Traditional looped playback | 1740 // Traditional looped playback |
1725 for (var i = 0; i < this.audioObjects.length; i++) { | 1741 playLoopSync.call(this, id); |
1726 this.audioObjects[i].play(audioContext.currentTime); | |
1727 if (id == i) { | |
1728 this.audioObjects[i].loopStart(setTime); | |
1729 } else { | |
1730 this.audioObjects[i].loopStop(setTime + specification.crossFade); | |
1731 } | |
1732 } | |
1733 } else { | 1742 } else { |
1734 for (var i = 0; i < this.audioObjects.length; i++) { | 1743 if (this.bufferReady(id) === false) { |
1735 if (i != id) { | 1744 console.log("Cannot play. Buffer not ready"); |
1736 this.audioObjects[i].stop(setTime + specification.crossFade); | 1745 return; |
1737 } else if (i == id) { | 1746 } |
1738 this.audioObjects[id].play(setTime); | 1747 playNormal.call(this, id); |
1739 } | |
1740 } | |
1741 } | 1748 } |
1742 interfaceContext.playhead.start(); | 1749 interfaceContext.playhead.start(); |
1743 } | 1750 } |
1744 }; | 1751 }; |
1745 | 1752 |
1746 this.stop = function () { | 1753 this.stop = function () { |
1747 // Send stop and reset command to all playback buffers | 1754 // Send stop and reset command to all playback buffers |
1748 if (this.status == 1) { | 1755 if (this.status == 1) { |
1749 var setTime = audioContext.currentTime + 0.1; | 1756 var setTime = audioContext.currentTime + 0.1; |
1750 for (var i = 0; i < this.audioObjects.length; i++) { | 1757 this.audioObjects.forEach(function (a) { |
1751 this.audioObjects[i].stop(setTime); | 1758 a.stop(setTime); |
1752 } | 1759 }); |
1753 interfaceContext.playhead.stop(); | 1760 interfaceContext.playhead.stop(); |
1754 } | 1761 } |
1755 }; | 1762 }; |
1756 | 1763 |
1757 this.newTrack = function (element) { | 1764 this.newTrack = function (element) { |
1762 audioObjectId = this.audioObjects.length; | 1769 audioObjectId = this.audioObjects.length; |
1763 this.audioObjects[audioObjectId] = new audioObject(audioObjectId); | 1770 this.audioObjects[audioObjectId] = new audioObject(audioObjectId); |
1764 | 1771 |
1765 // Check if audioObject buffer is currently stored by full URL | 1772 // Check if audioObject buffer is currently stored by full URL |
1766 var URL = testState.currentStateMap.hostURL + element.url; | 1773 var URL = testState.currentStateMap.hostURL + element.url; |
1767 var buffer = null; | 1774 var buffer = this.buffers.find(function (buffObj) { |
1768 for (var i = 0; i < this.buffers.length; i++) { | 1775 return buffObj.hasUrl(URL); |
1769 if (this.buffers[i].hasUrl(URL)) { | 1776 }); |
1770 buffer = this.buffers[i]; | 1777 if (buffer === undefined) { |
1771 break; | |
1772 } | |
1773 } | |
1774 if (buffer === null) { | |
1775 console.log("[WARN]: Buffer was not loaded in pre-test! " + URL); | 1778 console.log("[WARN]: Buffer was not loaded in pre-test! " + URL); |
1776 buffer = new this.bufferObj(); | 1779 buffer = new this.bufferObj(); |
1777 this.buffers.push(buffer); | 1780 this.buffers.push(buffer); |
1778 buffer.getMedia(URL); | 1781 buffer.getMedia(URL); |
1779 } | 1782 } |
1795 this.pageStore = store; | 1798 this.pageStore = store; |
1796 this.pageSpecification = audioHolderObject; | 1799 this.pageSpecification = audioHolderObject; |
1797 this.status = 0; | 1800 this.status = 0; |
1798 this.audioObjectsReady = false; | 1801 this.audioObjectsReady = false; |
1799 this.metric.reset(); | 1802 this.metric.reset(); |
1800 for (var i = 0; i < this.buffers.length; i++) { | 1803 this.buffers.forEach(function (buffer) { |
1801 this.buffers[i].users = []; | 1804 buffer.users = []; |
1802 } | 1805 }); |
1803 this.audioObjects = []; | 1806 this.audioObjects = []; |
1804 this.timer = new timer(); | 1807 this.timer = new timer(); |
1805 this.loopPlayback = audioHolderObject.loop; | 1808 this.loopPlayback = audioHolderObject.loop; |
1806 this.synchPlayback = audioHolderObject.synchronous; | 1809 this.synchPlayback = audioHolderObject.synchronous; |
1807 }; | 1810 }; |
1838 duration = this.audioObjects[i].buffer.buffer.duration; | 1841 duration = this.audioObjects[i].buffer.buffer.duration; |
1839 maxId = i; | 1842 maxId = i; |
1840 } | 1843 } |
1841 } | 1844 } |
1842 // Extract the audio and zero-pad | 1845 // Extract the audio and zero-pad |
1843 for (var ao of this.audioObjects) { | 1846 this.audioObjects.forEach(function (ao) { |
1844 if (ao.buffer.buffer.duration !== duration) { | 1847 if (ao.buffer.buffer.duration !== duration) { |
1845 ao.buffer.buffer = ao.buffer.copyBuffer(0, duration - ao.buffer.buffer.duration); | 1848 ao.buffer.buffer = ao.buffer.copyBuffer(0, duration - ao.buffer.buffer.duration); |
1846 } | 1849 } |
1847 } | 1850 }); |
1848 }; | 1851 }; |
1849 | 1852 |
1850 this.bufferReady = function (id) { | 1853 this.bufferReady = function (id) { |
1851 if (this.checkAllReady()) { | 1854 if (this.checkAllReady()) { |
1852 if (this.synchPlayback) { | 1855 if (this.synchPlayback) { |
1856 return true; | 1859 return true; |
1857 } | 1860 } |
1858 return false; | 1861 return false; |
1859 }; | 1862 }; |
1860 | 1863 |
1861 this.exportXML = function () { | |
1862 | |
1863 }; | |
1864 | |
1865 } | 1864 } |
1866 | 1865 |
1867 function audioObject(id) { | 1866 function audioObject(id) { |
1868 // The main buffer object with common control nodes to the AudioEngine | 1867 // The main buffer object with common control nodes to the AudioEngine |
1869 | 1868 |
1870 this.specification; | 1869 this.specification = undefined; |
1871 this.id = id; | 1870 this.id = id; |
1872 this.state = 0; // 0 - no data, 1 - ready | 1871 this.state = 0; // 0 - no data, 1 - ready |
1873 this.url = null; // Hold the URL given for the output back to the results. | 1872 this.url = null; // Hold the URL given for the output back to the results. |
1874 this.metric = new metricTracker(this); | 1873 this.metric = new metricTracker(this); |
1875 this.storeDOM = null; | 1874 this.storeDOM = null; |
1888 this.outputGain.connect(audioEngineContext.outputGain); | 1887 this.outputGain.connect(audioEngineContext.outputGain); |
1889 audioEngineContext.nullBufferSource.connect(this.outputGain); | 1888 audioEngineContext.nullBufferSource.connect(this.outputGain); |
1890 | 1889 |
1891 // the audiobuffer is not designed for multi-start playback | 1890 // the audiobuffer is not designed for multi-start playback |
1892 // When stopeed, the buffer node is deleted and recreated with the stored buffer. | 1891 // When stopeed, the buffer node is deleted and recreated with the stored buffer. |
1893 this.buffer; | 1892 this.buffer = undefined; |
1894 | 1893 |
1895 this.bufferLoaded = function (callee) { | 1894 this.bufferLoaded = function (callee) { |
1896 // Called by the associated buffer when it has finished loading, will then 'bind' the buffer to the | 1895 // Called by the associated buffer when it has finished loading, will then 'bind' the buffer to the |
1897 // audioObject and trigger the interfaceDOM.enable() function for user feedback | 1896 // audioObject and trigger the interfaceDOM.enable() function for user feedback |
1898 if (callee.status == -1) { | 1897 if (callee.status == -1) { |
3587 this.state = "empty"; | 3586 this.state = "empty"; |
3588 this.XMLDOM = this.parent.document.createElement('page'); | 3587 this.XMLDOM = this.parent.document.createElement('page'); |
3589 this.XMLDOM.setAttribute('ref', specification.id); | 3588 this.XMLDOM.setAttribute('ref', specification.id); |
3590 this.XMLDOM.setAttribute('presentedId', specification.presentedId); | 3589 this.XMLDOM.setAttribute('presentedId', specification.presentedId); |
3591 this.XMLDOM.setAttribute("state", this.state); | 3590 this.XMLDOM.setAttribute("state", this.state); |
3592 if (specification.preTest !== undefined) { | 3591 if (specification.preTest !== null) { |
3593 this.preTest = new this.parent.surveyNode(this.parent, this.XMLDOM, this.specification.preTest); | 3592 this.preTest = new this.parent.surveyNode(this.parent, this.XMLDOM, this.specification.preTest); |
3594 } | 3593 } |
3595 if (specification.postTest !== undefined) { | 3594 if (specification.postTest !== null) { |
3596 this.postTest = new this.parent.surveyNode(this.parent, this.XMLDOM, this.specification.postTest); | 3595 this.postTest = new this.parent.surveyNode(this.parent, this.XMLDOM, this.specification.postTest); |
3597 } | 3596 } |
3598 | 3597 |
3599 // Add any page metrics | 3598 // Add any page metrics |
3600 var page_metric = this.parent.document.createElement('metric'); | 3599 var page_metric = this.parent.document.createElement('metric'); |