speaker-calibration 2.2.12 → 2.2.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/main.js CHANGED
@@ -796,7 +796,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac
796
796
  /***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
797
797
 
798
798
  "use strict";
799
- eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var qrcode__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! qrcode */ \"./node_modules/qrcode/lib/browser.js\");\n/* harmony import */ var _audioPeer__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./audioPeer */ \"./src/peer-connection/audioPeer.js\");\n/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../utils */ \"./src/utils.js\");\n/* harmony import */ var _peerErrors__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./peerErrors */ \"./src/peer-connection/peerErrors.js\");\n/* harmony import */ var _config_firebase__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../config/firebase */ \"./src/config/firebase.js\");\n/* harmony import */ var firebase_database__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! firebase/database */ \"./node_modules/firebase/database/dist/esm/index.esm.js\");\n/* harmony import */ var _dist_example_i18n__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../../dist/example/i18n */ \"./dist/example/i18n.js\");\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n/**\r\n * @class Handles the speaker's side of the connection. Responsible for initiating the connection,\r\n * rendering the QRCode, and answering the call.\r\n * @augments AudioPeer\r\n */\r\nclass Speaker extends _audioPeer__WEBPACK_IMPORTED_MODULE_1__[\"default\"] {\r\n /**\r\n * Takes the url of the current site and a target element where html elements will be appended.\r\n *\r\n * @param params - See type definition for initParameters.\r\n * @param Calibrator - An instance of the AudioCalibrator class, should not use AudioCalibrator directly, instead use an extended class available in /tasks/.\r\n * @param CalibratorInstance\r\n * @example\r\n */\r\n constructor(params, CalibratorInstance) {\r\n super(params);\r\n this.siteUrl += '/listener?';\r\n this.ac = CalibratorInstance;\r\n this.result = null;\r\n this.debug = params?.debug ?? false;\r\n this.isSmartPhone = params?.isSmartPhone ?? false;\r\n this.calibrateSoundHz = params?.calibrateSoundHz ?? 48000;\r\n this.instructionDisplayId = params?.instructionDisplayId ?? '';\r\n this.titleDisplayId = params?.titleDisplayId ?? '';\r\n this.timeToCalibrate = params?.timeToCalibrate ?? 10;\r\n\r\n /* Set up callbacks that handle any events related to our peer object. */\r\n this.peer.on('open', this.#onPeerOpen);\r\n this.peer.on('connection', this.#onPeerConnection);\r\n this.peer.on('close', this.#onPeerClose);\r\n this.peer.on('disconnected', this.#onPeerDisconnected);\r\n this.peer.on('error', this.#onPeerError);\r\n }\r\n\r\n static getMicrophoneNamesFromDatabase = async () => {\r\n const dbRef = (0,firebase_database__WEBPACK_IMPORTED_MODULE_5__.ref)(_config_firebase__WEBPACK_IMPORTED_MODULE_4__[\"default\"]);\r\n const snapshot = await (0,firebase_database__WEBPACK_IMPORTED_MODULE_5__.get)((0,firebase_database__WEBPACK_IMPORTED_MODULE_5__.child)(dbRef, 'Microphone'));\r\n if (snapshot.exists()) {\r\n return Object.keys(snapshot.val());\r\n }\r\n return null;\r\n };\r\n\r\n /**\r\n * Async factory method that creates the Speaker object, and returns a promise that resolves to the result of the calibration.\r\n *\r\n * @param params - The parameters to be passed to the peer object.\r\n * @param Calibrator - The class that defines the calibration process.\r\n * @param CalibratorInstance\r\n * @param timeOut - The amount of time to wait before timing out the connection (in milliseconds).\r\n * @public\r\n * @example\r\n */\r\n static startCalibration = async (params, CalibratorInstance, timeOut = 180000) => {\r\n window.speaker = new Speaker(params, CalibratorInstance);\r\n const {speaker} = window;\r\n\r\n // wrap the calibration process in a promise so we can await it\r\n return new Promise((resolve, reject) => {\r\n // when a call is received\r\n speaker.peer.on('call', async call => {\r\n // Answer the call (one way)\r\n call.answer();\r\n speaker.#removeUIElems();\r\n speaker.#showSpinner();\r\n speaker.ac.createLocalAudio(document.getElementById(speaker.targetElement));\r\n // when we start receiving audio\r\n call.on('stream', async stream => {\r\n window.localStream = stream;\r\n window.localAudio.srcObject = stream;\r\n window.localAudio.autoplay = false;\r\n\r\n // if the sinkSamplingRate is not set sleep\r\n while (!speaker.ac.sampleRatesSet()) {\r\n console.log('SinkSamplingRate is undefined, sleeping');\r\n await (0,_utils__WEBPACK_IMPORTED_MODULE_2__.sleep)(1);\r\n }\r\n // resolve when we have a result\r\n speaker.result = await speaker.ac.startCalibration(\r\n stream,\r\n params.gainValues,\r\n params.ICalib,\r\n params.knownIR,\r\n params.microphoneName,\r\n params.calibrateSoundCheck,\r\n params.isSmartPhone,\r\n params.calibrateSoundBurstRepeats,\r\n params.calibrateSoundBurstSec,\r\n params.calibrateSoundBurstsWarmup,\r\n params.calibrateSoundHz,\r\n params.micManufacturer,\r\n params.micSerialNumber,\r\n params.micModelNumber,\r\n params.micModelName\r\n );\r\n speaker.#removeUIElems();\r\n resolve(speaker.result);\r\n });\r\n // if we do not receive a result within the timeout, reject\r\n setTimeout(() => {\r\n reject(\r\n new _peerErrors__WEBPACK_IMPORTED_MODULE_3__.CalibrationTimedOutError(\r\n `Calibration failed to produce a result after ${\r\n timeOut / 1000\r\n } seconds. Please try again.`\r\n )\r\n );\r\n }, timeOut);\r\n });\r\n });\r\n };\r\n\r\n static testIIR = async (params, CalibratorInstance, IIR, timeOut = 180000) => {\r\n window.speaker = new Speaker(params, CalibratorInstance);\r\n const {speaker} = window;\r\n\r\n // wrap the calibration process in a promise so we can await it\r\n return new Promise((resolve, reject) => {\r\n // when a call is received\r\n speaker.peer.on('call', async call => {\r\n // Answer the call (one way)\r\n call.answer();\r\n speaker.#removeUIElems();\r\n speaker.#showSpinner();\r\n speaker.ac.createLocalAudio(document.getElementById(speaker.targetElement));\r\n // when we start receiving audio\r\n call.on('stream', async stream => {\r\n window.localStream = stream;\r\n window.localAudio.srcObject = stream;\r\n window.localAudio.autoplay = false;\r\n\r\n // if the sinkSamplingRate is not set sleep\r\n while (!speaker.ac.sampleRatesSet()) {\r\n console.log('SinkSamplingRate is undefined, sleeping');\r\n await (0,_utils__WEBPACK_IMPORTED_MODULE_2__.sleep)(1);\r\n }\r\n // resolve when we have a result\r\n speaker.result = await speaker.ac.playMLSwithIIR(stream, IIR);\r\n speaker.#removeUIElems();\r\n resolve(speaker.result);\r\n });\r\n // if we do not receive a result within the timeout, reject\r\n setTimeout(() => {\r\n reject(\r\n new _peerErrors__WEBPACK_IMPORTED_MODULE_3__.CalibrationTimedOutError(\r\n `Calibration failed to produce a result after ${\r\n timeOut / 1000\r\n } seconds. Please try again.`\r\n )\r\n );\r\n }, timeOut);\r\n });\r\n });\r\n };\r\n\r\n /**\r\n * Called after the peer conncection has been opened.\r\n * Generates a QR code for the connection and displays it.\r\n *\r\n * @private\r\n * @example\r\n */\r\n #showQRCode = () => {\r\n // Get query string, the URL parameters to specify a Listener\r\n const queryStringParameters = {\r\n speakerPeerId: this.peer.id,\r\n isSmartPhone: this.isSmartPhone,\r\n calibrateSoundHz: this.calibrateSoundHz,\r\n };\r\n const queryString = this.queryStringFromObject(queryStringParameters);\r\n const uri = this.siteUrl + queryString;\r\n if (this.isSmartPhone) {\r\n // Display QR code for the participant to scan\r\n const qrCanvas = document.createElement('canvas');\r\n qrCanvas.setAttribute('id', 'qrCanvas');\r\n console.log(uri);\r\n qrcode__WEBPACK_IMPORTED_MODULE_0__.toCanvas(qrCanvas, uri, error => {\r\n if (error) console.error(error);\r\n });\r\n document.getElementById(this.targetElement).appendChild(qrCanvas);\r\n } else {\r\n // show the link to the user\r\n // If specified HTML Id is available, show QR code there\r\n if (document.getElementById(this.targetElement)) {\r\n // const linkTag = document.createElement('a');\r\n // linkTag.setAttribute('href', uri);\r\n // linkTag.innerHTML = 'Click here to start the calibration';\r\n // linkTag.target = '_blank';\r\n // document.getElementById(this.targetElement).appendChild(linkTag);\r\n // document.getElementById(this.targetElement).appendChild(qrCanvas);\r\n\r\n const proceedButton = document.createElement('button');\r\n proceedButton.setAttribute('id', 'calibrationProceedButton');\r\n proceedButton.setAttribute('class', 'btn btn-success');\r\n proceedButton.innerHTML = 'Proceed';\r\n proceedButton.onclick = () => {\r\n // open the link in a new tab\r\n window.open(uri, '_blank');\r\n // remove the button\r\n document.getElementById('calibrationProceedButton').remove();\r\n };\r\n document.getElementById(this.targetElement).appendChild(proceedButton);\r\n }\r\n }\r\n // or just print it to console\r\n console.log('TEST: Peer reachable at: ', uri);\r\n };\r\n\r\n #showSpinner = () => {\r\n const spinner = document.createElement('div');\r\n spinner.className = 'spinner-border ml-auto';\r\n spinner.role = 'status';\r\n spinner.ariaHidden = 'true';\r\n document.getElementById(this.targetElement).appendChild(spinner);\r\n\r\n // clear instructionDisplay\r\n const instructionDisplay = document.getElementById(this.instructionDisplayId);\r\n const background = document.getElementById('background'); // todo: get background id from params\r\n if (instructionDisplay) {\r\n instructionDisplay.innerHTML = '';\r\n instructionDisplay.style.whiteSpace = 'nowrap';\r\n instructionDisplay.style.fontWeight = 'bold';\r\n instructionDisplay.style.width = 'fit-content';\r\n instructionDisplay.innerHTML = _dist_example_i18n__WEBPACK_IMPORTED_MODULE_6__.phrases.RC_soundRecording[\"en-US\"];\r\n let fontSize = 100;\r\n instructionDisplay.style.fontSize = fontSize + 'px';\r\n while (instructionDisplay.scrollWidth > background.scrollWidth * 0.9 && fontSize > 10) {\r\n fontSize--;\r\n instructionDisplay.style.fontSize = fontSize + 'px';\r\n }\r\n const p = document.createElement('p');\r\n // font size\r\n p.style.fontSize = '1.1rem';\r\n p.style.fontWeight = 'normal';\r\n p.style.paddingTop = '20px';\r\n const timeToCalibrateText = _dist_example_i18n__WEBPACK_IMPORTED_MODULE_6__.phrases.RC_howLongToCalibrate[\"en-US\"];\r\n p.innerHTML = timeToCalibrateText.replace('111', this.timeToCalibrate);\r\n instructionDisplay.appendChild(p);\r\n }\r\n\r\n // Update title - titleDisplayId\r\n const titleDisplay = document.getElementById(this.titleDisplayId);\r\n if (titleDisplay) {\r\n // replace 2 with 3\r\n titleDisplay.innerHTML = titleDisplay.innerHTML.replace('2', '3');\r\n // replace 1 with 3\r\n titleDisplay.innerHTML = titleDisplay.innerHTML.replace('1', '3');\r\n }\r\n };\r\n\r\n #removeUIElems = () => {\r\n const parent = document.getElementById(this.targetElement);\r\n while (parent.firstChild) {\r\n parent.firstChild.remove();\r\n }\r\n };\r\n\r\n /**\r\n * Called when the peer connection is opened.\r\n * Saves the peer id and calls the QR code generator.\r\n *\r\n * @param peerId - The peer id of the peer connection.\r\n * @param id\r\n * @private\r\n * @example\r\n */\r\n #onPeerOpen = id => {\r\n // Workaround for peer.reconnect deleting previous id\r\n if (id === null) {\r\n console.error('Received null id from peer open');\r\n this.peer.id = this.lastPeerId;\r\n } else {\r\n this.lastPeerId = this.peer.id;\r\n }\r\n\r\n if (id !== this.peer.id) {\r\n console.warn('DEBUG Check you assumption that id === this.peer.id');\r\n }\r\n\r\n this.#showQRCode();\r\n };\r\n\r\n /**\r\n * Called when the peer connection is established.\r\n * Enforces a single connection.\r\n *\r\n * @param connection - The connection object.\r\n * @private\r\n * @example\r\n */\r\n #onPeerConnection = connection => {\r\n // Allow only a single connection\r\n if (this.conn && this.conn.open) {\r\n connection.on('open', () => {\r\n connection.send('Already connected to another client');\r\n setTimeout(() => {\r\n connection.close();\r\n }, 500);\r\n });\r\n return;\r\n }\r\n\r\n this.conn = connection;\r\n console.log('Connected to: ', this.conn.peer);\r\n this.#ready();\r\n };\r\n\r\n /**\r\n * Called when the peer connection is closed.\r\n *\r\n * @private\r\n * @example\r\n */\r\n #onPeerClose = () => {\r\n this.conn = null;\r\n console.log('Connection destroyed');\r\n };\r\n\r\n /**\r\n * Called when the peer connection is disconnected.\r\n * Attempts to reconnect.\r\n *\r\n * @private\r\n * @example\r\n */\r\n #onPeerDisconnected = () => {\r\n console.log('Connection lost. Please reconnect');\r\n\r\n // Workaround for peer.reconnect deleting previous id\r\n this.peer.id = this.lastPeerId;\r\n // eslint-disable-next-line no-underscore-dangle\r\n this.peer._lastServerId = this.lastPeerId;\r\n this.peer.reconnect();\r\n };\r\n\r\n /**\r\n * Called when the peer connection encounters an error.\r\n *\r\n * @param error\r\n * @private\r\n * @example\r\n */\r\n #onPeerError = error => {\r\n // TODO: check if this function is needed or not\r\n console.error(error);\r\n };\r\n\r\n /**\r\n * Called when data is received from the peer connection.\r\n *\r\n * @param data\r\n * @private\r\n * @example\r\n */\r\n #onIncomingData = data => {\r\n // enforce object type\r\n if (\r\n !Object.prototype.hasOwnProperty.call(data, 'name') ||\r\n !Object.prototype.hasOwnProperty.call(data, 'payload')\r\n ) {\r\n console.error('Received malformed data: ', data);\r\n return;\r\n }\r\n\r\n switch (data.name) {\r\n case 'samplingRate':\r\n this.ac.setSamplingRates(data.payload);\r\n break;\r\n case 'deviceType':\r\n this.ac.setDeviceType(data.payload);\r\n break;\r\n case 'deviceName':\r\n this.ac.setDeviceName(data.payload);\r\n break;\r\n case 'deviceInfo':\r\n this.ac.setDeviceInfo(data.payload);\r\n case _peerErrors__WEBPACK_IMPORTED_MODULE_3__.UnsupportedDeviceError.name:\r\n case _peerErrors__WEBPACK_IMPORTED_MODULE_3__.MissingSpeakerIdError.name:\r\n throw data.payload;\r\n break;\r\n default:\r\n break;\r\n }\r\n };\r\n\r\n /**\r\n * Called when the peer connection is #ready.\r\n *\r\n * @private\r\n * @example\r\n */\r\n #ready = () => {\r\n // Perform callback with data\r\n this.conn.on('data', this.#onIncomingData);\r\n this.conn.on('close', () => {\r\n console.log('Connection reset<br>Awaiting connection...');\r\n this.conn = null;\r\n });\r\n };\r\n\r\n /** .\r\n * .\r\n * .\r\n * Debug method for downloading the recorded audio\r\n *\r\n * @public\r\n * @example\r\n */\r\n downloadData = () => {\r\n this.ac.downloadData();\r\n };\r\n}\r\n\r\n/* \r\nReferenced links:\r\nhttps://stackoverflow.com/questions/28016664/when-you-pass-this-as-an-argument/28016676#28016676\r\nhttps://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes\r\nhttps://stackoverflow.com/questions/879152/how-do-i-make-javascript-beep [3]\r\n*/\r\n\r\n/* harmony default export */ __webpack_exports__[\"default\"] = (Speaker);\r\n\n\n//# sourceURL=webpack://speakerCalibrator/./src/peer-connection/speaker.js?");
799
+ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var qrcode__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! qrcode */ \"./node_modules/qrcode/lib/browser.js\");\n/* harmony import */ var _audioPeer__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./audioPeer */ \"./src/peer-connection/audioPeer.js\");\n/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../utils */ \"./src/utils.js\");\n/* harmony import */ var _peerErrors__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./peerErrors */ \"./src/peer-connection/peerErrors.js\");\n/* harmony import */ var _config_firebase__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../config/firebase */ \"./src/config/firebase.js\");\n/* harmony import */ var firebase_database__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! firebase/database */ \"./node_modules/firebase/database/dist/esm/index.esm.js\");\n/* harmony import */ var _dist_example_i18n__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../../dist/example/i18n */ \"./dist/example/i18n.js\");\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n/**\r\n * @class Handles the speaker's side of the connection. Responsible for initiating the connection,\r\n * rendering the QRCode, and answering the call.\r\n * @augments AudioPeer\r\n */\r\nclass Speaker extends _audioPeer__WEBPACK_IMPORTED_MODULE_1__[\"default\"] {\r\n /**\r\n * Takes the url of the current site and a target element where html elements will be appended.\r\n *\r\n * @param params - See type definition for initParameters.\r\n * @param Calibrator - An instance of the AudioCalibrator class, should not use AudioCalibrator directly, instead use an extended class available in /tasks/.\r\n * @param CalibratorInstance\r\n * @example\r\n */\r\n constructor(params, CalibratorInstance) {\r\n super(params);\r\n this.siteUrl += '/listener?';\r\n this.ac = CalibratorInstance;\r\n this.result = null;\r\n this.debug = params?.debug ?? false;\r\n this.isSmartPhone = params?.isSmartPhone ?? false;\r\n this.calibrateSoundHz = params?.calibrateSoundHz ?? 48000;\r\n this.instructionDisplayId = params?.instructionDisplayId ?? '';\r\n this.titleDisplayId = params?.titleDisplayId ?? '';\r\n this.timeToCalibrate = params?.timeToCalibrate ?? 10;\r\n\r\n /* Set up callbacks that handle any events related to our peer object. */\r\n this.peer.on('open', this.#onPeerOpen);\r\n this.peer.on('connection', this.#onPeerConnection);\r\n this.peer.on('close', this.#onPeerClose);\r\n this.peer.on('disconnected', this.#onPeerDisconnected);\r\n this.peer.on('error', this.#onPeerError);\r\n }\r\n\r\n static getMicrophoneNamesFromDatabase = async () => {\r\n const dbRef = (0,firebase_database__WEBPACK_IMPORTED_MODULE_5__.ref)(_config_firebase__WEBPACK_IMPORTED_MODULE_4__[\"default\"]);\r\n const snapshot = await (0,firebase_database__WEBPACK_IMPORTED_MODULE_5__.get)((0,firebase_database__WEBPACK_IMPORTED_MODULE_5__.child)(dbRef, 'Microphone'));\r\n if (snapshot.exists()) {\r\n return Object.keys(snapshot.val());\r\n }\r\n return null;\r\n };\r\n\r\n /**\r\n * Async factory method that creates the Speaker object, and returns a promise that resolves to the result of the calibration.\r\n *\r\n * @param params - The parameters to be passed to the peer object.\r\n * @param Calibrator - The class that defines the calibration process.\r\n * @param CalibratorInstance\r\n * @param timeOut - The amount of time to wait before timing out the connection (in milliseconds).\r\n * @public\r\n * @example\r\n */\r\n static startCalibration = async (params, CalibratorInstance, timeOut = 180000) => {\r\n window.speaker = new Speaker(params, CalibratorInstance);\r\n const {speaker} = window;\r\n\r\n // wrap the calibration process in a promise so we can await it\r\n return new Promise((resolve, reject) => {\r\n // when a call is received\r\n speaker.peer.on('call', async call => {\r\n // Answer the call (one way)\r\n call.answer();\r\n speaker.#removeUIElems();\r\n speaker.#showSpinner();\r\n speaker.ac.createLocalAudio(document.getElementById(speaker.targetElement));\r\n // when we start receiving audio\r\n call.on('stream', async stream => {\r\n window.localStream = stream;\r\n window.localAudio.srcObject = stream;\r\n window.localAudio.autoplay = false;\r\n\r\n // if the sinkSamplingRate is not set sleep\r\n while (!speaker.ac.sampleRatesSet()) {\r\n console.log('SinkSamplingRate is undefined, sleeping');\r\n await (0,_utils__WEBPACK_IMPORTED_MODULE_2__.sleep)(1);\r\n }\r\n // resolve when we have a result\r\n speaker.result = await speaker.ac.startCalibration(\r\n stream,\r\n params.gainValues,\r\n params.ICalib,\r\n params.knownIR,\r\n params.microphoneName,\r\n params.calibrateSoundCheck,\r\n params.isSmartPhone,\r\n params.calibrateSoundBurstRepeats,\r\n params.calibrateSoundBurstSec,\r\n params.calibrateSoundBurstsWarmup,\r\n params.calibrateSoundHz,\r\n params.calibrateSoundIIRSec,\r\n params.micManufacturer,\r\n params.micSerialNumber,\r\n params.micModelNumber,\r\n params.micModelName\r\n );\r\n speaker.#removeUIElems();\r\n resolve(speaker.result);\r\n });\r\n // if we do not receive a result within the timeout, reject\r\n setTimeout(() => {\r\n reject(\r\n new _peerErrors__WEBPACK_IMPORTED_MODULE_3__.CalibrationTimedOutError(\r\n `Calibration failed to produce a result after ${\r\n timeOut / 1000\r\n } seconds. Please try again.`\r\n )\r\n );\r\n }, timeOut);\r\n });\r\n });\r\n };\r\n\r\n static testIIR = async (params, CalibratorInstance, IIR, timeOut = 180000) => {\r\n window.speaker = new Speaker(params, CalibratorInstance);\r\n const {speaker} = window;\r\n\r\n // wrap the calibration process in a promise so we can await it\r\n return new Promise((resolve, reject) => {\r\n // when a call is received\r\n speaker.peer.on('call', async call => {\r\n // Answer the call (one way)\r\n call.answer();\r\n speaker.#removeUIElems();\r\n speaker.#showSpinner();\r\n speaker.ac.createLocalAudio(document.getElementById(speaker.targetElement));\r\n // when we start receiving audio\r\n call.on('stream', async stream => {\r\n window.localStream = stream;\r\n window.localAudio.srcObject = stream;\r\n window.localAudio.autoplay = false;\r\n\r\n // if the sinkSamplingRate is not set sleep\r\n while (!speaker.ac.sampleRatesSet()) {\r\n console.log('SinkSamplingRate is undefined, sleeping');\r\n await (0,_utils__WEBPACK_IMPORTED_MODULE_2__.sleep)(1);\r\n }\r\n // resolve when we have a result\r\n speaker.result = await speaker.ac.playMLSwithIIR(stream, IIR);\r\n speaker.#removeUIElems();\r\n resolve(speaker.result);\r\n });\r\n // if we do not receive a result within the timeout, reject\r\n setTimeout(() => {\r\n reject(\r\n new _peerErrors__WEBPACK_IMPORTED_MODULE_3__.CalibrationTimedOutError(\r\n `Calibration failed to produce a result after ${\r\n timeOut / 1000\r\n } seconds. Please try again.`\r\n )\r\n );\r\n }, timeOut);\r\n });\r\n });\r\n };\r\n\r\n /**\r\n * Called after the peer conncection has been opened.\r\n * Generates a QR code for the connection and displays it.\r\n *\r\n * @private\r\n * @example\r\n */\r\n #showQRCode = () => {\r\n // Get query string, the URL parameters to specify a Listener\r\n const queryStringParameters = {\r\n speakerPeerId: this.peer.id,\r\n isSmartPhone: this.isSmartPhone,\r\n calibrateSoundHz: this.calibrateSoundHz,\r\n };\r\n const queryString = this.queryStringFromObject(queryStringParameters);\r\n const uri = this.siteUrl + queryString;\r\n if (this.isSmartPhone) {\r\n // Display QR code for the participant to scan\r\n const qrCanvas = document.createElement('canvas');\r\n qrCanvas.setAttribute('id', 'qrCanvas');\r\n console.log(uri);\r\n qrcode__WEBPACK_IMPORTED_MODULE_0__.toCanvas(qrCanvas, uri, error => {\r\n if (error) console.error(error);\r\n });\r\n document.getElementById(this.targetElement).appendChild(qrCanvas);\r\n } else {\r\n // show the link to the user\r\n // If specified HTML Id is available, show QR code there\r\n if (document.getElementById(this.targetElement)) {\r\n // const linkTag = document.createElement('a');\r\n // linkTag.setAttribute('href', uri);\r\n // linkTag.innerHTML = 'Click here to start the calibration';\r\n // linkTag.target = '_blank';\r\n // document.getElementById(this.targetElement).appendChild(linkTag);\r\n // document.getElementById(this.targetElement).appendChild(qrCanvas);\r\n\r\n const proceedButton = document.createElement('button');\r\n proceedButton.setAttribute('id', 'calibrationProceedButton');\r\n proceedButton.setAttribute('class', 'btn btn-success');\r\n proceedButton.innerHTML = 'Proceed';\r\n proceedButton.onclick = () => {\r\n // open the link in a new tab\r\n window.open(uri, '_blank');\r\n // remove the button\r\n document.getElementById('calibrationProceedButton').remove();\r\n };\r\n document.getElementById(this.targetElement).appendChild(proceedButton);\r\n }\r\n }\r\n // or just print it to console\r\n console.log('TEST: Peer reachable at: ', uri);\r\n };\r\n\r\n #showSpinner = () => {\r\n const spinner = document.createElement('div');\r\n spinner.className = 'spinner-border ml-auto';\r\n spinner.role = 'status';\r\n spinner.ariaHidden = 'true';\r\n document.getElementById(this.targetElement).appendChild(spinner);\r\n\r\n // clear instructionDisplay\r\n const instructionDisplay = document.getElementById(this.instructionDisplayId);\r\n const background = document.getElementById('background'); // todo: get background id from params\r\n if (instructionDisplay) {\r\n instructionDisplay.innerHTML = '';\r\n instructionDisplay.style.whiteSpace = 'nowrap';\r\n instructionDisplay.style.fontWeight = 'bold';\r\n instructionDisplay.style.width = 'fit-content';\r\n instructionDisplay.innerHTML = _dist_example_i18n__WEBPACK_IMPORTED_MODULE_6__.phrases.RC_soundRecording[\"en-US\"];\r\n let fontSize = 100;\r\n instructionDisplay.style.fontSize = fontSize + 'px';\r\n while (instructionDisplay.scrollWidth > background.scrollWidth * 0.9 && fontSize > 10) {\r\n fontSize--;\r\n instructionDisplay.style.fontSize = fontSize + 'px';\r\n }\r\n const p = document.createElement('p');\r\n // font size\r\n p.style.fontSize = '1.1rem';\r\n p.style.fontWeight = 'normal';\r\n p.style.paddingTop = '20px';\r\n const timeToCalibrateText = _dist_example_i18n__WEBPACK_IMPORTED_MODULE_6__.phrases.RC_howLongToCalibrate[\"en-US\"];\r\n p.innerHTML = timeToCalibrateText.replace('111', this.timeToCalibrate);\r\n instructionDisplay.appendChild(p);\r\n }\r\n\r\n // Update title - titleDisplayId\r\n const titleDisplay = document.getElementById(this.titleDisplayId);\r\n if (titleDisplay) {\r\n // replace 2 with 3\r\n titleDisplay.innerHTML = titleDisplay.innerHTML.replace('2', '3');\r\n // replace 1 with 3\r\n titleDisplay.innerHTML = titleDisplay.innerHTML.replace('1', '3');\r\n }\r\n };\r\n\r\n #removeUIElems = () => {\r\n const parent = document.getElementById(this.targetElement);\r\n while (parent.firstChild) {\r\n parent.firstChild.remove();\r\n }\r\n };\r\n\r\n /**\r\n * Called when the peer connection is opened.\r\n * Saves the peer id and calls the QR code generator.\r\n *\r\n * @param peerId - The peer id of the peer connection.\r\n * @param id\r\n * @private\r\n * @example\r\n */\r\n #onPeerOpen = id => {\r\n // Workaround for peer.reconnect deleting previous id\r\n if (id === null) {\r\n console.error('Received null id from peer open');\r\n this.peer.id = this.lastPeerId;\r\n } else {\r\n this.lastPeerId = this.peer.id;\r\n }\r\n\r\n if (id !== this.peer.id) {\r\n console.warn('DEBUG Check you assumption that id === this.peer.id');\r\n }\r\n\r\n this.#showQRCode();\r\n };\r\n\r\n /**\r\n * Called when the peer connection is established.\r\n * Enforces a single connection.\r\n *\r\n * @param connection - The connection object.\r\n * @private\r\n * @example\r\n */\r\n #onPeerConnection = connection => {\r\n // Allow only a single connection\r\n if (this.conn && this.conn.open) {\r\n connection.on('open', () => {\r\n connection.send('Already connected to another client');\r\n setTimeout(() => {\r\n connection.close();\r\n }, 500);\r\n });\r\n return;\r\n }\r\n\r\n this.conn = connection;\r\n console.log('Connected to: ', this.conn.peer);\r\n this.#ready();\r\n };\r\n\r\n /**\r\n * Called when the peer connection is closed.\r\n *\r\n * @private\r\n * @example\r\n */\r\n #onPeerClose = () => {\r\n this.conn = null;\r\n console.log('Connection destroyed');\r\n };\r\n\r\n /**\r\n * Called when the peer connection is disconnected.\r\n * Attempts to reconnect.\r\n *\r\n * @private\r\n * @example\r\n */\r\n #onPeerDisconnected = () => {\r\n console.log('Connection lost. Please reconnect');\r\n\r\n // Workaround for peer.reconnect deleting previous id\r\n this.peer.id = this.lastPeerId;\r\n // eslint-disable-next-line no-underscore-dangle\r\n this.peer._lastServerId = this.lastPeerId;\r\n this.peer.reconnect();\r\n };\r\n\r\n /**\r\n * Called when the peer connection encounters an error.\r\n *\r\n * @param error\r\n * @private\r\n * @example\r\n */\r\n #onPeerError = error => {\r\n // TODO: check if this function is needed or not\r\n console.error(error);\r\n };\r\n\r\n /**\r\n * Called when data is received from the peer connection.\r\n *\r\n * @param data\r\n * @private\r\n * @example\r\n */\r\n #onIncomingData = data => {\r\n // enforce object type\r\n if (\r\n !Object.prototype.hasOwnProperty.call(data, 'name') ||\r\n !Object.prototype.hasOwnProperty.call(data, 'payload')\r\n ) {\r\n console.error('Received malformed data: ', data);\r\n return;\r\n }\r\n\r\n switch (data.name) {\r\n case 'samplingRate':\r\n this.ac.setSamplingRates(data.payload);\r\n break;\r\n case 'deviceType':\r\n this.ac.setDeviceType(data.payload);\r\n break;\r\n case 'deviceName':\r\n this.ac.setDeviceName(data.payload);\r\n break;\r\n case 'deviceInfo':\r\n this.ac.setDeviceInfo(data.payload);\r\n case _peerErrors__WEBPACK_IMPORTED_MODULE_3__.UnsupportedDeviceError.name:\r\n case _peerErrors__WEBPACK_IMPORTED_MODULE_3__.MissingSpeakerIdError.name:\r\n throw data.payload;\r\n break;\r\n default:\r\n break;\r\n }\r\n };\r\n\r\n /**\r\n * Called when the peer connection is #ready.\r\n *\r\n * @private\r\n * @example\r\n */\r\n #ready = () => {\r\n // Perform callback with data\r\n this.conn.on('data', this.#onIncomingData);\r\n this.conn.on('close', () => {\r\n console.log('Connection reset<br>Awaiting connection...');\r\n this.conn = null;\r\n });\r\n };\r\n\r\n /** .\r\n * .\r\n * .\r\n * Debug method for downloading the recorded audio\r\n *\r\n * @public\r\n * @example\r\n */\r\n downloadData = () => {\r\n this.ac.downloadData();\r\n };\r\n}\r\n\r\n/* \r\nReferenced links:\r\nhttps://stackoverflow.com/questions/28016664/when-you-pass-this-as-an-argument/28016676#28016676\r\nhttps://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes\r\nhttps://stackoverflow.com/questions/879152/how-do-i-make-javascript-beep [3]\r\n*/\r\n\r\n/* harmony default export */ __webpack_exports__[\"default\"] = (Speaker);\r\n\n\n//# sourceURL=webpack://speakerCalibrator/./src/peer-connection/speaker.js?");
800
800
 
