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');