speaker-calibration 2.2.106 → 2.2.107

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
@@ -851,7 +851,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _myE
851
851
  /***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
852
852
 
853
853
  "use strict";
854
- 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/* harmony import */ var firebase_firestore__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! firebase/firestore */ \"./node_modules/firebase/firestore/dist/esm/index.esm.js\");\n\r\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 = 2] - 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 = 2,\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 componentIROrigin = {\r\n Freq: [],\r\n Gain: [],\r\n };\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 CALIBRATION_TONE_DURATION = 5; // seconds\r\n calibrateSound1000HzPreSec = 3.5;\r\n calibrateSound1000HzSec = 1.0;\r\n calibrateSound1000HzPostSec = 0.5;\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 irLength = 0;\r\n\r\n componentInvertedImpulseResponseNoBandpass = [];\r\n\r\n componentIRInTimeDomain = [];\r\n\r\n systemInvertedImpulseResponseNoBandpass = [];\r\n\r\n _calibrateSoundBackgroundSecs;\r\n\r\n _calibrateSoundSmoothOctaves;\r\n\r\n background_noise = {};\r\n\r\n numSuccessfulBackgroundCaptured;\r\n\r\n _calibrateSoundBurstDb;\r\n\r\n componentIRPhase = [];\r\n\r\n systemIRPhase = [];\r\n\r\n webAudioDeviceNames = {loudspeaker: '', microphone: '', loudspeakerText: '', microphoneText: ''};\r\n\r\n recordingChecks = {\r\n volume: {},\r\n unfiltered: [],\r\n system: [],\r\n component: [],\r\n };\r\n\r\n inDB;\r\n\r\n soundCheck = '';\r\n\r\n filteredMLSRange = {\r\n component: {\r\n Min: null,\r\n Max: null,\r\n },\r\n system: {\r\n Min: null,\r\n Max: null,\r\n },\r\n };\r\n\r\n /** @private */\r\n timeStamp = [];\r\n\r\n /** @private */\r\n startTime;\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 reportWebAudioNames = `<br>${this.webAudioDeviceNames.loudspeakerText} <br> ${this.webAudioDeviceNames.microphoneText}`;\r\n const reportParameters = `<br> Sampling: Loudspeaker ${this.sourceSamplingRate} Hz, Microphone ${this.sinkSamplingRate} Hz, ${this.sampleSize} bits`;\r\n const template = `<div style=\"display: flex; justify-content: center; margin-top:12px;\"><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 reportWebAudioNames + reportParameters + 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 this.addTimeStamp('Get system iir');\r\n const computedIRs = await Promise.all(this.impulseResponses);\r\n const filteredComputedIRs = computedIRs.filter(element => {\r\n return element != undefined;\r\n }); //log any errors that are found in this step\r\n const mls = this.#mls;\r\n const lowHz = this.#lowHz; //gain of 1 below cutoff, need gain of 0\r\n const highHz = this.#highHz; //check error for anything other than 10 kHz\r\n const iirLength = this.iirLength;\r\n const num_periods = this.numMLSPerCapture + this.num_mls_to_skip;\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 num_periods,\r\n sampleRate: this.sourceSamplingRate || 96000,\r\n calibrateSoundBurstDb: this._calibrateSoundBurstDb,\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 this.systemInvertedImpulseResponseNoBandpass = res['iirNoBandpass'];\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 this.addTimeStamp('Get component iir');\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 irLength = this.irLength;\r\n const num_periods = this.numMLSPerCapture + this.num_mls_to_skip;\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 num_periods,\r\n sampleRate: this.sourceSamplingRate || 96000,\r\n calibrateSoundBurstDb: this._calibrateSoundBurstDb,\r\n irLength,\r\n calibrateSoundSmoothOctaves: this._calibrateSoundSmoothOctaves,\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.componentIRPhase = res['component_angle'];\r\n this.systemIRPhase = res['system_angle']\r\n this.componentIROrigin['Freq'] = res['frequencies'];\r\n this.componentIROrigin['Gain'] = res['irOrigin'];\r\n this.componentConvolution = res['convolution'];\r\n this.componentInvertedImpulseResponseNoBandpass = res['iirNoBandpass'];\r\n this.componentIRInTimeDomain = res['irTime'];\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 sendBackgroundRecording = () => {\r\n const allSignals = this.getAllBackgroundRecordings();\r\n const numSignals = allSignals.length;\r\n const background_rec_whole = allSignals[numSignals - 1];\r\n const fraction = 0.5 / (this._calibrateSoundBackgroundSecs + 0.5);\r\n // Calculate the starting index for slicing the array\r\n const startIndex = Math.round(fraction * background_rec_whole.length);\r\n // Slice the array from the calculated start index to the end of the array\r\n const background_rec = background_rec_whole.slice(startIndex);\r\n console.log('Sending background recording to server for processing');\r\n this.addTimeStamp('Get background PSD');\r\n this.pyServerAPI\r\n .getBackgroundNoisePSDWithRetry({\r\n background_rec,\r\n sampleRate: this.sourceSamplingRate || 96000,\r\n })\r\n .then(res => {\r\n if (this.numSuccessfulBackgroundCaptured < 1) {\r\n this.numSuccessfulBackgroundCaptured += 1;\r\n //storing all background data in background_psd object\r\n this.background_noise['x_background'] = res['x_background'];\r\n this.background_noise['y_background'] = res['y_background'];\r\n this.background_noise['recording'] = background_rec;\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 * .\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 = async signalCsv => {\r\n const allSignals = this.getAllUnfilteredRecordedSignals();\r\n console.log(\r\n 'Obtaining last all hz unfiltered recording from #allHzUnfilteredRecordings to send to server for processing'\r\n );\r\n const numSignals = allSignals.length;\r\n const mls = this.#mlsBufferView;\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 await this.pyServerAPI\r\n .allHzPowerCheck({\r\n payload,\r\n sampleRate: this.sourceSamplingRate || 96000,\r\n binDesiredSec: this._calibrateSoundPowerBinDesiredSec,\r\n burstSec: this.desired_time_per_mls,\r\n })\r\n .then(result => {\r\n if (result) {\r\n this.recordingChecks['unfiltered'].push(result);\r\n if (result['sd'] < this._calibrateSoundPowerDbSDToleratedDb) {\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, //get rid of this\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 } else if (result['sd'] > this._calibrateSoundPowerDbSDToleratedDb) {\r\n this.clearLastUnfilteredRecordedSignals();\r\n console.log('unfiltered rec', this.getAllUnfilteredRecordedSignals.length);\r\n }\r\n }\r\n })\r\n .catch(err => {\r\n console.error(err);\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 //unfiltered\r\n time_to_wait = (this.#mls.length / this.sourceSamplingRate) * this.numMLSPerCapture;\r\n time_to_wait = time_to_wait * 1.1;\r\n } else if (this.mode === 'filtered') {\r\n //filtered\r\n // time_to_wait =\r\n // (this.#currentConvolution.length / this.sourceSamplingRate) *\r\n // (this.numMLSPerCapture / (this.num_mls_to_skip + this.numMLSPerCapture));\r\n time_to_wait =\r\n (this.#currentConvolution.length / this.sourceSamplingRate) * this.numMLSPerCapture;\r\n time_to_wait = time_to_wait * 1.1;\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);\r\n };\r\n\r\n /**\r\n * Passed to the background noise recording function, awaits the desired amount of seconds to capture the desired number\r\n * of seconds of background noise\r\n *\r\n * @example\r\n */\r\n #awaitBackgroundNoiseRecording = async () => {\r\n console.log(\r\n 'Waiting ' + this._calibrateSoundBackgroundSecs + ' second(s) to record background noise'\r\n );\r\n let time_to_wait = this._calibrateSoundBackgroundSecs + 0.5;\r\n await (0,_utils__WEBPACK_IMPORTED_MODULE_1__.sleep)(time_to_wait);\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) * number_of_bursts_to_skip;\r\n } else if (this.mode === 'filtered') {\r\n console.log(this.#currentConvolution.length);\r\n // time_to_sleep =\r\n // (this.#currentConvolution.length / this.sourceSamplingRate) *\r\n // (number_of_bursts_to_skip / (number_of_bursts_to_skip + this.numMLSPerCapture));\r\n time_to_sleep =\r\n (this.#currentConvolution.length / this.sourceSamplingRate) * number_of_bursts_to_skip;\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 = async () => {\r\n await this.checkPowerVariation();\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('length databuffer');\r\n console.log(dataBuffer.length);\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\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];\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\r\n if (this.mode === 'filtered') {\r\n //used to not loop filtered\r\n this.sourceNode.loop = true;\r\n } else {\r\n this.sourceNode.loop = true;\r\n }\r\n\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 * 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 this.addTimeStamp('Play unfiltered mls');\r\n this.calibrationNodes[0].start(0);\r\n this.status = ``;\r\n if (this.mode === 'unfiltered') {\r\n console.log('mls', this.#mls); // before multiplied by calibrateSoundBurstDb\r\n console.log('mls buffer view', this.#mlsBufferView); // after multiplied by calibrateSoundBurstDb\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 } else if (this.mode === 'filtered') {\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 } else {\r\n throw new Error('Mode is incorrect');\r\n }\r\n this.emit('update', {message: this.status});\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 console.log('sample size');\r\n console.log(this.sampleSize);\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, convolution) => {\r\n let checkRec = false;\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(convolution), // 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 checkRec\r\n );\r\n };\r\n\r\n bothSoundCheck = async stream => {\r\n let iir_ir_and_plots;\r\n this.#currentConvolution = this.componentConvolution;\r\n this.filteredMLSRange.component.Min = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.findMinValue)(this.#currentConvolution);\r\n this.filteredMLSRange.component.Max = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.findMaxValue)(this.#currentConvolution);\r\n this.addTimeStamp('Play MLS with component IIR');\r\n this.soundCheck = 'component';\r\n await this.playMLSwithIIR(stream, this.#currentConvolution);\r\n this.#stopCalibrationAudio();\r\n let component_conv_recs = this.getAllFilteredRecordedSignals();\r\n let return_component_conv_rec = component_conv_recs[component_conv_recs.length - 1];\r\n this.clearAllFilteredRecordedSignals();\r\n // await this.checkPowerVariation(return_component_conv_rec);\r\n this.numSuccessfulCaptured = 0;\r\n this.#currentConvolution = this.systemConvolution;\r\n this.filteredMLSRange.system.Min = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.findMinValue)(this.#currentConvolution);\r\n this.filteredMLSRange.system.Max = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.findMaxValue)(this.#currentConvolution);\r\n this.soundCheck = 'system';\r\n this.addTimeStamp('Play MLS with system IIR');\r\n await this.playMLSwithIIR(stream, this.#currentConvolution);\r\n\r\n this.#stopCalibrationAudio();\r\n\r\n let system_conv_recs = this.getAllFilteredRecordedSignals();\r\n let return_system_conv_rec = system_conv_recs[system_conv_recs.length - 1];\r\n // await this.checkPowerVariation(return_system_conv_rec);\r\n\r\n this.clearAllFilteredRecordedSignals();\r\n\r\n this.sourceAudioContext.close();\r\n let recs = this.getAllUnfilteredRecordedSignals();\r\n let unconv_rec = recs[0];\r\n let return_unconv_rec = unconv_rec;\r\n let conv_rec = component_conv_recs[component_conv_recs.length - 1];\r\n\r\n //psd of component\r\n let knownGain = this.oldComponentIR.Gain;\r\n let knownFreq = this.oldComponentIR.Freq;\r\n let sampleRate = this.sourceSamplingRate || 96000;\r\n this.addTimeStamp('Get PSD of mls recording');\r\n let component_unconv_rec_psd = 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 this.addTimeStamp('Get PSD of filtered recording (component)');\r\n let component_conv_rec_psd = 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\r\n conv_rec = system_conv_recs[system_conv_recs.length - 1];\r\n //psd of system\r\n this.addTimeStamp('Get PSD of filtered recording (system) and unfiltered recording');\r\n let system_recs_psd = 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\r\n //iir w/ and without bandpass psd. done\r\n unconv_rec = this.componentInvertedImpulseResponseNoBandpass;\r\n conv_rec = this.componentInvertedImpulseResponse;\r\n this.addTimeStamp('Get PSD of component iir and component iir no band pass');\r\n let component_iir_psd = 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 unconv_rec = this.systemInvertedImpulseResponseNoBandpass;\r\n conv_rec = this.systemInvertedImpulseResponse;\r\n this.addTimeStamp('Get PSD of system iir and system iir no band pass');\r\n let system_iir_psd = 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\r\n this.addTimeStamp('Get PSD of mls sequence');\r\n let mls_psd = await this.pyServerAPI\r\n .getMLSPSDWithRetry({mls: this.#mlsBufferView, sampleRate: this.sourceSamplingRate || 96000})\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 this.addTimeStamp('Get PSD of filered mls (system)');\r\n let system_filtered_mls_psd = await this.pyServerAPI\r\n .getMLSPSDWithRetry({\r\n mls: this.systemConvolution,\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\r\n this.addTimeStamp('Get PSD of filered mls (component)');\r\n let component_filtered_mls_psd = await this.pyServerAPI\r\n .getMLSPSDWithRetry({\r\n mls: this.componentConvolution,\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\r\n let gainValue = this.getGainDBSPL();\r\n\r\n iir_ir_and_plots = {\r\n filtered_recording: {\r\n component: return_component_conv_rec,\r\n system: return_system_conv_rec,\r\n },\r\n unfiltered_recording: this.getAllUnfilteredRecordedSignals()[0],\r\n system: {\r\n iir: this.systemInvertedImpulseResponse,\r\n ir: this.systemIR,\r\n iir_psd: {\r\n y: system_iir_psd['y_conv'],\r\n x: system_iir_psd['x_conv'],\r\n y_no_bandpass: system_iir_psd['y_unconv'],\r\n x_no_bandpass: system_iir_psd['x_unconv'],\r\n },\r\n filtered_mls_psd: {\r\n x: system_filtered_mls_psd['x_mls'],\r\n y: system_filtered_mls_psd['y_mls'],\r\n },\r\n convolution: this.systemConvolution,\r\n psd: {\r\n unconv: {\r\n x: system_recs_psd['x_unconv'],\r\n y: system_recs_psd['y_unconv'],\r\n },\r\n conv: {\r\n x: system_recs_psd['x_conv'],\r\n y: system_recs_psd['y_conv'],\r\n },\r\n },\r\n },\r\n component: {\r\n iir: this.componentInvertedImpulseResponse,\r\n ir: this.componentIR,\r\n ir_origin: this.componentIROrigin,\r\n ir_in_time_domain: this.componentIRInTimeDomain,\r\n iir_psd: {\r\n y: component_iir_psd['y_conv'],\r\n x: component_iir_psd['x_conv'],\r\n y_no_bandpass: component_iir_psd['y_unconv'],\r\n x_no_bandpass: component_iir_psd['x_unconv'],\r\n },\r\n filtered_mls_psd: {\r\n x: component_filtered_mls_psd['x_mls'],\r\n y: component_filtered_mls_psd['y_mls'],\r\n },\r\n convolution: this.componentConvolution,\r\n psd: {\r\n unconv: {\r\n x: component_unconv_rec_psd['x'],\r\n y: component_unconv_rec_psd['y'],\r\n },\r\n conv: {\r\n x: component_conv_rec_psd['x'],\r\n y: component_conv_rec_psd['y'],\r\n },\r\n },\r\n gainDBSPL: gainValue,\r\n },\r\n mls: this.#mlsBufferView,\r\n mls_psd: {\r\n x: mls_psd['x_mls'],\r\n y: mls_psd['y_mls'],\r\n },\r\n autocorrelations: this.autocorrelations,\r\n impulseResponses: [],\r\n };\r\n\r\n return iir_ir_and_plots;\r\n };\r\n\r\n singleSoundCheck = async stream => {\r\n let iir_ir_and_plots;\r\n if (this._calibrateSoundCheck != 'system') {\r\n this.#currentConvolution = this.componentConvolution;\r\n this.filteredMLSRange.component.Min = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.findMinValue)(this.#currentConvolution);\r\n this.filteredMLSRange.component.Max = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.findMaxValue)(this.#currentConvolution);\r\n this.addTimeStamp('Play MLS with component IIR');\r\n this.soundCheck = 'component';\r\n await this.playMLSwithIIR(stream, this.#currentConvolution);\r\n this.#stopCalibrationAudio();\r\n } else {\r\n this.#currentConvolution = this.systemConvolution;\r\n this.filteredMLSRange.system.Min = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.findMinValue)(this.#currentConvolution);\r\n this.filteredMLSRange.system.Max = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.findMaxValue)(this.#currentConvolution);\r\n this.addTimeStamp('Play MLS with system IIR');\r\n this.soundCheck = 'systen';\r\n await this.playMLSwithIIR(stream, this.#currentConvolution);\r\n this.#stopCalibrationAudio();\r\n }\r\n let conv_recs = this.getAllFilteredRecordedSignals();\r\n let recs = this.getAllUnfilteredRecordedSignals();\r\n this.clearAllFilteredRecordedSignals();\r\n console.log('Obtaining unfiltered recording from #allHzUnfilteredRecordings to calculate PSD');\r\n console.log('Obtaining filtered recording from #allHzFilteredRecordings to calculate PSD');\r\n let unconv_rec = recs[0];\r\n let return_unconv_rec = unconv_rec;\r\n let conv_rec = conv_recs[conv_recs.length - 1];\r\n let return_conv_rec = conv_rec;\r\n this.sourceAudioContext.close();\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 this.addTimeStamp('Get PSD of mls recording');\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 this.addTimeStamp('Get PSD recording of filtered recording (component)');\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\r\n unconv_rec = this.componentInvertedImpulseResponseNoBandpass;\r\n conv_rec = this.componentInvertedImpulseResponse;\r\n this.addTimeStamp('Get PSD of component iir and component iir no bandpass');\r\n let component_iir_psd = 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 unconv_rec = this.systemInvertedImpulseResponseNoBandpass;\r\n conv_rec = this.systemInvertedImpulseResponse;\r\n this.addTimeStamp('Get PSD of system iir and system iir no bandpass');\r\n let system_iir_psd = 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\r\n this.addTimeStamp('Get PSD of mls sequence');\r\n let mls_psd = await this.pyServerAPI\r\n .getMLSPSDWithRetry({\r\n mls: this.#mlsBufferView,\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\r\n this.addTimeStamp('Get PSD of filtered mls (component)');\r\n let filtered_mls_psd = await this.pyServerAPI\r\n .getMLSPSDWithRetry({\r\n mls: this.componentConvolution,\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\r\n let gainValue = this.getGainDBSPL();\r\n iir_ir_and_plots = {\r\n unfiltered_recording: return_unconv_rec,\r\n filtered_recording: return_conv_rec,\r\n system: {\r\n iir: this.systemInvertedImpulseResponse,\r\n ir: this.systemIR,\r\n iir_psd: {\r\n y: system_iir_psd['y_conv'],\r\n x: system_iir_psd['y_conv'],\r\n y_no_bandpass: system_iir_psd['y_unconv'],\r\n x_no_bandpass: system_iir_psd['x_unconv'],\r\n },\r\n filtered_recording: [],\r\n filtered_mls_psd: {},\r\n convolution: this.systemConvolution,\r\n psd: {\r\n unconv: {\r\n x: [],\r\n y: [],\r\n },\r\n conv: {\r\n x: [],\r\n y: [],\r\n },\r\n },\r\n },\r\n component: {\r\n iir: this.componentInvertedImpulseResponse,\r\n ir: this.componentIR,\r\n ir_origin: this.componentIROrigin,\r\n ir_in_time_domain: this.componentIRInTimeDomain,\r\n iir_psd: {\r\n y: component_iir_psd['y_conv'],\r\n x: component_iir_psd['x_conv'],\r\n y_no_bandpass: component_iir_psd['y_unconv'],\r\n x_no_bandpass: component_iir_psd['x_unconv'],\r\n },\r\n filtered_mls_psd: {\r\n x: filtered_mls_psd['x_mls'],\r\n y: filtered_mls_psd['y_mls'],\r\n },\r\n convolution: this.componentConvolution,\r\n psd: {\r\n unconv: {\r\n x: unconv_results['x'],\r\n y: unconv_results['y'],\r\n },\r\n conv: {\r\n x: conv_results['x'],\r\n y: conv_results['y'],\r\n },\r\n },\r\n gainDBSPL: gainValue,\r\n },\r\n mls: this.#mlsBufferView,\r\n mls_psd: {\r\n x: mls_psd['x_mls'],\r\n y: mls_psd['y_mls'],\r\n },\r\n autocorrelations: this.autocorrelations,\r\n impulseResponses: [],\r\n };\r\n } else {\r\n this.addTimeStamp('Get PSD of filtered recording (system) and unfiltered recording');\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\r\n //iir w/ and without bandpass psd\r\n unconv_rec = this.componentInvertedImpulseResponseNoBandpass;\r\n conv_rec = this.componentInvertedImpulseResponse;\r\n this.addTimeStamp('Get PSD of component iir and component iir no band pass');\r\n let component_iir_psd = 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 unconv_rec = this.systemInvertedImpulseResponseNoBandpass;\r\n conv_rec = this.systemInvertedImpulseResponse;\r\n this.addTimeStamp('Get PSD of system iir and system iir no band pass');\r\n let system_iir_psd = 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\r\n this.addTimeStamp('Get PSD of mls sequence');\r\n let mls_psd = await this.pyServerAPI\r\n .getMLSPSDWithRetry({\r\n mls: this.#mlsBufferView,\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\r\n this.addTimeStamp('Get PSD of filtered mls (system)');\r\n let filtered_mls_psd = await this.pyServerAPI\r\n .getMLSPSDWithRetry({\r\n mls: this.systemConvolution,\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\r\n let gainValue = this.getGainDBSPL();\r\n iir_ir_and_plots = {\r\n unfiltered_recording: return_unconv_rec,\r\n filtered_recording: return_conv_rec,\r\n system: {\r\n iir: this.systemInvertedImpulseResponse,\r\n ir: this.systemIR,\r\n iir_psd: {\r\n y: system_iir_psd['y_conv'],\r\n x: system_iir_psd['y_conv'],\r\n y_no_bandpass: system_iir_psd['y_unconv'],\r\n x_no_bandpass: system_iir_psd['x_unconv'],\r\n },\r\n filtered_recording: [],\r\n filtered_mls_psd: {\r\n x: filtered_mls_psd['x_mls'],\r\n y: filtered_mls_psd['y_mls'],\r\n },\r\n convolution: this.systemConvolution,\r\n psd: {\r\n unconv: {\r\n x: results['x_unconv'],\r\n y: results['y_unconv'],\r\n },\r\n conv: {\r\n x: results['x_conv'],\r\n y: results['y_conv'],\r\n },\r\n },\r\n },\r\n component: {\r\n iir: this.componentInvertedImpulseResponse,\r\n ir: this.componentIR,\r\n ir_origin: this.componentIROrigin,\r\n ir_in_time_domain: this.componentIRInTimeDomain,\r\n iir_psd: {\r\n y: component_iir_psd['y_conv'],\r\n x: component_iir_psd['x_conv'],\r\n y_no_bandpass: component_iir_psd['y_unconv'],\r\n x_no_bandpass: component_iir_psd['x_unconv'],\r\n },\r\n filtered_mls_psd: {},\r\n convolution: this.componentConvolution,\r\n psd: {\r\n unconv: {\r\n x: [],\r\n y: [],\r\n },\r\n conv: {\r\n x: [],\r\n y: [],\r\n },\r\n },\r\n gainDBSPL: gainValue,\r\n },\r\n mls: this.#mlsBufferView,\r\n mls_psd: {\r\n x: mls_psd['x_mls'],\r\n y: mls_psd['y_mls'],\r\n },\r\n autocorrelations: this.autocorrelations,\r\n impulseResponses: [],\r\n };\r\n }\r\n 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 iir_ir_and_plots['impulseResponses'].push(res[i]);\r\n }\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\r\n return iir_ir_and_plots;\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 let checkRec = 'allhz';\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 const calibrateSoundBurstDb = this._calibrateSoundBurstDb;\r\n this.addTimeStamp('Get MLS sequence');\r\n await this.pyServerAPI\r\n .getMLSWithRetry({length, calibrateSoundBurstDb})\r\n .then(res => {\r\n console.log(res);\r\n this.#mlsBufferView = res['mls'];\r\n this.#mls = res['unscaledMLS'];\r\n })\r\n .catch(err => {\r\n // this.emit('InvertedImpulseResponse', {res: false});\r\n console.error(err);\r\n });\r\n this.numSuccessfulBackgroundCaptured = 0;\r\n if (this._calibrateSoundBackgroundSecs > 0) {\r\n this.mode = 'background';\r\n this.status =\r\n `All Hz Calibration: sampling the background noise...`.toString() +\r\n this.generateTemplate().toString();\r\n this.emit('update', {message: this.status});\r\n await this.recordBackground(\r\n stream, //stream\r\n () => this.numSuccessfulBackgroundCaptured < 1, //loop condition\r\n this.#awaitBackgroundNoiseRecording, //sleep to record\r\n this.sendBackgroundRecording, //send to get PSD\r\n this.mode,\r\n checkRec\r\n );\r\n this.incrementStatusBar();\r\n }\r\n this.mode = 'unfiltered';\r\n this.numSuccessfulCaptured = 0;\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 checkRec\r\n ),\r\n this.#stopCalibrationAudio();\r\n checkRec = false;\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 //do single check\r\n if (this._calibrateSoundCheck == 'goal' || this._calibrateSoundCheck == 'system') {\r\n iir_ir_and_plots = await this.singleSoundCheck(stream);\r\n } else {\r\n //both\r\n iir_ir_and_plots = await this.bothSoundCheck(stream);\r\n }\r\n } else {\r\n let unconv_rec = this.componentInvertedImpulseResponseNoBandpass;\r\n let conv_rec = this.componentInvertedImpulseResponse;\r\n let component_iir_psd = 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 unconv_rec = this.systemInvertedImpulseResponseNoBandpass;\r\n conv_rec = this.systemInvertedImpulseResponse;\r\n let system_iir_psd = 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\r\n let gainValue = this.getGainDBSPL();\r\n iir_ir_and_plots = {\r\n unfiltered_recording: return_unconv_rec,\r\n filtered_recording: return_conv_rec,\r\n system: {\r\n iir: this.systemInvertedImpulseResponse,\r\n ir: this.systemIR,\r\n iir_psd: {\r\n y: system_iir_psd['y_conv'],\r\n x: system_iir_psd['y_conv'],\r\n y_no_bandpass: system_iir_psd['y_unconv'],\r\n x_no_bandpass: system_iir_psd['x_unconv'],\r\n },\r\n filtered_recording: [],\r\n convolution: this.systemConvolution,\r\n psd: {\r\n unconv: {\r\n x: [],\r\n y: [],\r\n },\r\n conv: {\r\n x: [],\r\n y: [],\r\n },\r\n },\r\n },\r\n component: {\r\n iir: this.componentInvertedImpulseResponse,\r\n ir: this.componentIR,\r\n ir_in_time_domain: this.componentIRInTimeDomain,\r\n iir_psd: {\r\n y: component_iir_psd['y_conv'],\r\n x: component_iir_psd['x_conv'],\r\n y_no_bandpass: component_iir_psd['y_unconv'],\r\n x_no_bandpass: component_iir_psd['x_unconv'],\r\n },\r\n convolution: this.componentConvolution,\r\n psd: {\r\n unconv: {\r\n x: [],\r\n y: [],\r\n },\r\n conv: {\r\n x: [],\r\n y: [],\r\n },\r\n },\r\n gainDBSPL: gainValue,\r\n },\r\n mls: this.#mlsBufferView,\r\n autocorrelations: this.autocorrelations,\r\n impulseResponses: [],\r\n };\r\n 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 iir_ir_and_plots['impulseResponses'].push(res[i]);\r\n }\r\n }\r\n });\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 // saveToJSON(iir_ir_and_plots);\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.getLastVolumeRecordedSignal().slice(start, end));\r\n console.log(\r\n 'Obtaining last 1000 hz recording from #allVolumeRecordings to send for processing'\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.getAllVolumeRecordedSignals()\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.addTimeStamp('Send volume data to server');\r\n let left = this.calibrateSound1000HzPreSec;\r\n let right = this.calibrateSound1000HzPreSec + this.calibrateSound1000HzSec;\r\n this.pyServerAPI\r\n .getVolumeCalibration({\r\n sampleRate: this.sourceSamplingRate,\r\n payload: this.#getTruncatedSignal(left, right),\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 this.pyServerAPI\r\n .volumePowerCheck({\r\n payload: this.getLastVolumeRecordedSignal(),\r\n sampleRate: this.sourceSamplingRate || 96000,\r\n binDesiredSec: this._calibrateSoundPowerBinDesiredSec,\r\n preSec: this.calibrateSound1000HzPreSec,\r\n Sec: this.calibrateSound1000HzSec,\r\n })\r\n .then(res => {\r\n if (res && res['sd'] < this._calibrateSoundPowerDbSDToleratedDb) {\r\n this.recordingChecks['volume'][this.inDB] = res;\r\n }\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 let checkRec = false;\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.inDB = soundLevelToDiscard;\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 this.startTime = new Date().getTime();\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 checkRec\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 if (i == trialIterations - 1) {\r\n checkRec = 'loudest';\r\n }\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 this.inDB = inDB;\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 checkRec\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 this.addTimeStamp('Get Volume Calibration Parameters');\r\n\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 writeFrqGainToFirestore = async (speakerID, frq, gain, OEM, documentID) => {\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 // update Microphone/OEM/speakerID/default/linear\r\n const docRef = (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.doc)(_config_firebase__WEBPACK_IMPORTED_MODULE_2__[\"default\"], 'Microphone', OEM, speakerID, documentID);\r\n await (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.updateDoc)(docRef, {\r\n linear: data,\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 readFrqGainFromFirestore = async (speakerID, OEM, documentID) => {\r\n const docRef = (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.doc)(_config_firebase__WEBPACK_IMPORTED_MODULE_2__[\"default\"], 'Microphone', OEM, speakerID, documentID);\r\n\r\n const docSnap = await (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.getDoc)(docRef);\r\n\r\n if (docSnap.exists()) {\r\n return docSnap.data().linear;\r\n } else {\r\n return null;\r\n }\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 readGainat1000HzFromFirestore = async (speakerID, OEM, documentID) => {\r\n const docRef = (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.doc)(_config_firebase__WEBPACK_IMPORTED_MODULE_2__[\"default\"], 'Microphone', OEM, speakerID, documentID);\r\n const docSnap = await (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.getDoc)(docRef);\r\n\r\n if (docSnap.exists()) {\r\n return docSnap.data().Gain1000;\r\n } else {\r\n return null;\r\n }\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 writeGainat1000HzToFirestore = async (speakerID, gain, OEM, documentID) => {\r\n const docRef = (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.doc)(_config_firebase__WEBPACK_IMPORTED_MODULE_2__[\"default\"], 'Microphone', OEM, speakerID, documentID);\r\n\r\n await (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.updateDoc)(docRef, {\r\n Gain1000: gain,\r\n });\r\n };\r\n\r\n writeGainat1000Hz = async (speakerID, gain, OEM) => {\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 writeIsSmartPhoneToFirestore = async (speakerID, isSmartPhone, OEM) => {\r\n // if Microphone/OEM/speakerID/default exists, leave it alone and create a new document at Microphone/OEM/speakerID and return the id of the new document\r\n const OEMdocRef = (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.doc)(_config_firebase__WEBPACK_IMPORTED_MODULE_2__[\"default\"], 'Microphone', OEM);\r\n const OEMdocSnap = await (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.getDoc)(OEMdocRef);\r\n // if OEM does not exist, create it with dummy field\r\n if (!OEMdocSnap.exists()) {\r\n await (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.setDoc)(OEMdocRef, {dummy: 'dummy'});\r\n }\r\n // save the collectionIDs in the OEM document as a field. If the field already exists, add the new collectionID to the array\r\n await (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.updateDoc)(OEMdocRef, {\r\n collectionIDs: (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.arrayUnion)(speakerID),\r\n });\r\n const docRef = (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.doc)(_config_firebase__WEBPACK_IMPORTED_MODULE_2__[\"default\"], 'Microphone', OEM, speakerID, 'default');\r\n const docSnap = await (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.getDoc)(docRef);\r\n\r\n if (docSnap.exists()) {\r\n // add new document\r\n const collectionRef = (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.collection)(_config_firebase__WEBPACK_IMPORTED_MODULE_2__[\"default\"], 'Microphone', OEM, speakerID);\r\n // add the new document and return the id\r\n const docRef = await (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.addDoc)(collectionRef, {isSmartPhone: isSmartPhone});\r\n return docRef.id;\r\n } else {\r\n // create document at Microphone/OEM/speakerID/default\r\n await (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.setDoc)(docRef, {isSmartPhone: isSmartPhone});\r\n return 'default';\r\n }\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 writeMicrophoneInfoToFirestore = async (speakerID, micInfo, OEM, documentID) => {\r\n const docRef = (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.doc)(_config_firebase__WEBPACK_IMPORTED_MODULE_2__[\"default\"], 'Microphone', OEM, speakerID, documentID);\r\n await (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.updateDoc)(docRef, {\r\n info: micInfo,\r\n });\r\n };\r\n\r\n doesMicrophoneExistInFirestore = async (speakerID, OEM, documentID) => {\r\n const docRef = (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.doc)(_config_firebase__WEBPACK_IMPORTED_MODULE_2__[\"default\"], 'Microphone', OEM, speakerID, documentID);\r\n const docSnap = await (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.getDoc)(docRef);\r\n if (docSnap.exists()) {\r\n return true;\r\n }\r\n return false;\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 addMicrophoneInfo = async (speakerID, OEM, micInfo) => {\r\n // add to database if /info does not exist\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}/info`));\r\n if (!snapshot.exists()) {\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}/info`), micInfo);\r\n }\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 // add time stamp\r\n addTimeStamp = taskName => {\r\n let startTaskTime = (new Date().getTime() - this.startTime) / 1000;\r\n this.timeStamp.push(`SOUND ${Number(startTaskTime.toFixed(1))} s. ${taskName}`);\r\n };\r\n\r\n checkPowerVariation = async () => {\r\n const recordings = this.getAllFilteredRecordedSignals();\r\n const rec = recordings[recordings.length - 1];\r\n console.log(rec);\r\n await this.pyServerAPI\r\n .allHzPowerCheck({\r\n payload: rec,\r\n sampleRate: this.sourceSamplingRate || 96000,\r\n binDesiredSec: this._calibrateSoundPowerBinDesiredSec,\r\n burstSec: this.desired_time_per_mls,\r\n })\r\n .then(result => {\r\n if (result) {\r\n this.recordingChecks[this.soundCheck].push(result);\r\n if (result['sd'] > this._calibrateSoundPowerDbSDToleratedDb) {\r\n console.log('filtered recording sd too high');\r\n } else {\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 getGainDBSPL = () => {\r\n var freqIndex = this.componentIR.Freq.indexOf(1000);\r\n\r\n // If freqIndex is not -1 (meaning 1000 is found in the freq array)\r\n if (freqIndex !== -1) {\r\n // Get the corresponding gain value using the index\r\n var gainValue = this.componentIR.Gain[freqIndex];\r\n return gainValue;\r\n } else {\r\n console.log('Freq 1000 not found in the array.');\r\n return null;\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 _calibrateSoundBurstDb = 0.1,\r\n _calibrateSoundBurstRepeats = 3,\r\n _calibrateSoundBurstSec = 1,\r\n _calibrateSoundBurstsWarmup = 0,\r\n _calibrateSoundHz = 48000,\r\n _calibrateSoundIIRSec = 0.2,\r\n _calibrateSoundIRSec = 0.2,\r\n calibrateSound1000HzPreSec = 3.5,\r\n calibrateSound1000HzSec = 1.0,\r\n calibrateSound1000HzPostSec = 0.5,\r\n _calibrateSoundBackgroundSecs = 0,\r\n _calibrateSoundSmoothOctaves = 0.33,\r\n _calibrateSoundPowerBinDesiredSec = 0.2,\r\n _calibrateSoundPowerDbSDToleratedDb = 1,\r\n micManufacturer = '',\r\n micSerialNumber = '',\r\n micModelNumber = '',\r\n micModelName = '',\r\n calibrateMicrophonesBool,\r\n authorEmails,\r\n webAudioDeviceNames = {loudspeaker: '', microphone: '', microphoneText: 'xxxXXX'},\r\n userIDs\r\n ) => {\r\n this._calibrateSoundBurstDb = _calibrateSoundBurstDb;\r\n this.CALIBRATION_TONE_DURATION =\r\n calibrateSound1000HzPreSec + calibrateSound1000HzSec + calibrateSound1000HzPostSec;\r\n this.calibrateSound1000HzPreSec = calibrateSound1000HzPreSec;\r\n this.calibrateSound1000HzSec = calibrateSound1000HzSec;\r\n this.calibrateSound1000HzPostSec = calibrateSound1000HzPostSec;\r\n this.iirLength = Math.floor(_calibrateSoundIIRSec * this.sourceSamplingRate);\r\n this.irLength = Math.floor(_calibrateSoundIRSec * this.sourceSamplingRate);\r\n console.log('device info:', this.deviceInfo);\r\n this.numMLSPerCapture = _calibrateSoundBurstRepeats + 1;\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 this._calibrateSoundBackgroundSecs = _calibrateSoundBackgroundSecs;\r\n this._calibrateSoundSmoothOctaves = _calibrateSoundSmoothOctaves;\r\n this._calibrateSoundPowerBinDesiredSec = _calibrateSoundPowerBinDesiredSec;\r\n this._calibrateSoundPowerDbSDToleratedDb = _calibrateSoundPowerDbSDToleratedDb;\r\n this.webAudioDeviceNames = webAudioDeviceNames;\r\n if (isSmartPhone) this.webAudioDeviceNames.microphone = this.deviceInfo.microphoneFromAPI;\r\n this.webAudioDeviceNames.microphoneText = this.webAudioDeviceNames.microphoneText\r\n .replace('xxx', this.webAudioDeviceNames.microphone)\r\n .replace('XXX', this.webAudioDeviceNames.microphone);\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\r\n ? micModelName === 'umik-1' || micModelName === 'umik-2'\r\n ? 'minidsp'\r\n : this.deviceInfo.OEM.toLowerCase().split(' ').join('')\r\n : micManufacturer;\r\n // const ID = \"712-5669\";\r\n // const OEM = \"minidsp\";\r\n const micInfo = {\r\n micModelName: isSmartPhone ? micModelName : microphoneName,\r\n OEM: isSmartPhone\r\n ? micModelName === 'umik-1' || micModelName === 'umik-2'\r\n ? 'miniDSP'\r\n : this.deviceInfo.OEM\r\n : micManufacturer,\r\n ID: ID,\r\n createDate: new Date(),\r\n DateText: (0,_utils__WEBPACK_IMPORTED_MODULE_1__.getCurrentTimeString)(),\r\n HardwareName: isSmartPhone ? this.deviceInfo.hardwarename : microphoneName,\r\n hardwareFamily: isSmartPhone ? this.deviceInfo.hardwarefamily : microphoneName,\r\n HardwareModel: isSmartPhone ? this.deviceInfo.hardwaremodel : microphoneName,\r\n PlatformName: isSmartPhone ? this.deviceInfo.platformname : 'N/A',\r\n PlatformVersion: isSmartPhone ? this.deviceInfo.platformversion : 'N/A',\r\n DeviceType: isSmartPhone ? this.deviceInfo.devicetype : 'N/A',\r\n ID_from_51Degrees: isSmartPhone ? this.deviceInfo.DeviceId : 'N/A',\r\n calibrateMicrophonesBool: calibrateMicrophonesBool,\r\n webAudioDeviceNames: {\r\n loudspeaker: this.webAudioDeviceNames.loudspeaker,\r\n microphone: this.webAudioDeviceNames.microphone,\r\n },\r\n userIDs: userIDs,\r\n };\r\n if (calibrateMicrophonesBool) {\r\n micInfo['authorEmails'] = authorEmails;\r\n }\r\n // if undefined in micInfo, set to empty string\r\n for (const [key, value] of Object.entries(micInfo)) {\r\n if (value === undefined) {\r\n micInfo[key] = '';\r\n }\r\n }\r\n\r\n // this.writeMicrophoneInfoToFirestore(ID, micInfo, OEM, 'default');\r\n // this.addMicrophoneInfo(ID, OEM, micInfo);\r\n if (componentIR == null) {\r\n //mode 'ir'\r\n //global variable this.componentIR must be set\r\n this.componentIR = await this.readFrqGainFromFirestore(ID, OEM, 'default').then(data => {\r\n return data;\r\n });\r\n // 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 lCalib = await this.readGainat1000HzFromFirestore(ID, OEM, 'default');\r\n micInfo['gainDBSPL'] = lCalib;\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.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 impulseResponseResults['background_noise'] = this.background_noise;\r\n if (componentIR != null) {\r\n // I corrected microphone/loudpeaker IR scale in easyeyes, \r\n // but since we write microphone IR to firestore here\r\n // we need to correct microphone IR here \r\n let correctGain = Math.round(\r\n (volumeResults.parameters.gainDBSPL -\r\n this.componentGainDBSPL) *\r\n 10\r\n ) / 10;\r\n\r\n let IrFreq = impulseResponseResults?.component.ir.Freq.map((freq) => Math.round(freq));\r\n let IrGain = impulseResponseResults?.component?.ir.Gain;\r\n const IrGainAt1000Hz = IrGain[IrFreq.findIndex((freq) => freq === 1000)];\r\n const difference = Math.round(10 * (IrGainAt1000Hz - correctGain)) / 10;\r\n IrGain = IrGain.map((gain) => gain - difference);\r\n const id = await this.writeIsSmartPhoneToFirestore(ID, isSmartPhone, OEM);\r\n await this.writeMicrophoneInfoToFirestore(ID, micInfo, OEM, id);\r\n await this.writeFrqGainToFirestore(\r\n ID,\r\n IrFreq,\r\n IrGain,\r\n OEM,\r\n id\r\n );\r\n micInfo['gainDBSPL'] = impulseResponseResults.component.gainDBSPL;\r\n await this.writeGainat1000HzToFirestore(ID, micInfo['gainDBSPL'], OEM, id);\r\n // await this.writeGainat1000Hz(ID, micInfo['gainDBSPL'], OEM);\r\n }\r\n const total_results = {...volumeResults, ...impulseResponseResults};\r\n total_results['filteredMLSRange'] = this.filteredMLSRange;\r\n total_results['micInfo'] = micInfo;\r\n total_results['audioInfo'] = {};\r\n total_results['audioInfo']['sinkSampleRate'] = this.sinkSamplingRate;\r\n total_results['audioInfo']['sourceSampleRate'] = this.sourceSamplingRate;\r\n total_results['audioInfo']['bitsPerSample'] = this.sampleSize;\r\n const timeStampresult = [...this.timeStamp].join('\\n');\r\n total_results['timeStamps'] = timeStampresult;\r\n total_results['recordingChecks'] = this.recordingChecks;\r\n total_results['component']['phase'] = this.componentIRPhase;\r\n total_results['system']['phase'] = this.systemIRPhase;\r\n console.log('total results');\r\n console.log(total_results);\r\n console.log('Time Stamps');\r\n console.log(timeStampresult);\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?");
854
+ 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/* harmony import */ var firebase_firestore__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! firebase/firestore */ \"./node_modules/firebase/firestore/dist/esm/index.esm.js\");\n\r\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 = 2] - 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 = 2,\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 componentIROrigin = {\r\n Freq: [],\r\n Gain: [],\r\n };\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 CALIBRATION_TONE_DURATION = 5; // seconds\r\n calibrateSound1000HzPreSec = 3.5;\r\n calibrateSound1000HzSec = 1.0;\r\n calibrateSound1000HzPostSec = 0.5;\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 irLength = 0;\r\n\r\n componentInvertedImpulseResponseNoBandpass = [];\r\n\r\n componentIRInTimeDomain = [];\r\n\r\n systemInvertedImpulseResponseNoBandpass = [];\r\n\r\n _calibrateSoundBackgroundSecs;\r\n\r\n _calibrateSoundSmoothOctaves;\r\n\r\n background_noise = {};\r\n\r\n numSuccessfulBackgroundCaptured;\r\n\r\n _calibrateSoundBurstDb;\r\n\r\n componentIRPhase = [];\r\n\r\n systemIRPhase = [];\r\n\r\n webAudioDeviceNames = {loudspeaker: '', microphone: '', loudspeakerText: '', microphoneText: ''};\r\n\r\n recordingChecks = {\r\n volume: {},\r\n unfiltered: [],\r\n system: [],\r\n component: [],\r\n };\r\n\r\n inDB;\r\n\r\n soundCheck = '';\r\n\r\n filteredMLSRange = {\r\n component: {\r\n Min: null,\r\n Max: null,\r\n },\r\n system: {\r\n Min: null,\r\n Max: null,\r\n },\r\n };\r\n\r\n /** @private */\r\n timeStamp = [];\r\n\r\n /** @private */\r\n startTime;\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 reportWebAudioNames = `<br>${this.webAudioDeviceNames.loudspeakerText} <br> ${this.webAudioDeviceNames.microphoneText}`;\r\n const reportParameters = `<br> Sampling: Loudspeaker ${this.sourceSamplingRate} Hz, Microphone ${this.sinkSamplingRate} Hz, ${this.sampleSize} bits`;\r\n const template = `<div style=\"display: flex; justify-content: center; margin-top:12px;\"><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 reportWebAudioNames + reportParameters + 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 this.addTimeStamp('Get system iir');\r\n const computedIRs = await Promise.all(this.impulseResponses);\r\n const filteredComputedIRs = computedIRs.filter(element => {\r\n return element != undefined;\r\n }); //log any errors that are found in this step\r\n const mls = this.#mls;\r\n const lowHz = this.#lowHz; //gain of 1 below cutoff, need gain of 0\r\n const highHz = this.#highHz; //check error for anything other than 10 kHz\r\n const iirLength = this.iirLength;\r\n const num_periods = this.numMLSPerCapture + this.num_mls_to_skip;\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 num_periods,\r\n sampleRate: this.sourceSamplingRate || 96000,\r\n calibrateSoundBurstDb: this._calibrateSoundBurstDb,\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 this.systemInvertedImpulseResponseNoBandpass = res['iirNoBandpass'];\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 this.addTimeStamp('Get component iir');\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 irLength = this.irLength;\r\n const num_periods = this.numMLSPerCapture + this.num_mls_to_skip;\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 num_periods,\r\n sampleRate: this.sourceSamplingRate || 96000,\r\n calibrateSoundBurstDb: this._calibrateSoundBurstDb,\r\n irLength,\r\n calibrateSoundSmoothOctaves: this._calibrateSoundSmoothOctaves,\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.componentIRPhase = res['component_angle'];\r\n this.systemIRPhase = res['system_angle'];\r\n this.componentIROrigin['Freq'] = res['frequencies'];\r\n this.componentIROrigin['Gain'] = res['irOrigin'];\r\n this.componentConvolution = res['convolution'];\r\n this.componentInvertedImpulseResponseNoBandpass = res['iirNoBandpass'];\r\n this.componentIRInTimeDomain = res['irTime'];\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 sendBackgroundRecording = () => {\r\n const allSignals = this.getAllBackgroundRecordings();\r\n const numSignals = allSignals.length;\r\n const background_rec_whole = allSignals[numSignals - 1];\r\n const fraction = 0.5 / (this._calibrateSoundBackgroundSecs + 0.5);\r\n // Calculate the starting index for slicing the array\r\n const startIndex = Math.round(fraction * background_rec_whole.length);\r\n // Slice the array from the calculated start index to the end of the array\r\n const background_rec = background_rec_whole.slice(startIndex);\r\n console.log('Sending background recording to server for processing');\r\n this.addTimeStamp('Get background PSD');\r\n this.pyServerAPI\r\n .getBackgroundNoisePSDWithRetry({\r\n background_rec,\r\n sampleRate: this.sourceSamplingRate || 96000,\r\n })\r\n .then(res => {\r\n if (this.numSuccessfulBackgroundCaptured < 1) {\r\n this.numSuccessfulBackgroundCaptured += 1;\r\n //storing all background data in background_psd object\r\n this.background_noise['x_background'] = res['x_background'];\r\n this.background_noise['y_background'] = res['y_background'];\r\n this.background_noise['recording'] = background_rec;\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 * .\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 = async signalCsv => {\r\n const allSignals = this.getAllUnfilteredRecordedSignals();\r\n console.log(\r\n 'Obtaining last all hz unfiltered recording from #allHzUnfilteredRecordings to send to server for processing'\r\n );\r\n const numSignals = allSignals.length;\r\n const mls = this.#mlsBufferView;\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 await this.pyServerAPI\r\n .allHzPowerCheck({\r\n payload,\r\n sampleRate: this.sourceSamplingRate || 96000,\r\n binDesiredSec: this._calibrateSoundPowerBinDesiredSec,\r\n burstSec: this.desired_time_per_mls,\r\n })\r\n .then(result => {\r\n if (result) {\r\n this.recordingChecks['unfiltered'].push(result);\r\n if (result['sd'] < this._calibrateSoundPowerDbSDToleratedDb) {\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, //get rid of this\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 } else if (result['sd'] > this._calibrateSoundPowerDbSDToleratedDb) {\r\n this.clearLastUnfilteredRecordedSignals();\r\n console.log('unfiltered rec', this.getAllUnfilteredRecordedSignals.length);\r\n }\r\n }\r\n })\r\n .catch(err => {\r\n console.error(err);\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 //unfiltered\r\n time_to_wait = (this.#mls.length / this.sourceSamplingRate) * this.numMLSPerCapture;\r\n time_to_wait = time_to_wait * 1.1;\r\n } else if (this.mode === 'filtered') {\r\n //filtered\r\n // time_to_wait =\r\n // (this.#currentConvolution.length / this.sourceSamplingRate) *\r\n // (this.numMLSPerCapture / (this.num_mls_to_skip + this.numMLSPerCapture));\r\n time_to_wait =\r\n (this.#currentConvolution.length / this.sourceSamplingRate) * this.numMLSPerCapture;\r\n time_to_wait = time_to_wait * 1.1;\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);\r\n };\r\n\r\n /**\r\n * Passed to the background noise recording function, awaits the desired amount of seconds to capture the desired number\r\n * of seconds of background noise\r\n *\r\n * @example\r\n */\r\n #awaitBackgroundNoiseRecording = async () => {\r\n console.log(\r\n 'Waiting ' + this._calibrateSoundBackgroundSecs + ' second(s) to record background noise'\r\n );\r\n let time_to_wait = this._calibrateSoundBackgroundSecs + 0.5;\r\n await (0,_utils__WEBPACK_IMPORTED_MODULE_1__.sleep)(time_to_wait);\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) * number_of_bursts_to_skip;\r\n } else if (this.mode === 'filtered') {\r\n console.log(this.#currentConvolution.length);\r\n // time_to_sleep =\r\n // (this.#currentConvolution.length / this.sourceSamplingRate) *\r\n // (number_of_bursts_to_skip / (number_of_bursts_to_skip + this.numMLSPerCapture));\r\n time_to_sleep =\r\n (this.#currentConvolution.length / this.sourceSamplingRate) * number_of_bursts_to_skip;\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 = async () => {\r\n await this.checkPowerVariation();\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('length databuffer');\r\n console.log(dataBuffer.length);\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\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];\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\r\n if (this.mode === 'filtered') {\r\n //used to not loop filtered\r\n this.sourceNode.loop = true;\r\n } else {\r\n this.sourceNode.loop = true;\r\n }\r\n\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 * 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 this.addTimeStamp('Play unfiltered mls');\r\n this.calibrationNodes[0].start(0);\r\n this.status = ``;\r\n if (this.mode === 'unfiltered') {\r\n console.log('mls', this.#mls); // before multiplied by calibrateSoundBurstDb\r\n console.log('mls buffer view', this.#mlsBufferView); // after multiplied by calibrateSoundBurstDb\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 } else if (this.mode === 'filtered') {\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 } else {\r\n throw new Error('Mode is incorrect');\r\n }\r\n this.emit('update', {message: this.status});\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 console.log('sample size');\r\n console.log(this.sampleSize);\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, convolution) => {\r\n let checkRec = false;\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(convolution), // 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 checkRec\r\n );\r\n };\r\n\r\n bothSoundCheck = async stream => {\r\n let iir_ir_and_plots;\r\n this.#currentConvolution = this.componentConvolution;\r\n this.filteredMLSRange.component.Min = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.findMinValue)(this.#currentConvolution);\r\n this.filteredMLSRange.component.Max = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.findMaxValue)(this.#currentConvolution);\r\n this.addTimeStamp('Play MLS with component IIR');\r\n this.soundCheck = 'component';\r\n await this.playMLSwithIIR(stream, this.#currentConvolution);\r\n this.#stopCalibrationAudio();\r\n let component_conv_recs = this.getAllFilteredRecordedSignals();\r\n let return_component_conv_rec = component_conv_recs[component_conv_recs.length - 1];\r\n this.clearAllFilteredRecordedSignals();\r\n // await this.checkPowerVariation(return_component_conv_rec);\r\n this.numSuccessfulCaptured = 0;\r\n this.#currentConvolution = this.systemConvolution;\r\n this.filteredMLSRange.system.Min = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.findMinValue)(this.#currentConvolution);\r\n this.filteredMLSRange.system.Max = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.findMaxValue)(this.#currentConvolution);\r\n this.soundCheck = 'system';\r\n this.addTimeStamp('Play MLS with system IIR');\r\n await this.playMLSwithIIR(stream, this.#currentConvolution);\r\n\r\n this.#stopCalibrationAudio();\r\n\r\n let system_conv_recs = this.getAllFilteredRecordedSignals();\r\n let return_system_conv_rec = system_conv_recs[system_conv_recs.length - 1];\r\n // await this.checkPowerVariation(return_system_conv_rec);\r\n\r\n this.clearAllFilteredRecordedSignals();\r\n\r\n this.sourceAudioContext.close();\r\n let recs = this.getAllUnfilteredRecordedSignals();\r\n let unconv_rec = recs[0];\r\n let return_unconv_rec = unconv_rec;\r\n let conv_rec = component_conv_recs[component_conv_recs.length - 1];\r\n\r\n //psd of component\r\n let knownGain = this.oldComponentIR.Gain;\r\n let knownFreq = this.oldComponentIR.Freq;\r\n let sampleRate = this.sourceSamplingRate || 96000;\r\n this.addTimeStamp('Get PSD of mls recording');\r\n let component_unconv_rec_psd = 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 this.addTimeStamp('Get PSD of filtered recording (component)');\r\n let component_conv_rec_psd = 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\r\n conv_rec = system_conv_recs[system_conv_recs.length - 1];\r\n //psd of system\r\n this.addTimeStamp('Get PSD of filtered recording (system) and unfiltered recording');\r\n let system_recs_psd = 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\r\n //iir w/ and without bandpass psd. done\r\n unconv_rec = this.componentInvertedImpulseResponseNoBandpass;\r\n conv_rec = this.componentInvertedImpulseResponse;\r\n this.addTimeStamp('Get PSD of component iir and component iir no band pass');\r\n let component_iir_psd = 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 unconv_rec = this.systemInvertedImpulseResponseNoBandpass;\r\n conv_rec = this.systemInvertedImpulseResponse;\r\n this.addTimeStamp('Get PSD of system iir and system iir no band pass');\r\n let system_iir_psd = 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\r\n this.addTimeStamp('Get PSD of mls sequence');\r\n let mls_psd = await this.pyServerAPI\r\n .getMLSPSDWithRetry({mls: this.#mlsBufferView, sampleRate: this.sourceSamplingRate || 96000})\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 this.addTimeStamp('Get PSD of filered mls (system)');\r\n let system_filtered_mls_psd = await this.pyServerAPI\r\n .getMLSPSDWithRetry({\r\n mls: this.systemConvolution,\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\r\n this.addTimeStamp('Get PSD of filered mls (component)');\r\n let component_filtered_mls_psd = await this.pyServerAPI\r\n .getMLSPSDWithRetry({\r\n mls: this.componentConvolution,\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\r\n let gainValue = this.getGainDBSPL();\r\n\r\n iir_ir_and_plots = {\r\n filtered_recording: {\r\n component: return_component_conv_rec,\r\n system: return_system_conv_rec,\r\n },\r\n unfiltered_recording: this.getAllUnfilteredRecordedSignals()[0],\r\n system: {\r\n iir: this.systemInvertedImpulseResponse,\r\n ir: this.systemIR,\r\n iir_psd: {\r\n y: system_iir_psd['y_conv'],\r\n x: system_iir_psd['x_conv'],\r\n y_no_bandpass: system_iir_psd['y_unconv'],\r\n x_no_bandpass: system_iir_psd['x_unconv'],\r\n },\r\n filtered_mls_psd: {\r\n x: system_filtered_mls_psd['x_mls'],\r\n y: system_filtered_mls_psd['y_mls'],\r\n },\r\n convolution: this.systemConvolution,\r\n psd: {\r\n unconv: {\r\n x: system_recs_psd['x_unconv'],\r\n y: system_recs_psd['y_unconv'],\r\n },\r\n conv: {\r\n x: system_recs_psd['x_conv'],\r\n y: system_recs_psd['y_conv'],\r\n },\r\n },\r\n },\r\n component: {\r\n iir: this.componentInvertedImpulseResponse,\r\n ir: this.componentIR,\r\n ir_origin: this.componentIROrigin,\r\n ir_in_time_domain: this.componentIRInTimeDomain,\r\n iir_psd: {\r\n y: component_iir_psd['y_conv'],\r\n x: component_iir_psd['x_conv'],\r\n y_no_bandpass: component_iir_psd['y_unconv'],\r\n x_no_bandpass: component_iir_psd['x_unconv'],\r\n },\r\n filtered_mls_psd: {\r\n x: component_filtered_mls_psd['x_mls'],\r\n y: component_filtered_mls_psd['y_mls'],\r\n },\r\n convolution: this.componentConvolution,\r\n psd: {\r\n unconv: {\r\n x: component_unconv_rec_psd['x'],\r\n y: component_unconv_rec_psd['y'],\r\n },\r\n conv: {\r\n x: component_conv_rec_psd['x'],\r\n y: component_conv_rec_psd['y'],\r\n },\r\n },\r\n gainDBSPL: gainValue,\r\n },\r\n mls: this.#mlsBufferView,\r\n mls_psd: {\r\n x: mls_psd['x_mls'],\r\n y: mls_psd['y_mls'],\r\n },\r\n autocorrelations: this.autocorrelations,\r\n impulseResponses: [],\r\n };\r\n\r\n return iir_ir_and_plots;\r\n };\r\n\r\n singleSoundCheck = async stream => {\r\n let iir_ir_and_plots;\r\n if (this._calibrateSoundCheck != 'system') {\r\n this.#currentConvolution = this.componentConvolution;\r\n this.filteredMLSRange.component.Min = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.findMinValue)(this.#currentConvolution);\r\n this.filteredMLSRange.component.Max = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.findMaxValue)(this.#currentConvolution);\r\n this.addTimeStamp('Play MLS with component IIR');\r\n this.soundCheck = 'component';\r\n await this.playMLSwithIIR(stream, this.#currentConvolution);\r\n this.#stopCalibrationAudio();\r\n } else {\r\n this.#currentConvolution = this.systemConvolution;\r\n this.filteredMLSRange.system.Min = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.findMinValue)(this.#currentConvolution);\r\n this.filteredMLSRange.system.Max = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.findMaxValue)(this.#currentConvolution);\r\n this.addTimeStamp('Play MLS with system IIR');\r\n this.soundCheck = 'systen';\r\n await this.playMLSwithIIR(stream, this.#currentConvolution);\r\n this.#stopCalibrationAudio();\r\n }\r\n let conv_recs = this.getAllFilteredRecordedSignals();\r\n let recs = this.getAllUnfilteredRecordedSignals();\r\n this.clearAllFilteredRecordedSignals();\r\n console.log('Obtaining unfiltered recording from #allHzUnfilteredRecordings to calculate PSD');\r\n console.log('Obtaining filtered recording from #allHzFilteredRecordings to calculate PSD');\r\n let unconv_rec = recs[0];\r\n let return_unconv_rec = unconv_rec;\r\n let conv_rec = conv_recs[conv_recs.length - 1];\r\n let return_conv_rec = conv_rec;\r\n this.sourceAudioContext.close();\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 this.addTimeStamp('Get PSD of mls recording');\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 this.addTimeStamp('Get PSD recording of filtered recording (component)');\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\r\n unconv_rec = this.componentInvertedImpulseResponseNoBandpass;\r\n conv_rec = this.componentInvertedImpulseResponse;\r\n this.addTimeStamp('Get PSD of component iir and component iir no bandpass');\r\n let component_iir_psd = 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 unconv_rec = this.systemInvertedImpulseResponseNoBandpass;\r\n conv_rec = this.systemInvertedImpulseResponse;\r\n this.addTimeStamp('Get PSD of system iir and system iir no bandpass');\r\n let system_iir_psd = 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\r\n this.addTimeStamp('Get PSD of mls sequence');\r\n let mls_psd = await this.pyServerAPI\r\n .getMLSPSDWithRetry({\r\n mls: this.#mlsBufferView,\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\r\n this.addTimeStamp('Get PSD of filtered mls (component)');\r\n let filtered_mls_psd = await this.pyServerAPI\r\n .getMLSPSDWithRetry({\r\n mls: this.componentConvolution,\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\r\n let gainValue = this.getGainDBSPL();\r\n iir_ir_and_plots = {\r\n unfiltered_recording: return_unconv_rec,\r\n filtered_recording: return_conv_rec,\r\n system: {\r\n iir: this.systemInvertedImpulseResponse,\r\n ir: this.systemIR,\r\n iir_psd: {\r\n y: system_iir_psd['y_conv'],\r\n x: system_iir_psd['y_conv'],\r\n y_no_bandpass: system_iir_psd['y_unconv'],\r\n x_no_bandpass: system_iir_psd['x_unconv'],\r\n },\r\n filtered_recording: [],\r\n filtered_mls_psd: {},\r\n convolution: this.systemConvolution,\r\n psd: {\r\n unconv: {\r\n x: [],\r\n y: [],\r\n },\r\n conv: {\r\n x: [],\r\n y: [],\r\n },\r\n },\r\n },\r\n component: {\r\n iir: this.componentInvertedImpulseResponse,\r\n ir: this.componentIR,\r\n ir_origin: this.componentIROrigin,\r\n ir_in_time_domain: this.componentIRInTimeDomain,\r\n iir_psd: {\r\n y: component_iir_psd['y_conv'],\r\n x: component_iir_psd['x_conv'],\r\n y_no_bandpass: component_iir_psd['y_unconv'],\r\n x_no_bandpass: component_iir_psd['x_unconv'],\r\n },\r\n filtered_mls_psd: {\r\n x: filtered_mls_psd['x_mls'],\r\n y: filtered_mls_psd['y_mls'],\r\n },\r\n convolution: this.componentConvolution,\r\n psd: {\r\n unconv: {\r\n x: unconv_results['x'],\r\n y: unconv_results['y'],\r\n },\r\n conv: {\r\n x: conv_results['x'],\r\n y: conv_results['y'],\r\n },\r\n },\r\n gainDBSPL: gainValue,\r\n },\r\n mls: this.#mlsBufferView,\r\n mls_psd: {\r\n x: mls_psd['x_mls'],\r\n y: mls_psd['y_mls'],\r\n },\r\n autocorrelations: this.autocorrelations,\r\n impulseResponses: [],\r\n };\r\n } else {\r\n this.addTimeStamp('Get PSD of filtered recording (system) and unfiltered recording');\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\r\n //iir w/ and without bandpass psd\r\n unconv_rec = this.componentInvertedImpulseResponseNoBandpass;\r\n conv_rec = this.componentInvertedImpulseResponse;\r\n this.addTimeStamp('Get PSD of component iir and component iir no band pass');\r\n let component_iir_psd = 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 unconv_rec = this.systemInvertedImpulseResponseNoBandpass;\r\n conv_rec = this.systemInvertedImpulseResponse;\r\n this.addTimeStamp('Get PSD of system iir and system iir no band pass');\r\n let system_iir_psd = 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\r\n this.addTimeStamp('Get PSD of mls sequence');\r\n let mls_psd = await this.pyServerAPI\r\n .getMLSPSDWithRetry({\r\n mls: this.#mlsBufferView,\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\r\n this.addTimeStamp('Get PSD of filtered mls (system)');\r\n let filtered_mls_psd = await this.pyServerAPI\r\n .getMLSPSDWithRetry({\r\n mls: this.systemConvolution,\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\r\n let gainValue = this.getGainDBSPL();\r\n iir_ir_and_plots = {\r\n unfiltered_recording: return_unconv_rec,\r\n filtered_recording: return_conv_rec,\r\n system: {\r\n iir: this.systemInvertedImpulseResponse,\r\n ir: this.systemIR,\r\n iir_psd: {\r\n y: system_iir_psd['y_conv'],\r\n x: system_iir_psd['y_conv'],\r\n y_no_bandpass: system_iir_psd['y_unconv'],\r\n x_no_bandpass: system_iir_psd['x_unconv'],\r\n },\r\n filtered_recording: [],\r\n filtered_mls_psd: {\r\n x: filtered_mls_psd['x_mls'],\r\n y: filtered_mls_psd['y_mls'],\r\n },\r\n convolution: this.systemConvolution,\r\n psd: {\r\n unconv: {\r\n x: results['x_unconv'],\r\n y: results['y_unconv'],\r\n },\r\n conv: {\r\n x: results['x_conv'],\r\n y: results['y_conv'],\r\n },\r\n },\r\n },\r\n component: {\r\n iir: this.componentInvertedImpulseResponse,\r\n ir: this.componentIR,\r\n ir_origin: this.componentIROrigin,\r\n ir_in_time_domain: this.componentIRInTimeDomain,\r\n iir_psd: {\r\n y: component_iir_psd['y_conv'],\r\n x: component_iir_psd['x_conv'],\r\n y_no_bandpass: component_iir_psd['y_unconv'],\r\n x_no_bandpass: component_iir_psd['x_unconv'],\r\n },\r\n filtered_mls_psd: {},\r\n convolution: this.componentConvolution,\r\n psd: {\r\n unconv: {\r\n x: [],\r\n y: [],\r\n },\r\n conv: {\r\n x: [],\r\n y: [],\r\n },\r\n },\r\n gainDBSPL: gainValue,\r\n },\r\n mls: this.#mlsBufferView,\r\n mls_psd: {\r\n x: mls_psd['x_mls'],\r\n y: mls_psd['y_mls'],\r\n },\r\n autocorrelations: this.autocorrelations,\r\n impulseResponses: [],\r\n };\r\n }\r\n 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 iir_ir_and_plots['impulseResponses'].push(res[i]);\r\n }\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\r\n return iir_ir_and_plots;\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 let checkRec = 'allhz';\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 const calibrateSoundBurstDb = this._calibrateSoundBurstDb;\r\n this.addTimeStamp('Get MLS sequence');\r\n await this.pyServerAPI\r\n .getMLSWithRetry({length, calibrateSoundBurstDb})\r\n .then(res => {\r\n console.log(res);\r\n this.#mlsBufferView = res['mls'];\r\n this.#mls = res['unscaledMLS'];\r\n })\r\n .catch(err => {\r\n // this.emit('InvertedImpulseResponse', {res: false});\r\n console.error(err);\r\n });\r\n this.numSuccessfulBackgroundCaptured = 0;\r\n if (this._calibrateSoundBackgroundSecs > 0) {\r\n this.mode = 'background';\r\n this.status =\r\n `All Hz Calibration: sampling the background noise...`.toString() +\r\n this.generateTemplate().toString();\r\n this.emit('update', {message: this.status});\r\n await this.recordBackground(\r\n stream, //stream\r\n () => this.numSuccessfulBackgroundCaptured < 1, //loop condition\r\n this.#awaitBackgroundNoiseRecording, //sleep to record\r\n this.sendBackgroundRecording, //send to get PSD\r\n this.mode,\r\n checkRec\r\n );\r\n this.incrementStatusBar();\r\n }\r\n this.mode = 'unfiltered';\r\n this.numSuccessfulCaptured = 0;\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 checkRec\r\n ),\r\n this.#stopCalibrationAudio();\r\n checkRec = false;\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 //do single check\r\n if (this._calibrateSoundCheck == 'goal' || this._calibrateSoundCheck == 'system') {\r\n iir_ir_and_plots = await this.singleSoundCheck(stream);\r\n } else {\r\n //both\r\n iir_ir_and_plots = await this.bothSoundCheck(stream);\r\n }\r\n } else {\r\n let unconv_rec = this.componentInvertedImpulseResponseNoBandpass;\r\n let conv_rec = this.componentInvertedImpulseResponse;\r\n let component_iir_psd = 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 unconv_rec = this.systemInvertedImpulseResponseNoBandpass;\r\n conv_rec = this.systemInvertedImpulseResponse;\r\n let system_iir_psd = 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\r\n let gainValue = this.getGainDBSPL();\r\n iir_ir_and_plots = {\r\n unfiltered_recording: return_unconv_rec,\r\n filtered_recording: return_conv_rec,\r\n system: {\r\n iir: this.systemInvertedImpulseResponse,\r\n ir: this.systemIR,\r\n iir_psd: {\r\n y: system_iir_psd['y_conv'],\r\n x: system_iir_psd['y_conv'],\r\n y_no_bandpass: system_iir_psd['y_unconv'],\r\n x_no_bandpass: system_iir_psd['x_unconv'],\r\n },\r\n filtered_recording: [],\r\n convolution: this.systemConvolution,\r\n psd: {\r\n unconv: {\r\n x: [],\r\n y: [],\r\n },\r\n conv: {\r\n x: [],\r\n y: [],\r\n },\r\n },\r\n },\r\n component: {\r\n iir: this.componentInvertedImpulseResponse,\r\n ir: this.componentIR,\r\n ir_in_time_domain: this.componentIRInTimeDomain,\r\n iir_psd: {\r\n y: component_iir_psd['y_conv'],\r\n x: component_iir_psd['x_conv'],\r\n y_no_bandpass: component_iir_psd['y_unconv'],\r\n x_no_bandpass: component_iir_psd['x_unconv'],\r\n },\r\n convolution: this.componentConvolution,\r\n psd: {\r\n unconv: {\r\n x: [],\r\n y: [],\r\n },\r\n conv: {\r\n x: [],\r\n y: [],\r\n },\r\n },\r\n gainDBSPL: gainValue,\r\n },\r\n mls: this.#mlsBufferView,\r\n autocorrelations: this.autocorrelations,\r\n impulseResponses: [],\r\n };\r\n 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 iir_ir_and_plots['impulseResponses'].push(res[i]);\r\n }\r\n }\r\n });\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 // saveToJSON(iir_ir_and_plots);\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.getLastVolumeRecordedSignal().slice(start, end));\r\n console.log(\r\n 'Obtaining last 1000 hz recording from #allVolumeRecordings to send for processing'\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.getAllVolumeRecordedSignals()\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.addTimeStamp('Send volume data to server');\r\n let left = this.calibrateSound1000HzPreSec;\r\n let right = this.calibrateSound1000HzPreSec + this.calibrateSound1000HzSec;\r\n this.pyServerAPI\r\n .getVolumeCalibration({\r\n sampleRate: this.sourceSamplingRate,\r\n payload: this.#getTruncatedSignal(left, right),\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 this.pyServerAPI\r\n .volumePowerCheck({\r\n payload: this.getLastVolumeRecordedSignal(),\r\n sampleRate: this.sourceSamplingRate || 96000,\r\n binDesiredSec: this._calibrateSoundPowerBinDesiredSec,\r\n preSec: this.calibrateSound1000HzPreSec,\r\n Sec: this.calibrateSound1000HzSec,\r\n })\r\n .then(res => {\r\n if (res && res['sd'] < this._calibrateSoundPowerDbSDToleratedDb) {\r\n this.recordingChecks['volume'][this.inDB] = res;\r\n }\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 let checkRec = false;\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.inDB = soundLevelToDiscard;\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 this.startTime = new Date().getTime();\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 checkRec\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 if (i == trialIterations - 1) {\r\n checkRec = 'loudest';\r\n }\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 this.inDB = inDB;\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 checkRec\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 this.addTimeStamp('Get Volume Calibration Parameters');\r\n\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 writeFrqGainToFirestore = async (speakerID, frq, gain, OEM, documentID) => {\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 const docRef = (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.doc)(_config_firebase__WEBPACK_IMPORTED_MODULE_2__[\"default\"], 'Microphones', documentID);\r\n await (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.updateDoc)(docRef, {\r\n linear: data,\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 readFrqGainFromFirestore = async (speakerID, OEM, isDefault) => {\r\n const collectionRef = (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.collection)(_config_firebase__WEBPACK_IMPORTED_MODULE_2__[\"default\"], 'Microphones');\r\n const q = (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.query)(\r\n collectionRef,\r\n (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.where)('ID', '==', speakerID),\r\n (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.where)('lowercaseOEM', '==', OEM),\r\n (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.where)('isDefault', '==', isDefault)\r\n );\r\n const querySnapshot = await (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.getDocs)(q);\r\n // if exists return the linear field of the first document\r\n if (querySnapshot.size > 0) {\r\n return querySnapshot.docs[0].data().linear;\r\n }\r\n return null;\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 readGainat1000HzFromFirestore = async (speakerID, OEM, isDefault) => {\r\n const collectionRef = (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.collection)(_config_firebase__WEBPACK_IMPORTED_MODULE_2__[\"default\"], 'Microphones');\r\n const q = (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.query)(\r\n collectionRef,\r\n (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.where)('ID', '==', speakerID),\r\n (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.where)('lowercaseOEM', '==', OEM),\r\n (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.where)('isDefault', '==', isDefault)\r\n );\r\n const querySnapshot = await (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.getDocs)(q);\r\n // if exists return the Gain1000 field of the first document\r\n if (querySnapshot.size > 0) {\r\n return querySnapshot.docs[0].data().Gain1000;\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 writeGainat1000HzToFirestore = async (speakerID, gain, OEM, documentID) => {\r\n const docRef = (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.doc)(_config_firebase__WEBPACK_IMPORTED_MODULE_2__[\"default\"], 'Microphones', documentID);\r\n await (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.updateDoc)(docRef, {\r\n Gain1000: gain,\r\n });\r\n };\r\n\r\n writeGainat1000Hz = async (speakerID, gain, OEM) => {\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 writeIsSmartPhoneToFirestore = async (speakerID, isSmartPhone, OEM) => {\r\n const collectionRef = (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.collection)(_config_firebase__WEBPACK_IMPORTED_MODULE_2__[\"default\"], 'Microphones');\r\n const q = (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.query)(\r\n collectionRef,\r\n (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.where)('ID', '==', speakerID),\r\n (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.where)('lowercaseOEM', '==', OEM),\r\n (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.where)('isDefault', '==', true)\r\n );\r\n const querySnapshot = await (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.getDocs)(q);\r\n if (querySnapshot.size > 0) {\r\n const docRef = await (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.addDoc)(collectionRef, {isSmartPhone: isSmartPhone, isDefault: false});\r\n return docRef.id;\r\n } else {\r\n const docRef = await (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.addDoc)(collectionRef, {isSmartPhone: isSmartPhone, isDefault: true});\r\n return docRef.id;\r\n }\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 writeMicrophoneInfoToFirestore = async (speakerID, micInfo, OEM, documentID) => {\r\n const docRef = (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.doc)(_config_firebase__WEBPACK_IMPORTED_MODULE_2__[\"default\"], 'Microphones', documentID);\r\n await (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.setDoc)(docRef, micInfo, {merge: true});\r\n };\r\n\r\n doesMicrophoneExistInFirestore = async (speakerID, OEM, documentID) => {\r\n const docRef = (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.doc)(_config_firebase__WEBPACK_IMPORTED_MODULE_2__[\"default\"], 'Microphone', OEM, speakerID, documentID);\r\n const docSnap = await (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.getDoc)(docRef);\r\n if (docSnap.exists()) {\r\n return true;\r\n }\r\n return false;\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 addMicrophoneInfo = async (speakerID, OEM, micInfo) => {\r\n // add to database if /info does not exist\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}/info`));\r\n if (!snapshot.exists()) {\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}/info`), micInfo);\r\n }\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 // add time stamp\r\n addTimeStamp = taskName => {\r\n let startTaskTime = (new Date().getTime() - this.startTime) / 1000;\r\n this.timeStamp.push(`SOUND ${Number(startTaskTime.toFixed(1))} s. ${taskName}`);\r\n };\r\n\r\n checkPowerVariation = async () => {\r\n const recordings = this.getAllFilteredRecordedSignals();\r\n const rec = recordings[recordings.length - 1];\r\n console.log(rec);\r\n await this.pyServerAPI\r\n .allHzPowerCheck({\r\n payload: rec,\r\n sampleRate: this.sourceSamplingRate || 96000,\r\n binDesiredSec: this._calibrateSoundPowerBinDesiredSec,\r\n burstSec: this.desired_time_per_mls,\r\n })\r\n .then(result => {\r\n if (result) {\r\n this.recordingChecks[this.soundCheck].push(result);\r\n if (result['sd'] > this._calibrateSoundPowerDbSDToleratedDb) {\r\n console.log('filtered recording sd too high');\r\n } else {\r\n if (this.numSuccessfulCaptured < 1) {\r\n this.numSuccessfulCaptured += 1;\r\n this.stepNum += 1;\r\n this.incrementStatusBar();\r\n console.log(\r\n 'after mls w iir record for some reason add numSucc capt ' + this.stepNum\r\n );\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 getGainDBSPL = () => {\r\n var freqIndex = this.componentIR.Freq.indexOf(1000);\r\n\r\n // If freqIndex is not -1 (meaning 1000 is found in the freq array)\r\n if (freqIndex !== -1) {\r\n // Get the corresponding gain value using the index\r\n var gainValue = this.componentIR.Gain[freqIndex];\r\n return gainValue;\r\n } else {\r\n console.log('Freq 1000 not found in the array.');\r\n return null;\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 _calibrateSoundBurstDb = 0.1,\r\n _calibrateSoundBurstRepeats = 3,\r\n _calibrateSoundBurstSec = 1,\r\n _calibrateSoundBurstsWarmup = 0,\r\n _calibrateSoundHz = 48000,\r\n _calibrateSoundIIRSec = 0.2,\r\n _calibrateSoundIRSec = 0.2,\r\n calibrateSound1000HzPreSec = 3.5,\r\n calibrateSound1000HzSec = 1.0,\r\n calibrateSound1000HzPostSec = 0.5,\r\n _calibrateSoundBackgroundSecs = 0,\r\n _calibrateSoundSmoothOctaves = 0.33,\r\n _calibrateSoundPowerBinDesiredSec = 0.2,\r\n _calibrateSoundPowerDbSDToleratedDb = 1,\r\n micManufacturer = '',\r\n micSerialNumber = '',\r\n micModelNumber = '',\r\n micModelName = '',\r\n calibrateMicrophonesBool,\r\n authorEmails,\r\n webAudioDeviceNames = {loudspeaker: '', microphone: '', microphoneText: 'xxxXXX'},\r\n userIDs\r\n ) => {\r\n this._calibrateSoundBurstDb = _calibrateSoundBurstDb;\r\n this.CALIBRATION_TONE_DURATION =\r\n calibrateSound1000HzPreSec + calibrateSound1000HzSec + calibrateSound1000HzPostSec;\r\n this.calibrateSound1000HzPreSec = calibrateSound1000HzPreSec;\r\n this.calibrateSound1000HzSec = calibrateSound1000HzSec;\r\n this.calibrateSound1000HzPostSec = calibrateSound1000HzPostSec;\r\n this.iirLength = Math.floor(_calibrateSoundIIRSec * this.sourceSamplingRate);\r\n this.irLength = Math.floor(_calibrateSoundIRSec * this.sourceSamplingRate);\r\n console.log('device info:', this.deviceInfo);\r\n this.numMLSPerCapture = _calibrateSoundBurstRepeats + 1;\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 this._calibrateSoundBackgroundSecs = _calibrateSoundBackgroundSecs;\r\n this._calibrateSoundSmoothOctaves = _calibrateSoundSmoothOctaves;\r\n this._calibrateSoundPowerBinDesiredSec = _calibrateSoundPowerBinDesiredSec;\r\n this._calibrateSoundPowerDbSDToleratedDb = _calibrateSoundPowerDbSDToleratedDb;\r\n this.webAudioDeviceNames = webAudioDeviceNames;\r\n if (isSmartPhone) this.webAudioDeviceNames.microphone = this.deviceInfo.microphoneFromAPI;\r\n this.webAudioDeviceNames.microphoneText = this.webAudioDeviceNames.microphoneText\r\n .replace('xxx', this.webAudioDeviceNames.microphone)\r\n .replace('XXX', this.webAudioDeviceNames.microphone);\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\r\n ? micModelName === 'umik-1' || micModelName === 'umik-2'\r\n ? 'minidsp'\r\n : this.deviceInfo.OEM.toLowerCase().split(' ').join('')\r\n : micManufacturer;\r\n // const ID = \"712-5669\";\r\n // const OEM = \"minidsp\";\r\n const micInfo = {\r\n micModelName: isSmartPhone ? micModelName : microphoneName,\r\n OEM: isSmartPhone\r\n ? micModelName === 'umik-1' || micModelName === 'umik-2'\r\n ? 'miniDSP'\r\n : this.deviceInfo.OEM\r\n : micManufacturer,\r\n ID: ID,\r\n createDate: new Date(),\r\n DateText: (0,_utils__WEBPACK_IMPORTED_MODULE_1__.getCurrentTimeString)(),\r\n HardwareName: isSmartPhone ? this.deviceInfo.hardwarename : microphoneName,\r\n hardwareFamily: isSmartPhone ? this.deviceInfo.hardwarefamily : microphoneName,\r\n HardwareModel: isSmartPhone ? this.deviceInfo.hardwaremodel : microphoneName,\r\n PlatformName: isSmartPhone ? this.deviceInfo.platformname : 'N/A',\r\n PlatformVersion: isSmartPhone ? this.deviceInfo.platformversion : 'N/A',\r\n DeviceType: isSmartPhone ? this.deviceInfo.devicetype : 'N/A',\r\n ID_from_51Degrees: isSmartPhone ? this.deviceInfo.DeviceId : 'N/A',\r\n calibrateMicrophonesBool: calibrateMicrophonesBool,\r\n webAudioDeviceNames: {\r\n loudspeaker: this.webAudioDeviceNames.loudspeaker,\r\n microphone: this.webAudioDeviceNames.microphone,\r\n },\r\n userIDs: userIDs,\r\n };\r\n if (calibrateMicrophonesBool) {\r\n micInfo['authorEmails'] = authorEmails;\r\n }\r\n // if undefined in micInfo, set to empty string\r\n for (const [key, value] of Object.entries(micInfo)) {\r\n if (value === undefined) {\r\n micInfo[key] = '';\r\n }\r\n }\r\n\r\n // this.writeMicrophoneInfoToFirestore(ID, micInfo, OEM, 'default');\r\n // this.addMicrophoneInfo(ID, OEM, micInfo);\r\n if (componentIR == null) {\r\n //mode 'ir'\r\n //global variable this.componentIR must be set\r\n this.componentIR = await this.readFrqGainFromFirestore(ID, OEM, true).then(data => {\r\n return data;\r\n });\r\n // 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 lCalib = await this.readGainat1000HzFromFirestore(ID, OEM, true);\r\n micInfo['gainDBSPL'] = lCalib;\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.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 impulseResponseResults['background_noise'] = this.background_noise;\r\n if (componentIR != null) {\r\n // I corrected microphone/loudpeaker IR scale in easyeyes,\r\n // but since we write microphone IR to firestore here\r\n // we need to correct microphone IR here\r\n let correctGain =\r\n Math.round((volumeResults.parameters.gainDBSPL - this.componentGainDBSPL) * 10) / 10;\r\n\r\n let IrFreq = impulseResponseResults?.component.ir.Freq.map(freq => Math.round(freq));\r\n let IrGain = impulseResponseResults?.component?.ir.Gain;\r\n const IrGainAt1000Hz = IrGain[IrFreq.findIndex(freq => freq === 1000)];\r\n const difference = Math.round(10 * (IrGainAt1000Hz - correctGain)) / 10;\r\n IrGain = IrGain.map(gain => gain - difference);\r\n const id = await this.writeIsSmartPhoneToFirestore(ID, isSmartPhone, OEM);\r\n await this.writeMicrophoneInfoToFirestore(ID, micInfo, OEM, id);\r\n await this.writeFrqGainToFirestore(ID, IrFreq, IrGain, OEM, id);\r\n micInfo['gainDBSPL'] = impulseResponseResults.component.gainDBSPL;\r\n await this.writeGainat1000HzToFirestore(ID, micInfo['gainDBSPL'], OEM, id);\r\n // await this.writeGainat1000Hz(ID, micInfo['gainDBSPL'], OEM);\r\n }\r\n const total_results = {...volumeResults, ...impulseResponseResults};\r\n total_results['filteredMLSRange'] = this.filteredMLSRange;\r\n total_results['micInfo'] = micInfo;\r\n total_results['audioInfo'] = {};\r\n total_results['audioInfo']['sinkSampleRate'] = this.sinkSamplingRate;\r\n total_results['audioInfo']['sourceSampleRate'] = this.sourceSamplingRate;\r\n total_results['audioInfo']['bitsPerSample'] = this.sampleSize;\r\n const timeStampresult = [...this.timeStamp].join('\\n');\r\n total_results['timeStamps'] = timeStampresult;\r\n total_results['recordingChecks'] = this.recordingChecks;\r\n total_results['component']['phase'] = this.componentIRPhase;\r\n total_results['system']['phase'] = this.systemIRPhase;\r\n console.log('total results');\r\n console.log(total_results);\r\n console.log('Time Stamps');\r\n console.log(timeStampresult);\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?");
855
855
 