801
801
  /***/ }),
802
802
 
@@ -807,7 +807,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var qrco
807
807
  /***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
808
808
 
809
809
  "use strict";
810
- eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var axios__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! axios */ \"./node_modules/axios/index.js\");\n/* harmony import */ var axios__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(axios__WEBPACK_IMPORTED_MODULE_0__);\n\r\n/**\r\n *\r\n */\r\nclass PythonServerAPI {\r\n static PYTHON_SERVER_URL = 'https://easyeyes-python-flask-server.herokuapp.com';\r\n\r\n static TEST_SERVER_URL = 'http://127.0.0.1:5000';\r\n\r\n /** @private */\r\n MAX_RETRY_COUNT = 3;\r\n /** @private */\r\n RETRY_DELAY_MS = 1000;\r\n /**\r\n * @param data- -\r\n * g = inverted impulse response, when convolved with the impulse\r\n * reponse, they cancel out.\r\n * @param data.payload\r\n * @param data.sampleRate\r\n * @param data.P\r\n * @param data-.payload\r\n * @param data-.sampleRate\r\n * @param data-.P\r\n * @returns\r\n * @example\r\n */\r\n getImpulseResponse = async ({mls, payload, sampleRate, P, numPeriods}) => {\r\n const task = 'impulse-response';\r\n let res = null;\r\n\r\n console.log({payload});\r\n\r\n const data = JSON.stringify({\r\n task,\r\n payload,\r\n 'sample-rate': sampleRate,\r\n mls,\r\n P,\r\n numPeriods,\r\n });\r\n\r\n await axios__WEBPACK_IMPORTED_MODULE_0___default()({\r\n method: 'post',\r\n baseURL: PythonServerAPI.PYTHON_SERVER_URL,\r\n url: `/task/${task}`,\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n },\r\n data,\r\n })\r\n .then(response => {\r\n res = response;\r\n })\r\n .catch(error => {\r\n throw error;\r\n });\r\n return res.data[task];\r\n };\r\n\r\n getMLS = async(length) => {\r\n const task = 'mls';\r\n let res = null\r\n\r\n const data = JSON.stringify({\r\n task,\r\n 'length':length,\r\n })\r\n\r\n await axios__WEBPACK_IMPORTED_MODULE_0___default()({\r\n method:'post',\r\n baseURL: PythonServerAPI.PYTHON_SERVER_URL,\r\n url: `/task/${task}`,\r\n headers: {\r\n 'Content-Type':'application/json',\r\n },\r\n data,\r\n })\r\n .then(response => {\r\n res = response;\r\n })\r\n .catch(error => {\r\n throw error;\r\n })\r\n\r\n return res.data[task];\r\n }\r\n getMLSWithRetry = async (length) => {\r\n let retryCount = 0;\r\n let response = null;\r\n\r\n while (retryCount < this.MAX_RETRY_COUNT) {\r\n try {\r\n response = await this.getMLS(length);\r\n // If the request is successful, break out of the loop\r\n break;\r\n } catch (error) {\r\n console.error(`Error occurred. Retrying... (${retryCount + 1}/${this.MAX_RETRY_COUNT})`);\r\n retryCount++;\r\n await new Promise(resolve => setTimeout(resolve, this.RETRY_DELAY_MS));\r\n }\r\n }\r\n\r\n if (response) {\r\n return response;\r\n } else {\r\n throw new Error(`Failed to get MLS after ${this.MAX_RETRY_COUNT} attempts.`);\r\n }\r\n };\r\n\r\n getPSD = async ({unconv_rec, conv_rec, sampleRate}) => {\r\n const task = 'psd';\r\n let res = null;\r\n\r\n const data = JSON.stringify({\r\n task,\r\n unconv_rec,\r\n conv_rec,\r\n sampleRate,\r\n });\r\n\r\n await axios__WEBPACK_IMPORTED_MODULE_0___default()({\r\n method: 'post',\r\n baseURL: PythonServerAPI.PYTHON_SERVER_URL,\r\n url: `/task/${task}`,\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n },\r\n data,\r\n })\r\n .then(response => {\r\n res = response;\r\n })\r\n .catch(error => {\r\n throw error;\r\n });\r\n return res.data[task];\r\n };\r\n\r\n getSubtractedPSD = async (rec, knownGains, knownFrequencies,sampleRate) => {\r\n const task = 'subtracted-psd';\r\n let res = null;\r\n\r\n const data = JSON.stringify({\r\n task,\r\n rec,\r\n knownGains,\r\n knownFrequencies,\r\n sampleRate,\r\n });\r\n\r\n await axios__WEBPACK_IMPORTED_MODULE_0___default()({\r\n method: 'post',\r\n baseURL: PythonServerAPI.PYTHON_SERVER_URL,\r\n url: `/task/${task}`,\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n },\r\n data,\r\n })\r\n .then(response => {\r\n res = response;\r\n })\r\n .catch(error => {\r\n throw error;\r\n });\r\n return res.data[task];\r\n };\r\n\r\n getSubtractedPSDWithRetry = async ( rec, knownGains, knownFrequencies,sampleRate) => {\r\n let retryCount = 0;\r\n let response = null;\r\n\r\n while (retryCount < this.MAX_RETRY_COUNT) {\r\n try {\r\n response = await this.getSubtractedPSD( rec, knownGains, knownFrequencies,sampleRate );\r\n // If the request is successful, break out of the loop\r\n break;\r\n } catch (error) {\r\n console.error(`Error occurred. Retrying... (${retryCount + 1}/${this.MAX_RETRY_COUNT})`);\r\n retryCount++;\r\n await new Promise(resolve => setTimeout(resolve, this.RETRY_DELAY_MS));\r\n }\r\n }\r\n\r\n if (response) {\r\n return response;\r\n } else {\r\n throw new Error(`Failed to get PSD after ${this.MAX_RETRY_COUNT} attempts.`);\r\n }\r\n };\r\n \r\n getPSDWithRetry = async ({ unconv_rec, conv_rec,sampleRate }) => {\r\n let retryCount = 0;\r\n let response = null;\r\n \r\n while (retryCount < this.MAX_RETRY_COUNT) {\r\n try {\r\n response = await this.getPSD({ unconv_rec, conv_rec,sampleRate });\r\n // If the request is successful, break out of the loop\r\n break;\r\n } catch (error) {\r\n console.error(`Error occurred. Retrying... (${retryCount + 1}/${this.MAX_RETRY_COUNT})`);\r\n retryCount++;\r\n await new Promise(resolve => setTimeout(resolve, this.RETRY_DELAY_MS));\r\n }\r\n }\r\n \r\n if (response) {\r\n return response;\r\n } else {\r\n throw new Error(`Failed to get PSD after ${this.MAX_RETRY_COUNT} attempts.`);\r\n }\r\n };\r\n \r\n\r\n getComponentInverseImpulseResponse = async ({payload,mls,lowHz,highHz,componentIRGains,componentIRFreqs,sampleRate}) => {\r\n const task = 'component-inverse-impulse-response';\r\n let res = null;\r\n\r\n console.log({payload});\r\n\r\n const data = JSON.stringify({\r\n task,\r\n payload,\r\n mls,\r\n lowHz,\r\n highHz,\r\n componentIRGains,\r\n componentIRFreqs,\r\n sampleRate,\r\n });\r\n\r\n await axios__WEBPACK_IMPORTED_MODULE_0___default()({\r\n method: 'post',\r\n baseURL: PythonServerAPI.PYTHON_SERVER_URL,\r\n url: `/task/${task}`,\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n },\r\n data,\r\n })\r\n .then(response => {\r\n res = response;\r\n })\r\n .catch(error => {\r\n throw error;\r\n });\r\n\r\n return res.data[task];\r\n };\r\n getSystemInverseImpulseResponse = async ({payload,mls,lowHz,highHz,sampleRate}) => {\r\n const task = 'system-inverse-impulse-response';\r\n let res = null;\r\n\r\n console.log({payload});\r\n\r\n const data = JSON.stringify({\r\n task,\r\n payload,\r\n mls,\r\n lowHz,\r\n highHz,\r\n sampleRate,\r\n });\r\n\r\n await axios__WEBPACK_IMPORTED_MODULE_0___default()({\r\n method: 'post',\r\n baseURL: PythonServerAPI.PYTHON_SERVER_URL,\r\n url: `/task/${task}`,\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n },\r\n data,\r\n })\r\n .then(response => {\r\n res = response;\r\n })\r\n .catch(error => {\r\n throw error;\r\n });\r\n\r\n return res.data[task];\r\n };\r\n\r\ngetComponentInverseImpulseResponseWithRetry = async ({ payload, mls, lowHz, highHz,componentIRGains,componentIRFreqs, sampleRate}) => {\r\n let retryCount = 0;\r\n let response = null;\r\n\r\n while (retryCount < this.MAX_RETRY_COUNT) {\r\n try {\r\n response = await this.getComponentInverseImpulseResponse({ payload, mls, lowHz, highHz,componentIRGains,componentIRFreqs,sampleRate});\r\n // If the request is successful, break out of the loop\r\n break;\r\n } catch (error) {\r\n console.error(`Error occurred. Retrying... (${retryCount + 1}/${this.MAX_RETRY_COUNT})`);\r\n retryCount++;\r\n await new Promise(resolve => setTimeout(resolve, this.RETRY_DELAY_MS));\r\n }\r\n }\r\n\r\n if (response) {\r\n return response;\r\n } else {\r\n throw new Error(`Failed to get inverse impulse response after ${this.MAX_RETRY_COUNT} attempts.`);\r\n }\r\n};\r\n\r\ngetSystemInverseImpulseResponseWithRetry = async ({ payload, mls, lowHz, highHz, sampleRate}) => {\r\n let retryCount = 0;\r\n let response = null;\r\n\r\n while (retryCount < this.MAX_RETRY_COUNT) {\r\n try {\r\n response = await this.getSystemInverseImpulseResponse({ payload, mls, lowHz, highHz,sampleRate});\r\n // If the request is successful, break out of the loop\r\n break;\r\n } catch (error) {\r\n console.error(`Error occurred. Retrying... (${retryCount + 1}/${this.MAX_RETRY_COUNT})`);\r\n retryCount++;\r\n await new Promise(resolve => setTimeout(resolve, this.RETRY_DELAY_MS));\r\n }\r\n }\r\n\r\n if (response) {\r\n return response;\r\n } else {\r\n throw new Error(`Failed to get inverse impulse response after ${this.MAX_RETRY_COUNT} attempts.`);\r\n }\r\n};\r\n\r\n\r\n\r\n getVolumeCalibration = async ({payload, sampleRate, lCalib}) => {\r\n const task = 'volume';\r\n let res = null;\r\n\r\n console.log({payload});\r\n\r\n const data = JSON.stringify({\r\n task,\r\n payload,\r\n 'sample-rate': sampleRate,\r\n lCalib,\r\n });\r\n\r\n await axios__WEBPACK_IMPORTED_MODULE_0___default()({\r\n method: 'post',\r\n baseURL: PythonServerAPI.PYTHON_SERVER_URL,\r\n url: `/task/${task}`,\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n },\r\n data,\r\n })\r\n .then(response => {\r\n res = response;\r\n })\r\n .catch(error => {\r\n throw error;\r\n });\r\n\r\n return res.data[task];\r\n };\r\n\r\n getVolumeCalibrationParameters = async ({inDBValues, outDBSPLValues, lCalib, componentGainDBSPL}) => {\r\n const task = 'volume-parameters';\r\n let res = null;\r\n\r\n const data = JSON.stringify({\r\n task,\r\n inDBValues,\r\n outDBSPLValues,\r\n lCalib,\r\n componentGainDBSPL,\r\n });\r\n\r\n await axios__WEBPACK_IMPORTED_MODULE_0___default()({\r\n method: 'post',\r\n baseURL: PythonServerAPI.PYTHON_SERVER_URL, //server\r\n url: `/task/${task}`,\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n },\r\n data,\r\n })\r\n .then(response => {\r\n res = response;\r\n console.log(res.data[task]);\r\n })\r\n .catch(error => {\r\n throw error;\r\n });\r\n\r\n\r\n\r\n // console.log(res.data[task]);\r\n //below is an example of res.data[task]\r\n //{\r\n // R: 16.56981076554259,\r\n // RMSError: 1.9289162528535229\r\n // T: -47.79799120884434,\r\n // W: 61.0485247483732,\r\n // backgroundDBSPL: 43.88233142069752,\r\n // gainDBSPL: -128.24742161208985\r\n //}\r\n return res.data[task];\r\n };\r\n}\r\n\r\n/* harmony default export */ __webpack_exports__[\"default\"] = (PythonServerAPI);\r\n\n\n//# sourceURL=webpack://speakerCalibrator/./src/server/PythonServerAPI.js?");
810
+ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var axios__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! axios */ \"./node_modules/axios/index.js\");\n/* harmony import */ var axios__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(axios__WEBPACK_IMPORTED_MODULE_0__);\n\r\n/**\r\n *\r\n */\r\nclass PythonServerAPI {\r\n static PYTHON_SERVER_URL = 'https://easyeyes-python-flask-server.herokuapp.com';\r\n\r\n static TEST_SERVER_URL = 'http://127.0.0.1:5000';\r\n\r\n /** @private */\r\n MAX_RETRY_COUNT = 3;\r\n /** @private */\r\n RETRY_DELAY_MS = 1000;\r\n /**\r\n * @param data- -\r\n * g = inverted impulse response, when convolved with the impulse\r\n * reponse, they cancel out.\r\n * @param data.payload\r\n * @param data.sampleRate\r\n * @param data.P\r\n * @param data-.payload\r\n * @param data-.sampleRate\r\n * @param data-.P\r\n * @returns\r\n * @example\r\n */\r\n getImpulseResponse = async ({mls, payload, sampleRate, P, numPeriods}) => {\r\n const task = 'impulse-response';\r\n let res = null;\r\n\r\n console.log({payload});\r\n\r\n const data = JSON.stringify({\r\n task,\r\n payload,\r\n 'sample-rate': sampleRate,\r\n mls,\r\n P,\r\n numPeriods,\r\n });\r\n\r\n await axios__WEBPACK_IMPORTED_MODULE_0___default()({\r\n method: 'post',\r\n baseURL: PythonServerAPI.PYTHON_SERVER_URL,\r\n url: `/task/${task}`,\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n },\r\n data,\r\n })\r\n .then(response => {\r\n res = response;\r\n })\r\n .catch(error => {\r\n throw error;\r\n });\r\n return res.data[task];\r\n };\r\n\r\n getMLS = async(length) => {\r\n const task = 'mls';\r\n let res = null\r\n\r\n const data = JSON.stringify({\r\n task,\r\n 'length':length,\r\n })\r\n\r\n await axios__WEBPACK_IMPORTED_MODULE_0___default()({\r\n method:'post',\r\n baseURL: PythonServerAPI.PYTHON_SERVER_URL,\r\n url: `/task/${task}`,\r\n headers: {\r\n 'Content-Type':'application/json',\r\n },\r\n data,\r\n })\r\n .then(response => {\r\n res = response;\r\n })\r\n .catch(error => {\r\n throw error;\r\n })\r\n\r\n return res.data[task];\r\n }\r\n getMLSWithRetry = async (length) => {\r\n let retryCount = 0;\r\n let response = null;\r\n\r\n while (retryCount < this.MAX_RETRY_COUNT) {\r\n try {\r\n response = await this.getMLS(length);\r\n // If the request is successful, break out of the loop\r\n break;\r\n } catch (error) {\r\n console.error(`Error occurred. Retrying... (${retryCount + 1}/${this.MAX_RETRY_COUNT})`);\r\n retryCount++;\r\n await new Promise(resolve => setTimeout(resolve, this.RETRY_DELAY_MS));\r\n }\r\n }\r\n\r\n if (response) {\r\n return response;\r\n } else {\r\n throw new Error(`Failed to get MLS after ${this.MAX_RETRY_COUNT} attempts.`);\r\n }\r\n };\r\n\r\n getPSD = async ({unconv_rec, conv_rec, sampleRate}) => {\r\n const task = 'psd';\r\n let res = null;\r\n\r\n const data = JSON.stringify({\r\n task,\r\n unconv_rec,\r\n conv_rec,\r\n sampleRate,\r\n });\r\n\r\n await axios__WEBPACK_IMPORTED_MODULE_0___default()({\r\n method: 'post',\r\n baseURL: PythonServerAPI.PYTHON_SERVER_URL,\r\n url: `/task/${task}`,\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n },\r\n data,\r\n })\r\n .then(response => {\r\n res = response;\r\n })\r\n .catch(error => {\r\n throw error;\r\n });\r\n return res.data[task];\r\n };\r\n\r\n getSubtractedPSD = async (rec, knownGains, knownFrequencies,sampleRate) => {\r\n const task = 'subtracted-psd';\r\n let res = null;\r\n\r\n const data = JSON.stringify({\r\n task,\r\n rec,\r\n knownGains,\r\n knownFrequencies,\r\n sampleRate,\r\n });\r\n\r\n await axios__WEBPACK_IMPORTED_MODULE_0___default()({\r\n method: 'post',\r\n baseURL: PythonServerAPI.PYTHON_SERVER_URL,\r\n url: `/task/${task}`,\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n },\r\n data,\r\n })\r\n .then(response => {\r\n res = response;\r\n })\r\n .catch(error => {\r\n throw error;\r\n });\r\n return res.data[task];\r\n };\r\n\r\n getSubtractedPSDWithRetry = async ( rec, knownGains, knownFrequencies,sampleRate) => {\r\n let retryCount = 0;\r\n let response = null;\r\n\r\n while (retryCount < this.MAX_RETRY_COUNT) {\r\n try {\r\n response = await this.getSubtractedPSD( rec, knownGains, knownFrequencies,sampleRate );\r\n // If the request is successful, break out of the loop\r\n break;\r\n } catch (error) {\r\n console.error(`Error occurred. Retrying... (${retryCount + 1}/${this.MAX_RETRY_COUNT})`);\r\n retryCount++;\r\n await new Promise(resolve => setTimeout(resolve, this.RETRY_DELAY_MS));\r\n }\r\n }\r\n\r\n if (response) {\r\n return response;\r\n } else {\r\n throw new Error(`Failed to get PSD after ${this.MAX_RETRY_COUNT} attempts.`);\r\n }\r\n };\r\n \r\n getPSDWithRetry = async ({ unconv_rec, conv_rec,sampleRate }) => {\r\n let retryCount = 0;\r\n let response = null;\r\n \r\n while (retryCount < this.MAX_RETRY_COUNT) {\r\n try {\r\n response = await this.getPSD({ unconv_rec, conv_rec,sampleRate });\r\n // If the request is successful, break out of the loop\r\n break;\r\n } catch (error) {\r\n console.error(`Error occurred. Retrying... (${retryCount + 1}/${this.MAX_RETRY_COUNT})`);\r\n retryCount++;\r\n await new Promise(resolve => setTimeout(resolve, this.RETRY_DELAY_MS));\r\n }\r\n }\r\n \r\n if (response) {\r\n return response;\r\n } else {\r\n throw new Error(`Failed to get PSD after ${this.MAX_RETRY_COUNT} attempts.`);\r\n }\r\n };\r\n \r\n\r\n getComponentInverseImpulseResponse = async ({payload,mls,lowHz,highHz,componentIRGains,iirLength,componentIRFreqs,sampleRate}) => {\r\n const task = 'component-inverse-impulse-response';\r\n let res = null;\r\n\r\n console.log({payload});\r\n\r\n const data = JSON.stringify({\r\n task,\r\n payload,\r\n mls,\r\n lowHz,\r\n highHz,\r\n iirLength,\r\n componentIRGains,\r\n componentIRFreqs,\r\n sampleRate,\r\n });\r\n\r\n await axios__WEBPACK_IMPORTED_MODULE_0___default()({\r\n method: 'post',\r\n baseURL: PythonServerAPI.PYTHON_SERVER_URL,\r\n url: `/task/${task}`,\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n },\r\n data,\r\n })\r\n .then(response => {\r\n res = response;\r\n })\r\n .catch(error => {\r\n throw error;\r\n });\r\n\r\n return res.data[task];\r\n };\r\n getSystemInverseImpulseResponse = async ({payload,mls,lowHz,highHz,iirLength,sampleRate}) => {\r\n const task = 'system-inverse-impulse-response';\r\n let res = null;\r\n\r\n console.log({payload});\r\n\r\n const data = JSON.stringify({\r\n task,\r\n payload,\r\n mls,\r\n lowHz,\r\n iirLength,\r\n highHz,\r\n sampleRate,\r\n });\r\n\r\n await axios__WEBPACK_IMPORTED_MODULE_0___default()({\r\n method: 'post',\r\n baseURL: PythonServerAPI.PYTHON_SERVER_URL,\r\n url: `/task/${task}`,\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n },\r\n data,\r\n })\r\n .then(response => {\r\n res = response;\r\n })\r\n .catch(error => {\r\n throw error;\r\n });\r\n\r\n return res.data[task];\r\n };\r\n\r\ngetComponentInverseImpulseResponseWithRetry = async ({ payload, mls, lowHz, highHz,componentIRGains,iirLength,componentIRFreqs, sampleRate}) => {\r\n let retryCount = 0;\r\n let response = null;\r\n\r\n while (retryCount < this.MAX_RETRY_COUNT) {\r\n try {\r\n response = await this.getComponentInverseImpulseResponse({ payload, mls, lowHz, highHz,componentIRGains,iirLength,componentIRFreqs,sampleRate});\r\n // If the request is successful, break out of the loop\r\n break;\r\n } catch (error) {\r\n console.error(`Error occurred. Retrying... (${retryCount + 1}/${this.MAX_RETRY_COUNT})`);\r\n retryCount++;\r\n await new Promise(resolve => setTimeout(resolve, this.RETRY_DELAY_MS));\r\n }\r\n }\r\n\r\n if (response) {\r\n return response;\r\n } else {\r\n throw new Error(`Failed to get inverse impulse response after ${this.MAX_RETRY_COUNT} attempts.`);\r\n }\r\n};\r\n\r\ngetSystemInverseImpulseResponseWithRetry = async ({ payload, mls, lowHz, highHz,iirLength, sampleRate}) => {\r\n let retryCount = 0;\r\n let response = null;\r\n\r\n while (retryCount < this.MAX_RETRY_COUNT) {\r\n try {\r\n response = await this.getSystemInverseImpulseResponse({ payload, mls, lowHz, highHz,iirLength,sampleRate});\r\n // If the request is successful, break out of the loop\r\n break;\r\n } catch (error) {\r\n console.error(`Error occurred. Retrying... (${retryCount + 1}/${this.MAX_RETRY_COUNT})`);\r\n retryCount++;\r\n await new Promise(resolve => setTimeout(resolve, this.RETRY_DELAY_MS));\r\n }\r\n }\r\n\r\n if (response) {\r\n return response;\r\n } else {\r\n throw new Error(`Failed to get inverse impulse response after ${this.MAX_RETRY_COUNT} attempts.`);\r\n }\r\n};\r\n\r\n\r\n\r\n getVolumeCalibration = async ({payload, sampleRate, lCalib}) => {\r\n const task = 'volume';\r\n let res = null;\r\n\r\n console.log({payload});\r\n\r\n const data = JSON.stringify({\r\n task,\r\n payload,\r\n 'sample-rate': sampleRate,\r\n lCalib,\r\n });\r\n\r\n await axios__WEBPACK_IMPORTED_MODULE_0___default()({\r\n method: 'post',\r\n baseURL: PythonServerAPI.PYTHON_SERVER_URL,\r\n url: `/task/${task}`,\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n },\r\n data,\r\n })\r\n .then(response => {\r\n res = response;\r\n })\r\n .catch(error => {\r\n throw error;\r\n });\r\n\r\n return res.data[task];\r\n };\r\n\r\n getVolumeCalibrationParameters = async ({inDBValues, outDBSPLValues, lCalib, componentGainDBSPL}) => {\r\n const task = 'volume-parameters';\r\n let res = null;\r\n\r\n const data = JSON.stringify({\r\n task,\r\n inDBValues,\r\n outDBSPLValues,\r\n lCalib,\r\n componentGainDBSPL,\r\n });\r\n\r\n await axios__WEBPACK_IMPORTED_MODULE_0___default()({\r\n method: 'post',\r\n baseURL: PythonServerAPI.PYTHON_SERVER_URL, //server\r\n url: `/task/${task}`,\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n },\r\n data,\r\n })\r\n .then(response => {\r\n res = response;\r\n console.log(res.data[task]);\r\n })\r\n .catch(error => {\r\n throw error;\r\n });\r\n\r\n\r\n\r\n // console.log(res.data[task]);\r\n //below is an example of res.data[task]\r\n //{\r\n // R: 16.56981076554259,\r\n // RMSError: 1.9289162528535229\r\n // T: -47.79799120884434,\r\n // W: 61.0485247483732,\r\n // backgroundDBSPL: 43.88233142069752,\r\n // gainDBSPL: -128.24742161208985\r\n //}\r\n return res.data[task];\r\n };\r\n}\r\n\r\n/* harmony default export */ __webpack_exports__[\"default\"] = (PythonServerAPI);\r\n\n\n//# sourceURL=webpack://speakerCalibrator/./src/server/PythonServerAPI.js?");
811
811
 
