speaker-calibration 2.2.11 → 2.2.13

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.
@@ -78,6 +78,12 @@
78
78
  >Number of MLS burts per capture</label
79
79
  >
80
80
  </div>
81
+ <div class="row">
82
+ <input type="number" id="calibrateSoundHzBox" class="form-control" value="48000" />
83
+ <label class="form-label" for="calibrateSoundHzBox"
84
+ >calibrateSoundHz</label
85
+ >
86
+ </div>
81
87
  <div class="row">
82
88
  <button id="calibrationBeginButton" type="button" class="btn btn-primary">
83
89
  Calibrate
@@ -217,6 +217,7 @@ window.onload = () => {
217
217
  numMLSPerCapture: document.getElementById('numMLSPerCaptureInput').value,
218
218
  mlsOrder: document.getElementById('mlsOrder').value,
219
219
  download: document.getElementById('flexSwitchCheckDownload').checked,
220
+ calibrateSoundHz: document.getElementById('calibrateSoundHzBox').value,
220
221
  };
221
222
 
222
223
  const calibrator = new CombinationCalibration(calibratorParams);
package/dist/main.js CHANGED
@@ -840,7 +840,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _myE
840
840
  /***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
841
841
 
842
842
  "use strict";
843
- eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _audioCalibrator__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../audioCalibrator */ \"./src/tasks/audioCalibrator.js\");\n/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../utils */ \"./src/utils.js\");\n/* harmony import */ var _config_firebase__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../config/firebase */ \"./src/config/firebase.js\");\n/* harmony import */ var firebase_database__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! firebase/database */ \"./node_modules/firebase/database/dist/esm/index.esm.js\");\n\r\n\r\n\r\n\r\n\r\n\r\n/**\r\n *\r\n */\r\nclass Combination extends _audioCalibrator__WEBPACK_IMPORTED_MODULE_0__[\"default\"] {\r\n /**\r\n * Default constructor. Creates an instance with any number of paramters passed or the default parameters defined here.\r\n *\r\n * @param {Object<boolean, number, number, number>} calibratorParams - paramter object\r\n * @param {boolean} [calibratorParams.download = false] - boolean flag to download captures\r\n * @param {number} [calibratorParams.mlsOrder = 18] - order of the MLS to be generated\r\n * @param {number} [calibratorParams.numCaptures = 5] - number of captures to perform\r\n * @param {number} [calibratorParams.numMLSPerCapture = 4] - number of bursts of MLS per capture\r\n */\r\n constructor({\r\n download = false,\r\n mlsOrder = 18,\r\n numCaptures = 3,\r\n numMLSPerCapture = 4,\r\n lowHz = 20,\r\n highHz = 10000,\r\n }) {\r\n super(numCaptures, numMLSPerCapture);\r\n this.#mlsOrder = parseInt(mlsOrder, 10);\r\n this.#P = 2 ** mlsOrder - 1;\r\n this.#download = download;\r\n this.#mls = [];\r\n this.#lowHz = lowHz;\r\n this.#highHz = highHz;\r\n }\r\n\r\n /** @private */\r\n stepNum = 0;\r\n\r\n /** @private */\r\n totalSteps = 25;\r\n\r\n /** @private */\r\n #download;\r\n\r\n /** @private */\r\n #mlsGenInterface;\r\n\r\n /** @private */\r\n #mlsBufferView;\r\n\r\n /** @private */\r\n componentInvertedImpulseResponse = null;\r\n\r\n /** @private */\r\n systemInvertedImpulseResponse = null;\r\n\r\n //averaged and subtracted ir returned from calibration used to calculated iir\r\n /** @private */\r\n ir = null;\r\n\r\n /** @private */\r\n impulseResponses = [];\r\n\r\n /** @private */\r\n #mlsOrder;\r\n\r\n /** @private */\r\n #lowHz;\r\n\r\n /** @private */\r\n #highHz;\r\n\r\n /** @private */\r\n #mls;\r\n\r\n /** @private */\r\n #P;\r\n\r\n /** @private */\r\n #audioContext;\r\n\r\n /** @private */\r\n TAPER_SECS = 5;\r\n\r\n /** @private */\r\n offsetGainNode;\r\n\r\n /** @private */\r\n componentConvolution;\r\n\r\n /** @private */\r\n systemConvolution;\r\n\r\n ////////////////////////volume\r\n /** @private */\r\n #CALIBRATION_TONE_FREQUENCY = 1000; // Hz\r\n\r\n /** @private */\r\n #CALIBRATION_TONE_TYPE = 'sine';\r\n\r\n /** @private */\r\n #CALIBRATION_TONE_DURATION = 5; // seconds\r\n\r\n /** @private */\r\n outDBSPL = null;\r\n THD = null;\r\n outDBSPL1000 = null;\r\n\r\n /** @private */\r\n TAPER_SECS = 0.01; // seconds\r\n\r\n /** @private */\r\n status_denominator = 8;\r\n\r\n /** @private */\r\n status_numerator = 0;\r\n\r\n /** @private */\r\n percent_complete = 0;\r\n\r\n /** @private */\r\n status = ``;\r\n\r\n /**@private */\r\n status_literal = `<div style=\"display: flex; justify-content: center;\"><div style=\"width: 200px; height: 20px; border: 2px solid #000; border-radius: 10px;\"><div style=\"width: ${this.percent_complete}%; height: 100%; background-color: #00aaff; border-radius: 8px;\"></div></div></div>`;\r\n\r\n /**@private */\r\n componentIR = null;\r\n\r\n /**@private */\r\n oldComponentIR = null;\r\n\r\n /**@private */\r\n systemIR = null;\r\n\r\n /**@private */\r\n _calibrateSoundCheck = '';\r\n\r\n deviceType = null;\r\n\r\n deviceName = null;\r\n\r\n deviceInfo = null;\r\n\r\n desired_time_per_mls = 0;\r\n\r\n num_mls_to_skip = 0;\r\n\r\n desired_sampling_rate = 0;\r\n\r\n #currentConvolution = [];\r\n\r\n mode = 'unfiltered';\r\n\r\n sourceNode;\r\n\r\n /**generate string template that gets reevaluated as variable increases */\r\n generateTemplate = () => {\r\n if (this.percent_complete > 100) {\r\n this.percent_complete = 100;\r\n }\r\n const template = `<div style=\"display: flex; justify-content: center;\"><div style=\"width: 800px; height: 20px; border: 2px solid #000; border-radius: 10px;\"><div style=\"width: ${this.percent_complete}%; height: 100%; background-color: #00aaff; border-radius: 8px;\"></div></div></div>`;\r\n return template;\r\n };\r\n\r\n /** increment numerator and percent for status bar */\r\n incrementStatusBar = () => {\r\n this.status_numerator += 1;\r\n this.percent_complete = (this.status_numerator / this.status_denominator) * 100;\r\n };\r\n\r\n setDeviceType = deviceType => {\r\n this.deviceType = deviceType;\r\n };\r\n\r\n setDeviceName = deviceName => {\r\n this.deviceName = deviceName;\r\n };\r\n\r\n setDeviceInfo = deviceInfo => {\r\n this.deviceInfo = deviceInfo;\r\n };\r\n\r\n /** .\r\n * .\r\n * .\r\n * Sends all the computed impulse responses to the backend server for processing\r\n *\r\n * @returns sets the resulting inverted impulse response to the class property\r\n * @example\r\n */\r\n sendSystemImpulseResponsesToServerForProcessing = async () => {\r\n const computedIRs = await Promise.all(this.impulseResponses);\r\n const filteredComputedIRs = computedIRs.filter(element => {\r\n return element != undefined;\r\n });\r\n const mls = this.#mls;\r\n const lowHz = this.#lowHz;\r\n const highHz = this.#highHz;\r\n this.stepNum += 1;\r\n console.log('send impulse responses to server: ' + this.stepNum);\r\n this.status =\r\n `All Hz Calibration: computing the IIR...`.toString() + this.generateTemplate().toString();\r\n this.emit('update', {message: this.status});\r\n return this.pyServerAPI\r\n .getSystemInverseImpulseResponseWithRetry({\r\n payload: filteredComputedIRs.slice(0, this.numCaptures),\r\n mls,\r\n lowHz,\r\n highHz,\r\n sampleRate: this.sourceSamplingRate || 96000,\r\n })\r\n .then(res => {\r\n console.log(res);\r\n this.stepNum += 1;\r\n console.log('got impulse response ' + this.stepNum);\r\n this.incrementStatusBar();\r\n this.status =\r\n `All Hz Calibration: done computing the IIR...`.toString() +\r\n this.generateTemplate().toString();\r\n this.emit('update', {message: this.status});\r\n this.systemInvertedImpulseResponse = res['iir'];\r\n this.systemIR = res['ir'];\r\n this.systemConvolution = res['convolution'];\r\n })\r\n .catch(err => {\r\n console.error(err);\r\n });\r\n };\r\n\r\n /** .\r\n * .\r\n * .\r\n * Sends all the computed impulse responses to the backend server for processing\r\n *\r\n * @returns sets the resulting inverted impulse response to the class property\r\n * @example\r\n */\r\n sendComponentImpulseResponsesToServerForProcessing = async () => {\r\n const computedIRs = await Promise.all(this.impulseResponses);\r\n const filteredComputedIRs = computedIRs.filter(element => {\r\n return element != undefined;\r\n });\r\n const componentIRGains = this.componentIR['Gain'];\r\n const componentIRFreqs = this.componentIR['Freq'];\r\n const mls = this.#mls;\r\n const lowHz = this.#lowHz;\r\n const highHz = this.#highHz;\r\n this.stepNum += 1;\r\n console.log('send impulse responses to server: ' + this.stepNum);\r\n this.status =\r\n `All Hz Calibration: computing the IIR...`.toString() + this.generateTemplate().toString();\r\n this.emit('update', {message: this.status});\r\n return this.pyServerAPI\r\n .getComponentInverseImpulseResponseWithRetry({\r\n payload: filteredComputedIRs.slice(0, this.numCaptures),\r\n mls,\r\n lowHz,\r\n highHz,\r\n componentIRGains,\r\n componentIRFreqs,\r\n sampleRate: this.sourceSamplingRate || 96000,\r\n })\r\n .then(res => {\r\n console.log(res);\r\n this.stepNum += 1;\r\n console.log('got impulse response ' + this.stepNum);\r\n this.incrementStatusBar();\r\n this.status =\r\n `All Hz Calibration: done computing the IIR...`.toString() +\r\n this.generateTemplate().toString();\r\n this.emit('update', {message: this.status});\r\n this.componentInvertedImpulseResponse = res['iir'];\r\n this.componentIR['Gain'] = res['ir'];\r\n this.componentIR['Freq'] = res['frequencies'];\r\n this.componentConvolution = res['convolution'];\r\n })\r\n .catch(err => {\r\n // this.emit('InvertedImpulseResponse', {res: false});\r\n console.error(err);\r\n });\r\n };\r\n\r\n /** .\r\n * .\r\n * .\r\n * Sends the recorded signal, or a given csv string of a signal, to the back end server for processing\r\n *\r\n * @param {<array>String} signalCsv - Optional csv string of a previously recorded signal, if given, this signal will be processed\r\n * @example\r\n */\r\n sendRecordingToServerForProcessing = signalCsv => {\r\n const allSignals = this.getAllRecordedSignals();\r\n const numSignals = allSignals.length;\r\n const mls = this.#mls;\r\n const payload =\r\n signalCsv && signalCsv.length > 0 ? (0,_utils__WEBPACK_IMPORTED_MODULE_1__.csvToArray)(signalCsv) : allSignals[numSignals - 1];\r\n console.log('sending rec');\r\n this.stepNum += 1;\r\n console.log('send rec ' + this.stepNum);\r\n this.status =\r\n `All Hz Calibration Step: computing the IR of the last recording...`.toString() +\r\n this.generateTemplate().toString();\r\n this.emit('update', {message: this.status});\r\n this.impulseResponses.push(\r\n this.pyServerAPI\r\n .getImpulseResponse({\r\n sampleRate: this.sourceSamplingRate || 96000,\r\n payload,\r\n mls,\r\n P: this.#P,\r\n numPeriods: this.numMLSPerCapture,\r\n })\r\n .then(res => {\r\n if (this.numSuccessfulCaptured < this.numCaptures) {\r\n this.numSuccessfulCaptured += 1;\r\n console.log('num succ capt: ' + this.numSuccessfulCaptured);\r\n this.stepNum += 1;\r\n console.log('got impulse response ' + this.stepNum);\r\n this.incrementStatusBar();\r\n this.status =\r\n `All Hz Calibration: ${this.numSuccessfulCaptured}/${this.numCaptures} IRs computed...`.toString() +\r\n this.generateTemplate().toString();\r\n this.emit('update', {\r\n message: this.status,\r\n });\r\n return res;\r\n }\r\n })\r\n .catch(err => {\r\n console.error(err);\r\n })\r\n );\r\n };\r\n\r\n /**\r\n * Passed to the calibration steps function, awaits the desired amount of seconds to capture the desired number\r\n * of MLS periods defined in the constructor.\r\n *\r\n * @example\r\n */\r\n #awaitDesiredMLSLength = async () => {\r\n // seconds per MLS = P / SR\r\n // await N * P / SR\r\n this.stepNum += 1;\r\n console.log('await desired length ' + this.stepNum);\r\n this.status =\r\n `All Hz Calibration: sampling the calibration signal...`.toString() +\r\n `\\niteration ${this.stepNum}` +\r\n this.generateTemplate();\r\n this.emit('update', {\r\n message: this.status,\r\n });\r\n let time_to_wait = 0;\r\n if (this.mode === 'unfiltered') {\r\n time_to_wait = (this.#mls.length / this.sourceSamplingRate) * this.numMLSPerCapture;\r\n } else if (this.mode === 'filtered') {\r\n time_to_wait =\r\n (this.#currentConvolution.length / this.sourceSamplingRate) * this.numMLSPerCapture;\r\n } else {\r\n throw new Error('Mode broke in awaitDesiredMLSLength');\r\n }\r\n\r\n await (0,_utils__WEBPACK_IMPORTED_MODULE_1__.sleep)(time_to_wait * 1.1);\r\n };\r\n\r\n /** .\r\n * .\r\n * .\r\n * Passed to the calibration steps function, awaits the onset of the signal to ensure a steady state\r\n *\r\n * @example\r\n */\r\n #awaitSignalOnset = async () => {\r\n this.stepNum += 1;\r\n console.log('await signal onset ' + this.stepNum);\r\n this.status =\r\n `All Hz Calibration: waiting for the signal to stabilize...`.toString() +\r\n this.generateTemplate();\r\n this.emit('update', {\r\n message: this.status,\r\n });\r\n let number_of_bursts_to_skip = this.num_mls_to_skip;\r\n let time_to_sleep = 0;\r\n if (this.mode === 'unfiltered') {\r\n time_to_sleep = this.#mls.length / this.sourceSamplingRate;\r\n } else if (this.mode === 'filtered') {\r\n time_to_sleep = this.#currentConvolution.length / this.sourceSamplingRate;\r\n } else {\r\n throw new Error('Mode broke in awaitSignalOnset');\r\n }\r\n await (0,_utils__WEBPACK_IMPORTED_MODULE_1__.sleep)(time_to_sleep);\r\n };\r\n\r\n /**\r\n * Called immediately after a recording is captured. Used to process the resulting signal\r\n * whether by sending the result to a server or by computing a result locally.\r\n *\r\n * @example\r\n */\r\n #afterMLSRecord = () => {\r\n console.log('after record');\r\n this.sendRecordingToServerForProcessing();\r\n };\r\n\r\n #afterMLSwIIRRecord = () => {\r\n if (this.numSuccessfulCaptured < 1) {\r\n this.numSuccessfulCaptured += 1;\r\n this.stepNum += 1;\r\n this.incrementStatusBar();\r\n console.log('after mls w iir record for some reason add numSucc capt ' + this.stepNum);\r\n this.status =\r\n `All Hz Calibration: ${this.numSuccessfulCaptured} recording of convolved MLS captured`.toString() +\r\n this.generateTemplate().toString();\r\n this.emit('update', {\r\n message: this.status,\r\n });\r\n }\r\n };\r\n\r\n /** .\r\n * .\r\n * .\r\n * Created an S Curver Buffer to taper the signal onset\r\n *\r\n * @param {*} length\r\n * @param {*} phase\r\n * @returns\r\n * @example\r\n */\r\n static createSCurveBuffer = (length, phase) => {\r\n const curve = new Float32Array(length);\r\n let i;\r\n for (i = 0; i < length; i += 1) {\r\n // scale the curve to be between 0-1\r\n curve[i] = Math.sin((Math.PI * i) / length - phase) / 2 + 0.5;\r\n }\r\n return curve;\r\n };\r\n\r\n static createInverseSCurveBuffer = (length, phase) => {\r\n const curve = new Float32Array(length);\r\n let i;\r\n let j = length - 1;\r\n for (i = 0; i < length; i += 1) {\r\n // scale the curve to be between 0-1\r\n curve[i] = Math.sin((Math.PI * j) / length - phase) / 2 + 0.5;\r\n j -= 1;\r\n }\r\n return curve;\r\n };\r\n\r\n /**\r\n * Construct a Calibration Node with the calibration parameters.\r\n *\r\n * @param dataBuffer\r\n * @private\r\n * @example\r\n */\r\n #createCalibrationNodeFromBuffer = dataBuffer => {\r\n console.log('databuffer');\r\n console.log(dataBuffer);\r\n if (!this.sourceAudioContext) {\r\n this.makeNewSourceAudioContext();\r\n }\r\n\r\n const buffer = this.sourceAudioContext.createBuffer(\r\n 1, // number of channels\r\n dataBuffer.length,\r\n this.sourceAudioContext.sampleRate // sample rate\r\n );\r\n\r\n const data = buffer.getChannelData(0); // get data\r\n // fill the buffer with our data\r\n try {\r\n for (let i = 0; i < dataBuffer.length; i += 1) {\r\n data[i] = dataBuffer[i] * 0.1;\r\n }\r\n } catch (error) {\r\n console.error(error);\r\n }\r\n\r\n this.sourceNode = this.sourceAudioContext.createBufferSource();\r\n\r\n this.sourceNode.buffer = buffer;\r\n this.sourceNode.loop = true;\r\n this.sourceNode.connect(this.sourceAudioContext.destination);\r\n\r\n this.addCalibrationNode(this.sourceNode);\r\n };\r\n\r\n /**\r\n * Given a data buffer, creates the required calibration node\r\n *\r\n * @param {*} dataBufferArray\r\n * @example\r\n */\r\n #setCalibrationNodesFromBuffer = (dataBufferArray = [this.#mlsBufferView]) => {\r\n if (dataBufferArray.length === 1) {\r\n this.#createCalibrationNodeFromBuffer(dataBufferArray[0]);\r\n } else {\r\n throw new Error('The length of the data buffer array must be 1');\r\n }\r\n };\r\n\r\n /**\r\n * function to put MLS filtered IIR data obtained from\r\n * python server into our audio buffer to be played aloud\r\n */\r\n #putInPythonConv = () => {\r\n const audioCtx = this.makeNewSourceAudioContextConvolved();\r\n\r\n //depends on goal\r\n if (this._calibrateSoundCheck != 'system') {\r\n this.#currentConvolution = this.componentConvolution;\r\n const buffer = audioCtx.createBuffer(\r\n 1, // number of channels\r\n this.componentConvolution.length,\r\n audioCtx.sampleRate // sample rate\r\n );\r\n\r\n const data = buffer.getChannelData(0); // get data\r\n // fill the buffer with our data\r\n try {\r\n for (let i = 0; i < this.componentConvolution.length; i += 1) {\r\n data[i] = this.componentConvolution[i];\r\n }\r\n } catch (error) {\r\n console.error(error);\r\n }\r\n\r\n const source = audioCtx.createBufferSource();\r\n\r\n source.buffer = buffer;\r\n source.loop = true;\r\n source.connect(audioCtx.destination);\r\n\r\n this.addCalibrationNodeConvolved(source);\r\n } else {\r\n this.#currentConvolution = this.systemConvolution;\r\n const buffer = audioCtx.createBuffer(\r\n 1, // number of channels\r\n this.systemConvolution.length,\r\n audioCtx.sampleRate // sample rate\r\n );\r\n const data = buffer.getChannelData(0); // get data\r\n // fill the buffer with our data\r\n try {\r\n for (let i = 0; i < this.systemConvolution.length; i += 1) {\r\n data[i] = this.systemConvolution[i];\r\n }\r\n } catch (error) {\r\n console.error(error);\r\n }\r\n\r\n const source = audioCtx.createBufferSource();\r\n\r\n source.buffer = buffer;\r\n source.loop = true;\r\n source.connect(audioCtx.destination);\r\n\r\n this.addCalibrationNodeConvolved(source);\r\n }\r\n };\r\n\r\n /**\r\n * Creates an audio context and plays it for a few seconds.\r\n *\r\n * @private\r\n * @returns - Resolves when the audio is done playing.\r\n * @example\r\n */\r\n #playCalibrationAudio = () => {\r\n if (this.mode === 'unfiltered') {\r\n this.calibrationNodes[0].start(0);\r\n this.#mls = this.calibrationNodes[0].buffer.getChannelData(0);\r\n console.log('play calibration audio ' + this.stepNum);\r\n this.status =\r\n `All Hz Calibration: playing the calibration tone...`.toString() +\r\n this.generateTemplate().toString();\r\n this.emit('update', {message: this.status});\r\n } else if (this.mode === 'filtered') {\r\n this.calibrationNodes[0].start(0);\r\n console.log('play convolved audio ' + this.stepNum);\r\n this.status =\r\n `All Hz Calibration: playing the convolved calibration tone...`.toString() +\r\n this.generateTemplate().toString();\r\n this.emit('update', {message: this.status});\r\n } else {\r\n throw new Error('Mode is incorrect');\r\n }\r\n this.stepNum += 1;\r\n console.log('sink sampling rate');\r\n console.log(this.sinkSamplingRate);\r\n console.log('source sampling rate');\r\n console.log(this.sourceSamplingRate);\r\n };\r\n\r\n /** .\r\n * .\r\n * .\r\n * Stops the audio with tapered offset\r\n *\r\n * @example\r\n */\r\n #stopCalibrationAudio = () => {\r\n this.calibrationNodes[0].stop(0);\r\n this.calibrationNodes = [];\r\n this.sourceNode.disconnect();\r\n this.stepNum += 1;\r\n console.log('stop calibration audio ' + this.stepNum);\r\n this.status =\r\n `All Hz Calibration: stopping the calibration tone...`.toString() +\r\n this.generateTemplate().toString();\r\n this.emit('update', {message: this.status});\r\n };\r\n\r\n playMLSwithIIR = async (stream, iir) => {\r\n this.mode = 'filtered';\r\n console.log('play mls with iir');\r\n this.invertedImpulseResponse = iir;\r\n\r\n await this.calibrationSteps(\r\n stream,\r\n this.#playCalibrationAudio, // play audio func (required)\r\n this.#createCalibrationNodeFromBuffer(this.#currentConvolution), // before play func\r\n this.#awaitSignalOnset, // before record\r\n () => this.numSuccessfulCaptured < 1,\r\n this.#awaitDesiredMLSLength, // during record\r\n this.#afterMLSwIIRRecord, // after record\r\n this.mode\r\n );\r\n };\r\n\r\n /**\r\n * Public method to start the calibration process. Objects intialized from webassembly allocate new memory\r\n * and must be manually freed. This function is responsible for intializing the MlsGenInterface,\r\n * and wrapping the calibration steps with a garbage collection safe gaurd.\r\n *\r\n * @public\r\n * @param stream - The stream of audio from the Listener.\r\n * @example\r\n */\r\n startCalibrationImpulseResponse = async stream => {\r\n let desired_time = this.desired_time_per_mls;\r\n\r\n console.log('MLS sequence should be of length: ' + this.sourceSamplingRate * desired_time);\r\n\r\n length = this.sourceSamplingRate * desired_time;\r\n //get mls here\r\n await this.pyServerAPI\r\n .getMLSWithRetry(length)\r\n .then(res => {\r\n console.log(res);\r\n this.#mlsBufferView = res['mls'];\r\n })\r\n .catch(err => {\r\n // this.emit('InvertedImpulseResponse', {res: false});\r\n console.error(err);\r\n });\r\n await this.calibrationSteps(\r\n stream,\r\n this.#playCalibrationAudio, // play audio func (required)\r\n this.#createCalibrationNodeFromBuffer(this.#mlsBufferView), // before play func\r\n this.#awaitSignalOnset, // before record\r\n () => this.numSuccessfulCaptured < this.numCaptures, // loop while true\r\n this.#awaitDesiredMLSLength, // during record\r\n this.#afterMLSRecord, // after record\r\n this.mode\r\n ),\r\n this.#stopCalibrationAudio();\r\n\r\n // at this stage we've captured all the required signals,\r\n // and have received IRs for each one\r\n // so let's send all the IRs to the server to be converted to a single IIR\r\n await this.sendSystemImpulseResponsesToServerForProcessing();\r\n await this.sendComponentImpulseResponsesToServerForProcessing();\r\n\r\n this.numSuccessfulCaptured = 0;\r\n\r\n let iir_ir_and_plots;\r\n if (this._calibrateSoundCheck != 'none') {\r\n if (this._calibrateSoundCheck != 'system') {\r\n this.#currentConvolution = this.componentConvolution;\r\n } else {\r\n this.#currentConvolution = this.systemConvolution;\r\n }\r\n await this.playMLSwithIIR(stream, this.invertedImpulseResponse);\r\n this.#stopCalibrationAudio();\r\n this.sourceAudioContext.close();\r\n let conv_recs = this.getAllFilteredRecordedSignals();\r\n let recs = this.getAllRecordedSignals();\r\n let unconv_rec = recs[0];\r\n let conv_rec = conv_recs[0];\r\n if (this._calibrateSoundCheck != 'system') {\r\n let knownGain = this.oldComponentIR.Gain;\r\n let knownFreq = this.oldComponentIR.Freq;\r\n let sampleRate = this.sourceSamplingRate || 96000;\r\n let unconv_results = await this.pyServerAPI\r\n .getSubtractedPSDWithRetry(unconv_rec, knownGain, knownFreq, sampleRate)\r\n .then(res => {\r\n this.incrementStatusBar();\r\n this.status =\r\n `All Hz Calibration: done computing the PSD graphs...`.toString() +\r\n this.generateTemplate().toString();\r\n this.emit('update', {message: this.status});\r\n return res;\r\n })\r\n .catch(err => {\r\n console.error(err);\r\n });\r\n\r\n let conv_results = await this.pyServerAPI\r\n .getSubtractedPSDWithRetry(conv_rec, knownGain, knownFreq, sampleRate)\r\n .then(res => {\r\n this.incrementStatusBar();\r\n this.status =\r\n `All Hz Calibration: done computing the PSD graphs...`.toString() +\r\n this.generateTemplate().toString();\r\n this.emit('update', {message: this.status});\r\n return res;\r\n })\r\n .catch(err => {\r\n console.error(err);\r\n });\r\n iir_ir_and_plots = {\r\n systemIIR: this.systemInvertedImpulseResponse,\r\n componentIIR: this.componentInvertedImpulseResponse,\r\n x_unconv: unconv_results['x'],\r\n y_unconv: unconv_results['y'],\r\n x_conv: conv_results['x'],\r\n y_conv: conv_results['y'],\r\n componentIR: this.componentIR,\r\n systemIR: this.systemIR,\r\n };\r\n } else {\r\n let results = await this.pyServerAPI\r\n .getPSDWithRetry({\r\n unconv_rec,\r\n conv_rec,\r\n sampleRate: this.sourceSamplingRate || 96000,\r\n })\r\n .then(res => {\r\n this.incrementStatusBar();\r\n this.status =\r\n `All Hz Calibration: done computing the PSD graphs...`.toString() +\r\n this.generateTemplate().toString();\r\n this.emit('update', {message: this.status});\r\n return res;\r\n })\r\n .catch(err => {\r\n console.error(err);\r\n });\r\n iir_ir_and_plots = {\r\n systemIIR: this.systemInvertedImpulseResponse,\r\n componentIIR: this.componentInvertedImpulseResponse,\r\n x_unconv: results['x_unconv'],\r\n y_unconv: results['y_unconv'],\r\n x_conv: results['x_conv'],\r\n y_conv: results['y_conv'],\r\n componentIR: this.componentIR,\r\n systemIR: this.systemIR,\r\n };\r\n }\r\n\r\n if (this.#download) {\r\n this.downloadSingleUnfilteredRecording();\r\n this.downloadSingleFilteredRecording();\r\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.#mls, 'MLS.csv');\r\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.componentConvolution, 'python_component_convolution_mls_iir.csv');\r\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.systemConvolution, 'python_system_convolution_mls_iir.csv');\r\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.componentInvertedImpulseResponse, 'componentIIR.csv');\r\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.systemInvertedImpulseResponse, 'systemIIR.csv');\r\n const computedIRagain = await Promise.all(this.impulseResponses).then(res => {\r\n for (let i = 0; i < res.length; i++) {\r\n if (res[i] != undefined) {\r\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(res[i], `IR_${i}`);\r\n }\r\n }\r\n });\r\n }\r\n } else {\r\n iir_ir_and_plots = {\r\n systemIIR: this.systemInvertedImpulseResponse,\r\n componentIIR: this.componentInvertedImpulseResponse,\r\n x_unconv: [],\r\n y_unconv: [],\r\n x_conv: [],\r\n y_conv: [],\r\n componentIR: this.componentIR,\r\n systemIR: this.systemIR,\r\n };\r\n if (this.#download) {\r\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.#mls, 'MLS.csv');\r\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.componentConvolution, 'python_component_convolution_mls_iir.csv');\r\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.systemConvolution, 'python_system_convolution_mls_iir.csv');\r\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.componentInvertedImpulseResponse, 'componentIIR.csv');\r\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.systemInvertedImpulseResponse, 'systemIIR.csv');\r\n const computedIRagain = await Promise.all(this.impulseResponses).then(res => {\r\n for (let i = 0; i < res.length; i++) {\r\n if (res[i] != undefined) {\r\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(res[i], `IR_${i}`);\r\n }\r\n }\r\n });\r\n }\r\n }\r\n\r\n this.percent_complete = 100;\r\n\r\n this.status = `All Hz Calibration: Finished`.toString() + this.generateTemplate().toString();\r\n this.emit('update', {message: this.status});\r\n\r\n //here after calibration we have the component calibration (either loudspeaker or microphone) in the same form as the componentIR\r\n //that was used to calibrate\r\n\r\n return iir_ir_and_plots;\r\n };\r\n\r\n //////////////////////volume\r\n\r\n handleIncomingData = data => {\r\n console.log('Received data: ', data);\r\n if (data.type === 'soundGainDBSPL') {\r\n this.soundGainDBSPL = data.value;\r\n } else {\r\n throw new Error(`Unknown data type: ${data.type}`);\r\n }\r\n };\r\n createSCurveBuffer = (onSetBool = true) => {\r\n const curve = new Float32Array(this.TAPER_SECS * this.sourceSamplingRate + 1);\r\n const frequency = 1 / (4 * this.TAPER_SECS);\r\n let j = 0;\r\n for (let i = 0; i < this.TAPER_SECS * this.sourceSamplingRate + 1; i += 1) {\r\n const phase = 2 * Math.PI * frequency * j;\r\n const onsetTaper = Math.pow(Math.sin(phase), 2);\r\n const offsetTaper = Math.pow(Math.cos(phase), 2);\r\n curve[i] = onSetBool ? onsetTaper : offsetTaper;\r\n j += 1 / this.sourceSamplingRate;\r\n }\r\n return curve;\r\n };\r\n\r\n #getTruncatedSignal = (left = 3.5, right = 4.5) => {\r\n const start = Math.floor(left * this.sourceSamplingRate);\r\n const end = Math.floor(right * this.sourceSamplingRate);\r\n const result = Array.from(this.getLastRecordedSignal().slice(start, end));\r\n\r\n /**\r\n * function to check that capture was properly made\r\n * @param {*} list\r\n */\r\n const checkResult = list => {\r\n const setItem = new Set(list);\r\n if (setItem.size === 1 && setItem.has(0)) {\r\n console.warn(\r\n 'The last capture failed, all recorded signal is zero',\r\n this.getAllRecordedSignals()\r\n );\r\n }\r\n if (setItem.size === 0) {\r\n console.warn('The last capture failed, no recorded signal');\r\n }\r\n };\r\n checkResult(result);\r\n return result;\r\n };\r\n\r\n /** \r\n * \r\n * \r\n Construct a calibration Node with the calibration parameters and given gain value\r\n * @param {*} gainValue\r\n * */\r\n #createCalibrationToneWithGainValue = gainValue => {\r\n const audioContext = this.makeNewSourceAudioContext();\r\n const oscilator = audioContext.createOscillator();\r\n const gainNode = audioContext.createGain();\r\n const taperGainNode = audioContext.createGain();\r\n const offsetGainNode = audioContext.createGain();\r\n const totalDuration = this.#CALIBRATION_TONE_DURATION * 1.2;\r\n\r\n oscilator.frequency.value = this.#CALIBRATION_TONE_FREQUENCY;\r\n oscilator.type = this.#CALIBRATION_TONE_TYPE;\r\n gainNode.gain.value = gainValue;\r\n\r\n oscilator.connect(gainNode);\r\n gainNode.connect(taperGainNode);\r\n const onsetCurve = this.createSCurveBuffer();\r\n taperGainNode.gain.setValueCurveAtTime(onsetCurve, 0, this.TAPER_SECS);\r\n taperGainNode.connect(offsetGainNode);\r\n const offsetCurve = this.createSCurveBuffer(false);\r\n offsetGainNode.gain.setValueCurveAtTime(\r\n offsetCurve,\r\n totalDuration - this.TAPER_SECS,\r\n this.TAPER_SECS\r\n );\r\n offsetGainNode.connect(audioContext.destination);\r\n\r\n this.addCalibrationNode(oscilator);\r\n };\r\n\r\n /**\r\n * Construct a Calibration Node with the calibration parameters.\r\n *\r\n * @private\r\n * @example\r\n */\r\n #createCalibrationNode = () => {\r\n const audioContext = this.makeNewSourceAudioContext();\r\n const oscilator = audioContext.createOscillator();\r\n const gainNode = audioContext.createGain();\r\n\r\n oscilator.frequency.value = this.#CALIBRATION_TONE_FREQUENCY;\r\n oscilator.type = this.#CALIBRATION_TONE_TYPE;\r\n gainNode.gain.value = 0.04;\r\n\r\n oscilator.connect(gainNode);\r\n gainNode.connect(audioContext.destination);\r\n\r\n this.addCalibrationNode(oscilator);\r\n };\r\n\r\n #playCalibrationAudioVolume = async () => {\r\n const totalDuration = this.#CALIBRATION_TONE_DURATION * 1.2;\r\n\r\n this.calibrationNodes[0].start(0);\r\n this.calibrationNodes[0].stop(totalDuration);\r\n console.log(`Playing a buffer of ${this.#CALIBRATION_TONE_DURATION} seconds of audio`);\r\n console.log(`Waiting a total of ${totalDuration} seconds`);\r\n await (0,_utils__WEBPACK_IMPORTED_MODULE_1__.sleep)(totalDuration);\r\n };\r\n\r\n #sendToServerForProcessing = lCalib => {\r\n console.log('Sending data to server');\r\n this.pyServerAPI\r\n .getVolumeCalibration({\r\n sampleRate: this.sourceSamplingRate,\r\n payload: this.#getTruncatedSignal(),\r\n lCalib: lCalib,\r\n })\r\n .then(res => {\r\n if (this.outDBSPL === null) {\r\n this.incrementStatusBar();\r\n this.outDBSPL = res['outDbSPL'];\r\n this.outDBSPL1000 = res['outDbSPL1000'];\r\n this.THD = res['thd'];\r\n }\r\n })\r\n .catch(err => {\r\n console.warn(err);\r\n });\r\n };\r\n\r\n startCalibrationVolume = async (stream, gainValues, lCalib, componentGainDBSPL) => {\r\n const trialIterations = gainValues.length;\r\n this.status_denominator += trialIterations;\r\n const thdValues = [];\r\n const inDBValues = [];\r\n let inDB = 0;\r\n const outDBSPLValues = [];\r\n const outDBSPL1000Values = [];\r\n\r\n // do one calibration that will be discarded\r\n const soundLevelToDiscard = -60;\r\n const gainToDiscard = Math.pow(10, soundLevelToDiscard / 20);\r\n this.status =\r\n `1000 Hz Calibration: Sound Level ${soundLevelToDiscard} dB`.toString() +\r\n this.generateTemplate().toString();\r\n //this.emit('update', {message: `1000 Hz Calibration: Sound Level ${soundLevelToDiscard} dB`});\r\n this.emit('update', {message: this.status});\r\n\r\n do {\r\n // eslint-disable-next-line no-await-in-loop\r\n await this.volumeCalibrationSteps(\r\n stream,\r\n this.#playCalibrationAudioVolume,\r\n this.#createCalibrationToneWithGainValue,\r\n this.#sendToServerForProcessing,\r\n gainToDiscard,\r\n lCalib //todo make this a class parameter\r\n );\r\n } while (this.outDBSPL === null);\r\n //reset the values\r\n //this.incrementStatusBar();\r\n\r\n this.outDBSPL = null;\r\n this.outDBSPL = null;\r\n this.outDBSPL1000 = null;\r\n this.THD = null;\r\n\r\n // run the calibration at different gain values provided by the user\r\n for (let i = 0; i < trialIterations; i++) {\r\n //convert gain to DB and add to inDB\r\n inDB = Math.log10(gainValues[i]) * 20;\r\n // precision to 1 decimal place\r\n inDB = Math.round(inDB * 10) / 10;\r\n inDBValues.push(inDB);\r\n console.log('next update');\r\n this.status =\r\n `1000 Hz Calibration: Sound Level ${inDB} dB`.toString() +\r\n this.generateTemplate().toString();\r\n this.emit('update', {message: this.status});\r\n do {\r\n // eslint-disable-next-line no-await-in-loop\r\n await this.volumeCalibrationSteps(\r\n stream,\r\n this.#playCalibrationAudioVolume,\r\n this.#createCalibrationToneWithGainValue,\r\n this.#sendToServerForProcessing,\r\n gainValues[i],\r\n lCalib //todo make this a class parameter\r\n );\r\n } while (this.outDBSPL === null);\r\n outDBSPL1000Values.push(this.outDBSPL1000);\r\n thdValues.push(this.THD);\r\n outDBSPLValues.push(this.outDBSPL);\r\n\r\n this.outDBSPL = null;\r\n this.outDBSPL1000 = null;\r\n this.THD = null;\r\n }\r\n\r\n // get the volume calibration parameters from the server\r\n const parameters = await this.pyServerAPI\r\n .getVolumeCalibrationParameters({\r\n inDBValues: inDBValues,\r\n outDBSPLValues: outDBSPL1000Values,\r\n lCalib: lCalib,\r\n componentGainDBSPL,\r\n })\r\n .then(res => {\r\n this.incrementStatusBar();\r\n return res;\r\n });\r\n const result = {\r\n parameters: parameters,\r\n inDBValues: inDBValues,\r\n outDBSPLValues: outDBSPLValues,\r\n outDBSPL1000Values: outDBSPL1000Values,\r\n thdValues: thdValues,\r\n };\r\n\r\n return result;\r\n };\r\n\r\n // function to write frq and gain to firebase database given speakerID\r\n writeFrqGain = async (speakerID, frq, gain) => {\r\n // freq and gain are too large to take samples 1 in every 100 samples\r\n\r\n const sampledFrq = [];\r\n const sampledGain = [];\r\n for (let i = 0; i < frq.length; i += 100) {\r\n sampledFrq.push(frq[i]);\r\n sampledGain.push(gain[i]);\r\n }\r\n\r\n const data = {Freq: sampledFrq, Gain: sampledGain};\r\n\r\n await (0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.set)((0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.ref)(_config_firebase__WEBPACK_IMPORTED_MODULE_2__[\"default\"], `Microphone/${speakerID}/linear`), data);\r\n };\r\n\r\n // Function to Read frq and gain from firebase database given speakerID\r\n // returns an array of frq and gain if speakerID exists, returns null otherwise\r\n\r\n readFrqGain = async speakerID => {\r\n const dbRef = (0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.ref)(_config_firebase__WEBPACK_IMPORTED_MODULE_2__[\"default\"]);\r\n const snapshot = await (0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.get)((0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.child)(dbRef, `Microphone/${speakerID}/linear`));\r\n if (snapshot.exists()) {\r\n return snapshot.val();\r\n }\r\n return null;\r\n };\r\n\r\n readGainat1000Hz = async speakerID => {\r\n const dbRef = (0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.ref)(_config_firebase__WEBPACK_IMPORTED_MODULE_2__[\"default\"]);\r\n const snapshot = await (0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.get)((0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.child)(dbRef, `Microphone/${speakerID}/Gain1000`));\r\n if (snapshot.exists()) {\r\n return snapshot.val();\r\n }\r\n return null;\r\n };\r\n\r\n writeGainat1000Hz = async (speakerID, gain) => {\r\n const data = {Gain: gain};\r\n await (0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.set)((0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.ref)(_config_firebase__WEBPACK_IMPORTED_MODULE_2__[\"default\"], `Microphone/${speakerID}/Gain1000`), gain);\r\n };\r\n\r\n convertToDB = gain => {\r\n return Math.log10(gain) * 20;\r\n };\r\n\r\n // Function to perform linear interpolation between two points\r\n interpolate(x, x0, y0, x1, y1) {\r\n return y0 + ((x - x0) * (y1 - y0)) / (x1 - x0);\r\n }\r\n\r\n findGainatFrequency = (frequencies, gains, targetFrequency) => {\r\n // Find the index of the first frequency in the array greater than the target frequency\r\n let index = 0;\r\n while (index < frequencies.length && frequencies[index] < targetFrequency) {\r\n index++;\r\n }\r\n\r\n // Handle cases when the target frequency is outside the range of the given data\r\n if (index === 0) {\r\n return gains[0];\r\n } else if (index === frequencies.length) {\r\n return gains[gains.length - 1];\r\n } else {\r\n // Interpolate the gain based on the surrounding frequencies\r\n const x0 = frequencies[index - 1];\r\n const y0 = gains[index - 1];\r\n const x1 = frequencies[index];\r\n const y1 = gains[index];\r\n return this.interpolate(targetFrequency, x0, y0, x1, y1);\r\n }\r\n };\r\n\r\n writeIsSmartPhone = async (speakerID, isSmartPhone) => {\r\n const data = {isSmartPhone: isSmartPhone};\r\n await (0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.set)((0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.ref)(_config_firebase__WEBPACK_IMPORTED_MODULE_2__[\"default\"], `Microphone/${speakerID}/isSmartPhone`), isSmartPhone);\r\n };\r\n\r\n // Example of how to use the writeFrqGain and readFrqGain functions\r\n // writeFrqGain('speaker1', [1, 2, 3], [4, 5, 6]);\r\n // Speaker1 is the speakerID you want to write to in the database\r\n // readFrqGain('MiniDSPUMIK_1').then(data => console.log(data));\r\n // MiniDSPUMIK_1 is the speakerID with some Data in the database\r\n //adding gainDBSPL\r\n startCalibration = async (\r\n stream,\r\n gainValues,\r\n lCalib = 104.92978421490648,\r\n componentIR = null,\r\n microphoneName = 'MiniDSP-UMIK1-711-4754-vertical',\r\n _calibrateSoundCheck = 'goal', //GOAL PASSed in by default\r\n isSmartPhone = false,\r\n _calibrateSoundBurstRepeats = 4,\r\n _calibrateSoundBurstSec = 1,\r\n _calibrateSoundBurstsWarmup = 1,\r\n _calibrateSoundHz = 48000,\r\n micManufacturer = '',\r\n micSerialNumber = '',\r\n micModelNumber = '',\r\n micModelName = ''\r\n ) => {\r\n this.numMLSPerCapture = _calibrateSoundBurstRepeats;\r\n this.desired_time_per_mls = _calibrateSoundBurstSec;\r\n this.num_mls_to_skip = _calibrateSoundBurstsWarmup;\r\n this.desired_sampling_rate = _calibrateSoundHz;\r\n\r\n //feed calibration goal here\r\n this._calibrateSoundCheck = _calibrateSoundCheck;\r\n //check if a componentIR was given to the system, if it isn't check for the microphone. using dummy data here bc we need to\r\n //check the db based on the microphone currently connected\r\n\r\n //new lCalib found at top of calibration files *1000hz, make sure to correct\r\n //based on zeroing of 1000hz, search for \"*1000Hz\"\r\n if (componentIR == null) {\r\n //mode 'ir'\r\n //global variable this.componentIR must be set\r\n this.componentIR = await this.readFrqGain(microphoneName).then(data => {\r\n return data;\r\n });\r\n\r\n lCalib = await this.readGainat1000Hz(microphoneName);\r\n // this.componentGainDBSPL = this.convertToDB(lCalib);\r\n this.componentGainDBSPL = lCalib;\r\n //TODO: if this call to database is unknown, cannot perform experiment => return false\r\n if (this.componentIR == null) {\r\n this.status =\r\n `Microphone ${microphoneName} is not in the database. Please add it to the database.`.toString();\r\n this.emit('update', {message: this.status});\r\n return false;\r\n }\r\n } else {\r\n this.componentIR = componentIR;\r\n lCalib = this.findGainatFrequency(this.componentIR.Freq, this.componentIR.Gain, 1000);\r\n // this.componentGainDBSPL = this.convertToDB(lCalib);\r\n this.componentGainDBSPL = lCalib;\r\n await this.writeGainat1000Hz(microphoneName, lCalib);\r\n await this.writeIsSmartPhone(microphoneName, isSmartPhone);\r\n }\r\n\r\n this.oldComponentIR = this.componentIR;\r\n\r\n let volumeResults = await this.startCalibrationVolume(\r\n stream,\r\n gainValues,\r\n lCalib,\r\n this.componentGainDBSPL\r\n );\r\n\r\n let impulseResponseResults = await this.startCalibrationImpulseResponse(stream);\r\n\r\n if (componentIR != null) {\r\n //insert Freq and Gain from this.componentIR into db\r\n await this.writeFrqGain(\r\n microphoneName,\r\n impulseResponseResults.componentIR.Freq,\r\n impulseResponseResults.componentIR.Gain\r\n );\r\n }\r\n\r\n const total_results = {...volumeResults, ...impulseResponseResults};\r\n console.log('total');\r\n console.log(total_results);\r\n return total_results;\r\n };\r\n}\r\n\r\n/* harmony default export */ __webpack_exports__[\"default\"] = (Combination);\r\n\n\n//# sourceURL=webpack://speakerCalibrator/./src/tasks/combination/combination.js?");
843
+ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _audioCalibrator__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../audioCalibrator */ \"./src/tasks/audioCalibrator.js\");\n/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../utils */ \"./src/utils.js\");\n/* harmony import */ var _config_firebase__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../config/firebase */ \"./src/config/firebase.js\");\n/* harmony import */ var firebase_database__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! firebase/database */ \"./node_modules/firebase/database/dist/esm/index.esm.js\");\n\r\n\r\n\r\n\r\n\r\n\r\n/**\r\n *\r\n */\r\nclass Combination extends _audioCalibrator__WEBPACK_IMPORTED_MODULE_0__[\"default\"] {\r\n /**\r\n * Default constructor. Creates an instance with any number of paramters passed or the default parameters defined here.\r\n *\r\n * @param {Object<boolean, number, number, number>} calibratorParams - paramter object\r\n * @param {boolean} [calibratorParams.download = false] - boolean flag to download captures\r\n * @param {number} [calibratorParams.mlsOrder = 18] - order of the MLS to be generated\r\n * @param {number} [calibratorParams.numCaptures = 5] - number of captures to perform\r\n * @param {number} [calibratorParams.numMLSPerCapture = 4] - number of bursts of MLS per capture\r\n */\r\n constructor({\r\n download = false,\r\n mlsOrder = 18,\r\n numCaptures = 3,\r\n numMLSPerCapture = 4,\r\n lowHz = 20,\r\n highHz = 10000,\r\n }) {\r\n super(numCaptures, numMLSPerCapture);\r\n this.#mlsOrder = parseInt(mlsOrder, 10);\r\n this.#P = 2 ** mlsOrder - 1;\r\n this.#download = download;\r\n this.#mls = [];\r\n this.#lowHz = lowHz;\r\n this.#highHz = highHz;\r\n }\r\n\r\n /** @private */\r\n stepNum = 0;\r\n\r\n /** @private */\r\n totalSteps = 25;\r\n\r\n /** @private */\r\n #download;\r\n\r\n /** @private */\r\n #mlsGenInterface;\r\n\r\n /** @private */\r\n #mlsBufferView;\r\n\r\n /** @private */\r\n componentInvertedImpulseResponse = null;\r\n\r\n /** @private */\r\n systemInvertedImpulseResponse = null;\r\n\r\n //averaged and subtracted ir returned from calibration used to calculated iir\r\n /** @private */\r\n ir = null;\r\n\r\n /** @private */\r\n impulseResponses = [];\r\n\r\n /** @private */\r\n #mlsOrder;\r\n\r\n /** @private */\r\n #lowHz;\r\n\r\n /** @private */\r\n #highHz;\r\n\r\n /** @private */\r\n #mls;\r\n\r\n /** @private */\r\n #P;\r\n\r\n /** @private */\r\n #audioContext;\r\n\r\n /** @private */\r\n TAPER_SECS = 5;\r\n\r\n /** @private */\r\n offsetGainNode;\r\n\r\n /** @private */\r\n componentConvolution;\r\n\r\n /** @private */\r\n systemConvolution;\r\n\r\n ////////////////////////volume\r\n /** @private */\r\n #CALIBRATION_TONE_FREQUENCY = 1000; // Hz\r\n\r\n /** @private */\r\n #CALIBRATION_TONE_TYPE = 'sine';\r\n\r\n /** @private */\r\n #CALIBRATION_TONE_DURATION = 5; // seconds\r\n\r\n /** @private */\r\n outDBSPL = null;\r\n THD = null;\r\n outDBSPL1000 = null;\r\n\r\n /** @private */\r\n TAPER_SECS = 0.01; // seconds\r\n\r\n /** @private */\r\n status_denominator = 8;\r\n\r\n /** @private */\r\n status_numerator = 0;\r\n\r\n /** @private */\r\n percent_complete = 0;\r\n\r\n /** @private */\r\n status = ``;\r\n\r\n /**@private */\r\n status_literal = `<div style=\"display: flex; justify-content: center;\"><div style=\"width: 200px; height: 20px; border: 2px solid #000; border-radius: 10px;\"><div style=\"width: ${this.percent_complete}%; height: 100%; background-color: #00aaff; border-radius: 8px;\"></div></div></div>`;\r\n\r\n /**@private */\r\n componentIR = null;\r\n\r\n /**@private */\r\n oldComponentIR = null;\r\n\r\n /**@private */\r\n systemIR = null;\r\n\r\n /**@private */\r\n _calibrateSoundCheck = '';\r\n\r\n deviceType = null;\r\n\r\n deviceName = null;\r\n\r\n deviceInfo = null;\r\n\r\n desired_time_per_mls = 0;\r\n\r\n num_mls_to_skip = 0;\r\n\r\n desired_sampling_rate = 0;\r\n\r\n #currentConvolution = [];\r\n\r\n mode = 'unfiltered';\r\n\r\n sourceNode;\r\n\r\n autocorrelations = [];\r\n\r\n /**generate string template that gets reevaluated as variable increases */\r\n generateTemplate = () => {\r\n if (this.percent_complete > 100) {\r\n this.percent_complete = 100;\r\n }\r\n const template = `<div style=\"display: flex; justify-content: center;\"><div style=\"width: 800px; height: 20px; border: 2px solid #000; border-radius: 10px;\"><div style=\"width: ${this.percent_complete}%; height: 100%; background-color: #00aaff; border-radius: 8px;\"></div></div></div>`;\r\n return template;\r\n };\r\n\r\n /** increment numerator and percent for status bar */\r\n incrementStatusBar = () => {\r\n this.status_numerator += 1;\r\n this.percent_complete = (this.status_numerator / this.status_denominator) * 100;\r\n };\r\n\r\n setDeviceType = deviceType => {\r\n this.deviceType = deviceType;\r\n };\r\n\r\n setDeviceName = deviceName => {\r\n this.deviceName = deviceName;\r\n };\r\n\r\n setDeviceInfo = deviceInfo => {\r\n this.deviceInfo = deviceInfo;\r\n };\r\n\r\n /** .\r\n * .\r\n * .\r\n * Sends all the computed impulse responses to the backend server for processing\r\n *\r\n * @returns sets the resulting inverted impulse response to the class property\r\n * @example\r\n */\r\n sendSystemImpulseResponsesToServerForProcessing = async () => {\r\n const computedIRs = await Promise.all(this.impulseResponses);\r\n const filteredComputedIRs = computedIRs.filter(element => {\r\n return element != undefined;\r\n });\r\n const mls = this.#mls;\r\n const lowHz = this.#lowHz;\r\n const highHz = this.#highHz;\r\n this.stepNum += 1;\r\n console.log('send impulse responses to server: ' + this.stepNum);\r\n this.status =\r\n `All Hz Calibration: computing the IIR...`.toString() + this.generateTemplate().toString();\r\n this.emit('update', {message: this.status});\r\n return this.pyServerAPI\r\n .getSystemInverseImpulseResponseWithRetry({\r\n payload: filteredComputedIRs.slice(0, this.numCaptures),\r\n mls,\r\n lowHz,\r\n highHz,\r\n sampleRate: this.sourceSamplingRate || 96000,\r\n })\r\n .then(res => {\r\n console.log(res);\r\n this.stepNum += 1;\r\n console.log('got impulse response ' + this.stepNum);\r\n this.incrementStatusBar();\r\n this.status =\r\n `All Hz Calibration: done computing the IIR...`.toString() +\r\n this.generateTemplate().toString();\r\n this.emit('update', {message: this.status});\r\n this.systemInvertedImpulseResponse = res['iir'];\r\n this.systemIR = res['ir'];\r\n this.systemConvolution = res['convolution'];\r\n })\r\n .catch(err => {\r\n console.error(err);\r\n });\r\n };\r\n\r\n /** .\r\n * .\r\n * .\r\n * Sends all the computed impulse responses to the backend server for processing\r\n *\r\n * @returns sets the resulting inverted impulse response to the class property\r\n * @example\r\n */\r\n sendComponentImpulseResponsesToServerForProcessing = async () => {\r\n const computedIRs = await Promise.all(this.impulseResponses);\r\n const filteredComputedIRs = computedIRs.filter(element => {\r\n return element != undefined;\r\n });\r\n const componentIRGains = this.componentIR['Gain'];\r\n const componentIRFreqs = this.componentIR['Freq'];\r\n const mls = this.#mls;\r\n const lowHz = this.#lowHz;\r\n const highHz = this.#highHz;\r\n this.stepNum += 1;\r\n console.log('send impulse responses to server: ' + this.stepNum);\r\n this.status =\r\n `All Hz Calibration: computing the IIR...`.toString() + this.generateTemplate().toString();\r\n this.emit('update', {message: this.status});\r\n return this.pyServerAPI\r\n .getComponentInverseImpulseResponseWithRetry({\r\n payload: filteredComputedIRs.slice(0, this.numCaptures),\r\n mls,\r\n lowHz,\r\n highHz,\r\n componentIRGains,\r\n componentIRFreqs,\r\n sampleRate: this.sourceSamplingRate || 96000,\r\n })\r\n .then(res => {\r\n console.log(res);\r\n this.stepNum += 1;\r\n console.log('got impulse response ' + this.stepNum);\r\n this.incrementStatusBar();\r\n this.status =\r\n `All Hz Calibration: done computing the IIR...`.toString() +\r\n this.generateTemplate().toString();\r\n this.emit('update', {message: this.status});\r\n this.componentInvertedImpulseResponse = res['iir'];\r\n this.componentIR['Gain'] = res['ir'];\r\n this.componentIR['Freq'] = res['frequencies'];\r\n this.componentConvolution = res['convolution'];\r\n })\r\n .catch(err => {\r\n // this.emit('InvertedImpulseResponse', {res: false});\r\n console.error(err);\r\n });\r\n };\r\n\r\n /** .\r\n * .\r\n * .\r\n * Sends the recorded signal, or a given csv string of a signal, to the back end server for processing\r\n *\r\n * @param {<array>String} signalCsv - Optional csv string of a previously recorded signal, if given, this signal will be processed\r\n * @example\r\n */\r\n sendRecordingToServerForProcessing = signalCsv => {\r\n const allSignals = this.getAllRecordedSignals();\r\n const numSignals = allSignals.length;\r\n const mls = this.#mls;\r\n const payload =\r\n signalCsv && signalCsv.length > 0 ? (0,_utils__WEBPACK_IMPORTED_MODULE_1__.csvToArray)(signalCsv) : allSignals[numSignals - 1];\r\n console.log('sending rec');\r\n this.stepNum += 1;\r\n console.log('send rec ' + this.stepNum);\r\n this.status =\r\n `All Hz Calibration Step: computing the IR of the last recording...`.toString() +\r\n this.generateTemplate().toString();\r\n this.emit('update', {message: this.status});\r\n this.impulseResponses.push(\r\n this.pyServerAPI\r\n .getImpulseResponse({\r\n sampleRate: this.sourceSamplingRate || 96000,\r\n payload,\r\n mls,\r\n P: this.#P,\r\n numPeriods: this.numMLSPerCapture,\r\n })\r\n .then(res => {\r\n if (this.numSuccessfulCaptured < this.numCaptures) {\r\n this.numSuccessfulCaptured += 1;\r\n console.log('num succ capt: ' + this.numSuccessfulCaptured);\r\n this.stepNum += 1;\r\n console.log('got impulse response ' + this.stepNum);\r\n this.incrementStatusBar();\r\n this.status =\r\n `All Hz Calibration: ${this.numSuccessfulCaptured}/${this.numCaptures} IRs computed...`.toString() +\r\n this.generateTemplate().toString();\r\n this.emit('update', {\r\n message: this.status,\r\n });\r\n this.autocorrelations.push(res['autocorrelation']);\r\n return res['ir'];\r\n }\r\n })\r\n .catch(err => {\r\n console.error(err);\r\n })\r\n );\r\n };\r\n\r\n /**\r\n * Passed to the calibration steps function, awaits the desired amount of seconds to capture the desired number\r\n * of MLS periods defined in the constructor.\r\n *\r\n * @example\r\n */\r\n #awaitDesiredMLSLength = async () => {\r\n // seconds per MLS = P / SR\r\n // await N * P / SR\r\n this.stepNum += 1;\r\n console.log('await desired length ' + this.stepNum);\r\n this.status =\r\n `All Hz Calibration: sampling the calibration signal...`.toString() +\r\n `\\niteration ${this.stepNum}` +\r\n this.generateTemplate();\r\n this.emit('update', {\r\n message: this.status,\r\n });\r\n let time_to_wait = 0;\r\n if (this.mode === 'unfiltered') {\r\n time_to_wait = (this.#mls.length / this.sourceSamplingRate) * this.numMLSPerCapture;\r\n } else if (this.mode === 'filtered') {\r\n time_to_wait =\r\n (this.#currentConvolution.length / this.sourceSamplingRate) * this.numMLSPerCapture;\r\n } else {\r\n throw new Error('Mode broke in awaitDesiredMLSLength');\r\n }\r\n\r\n await (0,_utils__WEBPACK_IMPORTED_MODULE_1__.sleep)(time_to_wait * 1.1);\r\n };\r\n\r\n /** .\r\n * .\r\n * .\r\n * Passed to the calibration steps function, awaits the onset of the signal to ensure a steady state\r\n *\r\n * @example\r\n */\r\n #awaitSignalOnset = async () => {\r\n this.stepNum += 1;\r\n console.log('await signal onset ' + this.stepNum);\r\n this.status =\r\n `All Hz Calibration: waiting for the signal to stabilize...`.toString() +\r\n this.generateTemplate();\r\n this.emit('update', {\r\n message: this.status,\r\n });\r\n let number_of_bursts_to_skip = this.num_mls_to_skip;\r\n let time_to_sleep = 0;\r\n if (this.mode === 'unfiltered') {\r\n time_to_sleep = this.#mls.length / this.sourceSamplingRate;\r\n } else if (this.mode === 'filtered') {\r\n time_to_sleep = this.#currentConvolution.length / this.sourceSamplingRate;\r\n } else {\r\n throw new Error('Mode broke in awaitSignalOnset');\r\n }\r\n await (0,_utils__WEBPACK_IMPORTED_MODULE_1__.sleep)(time_to_sleep);\r\n };\r\n\r\n /**\r\n * Called immediately after a recording is captured. Used to process the resulting signal\r\n * whether by sending the result to a server or by computing a result locally.\r\n *\r\n * @example\r\n */\r\n #afterMLSRecord = () => {\r\n console.log('after record');\r\n this.sendRecordingToServerForProcessing();\r\n };\r\n\r\n #afterMLSwIIRRecord = () => {\r\n if (this.numSuccessfulCaptured < 1) {\r\n this.numSuccessfulCaptured += 1;\r\n this.stepNum += 1;\r\n this.incrementStatusBar();\r\n console.log('after mls w iir record for some reason add numSucc capt ' + this.stepNum);\r\n this.status =\r\n `All Hz Calibration: ${this.numSuccessfulCaptured} recording of convolved MLS captured`.toString() +\r\n this.generateTemplate().toString();\r\n this.emit('update', {\r\n message: this.status,\r\n });\r\n }\r\n };\r\n\r\n /** .\r\n * .\r\n * .\r\n * Created an S Curver Buffer to taper the signal onset\r\n *\r\n * @param {*} length\r\n * @param {*} phase\r\n * @returns\r\n * @example\r\n */\r\n static createSCurveBuffer = (length, phase) => {\r\n const curve = new Float32Array(length);\r\n let i;\r\n for (i = 0; i < length; i += 1) {\r\n // scale the curve to be between 0-1\r\n curve[i] = Math.sin((Math.PI * i) / length - phase) / 2 + 0.5;\r\n }\r\n return curve;\r\n };\r\n\r\n static createInverseSCurveBuffer = (length, phase) => {\r\n const curve = new Float32Array(length);\r\n let i;\r\n let j = length - 1;\r\n for (i = 0; i < length; i += 1) {\r\n // scale the curve to be between 0-1\r\n curve[i] = Math.sin((Math.PI * j) / length - phase) / 2 + 0.5;\r\n j -= 1;\r\n }\r\n return curve;\r\n };\r\n\r\n /**\r\n * Construct a Calibration Node with the calibration parameters.\r\n *\r\n * @param dataBuffer\r\n * @private\r\n * @example\r\n */\r\n #createCalibrationNodeFromBuffer = dataBuffer => {\r\n console.log('databuffer');\r\n console.log(dataBuffer);\r\n if (!this.sourceAudioContext) {\r\n this.makeNewSourceAudioContext();\r\n }\r\n\r\n const buffer = this.sourceAudioContext.createBuffer(\r\n 1, // number of channels\r\n dataBuffer.length,\r\n this.sourceAudioContext.sampleRate // sample rate\r\n );\r\n\r\n const data = buffer.getChannelData(0); // get data\r\n // fill the buffer with our data\r\n try {\r\n for (let i = 0; i < dataBuffer.length; i += 1) {\r\n data[i] = dataBuffer[i] * 0.1;\r\n }\r\n } catch (error) {\r\n console.error(error);\r\n }\r\n\r\n this.sourceNode = this.sourceAudioContext.createBufferSource();\r\n\r\n this.sourceNode.buffer = buffer;\r\n this.sourceNode.loop = true;\r\n this.sourceNode.connect(this.sourceAudioContext.destination);\r\n\r\n this.addCalibrationNode(this.sourceNode);\r\n };\r\n\r\n /**\r\n * Given a data buffer, creates the required calibration node\r\n *\r\n * @param {*} dataBufferArray\r\n * @example\r\n */\r\n #setCalibrationNodesFromBuffer = (dataBufferArray = [this.#mlsBufferView]) => {\r\n if (dataBufferArray.length === 1) {\r\n this.#createCalibrationNodeFromBuffer(dataBufferArray[0]);\r\n } else {\r\n throw new Error('The length of the data buffer array must be 1');\r\n }\r\n };\r\n\r\n /**\r\n * function to put MLS filtered IIR data obtained from\r\n * python server into our audio buffer to be played aloud\r\n */\r\n #putInPythonConv = () => {\r\n const audioCtx = this.makeNewSourceAudioContextConvolved();\r\n\r\n //depends on goal\r\n if (this._calibrateSoundCheck != 'system') {\r\n this.#currentConvolution = this.componentConvolution;\r\n const buffer = audioCtx.createBuffer(\r\n 1, // number of channels\r\n this.componentConvolution.length,\r\n audioCtx.sampleRate // sample rate\r\n );\r\n\r\n const data = buffer.getChannelData(0); // get data\r\n // fill the buffer with our data\r\n try {\r\n for (let i = 0; i < this.componentConvolution.length; i += 1) {\r\n data[i] = this.componentConvolution[i];\r\n }\r\n } catch (error) {\r\n console.error(error);\r\n }\r\n\r\n const source = audioCtx.createBufferSource();\r\n\r\n source.buffer = buffer;\r\n source.loop = true;\r\n source.connect(audioCtx.destination);\r\n\r\n this.addCalibrationNodeConvolved(source);\r\n } else {\r\n this.#currentConvolution = this.systemConvolution;\r\n const buffer = audioCtx.createBuffer(\r\n 1, // number of channels\r\n this.systemConvolution.length,\r\n audioCtx.sampleRate // sample rate\r\n );\r\n const data = buffer.getChannelData(0); // get data\r\n // fill the buffer with our data\r\n try {\r\n for (let i = 0; i < this.systemConvolution.length; i += 1) {\r\n data[i] = this.systemConvolution[i];\r\n }\r\n } catch (error) {\r\n console.error(error);\r\n }\r\n\r\n const source = audioCtx.createBufferSource();\r\n\r\n source.buffer = buffer;\r\n source.loop = true;\r\n source.connect(audioCtx.destination);\r\n\r\n this.addCalibrationNodeConvolved(source);\r\n }\r\n };\r\n\r\n /**\r\n * Creates an audio context and plays it for a few seconds.\r\n *\r\n * @private\r\n * @returns - Resolves when the audio is done playing.\r\n * @example\r\n */\r\n #playCalibrationAudio = () => {\r\n if (this.mode === 'unfiltered') {\r\n this.calibrationNodes[0].start(0);\r\n this.#mls = this.calibrationNodes[0].buffer.getChannelData(0);\r\n console.log('play calibration audio ' + this.stepNum);\r\n this.status =\r\n `All Hz Calibration: playing the calibration tone...`.toString() +\r\n this.generateTemplate().toString();\r\n this.emit('update', {message: this.status});\r\n } else if (this.mode === 'filtered') {\r\n this.calibrationNodes[0].start(0);\r\n console.log('play convolved audio ' + this.stepNum);\r\n this.status =\r\n `All Hz Calibration: playing the convolved calibration tone...`.toString() +\r\n this.generateTemplate().toString();\r\n this.emit('update', {message: this.status});\r\n } else {\r\n throw new Error('Mode is incorrect');\r\n }\r\n this.stepNum += 1;\r\n console.log('sink sampling rate');\r\n console.log(this.sinkSamplingRate);\r\n console.log('source sampling rate');\r\n console.log(this.sourceSamplingRate);\r\n };\r\n\r\n /** .\r\n * .\r\n * .\r\n * Stops the audio with tapered offset\r\n *\r\n * @example\r\n */\r\n #stopCalibrationAudio = () => {\r\n this.calibrationNodes[0].stop(0);\r\n this.calibrationNodes = [];\r\n this.sourceNode.disconnect();\r\n this.stepNum += 1;\r\n console.log('stop calibration audio ' + this.stepNum);\r\n this.status =\r\n `All Hz Calibration: stopping the calibration tone...`.toString() +\r\n this.generateTemplate().toString();\r\n this.emit('update', {message: this.status});\r\n };\r\n\r\n playMLSwithIIR = async (stream, iir) => {\r\n this.mode = 'filtered';\r\n console.log('play mls with iir');\r\n this.invertedImpulseResponse = iir;\r\n\r\n await this.calibrationSteps(\r\n stream,\r\n this.#playCalibrationAudio, // play audio func (required)\r\n this.#createCalibrationNodeFromBuffer(this.#currentConvolution), // before play func\r\n this.#awaitSignalOnset, // before record\r\n () => this.numSuccessfulCaptured < 1,\r\n this.#awaitDesiredMLSLength, // during record\r\n this.#afterMLSwIIRRecord, // after record\r\n this.mode\r\n );\r\n };\r\n\r\n /**\r\n * Public method to start the calibration process. Objects intialized from webassembly allocate new memory\r\n * and must be manually freed. This function is responsible for intializing the MlsGenInterface,\r\n * and wrapping the calibration steps with a garbage collection safe gaurd.\r\n *\r\n * @public\r\n * @param stream - The stream of audio from the Listener.\r\n * @example\r\n */\r\n startCalibrationImpulseResponse = async stream => {\r\n let desired_time = this.desired_time_per_mls;\r\n\r\n console.log('MLS sequence should be of length: ' + this.sourceSamplingRate * desired_time);\r\n\r\n length = this.sourceSamplingRate * desired_time;\r\n //get mls here\r\n await this.pyServerAPI\r\n .getMLSWithRetry(length)\r\n .then(res => {\r\n console.log(res);\r\n this.#mlsBufferView = res['mls'];\r\n })\r\n .catch(err => {\r\n // this.emit('InvertedImpulseResponse', {res: false});\r\n console.error(err);\r\n });\r\n await this.calibrationSteps(\r\n stream,\r\n this.#playCalibrationAudio, // play audio func (required)\r\n this.#createCalibrationNodeFromBuffer(this.#mlsBufferView), // before play func\r\n this.#awaitSignalOnset, // before record\r\n () => this.numSuccessfulCaptured < this.numCaptures, // loop while true\r\n this.#awaitDesiredMLSLength, // during record\r\n this.#afterMLSRecord, // after record\r\n this.mode\r\n ),\r\n this.#stopCalibrationAudio();\r\n\r\n // at this stage we've captured all the required signals,\r\n // and have received IRs for each one\r\n // so let's send all the IRs to the server to be converted to a single IIR\r\n await this.sendSystemImpulseResponsesToServerForProcessing();\r\n await this.sendComponentImpulseResponsesToServerForProcessing();\r\n\r\n this.numSuccessfulCaptured = 0;\r\n\r\n let iir_ir_and_plots;\r\n if (this._calibrateSoundCheck != 'none') {\r\n if (this._calibrateSoundCheck != 'system') {\r\n this.#currentConvolution = this.componentConvolution;\r\n } else {\r\n this.#currentConvolution = this.systemConvolution;\r\n }\r\n await this.playMLSwithIIR(stream, this.invertedImpulseResponse);\r\n this.#stopCalibrationAudio();\r\n this.sourceAudioContext.close();\r\n let conv_recs = this.getAllFilteredRecordedSignals();\r\n let recs = this.getAllRecordedSignals();\r\n let unconv_rec = recs[0];\r\n let conv_rec = conv_recs[0];\r\n if (this._calibrateSoundCheck != 'system') {\r\n let knownGain = this.oldComponentIR.Gain;\r\n let knownFreq = this.oldComponentIR.Freq;\r\n let sampleRate = this.sourceSamplingRate || 96000;\r\n let unconv_results = await this.pyServerAPI\r\n .getSubtractedPSDWithRetry(unconv_rec, knownGain, knownFreq, sampleRate)\r\n .then(res => {\r\n this.incrementStatusBar();\r\n this.status =\r\n `All Hz Calibration: done computing the PSD graphs...`.toString() +\r\n this.generateTemplate().toString();\r\n this.emit('update', {message: this.status});\r\n return res;\r\n })\r\n .catch(err => {\r\n console.error(err);\r\n });\r\n\r\n let conv_results = await this.pyServerAPI\r\n .getSubtractedPSDWithRetry(conv_rec, knownGain, knownFreq, sampleRate)\r\n .then(res => {\r\n this.incrementStatusBar();\r\n this.status =\r\n `All Hz Calibration: done computing the PSD graphs...`.toString() +\r\n this.generateTemplate().toString();\r\n this.emit('update', {message: this.status});\r\n return res;\r\n })\r\n .catch(err => {\r\n console.error(err);\r\n });\r\n iir_ir_and_plots = {\r\n systemIIR: this.systemInvertedImpulseResponse,\r\n componentIIR: this.componentInvertedImpulseResponse,\r\n x_unconv: unconv_results['x'],\r\n y_unconv: unconv_results['y'],\r\n x_conv: conv_results['x'],\r\n y_conv: conv_results['y'],\r\n componentIR: this.componentIR,\r\n systemIR: this.systemIR,\r\n };\r\n } else {\r\n let results = await this.pyServerAPI\r\n .getPSDWithRetry({\r\n unconv_rec,\r\n conv_rec,\r\n sampleRate: this.sourceSamplingRate || 96000,\r\n })\r\n .then(res => {\r\n this.incrementStatusBar();\r\n this.status =\r\n `All Hz Calibration: done computing the PSD graphs...`.toString() +\r\n this.generateTemplate().toString();\r\n this.emit('update', {message: this.status});\r\n return res;\r\n })\r\n .catch(err => {\r\n console.error(err);\r\n });\r\n iir_ir_and_plots = {\r\n systemIIR: this.systemInvertedImpulseResponse,\r\n componentIIR: this.componentInvertedImpulseResponse,\r\n x_unconv: results['x_unconv'],\r\n y_unconv: results['y_unconv'],\r\n x_conv: results['x_conv'],\r\n y_conv: results['y_conv'],\r\n componentIR: this.componentIR,\r\n systemIR: this.systemIR,\r\n };\r\n }\r\n\r\n if (this.#download) {\r\n this.downloadSingleUnfilteredRecording();\r\n this.downloadSingleFilteredRecording();\r\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.#mls, 'MLS.csv');\r\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.componentConvolution, 'python_component_convolution_mls_iir.csv');\r\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.systemConvolution, 'python_system_convolution_mls_iir.csv');\r\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.componentInvertedImpulseResponse, 'componentIIR.csv');\r\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.systemInvertedImpulseResponse, 'systemIIR.csv');\r\n for (let i = 0; i < this.autocorrelations.length; i++) {\r\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.autocorrelations[i], `autocorrelation_${i}`);\r\n }\r\n const computedIRagain = await Promise.all(this.impulseResponses).then(res => {\r\n for (let i = 0; i < res.length; i++) {\r\n if (res[i] != undefined) {\r\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(res[i], `IR_${i}`);\r\n }\r\n }\r\n });\r\n }\r\n } else {\r\n iir_ir_and_plots = {\r\n systemIIR: this.systemInvertedImpulseResponse,\r\n componentIIR: this.componentInvertedImpulseResponse,\r\n x_unconv: [],\r\n y_unconv: [],\r\n x_conv: [],\r\n y_conv: [],\r\n componentIR: this.componentIR,\r\n systemIR: this.systemIR,\r\n };\r\n if (this.#download) {\r\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.#mls, 'MLS.csv');\r\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.componentConvolution, 'python_component_convolution_mls_iir.csv');\r\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.systemConvolution, 'python_system_convolution_mls_iir.csv');\r\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.componentInvertedImpulseResponse, 'componentIIR.csv');\r\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.systemInvertedImpulseResponse, 'systemIIR.csv');\r\n for (let i = 0; i < this.autocorrelations.length; i++) {\r\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.autocorrelations[i], `autocorrelation_${i}`);\r\n }\r\n const computedIRagain = await Promise.all(this.impulseResponses).then(res => {\r\n for (let i = 0; i < res.length; i++) {\r\n if (res[i] != undefined) {\r\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(res[i], `IR_${i}`);\r\n }\r\n }\r\n });\r\n }\r\n }\r\n\r\n this.percent_complete = 100;\r\n\r\n this.status = `All Hz Calibration: Finished`.toString() + this.generateTemplate().toString();\r\n this.emit('update', {message: this.status});\r\n\r\n //here after calibration we have the component calibration (either loudspeaker or microphone) in the same form as the componentIR\r\n //that was used to calibrate\r\n\r\n return iir_ir_and_plots;\r\n };\r\n\r\n //////////////////////volume\r\n\r\n handleIncomingData = data => {\r\n console.log('Received data: ', data);\r\n if (data.type === 'soundGainDBSPL') {\r\n this.soundGainDBSPL = data.value;\r\n } else {\r\n throw new Error(`Unknown data type: ${data.type}`);\r\n }\r\n };\r\n createSCurveBuffer = (onSetBool = true) => {\r\n const curve = new Float32Array(this.TAPER_SECS * this.sourceSamplingRate + 1);\r\n const frequency = 1 / (4 * this.TAPER_SECS);\r\n let j = 0;\r\n for (let i = 0; i < this.TAPER_SECS * this.sourceSamplingRate + 1; i += 1) {\r\n const phase = 2 * Math.PI * frequency * j;\r\n const onsetTaper = Math.pow(Math.sin(phase), 2);\r\n const offsetTaper = Math.pow(Math.cos(phase), 2);\r\n curve[i] = onSetBool ? onsetTaper : offsetTaper;\r\n j += 1 / this.sourceSamplingRate;\r\n }\r\n return curve;\r\n };\r\n\r\n #getTruncatedSignal = (left = 3.5, right = 4.5) => {\r\n const start = Math.floor(left * this.sourceSamplingRate);\r\n const end = Math.floor(right * this.sourceSamplingRate);\r\n const result = Array.from(this.getLastRecordedSignal().slice(start, end));\r\n\r\n /**\r\n * function to check that capture was properly made\r\n * @param {*} list\r\n */\r\n const checkResult = list => {\r\n const setItem = new Set(list);\r\n if (setItem.size === 1 && setItem.has(0)) {\r\n console.warn(\r\n 'The last capture failed, all recorded signal is zero',\r\n this.getAllRecordedSignals()\r\n );\r\n }\r\n if (setItem.size === 0) {\r\n console.warn('The last capture failed, no recorded signal');\r\n }\r\n };\r\n checkResult(result);\r\n return result;\r\n };\r\n\r\n /** \r\n * \r\n * \r\n Construct a calibration Node with the calibration parameters and given gain value\r\n * @param {*} gainValue\r\n * */\r\n #createCalibrationToneWithGainValue = gainValue => {\r\n const audioContext = this.makeNewSourceAudioContext();\r\n const oscilator = audioContext.createOscillator();\r\n const gainNode = audioContext.createGain();\r\n const taperGainNode = audioContext.createGain();\r\n const offsetGainNode = audioContext.createGain();\r\n const totalDuration = this.#CALIBRATION_TONE_DURATION * 1.2;\r\n\r\n oscilator.frequency.value = this.#CALIBRATION_TONE_FREQUENCY;\r\n oscilator.type = this.#CALIBRATION_TONE_TYPE;\r\n gainNode.gain.value = gainValue;\r\n\r\n oscilator.connect(gainNode);\r\n gainNode.connect(taperGainNode);\r\n const onsetCurve = this.createSCurveBuffer();\r\n taperGainNode.gain.setValueCurveAtTime(onsetCurve, 0, this.TAPER_SECS);\r\n taperGainNode.connect(offsetGainNode);\r\n const offsetCurve = this.createSCurveBuffer(false);\r\n offsetGainNode.gain.setValueCurveAtTime(\r\n offsetCurve,\r\n totalDuration - this.TAPER_SECS,\r\n this.TAPER_SECS\r\n );\r\n offsetGainNode.connect(audioContext.destination);\r\n\r\n this.addCalibrationNode(oscilator);\r\n };\r\n\r\n /**\r\n * Construct a Calibration Node with the calibration parameters.\r\n *\r\n * @private\r\n * @example\r\n */\r\n #createCalibrationNode = () => {\r\n const audioContext = this.makeNewSourceAudioContext();\r\n const oscilator = audioContext.createOscillator();\r\n const gainNode = audioContext.createGain();\r\n\r\n oscilator.frequency.value = this.#CALIBRATION_TONE_FREQUENCY;\r\n oscilator.type = this.#CALIBRATION_TONE_TYPE;\r\n gainNode.gain.value = 0.04;\r\n\r\n oscilator.connect(gainNode);\r\n gainNode.connect(audioContext.destination);\r\n\r\n this.addCalibrationNode(oscilator);\r\n };\r\n\r\n #playCalibrationAudioVolume = async () => {\r\n const totalDuration = this.#CALIBRATION_TONE_DURATION * 1.2;\r\n\r\n this.calibrationNodes[0].start(0);\r\n this.calibrationNodes[0].stop(totalDuration);\r\n console.log(`Playing a buffer of ${this.#CALIBRATION_TONE_DURATION} seconds of audio`);\r\n console.log(`Waiting a total of ${totalDuration} seconds`);\r\n await (0,_utils__WEBPACK_IMPORTED_MODULE_1__.sleep)(totalDuration);\r\n };\r\n\r\n #sendToServerForProcessing = lCalib => {\r\n console.log('Sending data to server');\r\n this.pyServerAPI\r\n .getVolumeCalibration({\r\n sampleRate: this.sourceSamplingRate,\r\n payload: this.#getTruncatedSignal(),\r\n lCalib: lCalib,\r\n })\r\n .then(res => {\r\n if (this.outDBSPL === null) {\r\n this.incrementStatusBar();\r\n this.outDBSPL = res['outDbSPL'];\r\n this.outDBSPL1000 = res['outDbSPL1000'];\r\n this.THD = res['thd'];\r\n }\r\n })\r\n .catch(err => {\r\n console.warn(err);\r\n });\r\n };\r\n\r\n startCalibrationVolume = async (stream, gainValues, lCalib, componentGainDBSPL) => {\r\n const trialIterations = gainValues.length;\r\n this.status_denominator += trialIterations;\r\n const thdValues = [];\r\n const inDBValues = [];\r\n let inDB = 0;\r\n const outDBSPLValues = [];\r\n const outDBSPL1000Values = [];\r\n\r\n // do one calibration that will be discarded\r\n const soundLevelToDiscard = -60;\r\n const gainToDiscard = Math.pow(10, soundLevelToDiscard / 20);\r\n this.status =\r\n `1000 Hz Calibration: Sound Level ${soundLevelToDiscard} dB`.toString() +\r\n this.generateTemplate().toString();\r\n //this.emit('update', {message: `1000 Hz Calibration: Sound Level ${soundLevelToDiscard} dB`});\r\n this.emit('update', {message: this.status});\r\n\r\n do {\r\n // eslint-disable-next-line no-await-in-loop\r\n await this.volumeCalibrationSteps(\r\n stream,\r\n this.#playCalibrationAudioVolume,\r\n this.#createCalibrationToneWithGainValue,\r\n this.#sendToServerForProcessing,\r\n gainToDiscard,\r\n lCalib //todo make this a class parameter\r\n );\r\n } while (this.outDBSPL === null);\r\n //reset the values\r\n //this.incrementStatusBar();\r\n\r\n this.outDBSPL = null;\r\n this.outDBSPL = null;\r\n this.outDBSPL1000 = null;\r\n this.THD = null;\r\n\r\n // run the calibration at different gain values provided by the user\r\n for (let i = 0; i < trialIterations; i++) {\r\n //convert gain to DB and add to inDB\r\n inDB = Math.log10(gainValues[i]) * 20;\r\n // precision to 1 decimal place\r\n inDB = Math.round(inDB * 10) / 10;\r\n inDBValues.push(inDB);\r\n console.log('next update');\r\n this.status =\r\n `1000 Hz Calibration: Sound Level ${inDB} dB`.toString() +\r\n this.generateTemplate().toString();\r\n this.emit('update', {message: this.status});\r\n do {\r\n // eslint-disable-next-line no-await-in-loop\r\n await this.volumeCalibrationSteps(\r\n stream,\r\n this.#playCalibrationAudioVolume,\r\n this.#createCalibrationToneWithGainValue,\r\n this.#sendToServerForProcessing,\r\n gainValues[i],\r\n lCalib //todo make this a class parameter\r\n );\r\n } while (this.outDBSPL === null);\r\n outDBSPL1000Values.push(this.outDBSPL1000);\r\n thdValues.push(this.THD);\r\n outDBSPLValues.push(this.outDBSPL);\r\n\r\n this.outDBSPL = null;\r\n this.outDBSPL1000 = null;\r\n this.THD = null;\r\n }\r\n\r\n // get the volume calibration parameters from the server\r\n const parameters = await this.pyServerAPI\r\n .getVolumeCalibrationParameters({\r\n inDBValues: inDBValues,\r\n outDBSPLValues: outDBSPL1000Values,\r\n lCalib: lCalib,\r\n componentGainDBSPL,\r\n })\r\n .then(res => {\r\n this.incrementStatusBar();\r\n return res;\r\n });\r\n const result = {\r\n parameters: parameters,\r\n inDBValues: inDBValues,\r\n outDBSPLValues: outDBSPLValues,\r\n outDBSPL1000Values: outDBSPL1000Values,\r\n thdValues: thdValues,\r\n };\r\n\r\n return result;\r\n };\r\n\r\n // function to write frq and gain to firebase database given speakerID\r\n writeFrqGain = async (speakerID, frq, gain, OEM) => {\r\n // freq and gain are too large to take samples 1 in every 100 samples\r\n\r\n const sampledFrq = [];\r\n const sampledGain = [];\r\n for (let i = 0; i < frq.length; i += 100) {\r\n sampledFrq.push(frq[i]);\r\n sampledGain.push(gain[i]);\r\n }\r\n\r\n const data = {Freq: sampledFrq, Gain: sampledGain};\r\n\r\n await (0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.set)((0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.ref)(_config_firebase__WEBPACK_IMPORTED_MODULE_2__[\"default\"], `Microphone2/${OEM}/${speakerID}/linear`), data);\r\n };\r\n\r\n // Function to Read frq and gain from firebase database given speakerID\r\n // returns an array of frq and gain if speakerID exists, returns null otherwise\r\n\r\n readFrqGain = async (speakerID, OEM) => {\r\n const dbRef = (0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.ref)(_config_firebase__WEBPACK_IMPORTED_MODULE_2__[\"default\"]);\r\n const snapshot = await (0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.get)((0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.child)(dbRef, `Microphone2/${OEM}/${speakerID}/linear`));\r\n if (snapshot.exists()) {\r\n return snapshot.val();\r\n }\r\n return null;\r\n };\r\n\r\n readGainat1000Hz = async (speakerID, OEM) => {\r\n const dbRef = (0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.ref)(_config_firebase__WEBPACK_IMPORTED_MODULE_2__[\"default\"]);\r\n const snapshot = await (0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.get)((0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.child)(dbRef, `Microphone2/${OEM}/${speakerID}/Gain1000`));\r\n if (snapshot.exists()) {\r\n return snapshot.val();\r\n }\r\n return null;\r\n };\r\n\r\n writeGainat1000Hz = async (speakerID, gain, OEM) => {\r\n const data = {Gain: gain};\r\n await (0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.set)((0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.ref)(_config_firebase__WEBPACK_IMPORTED_MODULE_2__[\"default\"], `Microphone2/${OEM}/${speakerID}/Gain1000`), gain);\r\n };\r\n\r\n writeIsSmartPhone = async (speakerID, isSmartPhone, OEM) => {\r\n const data = {isSmartPhone: isSmartPhone};\r\n await (0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.set)((0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.ref)(_config_firebase__WEBPACK_IMPORTED_MODULE_2__[\"default\"], `Microphone2/${OEM}/${speakerID}/isSmartPhone`), isSmartPhone);\r\n };\r\n\r\n doesMicrophoneExist = async (speakerID, OEM) => {\r\n const dbRef = (0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.ref)(_config_firebase__WEBPACK_IMPORTED_MODULE_2__[\"default\"]);\r\n const snapshot = await (0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.get)((0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.child)(dbRef, `Microphone2/${OEM}/${speakerID}`));\r\n if (snapshot.exists()) {\r\n return true;\r\n }\r\n return false;\r\n };\r\n\r\n convertToDB = gain => {\r\n return Math.log10(gain) * 20;\r\n };\r\n\r\n // Function to perform linear interpolation between two points\r\n interpolate(x, x0, y0, x1, y1) {\r\n return y0 + ((x - x0) * (y1 - y0)) / (x1 - x0);\r\n }\r\n\r\n findGainatFrequency = (frequencies, gains, targetFrequency) => {\r\n // Find the index of the first frequency in the array greater than the target frequency\r\n let index = 0;\r\n while (index < frequencies.length && frequencies[index] < targetFrequency) {\r\n index++;\r\n }\r\n\r\n // Handle cases when the target frequency is outside the range of the given data\r\n if (index === 0) {\r\n return gains[0];\r\n } else if (index === frequencies.length) {\r\n return gains[gains.length - 1];\r\n } else {\r\n // Interpolate the gain based on the surrounding frequencies\r\n const x0 = frequencies[index - 1];\r\n const y0 = gains[index - 1];\r\n const x1 = frequencies[index];\r\n const y1 = gains[index];\r\n return this.interpolate(targetFrequency, x0, y0, x1, y1);\r\n }\r\n };\r\n\r\n // Example of how to use the writeFrqGain and readFrqGain functions\r\n // writeFrqGain('speaker1', [1, 2, 3], [4, 5, 6]);\r\n // Speaker1 is the speakerID you want to write to in the database\r\n // readFrqGain('MiniDSPUMIK_1').then(data => console.log(data));\r\n // MiniDSPUMIK_1 is the speakerID with some Data in the database\r\n //adding gainDBSPL\r\n startCalibration = async (\r\n stream,\r\n gainValues,\r\n lCalib = 104.92978421490648,\r\n componentIR = null,\r\n microphoneName = 'MiniDSP-UMIK1-711-4754-vertical',\r\n _calibrateSoundCheck = 'goal', //GOAL PASSed in by default\r\n isSmartPhone = false,\r\n _calibrateSoundBurstRepeats = 4,\r\n _calibrateSoundBurstSec = 1,\r\n _calibrateSoundBurstsWarmup = 1,\r\n _calibrateSoundHz = 48000,\r\n micManufacturer = '',\r\n micSerialNumber = '',\r\n micModelNumber = '',\r\n micModelName = ''\r\n ) => {\r\n console.log('device info:', this.deviceInfo);\r\n this.numMLSPerCapture = _calibrateSoundBurstRepeats;\r\n this.desired_time_per_mls = _calibrateSoundBurstSec;\r\n this.num_mls_to_skip = _calibrateSoundBurstsWarmup;\r\n this.desired_sampling_rate = _calibrateSoundHz;\r\n\r\n //feed calibration goal here\r\n this._calibrateSoundCheck = _calibrateSoundCheck;\r\n //check if a componentIR was given to the system, if it isn't check for the microphone. using dummy data here bc we need to\r\n //check the db based on the microphone currently connected\r\n\r\n //new lCalib found at top of calibration files *1000hz, make sure to correct\r\n //based on zeroing of 1000hz, search for \"*1000Hz\"\r\n const ID = isSmartPhone ? micModelNumber : micSerialNumber;\r\n const OEM = isSmartPhone ? this.deviceInfo.OEM : micManufacturer;\r\n if (componentIR == null) {\r\n //mode 'ir'\r\n //global variable this.componentIR must be set\r\n this.componentIR = await this.readFrqGain(ID, OEM).then(data => {\r\n return data;\r\n });\r\n\r\n lCalib = await this.readGainat1000Hz(ID, OEM);\r\n // this.componentGainDBSPL = this.convertToDB(lCalib);\r\n this.componentGainDBSPL = lCalib;\r\n //TODO: if this call to database is unknown, cannot perform experiment => return false\r\n if (this.componentIR == null) {\r\n this.status =\r\n `Microphone (${OEM},${ID}) is not found in the database. Please add it to the database.`.toString();\r\n this.emit('update', {message: this.status});\r\n return false;\r\n }\r\n } else {\r\n this.componentIR = componentIR;\r\n lCalib = this.findGainatFrequency(this.componentIR.Freq, this.componentIR.Gain, 1000);\r\n // this.componentGainDBSPL = this.convertToDB(lCalib);\r\n this.componentGainDBSPL = lCalib;\r\n await this.writeGainat1000Hz(ID, lCalib, OEM);\r\n await this.writeIsSmartPhone(ID, isSmartPhone, OEM);\r\n }\r\n\r\n this.oldComponentIR = this.componentIR;\r\n\r\n let volumeResults = await this.startCalibrationVolume(\r\n stream,\r\n gainValues,\r\n lCalib,\r\n this.componentGainDBSPL\r\n );\r\n\r\n let impulseResponseResults = await this.startCalibrationImpulseResponse(stream);\r\n\r\n if (componentIR != null) {\r\n //insert Freq and Gain from this.componentIR into db\r\n await this.writeFrqGain(\r\n ID,\r\n impulseResponseResults.componentIR.Freq,\r\n impulseResponseResults.componentIR.Gain,\r\n OEM\r\n );\r\n }\r\n\r\n const total_results = {...volumeResults, ...impulseResponseResults};\r\n console.log('total');\r\n console.log(total_results);\r\n total_results['micInfo'] = {\r\n micManufacturer: micManufacturer,\r\n micSerialNumber: micSerialNumber,\r\n micModelNumber: micModelNumber,\r\n micModelName: micModelName,\r\n ID: ID,\r\n OEM: OEM,\r\n };\r\n return total_results;\r\n };\r\n}\r\n\r\n/* harmony default export */ __webpack_exports__[\"default\"] = (Combination);\r\n\n\n//# sourceURL=webpack://speakerCalibrator/./src/tasks/combination/combination.js?");
844
844
 