856
856
  /***/ }),
857
857
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "speaker-calibration",
3
- "version": "2.2.106",
3
+ "version": "2.2.107",
4
4
  "description": "Speaker calibration library for auditory testing",
5
5
  "main": "dist/main.js",
6
6
  "directories": {
@@ -1,9 +1,28 @@
1
1
  import AudioCalibrator from '../audioCalibrator';
2
2
 
3
- import {sleep, csvToArray, saveToCSV, saveToJSON, findMinValue, findMaxValue, getCurrentTimeString} from '../../utils';
3
+ import {
4
+ sleep,
5
+ csvToArray,
6
+ saveToCSV,
7
+ saveToJSON,
8
+ findMinValue,
9
+ findMaxValue,
10
+ getCurrentTimeString,
11
+ } from '../../utils';
4
12
  import database from '../../config/firebase';
5
13
  import {ref, set, get, child} from 'firebase/database';
6
- import {doc, getDoc, collection, addDoc, updateDoc, setDoc, arrayUnion} from 'firebase/firestore';
14
+ import {
15
+ doc,
16
+ getDoc,
17
+ collection,
18
+ addDoc,
19
+ updateDoc,
20
+ setDoc,
21
+ arrayUnion,
22
+ getDocs,
23
+ query,
24
+ where,
25
+ } from 'firebase/firestore';
7
26
 
8
27
  /**
9
28
  *
@@ -358,7 +377,7 @@ class Combination extends AudioCalibrator {
358
377
  this.componentIR['Gain'] = res['ir'];
359
378
  this.componentIR['Freq'] = res['frequencies'];
360
379
  this.componentIRPhase = res['component_angle'];
361
- this.systemIRPhase = res['system_angle']
380
+ this.systemIRPhase = res['system_angle'];
362
381
  this.componentIROrigin['Freq'] = res['frequencies'];
363
382
  this.componentIROrigin['Gain'] = res['irOrigin'];
364
383
  this.componentConvolution = res['convolution'];
@@ -1876,8 +1895,8 @@ class Combination extends AudioCalibrator {
1876
1895
  }
1877
1896
 
1878
1897
  const data = {Freq: sampledFrq, Gain: sampledGain};
1879
- // update Microphone/OEM/speakerID/default/linear
1880
- const docRef = doc(database, 'Microphone', OEM, speakerID, documentID);
1898
+
1899
+ const docRef = doc(database, 'Microphones', documentID);
1881
1900
  await updateDoc(docRef, {
1882
1901
  linear: data,
1883
1902
  });
@@ -1900,16 +1919,20 @@ class Combination extends AudioCalibrator {
1900
1919
 
1901
1920
  // Function to Read frq and gain from firebase database given speakerID
1902
1921
  // returns an array of frq and gain if speakerID exists, returns null otherwise
1903
- readFrqGainFromFirestore = async (speakerID, OEM, documentID) => {
1904
- const docRef = doc(database, 'Microphone', OEM, speakerID, documentID);
1905
-
1906
- const docSnap = await getDoc(docRef);
1907
-
1908
- if (docSnap.exists()) {
1909
- return docSnap.data().linear;
1910
- } else {
1911
- return null;
1922
+ readFrqGainFromFirestore = async (speakerID, OEM, isDefault) => {
1923
+ const collectionRef = collection(database, 'Microphones');
1924
+ const q = query(
1925
+ collectionRef,
1926
+ where('ID', '==', speakerID),
1927
+ where('lowercaseOEM', '==', OEM),
1928
+ where('isDefault', '==', isDefault)
1929
+ );
1930
+ const querySnapshot = await getDocs(q);
1931
+ // if exists return the linear field of the first document
1932
+ if (querySnapshot.size > 0) {
1933
+ return querySnapshot.docs[0].data().linear;
1912
1934
  }
1935
+ return null;
1913
1936
  };
1914
1937
  readFrqGain = async (speakerID, OEM) => {
1915
1938
  const dbRef = ref(database);
@@ -1919,15 +1942,20 @@ class Combination extends AudioCalibrator {
1919
1942
  }
1920
1943
  return null;
1921
1944
  };
1922
- readGainat1000HzFromFirestore = async (speakerID, OEM, documentID) => {
1923
- const docRef = doc(database, 'Microphone', OEM, speakerID, documentID);
1924
- const docSnap = await getDoc(docRef);
1925
-
1926
- if (docSnap.exists()) {
1927
- return docSnap.data().Gain1000;
1928
- } else {
1929
- return null;
1945
+ readGainat1000HzFromFirestore = async (speakerID, OEM, isDefault) => {
1946
+ const collectionRef = collection(database, 'Microphones');
1947
+ const q = query(
1948
+ collectionRef,
1949
+ where('ID', '==', speakerID),
1950
+ where('lowercaseOEM', '==', OEM),
1951
+ where('isDefault', '==', isDefault)
1952
+ );
1953
+ const querySnapshot = await getDocs(q);
1954
+ // if exists return the Gain1000 field of the first document
1955
+ if (querySnapshot.size > 0) {
1956
+ return querySnapshot.docs[0].data().Gain1000;
1930
1957
  }
1958
+ return null;
1931
1959
  };
1932
1960
 
1933
1961
  readGainat1000Hz = async (speakerID, OEM) => {
@@ -1940,8 +1968,7 @@ class Combination extends AudioCalibrator {
1940
1968
  };
1941
1969
 
1942
1970
  writeGainat1000HzToFirestore = async (speakerID, gain, OEM, documentID) => {
1943
- const docRef = doc(database, 'Microphone', OEM, speakerID, documentID);
1944
-
1971
+ const docRef = doc(database, 'Microphones', documentID);
1945
1972
  await updateDoc(docRef, {
1946
1973
  Gain1000: gain,
1947
1974
  });
@@ -1952,30 +1979,20 @@ class Combination extends AudioCalibrator {
1952
1979
  };
1953
1980
 
1954
1981
  writeIsSmartPhoneToFirestore = async (speakerID, isSmartPhone, OEM) => {
1955
- // if Microphone/OEM/speakerID/default exists, leave it alone and create a new document at Microphone/OEM/speakerID and return the id of the new document
1956
- const OEMdocRef = doc(database, 'Microphone', OEM);
1957
- const OEMdocSnap = await getDoc(OEMdocRef);
1958
- // if OEM does not exist, create it with dummy field
1959
- if (!OEMdocSnap.exists()) {
1960
- await setDoc(OEMdocRef, {dummy: 'dummy'});
1961
- }
1962
- // save the collectionIDs in the OEM document as a field. If the field already exists, add the new collectionID to the array
1963
- await updateDoc(OEMdocRef, {
1964
- collectionIDs: arrayUnion(speakerID),
1965
- });
1966
- const docRef = doc(database, 'Microphone', OEM, speakerID, 'default');
1967
- const docSnap = await getDoc(docRef);
1968
-
1969
- if (docSnap.exists()) {
1970
- // add new document
1971
- const collectionRef = collection(database, 'Microphone', OEM, speakerID);
1972
- // add the new document and return the id
1973
- const docRef = await addDoc(collectionRef, {isSmartPhone: isSmartPhone});
1982
+ const collectionRef = collection(database, 'Microphones');
1983
+ const q = query(
1984
+ collectionRef,
1985
+ where('ID', '==', speakerID),
1986
+ where('lowercaseOEM', '==', OEM),
1987
+ where('isDefault', '==', true)
1988
+ );
1989
+ const querySnapshot = await getDocs(q);
1990
+ if (querySnapshot.size > 0) {
1991
+ const docRef = await addDoc(collectionRef, {isSmartPhone: isSmartPhone, isDefault: false});
1974
1992
  return docRef.id;
1975
1993
  } else {
1976
- // create document at Microphone/OEM/speakerID/default
1977
- await setDoc(docRef, {isSmartPhone: isSmartPhone});
1978
- return 'default';
1994
+ const docRef = await addDoc(collectionRef, {isSmartPhone: isSmartPhone, isDefault: true});
1995
+ return docRef.id;
1979
1996
  }
1980
1997
  };
1981
1998
 
@@ -1985,10 +2002,8 @@ class Combination extends AudioCalibrator {
1985
2002
  };
1986
2003
 
1987
2004
  writeMicrophoneInfoToFirestore = async (speakerID, micInfo, OEM, documentID) => {
1988
- const docRef = doc(database, 'Microphone', OEM, speakerID, documentID);
1989
- await updateDoc(docRef, {
1990
- info: micInfo,
1991
- });
2005
+ const docRef = doc(database, 'Microphones', documentID);
2006
+ await setDoc(docRef, micInfo, {merge: true});
1992
2007
  };
1993
2008
 
1994
2009
  doesMicrophoneExistInFirestore = async (speakerID, OEM, documentID) => {
@@ -2069,23 +2084,25 @@ class Combination extends AudioCalibrator {
2069
2084
  .then(result => {
2070
2085
  if (result) {
2071
2086
  this.recordingChecks[this.soundCheck].push(result);
2072
- if (result['sd'] > this._calibrateSoundPowerDbSDToleratedDb) {
2073
- console.log('filtered recording sd too high');
2074
- } else {
2075
- if (this.numSuccessfulCaptured < 1) {
2076
- this.numSuccessfulCaptured += 1;
2077
- this.stepNum += 1;
2078
- this.incrementStatusBar();
2079
- console.log('after mls w iir record for some reason add numSucc capt ' + this.stepNum);
2080
- this.status =
2081
- `All Hz Calibration: ${this.numSuccessfulCaptured} recording of convolved MLS captured`.toString() +
2082
- this.generateTemplate().toString();
2083
- this.emit('update', {
2084
- message: this.status,
2085
- });
2087
+ if (result['sd'] > this._calibrateSoundPowerDbSDToleratedDb) {
2088
+ console.log('filtered recording sd too high');
2089
+ } else {
2090
+ if (this.numSuccessfulCaptured < 1) {
2091
+ this.numSuccessfulCaptured += 1;
2092
+ this.stepNum += 1;
2093
+ this.incrementStatusBar();
2094
+ console.log(
2095
+ 'after mls w iir record for some reason add numSucc capt ' + this.stepNum
2096
+ );
2097
+ this.status =
2098
+ `All Hz Calibration: ${this.numSuccessfulCaptured} recording of convolved MLS captured`.toString() +
2099
+ this.generateTemplate().toString();
2100
+ this.emit('update', {
2101
+ message: this.status,
2102
+ });
2103
+ }
2086
2104
  }
2087
2105
  }
2088
- }
2089
2106
  });
2090
2107
  };
2091
2108
 
@@ -2215,7 +2232,7 @@ class Combination extends AudioCalibrator {
2215
2232
  if (componentIR == null) {
2216
2233
  //mode 'ir'
2217
2234
  //global variable this.componentIR must be set
2218
- this.componentIR = await this.readFrqGainFromFirestore(ID, OEM, 'default').then(data => {
2235
+ this.componentIR = await this.readFrqGainFromFirestore(ID, OEM, true).then(data => {
2219
2236
  return data;
2220
2237
  });
2221
2238
  // await this.readFrqGain(ID, OEM).then(data => {
@@ -2223,7 +2240,7 @@ class Combination extends AudioCalibrator {
2223
2240
  // });
2224
2241
 
2225
2242
  // lCalib = await this.readGainat1000Hz(ID, OEM);
2226
- lCalib = await this.readGainat1000HzFromFirestore(ID, OEM, 'default');
2243
+ lCalib = await this.readGainat1000HzFromFirestore(ID, OEM, true);
2227
2244
  micInfo['gainDBSPL'] = lCalib;
2228
2245
  // this.componentGainDBSPL = this.convertToDB(lCalib);
2229
2246
  this.componentGainDBSPL = lCalib;
@@ -2254,29 +2271,20 @@ class Combination extends AudioCalibrator {
2254
2271
  let impulseResponseResults = await this.startCalibrationImpulseResponse(stream);
2255
2272
  impulseResponseResults['background_noise'] = this.background_noise;
2256
2273
  if (componentIR != null) {
2257
- // I corrected microphone/loudpeaker IR scale in easyeyes,
2274
+ // I corrected microphone/loudpeaker IR scale in easyeyes,
2258
2275
  // but since we write microphone IR to firestore here
2259
- // we need to correct microphone IR here
2260
- let correctGain = Math.round(
2261
- (volumeResults.parameters.gainDBSPL -
2262
- this.componentGainDBSPL) *
2263
- 10
2264
- ) / 10;
2265
-
2266
- let IrFreq = impulseResponseResults?.component.ir.Freq.map((freq) => Math.round(freq));
2276
+ // we need to correct microphone IR here
2277
+ let correctGain =
2278
+ Math.round((volumeResults.parameters.gainDBSPL - this.componentGainDBSPL) * 10) / 10;
2279
+
2280
+ let IrFreq = impulseResponseResults?.component.ir.Freq.map(freq => Math.round(freq));
2267
2281
  let IrGain = impulseResponseResults?.component?.ir.Gain;
2268
- const IrGainAt1000Hz = IrGain[IrFreq.findIndex((freq) => freq === 1000)];
2282
+ const IrGainAt1000Hz = IrGain[IrFreq.findIndex(freq => freq === 1000)];
2269
2283
  const difference = Math.round(10 * (IrGainAt1000Hz - correctGain)) / 10;
2270
- IrGain = IrGain.map((gain) => gain - difference);
2284
+ IrGain = IrGain.map(gain => gain - difference);
2271
2285
  const id = await this.writeIsSmartPhoneToFirestore(ID, isSmartPhone, OEM);
2272
2286
  await this.writeMicrophoneInfoToFirestore(ID, micInfo, OEM, id);
2273
- await this.writeFrqGainToFirestore(
2274
- ID,
2275
- IrFreq,
2276
- IrGain,
2277
- OEM,
2278
- id
2279
- );
2287
+ await this.writeFrqGainToFirestore(ID, IrFreq, IrGain, OEM, id);
2280
2288
  micInfo['gainDBSPL'] = impulseResponseResults.component.gainDBSPL;
2281
2289
  await this.writeGainat1000HzToFirestore(ID, micInfo['gainDBSPL'], OEM, id);
2282
2290
  // await this.writeGainat1000Hz(ID, micInfo['gainDBSPL'], OEM);