812
812
  /***/ }),
813
813
 
@@ -840,7 +840,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _myE
840
840
  /***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
841
841
 
842
842
  "use strict";
843
- eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _audioCalibrator__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../audioCalibrator */ \"./src/tasks/audioCalibrator.js\");\n/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../utils */ \"./src/utils.js\");\n/* harmony import */ var _config_firebase__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../config/firebase */ \"./src/config/firebase.js\");\n/* harmony import */ var firebase_database__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! firebase/database */ \"./node_modules/firebase/database/dist/esm/index.esm.js\");\n\r\n\r\n\r\n\r\n\r\n\r\n/**\r\n *\r\n */\r\nclass Combination extends _audioCalibrator__WEBPACK_IMPORTED_MODULE_0__[\"default\"] {\r\n /**\r\n * Default constructor. Creates an instance with any number of paramters passed or the default parameters defined here.\r\n *\r\n * @param {Object<boolean, number, number, number>} calibratorParams - paramter object\r\n * @param {boolean} [calibratorParams.download = false] - boolean flag to download captures\r\n * @param {number} [calibratorParams.mlsOrder = 18] - order of the MLS to be generated\r\n * @param {number} [calibratorParams.numCaptures = 5] - number of captures to perform\r\n * @param {number} [calibratorParams.numMLSPerCapture = 4] - number of bursts of MLS per capture\r\n */\r\n constructor({\r\n download = false,\r\n mlsOrder = 18,\r\n numCaptures = 3,\r\n numMLSPerCapture = 4,\r\n lowHz = 20,\r\n highHz = 10000,\r\n }) {\r\n super(numCaptures, numMLSPerCapture);\r\n this.#mlsOrder = parseInt(mlsOrder, 10);\r\n this.#P = 2 ** mlsOrder - 1;\r\n this.#download = download;\r\n this.#mls = [];\r\n this.#lowHz = lowHz;\r\n this.#highHz = highHz;\r\n }\r\n\r\n /** @private */\r\n stepNum = 0;\r\n\r\n /** @private */\r\n totalSteps = 25;\r\n\r\n /** @private */\r\n #download;\r\n\r\n /** @private */\r\n #mlsGenInterface;\r\n\r\n /** @private */\r\n #mlsBufferView;\r\n\r\n /** @private */\r\n componentInvertedImpulseResponse = null;\r\n\r\n /** @private */\r\n systemInvertedImpulseResponse = null;\r\n\r\n //averaged and subtracted ir returned from calibration used to calculated iir\r\n /** @private */\r\n ir = null;\r\n\r\n /** @private */\r\n impulseResponses = [];\r\n\r\n /** @private */\r\n #mlsOrder;\r\n\r\n /** @private */\r\n #lowHz;\r\n\r\n /** @private */\r\n #highHz;\r\n\r\n /** @private */\r\n #mls;\r\n\r\n /** @private */\r\n #P;\r\n\r\n /** @private */\r\n #audioContext;\r\n\r\n /** @private */\r\n TAPER_SECS = 5;\r\n\r\n /** @private */\r\n offsetGainNode;\r\n\r\n /** @private */\r\n componentConvolution;\r\n\r\n /** @private */\r\n systemConvolution;\r\n\r\n ////////////////////////volume\r\n /** @private */\r\n #CALIBRATION_TONE_FREQUENCY = 1000; // Hz\r\n\r\n /** @private */\r\n #CALIBRATION_TONE_TYPE = 'sine';\r\n\r\n /** @private */\r\n #CALIBRATION_TONE_DURATION = 5; // seconds\r\n\r\n /** @private */\r\n outDBSPL = null;\r\n THD = null;\r\n outDBSPL1000 = null;\r\n\r\n /** @private */\r\n TAPER_SECS = 0.01; // seconds\r\n\r\n /** @private */\r\n status_denominator = 8;\r\n\r\n /** @private */\r\n status_numerator = 0;\r\n\r\n /** @private */\r\n percent_complete = 0;\r\n\r\n /** @private */\r\n status = ``;\r\n\r\n /**@private */\r\n status_literal = `<div style=\"display: flex; justify-content: center;\"><div style=\"width: 200px; height: 20px; border: 2px solid #000; border-radius: 10px;\"><div style=\"width: ${this.percent_complete}%; height: 100%; background-color: #00aaff; border-radius: 8px;\"></div></div></div>`;\r\n\r\n /**@private */\r\n componentIR = null;\r\n\r\n /**@private */\r\n oldComponentIR = null;\r\n\r\n /**@private */\r\n systemIR = null;\r\n\r\n /**@private */\r\n _calibrateSoundCheck = '';\r\n\r\n deviceType = null;\r\n\r\n deviceName = null;\r\n\r\n deviceInfo = null;\r\n\r\n desired_time_per_mls = 0;\r\n\r\n num_mls_to_skip = 0;\r\n\r\n desired_sampling_rate = 0;\r\n\r\n #currentConvolution = [];\r\n\r\n mode = 'unfiltered';\r\n\r\n sourceNode;\r\n\r\n autocorrelations = [];\r\n\r\n /**generate string template that gets reevaluated as variable increases */\r\n generateTemplate = () => {\r\n if (this.percent_complete > 100) {\r\n this.percent_complete = 100;\r\n }\r\n const template = `<div style=\"display: flex; justify-content: center;\"><div style=\"width: 800px; height: 20px; border: 2px solid #000; border-radius: 10px;\"><div style=\"width: ${this.percent_complete}%; height: 100%; background-color: #00aaff; border-radius: 8px;\"></div></div></div>`;\r\n return template;\r\n };\r\n\r\n /** increment numerator and percent for status bar */\r\n incrementStatusBar = () => {\r\n this.status_numerator += 1;\r\n this.percent_complete = (this.status_numerator / this.status_denominator) * 100;\r\n };\r\n\r\n setDeviceType = deviceType => {\r\n this.deviceType = deviceType;\r\n };\r\n\r\n setDeviceName = deviceName => {\r\n this.deviceName = deviceName;\r\n };\r\n\r\n setDeviceInfo = deviceInfo => {\r\n this.deviceInfo = deviceInfo;\r\n };\r\n\r\n /** .\r\n * .\r\n * .\r\n * Sends all the computed impulse responses to the backend server for processing\r\n *\r\n * @returns sets the resulting inverted impulse response to the class property\r\n * @example\r\n */\r\n sendSystemImpulseResponsesToServerForProcessing = async () => {\r\n const computedIRs = await Promise.all(this.impulseResponses);\r\n const filteredComputedIRs = computedIRs.filter(element => {\r\n return element != undefined;\r\n });\r\n const mls = this.#mls;\r\n const lowHz = this.#lowHz;\r\n const highHz = this.#highHz;\r\n this.stepNum += 1;\r\n console.log('send impulse responses to server: ' + this.stepNum);\r\n this.status =\r\n `All Hz Calibration: computing the IIR...`.toString() + this.generateTemplate().toString();\r\n this.emit('update', {message: this.status});\r\n return this.pyServerAPI\r\n .getSystemInverseImpulseResponseWithRetry({\r\n payload: filteredComputedIRs.slice(0, this.numCaptures),\r\n mls,\r\n lowHz,\r\n highHz,\r\n sampleRate: this.sourceSamplingRate || 96000,\r\n })\r\n .then(res => {\r\n console.log(res);\r\n this.stepNum += 1;\r\n console.log('got impulse response ' + this.stepNum);\r\n this.incrementStatusBar();\r\n this.status =\r\n `All Hz Calibration: done computing the IIR...`.toString() +\r\n this.generateTemplate().toString();\r\n this.emit('update', {message: this.status});\r\n this.systemInvertedImpulseResponse = res['iir'];\r\n this.systemIR = res['ir'];\r\n this.systemConvolution = res['convolution'];\r\n })\r\n .catch(err => {\r\n console.error(err);\r\n });\r\n };\r\n\r\n /** .\r\n * .\r\n * .\r\n * Sends all the computed impulse responses to the backend server for processing\r\n *\r\n * @returns sets the resulting inverted impulse response to the class property\r\n * @example\r\n */\r\n sendComponentImpulseResponsesToServerForProcessing = async () => {\r\n const computedIRs = await Promise.all(this.impulseResponses);\r\n const filteredComputedIRs = computedIRs.filter(element => {\r\n return element != undefined;\r\n });\r\n const componentIRGains = this.componentIR['Gain'];\r\n const componentIRFreqs = this.componentIR['Freq'];\r\n const mls = this.#mls;\r\n const lowHz = this.#lowHz;\r\n const highHz = this.#highHz;\r\n this.stepNum += 1;\r\n console.log('send impulse responses to server: ' + this.stepNum);\r\n this.status =\r\n `All Hz Calibration: computing the IIR...`.toString() + this.generateTemplate().toString();\r\n this.emit('update', {message: this.status});\r\n return this.pyServerAPI\r\n .getComponentInverseImpulseResponseWithRetry({\r\n payload: filteredComputedIRs.slice(0, this.numCaptures),\r\n mls,\r\n lowHz,\r\n highHz,\r\n componentIRGains,\r\n componentIRFreqs,\r\n sampleRate: this.sourceSamplingRate || 96000,\r\n })\r\n .then(res => {\r\n console.log(res);\r\n this.stepNum += 1;\r\n console.log('got impulse response ' + this.stepNum);\r\n this.incrementStatusBar();\r\n this.status =\r\n `All Hz Calibration: done computing the IIR...`.toString() +\r\n this.generateTemplate().toString();\r\n this.emit('update', {message: this.status});\r\n this.componentInvertedImpulseResponse = res['iir'];\r\n this.componentIR['Gain'] = res['ir'];\r\n this.componentIR['Freq'] = res['frequencies'];\r\n this.componentConvolution = res['convolution'];\r\n })\r\n .catch(err => {\r\n // this.emit('InvertedImpulseResponse', {res: false});\r\n console.error(err);\r\n });\r\n };\r\n\r\n /** .\r\n * .\r\n * .\r\n * Sends the recorded signal, or a given csv string of a signal, to the back end server for processing\r\n *\r\n * @param {<array>String} signalCsv - Optional csv string of a previously recorded signal, if given, this signal will be processed\r\n * @example\r\n */\r\n sendRecordingToServerForProcessing = signalCsv => {\r\n const allSignals = this.getAllRecordedSignals();\r\n const numSignals = allSignals.length;\r\n const mls = this.#mls;\r\n const payload =\r\n signalCsv && signalCsv.length > 0 ? (0,_utils__WEBPACK_IMPORTED_MODULE_1__.csvToArray)(signalCsv) : allSignals[numSignals - 1];\r\n console.log('sending rec');\r\n this.stepNum += 1;\r\n console.log('send rec ' + this.stepNum);\r\n this.status =\r\n `All Hz Calibration Step: computing the IR of the last recording...`.toString() +\r\n this.generateTemplate().toString();\r\n this.emit('update', {message: this.status});\r\n this.impulseResponses.push(\r\n this.pyServerAPI\r\n .getImpulseResponse({\r\n sampleRate: this.sourceSamplingRate || 96000,\r\n payload,\r\n mls,\r\n P: this.#P,\r\n numPeriods: this.numMLSPerCapture,\r\n })\r\n .then(res => {\r\n if (this.numSuccessfulCaptured < this.numCaptures) {\r\n this.numSuccessfulCaptured += 1;\r\n console.log('num succ capt: ' + this.numSuccessfulCaptured);\r\n this.stepNum += 1;\r\n console.log('got impulse response ' + this.stepNum);\r\n this.incrementStatusBar();\r\n this.status =\r\n `All Hz Calibration: ${this.numSuccessfulCaptured}/${this.numCaptures} IRs computed...`.toString() +\r\n this.generateTemplate().toString();\r\n this.emit('update', {\r\n message: this.status,\r\n });\r\n this.autocorrelations.push(res['autocorrelation']);\r\n return res['ir'];\r\n }\r\n })\r\n .catch(err => {\r\n console.error(err);\r\n })\r\n );\r\n };\r\n\r\n /**\r\n * Passed to the calibration steps function, awaits the desired amount of seconds to capture the desired number\r\n * of MLS periods defined in the constructor.\r\n *\r\n * @example\r\n */\r\n #awaitDesiredMLSLength = async () => {\r\n // seconds per MLS = P / SR\r\n // await N * P / SR\r\n this.stepNum += 1;\r\n console.log('await desired length ' + this.stepNum);\r\n this.status =\r\n `All Hz Calibration: sampling the calibration signal...`.toString() +\r\n `\\niteration ${this.stepNum}` +\r\n this.generateTemplate();\r\n this.emit('update', {\r\n message: this.status,\r\n });\r\n let time_to_wait = 0;\r\n if (this.mode === 'unfiltered') {\r\n time_to_wait = (this.#mls.length / this.sourceSamplingRate) * this.numMLSPerCapture;\r\n } else if (this.mode === 'filtered') {\r\n time_to_wait =\r\n (this.#currentConvolution.length / this.sourceSamplingRate) * this.numMLSPerCapture;\r\n } else {\r\n throw new Error('Mode broke in awaitDesiredMLSLength');\r\n }\r\n\r\n await (0,_utils__WEBPACK_IMPORTED_MODULE_1__.sleep)(time_to_wait * 1.1);\r\n };\r\n\r\n /** .\r\n * .\r\n * .\r\n * Passed to the calibration steps function, awaits the onset of the signal to ensure a steady state\r\n *\r\n * @example\r\n */\r\n #awaitSignalOnset = async () => {\r\n this.stepNum += 1;\r\n console.log('await signal onset ' + this.stepNum);\r\n this.status =\r\n `All Hz Calibration: waiting for the signal to stabilize...`.toString() +\r\n this.generateTemplate();\r\n this.emit('update', {\r\n message: this.status,\r\n });\r\n let number_of_bursts_to_skip = this.num_mls_to_skip;\r\n let time_to_sleep = 0;\r\n if (this.mode === 'unfiltered') {\r\n time_to_sleep = this.#mls.length / this.sourceSamplingRate;\r\n } else if (this.mode === 'filtered') {\r\n time_to_sleep = this.#currentConvolution.length / this.sourceSamplingRate;\r\n } else {\r\n throw new Error('Mode broke in awaitSignalOnset');\r\n }\r\n await (0,_utils__WEBPACK_IMPORTED_MODULE_1__.sleep)(time_to_sleep);\r\n };\r\n\r\n /**\r\n * Called immediately after a recording is captured. Used to process the resulting signal\r\n * whether by sending the result to a server or by computing a result locally.\r\n *\r\n * @example\r\n */\r\n #afterMLSRecord = () => {\r\n console.log('after record');\r\n this.sendRecordingToServerForProcessing();\r\n };\r\n\r\n #afterMLSwIIRRecord = () => {\r\n if (this.numSuccessfulCaptured < 1) {\r\n this.numSuccessfulCaptured += 1;\r\n this.stepNum += 1;\r\n this.incrementStatusBar();\r\n console.log('after mls w iir record for some reason add numSucc capt ' + this.stepNum);\r\n this.status =\r\n `All Hz Calibration: ${this.numSuccessfulCaptured} recording of convolved MLS captured`.toString() +\r\n this.generateTemplate().toString();\r\n this.emit('update', {\r\n message: this.status,\r\n });\r\n }\r\n };\r\n\r\n /** .\r\n * .\r\n * .\r\n * Created an S Curver Buffer to taper the signal onset\r\n *\r\n * @param {*} length\r\n * @param {*} phase\r\n * @returns\r\n * @example\r\n */\r\n static createSCurveBuffer = (length, phase) => {\r\n const curve = new Float32Array(length);\r\n let i;\r\n for (i = 0; i < length; i += 1) {\r\n // scale the curve to be between 0-1\r\n curve[i] = Math.sin((Math.PI * i) / length - phase) / 2 + 0.5;\r\n }\r\n return curve;\r\n };\r\n\r\n static createInverseSCurveBuffer = (length, phase) => {\r\n const curve = new Float32Array(length);\r\n let i;\r\n let j = length - 1;\r\n for (i = 0; i < length; i += 1) {\r\n // scale the curve to be between 0-1\r\n curve[i] = Math.sin((Math.PI * j) / length - phase) / 2 + 0.5;\r\n j -= 1;\r\n }\r\n return curve;\r\n };\r\n\r\n /**\r\n * Construct a Calibration Node with the calibration parameters.\r\n *\r\n * @param dataBuffer\r\n * @private\r\n * @example\r\n */\r\n #createCalibrationNodeFromBuffer = dataBuffer => {\r\n console.log('databuffer');\r\n console.log(dataBuffer);\r\n if (!this.sourceAudioContext) {\r\n this.makeNewSourceAudioContext();\r\n }\r\n\r\n const buffer = this.sourceAudioContext.createBuffer(\r\n 1, // number of channels\r\n dataBuffer.length,\r\n this.sourceAudioContext.sampleRate // sample rate\r\n );\r\n\r\n const data = buffer.getChannelData(0); // get data\r\n // fill the buffer with our data\r\n try {\r\n for (let i = 0; i < dataBuffer.length; i += 1) {\r\n data[i] = dataBuffer[i] * 0.1;\r\n }\r\n } catch (error) {\r\n console.error(error);\r\n }\r\n\r\n this.sourceNode = this.sourceAudioContext.createBufferSource();\r\n\r\n this.sourceNode.buffer = buffer;\r\n this.sourceNode.loop = true;\r\n this.sourceNode.connect(this.sourceAudioContext.destination);\r\n\r\n this.addCalibrationNode(this.sourceNode);\r\n };\r\n\r\n /**\r\n * Given a data buffer, creates the required calibration node\r\n *\r\n * @param {*} dataBufferArray\r\n * @example\r\n */\r\n #setCalibrationNodesFromBuffer = (dataBufferArray = [this.#mlsBufferView]) => {\r\n if (dataBufferArray.length === 1) {\r\n this.#createCalibrationNodeFromBuffer(dataBufferArray[0]);\r\n } else {\r\n throw new Error('The length of the data buffer array must be 1');\r\n }\r\n };\r\n\r\n /**\r\n * function to put MLS filtered IIR data obtained from\r\n * python server into our audio buffer to be played aloud\r\n */\r\n #putInPythonConv = () => {\r\n const audioCtx = this.makeNewSourceAudioContextConvolved();\r\n\r\n //depends on goal\r\n if (this._calibrateSoundCheck != 'system') {\r\n this.#currentConvolution = this.componentConvolution;\r\n const buffer = audioCtx.createBuffer(\r\n 1, // number of channels\r\n this.componentConvolution.length,\r\n audioCtx.sampleRate // sample rate\r\n );\r\n\r\n const data = buffer.getChannelData(0); // get data\r\n // fill the buffer with our data\r\n try {\r\n for (let i = 0; i < this.componentConvolution.length; i += 1) {\r\n data[i] = this.componentConvolution[i];\r\n }\r\n } catch (error) {\r\n console.error(error);\r\n }\r\n\r\n const source = audioCtx.createBufferSource();\r\n\r\n source.buffer = buffer;\r\n source.loop = true;\r\n source.connect(audioCtx.destination);\r\n\r\n this.addCalibrationNodeConvolved(source);\r\n } else {\r\n this.#currentConvolution = this.systemConvolution;\r\n const buffer = audioCtx.createBuffer(\r\n 1, // number of channels\r\n this.systemConvolution.length,\r\n audioCtx.sampleRate // sample rate\r\n );\r\n const data = buffer.getChannelData(0); // get data\r\n // fill the buffer with our data\r\n try {\r\n for (let i = 0; i < this.systemConvolution.length; i += 1) {\r\n data[i] = this.systemConvolution[i];\r\n }\r\n } catch (error) {\r\n console.error(error);\r\n }\r\n\r\n const source = audioCtx.createBufferSource();\r\n\r\n source.buffer = buffer;\r\n source.loop = true;\r\n source.connect(audioCtx.destination);\r\n\r\n this.addCalibrationNodeConvolved(source);\r\n }\r\n };\r\n\r\n /**\r\n * Creates an audio context and plays it for a few seconds.\r\n *\r\n * @private\r\n * @returns - Resolves when the audio is done playing.\r\n * @example\r\n */\r\n #playCalibrationAudio = () => {\r\n if (this.mode === 'unfiltered') {\r\n this.calibrationNodes[0].start(0);\r\n this.#mls = this.calibrationNodes[0].buffer.getChannelData(0);\r\n console.log('play calibration audio ' + this.stepNum);\r\n this.status =\r\n `All Hz Calibration: playing the calibration tone...`.toString() +\r\n this.generateTemplate().toString();\r\n this.emit('update', {message: this.status});\r\n } else if (this.mode === 'filtered') {\r\n this.calibrationNodes[0].start(0);\r\n console.log('play convolved audio ' + this.stepNum);\r\n this.status =\r\n `All Hz Calibration: playing the convolved calibration tone...`.toString() +\r\n this.generateTemplate().toString();\r\n this.emit('update', {message: this.status});\r\n } else {\r\n throw new Error('Mode is incorrect');\r\n }\r\n this.stepNum += 1;\r\n console.log('sink sampling rate');\r\n console.log(this.sinkSamplingRate);\r\n console.log('source sampling rate');\r\n console.log(this.sourceSamplingRate);\r\n };\r\n\r\n /** .\r\n * .\r\n * .\r\n * Stops the audio with tapered offset\r\n *\r\n * @example\r\n */\r\n #stopCalibrationAudio = () => {\r\n this.calibrationNodes[0].stop(0);\r\n this.calibrationNodes = [];\r\n this.sourceNode.disconnect();\r\n this.stepNum += 1;\r\n console.log('stop calibration audio ' + this.stepNum);\r\n this.status =\r\n `All Hz Calibration: stopping the calibration tone...`.toString() +\r\n this.generateTemplate().toString();\r\n this.emit('update', {message: this.status});\r\n };\r\n\r\n playMLSwithIIR = async (stream, iir) => {\r\n this.mode = 'filtered';\r\n console.log('play mls with iir');\r\n this.invertedImpulseResponse = iir;\r\n\r\n await this.calibrationSteps(\r\n stream,\r\n this.#playCalibrationAudio, // play audio func (required)\r\n this.#createCalibrationNodeFromBuffer(this.#currentConvolution), // before play func\r\n this.#awaitSignalOnset, // before record\r\n () => this.numSuccessfulCaptured < 1,\r\n this.#awaitDesiredMLSLength, // during record\r\n this.#afterMLSwIIRRecord, // after record\r\n this.mode\r\n );\r\n };\r\n\r\n /**\r\n * Public method to start the calibration process. Objects intialized from webassembly allocate new memory\r\n * and must be manually freed. This function is responsible for intializing the MlsGenInterface,\r\n * and wrapping the calibration steps with a garbage collection safe gaurd.\r\n *\r\n * @public\r\n * @param stream - The stream of audio from the Listener.\r\n * @example\r\n */\r\n startCalibrationImpulseResponse = async stream => {\r\n let desired_time = this.desired_time_per_mls;\r\n\r\n console.log('MLS sequence should be of length: ' + this.sourceSamplingRate * desired_time);\r\n\r\n length = this.sourceSamplingRate * desired_time;\r\n //get mls here\r\n await this.pyServerAPI\r\n .getMLSWithRetry(length)\r\n .then(res => {\r\n console.log(res);\r\n this.#mlsBufferView = res['mls'];\r\n })\r\n .catch(err => {\r\n // this.emit('InvertedImpulseResponse', {res: false});\r\n console.error(err);\r\n });\r\n await this.calibrationSteps(\r\n stream,\r\n this.#playCalibrationAudio, // play audio func (required)\r\n this.#createCalibrationNodeFromBuffer(this.#mlsBufferView), // before play func\r\n this.#awaitSignalOnset, // before record\r\n () => this.numSuccessfulCaptured < this.numCaptures, // loop while true\r\n this.#awaitDesiredMLSLength, // during record\r\n this.#afterMLSRecord, // after record\r\n this.mode\r\n ),\r\n this.#stopCalibrationAudio();\r\n\r\n // at this stage we've captured all the required signals,\r\n // and have received IRs for each one\r\n // so let's send all the IRs to the server to be converted to a single IIR\r\n await this.sendSystemImpulseResponsesToServerForProcessing();\r\n await this.sendComponentImpulseResponsesToServerForProcessing();\r\n\r\n this.numSuccessfulCaptured = 0;\r\n\r\n let iir_ir_and_plots;\r\n if (this._calibrateSoundCheck != 'none') {\r\n if (this._calibrateSoundCheck != 'system') {\r\n this.#currentConvolution = this.componentConvolution;\r\n } else {\r\n this.#currentConvolution = this.systemConvolution;\r\n }\r\n await this.playMLSwithIIR(stream, this.invertedImpulseResponse);\r\n this.#stopCalibrationAudio();\r\n this.sourceAudioContext.close();\r\n let conv_recs = this.getAllFilteredRecordedSignals();\r\n let recs = this.getAllRecordedSignals();\r\n let unconv_rec = recs[0];\r\n let conv_rec = conv_recs[0];\r\n if (this._calibrateSoundCheck != 'system') {\r\n let knownGain = this.oldComponentIR.Gain;\r\n let knownFreq = this.oldComponentIR.Freq;\r\n let sampleRate = this.sourceSamplingRate || 96000;\r\n let unconv_results = await this.pyServerAPI\r\n .getSubtractedPSDWithRetry(unconv_rec, knownGain, knownFreq, sampleRate)\r\n .then(res => {\r\n this.incrementStatusBar();\r\n this.status =\r\n `All Hz Calibration: done computing the PSD graphs...`.toString() +\r\n this.generateTemplate().toString();\r\n this.emit('update', {message: this.status});\r\n return res;\r\n })\r\n .catch(err => {\r\n console.error(err);\r\n });\r\n\r\n let conv_results = await this.pyServerAPI\r\n .getSubtractedPSDWithRetry(conv_rec, knownGain, knownFreq, sampleRate)\r\n .then(res => {\r\n this.incrementStatusBar();\r\n this.status =\r\n `All Hz Calibration: done computing the PSD graphs...`.toString() +\r\n this.generateTemplate().toString();\r\n this.emit('update', {message: this.status});\r\n return res;\r\n })\r\n .catch(err => {\r\n console.error(err);\r\n });\r\n iir_ir_and_plots = {\r\n systemIIR: this.systemInvertedImpulseResponse,\r\n componentIIR: this.componentInvertedImpulseResponse,\r\n x_unconv: unconv_results['x'],\r\n y_unconv: unconv_results['y'],\r\n x_conv: conv_results['x'],\r\n y_conv: conv_results['y'],\r\n componentIR: this.componentIR,\r\n systemIR: this.systemIR,\r\n };\r\n } else {\r\n let results = await this.pyServerAPI\r\n .getPSDWithRetry({\r\n unconv_rec,\r\n conv_rec,\r\n sampleRate: this.sourceSamplingRate || 96000,\r\n })\r\n .then(res => {\r\n this.incrementStatusBar();\r\n this.status =\r\n `All Hz Calibration: done computing the PSD graphs...`.toString() +\r\n this.generateTemplate().toString();\r\n this.emit('update', {message: this.status});\r\n return res;\r\n })\r\n .catch(err => {\r\n console.error(err);\r\n });\r\n iir_ir_and_plots = {\r\n systemIIR: this.systemInvertedImpulseResponse,\r\n componentIIR: this.componentInvertedImpulseResponse,\r\n x_unconv: results['x_unconv'],\r\n y_unconv: results['y_unconv'],\r\n x_conv: results['x_conv'],\r\n y_conv: results['y_conv'],\r\n componentIR: this.componentIR,\r\n systemIR: this.systemIR,\r\n };\r\n }\r\n\r\n if (this.#download) {\r\n this.downloadSingleUnfilteredRecording();\r\n this.downloadSingleFilteredRecording();\r\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.#mls, 'MLS.csv');\r\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.componentConvolution, 'python_component_convolution_mls_iir.csv');\r\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.systemConvolution, 'python_system_convolution_mls_iir.csv');\r\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.componentInvertedImpulseResponse, 'componentIIR.csv');\r\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.systemInvertedImpulseResponse, 'systemIIR.csv');\r\n for (let i = 0; i < this.autocorrelations.length; i++){\r\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.autocorrelations[i],`autocorrelation_${i}`);\r\n }\r\n const computedIRagain = await Promise.all(this.impulseResponses).then(res => {\r\n for (let i = 0; i < res.length; i++) {\r\n if (res[i] != undefined) {\r\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(res[i], `IR_${i}`);\r\n }\r\n }\r\n });\r\n }\r\n } else {\r\n iir_ir_and_plots = {\r\n systemIIR: this.systemInvertedImpulseResponse,\r\n componentIIR: this.componentInvertedImpulseResponse,\r\n x_unconv: [],\r\n y_unconv: [],\r\n x_conv: [],\r\n y_conv: [],\r\n componentIR: this.componentIR,\r\n systemIR: this.systemIR,\r\n };\r\n if (this.#download) {\r\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.#mls, 'MLS.csv');\r\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.componentConvolution, 'python_component_convolution_mls_iir.csv');\r\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.systemConvolution, 'python_system_convolution_mls_iir.csv');\r\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.componentInvertedImpulseResponse, 'componentIIR.csv');\r\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.systemInvertedImpulseResponse, 'systemIIR.csv');\r\n for (let i = 0; i < this.autocorrelations.length; i++){\r\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.autocorrelations[i],`autocorrelation_${i}`);\r\n }\r\n const computedIRagain = await Promise.all(this.impulseResponses).then(res => {\r\n for (let i = 0; i < res.length; i++) {\r\n if (res[i] != undefined) {\r\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(res[i], `IR_${i}`);\r\n }\r\n }\r\n });\r\n }\r\n }\r\n\r\n this.percent_complete = 100;\r\n\r\n this.status = `All Hz Calibration: Finished`.toString() + this.generateTemplate().toString();\r\n this.emit('update', {message: this.status});\r\n\r\n //here after calibration we have the component calibration (either loudspeaker or microphone) in the same form as the componentIR\r\n //that was used to calibrate\r\n\r\n return iir_ir_and_plots;\r\n };\r\n\r\n //////////////////////volume\r\n\r\n handleIncomingData = data => {\r\n console.log('Received data: ', data);\r\n if (data.type === 'soundGainDBSPL') {\r\n this.soundGainDBSPL = data.value;\r\n } else {\r\n throw new Error(`Unknown data type: ${data.type}`);\r\n }\r\n };\r\n createSCurveBuffer = (onSetBool = true) => {\r\n const curve = new Float32Array(this.TAPER_SECS * this.sourceSamplingRate + 1);\r\n const frequency = 1 / (4 * this.TAPER_SECS);\r\n let j = 0;\r\n for (let i = 0; i < this.TAPER_SECS * this.sourceSamplingRate + 1; i += 1) {\r\n const phase = 2 * Math.PI * frequency * j;\r\n const onsetTaper = Math.pow(Math.sin(phase), 2);\r\n const offsetTaper = Math.pow(Math.cos(phase), 2);\r\n curve[i] = onSetBool ? onsetTaper : offsetTaper;\r\n j += 1 / this.sourceSamplingRate;\r\n }\r\n return curve;\r\n };\r\n\r\n #getTruncatedSignal = (left = 3.5, right = 4.5) => {\r\n const start = Math.floor(left * this.sourceSamplingRate);\r\n const end = Math.floor(right * this.sourceSamplingRate);\r\n const result = Array.from(this.getLastRecordedSignal().slice(start, end));\r\n\r\n /**\r\n * function to check that capture was properly made\r\n * @param {*} list\r\n */\r\n const checkResult = list => {\r\n const setItem = new Set(list);\r\n if (setItem.size === 1 && setItem.has(0)) {\r\n console.warn(\r\n 'The last capture failed, all recorded signal is zero',\r\n this.getAllRecordedSignals()\r\n );\r\n }\r\n if (setItem.size === 0) {\r\n console.warn('The last capture failed, no recorded signal');\r\n }\r\n };\r\n checkResult(result);\r\n return result;\r\n };\r\n\r\n /** \r\n * \r\n * \r\n Construct a calibration Node with the calibration parameters and given gain value\r\n * @param {*} gainValue\r\n * */\r\n #createCalibrationToneWithGainValue = gainValue => {\r\n const audioContext = this.makeNewSourceAudioContext();\r\n const oscilator = audioContext.createOscillator();\r\n const gainNode = audioContext.createGain();\r\n const taperGainNode = audioContext.createGain();\r\n const offsetGainNode = audioContext.createGain();\r\n const totalDuration = this.#CALIBRATION_TONE_DURATION * 1.2;\r\n\r\n oscilator.frequency.value = this.#CALIBRATION_TONE_FREQUENCY;\r\n oscilator.type = this.#CALIBRATION_TONE_TYPE;\r\n gainNode.gain.value = gainValue;\r\n\r\n oscilator.connect(gainNode);\r\n gainNode.connect(taperGainNode);\r\n const onsetCurve = this.createSCurveBuffer();\r\n taperGainNode.gain.setValueCurveAtTime(onsetCurve, 0, this.TAPER_SECS);\r\n taperGainNode.connect(offsetGainNode);\r\n const offsetCurve = this.createSCurveBuffer(false);\r\n offsetGainNode.gain.setValueCurveAtTime(\r\n offsetCurve,\r\n totalDuration - this.TAPER_SECS,\r\n this.TAPER_SECS\r\n );\r\n offsetGainNode.connect(audioContext.destination);\r\n\r\n this.addCalibrationNode(oscilator);\r\n };\r\n\r\n /**\r\n * Construct a Calibration Node with the calibration parameters.\r\n *\r\n * @private\r\n * @example\r\n */\r\n #createCalibrationNode = () => {\r\n const audioContext = this.makeNewSourceAudioContext();\r\n const oscilator = audioContext.createOscillator();\r\n const gainNode = audioContext.createGain();\r\n\r\n oscilator.frequency.value = this.#CALIBRATION_TONE_FREQUENCY;\r\n oscilator.type = this.#CALIBRATION_TONE_TYPE;\r\n gainNode.gain.value = 0.04;\r\n\r\n oscilator.connect(gainNode);\r\n gainNode.connect(audioContext.destination);\r\n\r\n this.addCalibrationNode(oscilator);\r\n };\r\n\r\n #playCalibrationAudioVolume = async () => {\r\n const totalDuration = this.#CALIBRATION_TONE_DURATION * 1.2;\r\n\r\n this.calibrationNodes[0].start(0);\r\n this.calibrationNodes[0].stop(totalDuration);\r\n console.log(`Playing a buffer of ${this.#CALIBRATION_TONE_DURATION} seconds of audio`);\r\n console.log(`Waiting a total of ${totalDuration} seconds`);\r\n await (0,_utils__WEBPACK_IMPORTED_MODULE_1__.sleep)(totalDuration);\r\n };\r\n\r\n #sendToServerForProcessing = lCalib => {\r\n console.log('Sending data to server');\r\n this.pyServerAPI\r\n .getVolumeCalibration({\r\n sampleRate: this.sourceSamplingRate,\r\n payload: this.#getTruncatedSignal(),\r\n lCalib: lCalib,\r\n })\r\n .then(res => {\r\n if (this.outDBSPL === null) {\r\n this.incrementStatusBar();\r\n this.outDBSPL = res['outDbSPL'];\r\n this.outDBSPL1000 = res['outDbSPL1000'];\r\n this.THD = res['thd'];\r\n }\r\n })\r\n .catch(err => {\r\n console.warn(err);\r\n });\r\n };\r\n\r\n startCalibrationVolume = async (stream, gainValues, lCalib, componentGainDBSPL) => {\r\n const trialIterations = gainValues.length;\r\n this.status_denominator += trialIterations;\r\n const thdValues = [];\r\n const inDBValues = [];\r\n let inDB = 0;\r\n const outDBSPLValues = [];\r\n const outDBSPL1000Values = [];\r\n\r\n // do one calibration that will be discarded\r\n const soundLevelToDiscard = -60;\r\n const gainToDiscard = Math.pow(10, soundLevelToDiscard / 20);\r\n this.status =\r\n `1000 Hz Calibration: Sound Level ${soundLevelToDiscard} dB`.toString() +\r\n this.generateTemplate().toString();\r\n //this.emit('update', {message: `1000 Hz Calibration: Sound Level ${soundLevelToDiscard} dB`});\r\n this.emit('update', {message: this.status});\r\n\r\n do {\r\n // eslint-disable-next-line no-await-in-loop\r\n await this.volumeCalibrationSteps(\r\n stream,\r\n this.#playCalibrationAudioVolume,\r\n this.#createCalibrationToneWithGainValue,\r\n this.#sendToServerForProcessing,\r\n gainToDiscard,\r\n lCalib //todo make this a class parameter\r\n );\r\n } while (this.outDBSPL === null);\r\n //reset the values\r\n //this.incrementStatusBar();\r\n\r\n this.outDBSPL = null;\r\n this.outDBSPL = null;\r\n this.outDBSPL1000 = null;\r\n this.THD = null;\r\n\r\n // run the calibration at different gain values provided by the user\r\n for (let i = 0; i < trialIterations; i++) {\r\n //convert gain to DB and add to inDB\r\n inDB = Math.log10(gainValues[i]) * 20;\r\n // precision to 1 decimal place\r\n inDB = Math.round(inDB * 10) / 10;\r\n inDBValues.push(inDB);\r\n console.log('next update');\r\n this.status =\r\n `1000 Hz Calibration: Sound Level ${inDB} dB`.toString() +\r\n this.generateTemplate().toString();\r\n this.emit('update', {message: this.status});\r\n do {\r\n // eslint-disable-next-line no-await-in-loop\r\n await this.volumeCalibrationSteps(\r\n stream,\r\n this.#playCalibrationAudioVolume,\r\n this.#createCalibrationToneWithGainValue,\r\n this.#sendToServerForProcessing,\r\n gainValues[i],\r\n lCalib //todo make this a class parameter\r\n );\r\n } while (this.outDBSPL === null);\r\n outDBSPL1000Values.push(this.outDBSPL1000);\r\n thdValues.push(this.THD);\r\n outDBSPLValues.push(this.outDBSPL);\r\n\r\n this.outDBSPL = null;\r\n this.outDBSPL1000 = null;\r\n this.THD = null;\r\n }\r\n\r\n // get the volume calibration parameters from the server\r\n const parameters = await this.pyServerAPI\r\n .getVolumeCalibrationParameters({\r\n inDBValues: inDBValues,\r\n outDBSPLValues: outDBSPL1000Values,\r\n lCalib: lCalib,\r\n componentGainDBSPL,\r\n })\r\n .then(res => {\r\n this.incrementStatusBar();\r\n return res;\r\n });\r\n const result = {\r\n parameters: parameters,\r\n inDBValues: inDBValues,\r\n outDBSPLValues: outDBSPLValues,\r\n outDBSPL1000Values: outDBSPL1000Values,\r\n thdValues: thdValues,\r\n };\r\n\r\n return result;\r\n };\r\n\r\n // function to write frq and gain to firebase database given speakerID\r\n writeFrqGain = async (speakerID, frq, gain) => {\r\n // freq and gain are too large to take samples 1 in every 100 samples\r\n\r\n const sampledFrq = [];\r\n const sampledGain = [];\r\n for (let i = 0; i < frq.length; i += 100) {\r\n sampledFrq.push(frq[i]);\r\n sampledGain.push(gain[i]);\r\n }\r\n\r\n const data = {Freq: sampledFrq, Gain: sampledGain};\r\n\r\n await (0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.set)((0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.ref)(_config_firebase__WEBPACK_IMPORTED_MODULE_2__[\"default\"], `Microphone/${speakerID}/linear`), data);\r\n };\r\n\r\n // Function to Read frq and gain from firebase database given speakerID\r\n // returns an array of frq and gain if speakerID exists, returns null otherwise\r\n\r\n readFrqGain = async speakerID => {\r\n const dbRef = (0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.ref)(_config_firebase__WEBPACK_IMPORTED_MODULE_2__[\"default\"]);\r\n const snapshot = await (0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.get)((0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.child)(dbRef, `Microphone/${speakerID}/linear`));\r\n if (snapshot.exists()) {\r\n return snapshot.val();\r\n }\r\n return null;\r\n };\r\n\r\n readGainat1000Hz = async speakerID => {\r\n const dbRef = (0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.ref)(_config_firebase__WEBPACK_IMPORTED_MODULE_2__[\"default\"]);\r\n const snapshot = await (0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.get)((0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.child)(dbRef, `Microphone/${speakerID}/Gain1000`));\r\n if (snapshot.exists()) {\r\n return snapshot.val();\r\n }\r\n return null;\r\n };\r\n\r\n writeGainat1000Hz = async (speakerID, gain) => {\r\n const data = {Gain: gain};\r\n await (0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.set)((0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.ref)(_config_firebase__WEBPACK_IMPORTED_MODULE_2__[\"default\"], `Microphone/${speakerID}/Gain1000`), gain);\r\n };\r\n\r\n convertToDB = gain => {\r\n return Math.log10(gain) * 20;\r\n };\r\n\r\n // Function to perform linear interpolation between two points\r\n interpolate(x, x0, y0, x1, y1) {\r\n return y0 + ((x - x0) * (y1 - y0)) / (x1 - x0);\r\n }\r\n\r\n findGainatFrequency = (frequencies, gains, targetFrequency) => {\r\n // Find the index of the first frequency in the array greater than the target frequency\r\n let index = 0;\r\n while (index < frequencies.length && frequencies[index] < targetFrequency) {\r\n index++;\r\n }\r\n\r\n // Handle cases when the target frequency is outside the range of the given data\r\n if (index === 0) {\r\n return gains[0];\r\n } else if (index === frequencies.length) {\r\n return gains[gains.length - 1];\r\n } else {\r\n // Interpolate the gain based on the surrounding frequencies\r\n const x0 = frequencies[index - 1];\r\n const y0 = gains[index - 1];\r\n const x1 = frequencies[index];\r\n const y1 = gains[index];\r\n return this.interpolate(targetFrequency, x0, y0, x1, y1);\r\n }\r\n };\r\n\r\n writeIsSmartPhone = async (speakerID, isSmartPhone) => {\r\n const data = {isSmartPhone: isSmartPhone};\r\n await (0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.set)((0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.ref)(_config_firebase__WEBPACK_IMPORTED_MODULE_2__[\"default\"], `Microphone/${speakerID}/isSmartPhone`), isSmartPhone);\r\n };\r\n\r\n // Example of how to use the writeFrqGain and readFrqGain functions\r\n // writeFrqGain('speaker1', [1, 2, 3], [4, 5, 6]);\r\n // Speaker1 is the speakerID you want to write to in the database\r\n // readFrqGain('MiniDSPUMIK_1').then(data => console.log(data));\r\n // MiniDSPUMIK_1 is the speakerID with some Data in the database\r\n //adding gainDBSPL\r\n startCalibration = async (\r\n stream,\r\n gainValues,\r\n lCalib = 104.92978421490648,\r\n componentIR = null,\r\n microphoneName = 'MiniDSP-UMIK1-711-4754-vertical',\r\n _calibrateSoundCheck = 'goal', //GOAL PASSed in by default\r\n isSmartPhone = false,\r\n _calibrateSoundBurstRepeats = 4,\r\n _calibrateSoundBurstSec = 1,\r\n _calibrateSoundBurstsWarmup = 1,\r\n _calibrateSoundHz = 48000,\r\n micManufacturer = '',\r\n micSerialNumber = '',\r\n micModelNumber = '',\r\n micModelName = ''\r\n ) => {\r\n this.numMLSPerCapture = _calibrateSoundBurstRepeats;\r\n this.desired_time_per_mls = _calibrateSoundBurstSec;\r\n this.num_mls_to_skip = _calibrateSoundBurstsWarmup;\r\n this.desired_sampling_rate = _calibrateSoundHz;\r\n\r\n //feed calibration goal here\r\n this._calibrateSoundCheck = _calibrateSoundCheck;\r\n //check if a componentIR was given to the system, if it isn't check for the microphone. using dummy data here bc we need to\r\n //check the db based on the microphone currently connected\r\n\r\n //new lCalib found at top of calibration files *1000hz, make sure to correct\r\n //based on zeroing of 1000hz, search for \"*1000Hz\"\r\n if (componentIR == null) {\r\n //mode 'ir'\r\n //global variable this.componentIR must be set\r\n this.componentIR = await this.readFrqGain(microphoneName).then(data => {\r\n return data;\r\n });\r\n\r\n lCalib = await this.readGainat1000Hz(microphoneName);\r\n // this.componentGainDBSPL = this.convertToDB(lCalib);\r\n this.componentGainDBSPL = lCalib;\r\n //TODO: if this call to database is unknown, cannot perform experiment => return false\r\n if (this.componentIR == null) {\r\n this.status =\r\n `Microphone ${microphoneName} is not in the database. Please add it to the database.`.toString();\r\n this.emit('update', {message: this.status});\r\n return false;\r\n }\r\n } else {\r\n this.componentIR = componentIR;\r\n lCalib = this.findGainatFrequency(this.componentIR.Freq, this.componentIR.Gain, 1000);\r\n // this.componentGainDBSPL = this.convertToDB(lCalib);\r\n this.componentGainDBSPL = lCalib;\r\n await this.writeGainat1000Hz(microphoneName, lCalib);\r\n await this.writeIsSmartPhone(microphoneName, isSmartPhone);\r\n }\r\n\r\n this.oldComponentIR = this.componentIR;\r\n\r\n let volumeResults = await this.startCalibrationVolume(\r\n stream,\r\n gainValues,\r\n lCalib,\r\n this.componentGainDBSPL\r\n );\r\n\r\n let impulseResponseResults = await this.startCalibrationImpulseResponse(stream);\r\n\r\n if (componentIR != null) {\r\n //insert Freq and Gain from this.componentIR into db\r\n await this.writeFrqGain(\r\n microphoneName,\r\n impulseResponseResults.componentIR.Freq,\r\n impulseResponseResults.componentIR.Gain\r\n );\r\n }\r\n\r\n const total_results = {...volumeResults, ...impulseResponseResults};\r\n console.log('total');\r\n console.log(total_results);\r\n return total_results;\r\n };\r\n}\r\n\r\n/* harmony default export */ __webpack_exports__[\"default\"] = (Combination);\r\n\n\n//# sourceURL=webpack://speakerCalibrator/./src/tasks/combination/combination.js?");
843
+ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _audioCalibrator__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../audioCalibrator */ \"./src/tasks/audioCalibrator.js\");\n/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../utils */ \"./src/utils.js\");\n/* harmony import */ var _config_firebase__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../config/firebase */ \"./src/config/firebase.js\");\n/* harmony import */ var firebase_database__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! firebase/database */ \"./node_modules/firebase/database/dist/esm/index.esm.js\");\n\r\n\r\n\r\n\r\n\r\n\r\n/**\r\n *\r\n */\r\nclass Combination extends _audioCalibrator__WEBPACK_IMPORTED_MODULE_0__[\"default\"] {\r\n /**\r\n * Default constructor. Creates an instance with any number of paramters passed or the default parameters defined here.\r\n *\r\n * @param {Object<boolean, number, number, number>} calibratorParams - paramter object\r\n * @param {boolean} [calibratorParams.download = false] - boolean flag to download captures\r\n * @param {number} [calibratorParams.mlsOrder = 18] - order of the MLS to be generated\r\n * @param {number} [calibratorParams.numCaptures = 5] - number of captures to perform\r\n * @param {number} [calibratorParams.numMLSPerCapture = 4] - number of bursts of MLS per capture\r\n */\r\n constructor({\r\n download = false,\r\n mlsOrder = 18,\r\n numCaptures = 3,\r\n numMLSPerCapture = 4,\r\n lowHz = 20,\r\n highHz = 10000,\r\n }) {\r\n super(numCaptures, numMLSPerCapture);\r\n this.#mlsOrder = parseInt(mlsOrder, 10);\r\n this.#P = 2 ** mlsOrder - 1;\r\n this.#download = download;\r\n this.#mls = [];\r\n this.#lowHz = lowHz;\r\n this.#highHz = highHz;\r\n }\r\n\r\n /** @private */\r\n stepNum = 0;\r\n\r\n /** @private */\r\n totalSteps = 25;\r\n\r\n /** @private */\r\n #download;\r\n\r\n /** @private */\r\n #mlsGenInterface;\r\n\r\n /** @private */\r\n #mlsBufferView;\r\n\r\n /** @private */\r\n componentInvertedImpulseResponse = null;\r\n\r\n /** @private */\r\n systemInvertedImpulseResponse = null;\r\n\r\n //averaged and subtracted ir returned from calibration used to calculated iir\r\n /** @private */\r\n ir = null;\r\n\r\n /** @private */\r\n impulseResponses = [];\r\n\r\n /** @private */\r\n #mlsOrder;\r\n\r\n /** @private */\r\n #lowHz;\r\n\r\n /** @private */\r\n #highHz;\r\n\r\n /** @private */\r\n #mls;\r\n\r\n /** @private */\r\n #P;\r\n\r\n /** @private */\r\n #audioContext;\r\n\r\n /** @private */\r\n TAPER_SECS = 5;\r\n\r\n /** @private */\r\n offsetGainNode;\r\n\r\n /** @private */\r\n componentConvolution;\r\n\r\n /** @private */\r\n systemConvolution;\r\n\r\n ////////////////////////volume\r\n /** @private */\r\n #CALIBRATION_TONE_FREQUENCY = 1000; // Hz\r\n\r\n /** @private */\r\n #CALIBRATION_TONE_TYPE = 'sine';\r\n\r\n /** @private */\r\n #CALIBRATION_TONE_DURATION = 5; // seconds\r\n\r\n /** @private */\r\n outDBSPL = null;\r\n THD = null;\r\n outDBSPL1000 = null;\r\n\r\n /** @private */\r\n TAPER_SECS = 0.01; // seconds\r\n\r\n /** @private */\r\n status_denominator = 8;\r\n\r\n /** @private */\r\n status_numerator = 0;\r\n\r\n /** @private */\r\n percent_complete = 0;\r\n\r\n /** @private */\r\n status = ``;\r\n\r\n /**@private */\r\n status_literal = `<div style=\"display: flex; justify-content: center;\"><div style=\"width: 200px; height: 20px; border: 2px solid #000; border-radius: 10px;\"><div style=\"width: ${this.percent_complete}%; height: 100%; background-color: #00aaff; border-radius: 8px;\"></div></div></div>`;\r\n\r\n /**@private */\r\n componentIR = null;\r\n\r\n /**@private */\r\n oldComponentIR = null;\r\n\r\n /**@private */\r\n systemIR = null;\r\n\r\n /**@private */\r\n _calibrateSoundCheck = '';\r\n\r\n deviceType = null;\r\n\r\n deviceName = null;\r\n\r\n deviceInfo = null;\r\n\r\n desired_time_per_mls = 0;\r\n\r\n num_mls_to_skip = 0;\r\n\r\n desired_sampling_rate = 0;\r\n\r\n #currentConvolution = [];\r\n\r\n mode = 'unfiltered';\r\n\r\n sourceNode;\r\n\r\n autocorrelations = [];\r\n\r\n iirLength = 0;\r\n\r\n /**generate string template that gets reevaluated as variable increases */\r\n generateTemplate = () => {\r\n if (this.percent_complete > 100) {\r\n this.percent_complete = 100;\r\n }\r\n const template = `<div style=\"display: flex; justify-content: center;\"><div style=\"width: 800px; height: 20px; border: 2px solid #000; border-radius: 10px;\"><div style=\"width: ${this.percent_complete}%; height: 100%; background-color: #00aaff; border-radius: 8px;\"></div></div></div>`;\r\n return template;\r\n };\r\n\r\n /** increment numerator and percent for status bar */\r\n incrementStatusBar = () => {\r\n this.status_numerator += 1;\r\n this.percent_complete = (this.status_numerator / this.status_denominator) * 100;\r\n };\r\n\r\n setDeviceType = deviceType => {\r\n this.deviceType = deviceType;\r\n };\r\n\r\n setDeviceName = deviceName => {\r\n this.deviceName = deviceName;\r\n };\r\n\r\n setDeviceInfo = deviceInfo => {\r\n this.deviceInfo = deviceInfo;\r\n };\r\n\r\n /** .\r\n * .\r\n * .\r\n * Sends all the computed impulse responses to the backend server for processing\r\n *\r\n * @returns sets the resulting inverted impulse response to the class property\r\n * @example\r\n */\r\n sendSystemImpulseResponsesToServerForProcessing = async () => {\r\n const computedIRs = await Promise.all(this.impulseResponses);\r\n const filteredComputedIRs = computedIRs.filter(element => {\r\n return element != undefined;\r\n });\r\n const mls = this.#mls;\r\n const lowHz = this.#lowHz;\r\n const highHz = this.#highHz;\r\n const iirLength = this.iirLength;\r\n this.stepNum += 1;\r\n console.log('send impulse responses to server: ' + this.stepNum);\r\n this.status =\r\n `All Hz Calibration: computing the IIR...`.toString() + this.generateTemplate().toString();\r\n this.emit('update', {message: this.status});\r\n return this.pyServerAPI\r\n .getSystemInverseImpulseResponseWithRetry({\r\n payload: filteredComputedIRs.slice(0, this.numCaptures),\r\n mls,\r\n lowHz,\r\n highHz,\r\n iirLength,\r\n sampleRate: this.sourceSamplingRate || 96000,\r\n })\r\n .then(res => {\r\n console.log(res);\r\n this.stepNum += 1;\r\n console.log('got impulse response ' + this.stepNum);\r\n this.incrementStatusBar();\r\n this.status =\r\n `All Hz Calibration: done computing the IIR...`.toString() +\r\n this.generateTemplate().toString();\r\n this.emit('update', {message: this.status});\r\n this.systemInvertedImpulseResponse = res['iir'];\r\n this.systemIR = res['ir'];\r\n this.systemConvolution = res['convolution'];\r\n })\r\n .catch(err => {\r\n console.error(err);\r\n });\r\n };\r\n\r\n /** .\r\n * .\r\n * .\r\n * Sends all the computed impulse responses to the backend server for processing\r\n *\r\n * @returns sets the resulting inverted impulse response to the class property\r\n * @example\r\n */\r\n sendComponentImpulseResponsesToServerForProcessing = async () => {\r\n const computedIRs = await Promise.all(this.impulseResponses);\r\n const filteredComputedIRs = computedIRs.filter(element => {\r\n return element != undefined;\r\n });\r\n const componentIRGains = this.componentIR['Gain'];\r\n const componentIRFreqs = this.componentIR['Freq'];\r\n const mls = this.#mls;\r\n const lowHz = this.#lowHz;\r\n const iirLength = this.iirLength;\r\n const highHz = this.#highHz;\r\n this.stepNum += 1;\r\n console.log('send impulse responses to server: ' + this.stepNum);\r\n this.status =\r\n `All Hz Calibration: computing the IIR...`.toString() + this.generateTemplate().toString();\r\n this.emit('update', {message: this.status});\r\n return this.pyServerAPI\r\n .getComponentInverseImpulseResponseWithRetry({\r\n payload: filteredComputedIRs.slice(0, this.numCaptures),\r\n mls,\r\n lowHz,\r\n highHz,\r\n iirLength,\r\n componentIRGains,\r\n componentIRFreqs,\r\n sampleRate: this.sourceSamplingRate || 96000,\r\n })\r\n .then(res => {\r\n console.log(res);\r\n this.stepNum += 1;\r\n console.log('got impulse response ' + this.stepNum);\r\n this.incrementStatusBar();\r\n this.status =\r\n `All Hz Calibration: done computing the IIR...`.toString() +\r\n this.generateTemplate().toString();\r\n this.emit('update', {message: this.status});\r\n this.componentInvertedImpulseResponse = res['iir'];\r\n this.componentIR['Gain'] = res['ir'];\r\n this.componentIR['Freq'] = res['frequencies'];\r\n this.componentConvolution = res['convolution'];\r\n })\r\n .catch(err => {\r\n // this.emit('InvertedImpulseResponse', {res: false});\r\n console.error(err);\r\n });\r\n };\r\n\r\n /** .\r\n * .\r\n * .\r\n * Sends the recorded signal, or a given csv string of a signal, to the back end server for processing\r\n *\r\n * @param {<array>String} signalCsv - Optional csv string of a previously recorded signal, if given, this signal will be processed\r\n * @example\r\n */\r\n sendRecordingToServerForProcessing = signalCsv => {\r\n const allSignals = this.getAllRecordedSignals();\r\n const numSignals = allSignals.length;\r\n const mls = this.#mls;\r\n const payload =\r\n signalCsv && signalCsv.length > 0 ? (0,_utils__WEBPACK_IMPORTED_MODULE_1__.csvToArray)(signalCsv) : allSignals[numSignals - 1];\r\n console.log('sending rec');\r\n this.stepNum += 1;\r\n console.log('send rec ' + this.stepNum);\r\n this.status =\r\n `All Hz Calibration Step: computing the IR of the last recording...`.toString() +\r\n this.generateTemplate().toString();\r\n this.emit('update', {message: this.status});\r\n this.impulseResponses.push(\r\n this.pyServerAPI\r\n .getImpulseResponse({\r\n sampleRate: this.sourceSamplingRate || 96000,\r\n payload,\r\n mls,\r\n P: this.#P,\r\n numPeriods: this.numMLSPerCapture,\r\n })\r\n .then(res => {\r\n if (this.numSuccessfulCaptured < this.numCaptures) {\r\n this.numSuccessfulCaptured += 1;\r\n console.log('num succ capt: ' + this.numSuccessfulCaptured);\r\n this.stepNum += 1;\r\n console.log('got impulse response ' + this.stepNum);\r\n this.incrementStatusBar();\r\n this.status =\r\n `All Hz Calibration: ${this.numSuccessfulCaptured}/${this.numCaptures} IRs computed...`.toString() +\r\n this.generateTemplate().toString();\r\n this.emit('update', {\r\n message: this.status,\r\n });\r\n this.autocorrelations.push(res['autocorrelation']);\r\n return res['ir'];\r\n }\r\n })\r\n .catch(err => {\r\n console.error(err);\r\n })\r\n );\r\n };\r\n\r\n /**\r\n * Passed to the calibration steps function, awaits the desired amount of seconds to capture the desired number\r\n * of MLS periods defined in the constructor.\r\n *\r\n * @example\r\n */\r\n #awaitDesiredMLSLength = async () => {\r\n // seconds per MLS = P / SR\r\n // await N * P / SR\r\n this.stepNum += 1;\r\n console.log('await desired length ' + this.stepNum);\r\n this.status =\r\n `All Hz Calibration: sampling the calibration signal...`.toString() +\r\n `\\niteration ${this.stepNum}` +\r\n this.generateTemplate();\r\n this.emit('update', {\r\n message: this.status,\r\n });\r\n let time_to_wait = 0;\r\n if (this.mode === 'unfiltered') {\r\n time_to_wait = (this.#mls.length / this.sourceSamplingRate) * this.numMLSPerCapture;\r\n } else if (this.mode === 'filtered') {\r\n time_to_wait =\r\n (this.#currentConvolution.length / this.sourceSamplingRate) * this.numMLSPerCapture;\r\n } else {\r\n throw new Error('Mode broke in awaitDesiredMLSLength');\r\n }\r\n\r\n await (0,_utils__WEBPACK_IMPORTED_MODULE_1__.sleep)(time_to_wait * 1.1);\r\n };\r\n\r\n /** .\r\n * .\r\n * .\r\n * Passed to the calibration steps function, awaits the onset of the signal to ensure a steady state\r\n *\r\n * @example\r\n */\r\n #awaitSignalOnset = async () => {\r\n this.stepNum += 1;\r\n console.log('await signal onset ' + this.stepNum);\r\n this.status =\r\n `All Hz Calibration: waiting for the signal to stabilize...`.toString() +\r\n this.generateTemplate();\r\n this.emit('update', {\r\n message: this.status,\r\n });\r\n let number_of_bursts_to_skip = this.num_mls_to_skip;\r\n let time_to_sleep = 0;\r\n if (this.mode === 'unfiltered') {\r\n time_to_sleep = this.#mls.length / this.sourceSamplingRate;\r\n } else if (this.mode === 'filtered') {\r\n time_to_sleep = this.#currentConvolution.length / this.sourceSamplingRate;\r\n } else {\r\n throw new Error('Mode broke in awaitSignalOnset');\r\n }\r\n await (0,_utils__WEBPACK_IMPORTED_MODULE_1__.sleep)(time_to_sleep);\r\n };\r\n\r\n /**\r\n * Called immediately after a recording is captured. Used to process the resulting signal\r\n * whether by sending the result to a server or by computing a result locally.\r\n *\r\n * @example\r\n */\r\n #afterMLSRecord = () => {\r\n console.log('after record');\r\n this.sendRecordingToServerForProcessing();\r\n };\r\n\r\n #afterMLSwIIRRecord = () => {\r\n if (this.numSuccessfulCaptured < 1) {\r\n this.numSuccessfulCaptured += 1;\r\n this.stepNum += 1;\r\n this.incrementStatusBar();\r\n console.log('after mls w iir record for some reason add numSucc capt ' + this.stepNum);\r\n this.status =\r\n `All Hz Calibration: ${this.numSuccessfulCaptured} recording of convolved MLS captured`.toString() +\r\n this.generateTemplate().toString();\r\n this.emit('update', {\r\n message: this.status,\r\n });\r\n }\r\n };\r\n\r\n /** .\r\n * .\r\n * .\r\n * Created an S Curver Buffer to taper the signal onset\r\n *\r\n * @param {*} length\r\n * @param {*} phase\r\n * @returns\r\n * @example\r\n */\r\n static createSCurveBuffer = (length, phase) => {\r\n const curve = new Float32Array(length);\r\n let i;\r\n for (i = 0; i < length; i += 1) {\r\n // scale the curve to be between 0-1\r\n curve[i] = Math.sin((Math.PI * i) / length - phase) / 2 + 0.5;\r\n }\r\n return curve;\r\n };\r\n\r\n static createInverseSCurveBuffer = (length, phase) => {\r\n const curve = new Float32Array(length);\r\n let i;\r\n let j = length - 1;\r\n for (i = 0; i < length; i += 1) {\r\n // scale the curve to be between 0-1\r\n curve[i] = Math.sin((Math.PI * j) / length - phase) / 2 + 0.5;\r\n j -= 1;\r\n }\r\n return curve;\r\n };\r\n\r\n /**\r\n * Construct a Calibration Node with the calibration parameters.\r\n *\r\n * @param dataBuffer\r\n * @private\r\n * @example\r\n */\r\n #createCalibrationNodeFromBuffer = dataBuffer => {\r\n console.log('databuffer');\r\n console.log(dataBuffer);\r\n if (!this.sourceAudioContext) {\r\n this.makeNewSourceAudioContext();\r\n }\r\n\r\n const buffer = this.sourceAudioContext.createBuffer(\r\n 1, // number of channels\r\n dataBuffer.length,\r\n this.sourceAudioContext.sampleRate // sample rate\r\n );\r\n\r\n const data = buffer.getChannelData(0); // get data\r\n // fill the buffer with our data\r\n try {\r\n for (let i = 0; i < dataBuffer.length; i += 1) {\r\n data[i] = dataBuffer[i] * 0.1;\r\n }\r\n } catch (error) {\r\n console.error(error);\r\n }\r\n\r\n this.sourceNode = this.sourceAudioContext.createBufferSource();\r\n\r\n this.sourceNode.buffer = buffer;\r\n this.sourceNode.loop = true;\r\n this.sourceNode.connect(this.sourceAudioContext.destination);\r\n\r\n this.addCalibrationNode(this.sourceNode);\r\n };\r\n\r\n /**\r\n * Given a data buffer, creates the required calibration node\r\n *\r\n * @param {*} dataBufferArray\r\n * @example\r\n */\r\n #setCalibrationNodesFromBuffer = (dataBufferArray = [this.#mlsBufferView]) => {\r\n if (dataBufferArray.length === 1) {\r\n this.#createCalibrationNodeFromBuffer(dataBufferArray[0]);\r\n } else {\r\n throw new Error('The length of the data buffer array must be 1');\r\n }\r\n };\r\n\r\n /**\r\n * function to put MLS filtered IIR data obtained from\r\n * python server into our audio buffer to be played aloud\r\n */\r\n #putInPythonConv = () => {\r\n const audioCtx = this.makeNewSourceAudioContextConvolved();\r\n\r\n //depends on goal\r\n if (this._calibrateSoundCheck != 'system') {\r\n this.#currentConvolution = this.componentConvolution;\r\n const buffer = audioCtx.createBuffer(\r\n 1, // number of channels\r\n this.componentConvolution.length,\r\n audioCtx.sampleRate // sample rate\r\n );\r\n\r\n const data = buffer.getChannelData(0); // get data\r\n // fill the buffer with our data\r\n try {\r\n for (let i = 0; i < this.componentConvolution.length; i += 1) {\r\n data[i] = this.componentConvolution[i];\r\n }\r\n } catch (error) {\r\n console.error(error);\r\n }\r\n\r\n const source = audioCtx.createBufferSource();\r\n\r\n source.buffer = buffer;\r\n source.loop = true;\r\n source.connect(audioCtx.destination);\r\n\r\n this.addCalibrationNodeConvolved(source);\r\n } else {\r\n this.#currentConvolution = this.systemConvolution;\r\n const buffer = audioCtx.createBuffer(\r\n 1, // number of channels\r\n this.systemConvolution.length,\r\n audioCtx.sampleRate // sample rate\r\n );\r\n const data = buffer.getChannelData(0); // get data\r\n // fill the buffer with our data\r\n try {\r\n for (let i = 0; i < this.systemConvolution.length; i += 1) {\r\n data[i] = this.systemConvolution[i];\r\n }\r\n } catch (error) {\r\n console.error(error);\r\n }\r\n\r\n const source = audioCtx.createBufferSource();\r\n\r\n source.buffer = buffer;\r\n source.loop = true;\r\n source.connect(audioCtx.destination);\r\n\r\n this.addCalibrationNodeConvolved(source);\r\n }\r\n };\r\n\r\n /**\r\n * Creates an audio context and plays it for a few seconds.\r\n *\r\n * @private\r\n * @returns - Resolves when the audio is done playing.\r\n * @example\r\n */\r\n #playCalibrationAudio = () => {\r\n if (this.mode === 'unfiltered') {\r\n this.calibrationNodes[0].start(0);\r\n this.#mls = this.calibrationNodes[0].buffer.getChannelData(0);\r\n console.log('play calibration audio ' + this.stepNum);\r\n this.status =\r\n `All Hz Calibration: playing the calibration tone...`.toString() +\r\n this.generateTemplate().toString();\r\n this.emit('update', {message: this.status});\r\n } else if (this.mode === 'filtered') {\r\n this.calibrationNodes[0].start(0);\r\n console.log('play convolved audio ' + this.stepNum);\r\n this.status =\r\n `All Hz Calibration: playing the convolved calibration tone...`.toString() +\r\n this.generateTemplate().toString();\r\n this.emit('update', {message: this.status});\r\n } else {\r\n throw new Error('Mode is incorrect');\r\n }\r\n this.stepNum += 1;\r\n console.log('sink sampling rate');\r\n console.log(this.sinkSamplingRate);\r\n console.log('source sampling rate');\r\n console.log(this.sourceSamplingRate);\r\n };\r\n\r\n /** .\r\n * .\r\n * .\r\n * Stops the audio with tapered offset\r\n *\r\n * @example\r\n */\r\n #stopCalibrationAudio = () => {\r\n this.calibrationNodes[0].stop(0);\r\n this.calibrationNodes = [];\r\n this.sourceNode.disconnect();\r\n this.stepNum += 1;\r\n console.log('stop calibration audio ' + this.stepNum);\r\n this.status =\r\n `All Hz Calibration: stopping the calibration tone...`.toString() +\r\n this.generateTemplate().toString();\r\n this.emit('update', {message: this.status});\r\n };\r\n\r\n playMLSwithIIR = async (stream, iir) => {\r\n this.mode = 'filtered';\r\n console.log('play mls with iir');\r\n this.invertedImpulseResponse = iir;\r\n\r\n await this.calibrationSteps(\r\n stream,\r\n this.#playCalibrationAudio, // play audio func (required)\r\n this.#createCalibrationNodeFromBuffer(this.#currentConvolution), // before play func\r\n this.#awaitSignalOnset, // before record\r\n () => this.numSuccessfulCaptured < 1,\r\n this.#awaitDesiredMLSLength, // during record\r\n this.#afterMLSwIIRRecord, // after record\r\n this.mode\r\n );\r\n };\r\n\r\n /**\r\n * Public method to start the calibration process. Objects intialized from webassembly allocate new memory\r\n * and must be manually freed. This function is responsible for intializing the MlsGenInterface,\r\n * and wrapping the calibration steps with a garbage collection safe gaurd.\r\n *\r\n * @public\r\n * @param stream - The stream of audio from the Listener.\r\n * @example\r\n */\r\n startCalibrationImpulseResponse = async stream => {\r\n let desired_time = this.desired_time_per_mls;\r\n\r\n console.log('MLS sequence should be of length: ' + this.sourceSamplingRate * desired_time);\r\n\r\n length = this.sourceSamplingRate * desired_time;\r\n //get mls here\r\n await this.pyServerAPI\r\n .getMLSWithRetry(length)\r\n .then(res => {\r\n console.log(res);\r\n this.#mlsBufferView = res['mls'];\r\n })\r\n .catch(err => {\r\n // this.emit('InvertedImpulseResponse', {res: false});\r\n console.error(err);\r\n });\r\n await this.calibrationSteps(\r\n stream,\r\n this.#playCalibrationAudio, // play audio func (required)\r\n this.#createCalibrationNodeFromBuffer(this.#mlsBufferView), // before play func\r\n this.#awaitSignalOnset, // before record\r\n () => this.numSuccessfulCaptured < this.numCaptures, // loop while true\r\n this.#awaitDesiredMLSLength, // during record\r\n this.#afterMLSRecord, // after record\r\n this.mode\r\n ),\r\n this.#stopCalibrationAudio();\r\n\r\n // at this stage we've captured all the required signals,\r\n // and have received IRs for each one\r\n // so let's send all the IRs to the server to be converted to a single IIR\r\n await this.sendSystemImpulseResponsesToServerForProcessing();\r\n await this.sendComponentImpulseResponsesToServerForProcessing();\r\n\r\n this.numSuccessfulCaptured = 0;\r\n\r\n let iir_ir_and_plots;\r\n if (this._calibrateSoundCheck != 'none') {\r\n if (this._calibrateSoundCheck != 'system') {\r\n this.#currentConvolution = this.componentConvolution;\r\n } else {\r\n this.#currentConvolution = this.systemConvolution;\r\n }\r\n await this.playMLSwithIIR(stream, this.invertedImpulseResponse);\r\n this.#stopCalibrationAudio();\r\n this.sourceAudioContext.close();\r\n let conv_recs = this.getAllFilteredRecordedSignals();\r\n let recs = this.getAllRecordedSignals();\r\n let unconv_rec = recs[0];\r\n let conv_rec = conv_recs[0];\r\n if (this._calibrateSoundCheck != 'system') {\r\n let knownGain = this.oldComponentIR.Gain;\r\n let knownFreq = this.oldComponentIR.Freq;\r\n let sampleRate = this.sourceSamplingRate || 96000;\r\n let unconv_results = await this.pyServerAPI\r\n .getSubtractedPSDWithRetry(unconv_rec, knownGain, knownFreq, sampleRate)\r\n .then(res => {\r\n this.incrementStatusBar();\r\n this.status =\r\n `All Hz Calibration: done computing the PSD graphs...`.toString() +\r\n this.generateTemplate().toString();\r\n this.emit('update', {message: this.status});\r\n return res;\r\n })\r\n .catch(err => {\r\n console.error(err);\r\n });\r\n\r\n let conv_results = await this.pyServerAPI\r\n .getSubtractedPSDWithRetry(conv_rec, knownGain, knownFreq, sampleRate)\r\n .then(res => {\r\n this.incrementStatusBar();\r\n this.status =\r\n `All Hz Calibration: done computing the PSD graphs...`.toString() +\r\n this.generateTemplate().toString();\r\n this.emit('update', {message: this.status});\r\n return res;\r\n })\r\n .catch(err => {\r\n console.error(err);\r\n });\r\n iir_ir_and_plots = {\r\n systemIIR: this.systemInvertedImpulseResponse,\r\n componentIIR: this.componentInvertedImpulseResponse,\r\n x_unconv: unconv_results['x'],\r\n y_unconv: unconv_results['y'],\r\n x_conv: conv_results['x'],\r\n y_conv: conv_results['y'],\r\n componentIR: this.componentIR,\r\n systemIR: this.systemIR,\r\n };\r\n } else {\r\n let results = await this.pyServerAPI\r\n .getPSDWithRetry({\r\n unconv_rec,\r\n conv_rec,\r\n sampleRate: this.sourceSamplingRate || 96000,\r\n })\r\n .then(res => {\r\n this.incrementStatusBar();\r\n this.status =\r\n `All Hz Calibration: done computing the PSD graphs...`.toString() +\r\n this.generateTemplate().toString();\r\n this.emit('update', {message: this.status});\r\n return res;\r\n })\r\n .catch(err => {\r\n console.error(err);\r\n });\r\n iir_ir_and_plots = {\r\n systemIIR: this.systemInvertedImpulseResponse,\r\n componentIIR: this.componentInvertedImpulseResponse,\r\n x_unconv: results['x_unconv'],\r\n y_unconv: results['y_unconv'],\r\n x_conv: results['x_conv'],\r\n y_conv: results['y_conv'],\r\n componentIR: this.componentIR,\r\n systemIR: this.systemIR,\r\n };\r\n }\r\n\r\n if (this.#download) {\r\n this.downloadSingleUnfilteredRecording();\r\n this.downloadSingleFilteredRecording();\r\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.#mls, 'MLS.csv');\r\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.componentConvolution, 'python_component_convolution_mls_iir.csv');\r\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.systemConvolution, 'python_system_convolution_mls_iir.csv');\r\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.componentInvertedImpulseResponse, 'componentIIR.csv');\r\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.systemInvertedImpulseResponse, 'systemIIR.csv');\r\n for (let i = 0; i < this.autocorrelations.length; i++) {\r\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.autocorrelations[i], `autocorrelation_${i}`);\r\n }\r\n const computedIRagain = await Promise.all(this.impulseResponses).then(res => {\r\n for (let i = 0; i < res.length; i++) {\r\n if (res[i] != undefined) {\r\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(res[i], `IR_${i}`);\r\n }\r\n }\r\n });\r\n }\r\n } else {\r\n iir_ir_and_plots = {\r\n systemIIR: this.systemInvertedImpulseResponse,\r\n componentIIR: this.componentInvertedImpulseResponse,\r\n x_unconv: [],\r\n y_unconv: [],\r\n x_conv: [],\r\n y_conv: [],\r\n componentIR: this.componentIR,\r\n systemIR: this.systemIR,\r\n };\r\n if (this.#download) {\r\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.#mls, 'MLS.csv');\r\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.componentConvolution, 'python_component_convolution_mls_iir.csv');\r\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.systemConvolution, 'python_system_convolution_mls_iir.csv');\r\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.componentInvertedImpulseResponse, 'componentIIR.csv');\r\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.systemInvertedImpulseResponse, 'systemIIR.csv');\r\n for (let i = 0; i < this.autocorrelations.length; i++) {\r\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.autocorrelations[i], `autocorrelation_${i}`);\r\n }\r\n const computedIRagain = await Promise.all(this.impulseResponses).then(res => {\r\n for (let i = 0; i < res.length; i++) {\r\n if (res[i] != undefined) {\r\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(res[i], `IR_${i}`);\r\n }\r\n }\r\n });\r\n }\r\n }\r\n\r\n this.percent_complete = 100;\r\n\r\n this.status = `All Hz Calibration: Finished`.toString() + this.generateTemplate().toString();\r\n this.emit('update', {message: this.status});\r\n\r\n //here after calibration we have the component calibration (either loudspeaker or microphone) in the same form as the componentIR\r\n //that was used to calibrate\r\n\r\n return iir_ir_and_plots;\r\n };\r\n\r\n //////////////////////volume\r\n\r\n handleIncomingData = data => {\r\n console.log('Received data: ', data);\r\n if (data.type === 'soundGainDBSPL') {\r\n this.soundGainDBSPL = data.value;\r\n } else {\r\n throw new Error(`Unknown data type: ${data.type}`);\r\n }\r\n };\r\n createSCurveBuffer = (onSetBool = true) => {\r\n const curve = new Float32Array(this.TAPER_SECS * this.sourceSamplingRate + 1);\r\n const frequency = 1 / (4 * this.TAPER_SECS);\r\n let j = 0;\r\n for (let i = 0; i < this.TAPER_SECS * this.sourceSamplingRate + 1; i += 1) {\r\n const phase = 2 * Math.PI * frequency * j;\r\n const onsetTaper = Math.pow(Math.sin(phase), 2);\r\n const offsetTaper = Math.pow(Math.cos(phase), 2);\r\n curve[i] = onSetBool ? onsetTaper : offsetTaper;\r\n j += 1 / this.sourceSamplingRate;\r\n }\r\n return curve;\r\n };\r\n\r\n #getTruncatedSignal = (left = 3.5, right = 4.5) => {\r\n const start = Math.floor(left * this.sourceSamplingRate);\r\n const end = Math.floor(right * this.sourceSamplingRate);\r\n const result = Array.from(this.getLastRecordedSignal().slice(start, end));\r\n\r\n /**\r\n * function to check that capture was properly made\r\n * @param {*} list\r\n */\r\n const checkResult = list => {\r\n const setItem = new Set(list);\r\n if (setItem.size === 1 && setItem.has(0)) {\r\n console.warn(\r\n 'The last capture failed, all recorded signal is zero',\r\n this.getAllRecordedSignals()\r\n );\r\n }\r\n if (setItem.size === 0) {\r\n console.warn('The last capture failed, no recorded signal');\r\n }\r\n };\r\n checkResult(result);\r\n return result;\r\n };\r\n\r\n /** \r\n * \r\n * \r\n Construct a calibration Node with the calibration parameters and given gain value\r\n * @param {*} gainValue\r\n * */\r\n #createCalibrationToneWithGainValue = gainValue => {\r\n const audioContext = this.makeNewSourceAudioContext();\r\n const oscilator = audioContext.createOscillator();\r\n const gainNode = audioContext.createGain();\r\n const taperGainNode = audioContext.createGain();\r\n const offsetGainNode = audioContext.createGain();\r\n const totalDuration = this.#CALIBRATION_TONE_DURATION * 1.2;\r\n\r\n oscilator.frequency.value = this.#CALIBRATION_TONE_FREQUENCY;\r\n oscilator.type = this.#CALIBRATION_TONE_TYPE;\r\n gainNode.gain.value = gainValue;\r\n\r\n oscilator.connect(gainNode);\r\n gainNode.connect(taperGainNode);\r\n const onsetCurve = this.createSCurveBuffer();\r\n taperGainNode.gain.setValueCurveAtTime(onsetCurve, 0, this.TAPER_SECS);\r\n taperGainNode.connect(offsetGainNode);\r\n const offsetCurve = this.createSCurveBuffer(false);\r\n offsetGainNode.gain.setValueCurveAtTime(\r\n offsetCurve,\r\n totalDuration - this.TAPER_SECS,\r\n this.TAPER_SECS\r\n );\r\n offsetGainNode.connect(audioContext.destination);\r\n\r\n this.addCalibrationNode(oscilator);\r\n };\r\n\r\n /**\r\n * Construct a Calibration Node with the calibration parameters.\r\n *\r\n * @private\r\n * @example\r\n */\r\n #createCalibrationNode = () => {\r\n const audioContext = this.makeNewSourceAudioContext();\r\n const oscilator = audioContext.createOscillator();\r\n const gainNode = audioContext.createGain();\r\n\r\n oscilator.frequency.value = this.#CALIBRATION_TONE_FREQUENCY;\r\n oscilator.type = this.#CALIBRATION_TONE_TYPE;\r\n gainNode.gain.value = 0.04;\r\n\r\n oscilator.connect(gainNode);\r\n gainNode.connect(audioContext.destination);\r\n\r\n this.addCalibrationNode(oscilator);\r\n };\r\n\r\n #playCalibrationAudioVolume = async () => {\r\n const totalDuration = this.#CALIBRATION_TONE_DURATION * 1.2;\r\n\r\n this.calibrationNodes[0].start(0);\r\n this.calibrationNodes[0].stop(totalDuration);\r\n console.log(`Playing a buffer of ${this.#CALIBRATION_TONE_DURATION} seconds of audio`);\r\n console.log(`Waiting a total of ${totalDuration} seconds`);\r\n await (0,_utils__WEBPACK_IMPORTED_MODULE_1__.sleep)(totalDuration);\r\n };\r\n\r\n #sendToServerForProcessing = lCalib => {\r\n console.log('Sending data to server');\r\n this.pyServerAPI\r\n .getVolumeCalibration({\r\n sampleRate: this.sourceSamplingRate,\r\n payload: this.#getTruncatedSignal(),\r\n lCalib: lCalib,\r\n })\r\n .then(res => {\r\n if (this.outDBSPL === null) {\r\n this.incrementStatusBar();\r\n this.outDBSPL = res['outDbSPL'];\r\n this.outDBSPL1000 = res['outDbSPL1000'];\r\n this.THD = res['thd'];\r\n }\r\n })\r\n .catch(err => {\r\n console.warn(err);\r\n });\r\n };\r\n\r\n startCalibrationVolume = async (stream, gainValues, lCalib, componentGainDBSPL) => {\r\n const trialIterations = gainValues.length;\r\n this.status_denominator += trialIterations;\r\n const thdValues = [];\r\n const inDBValues = [];\r\n let inDB = 0;\r\n const outDBSPLValues = [];\r\n const outDBSPL1000Values = [];\r\n\r\n // do one calibration that will be discarded\r\n const soundLevelToDiscard = -60;\r\n const gainToDiscard = Math.pow(10, soundLevelToDiscard / 20);\r\n this.status =\r\n `1000 Hz Calibration: Sound Level ${soundLevelToDiscard} dB`.toString() +\r\n this.generateTemplate().toString();\r\n //this.emit('update', {message: `1000 Hz Calibration: Sound Level ${soundLevelToDiscard} dB`});\r\n this.emit('update', {message: this.status});\r\n\r\n do {\r\n // eslint-disable-next-line no-await-in-loop\r\n await this.volumeCalibrationSteps(\r\n stream,\r\n this.#playCalibrationAudioVolume,\r\n this.#createCalibrationToneWithGainValue,\r\n this.#sendToServerForProcessing,\r\n gainToDiscard,\r\n lCalib //todo make this a class parameter\r\n );\r\n } while (this.outDBSPL === null);\r\n //reset the values\r\n //this.incrementStatusBar();\r\n\r\n this.outDBSPL = null;\r\n this.outDBSPL = null;\r\n this.outDBSPL1000 = null;\r\n this.THD = null;\r\n\r\n // run the calibration at different gain values provided by the user\r\n for (let i = 0; i < trialIterations; i++) {\r\n //convert gain to DB and add to inDB\r\n inDB = Math.log10(gainValues[i]) * 20;\r\n // precision to 1 decimal place\r\n inDB = Math.round(inDB * 10) / 10;\r\n inDBValues.push(inDB);\r\n console.log('next update');\r\n this.status =\r\n `1000 Hz Calibration: Sound Level ${inDB} dB`.toString() +\r\n this.generateTemplate().toString();\r\n this.emit('update', {message: this.status});\r\n do {\r\n // eslint-disable-next-line no-await-in-loop\r\n await this.volumeCalibrationSteps(\r\n stream,\r\n this.#playCalibrationAudioVolume,\r\n this.#createCalibrationToneWithGainValue,\r\n this.#sendToServerForProcessing,\r\n gainValues[i],\r\n lCalib //todo make this a class parameter\r\n );\r\n } while (this.outDBSPL === null);\r\n outDBSPL1000Values.push(this.outDBSPL1000);\r\n thdValues.push(this.THD);\r\n outDBSPLValues.push(this.outDBSPL);\r\n\r\n this.outDBSPL = null;\r\n this.outDBSPL1000 = null;\r\n this.THD = null;\r\n }\r\n\r\n // get the volume calibration parameters from the server\r\n const parameters = await this.pyServerAPI\r\n .getVolumeCalibrationParameters({\r\n inDBValues: inDBValues,\r\n outDBSPLValues: outDBSPL1000Values,\r\n lCalib: lCalib,\r\n componentGainDBSPL,\r\n })\r\n .then(res => {\r\n this.incrementStatusBar();\r\n return res;\r\n });\r\n const result = {\r\n parameters: parameters,\r\n inDBValues: inDBValues,\r\n outDBSPLValues: outDBSPLValues,\r\n outDBSPL1000Values: outDBSPL1000Values,\r\n thdValues: thdValues,\r\n };\r\n\r\n return result;\r\n };\r\n\r\n // function to write frq and gain to firebase database given speakerID\r\n writeFrqGain = async (speakerID, frq, gain, OEM) => {\r\n // freq and gain are too large to take samples 1 in every 100 samples\r\n\r\n const sampledFrq = [];\r\n const sampledGain = [];\r\n for (let i = 0; i < frq.length; i += 100) {\r\n sampledFrq.push(frq[i]);\r\n sampledGain.push(gain[i]);\r\n }\r\n\r\n const data = {Freq: sampledFrq, Gain: sampledGain};\r\n\r\n await (0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.set)((0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.ref)(_config_firebase__WEBPACK_IMPORTED_MODULE_2__[\"default\"], `Microphone2/${OEM}/${speakerID}/linear`), data);\r\n };\r\n\r\n // Function to Read frq and gain from firebase database given speakerID\r\n // returns an array of frq and gain if speakerID exists, returns null otherwise\r\n\r\n readFrqGain = async (speakerID, OEM) => {\r\n const dbRef = (0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.ref)(_config_firebase__WEBPACK_IMPORTED_MODULE_2__[\"default\"]);\r\n const snapshot = await (0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.get)((0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.child)(dbRef, `Microphone2/${OEM}/${speakerID}/linear`));\r\n if (snapshot.exists()) {\r\n return snapshot.val();\r\n }\r\n return null;\r\n };\r\n\r\n readGainat1000Hz = async (speakerID, OEM) => {\r\n const dbRef = (0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.ref)(_config_firebase__WEBPACK_IMPORTED_MODULE_2__[\"default\"]);\r\n const snapshot = await (0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.get)((0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.child)(dbRef, `Microphone2/${OEM}/${speakerID}/Gain1000`));\r\n if (snapshot.exists()) {\r\n return snapshot.val();\r\n }\r\n return null;\r\n };\r\n\r\n writeGainat1000Hz = async (speakerID, gain, OEM) => {\r\n const data = {Gain: gain};\r\n await (0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.set)((0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.ref)(_config_firebase__WEBPACK_IMPORTED_MODULE_2__[\"default\"], `Microphone2/${OEM}/${speakerID}/Gain1000`), gain);\r\n };\r\n\r\n writeIsSmartPhone = async (speakerID, isSmartPhone, OEM) => {\r\n const data = {isSmartPhone: isSmartPhone};\r\n await (0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.set)((0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.ref)(_config_firebase__WEBPACK_IMPORTED_MODULE_2__[\"default\"], `Microphone2/${OEM}/${speakerID}/isSmartPhone`), isSmartPhone);\r\n };\r\n\r\n doesMicrophoneExist = async (speakerID, OEM) => {\r\n const dbRef = (0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.ref)(_config_firebase__WEBPACK_IMPORTED_MODULE_2__[\"default\"]);\r\n const snapshot = await (0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.get)((0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.child)(dbRef, `Microphone2/${OEM}/${speakerID}`));\r\n if (snapshot.exists()) {\r\n return true;\r\n }\r\n return false;\r\n };\r\n\r\n convertToDB = gain => {\r\n return Math.log10(gain) * 20;\r\n };\r\n\r\n // Function to perform linear interpolation between two points\r\n interpolate(x, x0, y0, x1, y1) {\r\n return y0 + ((x - x0) * (y1 - y0)) / (x1 - x0);\r\n }\r\n\r\n findGainatFrequency = (frequencies, gains, targetFrequency) => {\r\n // Find the index of the first frequency in the array greater than the target frequency\r\n let index = 0;\r\n while (index < frequencies.length && frequencies[index] < targetFrequency) {\r\n index++;\r\n }\r\n\r\n // Handle cases when the target frequency is outside the range of the given data\r\n if (index === 0) {\r\n return gains[0];\r\n } else if (index === frequencies.length) {\r\n return gains[gains.length - 1];\r\n } else {\r\n // Interpolate the gain based on the surrounding frequencies\r\n const x0 = frequencies[index - 1];\r\n const y0 = gains[index - 1];\r\n const x1 = frequencies[index];\r\n const y1 = gains[index];\r\n return this.interpolate(targetFrequency, x0, y0, x1, y1);\r\n }\r\n };\r\n\r\n // Example of how to use the writeFrqGain and readFrqGain functions\r\n // writeFrqGain('speaker1', [1, 2, 3], [4, 5, 6]);\r\n // Speaker1 is the speakerID you want to write to in the database\r\n // readFrqGain('MiniDSPUMIK_1').then(data => console.log(data));\r\n // MiniDSPUMIK_1 is the speakerID with some Data in the database\r\n //adding gainDBSPL\r\n startCalibration = async (\r\n stream,\r\n gainValues,\r\n lCalib = 104.92978421490648,\r\n componentIR = null,\r\n microphoneName = 'MiniDSP-UMIK1-711-4754-vertical',\r\n _calibrateSoundCheck = 'goal', //GOAL PASSed in by default\r\n isSmartPhone = false,\r\n _calibrateSoundBurstRepeats = 4,\r\n _calibrateSoundBurstSec = 1,\r\n _calibrateSoundBurstsWarmup = 1,\r\n _calibrateSoundHz = 48000,\r\n _calibrateSoundIIRSec = 0.2,\r\n micManufacturer = '',\r\n micSerialNumber = '',\r\n micModelNumber = '',\r\n micModelName = ''\r\n ) => {\r\n this.iirLength = Math.floor(_calibrateSoundIIRSec*this.sourceSamplingRate);\r\n console.log('device info:', this.deviceInfo);\r\n this.numMLSPerCapture = _calibrateSoundBurstRepeats;\r\n this.desired_time_per_mls = _calibrateSoundBurstSec;\r\n this.num_mls_to_skip = _calibrateSoundBurstsWarmup;\r\n this.desired_sampling_rate = _calibrateSoundHz;\r\n\r\n //feed calibration goal here\r\n this._calibrateSoundCheck = _calibrateSoundCheck;\r\n //check if a componentIR was given to the system, if it isn't check for the microphone. using dummy data here bc we need to\r\n //check the db based on the microphone currently connected\r\n\r\n //new lCalib found at top of calibration files *1000hz, make sure to correct\r\n //based on zeroing of 1000hz, search for \"*1000Hz\"\r\n const ID = isSmartPhone ? micModelNumber : micSerialNumber;\r\n const OEM = isSmartPhone ? this.deviceInfo.OEM : micManufacturer;\r\n if (componentIR == null) {\r\n //mode 'ir'\r\n //global variable this.componentIR must be set\r\n this.componentIR = await this.readFrqGain(ID, OEM).then(data => {\r\n return data;\r\n });\r\n\r\n lCalib = await this.readGainat1000Hz(ID, OEM);\r\n // this.componentGainDBSPL = this.convertToDB(lCalib);\r\n this.componentGainDBSPL = lCalib;\r\n //TODO: if this call to database is unknown, cannot perform experiment => return false\r\n if (this.componentIR == null) {\r\n this.status =\r\n `Microphone (${OEM},${ID}) is not found in the database. Please add it to the database.`.toString();\r\n this.emit('update', {message: this.status});\r\n return false;\r\n }\r\n } else {\r\n this.componentIR = componentIR;\r\n lCalib = this.findGainatFrequency(this.componentIR.Freq, this.componentIR.Gain, 1000);\r\n // this.componentGainDBSPL = this.convertToDB(lCalib);\r\n this.componentGainDBSPL = lCalib;\r\n await this.writeGainat1000Hz(ID, lCalib, OEM);\r\n await this.writeIsSmartPhone(ID, isSmartPhone, OEM);\r\n }\r\n\r\n this.oldComponentIR = this.componentIR;\r\n\r\n let volumeResults = await this.startCalibrationVolume(\r\n stream,\r\n gainValues,\r\n lCalib,\r\n this.componentGainDBSPL\r\n );\r\n\r\n let impulseResponseResults = await this.startCalibrationImpulseResponse(stream);\r\n\r\n if (componentIR != null) {\r\n //insert Freq and Gain from this.componentIR into db\r\n await this.writeFrqGain(\r\n ID,\r\n impulseResponseResults.componentIR.Freq,\r\n impulseResponseResults.componentIR.Gain,\r\n OEM\r\n );\r\n }\r\n\r\n const total_results = {...volumeResults, ...impulseResponseResults};\r\n console.log('total');\r\n console.log(total_results);\r\n total_results['micInfo'] = {\r\n micManufacturer: micManufacturer,\r\n micSerialNumber: micSerialNumber,\r\n micModelNumber: micModelNumber,\r\n micModelName: micModelName,\r\n ID: ID,\r\n OEM: OEM,\r\n };\r\n return total_results;\r\n };\r\n}\r\n\r\n/* harmony default export */ __webpack_exports__[\"default\"] = (Combination);\r\n\n\n//# sourceURL=webpack://speakerCalibrator/./src/tasks/combination/combination.js?");
844
844
 