845
845
  /***/ }),
846
846
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "speaker-calibration",
3
- "version": "2.2.11",
3
+ "version": "2.2.13",
4
4
  "description": "Speaker calibration library for auditory testing",
5
5
  "main": "dist/main.js",
6
6
  "directories": {
@@ -155,6 +155,8 @@ class Combination extends AudioCalibrator {
155
155
 
156
156
  sourceNode;
157
157
 
158
+ autocorrelations = [];
159
+
158
160
  /**generate string template that gets reevaluated as variable increases */
159
161
  generateTemplate = () => {
160
162
  if (this.percent_complete > 100) {
@@ -325,7 +327,8 @@ class Combination extends AudioCalibrator {
325
327
  this.emit('update', {
326
328
  message: this.status,
327
329
  });
328
- return res;
330
+ this.autocorrelations.push(res['autocorrelation']);
331
+ return res['ir'];
329
332
  }
330
333
  })
331
334
  .catch(err => {
@@ -772,6 +775,9 @@ class Combination extends AudioCalibrator {
772
775
  saveToCSV(this.systemConvolution, 'python_system_convolution_mls_iir.csv');
773
776
  saveToCSV(this.componentInvertedImpulseResponse, 'componentIIR.csv');
774
777
  saveToCSV(this.systemInvertedImpulseResponse, 'systemIIR.csv');
778
+ for (let i = 0; i < this.autocorrelations.length; i++) {
779
+ saveToCSV(this.autocorrelations[i], `autocorrelation_${i}`);
780
+ }
775
781
  const computedIRagain = await Promise.all(this.impulseResponses).then(res => {
776
782
  for (let i = 0; i < res.length; i++) {
777
783
  if (res[i] != undefined) {
@@ -797,6 +803,9 @@ class Combination extends AudioCalibrator {
797
803
  saveToCSV(this.systemConvolution, 'python_system_convolution_mls_iir.csv');
798
804
  saveToCSV(this.componentInvertedImpulseResponse, 'componentIIR.csv');
799
805
  saveToCSV(this.systemInvertedImpulseResponse, 'systemIIR.csv');
806
+ for (let i = 0; i < this.autocorrelations.length; i++) {
807
+ saveToCSV(this.autocorrelations[i], `autocorrelation_${i}`);
808
+ }
800
809
  const computedIRagain = await Promise.all(this.impulseResponses).then(res => {
801
810
  for (let i = 0; i < res.length; i++) {
802
811
  if (res[i] != undefined) {
@@ -1046,7 +1055,7 @@ class Combination extends AudioCalibrator {
1046
1055
  };
1047
1056
 
1048
1057
  // function to write frq and gain to firebase database given speakerID
1049
- writeFrqGain = async (speakerID, frq, gain) => {
1058
+ writeFrqGain = async (speakerID, frq, gain, OEM) => {
1050
1059
  // freq and gain are too large to take samples 1 in every 100 samples
1051
1060
 
1052
1061
  const sampledFrq = [];
@@ -1058,33 +1067,47 @@ class Combination extends AudioCalibrator {
1058
1067
 
1059
1068
  const data = {Freq: sampledFrq, Gain: sampledGain};
1060
1069
 
1061
- await set(ref(database, `Microphone/${speakerID}/linear`), data);
1070
+ await set(ref(database, `Microphone2/${OEM}/${speakerID}/linear`), data);
1062
1071
  };
1063
1072
 
1064
1073
  // Function to Read frq and gain from firebase database given speakerID
1065
1074
  // returns an array of frq and gain if speakerID exists, returns null otherwise
1066
1075
 
1067
- readFrqGain = async speakerID => {
1076
+ readFrqGain = async (speakerID, OEM) => {
1068
1077
  const dbRef = ref(database);
1069
- const snapshot = await get(child(dbRef, `Microphone/${speakerID}/linear`));
1078
+ const snapshot = await get(child(dbRef, `Microphone2/${OEM}/${speakerID}/linear`));
1070
1079
  if (snapshot.exists()) {
1071
1080
  return snapshot.val();
1072
1081
  }
1073
1082
  return null;
1074
1083
  };
1075
1084
 
1076
- readGainat1000Hz = async speakerID => {
1085
+ readGainat1000Hz = async (speakerID, OEM) => {
1077
1086
  const dbRef = ref(database);
1078
- const snapshot = await get(child(dbRef, `Microphone/${speakerID}/Gain1000`));
1087
+ const snapshot = await get(child(dbRef, `Microphone2/${OEM}/${speakerID}/Gain1000`));
1079
1088
  if (snapshot.exists()) {
1080
1089
  return snapshot.val();
1081
1090
  }
1082
1091
  return null;
1083
1092
  };
1084
1093
 
1085
- writeGainat1000Hz = async (speakerID, gain) => {
1094
+ writeGainat1000Hz = async (speakerID, gain, OEM) => {
1086
1095
  const data = {Gain: gain};
1087
- await set(ref(database, `Microphone/${speakerID}/Gain1000`), gain);
1096
+ await set(ref(database, `Microphone2/${OEM}/${speakerID}/Gain1000`), gain);
1097
+ };
1098
+
1099
+ writeIsSmartPhone = async (speakerID, isSmartPhone, OEM) => {
1100
+ const data = {isSmartPhone: isSmartPhone};
1101
+ await set(ref(database, `Microphone2/${OEM}/${speakerID}/isSmartPhone`), isSmartPhone);
1102
+ };
1103
+
1104
+ doesMicrophoneExist = async (speakerID, OEM) => {
1105
+ const dbRef = ref(database);
1106
+ const snapshot = await get(child(dbRef, `Microphone2/${OEM}/${speakerID}`));
1107
+ if (snapshot.exists()) {
1108
+ return true;
1109
+ }
1110
+ return false;
1088
1111
  };
1089
1112
 
1090
1113
  convertToDB = gain => {
@@ -1118,11 +1141,6 @@ class Combination extends AudioCalibrator {
1118
1141
  }
1119
1142
  };
1120
1143
 
1121
- writeIsSmartPhone = async (speakerID, isSmartPhone) => {
1122
- const data = {isSmartPhone: isSmartPhone};
1123
- await set(ref(database, `Microphone/${speakerID}/isSmartPhone`), isSmartPhone);
1124
- };
1125
-
1126
1144
  // Example of how to use the writeFrqGain and readFrqGain functions
1127
1145
  // writeFrqGain('speaker1', [1, 2, 3], [4, 5, 6]);
1128
1146
  // Speaker1 is the speakerID you want to write to in the database
@@ -1146,6 +1164,7 @@ class Combination extends AudioCalibrator {
1146
1164
  micModelNumber = '',
1147
1165
  micModelName = ''
1148
1166
  ) => {
1167
+ console.log('device info:', this.deviceInfo);
1149
1168
  this.numMLSPerCapture = _calibrateSoundBurstRepeats;
1150
1169
  this.desired_time_per_mls = _calibrateSoundBurstSec;
1151
1170
  this.num_mls_to_skip = _calibrateSoundBurstsWarmup;
@@ -1158,20 +1177,22 @@ class Combination extends AudioCalibrator {
1158
1177
 
1159
1178
  //new lCalib found at top of calibration files *1000hz, make sure to correct
1160
1179
  //based on zeroing of 1000hz, search for "*1000Hz"
1180
+ const ID = isSmartPhone ? micModelNumber : micSerialNumber;
1181
+ const OEM = isSmartPhone ? this.deviceInfo.OEM : micManufacturer;
1161
1182
  if (componentIR == null) {
1162
1183
  //mode 'ir'
1163
1184
  //global variable this.componentIR must be set
1164
- this.componentIR = await this.readFrqGain(microphoneName).then(data => {
1185
+ this.componentIR = await this.readFrqGain(ID, OEM).then(data => {
1165
1186
  return data;
1166
1187
  });
1167
1188
 
1168
- lCalib = await this.readGainat1000Hz(microphoneName);
1189
+ lCalib = await this.readGainat1000Hz(ID, OEM);
1169
1190
  // this.componentGainDBSPL = this.convertToDB(lCalib);
1170
1191
  this.componentGainDBSPL = lCalib;
1171
1192
  //TODO: if this call to database is unknown, cannot perform experiment => return false
1172
1193
  if (this.componentIR == null) {
1173
1194
  this.status =
1174
- `Microphone ${microphoneName} is not in the database. Please add it to the database.`.toString();
1195
+ `Microphone (${OEM},${ID}) is not found in the database. Please add it to the database.`.toString();
1175
1196
  this.emit('update', {message: this.status});
1176
1197
  return false;
1177
1198
  }
@@ -1180,8 +1201,8 @@ class Combination extends AudioCalibrator {
1180
1201
  lCalib = this.findGainatFrequency(this.componentIR.Freq, this.componentIR.Gain, 1000);
1181
1202
  // this.componentGainDBSPL = this.convertToDB(lCalib);
1182
1203
  this.componentGainDBSPL = lCalib;
1183
- await this.writeGainat1000Hz(microphoneName, lCalib);
1184
- await this.writeIsSmartPhone(microphoneName, isSmartPhone);
1204
+ await this.writeGainat1000Hz(ID, lCalib, OEM);
1205
+ await this.writeIsSmartPhone(ID, isSmartPhone, OEM);
1185
1206
  }
1186
1207
 
1187
1208
  this.oldComponentIR = this.componentIR;
@@ -1198,15 +1219,24 @@ class Combination extends AudioCalibrator {
1198
1219
  if (componentIR != null) {
1199
1220
  //insert Freq and Gain from this.componentIR into db
1200
1221
  await this.writeFrqGain(
1201
- microphoneName,
1222
+ ID,
1202
1223
  impulseResponseResults.componentIR.Freq,
1203
- impulseResponseResults.componentIR.Gain
1224
+ impulseResponseResults.componentIR.Gain,
1225
+ OEM
1204
1226
  );
1205
1227
  }
1206
1228
 
1207
1229
  const total_results = {...volumeResults, ...impulseResponseResults};
1208
1230
  console.log('total');
1209
1231
  console.log(total_results);
1232
+ total_results['micInfo'] = {
1233
+ micManufacturer: micManufacturer,
1234
+ micSerialNumber: micSerialNumber,
1235
+ micModelNumber: micModelNumber,
1236
+ micModelName: micModelName,
1237
+ ID: ID,
1238
+ OEM: OEM,
1239
+ };
1210
1240
  return total_results;
1211
1241
  };
1212
1242
  }