speaker-calibration 2.2.208 → 2.2.210
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/main.js
CHANGED
|
@@ -435,7 +435,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var peer
|
|
|
435
435
|
/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
|
|
436
436
|
|
|
437
437
|
"use strict";
|
|
438
|
-
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _audioPeer__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./audioPeer */ \"./src/peer-connection/audioPeer.js\");\n/* harmony import */ var _peerErrors__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./peerErrors */ \"./src/peer-connection/peerErrors.js\");\n\n\n\n\n/**\n * @class Handles the listener's side of the connection. Responsible for getting access to user's microphone,\n * and initiating a call to the Speaker.\n * @augments AudioPeer\n */\nclass Listener extends _audioPeer__WEBPACK_IMPORTED_MODULE_0__[\"default\"] {\n /**\n * Takes a target element where html elements will be appended.\n *\n * @param params - See type definition for initParameters.\n * @example\n */\n constructor(params) {\n super(params);\n this.microphoneFromAPI = params.microphoneFromAPI ? params.microphoneFromAPI : '';\n this.microphoneDeviceId = params.microphoneDeviceId ? params.microphoneDeviceId : '';\n // this.deviceInfoFromUser = params.deviceInfoFromUser\n // ? params.deviceInfoFromUser\n // : {modelNumber: '', modelName: ''};\n this.startTime = Date.now();\n this.receiverPeerId = null;\n\n const urlParameters = this.parseURLSearchParams();\n this.calibrateSoundHz =\n // previous calibrateSoundHz\n urlParameters.hz !== null && urlParameters.hz !== undefined ? urlParameters.hz : 48000;\n this.calibrateSoundSamplingDesiredBits =\n // previous calibrateSoundSamplingDesiredBits\n urlParameters.bits !== null && urlParameters.bits !== undefined ? urlParameters.bits : 24;\n this.speakerPeerId = urlParameters.speakerPeerId;\n\n this.peer.on('open', this.onPeerOpen);\n this.peer.on('connection', this.onPeerConnection);\n this.peer.on('disconnected', this.onPeerDisconnected);\n this.peer.on('close', this.onPeerClose);\n this.peer.on('error', this.onPeerError);\n }\n\n onPeerOpen = id => {\n this.displayUpdate('Listener - onPeerOpen');\n // Workaround for peer.reconnect deleting previous id\n\n if (id === null) {\n this.displayUpdate('Received null id from peer open');\n this.peer.id = this.lastPeerId;\n } else {\n this.lastPeerId = this.peer.id;\n }\n\n this.join();\n };\n\n onPeerConnection = connection => {\n this.displayUpdate('Listener - onPeerConnection');\n // Disallow incoming connections\n connection.on('open', () => {\n connection.send('Sender does not accept incoming connections');\n setTimeout(() => {\n connection.close();\n }, 500);\n });\n };\n\n onConnData = data => {\n this.displayUpdate('Listener - onConnData');\n const hasSpeakerID = Object.prototype.hasOwnProperty.call(data, 'speakerPeerId');\n if (!hasSpeakerID) {\n this.displayUpdate('Error in parsing data received! Must set \"speakerPeerId\" property');\n throw new _peerErrors__WEBPACK_IMPORTED_MODULE_1__.MissingSpeakerIdError('Must set \"speakerPeerId\" property');\n } else {\n // this.conn.close();\n this.displayUpdate(this.speakerPeerId);\n this.speakerPeerId = data.speakerPeerId;\n const newParams = {\n speakerPeerId: this.speakerPeerId,\n };\n /*\n FUTURE does this limit usable environments?\n ie does this work if internet is lost after initial page load?\n */\n window.location.search = this.queryStringFromObject(newParams); // Redirect to correctly constructed keypad page\n }\n };\n\n join = async () => {\n this.displayUpdate('Listener - join');\n /**\n * Create the connection between the two Peers.\n *\n * Sets up callbacks that handle any events related to the\n * connection and data received on it.\n */\n // Close old connection\n if (this.conn) {\n this.displayUpdate('Closing old connection');\n this.conn.close();\n }\n\n // Create connection to destination peer specified by the query param\n this.displayUpdate(`Creating connection to: ${this.speakerPeerId}`);\n this.conn = this.peer.connect(this.speakerPeerId, {\n reliable: true,\n });\n\n this.displayUpdate('Created connection');\n this.conn.on('open', async () => {\n this.displayUpdate('Listener - conn open');\n await this.getDeviceInfo();\n // this.sendSamplingRate();\n await this.openAudioStream();\n });\n\n // Handle incoming data (messages only since this is the signal sender)\n this.conn.on('data', this.onConnData);\n this.conn.on('close', () => {\n console.log('Connection closed');\n });\n };\n\n getMobileOS = () => {\n const ua = navigator.userAgent;\n if (/android/i.test(ua)) {\n return 'Android';\n }\n if (\n /iPad|iPhone|iPod/.test(ua) ||\n ((navigator?.userAgentData?.platform || navigator?.platform) === 'MacIntel' &&\n navigator.maxTouchPoints > 1)\n ) {\n return 'iOS';\n }\n return 'Other';\n };\n\n sendSamplingRate = sampleRate => {\n this.displayUpdate('Listener - sendSamplingRate');\n this.conn.send({\n name: 'samplingRate',\n payload: sampleRate,\n });\n };\n\n sendSampleSize = sampleSize => {\n this.displayUpdate('Listener - sendSampleSize');\n this.conn.send({\n name: 'sampleSize',\n payload: sampleSize,\n });\n };\n\n sendFlags = flags => {\n this.displayUpdate('Listener - sendFlags');\n this.conn.send({\n name: 'flags',\n payload: flags,\n });\n };\n\n getDeviceInfo = async () => {\n try {\n const deviceInfo = {};\n fod.complete(function (data) {\n deviceInfo['IsMobile'] = data.device['ismobile'];\n deviceInfo['HardwareName'] = data.device['hardwarename'];\n deviceInfo['HardwareFamily'] = data.device['hardwarefamily'];\n deviceInfo['HardwareModel'] = data.device['hardwaremodel'];\n deviceInfo['OEM'] = data.device['oem'];\n deviceInfo['HardwareModelVariants'] = data.device['hardwaremodelvariants'];\n deviceInfo['DeviceId'] = data.device['deviceid'];\n deviceInfo['PlatformName'] = data.device['platformname'];\n deviceInfo['PlatformVersion'] = data.device['platformversion'];\n deviceInfo['DeviceType'] = data.device['devicetype'];\n // deviceInfo['deviceInfoFromUser'] = this.deviceInfoFromUser;\n });\n // deviceInfo['deviceInfoFromUser'] = this.deviceInfoFromUser;\n deviceInfo['microphoneFromAPI'] = this.microphoneFromAPI;\n deviceInfo['microphoneDeviceId'] = this.microphoneDeviceId;\n deviceInfo['screenWidth'] = window.screen.width;\n deviceInfo['screenHeight'] = window.screen.height;\n console.log('deviceInfo Inside getDeviceInfo: ', deviceInfo);\n this.conn.send({\n name: 'deviceInfo',\n payload: deviceInfo,\n });\n return deviceInfo;\n } catch (error) {\n console.error('Error fetching or executing script:', error.message);\n return null;\n }\n };\n\n applyHQTrackConstraints = async stream => {\n // Contraint the incoming audio to the sampling rate we want\n stream.getAudioTracks().forEach(track => {\n console.log(track, track.enabled);\n });\n const track = stream.getAudioTracks()[0];\n console.log(track);\n const capabilities = track.getCapabilities();\n\n this.displayUpdate(\n `Listener Track Capabilities - ${JSON.stringify(capabilities, undefined, 2)}`\n );\n\n const constraints = track.getConstraints();\n\n if (capabilities.echoCancellation) {\n constraints.echoCancellation = false;\n }\n\n if (capabilities.sampleRate) {\n constraints.sampleRate = this.calibrateSoundHz;\n }\n\n if (capabilities.sampleSize) {\n constraints.sampleSize = this.calibrateSoundSamplingDesiredBits;\n }\n\n if (capabilities.channelCount) {\n constraints.channelCount = 1;\n }\n\n this.displayUpdate(`Listener Track Constraints - ${JSON.stringify(constraints, undefined, 2)}`);\n\n // await the promise\n try {\n await track.applyConstraints(constraints);\n } catch (err) {\n console.error(err);\n this.displayUpdate(`Error applying constraints to track: ${err}`);\n }\n\n const settings = track.getSettings();\n this.displayUpdate(`Listener Track Settings - ${JSON.stringify(settings, undefined, 2)}`);\n return settings;\n };\n\n getMediaDevicesAudioContraints = async () => {\n const availableConstraints = navigator.mediaDevices.getSupportedConstraints();\n\n const contraints = {\n // ...(availableConstraints.echoCancellation && availableConstraints.echoCancellation == true\n // ? {echoCancellation: {exact: false}}\n // : {}),\n // ...(availableConstraints.sampleRate && availableConstraints.sampleRate == true\n // ? {sampleRate: {ideal: this.calibrateSoundHz}}\n // : {}),\n // ...(availableConstraints.sampleSize && availableConstraints.sampleSize == true\n // ? {sampleSize: {ideal: this.calibrateSoundSamplingDesiredBits}}\n // : {}),\n // ...(availableConstraints.channelCount && availableConstraints.channelCount == true\n // ? {channelCount: {exact: 1}}\n // : {}),\n echoCancellation: false,\n channelCount: 1,\n };\n\n if (this.microphoneDeviceId !== '') {\n contraints.deviceId = {exact: await this.getDeviceIdByLabel(this.microphoneDeviceId)};\n }\n\n console.log(contraints);\n\n return contraints;\n };\n getDeviceIdByLabel = async targetLabel => {\n try {\n //get permission to use audio first. (Returns empty labels on some computers if not done first)\n await navigator.mediaDevices.getUserMedia({audio: true});\n // Enumerate available media devices\n const devices = await navigator.mediaDevices.enumerateDevices();\n\n // Find the device with the matching label\n const matchingDevice = devices.find(\n device => device.kind === 'audioinput' && device.label === targetLabel\n );\n\n if (matchingDevice) {\n return matchingDevice.deviceId; // Return the deviceId if found\n } else {\n throw new Error(`No audio input device found with label: \"${targetLabel}\"`);\n }\n } catch (error) {\n console.error('Error finding device ID:', error);\n return null;\n }\n };\n\n openAudioStream = async () => {\n this.displayUpdate('Listener - openAudioStream');\n const mobileOS = this.getMobileOS();\n if (false) {}\n const constraints = await this.getMediaDevicesAudioContraints();\n console.log('Constraints right before getUserMedia:', constraints);\n navigator.mediaDevices\n .getUserMedia({\n audio: constraints,\n video: false,\n //audio: {echoCancellation: false, noiseSuppression: false, autoGainControl: false, deviceId: {exact: await this.getDeviceIdByLabel(this.microphoneDeviceId) }},\n })\n .then(stream => {\n this.displayUpdate(\n `Listener Track settings before applied constraints - ${JSON.stringify(\n stream.getAudioTracks()[0].getSettings(),\n undefined,\n 2\n )}`\n );\n this.applyHQTrackConstraints(stream)\n .then(settings => {\n console.log(settings);\n this.sendSamplingRate(settings.sampleRate);\n let sampleSize = settings.sampleSize;\n if (!sampleSize) {\n sampleSize = this.calibrateSoundSamplingDesiredBits;\n }\n this.sendSampleSize(sampleSize);\n this.sendFlags({\n autoGainControl: settings.autoGainControl,\n noiseSuppression: settings.noiseSuppression,\n echoCancellation: settings.echoCancellation,\n });\n this.peer.call(this.speakerPeerId, stream); // one-way call\n this.displayUpdate('Listener - openAudioStream');\n })\n .catch(err => {\n console.log(err);\n this.displayUpdate(\n `Listener - Error in applyHQTrackConstraints - ${JSON.stringify(err, undefined, 2)}`\n );\n });\n })\n .catch(err => {\n console.error(err);\n if (err.name === 'OverconstrainedError') {\n const constraint = err.constraint;\n const message = `The constraint \"${constraint}\" cannot be satisfied by the selected microphone. Please adjust your calibration settings or choose a different microphone.`;\n\n this.displayUpdate(`Listener - OverconstrainedError: ${message}`);\n console.error(message);\n\n alert(`Overconstrained Error: ${message}`);\n }\n this.displayUpdate(\n `Listener - Error in getUserMedia - ${JSON.stringify(err, undefined, 2)}`\n );\n });\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (Listener);\n\n\n//# sourceURL=webpack://speakerCalibrator/./src/peer-connection/listener.js?");
|
|
438
|
+
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _audioPeer__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./audioPeer */ \"./src/peer-connection/audioPeer.js\");\n/* harmony import */ var _peerErrors__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./peerErrors */ \"./src/peer-connection/peerErrors.js\");\n\n\n\n\n/**\n * @class Handles the listener's side of the connection. Responsible for getting access to user's microphone,\n * and initiating a call to the Speaker.\n * @augments AudioPeer\n */\nclass Listener extends _audioPeer__WEBPACK_IMPORTED_MODULE_0__[\"default\"] {\n /**\n * Takes a target element where html elements will be appended.\n *\n * @param params - See type definition for initParameters.\n * @example\n */\n constructor(params) {\n super(params);\n this.microphoneFromAPI = params.microphoneFromAPI ? params.microphoneFromAPI : '';\n this.microphoneDeviceId = params.microphoneDeviceId ? params.microphoneDeviceId : '';\n // this.deviceInfoFromUser = params.deviceInfoFromUser\n // ? params.deviceInfoFromUser\n // : {modelNumber: '', modelName: ''};\n this.startTime = Date.now();\n this.receiverPeerId = null;\n\n const urlParameters = this.parseURLSearchParams();\n this.calibrateSoundHz =\n // previous calibrateSoundHz\n urlParameters.hz !== null && urlParameters.hz !== undefined ? urlParameters.hz : 48000;\n this.calibrateSoundSamplingDesiredBits =\n // previous calibrateSoundSamplingDesiredBits\n urlParameters.bits !== null && urlParameters.bits !== undefined ? urlParameters.bits : 24;\n this.speakerPeerId = urlParameters.speakerPeerId;\n\n this.peer.on('open', this.onPeerOpen);\n this.peer.on('connection', this.onPeerConnection);\n this.peer.on('disconnected', this.onPeerDisconnected);\n this.peer.on('close', this.onPeerClose);\n this.peer.on('error', this.onPeerError);\n }\n\n onPeerOpen = id => {\n this.displayUpdate('Listener - onPeerOpen');\n // Workaround for peer.reconnect deleting previous id\n\n if (id === null) {\n this.displayUpdate('Received null id from peer open');\n this.peer.id = this.lastPeerId;\n } else {\n this.lastPeerId = this.peer.id;\n }\n\n this.join();\n };\n\n onPeerConnection = connection => {\n this.displayUpdate('Listener - onPeerConnection');\n // Disallow incoming connections\n connection.on('open', () => {\n connection.send('Sender does not accept incoming connections');\n setTimeout(() => {\n connection.close();\n }, 500);\n });\n };\n\n onConnData = data => {\n this.displayUpdate('Listener - onConnData');\n const hasSpeakerID = Object.prototype.hasOwnProperty.call(data, 'speakerPeerId');\n if (!hasSpeakerID) {\n this.displayUpdate('Error in parsing data received! Must set \"speakerPeerId\" property');\n throw new _peerErrors__WEBPACK_IMPORTED_MODULE_1__.MissingSpeakerIdError('Must set \"speakerPeerId\" property');\n } else {\n // this.conn.close();\n this.displayUpdate(this.speakerPeerId);\n this.speakerPeerId = data.speakerPeerId;\n const newParams = {\n speakerPeerId: this.speakerPeerId,\n };\n /*\n FUTURE does this limit usable environments?\n ie does this work if internet is lost after initial page load?\n */\n window.location.search = this.queryStringFromObject(newParams); // Redirect to correctly constructed keypad page\n }\n };\n\n join = async () => {\n this.displayUpdate('Listener - join');\n /**\n * Create the connection between the two Peers.\n *\n * Sets up callbacks that handle any events related to the\n * connection and data received on it.\n */\n // Close old connection\n if (this.conn) {\n this.displayUpdate('Closing old connection');\n this.conn.close();\n }\n\n // Create connection to destination peer specified by the query param\n this.displayUpdate(`Creating connection to: ${this.speakerPeerId}`);\n this.conn = this.peer.connect(this.speakerPeerId, {\n reliable: true,\n });\n\n this.displayUpdate('Created connection');\n this.conn.on('open', async () => {\n this.displayUpdate('Listener - conn open');\n await this.getDeviceInfo();\n // this.sendSamplingRate();\n await this.openAudioStream();\n });\n\n // Handle incoming data (messages only since this is the signal sender)\n this.conn.on('data', this.onConnData);\n this.conn.on('close', () => {\n console.log('Connection closed');\n });\n };\n\n getMobileOS = () => {\n const ua = navigator.userAgent;\n if (/android/i.test(ua)) {\n return 'Android';\n }\n if (\n /iPad|iPhone|iPod/.test(ua) ||\n ((navigator?.userAgentData?.platform || navigator?.platform) === 'MacIntel' &&\n navigator.maxTouchPoints > 1)\n ) {\n return 'iOS';\n }\n return 'Other';\n };\n\n sendSamplingRate = sampleRate => {\n this.displayUpdate('Listener - sendSamplingRate');\n this.conn.send({\n name: 'samplingRate',\n payload: sampleRate,\n });\n };\n\n sendSampleSize = sampleSize => {\n this.displayUpdate('Listener - sendSampleSize');\n this.conn.send({\n name: 'sampleSize',\n payload: sampleSize,\n });\n };\n\n sendFlags = flags => {\n this.displayUpdate('Listener - sendFlags');\n this.conn.send({\n name: 'flags',\n payload: flags,\n });\n };\n\n getDeviceInfo = async () => {\n try {\n const deviceInfo = {};\n fod.complete(function (data) {\n deviceInfo['IsMobile'] = data.device['ismobile'];\n deviceInfo['HardwareName'] = data.device['hardwarename'];\n deviceInfo['HardwareFamily'] = data.device['hardwarefamily'];\n deviceInfo['HardwareModel'] = data.device['hardwaremodel'];\n deviceInfo['OEM'] = data.device['oem'];\n deviceInfo['HardwareModelVariants'] = data.device['hardwaremodelvariants'];\n deviceInfo['DeviceId'] = data.device['deviceid'];\n deviceInfo['PlatformName'] = data.device['platformname'];\n deviceInfo['PlatformVersion'] = data.device['platformversion'];\n deviceInfo['DeviceType'] = data.device['devicetype'];\n // deviceInfo['deviceInfoFromUser'] = this.deviceInfoFromUser;\n });\n // deviceInfo['deviceInfoFromUser'] = this.deviceInfoFromUser;\n deviceInfo['microphoneFromAPI'] = this.microphoneFromAPI;\n deviceInfo['microphoneDeviceId'] = this.microphoneDeviceId;\n deviceInfo['screenWidth'] = window.screen.width;\n deviceInfo['screenHeight'] = window.screen.height;\n console.log('deviceInfo Inside getDeviceInfo: ', deviceInfo);\n this.conn.send({\n name: 'deviceInfo',\n payload: deviceInfo,\n });\n return deviceInfo;\n } catch (error) {\n console.error('Error fetching or executing script:', error.message);\n return null;\n }\n };\n\n applyHQTrackConstraints = async stream => {\n // Contraint the incoming audio to the sampling rate we want\n stream.getAudioTracks().forEach(track => {\n console.log(track, track.enabled);\n });\n const track = stream.getAudioTracks()[0];\n console.log(track);\n const capabilities = track.getCapabilities();\n\n this.displayUpdate(\n `Listener Track Capabilities - ${JSON.stringify(capabilities, undefined, 2)}`\n );\n\n const constraints = track.getConstraints();\n\n if (capabilities.echoCancellation) {\n constraints.echoCancellation = false;\n }\n\n if (capabilities.sampleRate) {\n constraints.sampleRate = this.calibrateSoundHz;\n }\n\n if (capabilities.sampleSize) {\n constraints.sampleSize = this.calibrateSoundSamplingDesiredBits;\n }\n\n if (capabilities.channelCount) {\n constraints.channelCount = 1;\n }\n\n this.displayUpdate(`Listener Track Constraints - ${JSON.stringify(constraints, undefined, 2)}`);\n\n // await the promise\n try {\n await track.applyConstraints(constraints);\n } catch (err) {\n console.error(err);\n this.displayUpdate(`Error applying constraints to track: ${err}`);\n }\n\n const settings = track.getSettings();\n this.displayUpdate(`Listener Track Settings - ${JSON.stringify(settings, undefined, 2)}`);\n return settings;\n };\n\n getMediaDevicesAudioContraints = async () => {\n const availableConstraints = navigator.mediaDevices.getSupportedConstraints();\n\n const contraints = {\n // ...(availableConstraints.echoCancellation && availableConstraints.echoCancellation == true\n // ? {echoCancellation: {exact: false}}\n // : {}),\n // ...(availableConstraints.sampleRate && availableConstraints.sampleRate == true\n // ? {sampleRate: {ideal: this.calibrateSoundHz}}\n // : {}),\n // ...(availableConstraints.sampleSize && availableConstraints.sampleSize == true\n // ? {sampleSize: {ideal: this.calibrateSoundSamplingDesiredBits}}\n // : {}),\n // ...(availableConstraints.channelCount && availableConstraints.channelCount == true\n // ? {channelCount: {exact: 1}}\n // : {}),\n autoGainControl: false,\n noiseSuppression: false,\n echoCancellation: false,\n channelCount: 1,\n };\n\n if (this.microphoneDeviceId !== '') {\n contraints.deviceId = {exact: await this.getDeviceIdByLabel(this.microphoneDeviceId)};\n }\n\n console.log(contraints);\n\n return contraints;\n };\n getDeviceIdByLabel = async targetLabel => {\n try {\n //get permission to use audio first. (Returns empty labels on some computers if not done first)\n await navigator.mediaDevices.getUserMedia({audio: true});\n // Enumerate available media devices\n const devices = await navigator.mediaDevices.enumerateDevices();\n\n // Find the device with the matching label\n const matchingDevice = devices.find(\n device => device.kind === 'audioinput' && device.label === targetLabel\n );\n\n if (matchingDevice) {\n return matchingDevice.deviceId; // Return the deviceId if found\n } else {\n throw new Error(`No audio input device found with label: \"${targetLabel}\"`);\n }\n } catch (error) {\n console.error('Error finding device ID:', error);\n return null;\n }\n };\n\n openAudioStream = async () => {\n this.displayUpdate('Listener - openAudioStream');\n const mobileOS = this.getMobileOS();\n if (false) {}\n const constraints = await this.getMediaDevicesAudioContraints();\n console.log('Constraints right before getUserMedia:', constraints);\n navigator.mediaDevices\n .getUserMedia({\n audio: constraints,\n video: false,\n //audio: {echoCancellation: false, noiseSuppression: false, autoGainControl: false, deviceId: {exact: await this.getDeviceIdByLabel(this.microphoneDeviceId) }},\n })\n .then(stream => {\n this.displayUpdate(\n `Listener Track settings before applied constraints - ${JSON.stringify(\n stream.getAudioTracks()[0].getSettings(),\n undefined,\n 2\n )}`\n );\n this.applyHQTrackConstraints(stream)\n .then(settings => {\n console.log(settings);\n this.sendSamplingRate(settings.sampleRate);\n let sampleSize = settings.sampleSize;\n if (!sampleSize) {\n sampleSize = this.calibrateSoundSamplingDesiredBits;\n }\n this.sendSampleSize(sampleSize);\n this.sendFlags({\n autoGainControl: settings.autoGainControl,\n noiseSuppression: settings.noiseSuppression,\n echoCancellation: settings.echoCancellation,\n });\n this.peer.call(this.speakerPeerId, stream); // one-way call\n this.displayUpdate('Listener - openAudioStream');\n })\n .catch(err => {\n console.log(err);\n this.displayUpdate(\n `Listener - Error in applyHQTrackConstraints - ${JSON.stringify(err, undefined, 2)}`\n );\n });\n })\n .catch(err => {\n console.error(err);\n if (err.name === 'OverconstrainedError') {\n const constraint = err.constraint;\n const message = `The constraint \"${constraint}\" cannot be satisfied by the selected microphone. Please adjust your calibration settings or choose a different microphone.`;\n\n this.displayUpdate(`Listener - OverconstrainedError: ${message}`);\n console.error(message);\n\n alert(`Overconstrained Error: ${message}`);\n }\n this.displayUpdate(\n `Listener - Error in getUserMedia - ${JSON.stringify(err, undefined, 2)}`\n );\n });\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (Listener);\n\n\n//# sourceURL=webpack://speakerCalibrator/./src/peer-connection/listener.js?");
|
|
439
439
|
|
|
440
440
|
/***/ }),
|
|
441
441
|
|
|
@@ -490,7 +490,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var axio
|
|
|
490
490
|
/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
|
|
491
491
|
|
|
492
492
|
"use strict";
|
|
493
|
-
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _audioRecorder__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./audioRecorder */ \"./src/tasks/audioRecorder.js\");\n/* harmony import */ var _server_PythonServerAPI__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../server/PythonServerAPI */ \"./src/server/PythonServerAPI.js\");\n/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../utils */ \"./src/utils.js\");\n/* eslint-disable no-await-in-loop */\n\n\n\n\n/**\n * .\n * .\n * .\n * Provides methods for calibrating the user's speakers\n *\n * @extends AudioRecorder\n */\nclass AudioCalibrator extends _audioRecorder__WEBPACK_IMPORTED_MODULE_0__[\"default\"] {\n /**\n *\n * @param numCaptures\n * @param numMLSPerCapture\n * @example\n */\n constructor(numCaptures = 1, numMLSPerCapture = 1) {\n super();\n this.numCaptures = numCaptures;\n this.numMLSPerCapture = numMLSPerCapture;\n this.pyServerAPI = new _server_PythonServerAPI__WEBPACK_IMPORTED_MODULE_1__[\"default\"]();\n }\n\n /** @private */\n isCalibrating = false;\n\n /** @private */\n sourceAudioContext;\n\n /** @private */\n sourceAudioContextConvolved;\n\n /** @protected */\n numCalibratingRounds = 1;\n\n /** @protected */\n numSuccessfulCaptured = 0;\n\n /** @private */\n sourceSamplingRate;\n\n /** @protected */\n calibrationNodes = [];\n\n /** @protected */\n calibrationNodesConvolved = [];\n\n /** @protected */\n localAudio;\n\n /** @private */\n startTime;\n\n numCalibratingRoundsCompleted=0;\n /**\n * Called when a call is received.\n * Creates a local audio DOM element and attaches it to the page.\n *\n * @param targetElement\n * @example\n */\n createLocalAudio = targetElement => {\n this.localAudio = document.createElement('audio');\n this.localAudio.setAttribute('id', 'localAudio');\n targetElement.appendChild(this.localAudio);\n };\n\n addTimeStamp = taskName => {\n let startTaskTime = (new Date().getTime() - this.startTime) / 1000;\n this.timeStamp.push(`SOUND ${Number(startTaskTime.toFixed(1))} s. ${taskName}`);\n };\n\n recordBackground = async (\n stream,\n loopCondition = () => false,\n duringRecord = async () => {},\n afterRecord = async () => {},\n mode,\n checkRec\n ) => {\n console.warn('before recording background noise');\n // calibration loop\n while (loopCondition()) {\n // start recording\n console.warn('startRecording');\n await this.startRecording(stream);\n\n // do something during the recording such as sleep n amount of time\n console.warn('duringRecord');\n await duringRecord();\n\n // when done, stop recording\n console.warn('stopRecording');\n await this.stopRecording(mode, checkRec);\n\n // do something after recording such as start processing values\n console.warn('afterRecord');\n await afterRecord();\n\n // eslint-disable-next-line no-await-in-loop\n await (0,_utils__WEBPACK_IMPORTED_MODULE_2__.sleep)(1);\n }\n };\n\n /**\n *\n * @param {MediaStream} stream\n * @param {Function} playCalibrationAudio - (async) function that plays the calibration audio\n * @param {*} beforePlay - (async) function that is called before playing the audio\n * @param {*} beforeRecord - (async) function that is called before recording\n * @param {*} duringRecord - (async) function that is called while recording\n * @param {*} afterRecord - (async) function that is called after recording\n * @example\n */\n calibrationSteps = async (\n stream,\n playCalibrationAudio,\n beforePlay = async () => {},\n beforeRecord = async () => {},\n loopCondition = () => false,\n duringRecord = async () => {},\n afterRecord = async () => {},\n mode,\n checkRec\n ) => {\n // if it finished 2 attempts, it move to next iteration so reset numSuccessfulCaptured\n if (this.numSuccessfulCaptured >=2) {\n this.numSuccessfulCaptured = 0;\n }\n\n // do something before playing such as using the MLS to fill the buffers\n console.warn('beforePlay');\n await beforePlay();\n\n // play calibration audio\n console.warn('playCalibrationAudio');\n playCalibrationAudio();\n\n // do something before recording such as awaiting a certain amount of time\n console.warn('beforeRecord');\n await beforeRecord();\n const totalSec = this._calibrateSoundBurstPreSec + (this.numMLSPerCapture - this.num_mls_to_skip) * this._calibrateSoundBurstSec + this._calibrateSoundBurstPostSec;\n this.addTimeStamp(`Record ${totalSec.toFixed(1)} s of MLS with speaker+microphone IIR.`);\n\n // calibration loop\n while (loopCondition()) {\n if (this.isCalibrating) break;\n // start recording\n console.warn('startRecording');\n await this.startRecording(stream);\n\n if (this.isCalibrating) break;\n // do something during the recording such as sleep n amount of time\n console.warn('duringRecord');\n await duringRecord();\n\n if (this.isCalibrating) break;\n // when done, stop recording\n console.warn('stopRecording');\n await this.stopRecording(mode, checkRec);\n\n if (this.isCalibrating) break;\n // do something after recording such as start processing values\n console.warn('afterRecord');\n await afterRecord();\n\n // eslint-disable-next-line no-await-in-loop\n await (0,_utils__WEBPACK_IMPORTED_MODULE_2__.sleep)(1);\n }\n };\n\n /**\n *\n * @param {MediaStream} stream\n * @param {Function} playCalibrationAudio - (async) function that plays the calibration audio\n * @param {*} beforeRecord - (async) function that is called before recording\n * @param {*} afterRecord - (async) function that is called after recording\n * @param {Number} gainValue - the gain value to set the gain node to\n */\n volumeCalibrationSteps = async (\n stream,\n playCalibrationAudio,\n beforeRecord = () => {},\n afterRecord = () => {},\n gainValue,\n lCalib = 104.92978421490648,\n checkRec,\n checkSD,\n maxSD\n ) => {\n this.numCalibratingRoundsCompleted = 0;\n this.numCalibratingRounds = 2;\n // calibration loop\n while (!this.isCalibrating && this.numCalibratingRoundsCompleted < this.numCalibratingRounds) {\n if (this.isCalibrating) break;\n // before recording\n await beforeRecord(gainValue);\n if (this.isCalibrating) break;\n // start recording\n await this.startRecording(stream);\n if (this.isCalibrating) break;\n // play calibration audio\n console.log(`Calibration Round ${this.numCalibratingRoundsCompleted}`);\n await playCalibrationAudio();\n if (this.isCalibrating) break;\n // when done, stop recording\n console.log('Calibration Round Complete');\n await this.stopRecording('volume', checkRec);\n if (this.isCalibrating) break;\n // after recording\n await afterRecord(lCalib);\n const sd = await checkSD() || Infinity;\n if (sd <= maxSD) {\n console.log(`SD =${sd}, less than calibrateSound1000HzMaxSD_dB=${maxSD}`);\n this.numCalibratingRoundsCompleted += 2;\n } else {\n // if exist the maxSD do it one more time and only one more time\n console.log(`SD =${sd}, greater than calibrateSound1000HzMaxSD_dB=${maxSD}`);\n this.numCalibratingRoundsCompleted += 1;\n }\n this.calibrationNodes = [];\n\n // eslint-disable-next-line no-await-in-loop\n await (0,_utils__WEBPACK_IMPORTED_MODULE_2__.sleep)(2);\n }\n };\n\n /**\n * Getter for the isCalibrating property.\n *\n * @public\n * @returns - True if the audio is being calibrated, false otherwise.\n * @example\n */\n getCalibrationStatus = () => this.isCalibrating;\n\n /** .\n * .\n * .\n * Set the sampling rate to the value received from the listener\n *\n * @param {*} sinkSamplingRate\n * @param samplingRate\n * @example\n */\n setSamplingRates = samplingRate => {\n this.sinkSamplingRate = samplingRate;\n this.sourceSamplingRate = samplingRate;\n\n // this.emit('update', {message: `sampling at ${samplingRate}Hz...`});\n };\n\n setSampleSize = sampleSize => {\n this.sampleSize = sampleSize;\n };\n\n setFlags = flags => {\n this.flags = flags;\n }\n\n sampleRatesSet = () => this.sourceSamplingRate && this.sinkSamplingRate;\n\n addCalibrationNode = node => {\n this.calibrationNodes.push(node);\n };\n\n addCalibrationNodeConvolved = node => {\n this.calibrationNodesConvolved.push(node);\n };\n\n makeNewSourceAudioContext = () => {\n const options = {\n sampleRate: this.sourceSamplingRate,\n };\n\n this.sourceAudioContext = new (window.AudioContext ||\n window.webkitAudioContext ||\n window.audioContext)(options);\n\n return this.sourceAudioContext;\n };\n\n makeNewSourceAudioContextConvolved = () => {\n const options = {\n sampleRate: this.sourceSamplingRate,\n };\n\n this.sourceAudioContextConvolved = new (window.AudioContext ||\n window.webkitAudioContext ||\n window.audioContext)(options);\n\n return this.sourceAudioContextConvolved;\n };\n\n /** .\n * .\n * .\n * Download the result of the calibration roudns\n *\n * @example\n */\n downloadData = () => {\n const recordings = this.getAllRecordedSignals();\n const i = recordings.length - 1;\n (0,_utils__WEBPACK_IMPORTED_MODULE_2__.saveToCSV)(recordings[i], `recordedMLSignal_${i}_unconvolved.csv`);\n };\n downloadSingleUnfilteredRecording = () => {\n const recordings = this.getAllUnfilteredRecordedSignals();\n (0,_utils__WEBPACK_IMPORTED_MODULE_2__.saveToCSV)(recordings[recordings.length - 1], `recordedMLSignal_unconvolved.csv`);\n };\n downloadSingleFilteredRecording = () => {\n const recordings = this.getAllFilteredRecordedSignals();\n console.log('Single filtered recording should be of length: ' + recordings[0].length);\n (0,_utils__WEBPACK_IMPORTED_MODULE_2__.saveToCSV)(recordings[0], `recordedMLSignal_convolved.csv`);\n };\n downloadUnfilteredRecordings = () => {\n const recordings = this.getAllRecordedSignals();\n console.log('unfilterd download?');\n for (let i = 0; i < recordings.length; i++) {\n console.log(i);\n (0,_utils__WEBPACK_IMPORTED_MODULE_2__.saveToCSV)(recordings[i], `recordedMLSignal_${i}_unconvolved.csv`);\n }\n };\n downloadFilteredRecordings = () => {\n const recordings = this.getAllFilteredRecordedSignals();\n for (let i = 0; i < recordings.length; i++) {\n (0,_utils__WEBPACK_IMPORTED_MODULE_2__.saveToCSV)(recordings[i], `recordedMLSignal_${i}_convolved.csv`);\n }\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (AudioCalibrator);\n\n\n//# sourceURL=webpack://speakerCalibrator/./src/tasks/audioCalibrator.js?");
|
|
493
|
+
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _audioRecorder__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./audioRecorder */ \"./src/tasks/audioRecorder.js\");\n/* harmony import */ var _server_PythonServerAPI__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../server/PythonServerAPI */ \"./src/server/PythonServerAPI.js\");\n/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../utils */ \"./src/utils.js\");\n/* eslint-disable no-await-in-loop */\n\n\n\n\n/**\n * .\n * .\n * .\n * Provides methods for calibrating the user's speakers\n *\n * @extends AudioRecorder\n */\nclass AudioCalibrator extends _audioRecorder__WEBPACK_IMPORTED_MODULE_0__[\"default\"] {\n /**\n *\n * @param numCaptures\n * @param numMLSPerCapture\n * @example\n */\n constructor(numCaptures = 1, numMLSPerCapture = 1) {\n super();\n this.numCaptures = numCaptures;\n this.numMLSPerCapture = numMLSPerCapture;\n this.pyServerAPI = new _server_PythonServerAPI__WEBPACK_IMPORTED_MODULE_1__[\"default\"]();\n this.currentTime = 0;\n }\n\n /** @private */\n isCalibrating = false;\n\n /** @private */\n sourceAudioContext;\n\n /** @private */\n sourceAudioContextConvolved;\n\n /** @protected */\n numCalibratingRounds = 1;\n\n /** @protected */\n numSuccessfulCaptured = 0;\n\n /** @private */\n sourceSamplingRate;\n\n /** @protected */\n calibrationNodes = [];\n\n /** @protected */\n calibrationNodesConvolved = [];\n\n /** @protected */\n localAudio;\n\n /** @private */\n startTime;\n\n numCalibratingRoundsCompleted=0;\n /**\n * Called when a call is received.\n * Creates a local audio DOM element and attaches it to the page.\n *\n * @param targetElement\n * @example\n */\n createLocalAudio = targetElement => {\n this.localAudio = document.createElement('audio');\n this.localAudio.setAttribute('id', 'localAudio');\n targetElement.appendChild(this.localAudio);\n };\n\n addTimeStamp = taskName => {\n const currentTime = new Date().getTime(); // Current time in milliseconds\n const elapsedTime = (currentTime - this.startTime) / 1000; // Total time since start in seconds\n const stepDuration = elapsedTime - this.currentTime; // Time taken for the current step\n \n this.currentTime = elapsedTime; // Update for the next step\n \n // Round to 1 decimal place for consistent formatting\n \n;\n \n // Log the timestamp with aligned bars\n this.timeStamp.push(\n `SOUND Total: ${elapsedTime.toFixed(1)} s Step: ${stepDuration.toFixed(1)} s ${taskName}`\n );\n };\n \n\n recordBackground = async (\n stream,\n loopCondition = () => false,\n duringRecord = async () => {},\n afterRecord = async () => {},\n mode,\n checkRec\n ) => {\n console.warn('before recording background noise');\n // calibration loop\n while (loopCondition()) {\n // start recording\n console.warn('startRecording');\n await this.startRecording(stream);\n\n // do something during the recording such as sleep n amount of time\n console.warn('duringRecord');\n await duringRecord();\n\n // when done, stop recording\n console.warn('stopRecording');\n await this.stopRecording(mode, checkRec);\n\n // do something after recording such as start processing values\n console.warn('afterRecord');\n await afterRecord();\n\n // eslint-disable-next-line no-await-in-loop\n await (0,_utils__WEBPACK_IMPORTED_MODULE_2__.sleep)(1);\n }\n };\n\n /**\n *\n * @param {MediaStream} stream\n * @param {Function} playCalibrationAudio - (async) function that plays the calibration audio\n * @param {*} beforePlay - (async) function that is called before playing the audio\n * @param {*} beforeRecord - (async) function that is called before recording\n * @param {*} duringRecord - (async) function that is called while recording\n * @param {*} afterRecord - (async) function that is called after recording\n * @example\n */\n calibrationSteps = async (\n stream,\n playCalibrationAudio,\n beforePlay = async () => {},\n beforeRecord = async () => {},\n loopCondition = () => false,\n duringRecord = async () => {},\n afterRecord = async () => {},\n mode,\n checkRec\n ) => {\n // if it finished 2 attempts, it move to next iteration so reset numSuccessfulCaptured\n if (this.numSuccessfulCaptured >=2) {\n this.numSuccessfulCaptured = 0;\n }\n\n // do something before playing such as using the MLS to fill the buffers\n console.warn('beforePlay');\n await beforePlay();\n\n // play calibration audio\n console.warn('playCalibrationAudio');\n playCalibrationAudio();\n\n // do something before recording such as awaiting a certain amount of time\n console.warn('beforeRecord');\n await beforeRecord();\n const totalSec = this._calibrateSoundBurstPreSec + (this.numMLSPerCapture - this.num_mls_to_skip) * this._calibrateSoundBurstSec + this._calibrateSoundBurstPostSec;\n \n\n // calibration loop\n while (loopCondition()) {\n if (this.isCalibrating) break;\n // start recording\n console.warn('startRecording');\n await this.startRecording(stream);\n\n if (this.isCalibrating) break;\n // do something during the recording such as sleep n amount of time\n console.warn('duringRecord');\n await duringRecord();\n\n if (this.isCalibrating) break;\n // when done, stop recording\n console.warn('stopRecording');\n await this.stopRecording(mode, checkRec);\n\n if (this.isCalibrating) break;\n // do something after recording such as start processing values\n console.warn('afterRecord');\n await afterRecord();\n\n // eslint-disable-next-line no-await-in-loop\n await (0,_utils__WEBPACK_IMPORTED_MODULE_2__.sleep)(1);\n }\n };\n\n /**\n *\n * @param {MediaStream} stream\n * @param {Function} playCalibrationAudio - (async) function that plays the calibration audio\n * @param {*} beforeRecord - (async) function that is called before recording\n * @param {*} afterRecord - (async) function that is called after recording\n * @param {Number} gainValue - the gain value to set the gain node to\n */\n volumeCalibrationSteps = async (\n stream,\n playCalibrationAudio,\n beforeRecord = () => {},\n afterRecord = () => {},\n gainValue,\n lCalib = 104.92978421490648,\n checkRec,\n checkSD,\n maxSD\n ) => {\n this.numCalibratingRoundsCompleted = 0;\n this.numCalibratingRounds = 2;\n console.log(\"maxSD in VolumeCaibrationSteps: \", maxSD, '0' >= maxSD);\n // calibration loop\n while (!this.isCalibrating && this.numCalibratingRoundsCompleted < this.numCalibratingRounds) {\n if (this.isCalibrating) break;\n // before recording\n await beforeRecord(gainValue);\n if (this.isCalibrating) break;\n // start recording\n await this.startRecording(stream);\n if (this.isCalibrating) break;\n // play calibration audio\n console.log(`Calibration Round ${this.numCalibratingRoundsCompleted}`);\n await playCalibrationAudio();\n if (this.isCalibrating) break;\n // when done, stop recording\n console.log('Calibration Round Complete');\n await this.stopRecording('volume', checkRec);\n if (this.isCalibrating) break;\n // after recording\n await afterRecord(lCalib);\n const sd = await checkSD();\n if (sd <= maxSD) {\n console.log(`SD =${sd}, less than calibrateSound1000HzMaxSD_dB=${maxSD}`);\n this.numCalibratingRoundsCompleted += 2;\n } else {\n // if exist the maxSD do it one more time and only one more time\n console.log(`SD =${sd}, greater than calibrateSound1000HzMaxSD_dB=${maxSD}`);\n this.numCalibratingRoundsCompleted += 1;\n }\n this.calibrationNodes = [];\n\n // eslint-disable-next-line no-await-in-loop\n await (0,_utils__WEBPACK_IMPORTED_MODULE_2__.sleep)(2);\n }\n };\n\n /**\n * Getter for the isCalibrating property.\n *\n * @public\n * @returns - True if the audio is being calibrated, false otherwise.\n * @example\n */\n getCalibrationStatus = () => this.isCalibrating;\n\n /** .\n * .\n * .\n * Set the sampling rate to the value received from the listener\n *\n * @param {*} sinkSamplingRate\n * @param samplingRate\n * @example\n */\n setSamplingRates = samplingRate => {\n this.sinkSamplingRate = samplingRate;\n this.sourceSamplingRate = samplingRate;\n\n // this.emit('update', {message: `sampling at ${samplingRate}Hz...`});\n };\n\n setSampleSize = sampleSize => {\n this.sampleSize = sampleSize;\n };\n\n setFlags = flags => {\n this.flags = flags;\n }\n\n sampleRatesSet = () => this.sourceSamplingRate && this.sinkSamplingRate;\n\n addCalibrationNode = node => {\n this.calibrationNodes.push(node);\n };\n\n addCalibrationNodeConvolved = node => {\n this.calibrationNodesConvolved.push(node);\n };\n\n makeNewSourceAudioContext = () => {\n const options = {\n sampleRate: this.sourceSamplingRate,\n };\n\n this.sourceAudioContext = new (window.AudioContext ||\n window.webkitAudioContext ||\n window.audioContext)(options);\n\n return this.sourceAudioContext;\n };\n\n makeNewSourceAudioContextConvolved = () => {\n const options = {\n sampleRate: this.sourceSamplingRate,\n };\n\n this.sourceAudioContextConvolved = new (window.AudioContext ||\n window.webkitAudioContext ||\n window.audioContext)(options);\n\n return this.sourceAudioContextConvolved;\n };\n\n /** .\n * .\n * .\n * Download the result of the calibration roudns\n *\n * @example\n */\n downloadData = () => {\n const recordings = this.getAllRecordedSignals();\n const i = recordings.length - 1;\n (0,_utils__WEBPACK_IMPORTED_MODULE_2__.saveToCSV)(recordings[i], `recordedMLSignal_${i}_unconvolved.csv`);\n };\n downloadSingleUnfilteredRecording = () => {\n const recordings = this.getAllUnfilteredRecordedSignals();\n (0,_utils__WEBPACK_IMPORTED_MODULE_2__.saveToCSV)(recordings[recordings.length - 1], `recordedMLSignal_unconvolved.csv`);\n };\n downloadSingleFilteredRecording = () => {\n const recordings = this.getAllFilteredRecordedSignals();\n console.log('Single filtered recording should be of length: ' + recordings[0].length);\n (0,_utils__WEBPACK_IMPORTED_MODULE_2__.saveToCSV)(recordings[0], `recordedMLSignal_convolved.csv`);\n };\n downloadUnfilteredRecordings = () => {\n const recordings = this.getAllRecordedSignals();\n console.log('unfilterd download?');\n for (let i = 0; i < recordings.length; i++) {\n console.log(i);\n (0,_utils__WEBPACK_IMPORTED_MODULE_2__.saveToCSV)(recordings[i], `recordedMLSignal_${i}_unconvolved.csv`);\n }\n };\n downloadFilteredRecordings = () => {\n const recordings = this.getAllFilteredRecordedSignals();\n for (let i = 0; i < recordings.length; i++) {\n (0,_utils__WEBPACK_IMPORTED_MODULE_2__.saveToCSV)(recordings[i], `recordedMLSignal_${i}_convolved.csv`);\n }\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (AudioCalibrator);\n\n\n//# sourceURL=webpack://speakerCalibrator/./src/tasks/audioCalibrator.js?");
|
|
494
494
|
|
|
495
495
|
/***/ }),
|
|
496
496
|
|
|
@@ -512,7 +512,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _myE
|
|
|
512
512
|
/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
|
|
513
513
|
|
|
514
514
|
"use strict";
|
|
515
|
-
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 _powerCheck__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../powerCheck */ \"./src/powerCheck.js\");\n/* harmony import */ var _config_firebase__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../config/firebase */ \"./src/config/firebase.js\");\n/* harmony import */ var firebase_database__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! firebase/database */ \"./node_modules/firebase/database/dist/esm/index.esm.js\");\n/* harmony import */ var firebase_firestore__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! firebase/firestore */ \"./node_modules/firebase/firestore/dist/esm/index.esm.js\");\n\n\n\n\n\n\n\n\n\n//import { phrases } from '../../../dist/example/i18n';\n\n/**\n *\n */\nclass Combination extends _audioCalibrator__WEBPACK_IMPORTED_MODULE_0__[\"default\"] {\n /**\n * Default constructor. Creates an instance with any number of paramters passed or the default parameters defined here.\n *\n * @param {Object<boolean, number, number, number>} calibratorParams - paramter object\n * @param {boolean} [calibratorParams.download = false] - boolean flag to download captures\n * @param {number} [calibratorParams.mlsOrder = 18] - order of the MLS to be generated\n * @param {number} [calibratorParams.numCaptures = 5] - number of captures to perform\n * @param {number} [calibratorParams.numMLSPerCapture = 2] - number of bursts of MLS per capture\n */\n constructor({\n download = false,\n mlsOrder = 18,\n numCaptures = 3,\n numMLSPerCapture = 2,\n lowHz = 20,\n highHz = 10000,\n }) {\n super(numCaptures, numMLSPerCapture);\n this.#mlsOrder = parseInt(mlsOrder, 10);\n this.#P = 2 ** mlsOrder - 1;\n this.#download = download;\n this.#mls = [];\n this.#lowHz = lowHz;\n this.#highHz = highHz;\n }\n\n /** @private */\n stepNum = 0;\n\n /** @private */\n totalSteps = 25;\n\n /** @private */\n #download;\n\n /** @private */\n #mlsGenInterface;\n\n /** @private */\n #mlsBufferView;\n\n /** @private */\n componentInvertedImpulseResponse = null;\n\n /** @private */\n systemInvertedImpulseResponse = null;\n\n //averaged and subtracted ir returned from calibration used to calculated iir\n /** @private */\n ir = null;\n\n /** @private */\n impulseResponses = [];\n\n /** @private */\n #mlsOrder;\n\n /** @private */\n #lowHz;\n\n /** @private */\n #highHz;\n\n /** @private */\n #mls;\n\n /** @private */\n #P;\n\n /** @private */\n #audioContext;\n\n /** @private */\n offsetGainNode;\n\n /** @private */\n componentConvolution;\n\n /** @private */\n componentConvolutionNoBandpass;\n\n /** @private */\n componentIROrigin = {\n Freq: [],\n Gain: [],\n };\n\n /** @private */\n systemConvolution;\n\n /** @private */\n systemConvolutionNoBandpass;\n\n ////////////////////////volume\n /** @private */\n #CALIBRATION_TONE_FREQUENCY = 1000; // Hz\n\n /** @private */\n #CALIBRATION_TONE_TYPE = 'sine';\n\n CALIBRATION_TONE_DURATION = 5; // seconds\n calibrateSound1000HzPreSec = 3.5;\n calibrateSound1000HzSec = 1.0;\n calibrateSound1000HzPostSec = 0.5;\n\n /** @private */\n outDBSPL = null;\n THD = null;\n outDBSPL1000 = null;\n\n /** @private */\n TAPER_SECS = 0.01; // seconds\n\n /** @private */\n status_denominator = 8;\n\n /** @private */\n status_numerator = 0;\n\n /** @private */\n percent_complete = 0;\n\n /** @private */\n status = ``;\n\n /**@private */\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>`;\n\n /**@private */\n componentIR = null;\n\n /**@private */\n oldComponentIR = null;\n\n /**@private */\n systemIR = null;\n\n /**@private */\n _calibrateSoundCheck = '';\n\n deviceType = null;\n\n deviceName = null;\n\n deviceInfo = null;\n\n desired_time_per_mls = 0;\n\n num_mls_to_skip = 0;\n\n desired_sampling_rate = 0;\n\n #currentConvolution = [];\n\n mode = 'unfiltered';\n\n sourceNode;\n\n autocorrelations = [];\n\n iirLength = 0;\n\n irLength = 0;\n\n calibrateSoundIIRPhase = 'linear';\n\n componentInvertedImpulseResponseNoBandpass = [];\n\n componentIRInTimeDomain = [];\n\n systemInvertedImpulseResponseNoBandpass = [];\n\n _calibrateSoundBackgroundSecs;\n\n _calibrateSoundSmoothOctaves;\n\n background_noise = {};\n\n numSuccessfulBackgroundCaptured;\n\n _calibrateSoundBurstDb;\n\n _calibrateSoundBurstFilteredExtraDb;\n\n _calibrateSoundBurstLevelReTBool;\n\n SDofFilteredRange = {\n mls: undefined,\n component: undefined,\n system: undefined,\n };\n\n transducerType = 'Loudspeaker';\n\n componentIRPhase = [];\n\n systemIRPhase = [];\n\n webAudioDeviceNames = {loudspeaker: '', microphone: '', loudspeakerText: '', microphoneText: ''};\n\n waveforms = {\n volume: {},\n };\n\n recordingChecks = {\n volume: {},\n unfiltered: [],\n system: [],\n component: [],\n warnings:[]\n };\n\n inDB;\n\n soundCheck = '';\n\n filteredMLSRange = {\n component: {\n Min: null,\n Max: null,\n },\n system: {\n Min: null,\n Max: null,\n },\n };\n\n /** @private */\n timeStamp = [];\n\n restartCalibration = false;\n\n calibrateSoundLimit = 1;\n\n filteredMLSAttenuation = {\n component: 1,\n system: 1,\n maxAbsSystem: 1,\n maxAbsComponent: 1,\n };\n\n //parameter result from volume calibration\n T = 0;\n //gainDBSPL result from volume calibration\n gainDBSPL = 0;\n //not always just using _calibrateSoundBurstDb for MLS so created a new parameter\n power_dB = 0;\n\n //system\n systemAttenuatorGainDB = 0;\n systemFMaxHz = 0;\n\n //component\n componentAttentuatorGainDB = 0;\n componentFMaxHz = 0;\n\n dL_n;\n L_new_n;\n fs2;\n icapture = 0;\n\n /**generate string template that gets reevaluated as variable increases */\n generateTemplate = (status) => {\n if (this.isCalibrating) {\n return '';\n }\n if (this.percent_complete > 100) {\n this.percent_complete = 100;\n }\n let MLSsd = '';\n let componentSD = '';\n let systemSD = '';\n let flags = '';\n const soundSubtitle = document.getElementById(this.soundSubtitleId);\n if(soundSubtitle)\n {\n const reportWebAudioNames = `${this.webAudioDeviceNames.loudspeakerText}<br/> ${this.webAudioDeviceNames.microphoneText}`;\n soundSubtitle.innerHTML = reportWebAudioNames;\n }\n \n const samplingParamText = this.phrases.RC_SamplingHzBits[this.language].replace('111', this.sourceSamplingRate).replace('222',this.sinkSamplingRate).replace('333', this.calibrateSoundSamplingDesiredBits);\n const reportParameters = `${samplingParamText}`;\n if (this.flags) {\n flags = `<br> autoGainControl: ${this.flags.autoGainControl}; \n echoCancellation: ${this.flags.echoCancellation};\n noiseSuppression: ${this.flags.noiseSuppression}`;\n }\n if (this.SDofFilteredRange['mls']) {\n MLSsd = `<br> Recorded MLS power SD: ${this.SDofFilteredRange['mls']} dB`;\n }\n if (this.SDofFilteredRange['system']) {\n systemSD = `<br> Loudspeaker+Microphone correction SD: ${this.SDofFilteredRange['system']} dB`;\n }\n if (this.SDofFilteredRange['component']) {\n componentSD = `<br> ${this.transducerType} correction SD: ${this.SDofFilteredRange['component']} dB`;\n }\n const template = `<div style=\"display: flex; justify-content: flex-start; margin-top: 0.4rem;\"><div style=\"width: 100%; 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>`;\n return `\n ${reportParameters} \n ${MLSsd}\n ${systemSD}\n ${componentSD}\n ${flags}\n <br>${status}\n ${template }`;\n };\n\n /** increment numerator and percent for status bar */\n incrementStatusBar = () => {\n this.status_numerator += 1;\n this.percent_complete = (this.status_numerator / this.status_denominator) * 100;\n };\n\n setDeviceType = deviceType => {\n this.deviceType = deviceType;\n };\n\n setDeviceName = deviceName => {\n this.deviceName = deviceName;\n };\n\n setDeviceInfo = deviceInfo => {\n this.deviceInfo = deviceInfo;\n };\n\n /** .\n * .\n * .\n * Sends all the computed impulse responses to the backend server for processing\n *\n * @returns sets the resulting inverted impulse response to the class property\n * @example\n */\n sendSystemImpulseResponsesToServerForProcessing = async () => {\n this.addTimeStamp('Compute system IIR');\n const computedIRs = await Promise.all(this.impulseResponses);\n const filteredComputedIRs = computedIRs.filter(element => {\n return element != undefined;\n }); //log any errors that are found in this step\n console.log('filteredComputedIRs', filteredComputedIRs);\n const mls = this.#mls[this.icapture];\n const lowHz = this.#lowHz; //gain of 1 below cutoff, need gain of 0\n const highHz = this.#highHz; //check error for anything other than 10 kHz\n const iirLength = this.iirLength;\n this.stepNum += 1;\n console.log('send impulse responses to server: ' + this.stepNum);\n this.status = this.generateTemplate(`All Hz Calibration: computing the IIR...`.toString()).toString();\n this.emit('update', {message: this.status});\n return await this.pyServerAPI\n .getSystemInverseImpulseResponseWithRetry({\n payload: filteredComputedIRs.slice(0, this.numCaptures),\n mls,\n lowHz,\n highHz,\n iirLength,\n sampleRate: this.sourceSamplingRate || 96000,\n mlsAmplitude: Math.pow(10, this.power_dB / 20),\n calibrateSoundBurstFilteredExtraDb: this._calibrateSoundBurstFilteredExtraDb,\n calibrateSoundIIRPhase: this.calibrateSoundIIRPhase,\n })\n .then(async res => {\n this.stepNum += 1;\n console.log('got impulse response ' + this.stepNum);\n this.incrementStatusBar();\n this.status = this.generateTemplate(`All Hz Calibration: done computing the IIR...`.toString()).toString();\n this.emit('update', {message: this.status});\n this.systemInvertedImpulseResponse = res['iir'];\n this.systemIR = res['ir'];\n this.systemInvertedImpulseResponseNoBandpass = res['iirNoBandpass'];\n this.systemAttenuatorGainDB = res['attenuatorGain_dB'];\n this.systemFMaxHz = res['fMaxHz'];\n await this.pyServerAPI.checkMemory();\n await this.pyServerAPI\n .getConvolution({\n mls,\n inverse_response: this.systemInvertedImpulseResponse,\n inverse_response_no_bandpass: this.systemInvertedImpulseResponseNoBandpass,\n attenuatorGain_dB: this.systemAttenuatorGainDB,\n mls_amplitude: Math.pow(10, this.power_dB / 20),\n })\n .then(result => {\n console.log(result);\n this.systemConvolution = result['convolution'];\n this.systemConvolutionNoBandpass = result['convolution_no_bandpass'];\n });\n\n // attenuate the system convolution if the amplitude is greater than this.calibrateSoundLimit\n // find max of absolute value of system convolution\n\n const max = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.findMaxValue)(this.systemConvolution);\n this.filteredMLSAttenuation.system =\n this.systemConvolution.reduce((a, b) => a + b ** 2, 0) / this.systemConvolution.length;\n this.filteredMLSAttenuation.maxAbsSystem = max;\n })\n .catch(err => {\n console.error(err);\n });\n };\n\n /** .\n * .\n * .\n * Sends all the computed impulse responses to the backend server for processing\n *\n * @returns sets the resulting inverted impulse response to the class property\n * @example\n */\n sendComponentImpulseResponsesToServerForProcessing = async () => {\n this.addTimeStamp('Compute component IIR');\n const computedIRs = await Promise.all(this.impulseResponses);\n const filteredComputedIRs = computedIRs.filter(element => {\n return element != undefined;\n });\n let componentIRGains = this.componentIR['Gain'];\n const componentIRFreqs = this.componentIR['Freq'];\n //normalize the component IR gains\n componentIRGains = componentIRGains.map(value => {\n return value + this._calibrateSoundBurstScalarDB - this._calibrateSoundBurstDb;\n });\n if (this._calibrateSoundBurstNormalizeBy1000HzGainBool) {\n const sineGainAt1000Hz_dB = this.gainDBSPL;\n componentIRGains = componentIRGains.map(value => {\n return value - sineGainAt1000Hz_dB;\n });\n }\n const mls = this.#mls[this.icapture];\n const lowHz = this.#lowHz;\n const iirLength = this.iirLength;\n const irLength = this.irLength;\n const highHz = this.#highHz;\n this.stepNum += 1;\n console.log('send impulse responses to server: ' + this.stepNum);\n this.status = this.generateTemplate(`All Hz Calibration: computing the IIR...`.toString()).toString();\n this.emit('update', {message: this.status});\n console.log()\n return this.pyServerAPI\n .getComponentInverseImpulseResponseWithRetry({\n payload: filteredComputedIRs.slice(0, this.numCaptures),\n mls,\n lowHz,\n highHz,\n iirLength,\n componentIRGains,\n componentIRFreqs,\n sampleRate: this.sourceSamplingRate || 96000,\n mlsAmplitude: Math.pow(10, this.power_dB / 20),\n irLength,\n calibrateSoundSmoothOctaves: this._calibrateSoundSmoothOctaves,\n calibrateSoundSmoothMinBandwidthHz: this._calibrateSoundSmoothMinBandwidthHz,\n calibrateSoundBurstFilteredExtraDb: this._calibrateSoundBurstFilteredExtraDb,\n calibrateSoundIIRPhase: this.calibrateSoundIIRPhase,\n })\n .then(async res => {\n this.stepNum += 1;\n console.log('got impulse response ' + this.stepNum);\n this.incrementStatusBar();\n this.status = this.generateTemplate(`All Hz Calibration: done computing the IIR...`.toString()).toString();\n this.emit('update', {message: this.status});\n this.componentInvertedImpulseResponse = res['iir'];\n this.componentInvertedImpulseResponseNoBandpass = res['iirNoBandpass'];\n this.componentIR['Gain'] = res['ir'];\n this.componentIR['Freq'] = res['frequencies'];\n this.componentIRPhase = res['component_angle'];\n this.systemIRPhase = res['system_angle'];\n this.componentIROrigin['Freq'] = res['frequencies'];\n this.componentIROrigin['Gain'] = res['irOrigin'];\n this.componentIRInTimeDomain = res['irTime'];\n this.componentAttenuatorGainDB = res['attenuatorGain_dB'];\n this.componentFMaxHz = res['fMaxHz'];\n await this.pyServerAPI.checkMemory();\n await this.pyServerAPI\n .getConvolution({\n mls,\n inverse_response: this.componentInvertedImpulseResponse,\n inverse_response_no_bandpass: this.componentInvertedImpulseResponseNoBandpass,\n attenuatorGain_dB: this.componentAttenuatorGainDB,\n mls_amplitude: Math.pow(10, this.power_dB / 20),\n })\n .then(result => {\n console.log(result);\n this.componentConvolution = result['convolution'];\n this.componentConvolutionNoBandpass = result['convolution_no_bandpass'];\n });\n // attenuate the component convolution if the amplitude is greater than this.calibrateSoundLimit\n // find max of absolute value of component convolution\n const max = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.findMaxValue)(this.componentConvolution);\n // if (max > this.calibrateSoundLimit) {\n // const gain = this.calibrateSoundLimit / max;\n // // apply gain to component convolution\n // this.componentConvolution = this.componentConvolution.map(value => value * gain);\n // this.filteredMLSAttenuation.component = gain;\n // }\n this.filteredMLSAttenuation.component =\n this.componentConvolution.reduce((a, b) => a + b ** 2, 0) /\n this.componentConvolution.length;\n this.filteredMLSAttenuation.maxAbsComponent = max;\n })\n .catch(err => {\n // this.emit('InvertedImpulseResponse', {res: false});\n console.error(err);\n });\n };\n\n sendBackgroundRecording = () => {\n const allSignals = this.getAllBackgroundRecordings();\n const numSignals = allSignals.length;\n const background_rec_whole = allSignals[numSignals - 1];\n const fraction = 0.5 / (this._calibrateSoundBackgroundSecs + 0.5);\n // Calculate the starting index for slicing the array\n const startIndex = Math.round(fraction * background_rec_whole.length);\n // Slice the array from the calculated start index to the end of the array\n const background_rec = background_rec_whole.slice(startIndex);\n console.log('Sending background recording to server for processing');\n this.addTimeStamp('Compute background PSD');\n this.pyServerAPI\n .getBackgroundNoisePSDWithRetry({\n background_rec,\n sampleRate: this.sourceSamplingRate || 96000,\n })\n .then(res => {\n if (this.numSuccessfulBackgroundCaptured < 1) {\n this.numSuccessfulBackgroundCaptured += 1;\n //storing all background data in background_psd object\n this.background_noise['x_background'] = res['x_background'];\n this.background_noise['y_background'] = res['y_background'];\n this.background_noise['recording'] = background_rec;\n }\n })\n .catch(err => {\n console.error(err);\n });\n };\n\n /** .\n * .\n * .\n * Sends the recorded signal, or a given csv string of a signal, to the back end server for processing\n *\n * @param {<array>String} signalCsv - Optional csv string of a previously recorded signal, if given, this signal will be processed\n * @example\n */\n sendRecordingToServerForProcessing = async signalCsv => {\n const allSignals = this.getAllUnfilteredRecordedSignals();\n console.log(\n 'Obtaining last all hz unfiltered recording from #allHzUnfilteredRecordings to send to server for processing'\n );\n const numSignals = allSignals.length;\n const mls = this.#mlsBufferView[this.icapture];\n const payload =\n signalCsv && signalCsv.length > 0 ? (0,_utils__WEBPACK_IMPORTED_MODULE_1__.csvToArray)(signalCsv) : allSignals[numSignals - 1];\n console.log('sending rec');\n this.stepNum += 1;\n console.log('send rec ' + this.stepNum);\n this.status = this.generateTemplate(`All Hz Calibration Step: computing the IR of the last recording...`.toString()).toString();\n this.emit('update', {message: this.status});\n if (this.isCalibrating) return null;\n await this.pyServerAPI\n .allHzPowerCheck({\n payload,\n sampleRate: this.sourceSamplingRate || 96000,\n binDesiredSec: this._calibrateSoundPowerBinDesiredSec,\n burstSec: this.desired_time_per_mls,\n repeats: this.numMLSPerCapture - this.num_mls_to_skip,\n warmUp: this.num_mls_to_skip \n })\n .then(async result => {\n if (result) {\n if (result['sd'] > this._calibrateSoundBurstMaxSD_dB && \n this.numSuccessfulCaptured == 0) {\n console.log('SD: ' + result['sd'] + ', greater than _calibrateSoundBurstMaxSD_dB: ' + this._calibrateSoundBurstMaxSD_dB);\n this.recordingChecks['unfiltered'].push(result);\n this.clearLastUnfilteredRecordedSignals();\n this.numSuccessfulCaptured +=1;\n } else {\n if (result['sd'] <= this._calibrateSoundBurstMaxSD_dB) {\n console.log('SD: ' + result['sd'] + ', less than _calibrateSoundBurstMaxSD_dB: ' + this._calibrateSoundBurstMaxSD_dB);\n } else {\n console.log('SD: ' + result['sd'] + ', greater than _calibrateSoundBurstMaxSD_dB: ' + this._calibrateSoundBurstMaxSD_dB);\n this.recordingChecks['warnings'].push(`All Hz. Re-recorded ${this.inDB} dB because SD ${result['sd']} > ${this._calibrateSoundBurstMaxSD_dB} dB`);\n this.status = this.generateTemplate(`All Hz: Re-recording at ${this.inDB} dB because SD ${result['sd']} > ${this._calibrateSoundBurstMaxSD_dB} dB`.toString()).toString();\n this.emit('update', {\n message: this.status,\n });\n }\n if (this.numSuccessfulCaptured == 1) {\n console.log('pop last unfiltered mls volume check');\n this.recordingChecks['unfiltered'].pop();\n }\n this.recordingChecks['unfiltered'].push(result);\n // let start = new Date().getTime() / 1000;\n // const payloadT = tf.tensor1d(payload);\n // payloadT.print();\n // const xfft = payloadT.rfft(); // tf.spe\n // xfft.array().then(array => {\n // console.log(\"fft:\", array);\n // let setItem = new Set(array);\n // console.log(\"all zero\", setItem.size === 1 && setItem.has(0));\n // });\n // console.log(\"dimention:\", xfft.shape);\n // let end = new Date().getTime() / 1000;\n // console.log(\"Time taken:\", end - start, \"seconds\");\n console.log('start calculate impulse response');\n const usedPeriodStart = this.num_mls_to_skip * this.sourceSamplingRate;\n const payload_skipped_warmUp = payload.slice(usedPeriodStart);\n await this.pyServerAPI\n .getAutocorrelation({\n mls:mls,\n payload: payload_skipped_warmUp,\n sampleRate: this.sourceSamplingRate || 96000,\n numPeriods: this.numMLSPerCapture - this.num_mls_to_skip,\n })\n .then(async res => {\n this.autocorrelations.push(res['autocorrelation']);\n this.fs2 = res['fs2'];\n this.L_new_n = res['L_new_n'];\n this.dL_n = res['dL_n'];\n this.impulseResponses.push(\n await this.pyServerAPI\n .getImpulseResponse({\n mls,\n sampleRate: this.sourceSamplingRate || 96000,\n numPeriods: this.numMLSPerCapture - this.num_mls_to_skip,\n sig: payload_skipped_warmUp,\n fs2: this.fs2,\n L_new_n: this.L_new_n,\n dL_n: this.dL_n,\n })\n .then(res => {\n this.numSuccessfulCaptured += 2;\n this.stepNum += 1;\n console.log('got impulse response ' + this.stepNum);\n this.incrementStatusBar();\n this.status = this.generateTemplate(`All Hz Calibration: ${this.numSuccessfulCaptured}/${this.numCaptures} IRs computed...`.toString()).toString();\n this.emit('update', {\n message: this.status,\n });\n return res['ir'];\n })\n .catch(err => {\n console.error(err);\n })\n );\n });\n }\n console.log('number of unfiltered recording checks:' + this.recordingChecks['unfiltered'].length);\n }\n })\n .catch(err => {\n console.error(err);\n });\n };\n\n /**\n * Passed to the calibration steps function, awaits the desired amount of seconds to capture the desired number\n * of MLS periods defined in the constructor.\n *\n * @example\n */\n #awaitDesiredMLSLength = async () => {\n // seconds per MLS = P / SR\n // await N * P / SR\n this.stepNum += 1;\n console.log('await desired length ' + this.stepNum);\n this.status = this.generateTemplate(`All Hz Calibration: sampling the calibration signal...`.toString() +\n `\\niteration ${this.stepNum}`);\n this.emit('update', {\n message: this.status,\n });\n let time_to_wait = 0;\n if (this.mode === 'unfiltered') {\n //unfiltered\n time_to_wait = (this.#mls[0].length / this.sourceSamplingRate) * this.numMLSPerCapture;\n time_to_wait = time_to_wait + this._calibrateSoundBurstPostSec;\n } else if (this.mode === 'filtered') {\n //filtered\n // time_to_wait =\n // (this.#currentConvolution.length / this.sourceSamplingRate) *\n // (this.numMLSPerCapture / (this.num_mls_to_skip + this.numMLSPerCapture));\n time_to_wait =\n (this.#currentConvolution.length / this.sourceSamplingRate) * this.numMLSPerCapture;\n time_to_wait = time_to_wait + this._calibrateSoundBurstPostSec;\n } else {\n throw new Error('Mode broke in awaitDesiredMLSLength');\n }\n\n await (0,_utils__WEBPACK_IMPORTED_MODULE_1__.sleep)(time_to_wait);\n };\n\n /**\n * Passed to the background noise recording function, awaits the desired amount of seconds to capture the desired number\n * of seconds of background noise\n *\n * @example\n */\n #awaitBackgroundNoiseRecording = async () => {\n console.log(\n 'Waiting ' + this._calibrateSoundBackgroundSecs + ' second(s) to record background noise'\n );\n let time_to_wait = this._calibrateSoundBackgroundSecs + 0.5;\n this.addTimeStamp(`Record ${time_to_wait.toFixed(1)} s of background.`)\n await (0,_utils__WEBPACK_IMPORTED_MODULE_1__.sleep)(time_to_wait);\n };\n\n /** .\n * .\n * .\n * Passed to the calibration steps function, awaits the onset of the signal to ensure a steady state\n *\n * @example\n */\n #awaitSignalOnset = async () => {\n this.stepNum += 1;\n console.log('await signal onset ' + this.stepNum);\n this.status = this.generateTemplate(`All Hz Calibration: waiting for the signal to stabilize...`.toString());\n this.emit('update', {\n message: this.status,\n });\n let number_of_bursts_to_skip = 0;\n let time_to_sleep = 0;\n if (this.mode === 'unfiltered') {\n time_to_sleep = (this.#mls[0].length / this.sourceSamplingRate) * number_of_bursts_to_skip;\n } else if (this.mode === 'filtered') {\n console.log(this.#currentConvolution.length);\n // time_to_sleep =\n // (this.#currentConvolution.length / this.sourceSamplingRate) *\n // (number_of_bursts_to_skip / (number_of_bursts_to_skip + this.numMLSPerCapture));\n time_to_sleep =\n (this.#currentConvolution.length / this.sourceSamplingRate) * number_of_bursts_to_skip;\n } else {\n throw new Error('Mode broke in awaitSignalOnset');\n }\n await (0,_utils__WEBPACK_IMPORTED_MODULE_1__.sleep)(time_to_sleep);\n };\n\n /**\n * Called immediately after a recording is captured. Used to process the resulting signal\n * whether by sending the result to a server or by computing a result locally.\n *\n * @example\n */\n #afterMLSRecord = async () => {\n console.log('after record');\n this.addTimeStamp(`Send unfiltered MLS to the server`);\n await this.sendRecordingToServerForProcessing();\n };\n\n #afterMLSwIIRRecord = async () => {\n await this.checkPowerVariation();\n };\n\n /** .\n * .\n * .\n * Created an S Curver Buffer to taper the signal onset\n *\n * @param {*} onSetBool\n * @returns\n * @example\n */\n createSCurveBuffer = (onSetBool = true) => {\n const curve = new Float32Array(this.TAPER_SECS * this.sourceSamplingRate + 1);\n const frequency = 1 / (4 * this.TAPER_SECS);\n let j = 0;\n for (let i = 0; i < this.TAPER_SECS * this.sourceSamplingRate + 1; i += 1) {\n const phase = 2 * Math.PI * frequency * j;\n const onsetTaper = Math.pow(Math.sin(phase), 2);\n const offsetTaper = Math.pow(Math.cos(phase), 2);\n curve[i] = onSetBool ? onsetTaper : offsetTaper;\n j += 1 / this.sourceSamplingRate;\n }\n return curve;\n };\n\n static createInverseSCurveBuffer = (length, phase) => {\n const curve = new Float32Array(length);\n let i;\n let j = length - 1;\n for (i = 0; i < length; i += 1) {\n // scale the curve to be between 0-1\n curve[i] = Math.sin((Math.PI * j) / length - phase) / 2 + 0.5;\n j -= 1;\n }\n return curve;\n };\n\n /**\n * Construct a Calibration Node with the calibration parameters.\n *\n * @param dataBuffer\n * @private\n * @example\n */\n #createCalibrationNodeFromBuffer = dataBuffer => {\n console.log('length databuffer');\n console.log(dataBuffer.length);\n if (!this.sourceAudioContext) {\n this.makeNewSourceAudioContext();\n }\n\n const buffer = this.sourceAudioContext.createBuffer(\n 1, // number of channels\n dataBuffer.length,\n this.sourceAudioContext.sampleRate // sample rate\n );\n\n const data = buffer.getChannelData(0); // get data\n\n // fill the buffer with our data\n try {\n for (let i = 0; i < dataBuffer.length; i += 1) {\n data[i] = dataBuffer[i];\n }\n } catch (error) {\n console.error(error);\n }\n\n this.sourceNode = this.sourceAudioContext.createBufferSource();\n\n this.sourceNode.buffer = buffer;\n\n if (this.mode === 'filtered') {\n //used to not loop filtered\n this.sourceNode.loop = true;\n } else {\n this.sourceNode.loop = true;\n }\n\n this.sourceNode.connect(this.sourceAudioContext.destination);\n\n this.addCalibrationNode(this.sourceNode);\n };\n\n /**\n * Given a data buffer, creates the required calibration node\n *\n * @param {*} dataBufferArray\n * @example\n */\n #setCalibrationNodesFromBuffer = (dataBufferArray = [this.#mlsBufferView[this.icapture]]) => {\n if (dataBufferArray.length === 1) {\n this.#createCalibrationNodeFromBuffer(dataBufferArray[0]);\n } else {\n throw new Error('The length of the data buffer array must be 1');\n }\n };\n\n /**\n * Creates an audio context and plays it for a few seconds.\n *\n * @private\n * @returns - Resolves when the audio is done playing.\n * @example\n */\n #playCalibrationAudio = () => {\n this.calibrationNodes[0].start(0);\n this.status = ``;\n if (this.mode === 'unfiltered') {\n console.log('play calibration audio ' + this.stepNum);\n\n const pre = this._calibrateSoundBurstPreSec; \n const repeats = this._calibrateSoundBurstRepeats; \n const burst = this._calibrateSoundBurstSec; \n const post = this._calibrateSoundBurstPostSec; \n const total_dur = pre + repeats * burst + post;\n this.addTimeStamp(\n `Record ${total_dur.toFixed(1)} s of MLS ver. ${this.icapture}, unfiltered. ` +\n `(${pre.toFixed(1)} s pre + ${repeats}×${burst.toFixed(1)} s used + ${post.toFixed(1)} s post).`\n );\n this.status = this.generateTemplate(`All Hz Calibration: playing the calibration tone...`.toString()).toString();\n } else if (this.mode === 'filtered') {\n console.log('play convolved audio ' + this.stepNum);\n this.status = this.generateTemplate().toString( `All Hz Calibration: playing the convolved calibration tone...`.toString());\n } else {\n throw new Error('Mode is incorrect');\n }\n this.emit('update', {message: this.status});\n this.stepNum += 1;\n console.log('sink sampling rate');\n console.log(this.sinkSamplingRate);\n console.log('source sampling rate');\n console.log(this.sourceSamplingRate);\n console.log('sample size');\n console.log(this.sampleSize);\n };\n\n /** .\n * .\n * .\n * Stops the audio with tapered offset\n *\n * @example\n */\n stopCalibrationAudio = () => {\n if (this.calibrationNodes.length === 0) {\n return;\n }\n this.calibrationNodes[0].stop(0);\n this.calibrationNodes = [];\n if (this.sourceNode) this.sourceNode.disconnect();\n this.stepNum += 1;\n console.log('stop calibration audio ' + this.stepNum);\n this.status = this.generateTemplate(`All Hz Calibration: stopping the calibration tone...`.toString()).toString();\n this.emit('update', {message: this.status});\n };\n\n playMLSwithIIR = async (stream, convolution) => {\n let checkRec = false;\n this.mode = 'filtered';\n console.log('play mls with iir');\n //this.invertedImpulseResponse = iir\n\n await this.calibrationSteps(\n stream,\n this.#playCalibrationAudio, // play audio func (required)\n this.#createCalibrationNodeFromBuffer(convolution), // before play func\n this.#awaitSignalOnset, // before record\n () => this.numSuccessfulCaptured < 2,\n this.#awaitDesiredMLSLength, // during record\n this.#afterMLSwIIRRecord, // after record\n this.mode,\n checkRec\n );\n };\n\n bothSoundCheck = async stream => {\n let iir_ir_and_plots;\n this.#currentConvolution = this.componentConvolution;\n this.filteredMLSRange.component.Min = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.findMinValue)(this.#currentConvolution);\n this.filteredMLSRange.component.Max = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.findMaxValue)(this.#currentConvolution);\n const pre = this._calibrateSoundBurstPreSec; \n const repeats = this._calibrateSoundBurstRepeats; \n const burst = this._calibrateSoundBurstSec; \n const post = this._calibrateSoundBurstPostSec; \n const total_dur = pre + repeats * burst + post;\n this.soundCheck = 'component';\n this.addTimeStamp(`Record ${total_dur} s of MLS with ${this.soundCheck} IIR.”`)\n \n if (this.isCalibrating) return null;\n await this.playMLSwithIIR(stream, this.#currentConvolution);\n this.stopCalibrationAudio();\n let component_conv_recs = this.getAllFilteredRecordedSignals();\n\n if (this.componentAttentuatorGainDB != 0) {\n let linearScaleAttenuation = Math.pow(10, this.componentAttentuatorGainDB / 20);\n component_conv_recs = component_conv_recs.map(rec => {\n return rec.map(value => value / this.linearScaleAttenuation);\n });\n }\n\n let return_component_conv_rec = component_conv_recs[component_conv_recs.length - 1];\n this.clearAllFilteredRecordedSignals();\n\n this.numSuccessfulCaptured = 0;\n this.#currentConvolution = this.systemConvolution;\n this.filteredMLSRange.system.Min = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.findMinValue)(this.#currentConvolution);\n this.filteredMLSRange.system.Max = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.findMaxValue)(this.#currentConvolution);\n this.soundCheck = 'system';\n\n this.addTimeStamp(`Record ${total_dur} s of MLS with ${this.soundCheck} IIR.”`)\n\n\n if (this.isCalibrating) return null;\n await this.playMLSwithIIR(stream, this.#currentConvolution);\n\n this.stopCalibrationAudio();\n\n let system_conv_recs = this.getAllFilteredRecordedSignals();\n\n if (this.systemAttenuatorGainDB != 0) {\n let linearScaleAttenuation = Math.pow(10, this.systemAttenuatorGainDB / 20);\n system_conv_recs = system_conv_recs.map(rec => {\n return rec.map(value => value / linearScaleAttenuation);\n });\n }\n\n let return_system_conv_rec = system_conv_recs[system_conv_recs.length - 1];\n // await this.checkPowerVariation(return_system_conv_rec);\n\n this.clearAllFilteredRecordedSignals();\n\n this.sourceAudioContext.close();\n let recs = this.getAllUnfilteredRecordedSignals();\n if (this.componentAttentuatorGainDB != 0) {\n let linearScaleAttenuation = Math.pow(10, this.componentAttentuatorGainDB / 20);\n recs = recs.map(rec => {\n return rec.map(value => value / this.linearScaleAttenuation);\n });\n }\n let unconv_rec = recs[0];\n let return_unconv_rec = unconv_rec;\n let conv_rec = component_conv_recs[component_conv_recs.length - 1];\n\n //psd of component\n let knownGain = this.oldComponentIR.Gain;\n let knownFreq = this.oldComponentIR.Freq;\n let sampleRate = this.sourceSamplingRate || 96000;\n this.addTimeStamp('Compute PSD of MLS recording');\n if (this.isCalibrating) return null;\n let component_unconv_rec_psd = await this.pyServerAPI\n .getSubtractedPSDWithRetry(unconv_rec, knownGain, knownFreq, sampleRate)\n .then(res => {\n this.incrementStatusBar();\n this.status = this.generateTemplate(`All Hz Calibration: done computing the PSD graphs...`.toString()).toString();\n this.emit('update', {message: this.status});\n return res;\n })\n .catch(err => {\n console.error(err);\n });\n\n this.addTimeStamp('Compute PSD of filtered recording (component)');\n if (this.isCalibrating) return null;\n let component_conv_rec_psd = await this.pyServerAPI\n .getSubtractedPSDWithRetry(conv_rec, knownGain, knownFreq, sampleRate)\n .then(res => {\n let interpolatedGain = res.x.map((freq, index) => {\n let i = 0;\n while (i < knownFreq.length && knownFreq[i] < freq) {\n i++;\n }\n if (i === 0 || i === knownFreq.length) {\n return knownGain[i];\n }\n return (0,_utils__WEBPACK_IMPORTED_MODULE_1__.interpolate)(freq, knownFreq[i - 1], knownFreq[i], knownGain[i - 1], knownGain[i]);\n });\n\n let correctedGain = res.y.map(\n (gain, index) => 10 * Math.log10(gain) - interpolatedGain[index]\n );\n\n let filtered_psd = correctedGain.filter(\n (value, index) => res.x[index] >= this.#lowHz && res.x[index] <= this.componentFMaxHz\n );\n\n this.SDofFilteredRange['component'] = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.standardDeviation)(filtered_psd);\n this.incrementStatusBar();\n this.status =this.generateTemplate(`All Hz Calibration: done computing the PSD graphs...`.toString()).toString();\n this.emit('update', {message: this.status});\n return res;\n })\n .catch(err => {\n console.error(err);\n });\n\n conv_rec = system_conv_recs[system_conv_recs.length - 1];\n //psd of system\n this.addTimeStamp('Compute PSD of filtered recording (system) and unfiltered recording');\n if (this.isCalibrating) return null;\n let system_recs_psd = await this.pyServerAPI\n .getPSDWithRetry({\n unconv_rec,\n conv_rec,\n sampleRate: this.sourceSamplingRate || 96000,\n })\n .then(res => {\n let filtered_psd = res.y_conv\n .filter(\n (value, index) => res.x_conv[index] >= this.#lowHz && res.x_conv[index] <= this.#highHz\n )\n .map(value => 10 * Math.log10(value));\n\n let mls_psd = res.y_unconv\n .filter(\n (value, index) =>\n res.x_unconv[index] >= this.#lowHz && res.x_conv[index] <= this.#highHz\n )\n .map(value => 10 * Math.log10(value));\n\n this.SDofFilteredRange['mls'] = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.standardDeviation)(mls_psd);\n console.log('mls_psd', this.SDofFilteredRange['mls']);\n this.SDofFilteredRange['system'] = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.standardDeviation)(filtered_psd);\n this.incrementStatusBar();\n this.status = this.generateTemplate(`All Hz Calibration: done computing the PSD graphs...`.toString()).toString();\n this.emit('update', {message: this.status});\n return res;\n })\n .catch(err => {\n console.error(err);\n });\n\n //iir w/ and without bandpass psd. done\n unconv_rec = this.componentInvertedImpulseResponseNoBandpass;\n conv_rec = this.componentInvertedImpulseResponse;\n this.addTimeStamp('Compute PSD of component IIR and component IIR no band pass');\n if (this.isCalibrating) return null;\n let component_iir_psd = await this.pyServerAPI\n .getPSDWithRetry({\n unconv_rec,\n conv_rec,\n sampleRate: this.sourceSamplingRate || 96000,\n })\n .then(res => {\n this.incrementStatusBar();\n this.status = this.generateTemplate(`All Hz Calibration: done computing the PSD graphs...`.toString()).toString();\n this.emit('update', {message: this.status});\n return res;\n })\n .catch(err => {\n console.error(err);\n });\n unconv_rec = this.systemInvertedImpulseResponseNoBandpass;\n conv_rec = this.systemInvertedImpulseResponse;\n this.addTimeStamp('Compute PSD of system IIR and system IIR no band pass');\n if (this.isCalibrating) return null;\n let system_iir_psd = await this.pyServerAPI\n .getPSDWithRetry({\n unconv_rec,\n conv_rec,\n sampleRate: this.sourceSamplingRate || 96000,\n })\n .then(res => {\n this.incrementStatusBar();\n this.status = this.generateTemplate(`All Hz Calibration: done computing the PSD graphs...`.toString() ).toString();\n this.emit('update', {message: this.status});\n return res;\n })\n .catch(err => {\n console.error(err);\n });\n\n this.addTimeStamp('Compute PSD of MLS sequence');\n if (this.isCalibrating) return null;\n let mls_psd = await this.pyServerAPI\n .getMLSPSDWithRetry({\n mls: this.#mlsBufferView[0],\n sampleRate: this.sourceSamplingRate || 96000,\n })\n .then(res => {\n this.incrementStatusBar();\n this.status = this.generateTemplate(`All Hz Calibration: done computing the PSD graphs...`.toString()).toString();\n this.emit('update', {message: this.status});\n return res;\n })\n .catch(err => {\n console.error(err);\n });\n\n this.addTimeStamp('Compute PSD of filtered MLS (system)');\n if (this.isCalibrating) return null;\n let system_filtered_mls_psd = await this.pyServerAPI\n .getMLSPSDWithRetry({\n mls: this.systemConvolution,\n sampleRate: this.sourceSamplingRate || 96000,\n })\n .then(res => {\n this.incrementStatusBar();\n this.status = this.generateTemplate(`All Hz Calibration: done computing the PSD graphs...`.toString() ).toString();\n this.emit('update', {message: this.status});\n return res;\n })\n .catch(err => {\n console.error(err);\n });\n\n let system_no_bandpass_filtered_mls_psd = await this.pyServerAPI\n .getMLSPSDWithRetry({\n mls: this.systemConvolution,\n sampleRate: this.sourceSamplingRate || 96000,\n })\n .then(res => {\n this.incrementStatusBar();\n this.status =this.generateTemplate(`All Hz Calibration: done computing the PSD graphs...`.toString()).toString();\n this.emit('update', {message: this.status});\n return res;\n })\n .catch(err => {\n console.error(err);\n });\n\n this.addTimeStamp('Compute PSD of filtered MLS (component)');\n if (this.isCalibrating) return null;\n let component_filtered_mls_psd = await this.pyServerAPI\n .getMLSPSDWithRetry({\n mls: this.componentConvolution,\n sampleRate: this.sourceSamplingRate || 96000,\n })\n .then(res => {\n this.incrementStatusBar();\n this.status = this.generateTemplate(`All Hz Calibration: done computing the PSD graphs...`.toString() ).toString();\n this.emit('update', {message: this.status});\n return res;\n })\n .catch(err => {\n console.error(err);\n });\n\n let component_no_bandpass_filtered_mls_psd = await this.pyServerAPI\n .getMLSPSDWithRetry({\n mls: this.componentConvolutionNoBandpass,\n sampleRate: this.sourceSamplingRate || 96000,\n })\n .then(res => {\n this.incrementStatusBar();\n this.status = this.generateTemplate(`All Hz Calibration: done computing the PSD graphs...`.toString() ).toString();\n this.emit('update', {message: this.status});\n return res;\n })\n .catch(err => {\n console.error(err);\n });\n\n let gainValue = this.getGainDBSPL();\n\n iir_ir_and_plots = {\n filtered_recording: {\n component: return_component_conv_rec,\n system: return_system_conv_rec,\n },\n unfiltered_recording: this.getAllUnfilteredRecordedSignals()[0],\n system: {\n iir: this.systemInvertedImpulseResponse,\n iir_no_bandpass: this.systemInvertedImpulseResponseNoBandpass,\n ir: this.systemIR,\n iir_psd: {\n y: system_iir_psd['y_conv'],\n x: system_iir_psd['x_conv'],\n y_no_bandpass: system_iir_psd['y_unconv'],\n x_no_bandpass: system_iir_psd['x_unconv'],\n },\n filtered_mls_psd: {\n x: system_filtered_mls_psd['x_mls'],\n y: system_filtered_mls_psd['y_mls'],\n },\n filtered_no_bandpass_mls_psd: {\n x: system_no_bandpass_filtered_mls_psd['x_mls'],\n y: system_no_bandpass_filtered_mls_psd['y_mls'],\n },\n convolution: this.systemConvolution,\n convolutionNoBandpass: this.systemConvolutionNoBandpass,\n psd: {\n unconv: {\n x: system_recs_psd['x_unconv'],\n y: system_recs_psd['y_unconv'],\n },\n conv: {\n x: system_recs_psd['x_conv'],\n y: system_recs_psd['y_conv'],\n },\n },\n },\n component: {\n iir: this.componentInvertedImpulseResponse,\n iir_no_bandpass: this.componentInvertedImpulseResponseNoBandpass,\n ir: this.componentIR,\n ir_origin: this.componentIROrigin,\n ir_in_time_domain: this.componentIRInTimeDomain,\n iir_psd: {\n y: component_iir_psd['y_conv'],\n x: component_iir_psd['x_conv'],\n y_no_bandpass: component_iir_psd['y_unconv'],\n x_no_bandpass: component_iir_psd['x_unconv'],\n },\n filtered_mls_psd: {\n x: component_filtered_mls_psd['x_mls'],\n y: component_filtered_mls_psd['y_mls'],\n },\n filtered_no_bandpass_mls_psd: {\n x: component_no_bandpass_filtered_mls_psd['x_mls'],\n y: component_no_bandpass_filtered_mls_psd['y_mls'],\n },\n convolution: this.componentConvolution,\n convolutionNoBandpass: this.componentConvolutionNoBandpass,\n psd: {\n unconv: {\n x: component_unconv_rec_psd['x'],\n y: component_unconv_rec_psd['y'],\n },\n conv: {\n x: component_conv_rec_psd['x'],\n y: component_conv_rec_psd['y'],\n },\n },\n gainDBSPL: gainValue,\n },\n mls: this.#mlsBufferView,\n mls_psd: {\n x: mls_psd['x_mls'],\n y: mls_psd['y_mls'],\n },\n autocorrelations: this.autocorrelations,\n impulseResponses: [],\n };\n\n return iir_ir_and_plots;\n };\n\n singleSoundCheck = async stream => {\n let iir_ir_and_plots;\n const pre = this._calibrateSoundBurstPreSec; \n const repeats = this._calibrateSoundBurstRepeats; \n const burst = this._calibrateSoundBurstSec; \n const post = this._calibrateSoundBurstPostSec; \n const total_dur = pre + repeats * burst + post;\n if (this._calibrateSoundCheck != 'system') {\n this.#currentConvolution = this.componentConvolution;\n this.filteredMLSRange.component.Min = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.findMinValue)(this.#currentConvolution);\n this.filteredMLSRange.component.Max = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.findMaxValue)(this.#currentConvolution);\n this.soundCheck = 'component';\n this.addTimeStamp(`Record ${total_dur} s of MLS with ${this.soundCheck} IIR.`)\n this.addTimeStamp(`Record ${total_dur} s of MLS with speaker or microphone IIR.`);\n if (this.isCalibrating) return null;\n await this.playMLSwithIIR(stream, this.#currentConvolution);\n this.stopCalibrationAudio();\n } else {\n this.#currentConvolution = this.systemConvolution;\n this.filteredMLSRange.system.Min = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.findMinValue)(this.#currentConvolution);\n this.filteredMLSRange.system.Max = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.findMaxValue)(this.#currentConvolution);\n this.soundCheck = 'system';\n this.addTimeStamp(`Record ${total_dur} s of MLS with ${this.soundCheck} IIR.`)\n this.addTimeStamp(`Record ${total_dur} s of MLS with speaker or microphone IIR.`);\n if (this.isCalibrating) return null;\n await this.playMLSwithIIR(stream, this.#currentConvolution);\n this.stopCalibrationAudio();\n }\n let conv_recs = this.getAllFilteredRecordedSignals();\n if (this._calibrateSoundCheck == 'goal') {\n if (this.componentAttentuatorGainDB != 0) {\n let linearScaleAttenuation = Math.pow(10, this.componentAttentuatorGainDB / 20);\n conv_recs = conv_recs.map(rec => {\n return rec.map(value => value / this.linearScaleAttenuation);\n });\n }\n } else if (this._calibrateSoundCheck == 'system') {\n if (this.systemAttentuatorGainDB != 0) {\n let linearScaleAttenuation = Math.pow(10, this.systemAttentuatorGainDB / 20);\n conv_recs = conv_recs.map(rec => {\n return rec.map(value => value / this.linearScaleAttenuation);\n });\n }\n }\n\n //remove the filteredMLSAttenuation from the recorded signals\n // conv_recs = conv_recs.map(rec => {\n // if (this.soundCheck === 'component') {\n // return rec.map(value => value / this.filteredMLSAttenuation.component);\n // }\n // return rec.map(value => value / this.filteredMLSAttenuation.system);\n // });\n\n let recs = this.getAllUnfilteredRecordedSignals();\n if (this._calibrateSoundCheck == 'goal') {\n if (this.componentAttentuatorGainDB != 0) {\n let linearScaleAttenuation = Math.pow(10, this.componentAttentuatorGainDB / 20);\n recs = recs.map(rec => {\n return rec.map(value => value / this.linearScaleAttenuation);\n });\n }\n } else if (this._calibrateSoundCheck == 'system') {\n if (this.systemAttentuatorGainDB != 0) {\n let linearScaleAttenuation = Math.pow(10, this.systemAttentuatorGainDB / 20);\n recs = recs.map(rec => {\n return rec.map(value => value / this.linearScaleAttenuation);\n });\n }\n }\n this.clearAllFilteredRecordedSignals();\n console.log('Obtaining unfiltered recording from #allHzUnfilteredRecordings to calculate PSD');\n console.log('Obtaining filtered recording from #allHzFilteredRecordings to calculate PSD');\n let unconv_rec = recs[0];\n let return_unconv_rec = unconv_rec;\n let conv_rec = conv_recs[conv_recs.length - 1];\n let return_conv_rec = conv_rec;\n this.sourceAudioContext.close();\n if (this._calibrateSoundCheck != 'system') {\n let knownGain = this.oldComponentIR.Gain;\n let knownFreq = this.oldComponentIR.Freq;\n let sampleRate = this.sourceSamplingRate || 96000;\n this.addTimeStamp('Compute PSD of MLS recording');\n if (this.isCalibrating) return null;\n let unconv_results = await this.pyServerAPI\n .getSubtractedPSDWithRetry(unconv_rec, knownGain, knownFreq, sampleRate)\n .then(res => {\n console.log(res);\n let mls_psd = res.y\n .filter(\n (value, index) => res.x[index] >= this.#lowHz && res.x[index] <= this.systemFMaxHz\n )\n .map(value => 10 * Math.log10(value));\n this.SDofFilteredRange['mls'] = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.standardDeviation)(mls_psd);\n console.log('mls sd', this.SDofFilteredRange['mls']);\n this.incrementStatusBar();\n this.status = this.generateTemplate(`All Hz Calibration: done computing the PSD graphs...`.toString()).toString();\n this.emit('update', {message: this.status});\n return res;\n })\n .catch(err => {\n console.error(err);\n });\n\n this.addTimeStamp('Compute PSD recording of filtered recording (component)');\n if (this.isCalibrating) return null;\n let conv_results = await this.pyServerAPI\n .getSubtractedPSDWithRetry(conv_rec, knownGain, knownFreq, sampleRate)\n .then(res => {\n let interpolatedGain = res.x.map((freq, index) => {\n let i = 0;\n while (i < knownFreq.length && knownFreq[i] < freq) {\n i++;\n }\n if (i === 0 || i === knownFreq.length) {\n return knownGain[i];\n }\n return (0,_utils__WEBPACK_IMPORTED_MODULE_1__.interpolate)(\n freq,\n knownFreq[i - 1],\n knownFreq[i],\n knownGain[i - 1],\n knownGain[i]\n );\n });\n\n let correctedGain = res.y.map(\n (gain, index) => 10 * Math.log10(gain) - interpolatedGain[index]\n );\n let filtered_psd = correctedGain.filter(\n (value, index) => res.x[index] >= this.#lowHz && res.x[index] <= this.#highHz\n );\n\n this.SDofFilteredRange['component'] = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.standardDeviation)(filtered_psd);\n this.incrementStatusBar();\n this.status = this.generateTemplate(`All Hz Calibration: done computing the PSD graphs...`.toString()).toString();\n this.emit('update', {message: this.status});\n return res;\n })\n .catch(err => {\n console.error(err);\n });\n\n unconv_rec = this.componentInvertedImpulseResponseNoBandpass;\n conv_rec = this.componentInvertedImpulseResponse;\n this.addTimeStamp('Compute PSD of component IIR and component IIR no bandpass');\n if (this.isCalibrating) return null;\n let component_iir_psd = await this.pyServerAPI\n .getPSDWithRetry({\n unconv_rec,\n conv_rec,\n sampleRate: this.sourceSamplingRate || 96000,\n })\n .then(res => {\n this.incrementStatusBar();\n this.status = this.generateTemplate(`All Hz Calibration: done computing the PSD graphs...`.toString()).toString();\n this.emit('update', {message: this.status});\n return res;\n })\n .catch(err => {\n console.error(err);\n });\n unconv_rec = this.systemInvertedImpulseResponseNoBandpass;\n conv_rec = this.systemInvertedImpulseResponse;\n this.addTimeStamp('Compute PSD of system IIR and system IIR no bandpass');\n if (this.isCalibrating) return null;\n let system_iir_psd = await this.pyServerAPI\n .getPSDWithRetry({\n unconv_rec,\n conv_rec,\n sampleRate: this.sourceSamplingRate || 96000,\n })\n .then(res => {\n this.incrementStatusBar();\n this.status = this.generateTemplate(`All Hz Calibration: done computing the PSD graphs...`.toString()).toString();\n this.emit('update', {message: this.status});\n return res;\n })\n .catch(err => {\n console.error(err);\n });\n\n this.addTimeStamp('Compute PSD of MLS sequence');\n if (this.isCalibrating) return null;\n let mls_psd = await this.pyServerAPI\n .getMLSPSDWithRetry({\n mls: this.#mlsBufferView[this.icapture],\n sampleRate: this.sourceSamplingRate || 96000,\n })\n .then(res => {\n this.incrementStatusBar();\n this.status = this.generateTemplate(`All Hz Calibration: done computing the PSD graphs...`.toString()).toString();\n this.emit('update', {message: this.status});\n return res;\n })\n .catch(err => {\n console.error(err);\n });\n\n this.addTimeStamp('Compute PSD of filtered MLS (component)');\n if (this.isCalibrating) return null;\n let filtered_mls_psd = await this.pyServerAPI\n .getMLSPSDWithRetry({\n mls: this.componentConvolution,\n sampleRate: this.sourceSamplingRate || 96000,\n })\n .then(res => {\n this.incrementStatusBar();\n this.status = this.generateTemplate(`All Hz Calibration: done computing the PSD graphs...`.toString()).toString();\n this.emit('update', {message: this.status});\n return res;\n })\n .catch(err => {\n console.error(err);\n });\n\n let filtered_no_bandpass_mls_psd = await this.pyServerAPI\n .getMLSPSDWithRetry({\n mls: this.componentConvolutionNoBandpass,\n sampleRate: this.sourceSamplingRate || 96000,\n })\n .then(res => {\n this.incrementStatusBar();\n this.status = this.generateTemplate(`All Hz Calibration: done computing the PSD graphs...`.toString()).toString();\n this.emit('update', {message: this.status});\n return res;\n })\n .catch(err => {\n console.error(err);\n });\n\n let gainValue = this.getGainDBSPL();\n iir_ir_and_plots = {\n unfiltered_recording: return_unconv_rec,\n filtered_recording: return_conv_rec,\n system: {\n iir: this.systemInvertedImpulseResponse,\n iir_no_bandpass: this.systemInvertedImpulseResponseNoBandpass,\n ir: this.systemIR,\n iir_psd: {\n y: system_iir_psd['y_conv'],\n x: system_iir_psd['y_conv'],\n y_no_bandpass: system_iir_psd['y_unconv'],\n x_no_bandpass: system_iir_psd['x_unconv'],\n },\n filtered_recording: [],\n filtered_mls_psd: {},\n filtered_no_bandpass_mls_psd: {},\n convolution: this.systemConvolution,\n convolutionNoBandpass: this.systemConvolutionNoBandpass,\n psd: {\n unconv: {\n x: [],\n y: [],\n },\n conv: {\n x: [],\n y: [],\n },\n },\n },\n component: {\n iir: this.componentInvertedImpulseResponse,\n iir_no_bandpass: this.componentInvertedImpulseResponseNoBandpass,\n ir: this.componentIR,\n ir_origin: this.componentIROrigin,\n ir_in_time_domain: this.componentIRInTimeDomain,\n iir_psd: {\n y: component_iir_psd['y_conv'],\n x: component_iir_psd['x_conv'],\n y_no_bandpass: component_iir_psd['y_unconv'],\n x_no_bandpass: component_iir_psd['x_unconv'],\n },\n filtered_mls_psd: {\n x: filtered_mls_psd['x_mls'],\n y: filtered_mls_psd['y_mls'],\n },\n filtered_no_bandpass_mls_psd: {\n x: filtered_no_bandpass_mls_psd['x_mls'],\n y: filtered_no_bandpass_mls_psd['y_mls'],\n },\n convolution: this.componentConvolution,\n convolutionNoBandpass: this.componentConvolutionNoBandpass,\n psd: {\n unconv: {\n x: unconv_results['x'],\n y: unconv_results['y'],\n },\n conv: {\n x: conv_results['x'],\n y: conv_results['y'],\n },\n },\n gainDBSPL: gainValue,\n },\n mls: this.#mlsBufferView,\n mls_psd: {\n x: mls_psd['x_mls'],\n y: mls_psd['y_mls'],\n },\n autocorrelations: this.autocorrelations,\n impulseResponses: [],\n };\n } else {\n this.addTimeStamp('Compute PSD of filtered recording (system) and unfiltered recording');\n if (this.isCalibrating) return null;\n let results = await this.pyServerAPI\n .getPSDWithRetry({\n unconv_rec,\n conv_rec,\n sampleRate: this.sourceSamplingRate || 96000,\n })\n .then(res => {\n let filtered_psd = res.y_conv\n .filter(\n (value, index) =>\n res.x_conv[index] >= this.#lowHz && res.x_conv[index] <= this.systemFMaxHz\n )\n .map(value => 10 * Math.log10(value));\n\n let mls_psd = res.y_unconv\n .filter(\n (value, index) =>\n res.x_unconv[index] >= this.#lowHz && res.x_conv[index] <= this.systemFMaxHz\n )\n .map(value => 10 * Math.log10(value));\n this.SDofFilteredRange['mls'] = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.standardDeviation)(mls_psd);\n this.SDofFilteredRange['system'] = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.standardDeviation)(filtered_psd);\n this.incrementStatusBar();\n this.status = this.generateTemplate(`All Hz Calibration: done computing the PSD graphs...`.toString()).toString();\n this.emit('update', {message: this.status});\n return res;\n })\n .catch(err => {\n console.error(err);\n });\n\n //iir w/ and without bandpass psd\n unconv_rec = this.componentInvertedImpulseResponseNoBandpass;\n conv_rec = this.componentInvertedImpulseResponse;\n this.addTimeStamp('Compute PSD of component IIR and component IIR no band pass');\n if (this.isCalibrating) return null;\n let component_iir_psd = await this.pyServerAPI\n .getPSDWithRetry({\n unconv_rec,\n conv_rec,\n sampleRate: this.sourceSamplingRate || 96000,\n })\n .then(res => {\n this.incrementStatusBar();\n this.status = this.generateTemplate(`All Hz Calibration: done computing the PSD graphs...`.toString()).toString();\n this.emit('update', {message: this.status});\n return res;\n })\n .catch(err => {\n console.error(err);\n });\n unconv_rec = this.systemInvertedImpulseResponseNoBandpass;\n conv_rec = this.systemInvertedImpulseResponse;\n this.addTimeStamp('Compute PSD of system IIR and system IIR no band pass');\n if (this.isCalibrating) return null;\n let system_iir_psd = await this.pyServerAPI\n .getPSDWithRetry({\n unconv_rec,\n conv_rec,\n sampleRate: this.sourceSamplingRate || 96000,\n })\n .then(res => {\n this.incrementStatusBar();\n this.status = this.generateTemplate( `All Hz Calibration: done computing the PSD graphs...`.toString()).toString();\n this.emit('update', {message: this.status});\n return res;\n })\n .catch(err => {\n console.error(err);\n });\n\n this.addTimeStamp('Compute PSD of MLS sequence');\n if (this.isCalibrating) return null;\n let mls_psd = await this.pyServerAPI\n .getMLSPSDWithRetry({\n mls: this.#mlsBufferView[this.icapture],\n sampleRate: this.sourceSamplingRate || 96000,\n })\n .then(res => {\n this.incrementStatusBar();\n this.status = this.generateTemplate(`All Hz Calibration: done computing the PSD graphs...`.toString()).toString();\n this.emit('update', {message: this.status});\n return res;\n })\n .catch(err => {\n console.error(err);\n });\n\n this.addTimeStamp('Compute PSD of filtered MLS (system)');\n if (this.isCalibrating) return null;\n let filtered_mls_psd = await this.pyServerAPI\n .getMLSPSDWithRetry({\n mls: this.systemConvolution,\n sampleRate: this.sourceSamplingRate || 96000,\n })\n .then(res => {\n this.incrementStatusBar();\n this.status = this.generateTemplate(`All Hz Calibration: done computing the PSD graphs...`.toString()).toString();\n this.emit('update', {message: this.status});\n return res;\n })\n .catch(err => {\n console.error(err);\n });\n\n let filtered_no_bandpass_mls_psd = await this.pyServerAPI\n .getMLSPSDWithRetry({\n mls: this.systemConvolutionNoBandpass,\n sampleRate: this.sourceSamplingRate || 96000,\n })\n .then(res => {\n this.incrementStatusBar();\n this.status = this.generateTemplate(`All Hz Calibration: done computing the PSD graphs...`.toString()).toString();\n this.emit('update', {message: this.status});\n return res;\n })\n .catch(err => {\n console.error(err);\n });\n\n let gainValue = this.getGainDBSPL();\n iir_ir_and_plots = {\n unfiltered_recording: return_unconv_rec,\n filtered_recording: return_conv_rec,\n system: {\n iir: this.systemInvertedImpulseResponse,\n iir_no_bandpass: this.systemInvertedImpulseResponseNoBandpass,\n ir: this.systemIR,\n iir_psd: {\n y: system_iir_psd['y_conv'],\n x: system_iir_psd['y_conv'],\n y_no_bandpass: system_iir_psd['y_unconv'],\n x_no_bandpass: system_iir_psd['x_unconv'],\n },\n filtered_recording: [],\n filtered_mls_psd: {\n x: filtered_mls_psd['x_mls'],\n y: filtered_mls_psd['y_mls'],\n },\n filtered_no_bandpass_mls_psd: {\n x: filtered_no_bandpass_mls_psd['x_mls'],\n y: filtered_no_bandpass_mls_psd['y_mls'],\n },\n convolution: this.systemConvolution,\n convolutionNoBandpass: this.systemConvolutionNoBandpass,\n psd: {\n unconv: {\n x: results['x_unconv'],\n y: results['y_unconv'],\n },\n conv: {\n x: results['x_conv'],\n y: results['y_conv'],\n },\n },\n },\n component: {\n iir: this.componentInvertedImpulseResponse,\n iir_no_bandpass: this.componentInvertedImpulseResponseNoBandpass,\n ir: this.componentIR,\n ir_origin: this.componentIROrigin,\n ir_in_time_domain: this.componentIRInTimeDomain,\n iir_psd: {\n y: component_iir_psd['y_conv'],\n x: component_iir_psd['x_conv'],\n y_no_bandpass: component_iir_psd['y_unconv'],\n x_no_bandpass: component_iir_psd['x_unconv'],\n },\n filtered_mls_psd: {},\n filtered_no_bandpass_mls_psd: {},\n convolution: this.componentConvolution,\n convolutionNoBandpass: this.componentConvolutionNoBandpass,\n psd: {\n unconv: {\n x: [],\n y: [],\n },\n conv: {\n x: [],\n y: [],\n },\n },\n gainDBSPL: gainValue,\n },\n mls: this.#mlsBufferView,\n mls_psd: {\n x: mls_psd['x_mls'],\n y: mls_psd['y_mls'],\n },\n autocorrelations: this.autocorrelations,\n impulseResponses: [],\n };\n }\n if (this.isCalibrating) return null;\n await Promise.all(this.impulseResponses).then(res => {\n for (let i = 0; i < res.length; i++) {\n if (res[i] != undefined) {\n iir_ir_and_plots['impulseResponses'].push(res[i]);\n }\n }\n });\n\n if (this.#download) {\n this.downloadSingleUnfilteredRecording();\n this.downloadSingleFilteredRecording();\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.#mls, 'MLS.csv');\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.componentConvolution, 'python_component_convolution_mls_iir.csv');\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.systemConvolution, 'python_system_convolution_mls_iir.csv');\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.componentInvertedImpulseResponse, 'componentIIR.csv');\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.systemInvertedImpulseResponse, 'systemIIR.csv');\n for (let i = 0; i < this.autocorrelations.length; i++) {\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.autocorrelations[i], `autocorrelation_${i}`);\n }\n const computedIRagain = await Promise.all(this.impulseResponses).then(res => {\n for (let i = 0; i < res.length; i++) {\n if (res[i] != undefined) {\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(res[i], `IR_${i}`);\n }\n }\n });\n }\n\n return iir_ir_and_plots;\n };\n\n /**\n * Public method to start the calibration process. Objects intialized from webassembly allocate new memory\n * and must be manually freed. This function is responsible for intializing the MlsGenInterface,\n * and wrapping the calibration steps with a garbage collection safe gaurd.\n *\n * @public\n * @param stream - The stream of audio from the Listener.\n * @example\n */\n startCalibrationImpulseResponse = async stream => {\n console.log('JS used memory:', performance.memory.usedJSHeapSize / 1024 / 1024, 'mb');\n let desired_time = this.desired_time_per_mls;\n let checkRec = 'allhz';\n\n console.log('MLS sequence should be of length: ' + this.sourceSamplingRate * desired_time);\n\n length = this.sourceSamplingRate * desired_time;\n //get mls here\n // const calibrateSoundBurstDb = Math.pow(10, this._calibrateSoundBurstDb / 20);\n\n this.power_dB = 0;\n\n if (!this._calibrateSoundBurstLevelReTBool) {\n this.power_dB = this._calibrateSoundBurstDb;\n } else {\n this.power_dB = this._calibrateSoundBurstDb + (this.T - this.gainDBSPL);\n }\n\n const amplitude = Math.pow(10, this.power_dB / 20);\n //MLSpower = Math.pow(10,this.power_dB/20);\n this.addTimeStamp('Compute MLS sequence');\n if (this.isCalibrating) return null;\n await this.pyServerAPI\n .getMLSWithRetry({\n length,\n amplitude,\n calibrateSoundBurstMLSVersions: this.numCaptures,\n })\n .then(res => {\n console.log(res);\n this.#mlsBufferView = res['mls'];\n this.#mls = res['unscaledMLS'];\n })\n .catch(err => {\n // this.emit('InvertedImpulseResponse', {res: false});\n console.error(err);\n });\n this.numSuccessfulBackgroundCaptured = 0;\n if (this._calibrateSoundBackgroundSecs > 0) {\n this.mode = 'background';\n this.status = this.generateTemplate(`All Hz Calibration: sampling the background noise...`.toString()).toString();\n this.emit('update', {message: this.status});\n if (this.isCalibrating) return null;\n await this.recordBackground(\n stream, //stream\n () => this.numSuccessfulBackgroundCaptured < 1, //loop condition\n this.#awaitBackgroundNoiseRecording, //sleep to record\n this.sendBackgroundRecording, //send to get PSD\n this.mode,\n checkRec\n );\n this.incrementStatusBar();\n }\n this.mode = 'unfiltered';\n this.numSuccessfulCaptured = 0;\n\n if (this.isCalibrating) return null;\n for (var i = 0; i < this.numCaptures; i++) {\n this.icapture = i;\n await this.calibrationSteps(\n stream,\n this.#playCalibrationAudio, // play audio func (required)\n this.#createCalibrationNodeFromBuffer(this.#mlsBufferView[this.icapture]), // before play func\n this.#awaitSignalOnset, // before record\n () => this.numSuccessfulCaptured < 2, // loop while true\n this.#awaitDesiredMLSLength, // during record\n this.#afterMLSRecord, // after record\n this.mode,\n checkRec\n );\n this.stopCalibrationAudio();\n }\n checkRec = false;\n\n // at this stage we've captured all the required signals,\n // and have received IRs for each one\n // so let's send all the IRs to the server to be converted to a single IIR\n if (this.isCalibrating) return null;\n await this.sendSystemImpulseResponsesToServerForProcessing();\n await this.pyServerAPI.checkMemory();\n if (this.isCalibrating) return null;\n await this.sendComponentImpulseResponsesToServerForProcessing();\n\n this.numSuccessfulCaptured = 0;\n\n let iir_ir_and_plots;\n if (this._calibrateSoundCheck != 'none') {\n //do single check\n if (this._calibrateSoundCheck == 'goal' || this._calibrateSoundCheck == 'system') {\n if (this.isCalibrating) return null;\n iir_ir_and_plots = await this.singleSoundCheck(stream);\n if (this.isCalibrating) return null;\n } else {\n //both\n if (this.isCalibrating) return null;\n iir_ir_and_plots = await this.bothSoundCheck(stream);\n if (this.isCalibrating) return null;\n }\n } else {\n let unconv_rec = this.componentInvertedImpulseResponseNoBandpass;\n let conv_rec = this.componentInvertedImpulseResponse;\n if (this.isCalibrating) return null;\n let component_iir_psd = await this.pyServerAPI\n .getPSDWithRetry({\n unconv_rec,\n conv_rec,\n sampleRate: this.sourceSamplingRate || 96000,\n })\n .then(res => {\n this.incrementStatusBar();\n this.status = this.generateTemplate(`All Hz Calibration: done computing the PSD graphs...`.toString()).toString();\n this.emit('update', {message: this.status});\n return res;\n })\n .catch(err => {\n console.error(err);\n });\n unconv_rec = this.systemInvertedImpulseResponseNoBandpass;\n conv_rec = this.systemInvertedImpulseResponse;\n if (this.isCalibrating) return null;\n let system_iir_psd = await this.pyServerAPI\n .getPSDWithRetry({\n unconv_rec,\n conv_rec,\n sampleRate: this.sourceSamplingRate || 96000,\n })\n .then(res => {\n this.incrementStatusBar();\n this.status = this.generateTemplate(`All Hz Calibration: done computing the PSD graphs...`.toString()).toString();\n this.emit('update', {message: this.status});\n return res;\n })\n .catch(err => {\n console.error(err);\n });\n\n let gainValue = this.getGainDBSPL();\n iir_ir_and_plots = {\n unfiltered_recording: return_unconv_rec,\n filtered_recording: return_conv_rec,\n system: {\n iir: this.systemInvertedImpulseResponse,\n iir_no_bandpass: this.systemInvertedImpulseResponseNoBandpass,\n ir: this.systemIR,\n iir_psd: {\n y: system_iir_psd['y_conv'],\n x: system_iir_psd['y_conv'],\n y_no_bandpass: system_iir_psd['y_unconv'],\n x_no_bandpass: system_iir_psd['x_unconv'],\n },\n filtered_recording: [],\n convolution: this.systemConvolution,\n convolutionNoBandpass: this.systemConvolutionNoBandpass,\n psd: {\n unconv: {\n x: [],\n y: [],\n },\n conv: {\n x: [],\n y: [],\n },\n },\n },\n component: {\n iir: this.componentInvertedImpulseResponse,\n iir_no_bandpass: this.componentInvertedImpulseResponseNoBandpass,\n ir: this.componentIR,\n ir_in_time_domain: this.componentIRInTimeDomain,\n iir_psd: {\n y: component_iir_psd['y_conv'],\n x: component_iir_psd['x_conv'],\n y_no_bandpass: component_iir_psd['y_unconv'],\n x_no_bandpass: component_iir_psd['x_unconv'],\n },\n convolution: this.componentConvolution,\n convolutionNoBandpass: this.componentConvolutionNoBandpass,\n psd: {\n unconv: {\n x: [],\n y: [],\n },\n conv: {\n x: [],\n y: [],\n },\n },\n gainDBSPL: gainValue,\n },\n mls: this.#mlsBufferView,\n autocorrelations: this.autocorrelations,\n impulseResponses: [],\n };\n if (this.isCalibrating) return null;\n await Promise.all(this.impulseResponses).then(res => {\n for (let i = 0; i < res.length; i++) {\n if (res[i] != undefined) {\n iir_ir_and_plots['impulseResponses'].push(res[i]);\n }\n }\n });\n\n if (this.#download) {\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.#mls, 'MLS.csv');\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.componentConvolution, 'python_component_convolution_mls_iir.csv');\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.systemConvolution, 'python_system_convolution_mls_iir.csv');\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.componentInvertedImpulseResponse, 'componentIIR.csv');\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.systemInvertedImpulseResponse, 'systemIIR.csv');\n for (let i = 0; i < this.autocorrelations.length; i++) {\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.autocorrelations[i], `autocorrelation_${i}`);\n }\n const computedIRagain = await Promise.all(this.impulseResponses).then(res => {\n for (let i = 0; i < res.length; i++) {\n if (res[i] != undefined) {\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(res[i], `IR_${i}`);\n }\n }\n });\n }\n }\n if (this.isCalibrating) return null;\n this.percent_complete = 100;\n this.status = this.generateTemplate(`All Hz Calibration: Finished`.toString()).toString();\n this.emit('update', {message: this.status});\n\n //here after calibration we have the component calibration (either loudspeaker or microphone) in the same form as the componentIR\n //that was used to calibrate\n // saveToJSON(iir_ir_and_plots);\n return iir_ir_and_plots;\n };\n\n //////////////////////volume\n\n handleIncomingData = data => {\n console.log('Received data: ', data);\n if (data.type === 'soundGainDBSPL') {\n this.soundGainDBSPL = data.value;\n } else {\n throw new Error(`Unknown data type: ${data.type}`);\n }\n };\n\n #getTruncatedSignal = (left = 3.5, right = 4.5) => {\n const start = Math.floor(left * this.sourceSamplingRate);\n const end = Math.floor(right * this.sourceSamplingRate);\n const result = Array.from(this.getLastVolumeRecordedSignal().slice(start, end));\n console.log(\n 'Obtaining last 1000 hz recording from #allVolumeRecordings to send for processing'\n );\n /**\n * function to check that capture was properly made\n * @param {*} list\n */\n const checkResult = list => {\n const setItem = new Set(list);\n if (setItem.size === 1 && setItem.has(0)) {\n console.warn(\n 'The last capture failed, all recorded signal is zero',\n this.getAllVolumeRecordedSignals()\n );\n this.stopCalibrationAudio();\n this.isCalibrating = true;\n // restartButton.style.display = 'none';\n this.emit('update', {message: 'Connection failed, hit restart button to reconnect'});\n }\n if (setItem.size === 0) {\n console.warn('The last capture failed, no recorded signal');\n this.stopCalibrationAudio();\n this.isCalibrating = true;\n // restartButton.style.display = 'none';\n this.emit('update', {message: 'Connection failed, hit restart button to reconnect'});\n }\n };\n checkResult(result);\n return result;\n };\n\n /** \n * \n * \n Construct a calibration Node with the calibration parameters and given gain value\n * @param {*} gainValue\n * */\n #createCalibrationToneWithGainValue = gainValue => {\n const audioContext = this.makeNewSourceAudioContext();\n const oscilator = audioContext.createOscillator();\n const gainNode = audioContext.createGain();\n const taperGainNode = audioContext.createGain();\n const offsetGainNode = audioContext.createGain();\n const totalDuration = this.CALIBRATION_TONE_DURATION * 1.2;\n\n oscilator.frequency.value = this.#CALIBRATION_TONE_FREQUENCY;\n oscilator.type = this.#CALIBRATION_TONE_TYPE;\n gainNode.gain.value = gainValue;\n\n oscilator.connect(gainNode);\n gainNode.connect(taperGainNode);\n const onsetCurve = this.createSCurveBuffer();\n taperGainNode.gain.setValueCurveAtTime(onsetCurve, 0, this.TAPER_SECS);\n taperGainNode.connect(offsetGainNode);\n const offsetCurve = this.createSCurveBuffer(false);\n offsetGainNode.gain.setValueCurveAtTime(\n offsetCurve,\n totalDuration - this.TAPER_SECS,\n this.TAPER_SECS\n );\n offsetGainNode.connect(audioContext.destination);\n\n const gainValuesOverTime = [];\n const sampleRate = this.#CALIBRATION_TONE_FREQUENCY; // Number of samples per second\n const interval = 1 / sampleRate; // Time between samples\n\n for (let t = 0; t <= totalDuration; t += interval) {\n let gainValueAtTime = gainValue;\n\n // Apply the onset curve\n if (t < this.TAPER_SECS) {\n const onsetIndex = Math.floor((t / this.TAPER_SECS) * onsetCurve.length);\n gainValueAtTime *= onsetCurve[onsetIndex];\n }\n\n // Apply the offset curve\n if (t > totalDuration - this.TAPER_SECS) {\n const offsetTime = t - (totalDuration - this.TAPER_SECS);\n const offsetIndex = Math.floor((offsetTime / this.TAPER_SECS) * offsetCurve.length);\n gainValueAtTime *= offsetCurve[offsetIndex];\n }\n\n gainValuesOverTime.push(gainValueAtTime);\n }\n\n this.waveforms['volume'][this.inDB] = gainValuesOverTime;\n\n this.addCalibrationNode(oscilator);\n };\n\n /**\n * Construct a Calibration Node with the calibration parameters.\n *\n * @private\n * @example\n */\n #createCalibrationNode = () => {\n const audioContext = this.makeNewSourceAudioContext();\n const oscilator = audioContext.createOscillator();\n const gainNode = audioContext.createGain();\n\n oscilator.frequency.value = this.#CALIBRATION_TONE_FREQUENCY;\n oscilator.type = this.#CALIBRATION_TONE_TYPE;\n gainNode.gain.value = 0.04;\n\n oscilator.connect(gainNode);\n gainNode.connect(audioContext.destination);\n\n this.addCalibrationNode(oscilator);\n };\n\n #playCalibrationAudioVolume = async () => {\n if (this.numCalibratingRoundsCompleted==1) {\n this.recordingChecks['warnings'].push(`1000Hz. Re-recorded ${this.inDB} dB because SD ${(this.recordingChecks['volume'][this.inDB]['sd'])} > ${this.calibrateSound1000HzMaxSD_dB} dB`);\n const currentStatus = `1000 Hz: Re-recording at ${this.inDB} dB because SD \n ${this.recordingChecks['volume'][this.inDB]['sd']} > \n ${this.calibrateSound1000HzMaxSD_dB} dB`.toString();\n this.status = this.generateTemplate(currentStatus).toString();\n this.emit('update', {\n message: this.status,\n });\n }\n const totalDuration = this.CALIBRATION_TONE_DURATION * 1.2;\n\n this.calibrationNodes[0].start(0);\n this.calibrationNodes[0].stop(totalDuration);\n console.log(`Playing a buffer of ${this.CALIBRATION_TONE_DURATION} seconds of audio`);\n console.log(`Waiting a total of ${totalDuration} seconds`);\n await (0,_utils__WEBPACK_IMPORTED_MODULE_1__.sleep)(totalDuration);\n };\n\n stopCalibrationAudioVolume = () => {\n if (this.calibrationNodes.length > 0) {\n this.calibrationNodes[0].stop();\n }\n };\n\n #sendToServerForProcessing = async lCalib => {\n console.log('Sending data to server');\n const total_dur = this.calibrateSound1000HzPreSec + this.calibrateSound1000HzSec + this.calibrateSound1000HzPostSec;\n this.addTimeStamp(\n \"1000 Hz: recorded at \" + this.inDB + \n \" dB (\" + this.calibrateSound1000HzPreSec.toFixed(1) + \n \" s pre + \" + this.calibrateSound1000HzSec.toFixed(1) + \n \" s used + \" + this.calibrateSound1000HzPostSec.toFixed(1) + \n \" s post)\"\n );\n let left = this.calibrateSound1000HzPreSec;\n let right = this.calibrateSound1000HzPreSec + this.calibrateSound1000HzSec;\n if (this.isCalibrating) return null;\n this.pyServerAPI\n .getVolumeCalibration({\n sampleRate: this.sourceSamplingRate,\n payload: this.#getTruncatedSignal(left, right),\n lCalib: lCalib,\n })\n .then(res => {\n if (this.outDBSPL === null) {\n this.incrementStatusBar();\n this.outDBSPL = res['outDbSPL'];\n this.outDBSPL1000 = res['outDbSPL1000'];\n this.THD = res['thd'];\n }\n })\n .catch(err => {\n console.warn(err);\n });\n const rec = this.getLastVolumeRecordedSignal();\n console.log('pre period power: ', (0,_powerCheck__WEBPACK_IMPORTED_MODULE_2__.getPower)(rec.slice(0,this.calibrateSound1000HzPreSec * this.sourceSamplingRate)).toFixed(1));\n console.log('pre period power: ', \n (0,_powerCheck__WEBPACK_IMPORTED_MODULE_2__.getPower)(rec.slice(\n this.calibrateSound1000HzPreSec * this.sourceSamplingRate,\n (this.calibrateSound1000HzPreSec + this.calibrateSound1000HzSec)* this.sourceSamplingRate)).toFixed(1));\n console.log('pre period power: ', (0,_powerCheck__WEBPACK_IMPORTED_MODULE_2__.getPower)(rec.slice(\n (this.calibrateSound1000HzPreSec + this.calibrateSound1000HzSec)* this.sourceSamplingRate)).toFixed(1));\n const res = (0,_powerCheck__WEBPACK_IMPORTED_MODULE_2__.volumePowerCheck)(\n rec,\n this.sourceSamplingRate || 96000,\n this.calibrateSound1000HzPreSec,\n this.calibrateSound1000HzSec, \n this._calibrateSoundPowerBinDesiredSec);\n console.log(res);\n this.recordingChecks['volume'][this.inDB] = res;\n };\n\n startCalibrationVolume = async (stream, gainValues, lCalib, componentGainDBSPL) => {\n if (this.isCalibrating) return null;\n const trialIterations = gainValues.length;\n this.status_denominator += trialIterations;\n const thdValues = [];\n const inDBValues = [];\n let inDB = 0;\n const outDBSPLValues = [];\n const outDBSPL1000Values = [];\n let checkRec = 'loudest';\n // do one calibration that will be discarded\n const soundLevelToDiscard = -60;\n const gainToDiscard = Math.pow(10, soundLevelToDiscard / 20);\n this.inDB = soundLevelToDiscard;\n this.status = this.generateTemplate(`1000 Hz Calibration: Sound Level ${soundLevelToDiscard} dB`.toString()).toString();\n //this.emit('update', {message: `1000 Hz Calibration: Sound Level ${soundLevelToDiscard} dB`});\n this.emit('update', {message: this.status});\n this.startTime = new Date().getTime();\n\n do {\n console.log('while loop');\n if (this.isCalibrating) {\n console.log('restart calibration');\n return null;\n }\n // eslint-disable-next-line no-await-in-loop\n await this.volumeCalibrationSteps(\n stream,\n this.#playCalibrationAudioVolume,\n this.#createCalibrationToneWithGainValue,\n this.#sendToServerForProcessing,\n gainToDiscard,\n lCalib, //todo make this a class parameter\n checkRec,\n () => {return this.recordingChecks['volume'][this.inDB]['sd']},\n this.calibrateSound1000HzMaxSD_dB\n );\n } while (this.outDBSPL === null);\n //reset the values\n //this.incrementStatusBar();\n\n this.outDBSPL = null;\n this.outDBSPL = null;\n this.outDBSPL1000 = null;\n this.THD = null;\n\n // run the calibration at different gain values provided by the user\n for (let i = 0; i < trialIterations; i++) {\n //convert gain to DB and add to inDB\n if (i == trialIterations - 1) {\n checkRec = 'loudest';\n }\n inDB = Math.log10(gainValues[i]) * 20;\n // precision to 1 decimal place\n inDB = Math.round(inDB * 10) / 10;\n this.inDB = inDB;\n inDBValues.push(inDB);\n console.log('next update');\n this.status = this.generateTemplate(`1000 Hz Calibration: Sound Level ${inDB} dB`.toString()).toString();\n this.emit('update', {message: this.status});\n do {\n if (this.isCalibrating) {\n console.log('restart calibration');\n return null;\n }\n // eslint-disable-next-line no-await-in-loop\n await this.volumeCalibrationSteps(\n stream,\n this.#playCalibrationAudioVolume,\n this.#createCalibrationToneWithGainValue,\n this.#sendToServerForProcessing,\n gainValues[i],\n lCalib, //todo make this a class parameter\n checkRec,\n () => {return this.recordingChecks?.['volume']?.[this.inDB]?.['sd'] || 0},\n this.calibrateSound1000HzMaxSD_dB\n );\n } while (this.outDBSPL === null);\n outDBSPL1000Values.push(this.outDBSPL1000);\n thdValues.push(this.THD);\n outDBSPLValues.push(this.outDBSPL);\n\n this.outDBSPL = null;\n this.outDBSPL1000 = null;\n this.THD = null;\n }\n if (this.isCalibrating) return null;\n // get the volume calibration parameters from the server\n this.addTimeStamp('Compute sound calibration parameters');\n\n const parameters = await this.pyServerAPI\n .getVolumeCalibrationParameters({\n inDBValues: inDBValues,\n outDBSPLValues: outDBSPL1000Values,\n lCalib: lCalib,\n componentGainDBSPL,\n })\n .then(res => {\n this.incrementStatusBar();\n return res;\n });\n if (this.isCalibrating) return null;\n const result = {\n parameters: parameters,\n inDBValues: inDBValues,\n outDBSPLValues: outDBSPLValues,\n outDBSPL1000Values: outDBSPL1000Values,\n thdValues: thdValues,\n };\n\n return result;\n };\n\n writeFrqGainToFirestore = async (speakerID, frq, gain, OEM, documentID) => {\n // freq and gain are too large to take samples 1 in every 100 samples\n // const sampledFrq = [];\n // const sampledGain = [];\n // for (let i = 0; i < frq.length; i += 100) {\n // sampledFrq.push(frq[i]);\n // sampledGain.push(gain[i]);\n // }\n\n const data = {Freq: frq, Gain: gain};\n\n const docRef = (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_5__.doc)(_config_firebase__WEBPACK_IMPORTED_MODULE_3__[\"default\"], 'Microphones', documentID);\n await (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_5__.updateDoc)(docRef, {\n linear: data,\n });\n\n // divide frq and gain into smaller chunks and write to firestore one chunk at a time\n // use arrayUnion to append to the array\n // const chunkSize = 600;\n // const chunkedFrq = [];\n // const chunkedGain = [];\n // for (let i = 0; i < frq.length; i += chunkSize) {\n // chunkedFrq.push(frq.slice(i, i + chunkSize));\n // chunkedGain.push(gain.slice(i, i + chunkSize));\n // }\n // const docRef = doc(database, 'Microphones', documentID);\n // for (let i = 0; i < chunkedFrq.length; i++) {\n // await updateDoc(docRef, {\n // linear: {\n // Freq: arrayUnion(...chunkedFrq[i]),\n // Gain: arrayUnion(...chunkedGain[i]),\n // },\n // });\n // }\n };\n // function to write frq and gain to firebase database given speakerID\n writeFrqGain = async (speakerID, frq, gain, OEM) => {\n // freq and gain are too large to take samples 1 in every 100 samples\n\n const sampledFrq = [];\n const sampledGain = [];\n for (let i = 0; i < frq.length; i += 100) {\n sampledFrq.push(frq[i]);\n sampledGain.push(gain[i]);\n }\n\n const data = {Freq: sampledFrq, Gain: sampledGain};\n\n await (0,firebase_database__WEBPACK_IMPORTED_MODULE_4__.set)((0,firebase_database__WEBPACK_IMPORTED_MODULE_4__.ref)(_config_firebase__WEBPACK_IMPORTED_MODULE_3__[\"default\"], `Microphone2/${OEM}/${speakerID}/linear`), data);\n };\n\n // Function to Read frq and gain from firebase database given speakerID\n // returns an array of frq and gain if speakerID exists, returns null otherwise\n readFrqGainFromFirestore = async (speakerID, OEM, isDefault) => {\n const collectionRef = (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_5__.collection)(_config_firebase__WEBPACK_IMPORTED_MODULE_3__[\"default\"], 'Microphones');\n const q = (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_5__.query)(\n collectionRef,\n (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_5__.where)('ID', '==', speakerID),\n (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_5__.where)('lowercaseOEM', '==', OEM),\n (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_5__.where)('isDefault', '==', isDefault)\n );\n const querySnapshot = await (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_5__.getDocs)(q);\n // if exists return the linear field of the first document\n if (querySnapshot.size > 0) {\n const timestamp = new firebase_firestore__WEBPACK_IMPORTED_MODULE_5__.Timestamp(\n querySnapshot.docs[0].data().createDate.seconds,\n querySnapshot.docs[0].data().createDate.nanoseconds\n );\n return {\n ir: querySnapshot.docs[0].data().linear,\n createDate: timestamp.toDate(),\n jsonFileName: querySnapshot.docs[0].data().jsonFileName,\n };\n }\n return null;\n };\n readFrqGain = async (speakerID, OEM) => {\n const dbRef = (0,firebase_database__WEBPACK_IMPORTED_MODULE_4__.ref)(_config_firebase__WEBPACK_IMPORTED_MODULE_3__[\"default\"]);\n const snapshot = await (0,firebase_database__WEBPACK_IMPORTED_MODULE_4__.get)((0,firebase_database__WEBPACK_IMPORTED_MODULE_4__.child)(dbRef, `Microphone2/${OEM}/${speakerID}/linear`));\n if (snapshot.exists()) {\n return snapshot.val();\n }\n return null;\n };\n readGainat1000HzFromFirestore = async (speakerID, OEM, isDefault) => {\n const collectionRef = (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_5__.collection)(_config_firebase__WEBPACK_IMPORTED_MODULE_3__[\"default\"], 'Microphones');\n const q = (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_5__.query)(\n collectionRef,\n (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_5__.where)('ID', '==', speakerID),\n (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_5__.where)('lowercaseOEM', '==', OEM),\n (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_5__.where)('isDefault', '==', isDefault)\n );\n const querySnapshot = await (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_5__.getDocs)(q);\n // if exists return the Gain1000 field of the first document\n if (querySnapshot.size > 0) {\n return querySnapshot.docs[0].data().Gain1000;\n }\n return null;\n };\n\n readGainat1000Hz = async (speakerID, OEM) => {\n const dbRef = (0,firebase_database__WEBPACK_IMPORTED_MODULE_4__.ref)(_config_firebase__WEBPACK_IMPORTED_MODULE_3__[\"default\"]);\n const snapshot = await (0,firebase_database__WEBPACK_IMPORTED_MODULE_4__.get)((0,firebase_database__WEBPACK_IMPORTED_MODULE_4__.child)(dbRef, `Microphone2/${OEM}/${speakerID}/Gain1000`));\n if (snapshot.exists()) {\n return snapshot.val();\n }\n return null;\n };\n\n writeGainat1000HzToFirestore = async (speakerID, gain, OEM, documentID) => {\n const docRef = (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_5__.doc)(_config_firebase__WEBPACK_IMPORTED_MODULE_3__[\"default\"], 'Microphones', documentID);\n await (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_5__.updateDoc)(docRef, {\n Gain1000: gain,\n });\n };\n\n writeGainat1000Hz = async (speakerID, gain, OEM) => {\n await (0,firebase_database__WEBPACK_IMPORTED_MODULE_4__.set)((0,firebase_database__WEBPACK_IMPORTED_MODULE_4__.ref)(_config_firebase__WEBPACK_IMPORTED_MODULE_3__[\"default\"], `Microphone2/${OEM}/${speakerID}/Gain1000`), gain);\n };\n\n writeIsSmartPhoneToFirestore = async (speakerID, isSmartPhone, OEM) => {\n const collectionRef = (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_5__.collection)(_config_firebase__WEBPACK_IMPORTED_MODULE_3__[\"default\"], 'Microphones');\n const q = (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_5__.query)(\n collectionRef,\n (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_5__.where)('ID', '==', speakerID),\n (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_5__.where)('lowercaseOEM', '==', OEM),\n (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_5__.where)('isDefault', '==', true)\n );\n const querySnapshot = await (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_5__.getDocs)(q);\n if (querySnapshot.size > 0) {\n const docRef = await (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_5__.addDoc)(collectionRef, {isSmartPhone: isSmartPhone, isDefault: false});\n return docRef.id;\n } else {\n const docRef = await (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_5__.addDoc)(collectionRef, {isSmartPhone: isSmartPhone, isDefault: true});\n return docRef.id;\n }\n };\n\n writeIsSmartPhone = async (speakerID, isSmartPhone, OEM) => {\n const data = {isSmartPhone: isSmartPhone};\n await (0,firebase_database__WEBPACK_IMPORTED_MODULE_4__.set)((0,firebase_database__WEBPACK_IMPORTED_MODULE_4__.ref)(_config_firebase__WEBPACK_IMPORTED_MODULE_3__[\"default\"], `Microphone2/${OEM}/${speakerID}/isSmartPhone`), isSmartPhone);\n };\n\n writeMicrophoneInfoToFirestore = async (speakerID, micInfo, OEM, documentID) => {\n const docRef = (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_5__.doc)(_config_firebase__WEBPACK_IMPORTED_MODULE_3__[\"default\"], 'Microphones', documentID);\n await (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_5__.setDoc)(docRef, micInfo, {merge: true});\n };\n\n doesMicrophoneExistInFirestore = async (speakerID, OEM, documentID) => {\n const docRef = (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_5__.doc)(_config_firebase__WEBPACK_IMPORTED_MODULE_3__[\"default\"], 'Microphone', OEM, speakerID, documentID);\n const docSnap = await (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_5__.getDoc)(docRef);\n if (docSnap.exists()) {\n return true;\n }\n return false;\n };\n\n doesMicrophoneExist = async (speakerID, OEM) => {\n const dbRef = (0,firebase_database__WEBPACK_IMPORTED_MODULE_4__.ref)(_config_firebase__WEBPACK_IMPORTED_MODULE_3__[\"default\"]);\n const snapshot = await (0,firebase_database__WEBPACK_IMPORTED_MODULE_4__.get)((0,firebase_database__WEBPACK_IMPORTED_MODULE_4__.child)(dbRef, `Microphone2/${OEM}/${speakerID}`));\n if (snapshot.exists()) {\n return true;\n }\n return false;\n };\n\n addMicrophoneInfo = async (speakerID, OEM, micInfo) => {\n // add to database if /info does not exist\n const dbRef = (0,firebase_database__WEBPACK_IMPORTED_MODULE_4__.ref)(_config_firebase__WEBPACK_IMPORTED_MODULE_3__[\"default\"]);\n const snapshot = await (0,firebase_database__WEBPACK_IMPORTED_MODULE_4__.get)((0,firebase_database__WEBPACK_IMPORTED_MODULE_4__.child)(dbRef, `Microphone2/${OEM}/${speakerID}/info`));\n if (!snapshot.exists()) {\n await (0,firebase_database__WEBPACK_IMPORTED_MODULE_4__.set)((0,firebase_database__WEBPACK_IMPORTED_MODULE_4__.ref)(_config_firebase__WEBPACK_IMPORTED_MODULE_3__[\"default\"], `Microphone2/${OEM}/${speakerID}/info`), micInfo);\n }\n };\n\n convertToDB = gain => {\n return Math.log10(gain) * 20;\n };\n\n // Function to perform linear interpolation between two points\n interpolate(x, x0, y0, x1, y1) {\n return y0 + ((x - x0) * (y1 - y0)) / (x1 - x0);\n }\n\n findGainatFrequency = (frequencies, gains, targetFrequency) => {\n // Find the index of the first frequency in the array greater than the target frequency\n let index = 0;\n while (index < frequencies.length && frequencies[index] < targetFrequency) {\n index++;\n }\n\n // Handle cases when the target frequency is outside the range of the given data\n if (index === 0) {\n return gains[0];\n } else if (index === frequencies.length) {\n return gains[gains.length - 1];\n } else {\n // Interpolate the gain based on the surrounding frequencies\n const x0 = frequencies[index - 1];\n const y0 = gains[index - 1];\n const x1 = frequencies[index];\n const y1 = gains[index];\n return this.interpolate(targetFrequency, x0, y0, x1, y1);\n }\n };\n\n checkPowerVariation = async () => {\n let recordings = this.getAllFilteredRecordedSignals();\n // remove filteredMLSAttenuation from the recordings\n\n // recordings = recordings.map(recording => {\n // if (this.soundCheck == 'component') {\n // return recording.map(value => value / this.filteredMLSAttenuation.component);\n // }\n // return recording.map(value => value / this.filteredMLSAttenuation.system);\n // });\n\n const rec = recordings[recordings.length - 1];\n\n await this.pyServerAPI\n .allHzPowerCheck({\n payload: rec,\n sampleRate: this.sourceSamplingRate || 96000,\n binDesiredSec: this._calibrateSoundPowerBinDesiredSec,\n burstSec: this.desired_time_per_mls,\n repeats: this.numMLSPerCapture - this.num_mls_to_skip ,\n warmUp: this.num_mls_to_skip \n })\n .then(result => {\n if (result) {\n if (result['sd'] > this._calibrateSoundBurstMaxSD_dB && this.numSuccessfulCaptured == 0) {\n console.log('filtered recording sd too high');\n this.recordingChecks['warnings'].push(`All Hz. Re-recorded ${this.inDB} because SD ${result['sd']} > ${this._calibrateSoundBurstMaxSD_dB} dB`);\n this.status = this.generateTemplate(`All Hz: Re-recording at ${this.inDB} dB because SD ${result['sd']} > ${this._calibrateSoundBurstMaxSD_dB} dB`.toString()).toString();\n this.emit('update', {\n message: this.status,\n });\n // numSuccessfulCaptured no longer to count number of successful capture but count attemps\n // is sd below _calibrateSoundBurstMaxSD_dB then count two attemps\n // else count one attemp\n this.numSuccessfulCaptured += 1;\n } else {\n this.recordingChecks[this.soundCheck].push(result);\n // Now we do at most 2 attempts if sd > _calibrateSoundBurstMaxSD_dB\n // Second attempt is the final\n if (this.numSuccessfulCaptured < 2) {\n this.numSuccessfulCaptured += 2;\n this.stepNum += 1;\n this.incrementStatusBar();\n console.log(\n 'after mls w iir record for some reason add numSucc capt ' + this.stepNum\n );\n this.status = this.generateTemplate(`All Hz Calibration: ${this.numSuccessfulCaptured} recording of convolved MLS captured`.toString()).toString();\n this.emit('update', {\n message: this.status,\n });\n }\n }\n }\n });\n };\n\n getGainDBSPL = () => {\n var freqIndex = this.componentIR.Freq.indexOf(1000);\n\n // If freqIndex is not -1 (meaning 1000 is found in the freq array)\n if (freqIndex !== -1) {\n // Get the corresponding gain value using the index\n var gainValue = this.componentIR.Gain[freqIndex];\n return gainValue;\n } else {\n console.log('Freq 1000 not found in the array.');\n return null;\n }\n };\n // Example of how to use the writeFrqGain and readFrqGain functions\n // writeFrqGain('speaker1', [1, 2, 3], [4, 5, 6]);\n // Speaker1 is the speakerID you want to write to in the database\n // readFrqGain('MiniDSPUMIK_1').then(data => console.log(data));\n // MiniDSPUMIK_1 is the speakerID with some Data in the database\n //adding gainDBSPL\n startCalibration = async (\n stream,\n gainValues,\n lCalib = 104.92978421490648,\n componentIR = null,\n microphoneName = 'MiniDSP-UMIK1-711-4754-vertical',\n _calibrateSoundCheck = 'goal', //GOAL PASSed in by default\n isSmartPhone = false,\n _calibrateSoundBurstDb = -18,\n _calibrateSoundBurstFilteredExtraDb = 6,\n _calibrateSoundBurstLevelReTBool = false,\n _calibrateSoundBurstUses1000HzGainBool = false,\n _calibrateSoundBurstRepeats = 3,\n _calibrateSoundBurstSec = 1,\n _calibrateSoundBurstPreSec = 1,\n _calibrateSoundBurstPostSec = 1,\n _calibrateSoundHz = 48000,\n _calibrateSoundIRSec = 0.2,\n _calibrateSoundIIRSec = 0.2,\n _calibrateSoundIIRPhase = 'linear',\n calibrateSound1000HzPreSec = 0.5,\n calibrateSound1000HzSec = 0.5,\n calibrateSound1000HzPostSec = 0.5,\n _calibrateSoundBackgroundSecs = 0,\n _calibrateSoundSmoothOctaves = 0.33,\n _calibrateSoundSmoothMinBandwidthHz = 200,\n _calibrateSoundPowerBinDesiredSec = 0.2,\n _calibrateSoundPowerDbSDToleratedDb = 1,\n _calibrateSoundTaperSec = 0.01,\n micManufacturer = '',\n micSerialNumber = '',\n micModelNumber = '',\n micModelName = '',\n calibrateMicrophonesBool,\n authorEmails,\n webAudioDeviceNames = {\n loudspeaker: 'loudspeaker',\n microphone: 'microphone',\n microphoneText: 'xxx XXX',\n },\n userIDs,\n restartButton,\n reminder,\n calibrateSoundLimit,\n _calibrateSoundBurstNormalizeBy1000HzGainBool = false,\n _calibrateSoundBurstScalarDB = 71,\n calibrateSound1000HzMaxSD_dB = 4,\n _calibrateSoundBurstMaxSD_dB = 4,\n calibrateSoundSamplingDesiredBits = 24,\n language,\n loudspeakerModelName='',\n phrases,\n soundSubtitleId,\n\n ) => {\n this._calibrateSoundBurstPreSec = _calibrateSoundBurstPreSec;\n this._calibrateSoundBurstRepeats = _calibrateSoundBurstRepeats;\n this._calibrateSoundBurstSec = _calibrateSoundBurstSec;\n this.micModelName = micModelName;\n this.loudspeakerModelName = loudspeakerModelName;\n this.language = language;\n this.TAPER_SECS = _calibrateSoundTaperSec;\n this.calibrateSoundLimit = calibrateSoundLimit;\n this._calibrateSoundBurstDb = _calibrateSoundBurstDb;\n this._calibrateSoundBurstFilteredExtraDb = _calibrateSoundBurstFilteredExtraDb;\n this._calibrateSoundBurstLevelReTBool = _calibrateSoundBurstLevelReTBool;\n this.CALIBRATION_TONE_DURATION =\n calibrateSound1000HzPreSec + calibrateSound1000HzSec + calibrateSound1000HzPostSec;\n this.calibrateSound1000HzPreSec = calibrateSound1000HzPreSec;\n this.calibrateSound1000HzSec = calibrateSound1000HzSec;\n this.calibrateSound1000HzPostSec = calibrateSound1000HzPostSec;\n this.iirLength = Math.floor(_calibrateSoundIIRSec * this.sourceSamplingRate);\n this.irLength = Math.floor(_calibrateSoundIRSec * this.sourceSamplingRate);\n this.calibrateSoundIIRPhase = _calibrateSoundIIRPhase;\n this.num_mls_to_skip = Math.ceil(_calibrateSoundBurstPreSec / _calibrateSoundBurstSec);\n this._calibrateSoundBurstPostSec = _calibrateSoundBurstPostSec;\n this.numMLSPerCapture = _calibrateSoundBurstRepeats + this.num_mls_to_skip;\n this.desired_time_per_mls = _calibrateSoundBurstSec;\n this.desired_sampling_rate = _calibrateSoundHz;\n this._calibrateSoundBackgroundSecs = _calibrateSoundBackgroundSecs;\n this._calibrateSoundSmoothOctaves = _calibrateSoundSmoothOctaves;\n this._calibrateSoundSmoothMinBandwidthHz = _calibrateSoundSmoothMinBandwidthHz;\n this._calibrateSoundPowerBinDesiredSec = _calibrateSoundPowerBinDesiredSec;\n this._calibrateSoundBurstUses1000HzGainBool = _calibrateSoundBurstUses1000HzGainBool;\n this._calibrateSoundPowerDbSDToleratedDb = _calibrateSoundPowerDbSDToleratedDb;\n this._calibrateSoundBurstNormalizeBy1000HzGainBool =\n _calibrateSoundBurstNormalizeBy1000HzGainBool;\n this._calibrateSoundBurstScalarDB = _calibrateSoundBurstScalarDB;\n this.webAudioDeviceNames = webAudioDeviceNames;\n this.calibrateSoundSamplingDesiredBits = calibrateSoundSamplingDesiredBits;\n this.phrases = phrases;\n this.soundSubtitleId = soundSubtitleId;\n if (isSmartPhone) {\n const leftQuote = \"\\u201C\"; // “\n const rightQuote = \"\\u201D\"; // ”\n this.webAudioDeviceNames.microphone = this.deviceInfo.microphoneFromAPI;\n const quotedWebAudioMic = leftQuote + this.webAudioDeviceNames.microphone + rightQuote;\n const combinedMicText = this.micModelName + \" \" + quotedWebAudioMic;\n webAudioDeviceNames.microphoneText = this.phrases.RC_nameMicrophone[this.language]\n .replace(\"“xxx”\", combinedMicText)\n .replace(\"“XXX”\", combinedMicText);\n }\n // this.webAudioDeviceNames.microphoneText = this.webAudioDeviceNames.microphoneText\n // .replace('xxx', this.webAudioDeviceNames.microphone)\n // .replace('XXX', this.webAudioDeviceNames.microphone);\n //feed calibration goal here\n this._calibrateSoundCheck = _calibrateSoundCheck;\n this.calibrateSound1000HzMaxSD_dB = calibrateSound1000HzMaxSD_dB;\n this._calibrateSoundBurstMaxSD_dB = _calibrateSoundBurstMaxSD_dB;\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\n //check the db based on the microphone currently connected\n\n //new lCalib found at top of calibration files *1000hz, make sure to correct\n //based on zeroing of 1000hz, search for \"*1000Hz\"\n const ID = isSmartPhone ? micModelNumber : micSerialNumber;\n const OEM = isSmartPhone\n ? micModelName === 'UMIK-1' || micModelName === 'UMIK-2'\n ? 'minidsp'\n : this.deviceInfo.OEM.toLowerCase().split(' ').join('')\n : micManufacturer.toLowerCase().split(' ').join('');\n // const ID = \"712-5669\";\n // const OEM = \"minidsp\";\n const micInfo = {\n micModelName: isSmartPhone ? micModelName : microphoneName,\n OEM: isSmartPhone\n ? micModelName === 'UMIK-1' || micModelName === 'UMIK-2'\n ? 'miniDSP'\n : this.deviceInfo.OEM\n : micManufacturer,\n ID: ID,\n createDate: new Date(),\n DateText: (0,_utils__WEBPACK_IMPORTED_MODULE_1__.getCurrentTimeString)(),\n HardwareName: isSmartPhone ? this.deviceInfo.hardwarename : microphoneName,\n hardwareFamily: isSmartPhone ? this.deviceInfo.hardwarefamily : microphoneName,\n HardwareModel: isSmartPhone ? this.deviceInfo.hardwaremodel : microphoneName,\n PlatformName: isSmartPhone ? this.deviceInfo.platformname : 'N/A',\n PlatformVersion: isSmartPhone ? this.deviceInfo.platformversion : 'N/A',\n DeviceType: isSmartPhone ? this.deviceInfo.devicetype : 'N/A',\n ID_from_51Degrees: isSmartPhone ? this.deviceInfo.DeviceId : 'N/A',\n calibrateMicrophonesBool: calibrateMicrophonesBool,\n screenHeight: this.deviceInfo.screenHeight,\n screenWidth: this.deviceInfo.screenWidth,\n webAudioDeviceNames: {\n loudspeaker: this.webAudioDeviceNames.loudspeaker,\n microphone: this.webAudioDeviceNames.microphone,\n },\n userIDs: userIDs,\n lowercaseOEM: OEM.toLowerCase().split(' ').join(''),\n };\n if (calibrateMicrophonesBool) {\n micInfo['authorEmails'] = authorEmails;\n }\n // if undefined in micInfo, set to empty string\n for (const [key, value] of Object.entries(micInfo)) {\n if (value === undefined) {\n micInfo[key] = '';\n }\n }\n\n // this.writeMicrophoneInfoToFirestore(ID, micInfo, OEM, 'default');\n // this.addMicrophoneInfo(ID, OEM, micInfo);\n if (componentIR == null) {\n //mode 'ir'\n //global variable this.componentIR must be set\n await this.readFrqGainFromFirestore(ID, OEM, true).then(data => {\n if (data !== null) {\n this.componentIR = data.ir;\n micInfo['parentTimestamp'] = data.createDate ? data.createDate : new Date();\n micInfo['parentFilenameJSON'] = data.jsonFileName ? data.jsonFileName : '';\n }\n });\n\n // await this.readFrqGain(ID, OEM).then(data => {\n // return data;\n // });\n\n // lCalib = await this.readGainat1000Hz(ID, OEM);\n lCalib = await this.readGainat1000HzFromFirestore(ID, OEM, true);\n micInfo['gainDBSPL'] = lCalib;\n // this.componentGainDBSPL = this.convertToDB(lCalib);\n this.componentGainDBSPL = lCalib;\n //TODO: if this call to database is unknown, cannot perform experiment => return false\n if (this.componentIR == null) {\n this.status =\n `Microphone (${OEM},${ID}) is not found in the database. Please add it to the database.`.toString();\n this.emit('update', {message: this.status});\n return false;\n }\n } else {\n this.transducerType = 'Microphone';\n this.componentIR = componentIR;\n lCalib = this.findGainatFrequency(this.componentIR.Freq, this.componentIR.Gain, 1000);\n // this.componentGainDBSPL = this.convertToDB(lCalib);\n this.componentGainDBSPL = lCalib;\n // await this.writeIsSmartPhone(ID, isSmartPhone, OEM);\n }\n\n this.oldComponentIR = JSON.parse(JSON.stringify(this.componentIR));\n\n return await new Promise(async (resolve, reject) => {\n // add event listner to params.restartButton to resolve the promise with a string: 'restart'\n\n if (reminder) {\n reminder.style.display = '';\n }\n if (restartButton) {\n restartButton.style.display = '';\n restartButton.addEventListener('click', () => {\n this.stopCalibrationAudio();\n this.isCalibrating = true;\n restartButton.style.display = 'none';\n if (reminder) {\n reminder.style.display = 'none';\n }\n this.emit('update', {message: 'Restarting calibration...'});\n resolve('restart');\n });\n }\n await this.pyServerAPI.checkMemory();\n // calibrate volume\n\n let volumeResults = await this.startCalibrationVolume(\n stream,\n gainValues,\n lCalib,\n this.componentGainDBSPL\n );\n if (!volumeResults) return;\n this.T = volumeResults['parameters']['T'];\n this.gainDBSPL = volumeResults['parameters']['gainDBSPL'];\n\n // end calibrate volume\n\n let impulseResponseResults = await this.startCalibrationImpulseResponse(stream);\n if (!impulseResponseResults) return;\n impulseResponseResults['background_noise'] = this.background_noise;\n impulseResponseResults['system']['background_noise'] = this.background_noise;\n impulseResponseResults['component']['background_noise'] = this.background_noise;\n\n //attenuate system background noise\n if (this.systemAttenuatorGainDB != 0) {\n let linearScaleAttenuation = Math.pow(10, this.systemAttenuatorGainDB / 20);\n let linearScalePowerAttenuation = Math.pow(10, this.systemAttenuatorGainDB / 10);\n impulseResponseResults['system']['background_noise']['recording'] = impulseResponseResults[\n 'background_noise'\n ]['recording'].map(value => value / linearScaleAttenuation);\n impulseResponseResults['system']['background_noise']['x_background'] =\n impulseResponseResults['background_noise']['x_background'];\n impulseResponseResults['system']['background_noise']['y_background'] =\n impulseResponseResults['background_noise']['y_background'].map(\n value => value / linearScalePowerAttenuation\n );\n }\n //attenuate component background noise\n if (this.componentAttentuatorGainDB != 0) {\n let linearScaleAttenuation = Math.pow(10, this.componentAttenuatorGainDB / 20);\n let linearScalePowerAttenuation = Math.pow(10, this.componentAttenuatorGainDB / 10);\n impulseResponseResults['component']['background_noise']['recording'] =\n impulseResponseResults['background_noise']['recording'].map(\n value => value / linearScaleAttenuation\n );\n impulseResponseResults['component']['background_noise']['x_background'] =\n impulseResponseResults['background_noise']['x_background'];\n impulseResponseResults['component']['background_noise']['y_background'] =\n impulseResponseResults['background_noise']['y_background'].map(\n value => value / linearScalePowerAttenuation\n );\n }\n impulseResponseResults['system']['attenuatorGainDB'] = this.systemAttenuatorGainDB;\n impulseResponseResults['component']['attenuatorGainDB'] = this.componentAttenuatorGainDB;\n impulseResponseResults['system']['fMaxHz'] = this.systemFMaxHz;\n impulseResponseResults['component']['fMaxHz'] = this.componentFMaxHz;\n impulseResponseResults['L_new_n'] = this.L_new_n;\n impulseResponseResults['fs2'] = this.fs2;\n\n if (componentIR != null) {\n // I corrected microphone/loudpeaker IR scale in easyeyes,\n // but since we write microphone IR to firestore here\n // we need to correct microphone IR here\n let correctGain =\n Math.round((volumeResults.parameters.gainDBSPL - this.componentGainDBSPL) * 10) / 10;\n\n let IrFreq = impulseResponseResults?.component.ir.Freq.map(freq => Math.round(freq));\n let IrGain = impulseResponseResults?.component?.ir.Gain;\n const IrGainAt1000Hz = IrGain[IrFreq.findIndex(freq => freq === 1000)];\n const difference = Math.round(10 * (IrGainAt1000Hz - correctGain)) / 10;\n IrGain = IrGain.map(gain => gain - difference);\n micInfo['mls'] = Number(this.SDofFilteredRange['mls']);\n micInfo['systemCorrectionSD'] = Number(this.SDofFilteredRange['system']);\n micInfo['componentCorrectionSD'] = Number(this.SDofFilteredRange['component']);\n console.log(typeof micInfo['componentCorrectionSD']);\n // const id = await this.writeIsSmartPhoneToFirestore(ID, isSmartPhone, OEM);\n // await this.writeMicrophoneInfoToFirestore(ID, micInfo, OEM, id);\n // await this.writeFrqGainToFirestore(ID, IrFreq, IrGain, OEM, id);\n // micInfo['gainDBSPL'] = impulseResponseResults.component.gainDBSPL;\n // await this.writeGainat1000HzToFirestore(ID, micInfo['gainDBSPL'], OEM, id);\n // await this.writeGainat1000Hz(ID, micInfo['gainDBSPL'], OEM);\n }\n const total_results = {...volumeResults, ...impulseResponseResults};\n total_results['filteredMLSRange'] = this.filteredMLSRange;\n total_results['filteredMLSAttenuation'] = this.filteredMLSAttenuation;\n total_results['micInfo'] = micInfo;\n total_results['audioInfo'] = {};\n total_results['audioInfo']['sinkSampleRate'] = this.sinkSamplingRate;\n total_results['audioInfo']['sourceSampleRate'] = this.sourceSamplingRate;\n total_results['audioInfo']['bitsPerSample'] = this.sampleSize;\n const timeStampresult = [...this.timeStamp].join('\\n');\n total_results['timeStamps'] = timeStampresult;\n total_results['recordingChecks'] = this.recordingChecks;\n total_results['waveforms'] = this.waveforms;\n total_results['component']['phase'] = this.componentIRPhase;\n total_results['system']['phase'] = this.systemIRPhase;\n total_results['qualityMetrics'] = this.SDofFilteredRange;\n total_results['flags'] = this.flags;\n console.log('total results');\n console.log(total_results);\n console.log('Time Stamps');\n console.log(timeStampresult);\n\n resolve(total_results);\n });\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (Combination);\n\n//# sourceURL=webpack://speakerCalibrator/./src/tasks/combination/combination.js?");
|
|
515
|
+
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 _powerCheck__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../powerCheck */ \"./src/powerCheck.js\");\n/* harmony import */ var _config_firebase__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../config/firebase */ \"./src/config/firebase.js\");\n/* harmony import */ var firebase_database__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! firebase/database */ \"./node_modules/firebase/database/dist/esm/index.esm.js\");\n/* harmony import */ var firebase_firestore__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! firebase/firestore */ \"./node_modules/firebase/firestore/dist/esm/index.esm.js\");\n\n\n\n\n\n\n\n\n\n//import { phrases } from '../../../dist/example/i18n';\n\n/**\n *\n */\nclass Combination extends _audioCalibrator__WEBPACK_IMPORTED_MODULE_0__[\"default\"] {\n /**\n * Default constructor. Creates an instance with any number of paramters passed or the default parameters defined here.\n *\n * @param {Object<boolean, number, number, number>} calibratorParams - paramter object\n * @param {boolean} [calibratorParams.download = false] - boolean flag to download captures\n * @param {number} [calibratorParams.mlsOrder = 18] - order of the MLS to be generated\n * @param {number} [calibratorParams.numCaptures = 5] - number of captures to perform\n * @param {number} [calibratorParams.numMLSPerCapture = 2] - number of bursts of MLS per capture\n */\n constructor({\n download = false,\n mlsOrder = 18,\n numCaptures = 3,\n numMLSPerCapture = 2,\n lowHz = 20,\n highHz = 10000,\n }) {\n super(numCaptures, numMLSPerCapture);\n this.#mlsOrder = parseInt(mlsOrder, 10);\n this.#P = 2 ** mlsOrder - 1;\n this.#download = download;\n this.#mls = [];\n this.#lowHz = lowHz;\n this.#highHz = highHz;\n }\n\n /** @private */\n stepNum = 0;\n\n /** @private */\n totalSteps = 25;\n\n /** @private */\n #download;\n\n /** @private */\n #mlsGenInterface;\n\n /** @private */\n #mlsBufferView;\n\n /** @private */\n componentInvertedImpulseResponse = null;\n\n /** @private */\n systemInvertedImpulseResponse = null;\n\n //averaged and subtracted ir returned from calibration used to calculated iir\n /** @private */\n ir = null;\n\n /** @private */\n impulseResponses = [];\n\n /** @private */\n #mlsOrder;\n\n /** @private */\n #lowHz;\n\n /** @private */\n #highHz;\n\n /** @private */\n #mls;\n\n /** @private */\n #P;\n\n /** @private */\n #audioContext;\n\n /** @private */\n offsetGainNode;\n\n /** @private */\n componentConvolution;\n\n /** @private */\n componentConvolutionNoBandpass;\n\n /** @private */\n componentIROrigin = {\n Freq: [],\n Gain: [],\n };\n\n /** @private */\n systemConvolution;\n\n /** @private */\n systemConvolutionNoBandpass;\n\n ////////////////////////volume\n /** @private */\n #CALIBRATION_TONE_FREQUENCY = 1000; // Hz\n\n /** @private */\n #CALIBRATION_TONE_TYPE = 'sine';\n\n CALIBRATION_TONE_DURATION = 5; // seconds\n calibrateSound1000HzPreSec = 3.5;\n calibrateSound1000HzSec = 1.0;\n calibrateSound1000HzPostSec = 0.5;\n\n /** @private */\n outDBSPL = null;\n THD = null;\n outDBSPL1000 = null;\n\n /** @private */\n TAPER_SECS = 0.01; // seconds\n\n /** @private */\n status_denominator = 8;\n\n /** @private */\n status_numerator = 0;\n\n /** @private */\n percent_complete = 0;\n\n /** @private */\n status = ``;\n\n /**@private */\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>`;\n\n /**@private */\n componentIR = null;\n\n /**@private */\n oldComponentIR = null;\n\n /**@private */\n systemIR = null;\n\n /**@private */\n _calibrateSoundCheck = '';\n\n deviceType = null;\n\n deviceName = null;\n\n deviceInfo = null;\n\n desired_time_per_mls = 0;\n\n num_mls_to_skip = 0;\n\n desired_sampling_rate = 0;\n\n #currentConvolution = [];\n\n mode = 'unfiltered';\n\n sourceNode;\n\n autocorrelations = [];\n\n iirLength = 0;\n\n irLength = 0;\n\n calibrateSoundIIRPhase = 'linear';\n\n componentInvertedImpulseResponseNoBandpass = [];\n\n componentIRInTimeDomain = [];\n\n systemInvertedImpulseResponseNoBandpass = [];\n\n _calibrateSoundBackgroundSecs;\n\n _calibrateSoundSmoothOctaves;\n\n background_noise = {};\n\n numSuccessfulBackgroundCaptured;\n\n _calibrateSoundBurstDb;\n\n _calibrateSoundBurstFilteredExtraDb;\n\n _calibrateSoundBurstLevelReTBool;\n\n SDofFilteredRange = {\n mls: undefined,\n component: undefined,\n system: undefined,\n };\n\n transducerType = 'Loudspeaker';\n\n componentIRPhase = [];\n\n systemIRPhase = [];\n\n webAudioDeviceNames = {loudspeaker: '', microphone: '', loudspeakerText: '', microphoneText: ''};\n\n waveforms = {\n volume: {},\n };\n\n recordingChecks = {\n volume: {},\n unfiltered: [],\n system: [],\n component: [],\n warnings:[]\n };\n\n inDB;\n\n soundCheck = '';\n\n filteredMLSRange = {\n component: {\n Min: null,\n Max: null,\n },\n system: {\n Min: null,\n Max: null,\n },\n };\n\n /** @private */\n timeStamp = [];\n\n restartCalibration = false;\n\n calibrateSoundLimit = 1;\n\n filteredMLSAttenuation = {\n component: 1,\n system: 1,\n maxAbsSystem: 1,\n maxAbsComponent: 1,\n };\n\n //parameter result from volume calibration\n T = 0;\n //gainDBSPL result from volume calibration\n gainDBSPL = 0;\n //not always just using _calibrateSoundBurstDb for MLS so created a new parameter\n power_dB = 0;\n\n //system\n systemAttenuatorGainDB = 0;\n systemFMaxHz = 0;\n\n //component\n componentAttentuatorGainDB = 0;\n componentFMaxHz = 0;\n\n dL_n;\n L_new_n;\n fs2;\n icapture = 0;\n\n /**generate string template that gets reevaluated as variable increases */\n generateTemplate = (status) => {\n if (this.isCalibrating) {\n return '';\n }\n if (this.percent_complete > 100) {\n this.percent_complete = 100;\n }\n let MLSsd = '';\n let componentSD = '';\n let systemSD = '';\n let flags = '';\n const soundSubtitle = document.getElementById(this.soundSubtitleId);\n if(soundSubtitle)\n {\n const reportWebAudioNames = `${this.webAudioDeviceNames.loudspeakerText}<br/> ${this.webAudioDeviceNames.microphoneText}`;\n soundSubtitle.innerHTML = reportWebAudioNames;\n }\n \n const samplingParamText = this.phrases.RC_SamplingHzBits[this.language].replace('111', this.sourceSamplingRate).replace('222',this.sinkSamplingRate).replace('333', this.calibrateSoundSamplingDesiredBits);\n const reportParameters = `${samplingParamText}`;\n if (this.flags) {\n flags = `<br> autoGainControl: ${this.flags.autoGainControl}; \n echoCancellation: ${this.flags.echoCancellation};\n noiseSuppression: ${this.flags.noiseSuppression}`;\n }\n if (this.SDofFilteredRange['mls']) {\n MLSsd = `<br> Recorded MLS power SD: ${this.SDofFilteredRange['mls']} dB`;\n }\n if (this.SDofFilteredRange['system']) {\n systemSD = `<br> Loudspeaker+Microphone correction SD: ${this.SDofFilteredRange['system']} dB`;\n }\n if (this.SDofFilteredRange['component']) {\n componentSD = `<br> ${this.transducerType} correction SD: ${this.SDofFilteredRange['component']} dB`;\n }\n const template = `<div style=\"display: flex; justify-content: flex-start; margin-top: 0.4rem;\"><div style=\"width: 100%; 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>`;\n return `\n ${reportParameters} \n ${MLSsd}\n ${systemSD}\n ${componentSD}\n ${flags}\n <br>${status}\n ${template }`;\n };\n\n /** increment numerator and percent for status bar */\n incrementStatusBar = () => {\n this.status_numerator += 1;\n this.percent_complete = (this.status_numerator / this.status_denominator) * 100;\n };\n\n setDeviceType = deviceType => {\n this.deviceType = deviceType;\n };\n\n setDeviceName = deviceName => {\n this.deviceName = deviceName;\n };\n\n setDeviceInfo = deviceInfo => {\n this.deviceInfo = deviceInfo;\n };\n\n /** .\n * .\n * .\n * Sends all the computed impulse responses to the backend server for processing\n *\n * @returns sets the resulting inverted impulse response to the class property\n * @example\n */\n sendSystemImpulseResponsesToServerForProcessing = async () => {\n this.addTimeStamp('Compute system IIR');\n const computedIRs = await Promise.all(this.impulseResponses);\n const filteredComputedIRs = computedIRs.filter(element => {\n return element != undefined;\n }); //log any errors that are found in this step\n console.log('filteredComputedIRs', filteredComputedIRs);\n const mls = this.#mls[this.icapture];\n const lowHz = this.#lowHz; //gain of 1 below cutoff, need gain of 0\n const highHz = this.#highHz; //check error for anything other than 10 kHz\n const iirLength = this.iirLength;\n this.stepNum += 1;\n console.log('send impulse responses to server: ' + this.stepNum);\n this.status = this.generateTemplate(`All Hz Calibration: computing the IIR...`.toString()).toString();\n this.emit('update', {message: this.status});\n return await this.pyServerAPI\n .getSystemInverseImpulseResponseWithRetry({\n payload: filteredComputedIRs.slice(0, this.numCaptures),\n mls,\n lowHz,\n highHz,\n iirLength,\n sampleRate: this.sourceSamplingRate || 96000,\n mlsAmplitude: Math.pow(10, this.power_dB / 20),\n calibrateSoundBurstFilteredExtraDb: this._calibrateSoundBurstFilteredExtraDb,\n calibrateSoundIIRPhase: this.calibrateSoundIIRPhase,\n })\n .then(async res => {\n this.stepNum += 1;\n console.log('got impulse response ' + this.stepNum);\n this.incrementStatusBar();\n this.status = this.generateTemplate(`All Hz Calibration: done computing the IIR...`.toString()).toString();\n this.emit('update', {message: this.status});\n this.systemInvertedImpulseResponse = res['iir'];\n this.systemIR = res['ir'];\n this.systemInvertedImpulseResponseNoBandpass = res['iirNoBandpass'];\n this.systemAttenuatorGainDB = res['attenuatorGain_dB'];\n this.systemFMaxHz = res['fMaxHz'];\n await this.pyServerAPI.checkMemory();\n await this.pyServerAPI\n .getConvolution({\n mls,\n inverse_response: this.systemInvertedImpulseResponse,\n inverse_response_no_bandpass: this.systemInvertedImpulseResponseNoBandpass,\n attenuatorGain_dB: this.systemAttenuatorGainDB,\n mls_amplitude: Math.pow(10, this.power_dB / 20),\n })\n .then(result => {\n console.log(result);\n this.systemConvolution = result['convolution'];\n this.systemConvolutionNoBandpass = result['convolution_no_bandpass'];\n });\n\n // attenuate the system convolution if the amplitude is greater than this.calibrateSoundLimit\n // find max of absolute value of system convolution\n\n const max = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.findMaxValue)(this.systemConvolution);\n this.filteredMLSAttenuation.system =\n this.systemConvolution.reduce((a, b) => a + b ** 2, 0) / this.systemConvolution.length;\n this.filteredMLSAttenuation.maxAbsSystem = max;\n })\n .catch(err => {\n console.error(err);\n });\n };\n\n /** .\n * .\n * .\n * Sends all the computed impulse responses to the backend server for processing\n *\n * @returns sets the resulting inverted impulse response to the class property\n * @example\n */\n sendComponentImpulseResponsesToServerForProcessing = async () => {\n this.addTimeStamp('Compute component IIR');\n const computedIRs = await Promise.all(this.impulseResponses);\n const filteredComputedIRs = computedIRs.filter(element => {\n return element != undefined;\n });\n let componentIRGains = this.componentIR['Gain'];\n const componentIRFreqs = this.componentIR['Freq'];\n //normalize the component IR gains\n componentIRGains = componentIRGains.map(value => {\n return value + this._calibrateSoundBurstScalarDB - this._calibrateSoundBurstDb;\n });\n if (this._calibrateSoundBurstNormalizeBy1000HzGainBool) {\n const sineGainAt1000Hz_dB = this.gainDBSPL;\n componentIRGains = componentIRGains.map(value => {\n return value - sineGainAt1000Hz_dB;\n });\n }\n const mls = this.#mls[this.icapture];\n const lowHz = this.#lowHz;\n const iirLength = this.iirLength;\n const irLength = this.irLength;\n const highHz = this.#highHz;\n this.stepNum += 1;\n console.log('send impulse responses to server: ' + this.stepNum);\n this.status = this.generateTemplate(`All Hz Calibration: computing the IIR...`.toString()).toString();\n this.emit('update', {message: this.status});\n console.log()\n return this.pyServerAPI\n .getComponentInverseImpulseResponseWithRetry({\n payload: filteredComputedIRs.slice(0, this.numCaptures),\n mls,\n lowHz,\n highHz,\n iirLength,\n componentIRGains,\n componentIRFreqs,\n sampleRate: this.sourceSamplingRate || 96000,\n mlsAmplitude: Math.pow(10, this.power_dB / 20),\n irLength,\n calibrateSoundSmoothOctaves: this._calibrateSoundSmoothOctaves,\n calibrateSoundSmoothMinBandwidthHz: this._calibrateSoundSmoothMinBandwidthHz,\n calibrateSoundBurstFilteredExtraDb: this._calibrateSoundBurstFilteredExtraDb,\n calibrateSoundIIRPhase: this.calibrateSoundIIRPhase,\n })\n .then(async res => {\n this.stepNum += 1;\n console.log('got impulse response ' + this.stepNum);\n this.incrementStatusBar();\n this.status = this.generateTemplate(`All Hz Calibration: done computing the IIR...`.toString()).toString();\n this.emit('update', {message: this.status});\n this.componentInvertedImpulseResponse = res['iir'];\n this.componentInvertedImpulseResponseNoBandpass = res['iirNoBandpass'];\n this.componentIR['Gain'] = res['ir'];\n this.componentIR['Freq'] = res['frequencies'];\n this.componentIRPhase = res['component_angle'];\n this.systemIRPhase = res['system_angle'];\n this.componentIROrigin['Freq'] = res['frequencies'];\n this.componentIROrigin['Gain'] = res['irOrigin'];\n this.componentIRInTimeDomain = res['irTime'];\n this.componentAttenuatorGainDB = res['attenuatorGain_dB'];\n this.componentFMaxHz = res['fMaxHz'];\n await this.pyServerAPI.checkMemory();\n await this.pyServerAPI\n .getConvolution({\n mls,\n inverse_response: this.componentInvertedImpulseResponse,\n inverse_response_no_bandpass: this.componentInvertedImpulseResponseNoBandpass,\n attenuatorGain_dB: this.componentAttenuatorGainDB,\n mls_amplitude: Math.pow(10, this.power_dB / 20),\n })\n .then(result => {\n console.log(result);\n this.componentConvolution = result['convolution'];\n this.componentConvolutionNoBandpass = result['convolution_no_bandpass'];\n });\n // attenuate the component convolution if the amplitude is greater than this.calibrateSoundLimit\n // find max of absolute value of component convolution\n const max = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.findMaxValue)(this.componentConvolution);\n // if (max > this.calibrateSoundLimit) {\n // const gain = this.calibrateSoundLimit / max;\n // // apply gain to component convolution\n // this.componentConvolution = this.componentConvolution.map(value => value * gain);\n // this.filteredMLSAttenuation.component = gain;\n // }\n this.filteredMLSAttenuation.component =\n this.componentConvolution.reduce((a, b) => a + b ** 2, 0) /\n this.componentConvolution.length;\n this.filteredMLSAttenuation.maxAbsComponent = max;\n })\n .catch(err => {\n // this.emit('InvertedImpulseResponse', {res: false});\n console.error(err);\n });\n };\n\n sendBackgroundRecording = () => {\n const allSignals = this.getAllBackgroundRecordings();\n const numSignals = allSignals.length;\n const background_rec_whole = allSignals[numSignals - 1];\n const fraction = 0.5 / (this._calibrateSoundBackgroundSecs + 0.5);\n // Calculate the starting index for slicing the array\n const startIndex = Math.round(fraction * background_rec_whole.length);\n // Slice the array from the calculated start index to the end of the array\n const background_rec = background_rec_whole.slice(startIndex);\n console.log('Sending background recording to server for processing');\n this.addTimeStamp('Compute PSD of background');\n this.pyServerAPI\n .getBackgroundNoisePSDWithRetry({\n background_rec,\n sampleRate: this.sourceSamplingRate || 96000,\n })\n .then(res => {\n if (this.numSuccessfulBackgroundCaptured < 1) {\n this.numSuccessfulBackgroundCaptured += 1;\n //storing all background data in background_psd object\n this.background_noise['x_background'] = res['x_background'];\n this.background_noise['y_background'] = res['y_background'];\n this.background_noise['recording'] = background_rec;\n }\n })\n .catch(err => {\n console.error(err);\n });\n };\n\n /** .\n * .\n * .\n * Sends the recorded signal, or a given csv string of a signal, to the back end server for processing\n *\n * @param {<array>String} signalCsv - Optional csv string of a previously recorded signal, if given, this signal will be processed\n * @example\n */\n sendRecordingToServerForProcessing = async signalCsv => {\n const allSignals = this.getAllUnfilteredRecordedSignals();\n console.log(\n 'Obtaining last all hz unfiltered recording from #allHzUnfilteredRecordings to send to server for processing'\n );\n const numSignals = allSignals.length;\n const mls = this.#mlsBufferView[this.icapture];\n const payload =\n signalCsv && signalCsv.length > 0 ? (0,_utils__WEBPACK_IMPORTED_MODULE_1__.csvToArray)(signalCsv) : allSignals[numSignals - 1];\n console.log('sending rec');\n this.stepNum += 1;\n console.log('send rec ' + this.stepNum);\n this.status = this.generateTemplate(`All Hz Calibration Step: computing the IR of the last recording...`.toString()).toString();\n this.emit('update', {message: this.status});\n if (this.isCalibrating) return null;\n await this.pyServerAPI\n .allHzPowerCheck({\n payload,\n sampleRate: this.sourceSamplingRate || 96000,\n binDesiredSec: this._calibrateSoundPowerBinDesiredSec,\n burstSec: this.desired_time_per_mls,\n repeats: this.numMLSPerCapture - this.num_mls_to_skip,\n warmUp: this.num_mls_to_skip \n })\n .then(async result => {\n if (result) {\n if (result['sd'] > this._calibrateSoundBurstMaxSD_dB && \n this.numSuccessfulCaptured == 0) {\n console.log('SD: ' + result['sd'] + ', greater than _calibrateSoundBurstMaxSD_dB: ' + this._calibrateSoundBurstMaxSD_dB);\n this.recordingChecks['unfiltered'].push(result);\n this.clearLastUnfilteredRecordedSignals();\n this.numSuccessfulCaptured +=1;\n } else {\n if (result['sd'] <= this._calibrateSoundBurstMaxSD_dB) {\n console.log('SD: ' + result['sd'] + ', less than _calibrateSoundBurstMaxSD_dB: ' + this._calibrateSoundBurstMaxSD_dB);\n } else {\n console.log('SD: ' + result['sd'] + ', greater than _calibrateSoundBurstMaxSD_dB: ' + this._calibrateSoundBurstMaxSD_dB);\n this.recordingChecks['warnings'].push(`All Hz. Re-recorded ${this.inDB} dB because SD ${result['sd']} > ${this._calibrateSoundBurstMaxSD_dB} dB`);\n this.status = this.generateTemplate(`All Hz: Re-recording at ${this.inDB} dB because SD ${result['sd']} > ${this._calibrateSoundBurstMaxSD_dB} dB`.toString()).toString();\n this.emit('update', {\n message: this.status,\n });\n }\n if (this.numSuccessfulCaptured == 1) {\n console.log('pop last unfiltered mls volume check');\n this.recordingChecks['unfiltered'].pop();\n }\n this.recordingChecks['unfiltered'].push(result);\n // let start = new Date().getTime() / 1000;\n // const payloadT = tf.tensor1d(payload);\n // payloadT.print();\n // const xfft = payloadT.rfft(); // tf.spe\n // xfft.array().then(array => {\n // console.log(\"fft:\", array);\n // let setItem = new Set(array);\n // console.log(\"all zero\", setItem.size === 1 && setItem.has(0));\n // });\n // console.log(\"dimention:\", xfft.shape);\n // let end = new Date().getTime() / 1000;\n // console.log(\"Time taken:\", end - start, \"seconds\");\n console.log('start calculate impulse response');\n const usedPeriodStart = this.num_mls_to_skip * this.sourceSamplingRate;\n const payload_skipped_warmUp = payload.slice(usedPeriodStart);\n await this.pyServerAPI\n .getAutocorrelation({\n mls:mls,\n payload: payload_skipped_warmUp,\n sampleRate: this.sourceSamplingRate || 96000,\n numPeriods: this.numMLSPerCapture - this.num_mls_to_skip,\n })\n .then(async res => {\n this.autocorrelations.push(res['autocorrelation']);\n this.fs2 = res['fs2'];\n this.L_new_n = res['L_new_n'];\n this.dL_n = res['dL_n'];\n this.impulseResponses.push(\n await this.pyServerAPI\n .getImpulseResponse({\n mls,\n sampleRate: this.sourceSamplingRate || 96000,\n numPeriods: this.numMLSPerCapture - this.num_mls_to_skip,\n sig: payload_skipped_warmUp,\n fs2: this.fs2,\n L_new_n: this.L_new_n,\n dL_n: this.dL_n,\n })\n .then(res => {\n this.numSuccessfulCaptured += 2;\n this.stepNum += 1;\n console.log('got impulse response ' + this.stepNum);\n this.incrementStatusBar();\n this.status = this.generateTemplate(`All Hz Calibration: ${this.numSuccessfulCaptured}/${this.numCaptures} IRs computed...`.toString()).toString();\n this.emit('update', {\n message: this.status,\n });\n return res['ir'];\n })\n .catch(err => {\n console.error(err);\n })\n );\n });\n }\n console.log('number of unfiltered recording checks:' + this.recordingChecks['unfiltered'].length);\n }\n })\n .catch(err => {\n console.error(err);\n });\n };\n\n /**\n * Passed to the calibration steps function, awaits the desired amount of seconds to capture the desired number\n * of MLS periods defined in the constructor.\n *\n * @example\n */\n #awaitDesiredMLSLength = async () => {\n // seconds per MLS = P / SR\n // await N * P / SR\n this.stepNum += 1;\n console.log('await desired length ' + this.stepNum);\n this.status = this.generateTemplate(`All Hz Calibration: sampling the calibration signal...`.toString() +\n `\\niteration ${this.stepNum}`);\n this.emit('update', {\n message: this.status,\n });\n let time_to_wait = 0;\n if (this.mode === 'unfiltered') {\n //unfiltered\n time_to_wait = (this.#mls[0].length / this.sourceSamplingRate) * this.numMLSPerCapture;\n time_to_wait = time_to_wait + this._calibrateSoundBurstPostSec;\n } else if (this.mode === 'filtered') {\n //filtered\n // time_to_wait =\n // (this.#currentConvolution.length / this.sourceSamplingRate) *\n // (this.numMLSPerCapture / (this.num_mls_to_skip + this.numMLSPerCapture));\n time_to_wait =\n (this.#currentConvolution.length / this.sourceSamplingRate) * this.numMLSPerCapture;\n time_to_wait = time_to_wait + this._calibrateSoundBurstPostSec;\n } else {\n throw new Error('Mode broke in awaitDesiredMLSLength');\n }\n\n await (0,_utils__WEBPACK_IMPORTED_MODULE_1__.sleep)(time_to_wait);\n };\n\n /**\n * Passed to the background noise recording function, awaits the desired amount of seconds to capture the desired number\n * of seconds of background noise\n *\n * @example\n */\n #awaitBackgroundNoiseRecording = async () => {\n console.log(\n 'Waiting ' + this._calibrateSoundBackgroundSecs + ' second(s) to record background noise'\n );\n let time_to_wait = this._calibrateSoundBackgroundSecs + 0.5;\n this.addTimeStamp(`Record ${time_to_wait.toFixed(1)} s of background.`)\n await (0,_utils__WEBPACK_IMPORTED_MODULE_1__.sleep)(time_to_wait);\n };\n\n /** .\n * .\n * .\n * Passed to the calibration steps function, awaits the onset of the signal to ensure a steady state\n *\n * @example\n */\n #awaitSignalOnset = async () => {\n this.stepNum += 1;\n console.log('await signal onset ' + this.stepNum);\n this.status = this.generateTemplate(`All Hz Calibration: waiting for the signal to stabilize...`.toString());\n this.emit('update', {\n message: this.status,\n });\n let number_of_bursts_to_skip = 0;\n let time_to_sleep = 0;\n if (this.mode === 'unfiltered') {\n time_to_sleep = (this.#mls[0].length / this.sourceSamplingRate) * number_of_bursts_to_skip;\n } else if (this.mode === 'filtered') {\n console.log(this.#currentConvolution.length);\n // time_to_sleep =\n // (this.#currentConvolution.length / this.sourceSamplingRate) *\n // (number_of_bursts_to_skip / (number_of_bursts_to_skip + this.numMLSPerCapture));\n time_to_sleep =\n (this.#currentConvolution.length / this.sourceSamplingRate) * number_of_bursts_to_skip;\n } else {\n throw new Error('Mode broke in awaitSignalOnset');\n }\n await (0,_utils__WEBPACK_IMPORTED_MODULE_1__.sleep)(time_to_sleep);\n };\n\n /**\n * Called immediately after a recording is captured. Used to process the resulting signal\n * whether by sending the result to a server or by computing a result locally.\n *\n * @example\n */\n #afterMLSRecord = async () => {\n console.log('after record');\n this.addTimeStamp(`Send MLS to the server`);\n await this.sendRecordingToServerForProcessing();\n };\n\n #afterMLSwIIRRecord = async () => {\n await this.checkPowerVariation();\n };\n\n /** .\n * .\n * .\n * Created an S Curver Buffer to taper the signal onset\n *\n * @param {*} onSetBool\n * @returns\n * @example\n */\n createSCurveBuffer = (onSetBool = true) => {\n const curve = new Float32Array(this.TAPER_SECS * this.sourceSamplingRate + 1);\n const frequency = 1 / (4 * this.TAPER_SECS);\n let j = 0;\n for (let i = 0; i < this.TAPER_SECS * this.sourceSamplingRate + 1; i += 1) {\n const phase = 2 * Math.PI * frequency * j;\n const onsetTaper = Math.pow(Math.sin(phase), 2);\n const offsetTaper = Math.pow(Math.cos(phase), 2);\n curve[i] = onSetBool ? onsetTaper : offsetTaper;\n j += 1 / this.sourceSamplingRate;\n }\n return curve;\n };\n\n static createInverseSCurveBuffer = (length, phase) => {\n const curve = new Float32Array(length);\n let i;\n let j = length - 1;\n for (i = 0; i < length; i += 1) {\n // scale the curve to be between 0-1\n curve[i] = Math.sin((Math.PI * j) / length - phase) / 2 + 0.5;\n j -= 1;\n }\n return curve;\n };\n\n /**\n * Construct a Calibration Node with the calibration parameters.\n *\n * @param dataBuffer\n * @private\n * @example\n */\n #createCalibrationNodeFromBuffer = dataBuffer => {\n console.log('length databuffer');\n console.log(dataBuffer.length);\n if (!this.sourceAudioContext) {\n this.makeNewSourceAudioContext();\n }\n\n const buffer = this.sourceAudioContext.createBuffer(\n 1, // number of channels\n dataBuffer.length,\n this.sourceAudioContext.sampleRate // sample rate\n );\n\n const data = buffer.getChannelData(0); // get data\n\n // fill the buffer with our data\n try {\n for (let i = 0; i < dataBuffer.length; i += 1) {\n data[i] = dataBuffer[i];\n }\n } catch (error) {\n console.error(error);\n }\n\n this.sourceNode = this.sourceAudioContext.createBufferSource();\n\n this.sourceNode.buffer = buffer;\n\n if (this.mode === 'filtered') {\n //used to not loop filtered\n this.sourceNode.loop = true;\n } else {\n this.sourceNode.loop = true;\n }\n\n this.sourceNode.connect(this.sourceAudioContext.destination);\n\n this.addCalibrationNode(this.sourceNode);\n };\n\n /**\n * Given a data buffer, creates the required calibration node\n *\n * @param {*} dataBufferArray\n * @example\n */\n #setCalibrationNodesFromBuffer = (dataBufferArray = [this.#mlsBufferView[this.icapture]]) => {\n if (dataBufferArray.length === 1) {\n this.#createCalibrationNodeFromBuffer(dataBufferArray[0]);\n } else {\n throw new Error('The length of the data buffer array must be 1');\n }\n };\n\n /**\n * Creates an audio context and plays it for a few seconds.\n *\n * @private\n * @returns - Resolves when the audio is done playing.\n * @example\n */\n #playCalibrationAudio = () => {\n this.calibrationNodes[0].start(0);\n this.status = ``;\n if (this.mode === 'unfiltered') {\n console.log('play calibration audio ' + this.stepNum);\n\n const pre = this._calibrateSoundBurstPreSec; \n const repeats = this._calibrateSoundBurstRepeats; \n const burst = this._calibrateSoundBurstSec; \n const post = this._calibrateSoundBurstPostSec; \n const total_dur = pre + repeats * burst + post;\n this.addTimeStamp(\n `Record ${total_dur.toFixed(1)} s ` +\n `(${pre.toFixed(1)} + ${repeats}×${burst.toFixed(1)} + ${post.toFixed(1)} s) of MLS ver. ${this.icapture}`\n );\n this.status = this.generateTemplate(`All Hz Calibration: playing the calibration tone...`.toString()).toString();\n } else if (this.mode === 'filtered') {\n console.log('play convolved audio ' + this.stepNum);\n this.status = this.generateTemplate().toString( `All Hz Calibration: playing the convolved calibration tone...`.toString());\n } else {\n throw new Error('Mode is incorrect');\n }\n this.emit('update', {message: this.status});\n this.stepNum += 1;\n console.log('sink sampling rate');\n console.log(this.sinkSamplingRate);\n console.log('source sampling rate');\n console.log(this.sourceSamplingRate);\n console.log('sample size');\n console.log(this.sampleSize);\n };\n\n /** .\n * .\n * .\n * Stops the audio with tapered offset\n *\n * @example\n */\n stopCalibrationAudio = () => {\n if (this.calibrationNodes.length === 0) {\n return;\n }\n this.calibrationNodes[0].stop(0);\n this.calibrationNodes = [];\n if (this.sourceNode) this.sourceNode.disconnect();\n this.stepNum += 1;\n console.log('stop calibration audio ' + this.stepNum);\n this.status = this.generateTemplate(`All Hz Calibration: stopping the calibration tone...`.toString()).toString();\n this.emit('update', {message: this.status});\n };\n\n playMLSwithIIR = async (stream, convolution) => {\n let checkRec = false;\n this.mode = 'filtered';\n console.log('play mls with iir');\n //this.invertedImpulseResponse = iir\n\n await this.calibrationSteps(\n stream,\n this.#playCalibrationAudio, // play audio func (required)\n this.#createCalibrationNodeFromBuffer(convolution), // before play func\n this.#awaitSignalOnset, // before record\n () => this.numSuccessfulCaptured < 2,\n this.#awaitDesiredMLSLength, // during record\n this.#afterMLSwIIRRecord, // after record\n this.mode,\n checkRec\n );\n };\n\n bothSoundCheck = async stream => {\n let iir_ir_and_plots;\n this.#currentConvolution = this.componentConvolution;\n this.filteredMLSRange.component.Min = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.findMinValue)(this.#currentConvolution);\n this.filteredMLSRange.component.Max = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.findMaxValue)(this.#currentConvolution);\n const pre = this._calibrateSoundBurstPreSec; \n const repeats = this._calibrateSoundBurstRepeats; \n const burst = this._calibrateSoundBurstSec; \n const post = this._calibrateSoundBurstPostSec; \n const total_dur = pre + repeats * burst + post;\n this.soundCheck = 'component';\n this.addTimeStamp(`Record ${total_dur} s of MLS with ${this.soundCheck} IIR.”`)\n \n if (this.isCalibrating) return null;\n await this.playMLSwithIIR(stream, this.#currentConvolution);\n this.stopCalibrationAudio();\n let component_conv_recs = this.getAllFilteredRecordedSignals();\n\n if (this.componentAttentuatorGainDB != 0) {\n let linearScaleAttenuation = Math.pow(10, this.componentAttentuatorGainDB / 20);\n component_conv_recs = component_conv_recs.map(rec => {\n return rec.map(value => value / this.linearScaleAttenuation);\n });\n }\n\n let return_component_conv_rec = component_conv_recs[component_conv_recs.length - 1];\n this.clearAllFilteredRecordedSignals();\n\n this.numSuccessfulCaptured = 0;\n this.#currentConvolution = this.systemConvolution;\n this.filteredMLSRange.system.Min = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.findMinValue)(this.#currentConvolution);\n this.filteredMLSRange.system.Max = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.findMaxValue)(this.#currentConvolution);\n this.soundCheck = 'system';\n\n this.addTimeStamp(`Record ${total_dur} s of MLS with ${this.soundCheck} IIR.”`)\n\n\n if (this.isCalibrating) return null;\n await this.playMLSwithIIR(stream, this.#currentConvolution);\n\n this.stopCalibrationAudio();\n\n let system_conv_recs = this.getAllFilteredRecordedSignals();\n\n if (this.systemAttenuatorGainDB != 0) {\n let linearScaleAttenuation = Math.pow(10, this.systemAttenuatorGainDB / 20);\n system_conv_recs = system_conv_recs.map(rec => {\n return rec.map(value => value / linearScaleAttenuation);\n });\n }\n\n let return_system_conv_rec = system_conv_recs[system_conv_recs.length - 1];\n // await this.checkPowerVariation(return_system_conv_rec);\n\n this.clearAllFilteredRecordedSignals();\n\n this.sourceAudioContext.close();\n let recs = this.getAllUnfilteredRecordedSignals();\n if (this.componentAttentuatorGainDB != 0) {\n let linearScaleAttenuation = Math.pow(10, this.componentAttentuatorGainDB / 20);\n recs = recs.map(rec => {\n return rec.map(value => value / this.linearScaleAttenuation);\n });\n }\n let unconv_rec = recs[0];\n let return_unconv_rec = unconv_rec;\n let conv_rec = component_conv_recs[component_conv_recs.length - 1];\n\n //psd of component\n let knownGain = this.oldComponentIR.Gain;\n let knownFreq = this.oldComponentIR.Freq;\n let sampleRate = this.sourceSamplingRate || 96000;\n this.addTimeStamp('Compute PSD of MLS recording');\n if (this.isCalibrating) return null;\n let component_unconv_rec_psd = await this.pyServerAPI\n .getSubtractedPSDWithRetry(unconv_rec, knownGain, knownFreq, sampleRate)\n .then(res => {\n this.incrementStatusBar();\n this.status = this.generateTemplate(`All Hz Calibration: done computing the PSD graphs...`.toString()).toString();\n this.emit('update', {message: this.status});\n return res;\n })\n .catch(err => {\n console.error(err);\n });\n\n this.addTimeStamp('Compute PSD of filtered recording (component)');\n if (this.isCalibrating) return null;\n let component_conv_rec_psd = await this.pyServerAPI\n .getSubtractedPSDWithRetry(conv_rec, knownGain, knownFreq, sampleRate)\n .then(res => {\n let interpolatedGain = res.x.map((freq, index) => {\n let i = 0;\n while (i < knownFreq.length && knownFreq[i] < freq) {\n i++;\n }\n if (i === 0 || i === knownFreq.length) {\n return knownGain[i];\n }\n return (0,_utils__WEBPACK_IMPORTED_MODULE_1__.interpolate)(freq, knownFreq[i - 1], knownFreq[i], knownGain[i - 1], knownGain[i]);\n });\n\n let correctedGain = res.y.map(\n (gain, index) => 10 * Math.log10(gain) - interpolatedGain[index]\n );\n\n let filtered_psd = correctedGain.filter(\n (value, index) => res.x[index] >= this.#lowHz && res.x[index] <= this.componentFMaxHz\n );\n\n this.SDofFilteredRange['component'] = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.standardDeviation)(filtered_psd);\n this.incrementStatusBar();\n this.status =this.generateTemplate(`All Hz Calibration: done computing the PSD graphs...`.toString()).toString();\n this.emit('update', {message: this.status});\n return res;\n })\n .catch(err => {\n console.error(err);\n });\n\n conv_rec = system_conv_recs[system_conv_recs.length - 1];\n //psd of system\n this.addTimeStamp('Compute PSD of filtered recording (system) and unfiltered recording');\n if (this.isCalibrating) return null;\n let system_recs_psd = await this.pyServerAPI\n .getPSDWithRetry({\n unconv_rec,\n conv_rec,\n sampleRate: this.sourceSamplingRate || 96000,\n })\n .then(res => {\n let filtered_psd = res.y_conv\n .filter(\n (value, index) => res.x_conv[index] >= this.#lowHz && res.x_conv[index] <= this.#highHz\n )\n .map(value => 10 * Math.log10(value));\n\n let mls_psd = res.y_unconv\n .filter(\n (value, index) =>\n res.x_unconv[index] >= this.#lowHz && res.x_conv[index] <= this.#highHz\n )\n .map(value => 10 * Math.log10(value));\n\n this.SDofFilteredRange['mls'] = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.standardDeviation)(mls_psd);\n console.log('mls_psd', this.SDofFilteredRange['mls']);\n this.SDofFilteredRange['system'] = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.standardDeviation)(filtered_psd);\n this.incrementStatusBar();\n this.status = this.generateTemplate(`All Hz Calibration: done computing the PSD graphs...`.toString()).toString();\n this.emit('update', {message: this.status});\n return res;\n })\n .catch(err => {\n console.error(err);\n });\n\n //iir w/ and without bandpass psd. done\n unconv_rec = this.componentInvertedImpulseResponseNoBandpass;\n conv_rec = this.componentInvertedImpulseResponse;\n this.addTimeStamp('Compute PSD of component IIR and component IIR no band pass');\n if (this.isCalibrating) return null;\n let component_iir_psd = await this.pyServerAPI\n .getPSDWithRetry({\n unconv_rec,\n conv_rec,\n sampleRate: this.sourceSamplingRate || 96000,\n })\n .then(res => {\n this.incrementStatusBar();\n this.status = this.generateTemplate(`All Hz Calibration: done computing the PSD graphs...`.toString()).toString();\n this.emit('update', {message: this.status});\n return res;\n })\n .catch(err => {\n console.error(err);\n });\n unconv_rec = this.systemInvertedImpulseResponseNoBandpass;\n conv_rec = this.systemInvertedImpulseResponse;\n this.addTimeStamp('Compute PSD of system IIR and system IIR no band pass');\n if (this.isCalibrating) return null;\n let system_iir_psd = await this.pyServerAPI\n .getPSDWithRetry({\n unconv_rec,\n conv_rec,\n sampleRate: this.sourceSamplingRate || 96000,\n })\n .then(res => {\n this.incrementStatusBar();\n this.status = this.generateTemplate(`All Hz Calibration: done computing the PSD graphs...`.toString() ).toString();\n this.emit('update', {message: this.status});\n return res;\n })\n .catch(err => {\n console.error(err);\n });\n\n this.addTimeStamp('Compute PSD of MLS sequence');\n if (this.isCalibrating) return null;\n let mls_psd = await this.pyServerAPI\n .getMLSPSDWithRetry({\n mls: this.#mlsBufferView[0],\n sampleRate: this.sourceSamplingRate || 96000,\n })\n .then(res => {\n this.incrementStatusBar();\n this.status = this.generateTemplate(`All Hz Calibration: done computing the PSD graphs...`.toString()).toString();\n this.emit('update', {message: this.status});\n return res;\n })\n .catch(err => {\n console.error(err);\n });\n\n this.addTimeStamp('Compute PSD of filtered MLS (system)');\n if (this.isCalibrating) return null;\n let system_filtered_mls_psd = await this.pyServerAPI\n .getMLSPSDWithRetry({\n mls: this.systemConvolution,\n sampleRate: this.sourceSamplingRate || 96000,\n })\n .then(res => {\n this.incrementStatusBar();\n this.status = this.generateTemplate(`All Hz Calibration: done computing the PSD graphs...`.toString() ).toString();\n this.emit('update', {message: this.status});\n return res;\n })\n .catch(err => {\n console.error(err);\n });\n\n let system_no_bandpass_filtered_mls_psd = await this.pyServerAPI\n .getMLSPSDWithRetry({\n mls: this.systemConvolution,\n sampleRate: this.sourceSamplingRate || 96000,\n })\n .then(res => {\n this.incrementStatusBar();\n this.status =this.generateTemplate(`All Hz Calibration: done computing the PSD graphs...`.toString()).toString();\n this.emit('update', {message: this.status});\n return res;\n })\n .catch(err => {\n console.error(err);\n });\n\n this.addTimeStamp('Compute PSD of filtered MLS (component)');\n if (this.isCalibrating) return null;\n let component_filtered_mls_psd = await this.pyServerAPI\n .getMLSPSDWithRetry({\n mls: this.componentConvolution,\n sampleRate: this.sourceSamplingRate || 96000,\n })\n .then(res => {\n this.incrementStatusBar();\n this.status = this.generateTemplate(`All Hz Calibration: done computing the PSD graphs...`.toString() ).toString();\n this.emit('update', {message: this.status});\n return res;\n })\n .catch(err => {\n console.error(err);\n });\n\n let component_no_bandpass_filtered_mls_psd = await this.pyServerAPI\n .getMLSPSDWithRetry({\n mls: this.componentConvolutionNoBandpass,\n sampleRate: this.sourceSamplingRate || 96000,\n })\n .then(res => {\n this.incrementStatusBar();\n this.status = this.generateTemplate(`All Hz Calibration: done computing the PSD graphs...`.toString() ).toString();\n this.emit('update', {message: this.status});\n return res;\n })\n .catch(err => {\n console.error(err);\n });\n\n let gainValue = this.getGainDBSPL();\n\n iir_ir_and_plots = {\n filtered_recording: {\n component: return_component_conv_rec,\n system: return_system_conv_rec,\n },\n unfiltered_recording: this.getAllUnfilteredRecordedSignals()[0],\n system: {\n iir: this.systemInvertedImpulseResponse,\n iir_no_bandpass: this.systemInvertedImpulseResponseNoBandpass,\n ir: this.systemIR,\n iir_psd: {\n y: system_iir_psd['y_conv'],\n x: system_iir_psd['x_conv'],\n y_no_bandpass: system_iir_psd['y_unconv'],\n x_no_bandpass: system_iir_psd['x_unconv'],\n },\n filtered_mls_psd: {\n x: system_filtered_mls_psd['x_mls'],\n y: system_filtered_mls_psd['y_mls'],\n },\n filtered_no_bandpass_mls_psd: {\n x: system_no_bandpass_filtered_mls_psd['x_mls'],\n y: system_no_bandpass_filtered_mls_psd['y_mls'],\n },\n convolution: this.systemConvolution,\n convolutionNoBandpass: this.systemConvolutionNoBandpass,\n psd: {\n unconv: {\n x: system_recs_psd['x_unconv'],\n y: system_recs_psd['y_unconv'],\n },\n conv: {\n x: system_recs_psd['x_conv'],\n y: system_recs_psd['y_conv'],\n },\n },\n },\n component: {\n iir: this.componentInvertedImpulseResponse,\n iir_no_bandpass: this.componentInvertedImpulseResponseNoBandpass,\n ir: this.componentIR,\n ir_origin: this.componentIROrigin,\n ir_in_time_domain: this.componentIRInTimeDomain,\n iir_psd: {\n y: component_iir_psd['y_conv'],\n x: component_iir_psd['x_conv'],\n y_no_bandpass: component_iir_psd['y_unconv'],\n x_no_bandpass: component_iir_psd['x_unconv'],\n },\n filtered_mls_psd: {\n x: component_filtered_mls_psd['x_mls'],\n y: component_filtered_mls_psd['y_mls'],\n },\n filtered_no_bandpass_mls_psd: {\n x: component_no_bandpass_filtered_mls_psd['x_mls'],\n y: component_no_bandpass_filtered_mls_psd['y_mls'],\n },\n convolution: this.componentConvolution,\n convolutionNoBandpass: this.componentConvolutionNoBandpass,\n psd: {\n unconv: {\n x: component_unconv_rec_psd['x'],\n y: component_unconv_rec_psd['y'],\n },\n conv: {\n x: component_conv_rec_psd['x'],\n y: component_conv_rec_psd['y'],\n },\n },\n gainDBSPL: gainValue,\n },\n mls: this.#mlsBufferView,\n mls_psd: {\n x: mls_psd['x_mls'],\n y: mls_psd['y_mls'],\n },\n autocorrelations: this.autocorrelations,\n impulseResponses: [],\n };\n\n return iir_ir_and_plots;\n };\n\n singleSoundCheck = async stream => {\n let iir_ir_and_plots;\n const pre = this._calibrateSoundBurstPreSec; \n const repeats = this._calibrateSoundBurstRepeats; \n const burst = this._calibrateSoundBurstSec; \n const post = this._calibrateSoundBurstPostSec; \n const total_dur = pre + repeats * burst + post;\n if (this._calibrateSoundCheck != 'system') {\n this.#currentConvolution = this.componentConvolution;\n this.filteredMLSRange.component.Min = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.findMinValue)(this.#currentConvolution);\n this.filteredMLSRange.component.Max = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.findMaxValue)(this.#currentConvolution);\n this.soundCheck = 'component';\n this.addTimeStamp(`Record ${total_dur} s of MLS with ${this.soundCheck} IIR.`)\n //this.addTimeStamp(`Record ${total_dur} s of MLS with speaker or microphone IIR.`);\n if (this.isCalibrating) return null;\n await this.playMLSwithIIR(stream, this.#currentConvolution);\n this.stopCalibrationAudio();\n } else {\n this.#currentConvolution = this.systemConvolution;\n this.filteredMLSRange.system.Min = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.findMinValue)(this.#currentConvolution);\n this.filteredMLSRange.system.Max = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.findMaxValue)(this.#currentConvolution);\n this.soundCheck = 'system';\n this.addTimeStamp(`Record ${total_dur} s of MLS with ${this.soundCheck} IIR.`)\n //this.addTimeStamp(`Record ${total_dur} s of MLS with speaker or microphone IIR.`);\n if (this.isCalibrating) return null;\n await this.playMLSwithIIR(stream, this.#currentConvolution);\n this.stopCalibrationAudio();\n }\n let conv_recs = this.getAllFilteredRecordedSignals();\n if (this._calibrateSoundCheck == 'goal') {\n if (this.componentAttentuatorGainDB != 0) {\n let linearScaleAttenuation = Math.pow(10, this.componentAttentuatorGainDB / 20);\n conv_recs = conv_recs.map(rec => {\n return rec.map(value => value / this.linearScaleAttenuation);\n });\n }\n } else if (this._calibrateSoundCheck == 'system') {\n if (this.systemAttentuatorGainDB != 0) {\n let linearScaleAttenuation = Math.pow(10, this.systemAttentuatorGainDB / 20);\n conv_recs = conv_recs.map(rec => {\n return rec.map(value => value / this.linearScaleAttenuation);\n });\n }\n }\n\n //remove the filteredMLSAttenuation from the recorded signals\n // conv_recs = conv_recs.map(rec => {\n // if (this.soundCheck === 'component') {\n // return rec.map(value => value / this.filteredMLSAttenuation.component);\n // }\n // return rec.map(value => value / this.filteredMLSAttenuation.system);\n // });\n\n let recs = this.getAllUnfilteredRecordedSignals();\n if (this._calibrateSoundCheck == 'goal') {\n if (this.componentAttentuatorGainDB != 0) {\n let linearScaleAttenuation = Math.pow(10, this.componentAttentuatorGainDB / 20);\n recs = recs.map(rec => {\n return rec.map(value => value / this.linearScaleAttenuation);\n });\n }\n } else if (this._calibrateSoundCheck == 'system') {\n if (this.systemAttentuatorGainDB != 0) {\n let linearScaleAttenuation = Math.pow(10, this.systemAttentuatorGainDB / 20);\n recs = recs.map(rec => {\n return rec.map(value => value / this.linearScaleAttenuation);\n });\n }\n }\n this.clearAllFilteredRecordedSignals();\n console.log('Obtaining unfiltered recording from #allHzUnfilteredRecordings to calculate PSD');\n console.log('Obtaining filtered recording from #allHzFilteredRecordings to calculate PSD');\n let unconv_rec = recs[0];\n let return_unconv_rec = unconv_rec;\n let conv_rec = conv_recs[conv_recs.length - 1];\n let return_conv_rec = conv_rec;\n this.sourceAudioContext.close();\n if (this._calibrateSoundCheck != 'system') {\n let knownGain = this.oldComponentIR.Gain;\n let knownFreq = this.oldComponentIR.Freq;\n let sampleRate = this.sourceSamplingRate || 96000;\n this.addTimeStamp('Compute PSD of MLS recording');\n if (this.isCalibrating) return null;\n let unconv_results = await this.pyServerAPI\n .getSubtractedPSDWithRetry(unconv_rec, knownGain, knownFreq, sampleRate)\n .then(res => {\n console.log(res);\n let mls_psd = res.y\n .filter(\n (value, index) => res.x[index] >= this.#lowHz && res.x[index] <= this.systemFMaxHz\n )\n .map(value => 10 * Math.log10(value));\n this.SDofFilteredRange['mls'] = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.standardDeviation)(mls_psd);\n console.log('mls sd', this.SDofFilteredRange['mls']);\n this.incrementStatusBar();\n this.status = this.generateTemplate(`All Hz Calibration: done computing the PSD graphs...`.toString()).toString();\n this.emit('update', {message: this.status});\n return res;\n })\n .catch(err => {\n console.error(err);\n });\n\n this.addTimeStamp('Compute PSD recording of speaker+ mic IIR-filtered MLS recording');\n if (this.isCalibrating) return null;\n let conv_results = await this.pyServerAPI\n .getSubtractedPSDWithRetry(conv_rec, knownGain, knownFreq, sampleRate)\n .then(res => {\n let interpolatedGain = res.x.map((freq, index) => {\n let i = 0;\n while (i < knownFreq.length && knownFreq[i] < freq) {\n i++;\n }\n if (i === 0 || i === knownFreq.length) {\n return knownGain[i];\n }\n return (0,_utils__WEBPACK_IMPORTED_MODULE_1__.interpolate)(\n freq,\n knownFreq[i - 1],\n knownFreq[i],\n knownGain[i - 1],\n knownGain[i]\n );\n });\n\n let correctedGain = res.y.map(\n (gain, index) => 10 * Math.log10(gain) - interpolatedGain[index]\n );\n let filtered_psd = correctedGain.filter(\n (value, index) => res.x[index] >= this.#lowHz && res.x[index] <= this.#highHz\n );\n\n this.SDofFilteredRange['component'] = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.standardDeviation)(filtered_psd);\n this.incrementStatusBar();\n this.status = this.generateTemplate(`All Hz Calibration: done computing the PSD graphs...`.toString()).toString();\n this.emit('update', {message: this.status});\n return res;\n })\n .catch(err => {\n console.error(err);\n });\n\n unconv_rec = this.componentInvertedImpulseResponseNoBandpass;\n conv_rec = this.componentInvertedImpulseResponse;\n this.addTimeStamp('Compute PSD of speaker or mic IIR, with and without bandpass');\n if (this.isCalibrating) return null;\n let component_iir_psd = await this.pyServerAPI\n .getPSDWithRetry({\n unconv_rec,\n conv_rec,\n sampleRate: this.sourceSamplingRate || 96000,\n })\n .then(res => {\n this.incrementStatusBar();\n this.status = this.generateTemplate(`All Hz Calibration: done computing the PSD graphs...`.toString()).toString();\n this.emit('update', {message: this.status});\n return res;\n })\n .catch(err => {\n console.error(err);\n });\n unconv_rec = this.systemInvertedImpulseResponseNoBandpass;\n conv_rec = this.systemInvertedImpulseResponse;\n this.addTimeStamp('Compute PSD of speaker +mic IIR, with and without bandpass');\n if (this.isCalibrating) return null;\n let system_iir_psd = await this.pyServerAPI\n .getPSDWithRetry({\n unconv_rec,\n conv_rec,\n sampleRate: this.sourceSamplingRate || 96000,\n })\n .then(res => {\n this.incrementStatusBar();\n this.status = this.generateTemplate(`All Hz Calibration: done computing the PSD graphs...`.toString()).toString();\n this.emit('update', {message: this.status});\n return res;\n })\n .catch(err => {\n console.error(err);\n });\n\n this.addTimeStamp('Compute PSD of MLS');\n if (this.isCalibrating) return null;\n let mls_psd = await this.pyServerAPI\n .getMLSPSDWithRetry({\n mls: this.#mlsBufferView[this.icapture],\n sampleRate: this.sourceSamplingRate || 96000,\n })\n .then(res => {\n this.incrementStatusBar();\n this.status = this.generateTemplate(`All Hz Calibration: done computing the PSD graphs...`.toString()).toString();\n this.emit('update', {message: this.status});\n return res;\n })\n .catch(err => {\n console.error(err);\n });\n\n this.addTimeStamp('Compute PSD of speaker or mic');\n if (this.isCalibrating) return null;\n let filtered_mls_psd = await this.pyServerAPI\n .getMLSPSDWithRetry({\n mls: this.componentConvolution,\n sampleRate: this.sourceSamplingRate || 96000,\n })\n .then(res => {\n this.incrementStatusBar();\n this.status = this.generateTemplate(`All Hz Calibration: done computing the PSD graphs...`.toString()).toString();\n this.emit('update', {message: this.status});\n return res;\n })\n .catch(err => {\n console.error(err);\n });\n\n let filtered_no_bandpass_mls_psd = await this.pyServerAPI\n .getMLSPSDWithRetry({\n mls: this.componentConvolutionNoBandpass,\n sampleRate: this.sourceSamplingRate || 96000,\n })\n .then(res => {\n this.incrementStatusBar();\n this.status = this.generateTemplate(`All Hz Calibration: done computing the PSD graphs...`.toString()).toString();\n this.emit('update', {message: this.status});\n return res;\n })\n .catch(err => {\n console.error(err);\n });\n\n let gainValue = this.getGainDBSPL();\n iir_ir_and_plots = {\n unfiltered_recording: return_unconv_rec,\n filtered_recording: return_conv_rec,\n system: {\n iir: this.systemInvertedImpulseResponse,\n iir_no_bandpass: this.systemInvertedImpulseResponseNoBandpass,\n ir: this.systemIR,\n iir_psd: {\n y: system_iir_psd['y_conv'],\n x: system_iir_psd['y_conv'],\n y_no_bandpass: system_iir_psd['y_unconv'],\n x_no_bandpass: system_iir_psd['x_unconv'],\n },\n filtered_recording: [],\n filtered_mls_psd: {},\n filtered_no_bandpass_mls_psd: {},\n convolution: this.systemConvolution,\n convolutionNoBandpass: this.systemConvolutionNoBandpass,\n psd: {\n unconv: {\n x: [],\n y: [],\n },\n conv: {\n x: [],\n y: [],\n },\n },\n },\n component: {\n iir: this.componentInvertedImpulseResponse,\n iir_no_bandpass: this.componentInvertedImpulseResponseNoBandpass,\n ir: this.componentIR,\n ir_origin: this.componentIROrigin,\n ir_in_time_domain: this.componentIRInTimeDomain,\n iir_psd: {\n y: component_iir_psd['y_conv'],\n x: component_iir_psd['x_conv'],\n y_no_bandpass: component_iir_psd['y_unconv'],\n x_no_bandpass: component_iir_psd['x_unconv'],\n },\n filtered_mls_psd: {\n x: filtered_mls_psd['x_mls'],\n y: filtered_mls_psd['y_mls'],\n },\n filtered_no_bandpass_mls_psd: {\n x: filtered_no_bandpass_mls_psd['x_mls'],\n y: filtered_no_bandpass_mls_psd['y_mls'],\n },\n convolution: this.componentConvolution,\n convolutionNoBandpass: this.componentConvolutionNoBandpass,\n psd: {\n unconv: {\n x: unconv_results['x'],\n y: unconv_results['y'],\n },\n conv: {\n x: conv_results['x'],\n y: conv_results['y'],\n },\n },\n gainDBSPL: gainValue,\n },\n mls: this.#mlsBufferView,\n mls_psd: {\n x: mls_psd['x_mls'],\n y: mls_psd['y_mls'],\n },\n autocorrelations: this.autocorrelations,\n impulseResponses: [],\n };\n } else {\n this.addTimeStamp('Compute PSD of filtered recording (system) and unfiltered recording');\n if (this.isCalibrating) return null;\n let results = await this.pyServerAPI\n .getPSDWithRetry({\n unconv_rec,\n conv_rec,\n sampleRate: this.sourceSamplingRate || 96000,\n })\n .then(res => {\n let filtered_psd = res.y_conv\n .filter(\n (value, index) =>\n res.x_conv[index] >= this.#lowHz && res.x_conv[index] <= this.systemFMaxHz\n )\n .map(value => 10 * Math.log10(value));\n\n let mls_psd = res.y_unconv\n .filter(\n (value, index) =>\n res.x_unconv[index] >= this.#lowHz && res.x_conv[index] <= this.systemFMaxHz\n )\n .map(value => 10 * Math.log10(value));\n this.SDofFilteredRange['mls'] = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.standardDeviation)(mls_psd);\n this.SDofFilteredRange['system'] = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.standardDeviation)(filtered_psd);\n this.incrementStatusBar();\n this.status = this.generateTemplate(`All Hz Calibration: done computing the PSD graphs...`.toString()).toString();\n this.emit('update', {message: this.status});\n return res;\n })\n .catch(err => {\n console.error(err);\n });\n\n //iir w/ and without bandpass psd\n unconv_rec = this.componentInvertedImpulseResponseNoBandpass;\n conv_rec = this.componentInvertedImpulseResponse;\n this.addTimeStamp('Compute PSD of component IIR and component IIR no band pass');\n if (this.isCalibrating) return null;\n let component_iir_psd = await this.pyServerAPI\n .getPSDWithRetry({\n unconv_rec,\n conv_rec,\n sampleRate: this.sourceSamplingRate || 96000,\n })\n .then(res => {\n this.incrementStatusBar();\n this.status = this.generateTemplate(`All Hz Calibration: done computing the PSD graphs...`.toString()).toString();\n this.emit('update', {message: this.status});\n return res;\n })\n .catch(err => {\n console.error(err);\n });\n unconv_rec = this.systemInvertedImpulseResponseNoBandpass;\n conv_rec = this.systemInvertedImpulseResponse;\n this.addTimeStamp('Compute PSD of system IIR and system IIR no band pass');\n if (this.isCalibrating) return null;\n let system_iir_psd = await this.pyServerAPI\n .getPSDWithRetry({\n unconv_rec,\n conv_rec,\n sampleRate: this.sourceSamplingRate || 96000,\n })\n .then(res => {\n this.incrementStatusBar();\n this.status = this.generateTemplate( `All Hz Calibration: done computing the PSD graphs...`.toString()).toString();\n this.emit('update', {message: this.status});\n return res;\n })\n .catch(err => {\n console.error(err);\n });\n\n this.addTimeStamp('Compute PSD of MLS sequence');\n if (this.isCalibrating) return null;\n let mls_psd = await this.pyServerAPI\n .getMLSPSDWithRetry({\n mls: this.#mlsBufferView[this.icapture],\n sampleRate: this.sourceSamplingRate || 96000,\n })\n .then(res => {\n this.incrementStatusBar();\n this.status = this.generateTemplate(`All Hz Calibration: done computing the PSD graphs...`.toString()).toString();\n this.emit('update', {message: this.status});\n return res;\n })\n .catch(err => {\n console.error(err);\n });\n\n this.addTimeStamp('Compute PSD of filtered MLS (system)');\n if (this.isCalibrating) return null;\n let filtered_mls_psd = await this.pyServerAPI\n .getMLSPSDWithRetry({\n mls: this.systemConvolution,\n sampleRate: this.sourceSamplingRate || 96000,\n })\n .then(res => {\n this.incrementStatusBar();\n this.status = this.generateTemplate(`All Hz Calibration: done computing the PSD graphs...`.toString()).toString();\n this.emit('update', {message: this.status});\n return res;\n })\n .catch(err => {\n console.error(err);\n });\n\n let filtered_no_bandpass_mls_psd = await this.pyServerAPI\n .getMLSPSDWithRetry({\n mls: this.systemConvolutionNoBandpass,\n sampleRate: this.sourceSamplingRate || 96000,\n })\n .then(res => {\n this.incrementStatusBar();\n this.status = this.generateTemplate(`All Hz Calibration: done computing the PSD graphs...`.toString()).toString();\n this.emit('update', {message: this.status});\n return res;\n })\n .catch(err => {\n console.error(err);\n });\n\n let gainValue = this.getGainDBSPL();\n iir_ir_and_plots = {\n unfiltered_recording: return_unconv_rec,\n filtered_recording: return_conv_rec,\n system: {\n iir: this.systemInvertedImpulseResponse,\n iir_no_bandpass: this.systemInvertedImpulseResponseNoBandpass,\n ir: this.systemIR,\n iir_psd: {\n y: system_iir_psd['y_conv'],\n x: system_iir_psd['y_conv'],\n y_no_bandpass: system_iir_psd['y_unconv'],\n x_no_bandpass: system_iir_psd['x_unconv'],\n },\n filtered_recording: [],\n filtered_mls_psd: {\n x: filtered_mls_psd['x_mls'],\n y: filtered_mls_psd['y_mls'],\n },\n filtered_no_bandpass_mls_psd: {\n x: filtered_no_bandpass_mls_psd['x_mls'],\n y: filtered_no_bandpass_mls_psd['y_mls'],\n },\n convolution: this.systemConvolution,\n convolutionNoBandpass: this.systemConvolutionNoBandpass,\n psd: {\n unconv: {\n x: results['x_unconv'],\n y: results['y_unconv'],\n },\n conv: {\n x: results['x_conv'],\n y: results['y_conv'],\n },\n },\n },\n component: {\n iir: this.componentInvertedImpulseResponse,\n iir_no_bandpass: this.componentInvertedImpulseResponseNoBandpass,\n ir: this.componentIR,\n ir_origin: this.componentIROrigin,\n ir_in_time_domain: this.componentIRInTimeDomain,\n iir_psd: {\n y: component_iir_psd['y_conv'],\n x: component_iir_psd['x_conv'],\n y_no_bandpass: component_iir_psd['y_unconv'],\n x_no_bandpass: component_iir_psd['x_unconv'],\n },\n filtered_mls_psd: {},\n filtered_no_bandpass_mls_psd: {},\n convolution: this.componentConvolution,\n convolutionNoBandpass: this.componentConvolutionNoBandpass,\n psd: {\n unconv: {\n x: [],\n y: [],\n },\n conv: {\n x: [],\n y: [],\n },\n },\n gainDBSPL: gainValue,\n },\n mls: this.#mlsBufferView,\n mls_psd: {\n x: mls_psd['x_mls'],\n y: mls_psd['y_mls'],\n },\n autocorrelations: this.autocorrelations,\n impulseResponses: [],\n };\n }\n if (this.isCalibrating) return null;\n await Promise.all(this.impulseResponses).then(res => {\n for (let i = 0; i < res.length; i++) {\n if (res[i] != undefined) {\n iir_ir_and_plots['impulseResponses'].push(res[i]);\n }\n }\n });\n\n if (this.#download) {\n this.downloadSingleUnfilteredRecording();\n this.downloadSingleFilteredRecording();\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.#mls, 'MLS.csv');\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.componentConvolution, 'python_component_convolution_mls_iir.csv');\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.systemConvolution, 'python_system_convolution_mls_iir.csv');\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.componentInvertedImpulseResponse, 'componentIIR.csv');\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.systemInvertedImpulseResponse, 'systemIIR.csv');\n for (let i = 0; i < this.autocorrelations.length; i++) {\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.autocorrelations[i], `autocorrelation_${i}`);\n }\n const computedIRagain = await Promise.all(this.impulseResponses).then(res => {\n for (let i = 0; i < res.length; i++) {\n if (res[i] != undefined) {\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(res[i], `IR_${i}`);\n }\n }\n });\n }\n\n return iir_ir_and_plots;\n };\n\n /**\n * Public method to start the calibration process. Objects intialized from webassembly allocate new memory\n * and must be manually freed. This function is responsible for intializing the MlsGenInterface,\n * and wrapping the calibration steps with a garbage collection safe gaurd.\n *\n * @public\n * @param stream - The stream of audio from the Listener.\n * @example\n */\n startCalibrationImpulseResponse = async stream => {\n console.log('JS used memory:', performance.memory.usedJSHeapSize / 1024 / 1024, 'mb');\n let desired_time = this.desired_time_per_mls;\n let checkRec = 'allhz';\n\n console.log('MLS sequence should be of length: ' + this.sourceSamplingRate * desired_time);\n\n length = this.sourceSamplingRate * desired_time;\n //get mls here\n // const calibrateSoundBurstDb = Math.pow(10, this._calibrateSoundBurstDb / 20);\n\n this.power_dB = 0;\n\n if (!this._calibrateSoundBurstLevelReTBool) {\n this.power_dB = this._calibrateSoundBurstDb;\n } else {\n this.power_dB = this._calibrateSoundBurstDb + (this.T - this.gainDBSPL);\n }\n\n const amplitude = Math.pow(10, this.power_dB / 20);\n //MLSpower = Math.pow(10,this.power_dB/20);\n this.addTimeStamp('Compute MLS sequence');\n if (this.isCalibrating) return null;\n await this.pyServerAPI\n .getMLSWithRetry({\n length,\n amplitude,\n calibrateSoundBurstMLSVersions: this.numCaptures,\n })\n .then(res => {\n console.log(res);\n this.#mlsBufferView = res['mls'];\n this.#mls = res['unscaledMLS'];\n })\n .catch(err => {\n // this.emit('InvertedImpulseResponse', {res: false});\n console.error(err);\n });\n this.numSuccessfulBackgroundCaptured = 0;\n if (this._calibrateSoundBackgroundSecs > 0) {\n this.mode = 'background';\n this.status = this.generateTemplate(`All Hz Calibration: sampling the background noise...`.toString()).toString();\n this.emit('update', {message: this.status});\n if (this.isCalibrating) return null;\n await this.recordBackground(\n stream, //stream\n () => this.numSuccessfulBackgroundCaptured < 1, //loop condition\n this.#awaitBackgroundNoiseRecording, //sleep to record\n this.sendBackgroundRecording, //send to get PSD\n this.mode,\n checkRec\n );\n this.incrementStatusBar();\n }\n this.mode = 'unfiltered';\n this.numSuccessfulCaptured = 0;\n\n if (this.isCalibrating) return null;\n for (var i = 0; i < this.numCaptures; i++) {\n this.icapture = i;\n await this.calibrationSteps(\n stream,\n this.#playCalibrationAudio, // play audio func (required)\n this.#createCalibrationNodeFromBuffer(this.#mlsBufferView[this.icapture]), // before play func\n this.#awaitSignalOnset, // before record\n () => this.numSuccessfulCaptured < 2, // loop while true\n this.#awaitDesiredMLSLength, // during record\n this.#afterMLSRecord, // after record\n this.mode,\n checkRec\n );\n this.stopCalibrationAudio();\n }\n checkRec = false;\n\n // at this stage we've captured all the required signals,\n // and have received IRs for each one\n // so let's send all the IRs to the server to be converted to a single IIR\n if (this.isCalibrating) return null;\n await this.sendSystemImpulseResponsesToServerForProcessing();\n await this.pyServerAPI.checkMemory();\n if (this.isCalibrating) return null;\n await this.sendComponentImpulseResponsesToServerForProcessing();\n\n this.numSuccessfulCaptured = 0;\n\n let iir_ir_and_plots;\n if (this._calibrateSoundCheck != 'none') {\n //do single check\n if (this._calibrateSoundCheck == 'goal' || this._calibrateSoundCheck == 'system') {\n if (this.isCalibrating) return null;\n iir_ir_and_plots = await this.singleSoundCheck(stream);\n if (this.isCalibrating) return null;\n } else {\n //both\n if (this.isCalibrating) return null;\n iir_ir_and_plots = await this.bothSoundCheck(stream);\n if (this.isCalibrating) return null;\n }\n } else {\n let unconv_rec = this.componentInvertedImpulseResponseNoBandpass;\n let conv_rec = this.componentInvertedImpulseResponse;\n if (this.isCalibrating) return null;\n let component_iir_psd = await this.pyServerAPI\n .getPSDWithRetry({\n unconv_rec,\n conv_rec,\n sampleRate: this.sourceSamplingRate || 96000,\n })\n .then(res => {\n this.incrementStatusBar();\n this.status = this.generateTemplate(`All Hz Calibration: done computing the PSD graphs...`.toString()).toString();\n this.emit('update', {message: this.status});\n return res;\n })\n .catch(err => {\n console.error(err);\n });\n unconv_rec = this.systemInvertedImpulseResponseNoBandpass;\n conv_rec = this.systemInvertedImpulseResponse;\n if (this.isCalibrating) return null;\n let system_iir_psd = await this.pyServerAPI\n .getPSDWithRetry({\n unconv_rec,\n conv_rec,\n sampleRate: this.sourceSamplingRate || 96000,\n })\n .then(res => {\n this.incrementStatusBar();\n this.status = this.generateTemplate(`All Hz Calibration: done computing the PSD graphs...`.toString()).toString();\n this.emit('update', {message: this.status});\n return res;\n })\n .catch(err => {\n console.error(err);\n });\n\n let gainValue = this.getGainDBSPL();\n iir_ir_and_plots = {\n unfiltered_recording: return_unconv_rec,\n filtered_recording: return_conv_rec,\n system: {\n iir: this.systemInvertedImpulseResponse,\n iir_no_bandpass: this.systemInvertedImpulseResponseNoBandpass,\n ir: this.systemIR,\n iir_psd: {\n y: system_iir_psd['y_conv'],\n x: system_iir_psd['y_conv'],\n y_no_bandpass: system_iir_psd['y_unconv'],\n x_no_bandpass: system_iir_psd['x_unconv'],\n },\n filtered_recording: [],\n convolution: this.systemConvolution,\n convolutionNoBandpass: this.systemConvolutionNoBandpass,\n psd: {\n unconv: {\n x: [],\n y: [],\n },\n conv: {\n x: [],\n y: [],\n },\n },\n },\n component: {\n iir: this.componentInvertedImpulseResponse,\n iir_no_bandpass: this.componentInvertedImpulseResponseNoBandpass,\n ir: this.componentIR,\n ir_in_time_domain: this.componentIRInTimeDomain,\n iir_psd: {\n y: component_iir_psd['y_conv'],\n x: component_iir_psd['x_conv'],\n y_no_bandpass: component_iir_psd['y_unconv'],\n x_no_bandpass: component_iir_psd['x_unconv'],\n },\n convolution: this.componentConvolution,\n convolutionNoBandpass: this.componentConvolutionNoBandpass,\n psd: {\n unconv: {\n x: [],\n y: [],\n },\n conv: {\n x: [],\n y: [],\n },\n },\n gainDBSPL: gainValue,\n },\n mls: this.#mlsBufferView,\n autocorrelations: this.autocorrelations,\n impulseResponses: [],\n };\n if (this.isCalibrating) return null;\n await Promise.all(this.impulseResponses).then(res => {\n for (let i = 0; i < res.length; i++) {\n if (res[i] != undefined) {\n iir_ir_and_plots['impulseResponses'].push(res[i]);\n }\n }\n });\n\n if (this.#download) {\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.#mls, 'MLS.csv');\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.componentConvolution, 'python_component_convolution_mls_iir.csv');\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.systemConvolution, 'python_system_convolution_mls_iir.csv');\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.componentInvertedImpulseResponse, 'componentIIR.csv');\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.systemInvertedImpulseResponse, 'systemIIR.csv');\n for (let i = 0; i < this.autocorrelations.length; i++) {\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(this.autocorrelations[i], `autocorrelation_${i}`);\n }\n const computedIRagain = await Promise.all(this.impulseResponses).then(res => {\n for (let i = 0; i < res.length; i++) {\n if (res[i] != undefined) {\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.saveToCSV)(res[i], `IR_${i}`);\n }\n }\n });\n }\n }\n if (this.isCalibrating) return null;\n this.percent_complete = 100;\n this.status = this.generateTemplate(`All Hz Calibration: Finished`.toString()).toString();\n this.addTimeStamp('Done');\n this.emit('update', {message: this.status});\n\n //here after calibration we have the component calibration (either loudspeaker or microphone) in the same form as the componentIR\n //that was used to calibrate\n // saveToJSON(iir_ir_and_plots);\n return iir_ir_and_plots;\n };\n\n //////////////////////volume\n\n handleIncomingData = data => {\n console.log('Received data: ', data);\n if (data.type === 'soundGainDBSPL') {\n this.soundGainDBSPL = data.value;\n } else {\n throw new Error(`Unknown data type: ${data.type}`);\n }\n };\n\n #getTruncatedSignal = (left = 3.5, right = 4.5) => {\n const start = Math.floor(left * this.sourceSamplingRate);\n const end = Math.floor(right * this.sourceSamplingRate);\n const result = Array.from(this.getLastVolumeRecordedSignal().slice(start, end));\n console.log(\n 'Obtaining last 1000 hz recording from #allVolumeRecordings to send for processing'\n );\n /**\n * function to check that capture was properly made\n * @param {*} list\n */\n const checkResult = list => {\n const setItem = new Set(list);\n if (setItem.size === 1 && setItem.has(0)) {\n console.warn(\n 'The last capture failed, all recorded signal is zero',\n this.getAllVolumeRecordedSignals()\n );\n this.stopCalibrationAudio();\n this.isCalibrating = true;\n // restartButton.style.display = 'none';\n this.emit('update', {message: 'Connection failed, hit restart button to reconnect'});\n }\n if (setItem.size === 0) {\n console.warn('The last capture failed, no recorded signal');\n this.stopCalibrationAudio();\n this.isCalibrating = true;\n // restartButton.style.display = 'none';\n this.emit('update', {message: 'Connection failed, hit restart button to reconnect'});\n }\n };\n checkResult(result);\n return result;\n };\n\n /** \n * \n * \n Construct a calibration Node with the calibration parameters and given gain value\n * @param {*} gainValue\n * */\n #createCalibrationToneWithGainValue = gainValue => {\n const audioContext = this.makeNewSourceAudioContext();\n const oscilator = audioContext.createOscillator();\n const gainNode = audioContext.createGain();\n const taperGainNode = audioContext.createGain();\n const offsetGainNode = audioContext.createGain();\n const totalDuration = this.CALIBRATION_TONE_DURATION * 1.2;\n\n oscilator.frequency.value = this.#CALIBRATION_TONE_FREQUENCY;\n oscilator.type = this.#CALIBRATION_TONE_TYPE;\n gainNode.gain.value = gainValue;\n\n oscilator.connect(gainNode);\n gainNode.connect(taperGainNode);\n const onsetCurve = this.createSCurveBuffer();\n taperGainNode.gain.setValueCurveAtTime(onsetCurve, 0, this.TAPER_SECS);\n taperGainNode.connect(offsetGainNode);\n const offsetCurve = this.createSCurveBuffer(false);\n offsetGainNode.gain.setValueCurveAtTime(\n offsetCurve,\n totalDuration - this.TAPER_SECS,\n this.TAPER_SECS\n );\n offsetGainNode.connect(audioContext.destination);\n\n const gainValuesOverTime = [];\n const sampleRate = this.#CALIBRATION_TONE_FREQUENCY; // Number of samples per second\n const interval = 1 / sampleRate; // Time between samples\n\n for (let t = 0; t <= totalDuration; t += interval) {\n let gainValueAtTime = gainValue;\n\n // Apply the onset curve\n if (t < this.TAPER_SECS) {\n const onsetIndex = Math.floor((t / this.TAPER_SECS) * onsetCurve.length);\n gainValueAtTime *= onsetCurve[onsetIndex];\n }\n\n // Apply the offset curve\n if (t > totalDuration - this.TAPER_SECS) {\n const offsetTime = t - (totalDuration - this.TAPER_SECS);\n const offsetIndex = Math.floor((offsetTime / this.TAPER_SECS) * offsetCurve.length);\n gainValueAtTime *= offsetCurve[offsetIndex];\n }\n\n gainValuesOverTime.push(gainValueAtTime);\n }\n\n this.waveforms['volume'][this.inDB] = gainValuesOverTime;\n\n this.addCalibrationNode(oscilator);\n };\n\n /**\n * Construct a Calibration Node with the calibration parameters.\n *\n * @private\n * @example\n */\n #createCalibrationNode = () => {\n const audioContext = this.makeNewSourceAudioContext();\n const oscilator = audioContext.createOscillator();\n const gainNode = audioContext.createGain();\n\n oscilator.frequency.value = this.#CALIBRATION_TONE_FREQUENCY;\n oscilator.type = this.#CALIBRATION_TONE_TYPE;\n gainNode.gain.value = 0.04;\n\n oscilator.connect(gainNode);\n gainNode.connect(audioContext.destination);\n\n this.addCalibrationNode(oscilator);\n };\n\n #playCalibrationAudioVolume = async () => {\n if (this.numCalibratingRoundsCompleted==1) {\n this.recordingChecks['warnings'].push(`1000Hz. Re-recorded ${this.inDB} dB because SD ${(this.recordingChecks['volume'][this.inDB]['sd'])} > ${this.calibrateSound1000HzMaxSD_dB} dB`);\n const currentStatus = `1000 Hz: Re-recording at ${this.inDB} dB because SD \n ${this.recordingChecks['volume'][this.inDB]['sd']} > \n ${this.calibrateSound1000HzMaxSD_dB} dB`.toString();\n this.status = this.generateTemplate(currentStatus).toString();\n this.emit('update', {\n message: this.status,\n });\n }\n const totalDuration = this.CALIBRATION_TONE_DURATION * 1.2;\n\n this.calibrationNodes[0].start(0);\n this.calibrationNodes[0].stop(totalDuration);\n console.log(`Playing a buffer of ${this.CALIBRATION_TONE_DURATION} seconds of audio`);\n console.log(`Waiting a total of ${totalDuration} seconds`);\n await (0,_utils__WEBPACK_IMPORTED_MODULE_1__.sleep)(totalDuration);\n };\n\n stopCalibrationAudioVolume = () => {\n if (this.calibrationNodes.length > 0) {\n this.calibrationNodes[0].stop();\n }\n };\n\n #sendToServerForProcessing = async lCalib => {\n console.log('Sending data to server');\n \n let left = this.calibrateSound1000HzPreSec;\n let right = this.calibrateSound1000HzPreSec + this.calibrateSound1000HzSec;\n if (this.isCalibrating) return null;\n this.pyServerAPI\n .getVolumeCalibration({\n sampleRate: this.sourceSamplingRate,\n payload: this.#getTruncatedSignal(left, right),\n lCalib: lCalib,\n })\n .then(res => {\n if (this.outDBSPL === null) {\n this.incrementStatusBar();\n this.outDBSPL = res['outDbSPL'];\n this.outDBSPL1000 = res['outDbSPL1000'];\n this.THD = res['thd'];\n }\n })\n .catch(err => {\n console.warn(err);\n });\n const rec = this.getLastVolumeRecordedSignal();\n console.log('pre period power: ', (0,_powerCheck__WEBPACK_IMPORTED_MODULE_2__.getPower)(rec.slice(0,this.calibrateSound1000HzPreSec * this.sourceSamplingRate)).toFixed(1));\n console.log('pre period power: ', \n (0,_powerCheck__WEBPACK_IMPORTED_MODULE_2__.getPower)(rec.slice(\n this.calibrateSound1000HzPreSec * this.sourceSamplingRate,\n (this.calibrateSound1000HzPreSec + this.calibrateSound1000HzSec)* this.sourceSamplingRate)).toFixed(1));\n console.log('pre period power: ', (0,_powerCheck__WEBPACK_IMPORTED_MODULE_2__.getPower)(rec.slice(\n (this.calibrateSound1000HzPreSec + this.calibrateSound1000HzSec)* this.sourceSamplingRate)).toFixed(1));\n const res = (0,_powerCheck__WEBPACK_IMPORTED_MODULE_2__.volumePowerCheck)(\n rec,\n this.sourceSamplingRate || 96000,\n this.calibrateSound1000HzPreSec,\n this.calibrateSound1000HzSec, \n this._calibrateSoundPowerBinDesiredSec);\n console.log(res);\n this.recordingChecks['volume'][this.inDB] = res;\n console.log(\"Recording checks in sendToServer\", this.recordingChecks['volume']);\n const getSD = () => this.recordingChecks['volume'][this.inDB]['sd'];\n const getSDMessage = () => {\n //SOUND 6.7 s. 2.5+2.5+0.5 s. 1000 Hz at -60 dB. SD = 1.3 dB\n // And reporting each rejected recording as\n // SOUND 6.7 s. 2.5+2.5+0.5 s. 1000 Hz at -60 dB, SD = 19.7 > 4 dB.\n\n if(this.numCalibratingRoundsCompleted==1)\n return `, SD = ${getSD()} > ${this.calibrateSound1000HzMaxSD_dB} dB.`;\n return `. SD = ${getSD()} dB`\n }\n const total_dur = this.calibrateSound1000HzPreSec + this.calibrateSound1000HzSec + this.calibrateSound1000HzPostSec;\n \n this.addTimeStamp(\n `${this.calibrateSound1000HzPreSec.toFixed(1)}` + \n `+ ${this.calibrateSound1000HzSec.toFixed(1)}` + \n `+ ${this.calibrateSound1000HzPostSec.toFixed(1)} s.` + \n `1000 Hz at ${this.inDB} dB${getSDMessage()}`\n );\n \n \n };\n\n startCalibrationVolume = async (stream, gainValues, lCalib, componentGainDBSPL) => {\n if (this.isCalibrating) return null;\n const trialIterations = gainValues.length;\n this.status_denominator += trialIterations;\n const thdValues = [];\n const inDBValues = [];\n let inDB = 0;\n const outDBSPLValues = [];\n const outDBSPL1000Values = [];\n let checkRec = 'loudest';\n // do one calibration that will be discarded\n const soundLevelToDiscard = -60;\n const gainToDiscard = Math.pow(10, soundLevelToDiscard / 20);\n this.inDB = soundLevelToDiscard;\n this.status = this.generateTemplate(`1000 Hz Calibration: Sound Level ${soundLevelToDiscard} dB`.toString()).toString();\n //this.emit('update', {message: `1000 Hz Calibration: Sound Level ${soundLevelToDiscard} dB`});\n this.emit('update', {message: this.status});\n this.startTime = new Date().getTime();\n\n do {\n console.log('while loop');\n if (this.isCalibrating) {\n console.log('restart calibration');\n return null;\n }\n // eslint-disable-next-line no-await-in-loop\n await this.volumeCalibrationSteps(\n stream,\n this.#playCalibrationAudioVolume,\n this.#createCalibrationToneWithGainValue,\n this.#sendToServerForProcessing,\n gainToDiscard,\n lCalib, //todo make this a class parameter\n checkRec,\n () => {return this.recordingChecks['volume'][this.inDB]['sd']},\n this.calibrateSound1000HzMaxSD_dB\n );\n } while (this.outDBSPL === null);\n //reset the values\n //this.incrementStatusBar();\n\n this.outDBSPL = null;\n this.outDBSPL = null;\n this.outDBSPL1000 = null;\n this.THD = null;\n\n // run the calibration at different gain values provided by the user\n for (let i = 0; i < trialIterations; i++) {\n //convert gain to DB and add to inDB\n if (i == trialIterations - 1) {\n checkRec = 'loudest';\n }\n inDB = Math.log10(gainValues[i]) * 20;\n // precision to 1 decimal place\n inDB = Math.round(inDB * 10) / 10;\n this.inDB = inDB;\n inDBValues.push(inDB);\n console.log('next update');\n this.status = this.generateTemplate(`1000 Hz Calibration: Sound Level ${inDB} dB`.toString()).toString();\n this.emit('update', {message: this.status});\n do {\n if (this.isCalibrating) {\n console.log('restart calibration');\n return null;\n }\n // eslint-disable-next-line no-await-in-loop\n await this.volumeCalibrationSteps(\n stream,\n this.#playCalibrationAudioVolume,\n this.#createCalibrationToneWithGainValue,\n this.#sendToServerForProcessing,\n gainValues[i],\n lCalib, //todo make this a class parameter\n checkRec,\n () => {return this.recordingChecks?.['volume']?.[this.inDB]?.['sd'] || 0},\n this.calibrateSound1000HzMaxSD_dB\n );\n } while (this.outDBSPL === null);\n outDBSPL1000Values.push(this.outDBSPL1000);\n thdValues.push(this.THD);\n outDBSPLValues.push(this.outDBSPL);\n\n this.outDBSPL = null;\n this.outDBSPL1000 = null;\n this.THD = null;\n }\n if (this.isCalibrating) return null;\n // get the volume calibration parameters from the server\n this.addTimeStamp('Compute sound calibration parameters');\n\n const parameters = await this.pyServerAPI\n .getVolumeCalibrationParameters({\n inDBValues: inDBValues,\n outDBSPLValues: outDBSPL1000Values,\n lCalib: lCalib,\n componentGainDBSPL,\n })\n .then(res => {\n this.incrementStatusBar();\n return res;\n });\n if (this.isCalibrating) return null;\n const result = {\n parameters: parameters,\n inDBValues: inDBValues,\n outDBSPLValues: outDBSPLValues,\n outDBSPL1000Values: outDBSPL1000Values,\n thdValues: thdValues,\n };\n\n return result;\n };\n\n writeFrqGainToFirestore = async (speakerID, frq, gain, OEM, documentID) => {\n // freq and gain are too large to take samples 1 in every 100 samples\n // const sampledFrq = [];\n // const sampledGain = [];\n // for (let i = 0; i < frq.length; i += 100) {\n // sampledFrq.push(frq[i]);\n // sampledGain.push(gain[i]);\n // }\n\n const data = {Freq: frq, Gain: gain};\n\n const docRef = (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_5__.doc)(_config_firebase__WEBPACK_IMPORTED_MODULE_3__[\"default\"], 'Microphones', documentID);\n await (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_5__.updateDoc)(docRef, {\n linear: data,\n });\n\n // divide frq and gain into smaller chunks and write to firestore one chunk at a time\n // use arrayUnion to append to the array\n // const chunkSize = 600;\n // const chunkedFrq = [];\n // const chunkedGain = [];\n // for (let i = 0; i < frq.length; i += chunkSize) {\n // chunkedFrq.push(frq.slice(i, i + chunkSize));\n // chunkedGain.push(gain.slice(i, i + chunkSize));\n // }\n // const docRef = doc(database, 'Microphones', documentID);\n // for (let i = 0; i < chunkedFrq.length; i++) {\n // await updateDoc(docRef, {\n // linear: {\n // Freq: arrayUnion(...chunkedFrq[i]),\n // Gain: arrayUnion(...chunkedGain[i]),\n // },\n // });\n // }\n };\n // function to write frq and gain to firebase database given speakerID\n writeFrqGain = async (speakerID, frq, gain, OEM) => {\n // freq and gain are too large to take samples 1 in every 100 samples\n\n const sampledFrq = [];\n const sampledGain = [];\n for (let i = 0; i < frq.length; i += 100) {\n sampledFrq.push(frq[i]);\n sampledGain.push(gain[i]);\n }\n\n const data = {Freq: sampledFrq, Gain: sampledGain};\n\n await (0,firebase_database__WEBPACK_IMPORTED_MODULE_4__.set)((0,firebase_database__WEBPACK_IMPORTED_MODULE_4__.ref)(_config_firebase__WEBPACK_IMPORTED_MODULE_3__[\"default\"], `Microphone2/${OEM}/${speakerID}/linear`), data);\n };\n\n // Function to Read frq and gain from firebase database given speakerID\n // returns an array of frq and gain if speakerID exists, returns null otherwise\n readFrqGainFromFirestore = async (speakerID, OEM, isDefault) => {\n const collectionRef = (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_5__.collection)(_config_firebase__WEBPACK_IMPORTED_MODULE_3__[\"default\"], 'Microphones');\n const q = (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_5__.query)(\n collectionRef,\n (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_5__.where)('ID', '==', speakerID),\n (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_5__.where)('lowercaseOEM', '==', OEM),\n (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_5__.where)('isDefault', '==', isDefault)\n );\n const querySnapshot = await (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_5__.getDocs)(q);\n // if exists return the linear field of the first document\n if (querySnapshot.size > 0) {\n const timestamp = new firebase_firestore__WEBPACK_IMPORTED_MODULE_5__.Timestamp(\n querySnapshot.docs[0].data().createDate.seconds,\n querySnapshot.docs[0].data().createDate.nanoseconds\n );\n return {\n ir: querySnapshot.docs[0].data().linear,\n createDate: timestamp.toDate(),\n jsonFileName: querySnapshot.docs[0].data().jsonFileName,\n };\n }\n return null;\n };\n readFrqGain = async (speakerID, OEM) => {\n const dbRef = (0,firebase_database__WEBPACK_IMPORTED_MODULE_4__.ref)(_config_firebase__WEBPACK_IMPORTED_MODULE_3__[\"default\"]);\n const snapshot = await (0,firebase_database__WEBPACK_IMPORTED_MODULE_4__.get)((0,firebase_database__WEBPACK_IMPORTED_MODULE_4__.child)(dbRef, `Microphone2/${OEM}/${speakerID}/linear`));\n if (snapshot.exists()) {\n return snapshot.val();\n }\n return null;\n };\n readGainat1000HzFromFirestore = async (speakerID, OEM, isDefault) => {\n const collectionRef = (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_5__.collection)(_config_firebase__WEBPACK_IMPORTED_MODULE_3__[\"default\"], 'Microphones');\n const q = (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_5__.query)(\n collectionRef,\n (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_5__.where)('ID', '==', speakerID),\n (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_5__.where)('lowercaseOEM', '==', OEM),\n (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_5__.where)('isDefault', '==', isDefault)\n );\n const querySnapshot = await (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_5__.getDocs)(q);\n // if exists return the Gain1000 field of the first document\n if (querySnapshot.size > 0) {\n return querySnapshot.docs[0].data().Gain1000;\n }\n return null;\n };\n\n readGainat1000Hz = async (speakerID, OEM) => {\n const dbRef = (0,firebase_database__WEBPACK_IMPORTED_MODULE_4__.ref)(_config_firebase__WEBPACK_IMPORTED_MODULE_3__[\"default\"]);\n const snapshot = await (0,firebase_database__WEBPACK_IMPORTED_MODULE_4__.get)((0,firebase_database__WEBPACK_IMPORTED_MODULE_4__.child)(dbRef, `Microphone2/${OEM}/${speakerID}/Gain1000`));\n if (snapshot.exists()) {\n return snapshot.val();\n }\n return null;\n };\n\n writeGainat1000HzToFirestore = async (speakerID, gain, OEM, documentID) => {\n const docRef = (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_5__.doc)(_config_firebase__WEBPACK_IMPORTED_MODULE_3__[\"default\"], 'Microphones', documentID);\n await (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_5__.updateDoc)(docRef, {\n Gain1000: gain,\n });\n };\n\n writeGainat1000Hz = async (speakerID, gain, OEM) => {\n await (0,firebase_database__WEBPACK_IMPORTED_MODULE_4__.set)((0,firebase_database__WEBPACK_IMPORTED_MODULE_4__.ref)(_config_firebase__WEBPACK_IMPORTED_MODULE_3__[\"default\"], `Microphone2/${OEM}/${speakerID}/Gain1000`), gain);\n };\n\n writeIsSmartPhoneToFirestore = async (speakerID, isSmartPhone, OEM) => {\n const collectionRef = (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_5__.collection)(_config_firebase__WEBPACK_IMPORTED_MODULE_3__[\"default\"], 'Microphones');\n const q = (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_5__.query)(\n collectionRef,\n (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_5__.where)('ID', '==', speakerID),\n (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_5__.where)('lowercaseOEM', '==', OEM),\n (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_5__.where)('isDefault', '==', true)\n );\n const querySnapshot = await (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_5__.getDocs)(q);\n if (querySnapshot.size > 0) {\n const docRef = await (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_5__.addDoc)(collectionRef, {isSmartPhone: isSmartPhone, isDefault: false});\n return docRef.id;\n } else {\n const docRef = await (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_5__.addDoc)(collectionRef, {isSmartPhone: isSmartPhone, isDefault: true});\n return docRef.id;\n }\n };\n\n writeIsSmartPhone = async (speakerID, isSmartPhone, OEM) => {\n const data = {isSmartPhone: isSmartPhone};\n await (0,firebase_database__WEBPACK_IMPORTED_MODULE_4__.set)((0,firebase_database__WEBPACK_IMPORTED_MODULE_4__.ref)(_config_firebase__WEBPACK_IMPORTED_MODULE_3__[\"default\"], `Microphone2/${OEM}/${speakerID}/isSmartPhone`), isSmartPhone);\n };\n\n writeMicrophoneInfoToFirestore = async (speakerID, micInfo, OEM, documentID) => {\n const docRef = (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_5__.doc)(_config_firebase__WEBPACK_IMPORTED_MODULE_3__[\"default\"], 'Microphones', documentID);\n await (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_5__.setDoc)(docRef, micInfo, {merge: true});\n };\n\n doesMicrophoneExistInFirestore = async (speakerID, OEM, documentID) => {\n const docRef = (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_5__.doc)(_config_firebase__WEBPACK_IMPORTED_MODULE_3__[\"default\"], 'Microphone', OEM, speakerID, documentID);\n const docSnap = await (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_5__.getDoc)(docRef);\n if (docSnap.exists()) {\n return true;\n }\n return false;\n };\n\n doesMicrophoneExist = async (speakerID, OEM) => {\n const dbRef = (0,firebase_database__WEBPACK_IMPORTED_MODULE_4__.ref)(_config_firebase__WEBPACK_IMPORTED_MODULE_3__[\"default\"]);\n const snapshot = await (0,firebase_database__WEBPACK_IMPORTED_MODULE_4__.get)((0,firebase_database__WEBPACK_IMPORTED_MODULE_4__.child)(dbRef, `Microphone2/${OEM}/${speakerID}`));\n if (snapshot.exists()) {\n return true;\n }\n return false;\n };\n\n addMicrophoneInfo = async (speakerID, OEM, micInfo) => {\n // add to database if /info does not exist\n const dbRef = (0,firebase_database__WEBPACK_IMPORTED_MODULE_4__.ref)(_config_firebase__WEBPACK_IMPORTED_MODULE_3__[\"default\"]);\n const snapshot = await (0,firebase_database__WEBPACK_IMPORTED_MODULE_4__.get)((0,firebase_database__WEBPACK_IMPORTED_MODULE_4__.child)(dbRef, `Microphone2/${OEM}/${speakerID}/info`));\n if (!snapshot.exists()) {\n await (0,firebase_database__WEBPACK_IMPORTED_MODULE_4__.set)((0,firebase_database__WEBPACK_IMPORTED_MODULE_4__.ref)(_config_firebase__WEBPACK_IMPORTED_MODULE_3__[\"default\"], `Microphone2/${OEM}/${speakerID}/info`), micInfo);\n }\n };\n\n convertToDB = gain => {\n return Math.log10(gain) * 20;\n };\n\n // Function to perform linear interpolation between two points\n interpolate(x, x0, y0, x1, y1) {\n return y0 + ((x - x0) * (y1 - y0)) / (x1 - x0);\n }\n\n findGainatFrequency = (frequencies, gains, targetFrequency) => {\n // Find the index of the first frequency in the array greater than the target frequency\n let index = 0;\n while (index < frequencies.length && frequencies[index] < targetFrequency) {\n index++;\n }\n\n // Handle cases when the target frequency is outside the range of the given data\n if (index === 0) {\n return gains[0];\n } else if (index === frequencies.length) {\n return gains[gains.length - 1];\n } else {\n // Interpolate the gain based on the surrounding frequencies\n const x0 = frequencies[index - 1];\n const y0 = gains[index - 1];\n const x1 = frequencies[index];\n const y1 = gains[index];\n return this.interpolate(targetFrequency, x0, y0, x1, y1);\n }\n };\n\n checkPowerVariation = async () => {\n let recordings = this.getAllFilteredRecordedSignals();\n // remove filteredMLSAttenuation from the recordings\n\n // recordings = recordings.map(recording => {\n // if (this.soundCheck == 'component') {\n // return recording.map(value => value / this.filteredMLSAttenuation.component);\n // }\n // return recording.map(value => value / this.filteredMLSAttenuation.system);\n // });\n\n const rec = recordings[recordings.length - 1];\n\n await this.pyServerAPI\n .allHzPowerCheck({\n payload: rec,\n sampleRate: this.sourceSamplingRate || 96000,\n binDesiredSec: this._calibrateSoundPowerBinDesiredSec,\n burstSec: this.desired_time_per_mls,\n repeats: this.numMLSPerCapture - this.num_mls_to_skip ,\n warmUp: this.num_mls_to_skip \n })\n .then(result => {\n if (result) {\n if (result['sd'] > this._calibrateSoundBurstMaxSD_dB && this.numSuccessfulCaptured == 0) {\n console.log('filtered recording sd too high');\n this.recordingChecks['warnings'].push(`All Hz. Re-recorded ${this.inDB} because SD ${result['sd']} > ${this._calibrateSoundBurstMaxSD_dB} dB`);\n this.status = this.generateTemplate(`All Hz: Re-recording at ${this.inDB} dB because SD ${result['sd']} > ${this._calibrateSoundBurstMaxSD_dB} dB`.toString()).toString();\n this.emit('update', {\n message: this.status,\n });\n // numSuccessfulCaptured no longer to count number of successful capture but count attemps\n // is sd below _calibrateSoundBurstMaxSD_dB then count two attemps\n // else count one attemp\n this.numSuccessfulCaptured += 1;\n } else {\n this.recordingChecks[this.soundCheck].push(result);\n // Now we do at most 2 attempts if sd > _calibrateSoundBurstMaxSD_dB\n // Second attempt is the final\n if (this.numSuccessfulCaptured < 2) {\n this.numSuccessfulCaptured += 2;\n this.stepNum += 1;\n this.incrementStatusBar();\n console.log(\n 'after mls w iir record for some reason add numSucc capt ' + this.stepNum\n );\n this.status = this.generateTemplate(`All Hz Calibration: ${this.numSuccessfulCaptured} recording of convolved MLS captured`.toString()).toString();\n this.emit('update', {\n message: this.status,\n });\n }\n }\n }\n });\n };\n\n getGainDBSPL = () => {\n var freqIndex = this.componentIR.Freq.indexOf(1000);\n\n // If freqIndex is not -1 (meaning 1000 is found in the freq array)\n if (freqIndex !== -1) {\n // Get the corresponding gain value using the index\n var gainValue = this.componentIR.Gain[freqIndex];\n return gainValue;\n } else {\n console.log('Freq 1000 not found in the array.');\n return null;\n }\n };\n // Example of how to use the writeFrqGain and readFrqGain functions\n // writeFrqGain('speaker1', [1, 2, 3], [4, 5, 6]);\n // Speaker1 is the speakerID you want to write to in the database\n // readFrqGain('MiniDSPUMIK_1').then(data => console.log(data));\n // MiniDSPUMIK_1 is the speakerID with some Data in the database\n //adding gainDBSPL\n startCalibration = async (\n stream,\n gainValues,\n lCalib = 104.92978421490648,\n componentIR = null,\n microphoneName = 'MiniDSP-UMIK1-711-4754-vertical',\n _calibrateSoundCheck = 'goal', //GOAL PASSed in by default\n isSmartPhone = false,\n _calibrateSoundBurstDb = -18,\n _calibrateSoundBurstFilteredExtraDb = 6,\n _calibrateSoundBurstLevelReTBool = false,\n _calibrateSoundBurstUses1000HzGainBool = false,\n _calibrateSoundBurstRepeats = 3,\n _calibrateSoundBurstSec = 1,\n _calibrateSoundBurstPreSec = 1,\n _calibrateSoundBurstPostSec = 1,\n _calibrateSoundHz = 48000,\n _calibrateSoundIRSec = 0.2,\n _calibrateSoundIIRSec = 0.2,\n _calibrateSoundIIRPhase = 'linear',\n calibrateSound1000HzPreSec = 0.5,\n calibrateSound1000HzSec = 0.5,\n calibrateSound1000HzPostSec = 0.5,\n _calibrateSoundBackgroundSecs = 0,\n _calibrateSoundSmoothOctaves = 0.33,\n _calibrateSoundSmoothMinBandwidthHz = 200,\n _calibrateSoundPowerBinDesiredSec = 0.2,\n _calibrateSoundPowerDbSDToleratedDb = 1,\n _calibrateSoundTaperSec = 0.01,\n micManufacturer = '',\n micSerialNumber = '',\n micModelNumber = '',\n micModelName = '',\n calibrateMicrophonesBool,\n authorEmails,\n webAudioDeviceNames = {\n loudspeaker: 'loudspeaker',\n microphone: 'microphone',\n microphoneText: 'xxx XXX',\n },\n userIDs,\n restartButton,\n reminder,\n calibrateSoundLimit,\n _calibrateSoundBurstNormalizeBy1000HzGainBool = false,\n _calibrateSoundBurstScalarDB = 71,\n calibrateSound1000HzMaxSD_dB = 4,\n _calibrateSoundBurstMaxSD_dB = 4,\n calibrateSoundSamplingDesiredBits = 24,\n language,\n loudspeakerModelName='',\n phrases,\n soundSubtitleId,\n\n ) => {\n this._calibrateSoundBurstPreSec = _calibrateSoundBurstPreSec;\n this._calibrateSoundBurstRepeats = _calibrateSoundBurstRepeats;\n this._calibrateSoundBurstSec = _calibrateSoundBurstSec;\n this.micModelName = micModelName;\n this.loudspeakerModelName = loudspeakerModelName;\n this.language = language;\n this.TAPER_SECS = _calibrateSoundTaperSec;\n this.calibrateSoundLimit = calibrateSoundLimit;\n this._calibrateSoundBurstDb = _calibrateSoundBurstDb;\n this._calibrateSoundBurstFilteredExtraDb = _calibrateSoundBurstFilteredExtraDb;\n this._calibrateSoundBurstLevelReTBool = _calibrateSoundBurstLevelReTBool;\n this.CALIBRATION_TONE_DURATION =\n calibrateSound1000HzPreSec + calibrateSound1000HzSec + calibrateSound1000HzPostSec;\n this.calibrateSound1000HzPreSec = calibrateSound1000HzPreSec;\n this.calibrateSound1000HzSec = calibrateSound1000HzSec;\n this.calibrateSound1000HzPostSec = calibrateSound1000HzPostSec;\n this.iirLength = Math.floor(_calibrateSoundIIRSec * this.sourceSamplingRate);\n this.irLength = Math.floor(_calibrateSoundIRSec * this.sourceSamplingRate);\n this.calibrateSoundIIRPhase = _calibrateSoundIIRPhase;\n this.num_mls_to_skip = Math.ceil(_calibrateSoundBurstPreSec / _calibrateSoundBurstSec);\n this._calibrateSoundBurstPostSec = _calibrateSoundBurstPostSec;\n this.numMLSPerCapture = _calibrateSoundBurstRepeats + this.num_mls_to_skip;\n this.desired_time_per_mls = _calibrateSoundBurstSec;\n this.desired_sampling_rate = _calibrateSoundHz;\n this._calibrateSoundBackgroundSecs = _calibrateSoundBackgroundSecs;\n this._calibrateSoundSmoothOctaves = _calibrateSoundSmoothOctaves;\n this._calibrateSoundSmoothMinBandwidthHz = _calibrateSoundSmoothMinBandwidthHz;\n this._calibrateSoundPowerBinDesiredSec = _calibrateSoundPowerBinDesiredSec;\n this._calibrateSoundBurstUses1000HzGainBool = _calibrateSoundBurstUses1000HzGainBool;\n this._calibrateSoundPowerDbSDToleratedDb = _calibrateSoundPowerDbSDToleratedDb;\n this._calibrateSoundBurstNormalizeBy1000HzGainBool =\n _calibrateSoundBurstNormalizeBy1000HzGainBool;\n this._calibrateSoundBurstScalarDB = _calibrateSoundBurstScalarDB;\n this.webAudioDeviceNames = webAudioDeviceNames;\n this.calibrateSoundSamplingDesiredBits = calibrateSoundSamplingDesiredBits;\n this.phrases = phrases;\n this.soundSubtitleId = soundSubtitleId;\n if (isSmartPhone) {\n const leftQuote = \"\\u201C\"; // “\n const rightQuote = \"\\u201D\"; // ”\n this.webAudioDeviceNames.microphone = this.deviceInfo.microphoneFromAPI;\n const quotedWebAudioMic = leftQuote + this.webAudioDeviceNames.microphone + rightQuote;\n const combinedMicText = this.micModelName + \" \" + quotedWebAudioMic;\n webAudioDeviceNames.microphoneText = this.phrases.RC_nameMicrophone[this.language]\n .replace(\"“xxx”\", combinedMicText)\n .replace(\"“XXX”\", combinedMicText);\n }\n // this.webAudioDeviceNames.microphoneText = this.webAudioDeviceNames.microphoneText\n // .replace('xxx', this.webAudioDeviceNames.microphone)\n // .replace('XXX', this.webAudioDeviceNames.microphone);\n //feed calibration goal here\n this._calibrateSoundCheck = _calibrateSoundCheck;\n this.calibrateSound1000HzMaxSD_dB = calibrateSound1000HzMaxSD_dB;\n this._calibrateSoundBurstMaxSD_dB = _calibrateSoundBurstMaxSD_dB;\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\n //check the db based on the microphone currently connected\n\n //new lCalib found at top of calibration files *1000hz, make sure to correct\n //based on zeroing of 1000hz, search for \"*1000Hz\"\n const ID = isSmartPhone ? micModelNumber : micSerialNumber;\n const OEM = isSmartPhone\n ? micModelName === 'UMIK-1' || micModelName === 'UMIK-2'\n ? 'minidsp'\n : this.deviceInfo.OEM.toLowerCase().split(' ').join('')\n : micManufacturer.toLowerCase().split(' ').join('');\n // const ID = \"712-5669\";\n // const OEM = \"minidsp\";\n const micInfo = {\n micModelName: isSmartPhone ? micModelName : microphoneName,\n OEM: isSmartPhone\n ? micModelName === 'UMIK-1' || micModelName === 'UMIK-2'\n ? 'miniDSP'\n : this.deviceInfo.OEM\n : micManufacturer,\n ID: ID,\n createDate: new Date(),\n DateText: (0,_utils__WEBPACK_IMPORTED_MODULE_1__.getCurrentTimeString)(),\n HardwareName: isSmartPhone ? this.deviceInfo.hardwarename : microphoneName,\n hardwareFamily: isSmartPhone ? this.deviceInfo.hardwarefamily : microphoneName,\n HardwareModel: isSmartPhone ? this.deviceInfo.hardwaremodel : microphoneName,\n PlatformName: isSmartPhone ? this.deviceInfo.platformname : 'N/A',\n PlatformVersion: isSmartPhone ? this.deviceInfo.platformversion : 'N/A',\n DeviceType: isSmartPhone ? this.deviceInfo.devicetype : 'N/A',\n ID_from_51Degrees: isSmartPhone ? this.deviceInfo.DeviceId : 'N/A',\n calibrateMicrophonesBool: calibrateMicrophonesBool,\n screenHeight: this.deviceInfo.screenHeight,\n screenWidth: this.deviceInfo.screenWidth,\n webAudioDeviceNames: {\n loudspeaker: this.webAudioDeviceNames.loudspeaker,\n microphone: this.webAudioDeviceNames.microphone,\n },\n userIDs: userIDs,\n lowercaseOEM: OEM.toLowerCase().split(' ').join(''),\n };\n if (calibrateMicrophonesBool) {\n micInfo['authorEmails'] = authorEmails;\n }\n // if undefined in micInfo, set to empty string\n for (const [key, value] of Object.entries(micInfo)) {\n if (value === undefined) {\n micInfo[key] = '';\n }\n }\n\n // this.writeMicrophoneInfoToFirestore(ID, micInfo, OEM, 'default');\n // this.addMicrophoneInfo(ID, OEM, micInfo);\n if (componentIR == null) {\n //mode 'ir'\n //global variable this.componentIR must be set\n await this.readFrqGainFromFirestore(ID, OEM, true).then(data => {\n if (data !== null) {\n this.componentIR = data.ir;\n micInfo['parentTimestamp'] = data.createDate ? data.createDate : new Date();\n micInfo['parentFilenameJSON'] = data.jsonFileName ? data.jsonFileName : '';\n }\n });\n\n // await this.readFrqGain(ID, OEM).then(data => {\n // return data;\n // });\n\n // lCalib = await this.readGainat1000Hz(ID, OEM);\n lCalib = await this.readGainat1000HzFromFirestore(ID, OEM, true);\n micInfo['gainDBSPL'] = lCalib;\n // this.componentGainDBSPL = this.convertToDB(lCalib);\n this.componentGainDBSPL = lCalib;\n //TODO: if this call to database is unknown, cannot perform experiment => return false\n if (this.componentIR == null) {\n this.status =\n `Microphone (${OEM},${ID}) is not found in the database. Please add it to the database.`.toString();\n this.emit('update', {message: this.status});\n return false;\n }\n } else {\n this.transducerType = 'Microphone';\n this.componentIR = componentIR;\n lCalib = this.findGainatFrequency(this.componentIR.Freq, this.componentIR.Gain, 1000);\n // this.componentGainDBSPL = this.convertToDB(lCalib);\n this.componentGainDBSPL = lCalib;\n // await this.writeIsSmartPhone(ID, isSmartPhone, OEM);\n }\n\n this.oldComponentIR = JSON.parse(JSON.stringify(this.componentIR));\n\n return await new Promise(async (resolve, reject) => {\n // add event listner to params.restartButton to resolve the promise with a string: 'restart'\n\n if (reminder) {\n reminder.style.display = '';\n }\n if (restartButton) {\n restartButton.style.display = '';\n restartButton.addEventListener('click', () => {\n this.stopCalibrationAudio();\n this.isCalibrating = true;\n restartButton.style.display = 'none';\n if (reminder) {\n reminder.style.display = 'none';\n }\n this.emit('update', {message: 'Restarting calibration...'});\n resolve('restart');\n });\n }\n await this.pyServerAPI.checkMemory();\n // calibrate volume\n\n let volumeResults = await this.startCalibrationVolume(\n stream,\n gainValues,\n lCalib,\n this.componentGainDBSPL\n );\n if (!volumeResults) return;\n this.T = volumeResults['parameters']['T'];\n this.gainDBSPL = volumeResults['parameters']['gainDBSPL'];\n\n // end calibrate volume\n\n let impulseResponseResults = await this.startCalibrationImpulseResponse(stream);\n if (!impulseResponseResults) return;\n impulseResponseResults['background_noise'] = this.background_noise;\n impulseResponseResults['system']['background_noise'] = this.background_noise;\n impulseResponseResults['component']['background_noise'] = this.background_noise;\n\n //attenuate system background noise\n if (this.systemAttenuatorGainDB != 0) {\n let linearScaleAttenuation = Math.pow(10, this.systemAttenuatorGainDB / 20);\n let linearScalePowerAttenuation = Math.pow(10, this.systemAttenuatorGainDB / 10);\n impulseResponseResults['system']['background_noise']['recording'] = impulseResponseResults[\n 'background_noise'\n ]['recording'].map(value => value / linearScaleAttenuation);\n impulseResponseResults['system']['background_noise']['x_background'] =\n impulseResponseResults['background_noise']['x_background'];\n impulseResponseResults['system']['background_noise']['y_background'] =\n impulseResponseResults['background_noise']['y_background'].map(\n value => value / linearScalePowerAttenuation\n );\n }\n //attenuate component background noise\n if (this.componentAttentuatorGainDB != 0) {\n let linearScaleAttenuation = Math.pow(10, this.componentAttenuatorGainDB / 20);\n let linearScalePowerAttenuation = Math.pow(10, this.componentAttenuatorGainDB / 10);\n impulseResponseResults['component']['background_noise']['recording'] =\n impulseResponseResults['background_noise']['recording'].map(\n value => value / linearScaleAttenuation\n );\n impulseResponseResults['component']['background_noise']['x_background'] =\n impulseResponseResults['background_noise']['x_background'];\n impulseResponseResults['component']['background_noise']['y_background'] =\n impulseResponseResults['background_noise']['y_background'].map(\n value => value / linearScalePowerAttenuation\n );\n }\n impulseResponseResults['system']['attenuatorGainDB'] = this.systemAttenuatorGainDB;\n impulseResponseResults['component']['attenuatorGainDB'] = this.componentAttenuatorGainDB;\n impulseResponseResults['system']['fMaxHz'] = this.systemFMaxHz;\n impulseResponseResults['component']['fMaxHz'] = this.componentFMaxHz;\n impulseResponseResults['L_new_n'] = this.L_new_n;\n impulseResponseResults['fs2'] = this.fs2;\n\n if (componentIR != null) {\n // I corrected microphone/loudpeaker IR scale in easyeyes,\n // but since we write microphone IR to firestore here\n // we need to correct microphone IR here\n let correctGain =\n Math.round((volumeResults.parameters.gainDBSPL - this.componentGainDBSPL) * 10) / 10;\n\n let IrFreq = impulseResponseResults?.component.ir.Freq.map(freq => Math.round(freq));\n let IrGain = impulseResponseResults?.component?.ir.Gain;\n const IrGainAt1000Hz = IrGain[IrFreq.findIndex(freq => freq === 1000)];\n const difference = Math.round(10 * (IrGainAt1000Hz - correctGain)) / 10;\n IrGain = IrGain.map(gain => gain - difference);\n micInfo['mls'] = Number(this.SDofFilteredRange['mls']);\n micInfo['systemCorrectionSD'] = Number(this.SDofFilteredRange['system']);\n micInfo['componentCorrectionSD'] = Number(this.SDofFilteredRange['component']);\n console.log(typeof micInfo['componentCorrectionSD']);\n // const id = await this.writeIsSmartPhoneToFirestore(ID, isSmartPhone, OEM);\n // await this.writeMicrophoneInfoToFirestore(ID, micInfo, OEM, id);\n // await this.writeFrqGainToFirestore(ID, IrFreq, IrGain, OEM, id);\n // micInfo['gainDBSPL'] = impulseResponseResults.component.gainDBSPL;\n // await this.writeGainat1000HzToFirestore(ID, micInfo['gainDBSPL'], OEM, id);\n // await this.writeGainat1000Hz(ID, micInfo['gainDBSPL'], OEM);\n }\n const total_results = {...volumeResults, ...impulseResponseResults};\n total_results['filteredMLSRange'] = this.filteredMLSRange;\n total_results['filteredMLSAttenuation'] = this.filteredMLSAttenuation;\n total_results['micInfo'] = micInfo;\n total_results['audioInfo'] = {};\n total_results['audioInfo']['sinkSampleRate'] = this.sinkSamplingRate;\n total_results['audioInfo']['sourceSampleRate'] = this.sourceSamplingRate;\n total_results['audioInfo']['bitsPerSample'] = this.sampleSize;\n const timeStampresult = [...this.timeStamp].join('\\n');\n total_results['timeStamps'] = timeStampresult;\n total_results['recordingChecks'] = this.recordingChecks;\n total_results['waveforms'] = this.waveforms;\n total_results['component']['phase'] = this.componentIRPhase;\n total_results['system']['phase'] = this.systemIRPhase;\n total_results['qualityMetrics'] = this.SDofFilteredRange;\n total_results['flags'] = this.flags;\n console.log('total results');\n console.log(total_results);\n console.log('Time Stamps');\n console.log(timeStampresult);\n\n resolve(total_results);\n });\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (Combination);\n\n//# sourceURL=webpack://speakerCalibrator/./src/tasks/combination/combination.js?");
|
|
516
516
|
|
|
517
517
|
/***/ }),
|
|
518
518
|
|
package/package.json
CHANGED
|
@@ -255,6 +255,8 @@ class Listener extends AudioPeer {
|
|
|
255
255
|
// ...(availableConstraints.channelCount && availableConstraints.channelCount == true
|
|
256
256
|
// ? {channelCount: {exact: 1}}
|
|
257
257
|
// : {}),
|
|
258
|
+
autoGainControl: false,
|
|
259
|
+
noiseSuppression: false,
|
|
258
260
|
echoCancellation: false,
|
|
259
261
|
channelCount: 1,
|
|
260
262
|
};
|
|
@@ -23,6 +23,7 @@ class AudioCalibrator extends AudioRecorder {
|
|
|
23
23
|
this.numCaptures = numCaptures;
|
|
24
24
|
this.numMLSPerCapture = numMLSPerCapture;
|
|
25
25
|
this.pyServerAPI = new PythonServerAPI();
|
|
26
|
+
this.currentTime = 0;
|
|
26
27
|
}
|
|
27
28
|
|
|
28
29
|
/** @private */
|
|
@@ -70,9 +71,22 @@ class AudioCalibrator extends AudioRecorder {
|
|
|
70
71
|
};
|
|
71
72
|
|
|
72
73
|
addTimeStamp = taskName => {
|
|
73
|
-
|
|
74
|
-
|
|
74
|
+
const currentTime = new Date().getTime(); // Current time in milliseconds
|
|
75
|
+
const elapsedTime = (currentTime - this.startTime) / 1000; // Total time since start in seconds
|
|
76
|
+
const stepDuration = elapsedTime - this.currentTime; // Time taken for the current step
|
|
77
|
+
|
|
78
|
+
this.currentTime = elapsedTime; // Update for the next step
|
|
79
|
+
|
|
80
|
+
// Round to 1 decimal place for consistent formatting
|
|
81
|
+
|
|
82
|
+
;
|
|
83
|
+
|
|
84
|
+
// Log the timestamp with aligned bars
|
|
85
|
+
this.timeStamp.push(
|
|
86
|
+
`SOUND Total: ${elapsedTime.toFixed(1)} s Step: ${stepDuration.toFixed(1)} s ${taskName}`
|
|
87
|
+
);
|
|
75
88
|
};
|
|
89
|
+
|
|
76
90
|
|
|
77
91
|
recordBackground = async (
|
|
78
92
|
stream,
|
|
@@ -144,7 +158,7 @@ class AudioCalibrator extends AudioRecorder {
|
|
|
144
158
|
console.warn('beforeRecord');
|
|
145
159
|
await beforeRecord();
|
|
146
160
|
const totalSec = this._calibrateSoundBurstPreSec + (this.numMLSPerCapture - this.num_mls_to_skip) * this._calibrateSoundBurstSec + this._calibrateSoundBurstPostSec;
|
|
147
|
-
|
|
161
|
+
|
|
148
162
|
|
|
149
163
|
// calibration loop
|
|
150
164
|
while (loopCondition()) {
|
|
@@ -194,6 +208,7 @@ class AudioCalibrator extends AudioRecorder {
|
|
|
194
208
|
) => {
|
|
195
209
|
this.numCalibratingRoundsCompleted = 0;
|
|
196
210
|
this.numCalibratingRounds = 2;
|
|
211
|
+
console.log("maxSD in VolumeCaibrationSteps: ", maxSD, '0' >= maxSD);
|
|
197
212
|
// calibration loop
|
|
198
213
|
while (!this.isCalibrating && this.numCalibratingRoundsCompleted < this.numCalibratingRounds) {
|
|
199
214
|
if (this.isCalibrating) break;
|
|
@@ -213,7 +228,7 @@ class AudioCalibrator extends AudioRecorder {
|
|
|
213
228
|
if (this.isCalibrating) break;
|
|
214
229
|
// after recording
|
|
215
230
|
await afterRecord(lCalib);
|
|
216
|
-
const sd = await checkSD()
|
|
231
|
+
const sd = await checkSD();
|
|
217
232
|
if (sd <= maxSD) {
|
|
218
233
|
console.log(`SD =${sd}, less than calibrateSound1000HzMaxSD_dB=${maxSD}`);
|
|
219
234
|
this.numCalibratingRoundsCompleted += 2;
|
|
@@ -551,7 +551,7 @@ class Combination extends AudioCalibrator {
|
|
|
551
551
|
// Slice the array from the calculated start index to the end of the array
|
|
552
552
|
const background_rec = background_rec_whole.slice(startIndex);
|
|
553
553
|
console.log('Sending background recording to server for processing');
|
|
554
|
-
this.addTimeStamp('Compute background
|
|
554
|
+
this.addTimeStamp('Compute PSD of background');
|
|
555
555
|
this.pyServerAPI
|
|
556
556
|
.getBackgroundNoisePSDWithRetry({
|
|
557
557
|
background_rec,
|
|
@@ -780,7 +780,7 @@ class Combination extends AudioCalibrator {
|
|
|
780
780
|
*/
|
|
781
781
|
#afterMLSRecord = async () => {
|
|
782
782
|
console.log('after record');
|
|
783
|
-
this.addTimeStamp(`Send
|
|
783
|
+
this.addTimeStamp(`Send MLS to the server`);
|
|
784
784
|
await this.sendRecordingToServerForProcessing();
|
|
785
785
|
};
|
|
786
786
|
|
|
@@ -903,8 +903,8 @@ class Combination extends AudioCalibrator {
|
|
|
903
903
|
const post = this._calibrateSoundBurstPostSec;
|
|
904
904
|
const total_dur = pre + repeats * burst + post;
|
|
905
905
|
this.addTimeStamp(
|
|
906
|
-
`Record ${total_dur.toFixed(1)} s
|
|
907
|
-
`(${pre.toFixed(1)}
|
|
906
|
+
`Record ${total_dur.toFixed(1)} s ` +
|
|
907
|
+
`(${pre.toFixed(1)} + ${repeats}×${burst.toFixed(1)} + ${post.toFixed(1)} s) of MLS ver. ${this.icapture}`
|
|
908
908
|
);
|
|
909
909
|
this.status = this.generateTemplate(`All Hz Calibration: playing the calibration tone...`.toString()).toString();
|
|
910
910
|
} else if (this.mode === 'filtered') {
|
|
@@ -1337,7 +1337,7 @@ class Combination extends AudioCalibrator {
|
|
|
1337
1337
|
this.filteredMLSRange.component.Max = findMaxValue(this.#currentConvolution);
|
|
1338
1338
|
this.soundCheck = 'component';
|
|
1339
1339
|
this.addTimeStamp(`Record ${total_dur} s of MLS with ${this.soundCheck} IIR.`)
|
|
1340
|
-
this.addTimeStamp(`Record ${total_dur} s of MLS with speaker or microphone IIR.`);
|
|
1340
|
+
//this.addTimeStamp(`Record ${total_dur} s of MLS with speaker or microphone IIR.`);
|
|
1341
1341
|
if (this.isCalibrating) return null;
|
|
1342
1342
|
await this.playMLSwithIIR(stream, this.#currentConvolution);
|
|
1343
1343
|
this.stopCalibrationAudio();
|
|
@@ -1347,7 +1347,7 @@ class Combination extends AudioCalibrator {
|
|
|
1347
1347
|
this.filteredMLSRange.system.Max = findMaxValue(this.#currentConvolution);
|
|
1348
1348
|
this.soundCheck = 'system';
|
|
1349
1349
|
this.addTimeStamp(`Record ${total_dur} s of MLS with ${this.soundCheck} IIR.`)
|
|
1350
|
-
this.addTimeStamp(`Record ${total_dur} s of MLS with speaker or microphone IIR.`);
|
|
1350
|
+
//this.addTimeStamp(`Record ${total_dur} s of MLS with speaker or microphone IIR.`);
|
|
1351
1351
|
if (this.isCalibrating) return null;
|
|
1352
1352
|
await this.playMLSwithIIR(stream, this.#currentConvolution);
|
|
1353
1353
|
this.stopCalibrationAudio();
|
|
@@ -1427,7 +1427,7 @@ class Combination extends AudioCalibrator {
|
|
|
1427
1427
|
console.error(err);
|
|
1428
1428
|
});
|
|
1429
1429
|
|
|
1430
|
-
this.addTimeStamp('Compute PSD recording of filtered recording
|
|
1430
|
+
this.addTimeStamp('Compute PSD recording of speaker+ mic IIR-filtered MLS recording');
|
|
1431
1431
|
if (this.isCalibrating) return null;
|
|
1432
1432
|
let conv_results = await this.pyServerAPI
|
|
1433
1433
|
.getSubtractedPSDWithRetry(conv_rec, knownGain, knownFreq, sampleRate)
|
|
@@ -1468,7 +1468,7 @@ class Combination extends AudioCalibrator {
|
|
|
1468
1468
|
|
|
1469
1469
|
unconv_rec = this.componentInvertedImpulseResponseNoBandpass;
|
|
1470
1470
|
conv_rec = this.componentInvertedImpulseResponse;
|
|
1471
|
-
this.addTimeStamp('Compute PSD of
|
|
1471
|
+
this.addTimeStamp('Compute PSD of speaker or mic IIR, with and without bandpass');
|
|
1472
1472
|
if (this.isCalibrating) return null;
|
|
1473
1473
|
let component_iir_psd = await this.pyServerAPI
|
|
1474
1474
|
.getPSDWithRetry({
|
|
@@ -1487,7 +1487,7 @@ class Combination extends AudioCalibrator {
|
|
|
1487
1487
|
});
|
|
1488
1488
|
unconv_rec = this.systemInvertedImpulseResponseNoBandpass;
|
|
1489
1489
|
conv_rec = this.systemInvertedImpulseResponse;
|
|
1490
|
-
this.addTimeStamp('Compute PSD of
|
|
1490
|
+
this.addTimeStamp('Compute PSD of speaker +mic IIR, with and without bandpass');
|
|
1491
1491
|
if (this.isCalibrating) return null;
|
|
1492
1492
|
let system_iir_psd = await this.pyServerAPI
|
|
1493
1493
|
.getPSDWithRetry({
|
|
@@ -1505,7 +1505,7 @@ class Combination extends AudioCalibrator {
|
|
|
1505
1505
|
console.error(err);
|
|
1506
1506
|
});
|
|
1507
1507
|
|
|
1508
|
-
this.addTimeStamp('Compute PSD of MLS
|
|
1508
|
+
this.addTimeStamp('Compute PSD of MLS');
|
|
1509
1509
|
if (this.isCalibrating) return null;
|
|
1510
1510
|
let mls_psd = await this.pyServerAPI
|
|
1511
1511
|
.getMLSPSDWithRetry({
|
|
@@ -1522,7 +1522,7 @@ class Combination extends AudioCalibrator {
|
|
|
1522
1522
|
console.error(err);
|
|
1523
1523
|
});
|
|
1524
1524
|
|
|
1525
|
-
this.addTimeStamp('Compute PSD of
|
|
1525
|
+
this.addTimeStamp('Compute PSD of speaker or mic');
|
|
1526
1526
|
if (this.isCalibrating) return null;
|
|
1527
1527
|
let filtered_mls_psd = await this.pyServerAPI
|
|
1528
1528
|
.getMLSPSDWithRetry({
|
|
@@ -2087,6 +2087,7 @@ class Combination extends AudioCalibrator {
|
|
|
2087
2087
|
if (this.isCalibrating) return null;
|
|
2088
2088
|
this.percent_complete = 100;
|
|
2089
2089
|
this.status = this.generateTemplate(`All Hz Calibration: Finished`.toString()).toString();
|
|
2090
|
+
this.addTimeStamp('Done');
|
|
2090
2091
|
this.emit('update', {message: this.status});
|
|
2091
2092
|
|
|
2092
2093
|
//here after calibration we have the component calibration (either loudspeaker or microphone) in the same form as the componentIR
|
|
@@ -2249,14 +2250,7 @@ class Combination extends AudioCalibrator {
|
|
|
2249
2250
|
|
|
2250
2251
|
#sendToServerForProcessing = async lCalib => {
|
|
2251
2252
|
console.log('Sending data to server');
|
|
2252
|
-
|
|
2253
|
-
this.addTimeStamp(
|
|
2254
|
-
"1000 Hz: recorded at " + this.inDB +
|
|
2255
|
-
" dB (" + this.calibrateSound1000HzPreSec.toFixed(1) +
|
|
2256
|
-
" s pre + " + this.calibrateSound1000HzSec.toFixed(1) +
|
|
2257
|
-
" s used + " + this.calibrateSound1000HzPostSec.toFixed(1) +
|
|
2258
|
-
" s post)"
|
|
2259
|
-
);
|
|
2253
|
+
|
|
2260
2254
|
let left = this.calibrateSound1000HzPreSec;
|
|
2261
2255
|
let right = this.calibrateSound1000HzPreSec + this.calibrateSound1000HzSec;
|
|
2262
2256
|
if (this.isCalibrating) return null;
|
|
@@ -2293,6 +2287,27 @@ class Combination extends AudioCalibrator {
|
|
|
2293
2287
|
this._calibrateSoundPowerBinDesiredSec);
|
|
2294
2288
|
console.log(res);
|
|
2295
2289
|
this.recordingChecks['volume'][this.inDB] = res;
|
|
2290
|
+
console.log("Recording checks in sendToServer", this.recordingChecks['volume']);
|
|
2291
|
+
const getSD = () => this.recordingChecks['volume'][this.inDB]['sd'];
|
|
2292
|
+
const getSDMessage = () => {
|
|
2293
|
+
//SOUND 6.7 s. 2.5+2.5+0.5 s. 1000 Hz at -60 dB. SD = 1.3 dB
|
|
2294
|
+
// And reporting each rejected recording as
|
|
2295
|
+
// SOUND 6.7 s. 2.5+2.5+0.5 s. 1000 Hz at -60 dB, SD = 19.7 > 4 dB.
|
|
2296
|
+
|
|
2297
|
+
if(this.numCalibratingRoundsCompleted==1)
|
|
2298
|
+
return `, SD = ${getSD()} > ${this.calibrateSound1000HzMaxSD_dB} dB.`;
|
|
2299
|
+
return `. SD = ${getSD()} dB`
|
|
2300
|
+
}
|
|
2301
|
+
const total_dur = this.calibrateSound1000HzPreSec + this.calibrateSound1000HzSec + this.calibrateSound1000HzPostSec;
|
|
2302
|
+
|
|
2303
|
+
this.addTimeStamp(
|
|
2304
|
+
`${this.calibrateSound1000HzPreSec.toFixed(1)}` +
|
|
2305
|
+
`+ ${this.calibrateSound1000HzSec.toFixed(1)}` +
|
|
2306
|
+
`+ ${this.calibrateSound1000HzPostSec.toFixed(1)} s.` +
|
|
2307
|
+
`1000 Hz at ${this.inDB} dB${getSDMessage()}`
|
|
2308
|
+
);
|
|
2309
|
+
|
|
2310
|
+
|
|
2296
2311
|
};
|
|
2297
2312
|
|
|
2298
2313
|
startCalibrationVolume = async (stream, gainValues, lCalib, componentGainDBSPL) => {
|