845
845
  /***/ }),
846
846
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "speaker-calibration",
3
- "version": "2.2.12",
3
+ "version": "2.2.14",
4
4
  "description": "Speaker calibration library for auditory testing",
5
5
  "main": "dist/main.js",
6
6
  "directories": {
@@ -101,6 +101,7 @@ class Speaker extends AudioPeer {
101
101
  params.calibrateSoundBurstSec,
102
102
  params.calibrateSoundBurstsWarmup,
103
103
  params.calibrateSoundHz,
104
+ params.calibrateSoundIIRSec,
104
105
  params.micManufacturer,
105
106
  params.micSerialNumber,
106
107
  params.micModelNumber,
@@ -213,7 +213,7 @@ class PythonServerAPI {
213
213
  };
214
214
 
215
215
 
216
- getComponentInverseImpulseResponse = async ({payload,mls,lowHz,highHz,componentIRGains,componentIRFreqs,sampleRate}) => {
216
+ getComponentInverseImpulseResponse = async ({payload,mls,lowHz,highHz,componentIRGains,iirLength,componentIRFreqs,sampleRate}) => {
217
217
  const task = 'component-inverse-impulse-response';
218
218
  let res = null;
219
219
 
@@ -225,6 +225,7 @@ class PythonServerAPI {
225
225
  mls,
226
226
  lowHz,
227
227
  highHz,
228
+ iirLength,
228
229
  componentIRGains,
229
230
  componentIRFreqs,
230
231
  sampleRate,
@@ -248,7 +249,7 @@ class PythonServerAPI {
248
249
 
249
250
  return res.data[task];
250
251
  };
251
- getSystemInverseImpulseResponse = async ({payload,mls,lowHz,highHz,sampleRate}) => {
252
+ getSystemInverseImpulseResponse = async ({payload,mls,lowHz,highHz,iirLength,sampleRate}) => {
252
253
  const task = 'system-inverse-impulse-response';
253
254
  let res = null;
254
255
 
@@ -259,6 +260,7 @@ class PythonServerAPI {
259
260
  payload,
260
261
  mls,
261
262
  lowHz,
263
+ iirLength,
262
264
  highHz,
263
265
  sampleRate,
264
266
  });
@@ -282,13 +284,13 @@ class PythonServerAPI {
282
284
  return res.data[task];
283
285
  };
284
286
 
285
- getComponentInverseImpulseResponseWithRetry = async ({ payload, mls, lowHz, highHz,componentIRGains,componentIRFreqs, sampleRate}) => {
287
+ getComponentInverseImpulseResponseWithRetry = async ({ payload, mls, lowHz, highHz,componentIRGains,iirLength,componentIRFreqs, sampleRate}) => {
286
288
  let retryCount = 0;
287
289
  let response = null;
288
290
 
289
291
  while (retryCount < this.MAX_RETRY_COUNT) {
290
292
  try {
291
- response = await this.getComponentInverseImpulseResponse({ payload, mls, lowHz, highHz,componentIRGains,componentIRFreqs,sampleRate});
293
+ response = await this.getComponentInverseImpulseResponse({ payload, mls, lowHz, highHz,componentIRGains,iirLength,componentIRFreqs,sampleRate});
292
294
  // If the request is successful, break out of the loop
293
295
  break;
294
296
  } catch (error) {
@@ -305,13 +307,13 @@ getComponentInverseImpulseResponseWithRetry = async ({ payload, mls, lowHz, high
305
307
  }
306
308
  };
307
309
 
308
- getSystemInverseImpulseResponseWithRetry = async ({ payload, mls, lowHz, highHz, sampleRate}) => {
310
+ getSystemInverseImpulseResponseWithRetry = async ({ payload, mls, lowHz, highHz,iirLength, sampleRate}) => {
309
311
  let retryCount = 0;
310
312
  let response = null;
311
313
 
312
314
  while (retryCount < this.MAX_RETRY_COUNT) {
313
315
  try {
314
- response = await this.getSystemInverseImpulseResponse({ payload, mls, lowHz, highHz,sampleRate});
316
+ response = await this.getSystemInverseImpulseResponse({ payload, mls, lowHz, highHz,iirLength,sampleRate});
315
317
  // If the request is successful, break out of the loop
316
318
  break;
317
319
  } catch (error) {
@@ -157,6 +157,8 @@ class Combination extends AudioCalibrator {
157
157
 
158
158
  autocorrelations = [];
159
159
 
160
+ iirLength = 0;
161
+
160
162
  /**generate string template that gets reevaluated as variable increases */
161
163
  generateTemplate = () => {
162
164
  if (this.percent_complete > 100) {
@@ -200,6 +202,7 @@ class Combination extends AudioCalibrator {
200
202
  const mls = this.#mls;
201
203
  const lowHz = this.#lowHz;
202
204
  const highHz = this.#highHz;
205
+ const iirLength = this.iirLength;
203
206
  this.stepNum += 1;
204
207
  console.log('send impulse responses to server: ' + this.stepNum);
205
208
  this.status =
@@ -211,6 +214,7 @@ class Combination extends AudioCalibrator {
211
214
  mls,
212
215
  lowHz,
213
216
  highHz,
217
+ iirLength,
214
218
  sampleRate: this.sourceSamplingRate || 96000,
215
219
  })
216
220
  .then(res => {
@@ -248,6 +252,7 @@ class Combination extends AudioCalibrator {
248
252
  const componentIRFreqs = this.componentIR['Freq'];
249
253
  const mls = this.#mls;
250
254
  const lowHz = this.#lowHz;
255
+ const iirLength = this.iirLength;
251
256
  const highHz = this.#highHz;
252
257
  this.stepNum += 1;
253
258
  console.log('send impulse responses to server: ' + this.stepNum);
@@ -260,6 +265,7 @@ class Combination extends AudioCalibrator {
260
265
  mls,
261
266
  lowHz,
262
267
  highHz,
268
+ iirLength,
263
269
  componentIRGains,
264
270
  componentIRFreqs,
265
271
  sampleRate: this.sourceSamplingRate || 96000,
@@ -775,8 +781,8 @@ class Combination extends AudioCalibrator {
775
781
  saveToCSV(this.systemConvolution, 'python_system_convolution_mls_iir.csv');
776
782
  saveToCSV(this.componentInvertedImpulseResponse, 'componentIIR.csv');
777
783
  saveToCSV(this.systemInvertedImpulseResponse, 'systemIIR.csv');
778
- for (let i = 0; i < this.autocorrelations.length; i++){
779
- saveToCSV(this.autocorrelations[i],`autocorrelation_${i}`);
784
+ for (let i = 0; i < this.autocorrelations.length; i++) {
785
+ saveToCSV(this.autocorrelations[i], `autocorrelation_${i}`);
780
786
  }
781
787
  const computedIRagain = await Promise.all(this.impulseResponses).then(res => {
782
788
  for (let i = 0; i < res.length; i++) {
@@ -803,8 +809,8 @@ class Combination extends AudioCalibrator {
803
809
  saveToCSV(this.systemConvolution, 'python_system_convolution_mls_iir.csv');
804
810
  saveToCSV(this.componentInvertedImpulseResponse, 'componentIIR.csv');
805
811
  saveToCSV(this.systemInvertedImpulseResponse, 'systemIIR.csv');
806
- for (let i = 0; i < this.autocorrelations.length; i++){
807
- saveToCSV(this.autocorrelations[i],`autocorrelation_${i}`);
812
+ for (let i = 0; i < this.autocorrelations.length; i++) {
813
+ saveToCSV(this.autocorrelations[i], `autocorrelation_${i}`);
808
814
  }
809
815
  const computedIRagain = await Promise.all(this.impulseResponses).then(res => {
810
816
  for (let i = 0; i < res.length; i++) {
@@ -1055,7 +1061,7 @@ class Combination extends AudioCalibrator {
1055
1061
  };
1056
1062
 
1057
1063
  // function to write frq and gain to firebase database given speakerID
1058
- writeFrqGain = async (speakerID, frq, gain) => {
1064
+ writeFrqGain = async (speakerID, frq, gain, OEM) => {
1059
1065
  // freq and gain are too large to take samples 1 in every 100 samples
1060
1066
 
1061
1067
  const sampledFrq = [];
@@ -1067,33 +1073,47 @@ class Combination extends AudioCalibrator {
1067
1073
 
1068
1074
  const data = {Freq: sampledFrq, Gain: sampledGain};
1069
1075
 
1070
- await set(ref(database, `Microphone/${speakerID}/linear`), data);
1076
+ await set(ref(database, `Microphone2/${OEM}/${speakerID}/linear`), data);
1071
1077
  };
1072
1078
 
1073
1079
  // Function to Read frq and gain from firebase database given speakerID
1074
1080
  // returns an array of frq and gain if speakerID exists, returns null otherwise
1075
1081
 
1076
- readFrqGain = async speakerID => {
1082
+ readFrqGain = async (speakerID, OEM) => {
1077
1083
  const dbRef = ref(database);
1078
- const snapshot = await get(child(dbRef, `Microphone/${speakerID}/linear`));
1084
+ const snapshot = await get(child(dbRef, `Microphone2/${OEM}/${speakerID}/linear`));
1079
1085
  if (snapshot.exists()) {
1080
1086
  return snapshot.val();
1081
1087
  }
1082
1088
  return null;
1083
1089
  };
1084
1090
 
1085
- readGainat1000Hz = async speakerID => {
1091
+ readGainat1000Hz = async (speakerID, OEM) => {
1086
1092
  const dbRef = ref(database);
1087
- const snapshot = await get(child(dbRef, `Microphone/${speakerID}/Gain1000`));
1093
+ const snapshot = await get(child(dbRef, `Microphone2/${OEM}/${speakerID}/Gain1000`));
1088
1094
  if (snapshot.exists()) {
1089
1095
  return snapshot.val();
1090
1096
  }
1091
1097
  return null;
1092
1098
  };
1093
1099
 
1094
- writeGainat1000Hz = async (speakerID, gain) => {
1100
+ writeGainat1000Hz = async (speakerID, gain, OEM) => {
1095
1101
  const data = {Gain: gain};
1096
- await set(ref(database, `Microphone/${speakerID}/Gain1000`), gain);
1102
+ await set(ref(database, `Microphone2/${OEM}/${speakerID}/Gain1000`), gain);
1103
+ };
1104
+
1105
+ writeIsSmartPhone = async (speakerID, isSmartPhone, OEM) => {
1106
+ const data = {isSmartPhone: isSmartPhone};
1107
+ await set(ref(database, `Microphone2/${OEM}/${speakerID}/isSmartPhone`), isSmartPhone);
1108
+ };
1109
+
1110
+ doesMicrophoneExist = async (speakerID, OEM) => {
1111
+ const dbRef = ref(database);
1112
+ const snapshot = await get(child(dbRef, `Microphone2/${OEM}/${speakerID}`));
1113
+ if (snapshot.exists()) {
1114
+ return true;
1115
+ }
1116
+ return false;
1097
1117
  };
1098
1118
 
1099
1119
  convertToDB = gain => {
@@ -1127,11 +1147,6 @@ class Combination extends AudioCalibrator {
1127
1147
  }
1128
1148
  };
1129
1149
 
1130
- writeIsSmartPhone = async (speakerID, isSmartPhone) => {
1131
- const data = {isSmartPhone: isSmartPhone};
1132
- await set(ref(database, `Microphone/${speakerID}/isSmartPhone`), isSmartPhone);
1133
- };
1134
-
1135
1150
  // Example of how to use the writeFrqGain and readFrqGain functions
1136
1151
  // writeFrqGain('speaker1', [1, 2, 3], [4, 5, 6]);
1137
1152
  // Speaker1 is the speakerID you want to write to in the database
@@ -1150,11 +1165,14 @@ class Combination extends AudioCalibrator {
1150
1165
  _calibrateSoundBurstSec = 1,
1151
1166
  _calibrateSoundBurstsWarmup = 1,
1152
1167
  _calibrateSoundHz = 48000,
1168
+ _calibrateSoundIIRSec = 0.2,
1153
1169
  micManufacturer = '',
1154
1170
  micSerialNumber = '',
1155
1171
  micModelNumber = '',
1156
1172
  micModelName = ''
1157
1173
  ) => {
1174
+ this.iirLength = Math.floor(_calibrateSoundIIRSec*this.sourceSamplingRate);
1175
+ console.log('device info:', this.deviceInfo);
1158
1176
  this.numMLSPerCapture = _calibrateSoundBurstRepeats;
1159
1177
  this.desired_time_per_mls = _calibrateSoundBurstSec;
1160
1178
  this.num_mls_to_skip = _calibrateSoundBurstsWarmup;
@@ -1167,20 +1185,22 @@ class Combination extends AudioCalibrator {
1167
1185
 
1168
1186
  //new lCalib found at top of calibration files *1000hz, make sure to correct
1169
1187
  //based on zeroing of 1000hz, search for "*1000Hz"
1188
+ const ID = isSmartPhone ? micModelNumber : micSerialNumber;
1189
+ const OEM = isSmartPhone ? this.deviceInfo.OEM : micManufacturer;
1170
1190
  if (componentIR == null) {
1171
1191
  //mode 'ir'
1172
1192
  //global variable this.componentIR must be set
1173
- this.componentIR = await this.readFrqGain(microphoneName).then(data => {
1193
+ this.componentIR = await this.readFrqGain(ID, OEM).then(data => {
1174
1194
  return data;
1175
1195
  });
1176
1196
 
1177
- lCalib = await this.readGainat1000Hz(microphoneName);
1197
+ lCalib = await this.readGainat1000Hz(ID, OEM);
1178
1198
  // this.componentGainDBSPL = this.convertToDB(lCalib);
1179
1199
  this.componentGainDBSPL = lCalib;
1180
1200
  //TODO: if this call to database is unknown, cannot perform experiment => return false
1181
1201
  if (this.componentIR == null) {
1182
1202
  this.status =
1183
- `Microphone ${microphoneName} is not in the database. Please add it to the database.`.toString();
1203
+ `Microphone (${OEM},${ID}) is not found in the database. Please add it to the database.`.toString();
1184
1204
  this.emit('update', {message: this.status});
1185
1205
  return false;
1186
1206
  }
@@ -1189,8 +1209,8 @@ class Combination extends AudioCalibrator {
1189
1209
  lCalib = this.findGainatFrequency(this.componentIR.Freq, this.componentIR.Gain, 1000);
1190
1210
  // this.componentGainDBSPL = this.convertToDB(lCalib);
1191
1211
  this.componentGainDBSPL = lCalib;
1192
- await this.writeGainat1000Hz(microphoneName, lCalib);
1193
- await this.writeIsSmartPhone(microphoneName, isSmartPhone);
1212
+ await this.writeGainat1000Hz(ID, lCalib, OEM);
1213
+ await this.writeIsSmartPhone(ID, isSmartPhone, OEM);
1194
1214
  }
1195
1215
 
1196
1216
  this.oldComponentIR = this.componentIR;
@@ -1207,15 +1227,24 @@ class Combination extends AudioCalibrator {
1207
1227
  if (componentIR != null) {
1208
1228
  //insert Freq and Gain from this.componentIR into db
1209
1229
  await this.writeFrqGain(
1210
- microphoneName,
1230
+ ID,
1211
1231
  impulseResponseResults.componentIR.Freq,
1212
- impulseResponseResults.componentIR.Gain
1232
+ impulseResponseResults.componentIR.Gain,
1233
+ OEM
1213
1234
  );
1214
1235
  }
1215
1236
 
1216
1237
  const total_results = {...volumeResults, ...impulseResponseResults};
1217
1238
  console.log('total');
1218
1239
  console.log(total_results);
1240
+ total_results['micInfo'] = {
1241
+ micManufacturer: micManufacturer,
1242
+ micSerialNumber: micSerialNumber,
1243
+ micModelNumber: micModelNumber,
1244
+ micModelName: micModelName,
1245
+ ID: ID,
1246
+ OEM: OEM,
1247
+ };
1219
1248
  return total_results;
1220
1249
  };
1221
1250
  }