speaker-calibration 2.2.127 → 2.2.129
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/example/listener.js
CHANGED
|
@@ -61,7 +61,6 @@ switch (isSmartPhone) {
|
|
|
61
61
|
const externalMicList = ['UMIK', 'Airpods', 'Bluetooth'];
|
|
62
62
|
try {
|
|
63
63
|
const stream = await navigator.mediaDevices.getUserMedia({audio: true});
|
|
64
|
-
console.log(stream);
|
|
65
64
|
if (stream) {
|
|
66
65
|
const devices = await navigator.mediaDevices.enumerateDevices();
|
|
67
66
|
console.log(devices);
|
package/dist/main.js
CHANGED
|
@@ -785,7 +785,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var peer
|
|
|
785
785
|
/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
|
|
786
786
|
|
|
787
787
|
"use strict";
|
|
788
|
-
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/* harmony import */ var axios__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! axios */ \"./node_modules/axios/index.js\");\n/* harmony import */ var axios__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(axios__WEBPACK_IMPORTED_MODULE_2__);\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 urlParameters.calibrateSoundHz !== null && urlParameters.calibrateSoundHz !== undefined\n ? urlParameters.calibrateSoundHz\n : 48000;\n this.calibrateSoundSamplingDesiredBits =\n urlParameters.calibrateSoundSamplingDesiredBits !== null &&\n urlParameters.calibrateSoundSamplingDesiredBits !== undefined\n ? urlParameters.calibrateSoundSamplingDesiredBits\n : 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 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\n const track = stream.getAudioTracks()[0];\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 = () => {\n const availableConstraints = navigator.mediaDevices.getSupportedConstraints();\n\n this.displayUpdate(\n `Listener MediaDevices Available Contraints - ${JSON.stringify(\n availableConstraints,\n undefined,\n 2\n )}`\n );\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 noiseSuppression: false,\n autoGainControl: false,\n };\n\n if (this.microphoneDeviceId !== '') {\n contraints.deviceId = {exact: this.microphoneDeviceId};\n }\n\n console.log(contraints);\n\n this.displayUpdate(\n `Listener MediaDevices Contraints - ${JSON.stringify(contraints, undefined, 2)}`\n );\n\n return contraints;\n };\n\n openAudioStream = async () => {\n this.displayUpdate('Listener - openAudioStream');\n const mobileOS = this.getMobileOS();\n if (false) {}\n\n navigator.mediaDevices\n .getUserMedia({\n audio: this.getMediaDevicesAudioContraints(),\n video: false,\n })\n .then(stream => {\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.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 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?");
|
|
788
|
+
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/* harmony import */ var axios__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! axios */ \"./node_modules/axios/index.js\");\n/* harmony import */ var axios__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(axios__WEBPACK_IMPORTED_MODULE_2__);\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 urlParameters.calibrateSoundHz !== null && urlParameters.calibrateSoundHz !== undefined\n ? urlParameters.calibrateSoundHz\n : 48000;\n this.calibrateSoundSamplingDesiredBits =\n urlParameters.calibrateSoundSamplingDesiredBits !== null &&\n urlParameters.calibrateSoundSamplingDesiredBits !== undefined\n ? urlParameters.calibrateSoundSamplingDesiredBits\n : 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 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 => {console.log(track, track.enabled)});\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 = () => {\n const availableConstraints = navigator.mediaDevices.getSupportedConstraints();\n\n this.displayUpdate(\n `Listener MediaDevices Available Contraints - ${JSON.stringify(\n availableConstraints,\n undefined,\n 2\n )}`\n );\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 noiseSuppression: false,\n autoGainControl: false,\n };\n\n if (this.microphoneDeviceId !== '') {\n contraints.deviceId = {exact: this.microphoneDeviceId};\n }\n\n console.log(contraints);\n\n this.displayUpdate(\n `Listener MediaDevices Contraints - ${JSON.stringify(contraints, undefined, 2)}`\n );\n\n return contraints;\n };\n\n openAudioStream = async () => {\n this.displayUpdate('Listener - openAudioStream');\n const mobileOS = this.getMobileOS();\n if (false) {}\n\n navigator.mediaDevices\n .getUserMedia({\n audio: this.getMediaDevicesAudioContraints(),\n video: false,\n })\n .then(stream => {\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.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 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?");
|
|
789
789
|
|
|
790
790
|
/***/ }),
|
|
791
791
|
|
|
@@ -807,7 +807,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac
|
|
|
807
807
|
/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
|
|
808
808
|
|
|
809
809
|
"use strict";
|
|
810
|
-
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var qrcode__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! qrcode */ \"./node_modules/qrcode/lib/browser.js\");\n/* harmony import */ var _audioPeer__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./audioPeer */ \"./src/peer-connection/audioPeer.js\");\n/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../utils */ \"./src/utils.js\");\n/* harmony import */ var _peerErrors__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./peerErrors */ \"./src/peer-connection/peerErrors.js\");\n/* harmony import */ var _dist_example_i18n__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../dist/example/i18n */ \"./dist/example/i18n.js\");\n\n\n\n\n\n\n\n/**\n * @class Handles the speaker's side of the connection. Responsible for initiating the connection,\n * rendering the QRCode, and answering the call.\n * @augments AudioPeer\n */\nclass Speaker extends _audioPeer__WEBPACK_IMPORTED_MODULE_1__[\"default\"] {\n /**\n * Takes the url of the current site and a target element where html elements will be appended.\n *\n * @param params - See type definition for initParameters.\n * @param Calibrator - An instance of the AudioCalibrator class, should not use AudioCalibrator directly, instead use an extended class available in /tasks/.\n * @param CalibratorInstance\n * @example\n */\n constructor(params, CalibratorInstance) {\n super(params);\n this.language = params?.language ?? 'en-US';\n this.siteUrl += '/listener?';\n this.ac = CalibratorInstance;\n this.result = null;\n this.debug = params?.debug ?? false;\n this.isSmartPhone = params?.isSmartPhone ?? false;\n this.calibrateSoundHz = params?.calibrateSoundHz ?? 48000;\n this.calibrateSoundSamplingDesiredBits = params?.calibrateSoundSamplingDesiredBits ?? 24;\n this.instructionDisplayId = params?.instructionDisplayId ?? '';\n this.soundSubtitleId = params?.soundSubtitleId ?? '';\n this.timeToCalibrateDisplay = params?.timeToCalibrateId ?? '';\n this.soundMessageId = params?.soundMessageId ?? '';\n this.titleDisplayId = params?.titleDisplayId ?? '';\n this.timeToCalibrate = params?.timeToCalibrate ?? 10;\n\n /* Set up callbacks that handle any events related to our peer object. */\n this.peer.on('open', this.#onPeerOpen);\n this.peer.on('connection', this.#onPeerConnection);\n this.peer.on('close', this.onPeerClose);\n this.peer.on('disconnected', this.#onPeerDisconnected);\n this.peer.on('error', this.#onPeerError);\n }\n /**\n * Async factory method that creates the Speaker object, and returns a promise that resolves to the result of the calibration.\n *\n * @param params - The parameters to be passed to the peer object.\n * @param Calibrator - The class that defines the calibration process.\n * @param CalibratorInstance\n * @param timeOut - The amount of time to wait before timing out the connection (in milliseconds).\n * @public\n * @example\n */\n static startCalibration = async (params, CalibratorInstance, timeOut = 180000) => {\n window.speaker = new Speaker(params, CalibratorInstance);\n const {speaker} = window;\n\n // wrap the calibration process in a promise so we can await it\n return new Promise((resolve, reject) => {\n // when a call is received\n speaker.peer.on('call', async call => {\n // Answer the call (one way)\n\n call.answer();\n speaker.#removeUIElems();\n speaker.#showSpinner();\n speaker.ac.createLocalAudio(document.getElementById(speaker.targetElement));\n // when we start receiving audio\n call.on('stream', async stream => {\n console.log(stream);\n window.localStream = stream;\n window.localAudio.srcObject = stream;\n window.localAudio.autoplay = false;\n\n // if the sinkSamplingRate is not set sleep\n while (!speaker.ac.sampleRatesSet()) {\n console.log('SinkSamplingRate is undefined, sleeping');\n await (0,_utils__WEBPACK_IMPORTED_MODULE_2__.sleep)(1);\n }\n\n if (params.displayUpdate) {\n params.displayUpdate.style.display = '';\n }\n\n // resolve when we have a result\n speaker.result = await speaker.ac.startCalibration(\n stream,\n params.gainValues,\n params.ICalib,\n params.knownIR,\n params.microphoneName,\n params.calibrateSoundCheck,\n params.isSmartPhone,\n params.calibrateSoundBurstDb,\n params.calibrateSoundBurstRepeats,\n params.calibrateSoundBurstSec,\n params.calibrateSoundBurstsWarmup,\n params.calibrateSoundHz,\n params.calibrateSoundIRSec,\n params.calibrateSoundIIRSec,\n params.calibrateSound1000HzPreSec,\n params.calibrateSound1000HzSec,\n params.calibrateSound1000HzPostSec,\n params.calibrateSoundBackgroundSecs,\n params.calibrateSoundSmoothOctaves,\n params.calibrateSoundPowerBinDesiredSec,\n params.calibrateSoundPowerDbSDToleratedDb,\n params.micManufacturer,\n params.micSerialNumber,\n params.micModelNumber,\n params.micModelName,\n params.calibrateMicrophonesBool,\n params.authorEmails,\n params.webAudioDeviceNames,\n params.IDsToSaveInSoundProfileLibrary,\n params.restartButton,\n params.calibrateSoundLimit\n );\n speaker.#removeUIElems();\n resolve(speaker.result);\n });\n // if we do not receive a result within the timeout, reject\n setTimeout(() => {\n reject(\n new _peerErrors__WEBPACK_IMPORTED_MODULE_3__.CalibrationTimedOutError(\n `Calibration failed to produce a result after ${\n timeOut / 1000\n } seconds. Please try again.`\n )\n );\n }, timeOut);\n });\n });\n };\n\n static testIIR = async (params, CalibratorInstance, IIR, timeOut = 180000) => {\n window.speaker = new Speaker(params, CalibratorInstance);\n const {speaker} = window;\n\n // wrap the calibration process in a promise so we can await it\n return new Promise((resolve, reject) => {\n // when a call is received\n speaker.peer.on('call', async call => {\n // Answer the call (one way)\n call.answer();\n speaker.#removeUIElems();\n speaker.#showSpinner();\n speaker.ac.createLocalAudio(document.getElementById(speaker.targetElement));\n // when we start receiving audio\n call.on('stream', async stream => {\n window.localStream = stream;\n window.localAudio.srcObject = stream;\n window.localAudio.autoplay = false;\n\n // if the sinkSamplingRate is not set sleep\n while (!speaker.ac.sampleRatesSet()) {\n console.log('SinkSamplingRate is undefined, sleeping');\n await (0,_utils__WEBPACK_IMPORTED_MODULE_2__.sleep)(1);\n }\n // resolve when we have a result\n speaker.result = await speaker.ac.playMLSwithIIR(stream, IIR);\n speaker.#removeUIElems();\n resolve(speaker.result);\n });\n // if we do not receive a result within the timeout, reject\n setTimeout(() => {\n reject(\n new _peerErrors__WEBPACK_IMPORTED_MODULE_3__.CalibrationTimedOutError(\n `Calibration failed to produce a result after ${\n timeOut / 1000\n } seconds. Please try again.`\n )\n );\n }, timeOut);\n });\n });\n };\n\n /**\n * Called after the peer conncection has been opened.\n * Generates a QR code for the connection and displays it.\n *\n * @private\n * @example\n */\n #showQRCode = () => {\n // Get query string, the URL parameters to specify a Listener\n const queryStringParameters = {\n speakerPeerId: this.peer.id,\n isSmartPhone: this.isSmartPhone,\n calibrateSoundHz: this.calibrateSoundHz,\n calibrateSoundSamplingDesiredBits: this.calibrateSoundSamplingDesiredBits,\n lang: this.language,\n };\n const queryString = this.queryStringFromObject(queryStringParameters);\n const uri = this.siteUrl + queryString;\n if (this.isSmartPhone) {\n // Display QR code for the participant to scan\n const qrCanvas = document.createElement('canvas');\n qrCanvas.setAttribute('id', 'qrCanvas');\n console.log(uri);\n qrcode__WEBPACK_IMPORTED_MODULE_0__.toCanvas(qrCanvas, uri, error => {\n if (error) console.error(error);\n });\n const qrImage = new Image(400, 400);\n qrImage.setAttribute('id', 'compatibilityCheckQRImage');\n qrImage.style.zIndex = Infinity;\n qrImage.style.width = 400;\n qrImage.style.height = 400;\n qrImage.style.aspectRatio = 1;\n qrImage.src = qrCanvas.toDataURL();\n qrImage.style.maxHeight = '150px';\n qrImage.style.maxWidth = '150px';\n document.getElementById(this.targetElement).appendChild(qrImage);\n } else {\n // show the link to the user\n // If specified HTML Id is available, show QR code there\n if (document.getElementById(this.targetElement)) {\n // const linkTag = document.createElement('a');\n // linkTag.setAttribute('href', uri);\n // linkTag.innerHTML = 'Click here to start the calibration';\n // linkTag.target = '_blank';\n // document.getElementById(this.targetElement).appendChild(linkTag);\n // document.getElementById(this.targetElement).appendChild(qrCanvas);\n\n const proceedButton = document.createElement('button');\n proceedButton.setAttribute('id', 'calibrationProceedButton');\n proceedButton.setAttribute('class', 'btn btn-success');\n proceedButton.innerHTML = 'Proceed';\n proceedButton.onclick = () => {\n // open the link in a new tab\n window.open(uri, '_blank');\n // remove the button\n document.getElementById('calibrationProceedButton').remove();\n };\n document.getElementById(this.targetElement).appendChild(proceedButton);\n }\n }\n // or just print it to console\n console.log('TEST: Peer reachable at: ', uri);\n };\n\n #showSpinner = () => {\n const spinner = document.createElement('div');\n spinner.className = 'spinner-border ml-auto';\n spinner.role = 'status';\n spinner.ariaHidden = 'true';\n document.getElementById(this.targetElement).appendChild(spinner);\n\n // clear instructionDisplay\n const soundMessage = document.getElementById(this.soundMessageId);\n soundMessage.innerHTML = '';\n soundMessage.style.display = 'none';\n const instructionDisplay = document.getElementById(this.instructionDisplayId);\n const background = document.getElementById('background'); // todo: get background id from params\n const subtitle = document.getElementById(this.soundSubtitleId);\n if (subtitle) {\n subtitle.innerHTML = '';\n }\n if (instructionDisplay) {\n instructionDisplay.innerHTML = '';\n instructionDisplay.style.whiteSpace = 'nowrap';\n instructionDisplay.style.fontWeight = 'bold';\n instructionDisplay.style.width = 'fit-content';\n instructionDisplay.innerHTML = _dist_example_i18n__WEBPACK_IMPORTED_MODULE_4__.phrases.RC_soundRecording[this.language];\n let fontSize = 100;\n instructionDisplay.style.fontSize = fontSize + 'px';\n while (instructionDisplay.scrollWidth > background.scrollWidth * 0.9 && fontSize > 10) {\n fontSize--;\n instructionDisplay.style.fontSize = fontSize + 'px';\n }\n // const p = document.createElement('p');\n // // font size\n // p.style.fontSize = '1.1rem';\n // p.style.fontWeight = 'normal';\n // p.style.paddingTop = '20px';\n // const timeToCalibrateText = phrases.RC_howLongToCalibrate['en-US'];\n // p.innerHTML = timeToCalibrateText.replace('111', this.timeToCalibrate);\n // instructionDisplay.appendChild(p);\n }\n\n const timeToCalibrateDisplay = document.getElementById(this.timeToCalibrateDisplay);\n if (timeToCalibrateDisplay) {\n const timeToCalibrateText = _dist_example_i18n__WEBPACK_IMPORTED_MODULE_4__.phrases.RC_howLongToCalibrate[this.language];\n timeToCalibrateDisplay.innerHTML = timeToCalibrateText.replace('111', this.timeToCalibrate);\n timeToCalibrateDisplay.style.fontWeight = 'normal';\n timeToCalibrateDisplay.style.fontSize = '1rem';\n // timeToCalibrateDisplay.style.paddingTop = '20px';\n }\n\n // Update title - titleDisplayId\n const titleDisplay = document.getElementById(this.titleDisplayId);\n if (titleDisplay) {\n // replace 5 with 6\n titleDisplay.innerHTML = this.isSmartPhone\n ? titleDisplay.innerHTML.replace('2', '3')\n : titleDisplay.innerHTML.replace('4', '5');\n }\n };\n\n #removeUIElems = () => {\n const parent = document.getElementById(this.targetElement);\n while (parent.firstChild) {\n parent.firstChild.remove();\n }\n };\n\n /**\n * Called when the peer connection is opened.\n * Saves the peer id and calls the QR code generator.\n *\n * @param peerId - The peer id of the peer connection.\n * @param id\n * @private\n * @example\n */\n #onPeerOpen = id => {\n // Workaround for peer.reconnect deleting previous id\n if (id === null) {\n console.error('Received null id from peer open');\n this.peer.id = this.lastPeerId;\n } else {\n this.lastPeerId = this.peer.id;\n }\n\n if (id !== this.peer.id) {\n console.warn('DEBUG Check you assumption that id === this.peer.id');\n }\n\n this.#showQRCode();\n };\n\n /**\n * Called when the peer connection is established.\n * Enforces a single connection.\n *\n * @param connection - The connection object.\n * @private\n * @example\n */\n #onPeerConnection = connection => {\n // Allow only a single connection\n if (this.conn && this.conn.open) {\n connection.on('open', () => {\n connection.send('Already connected to another client');\n setTimeout(() => {\n connection.close();\n }, 500);\n });\n return;\n }\n\n this.conn = connection;\n console.log('Connected to: ', this.conn.peer);\n this.#ready();\n };\n\n /**\n * Called when the peer connection is closed.\n *\n * @private\n * @example\n */\n onPeerClose = () => {\n this.conn = null;\n console.log('Connection destroyed');\n };\n\n static closeConnection = () => {\n this.conn = null;\n console.log('Connection destroyed');\n };\n\n /**\n * Called when the peer connection is disconnected.\n * Attempts to reconnect.\n *\n * @private\n * @example\n */\n #onPeerDisconnected = () => {\n console.log('Connection lost. Please reconnect');\n\n // Workaround for peer.reconnect deleting previous id\n this.peer.id = this.lastPeerId;\n // eslint-disable-next-line no-underscore-dangle\n this.peer._lastServerId = this.lastPeerId;\n this.peer.reconnect();\n };\n\n /**\n * Called when the peer connection encounters an error.\n *\n * @param error\n * @private\n * @example\n */\n #onPeerError = error => {\n // TODO: check if this function is needed or not\n console.error(error);\n };\n\n /**\n * Called when data is received from the peer connection.\n *\n * @param data\n * @private\n * @example\n */\n #onIncomingData = data => {\n // enforce object type\n if (\n !Object.prototype.hasOwnProperty.call(data, 'name') ||\n !Object.prototype.hasOwnProperty.call(data, 'payload')\n ) {\n console.error('Received malformed data: ', data);\n return;\n }\n\n switch (data.name) {\n case 'samplingRate':\n this.ac.setSamplingRates(data.payload);\n break;\n case 'sampleSize':\n this.ac.setSampleSize(data.payload);\n break;\n case 'deviceType':\n this.ac.setDeviceType(data.payload);\n break;\n case 'deviceName':\n this.ac.setDeviceName(data.payload);\n break;\n case 'deviceInfo':\n this.ac.setDeviceInfo(data.payload);\n console.log('Received device info from listener: ', data.payload);\n break;\n case _peerErrors__WEBPACK_IMPORTED_MODULE_3__.UnsupportedDeviceError.name:\n case _peerErrors__WEBPACK_IMPORTED_MODULE_3__.MissingSpeakerIdError.name:\n throw data.payload;\n break;\n default:\n break;\n }\n };\n\n /**\n * Called when the peer connection is #ready.\n *\n * @private\n * @example\n */\n #ready = () => {\n // Perform callback with data\n this.conn.on('data', this.#onIncomingData);\n this.conn.on('close', () => {\n console.log('Connection reset<br>Awaiting connection...');\n this.conn = null;\n });\n };\n\n /** .\n * .\n * .\n * Debug method for downloading the recorded audio\n *\n * @public\n * @example\n */\n downloadData = () => {\n this.ac.downloadData();\n };\n}\n\n/* \nReferenced links:\nhttps://stackoverflow.com/questions/28016664/when-you-pass-this-as-an-argument/28016676#28016676\nhttps://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes\nhttps://stackoverflow.com/questions/879152/how-do-i-make-javascript-beep [3]\n*/\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (Speaker);\n\n\n//# sourceURL=webpack://speakerCalibrator/./src/peer-connection/speaker.js?");
|
|
810
|
+
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var qrcode__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! qrcode */ \"./node_modules/qrcode/lib/browser.js\");\n/* harmony import */ var _audioPeer__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./audioPeer */ \"./src/peer-connection/audioPeer.js\");\n/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../utils */ \"./src/utils.js\");\n/* harmony import */ var _peerErrors__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./peerErrors */ \"./src/peer-connection/peerErrors.js\");\n/* harmony import */ var _dist_example_i18n__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../dist/example/i18n */ \"./dist/example/i18n.js\");\n\n\n\n\n\n\n\n/**\n * @class Handles the speaker's side of the connection. Responsible for initiating the connection,\n * rendering the QRCode, and answering the call.\n * @augments AudioPeer\n */\nclass Speaker extends _audioPeer__WEBPACK_IMPORTED_MODULE_1__[\"default\"] {\n /**\n * Takes the url of the current site and a target element where html elements will be appended.\n *\n * @param params - See type definition for initParameters.\n * @param Calibrator - An instance of the AudioCalibrator class, should not use AudioCalibrator directly, instead use an extended class available in /tasks/.\n * @param CalibratorInstance\n * @example\n */\n constructor(params, CalibratorInstance) {\n super(params);\n this.language = params?.language ?? 'en-US';\n this.siteUrl += '/listener?';\n this.ac = CalibratorInstance;\n this.result = null;\n this.debug = params?.debug ?? false;\n this.isSmartPhone = params?.isSmartPhone ?? false;\n this.calibrateSoundHz = params?.calibrateSoundHz ?? 48000;\n this.calibrateSoundSamplingDesiredBits = params?.calibrateSoundSamplingDesiredBits ?? 24;\n this.instructionDisplayId = params?.instructionDisplayId ?? '';\n this.soundSubtitleId = params?.soundSubtitleId ?? '';\n this.timeToCalibrateDisplay = params?.timeToCalibrateId ?? '';\n this.soundMessageId = params?.soundMessageId ?? '';\n this.titleDisplayId = params?.titleDisplayId ?? '';\n this.timeToCalibrate = params?.timeToCalibrate ?? 10;\n\n /* Set up callbacks that handle any events related to our peer object. */\n this.peer.on('open', this.#onPeerOpen);\n this.peer.on('connection', this.#onPeerConnection);\n this.peer.on('close', this.onPeerClose);\n this.peer.on('disconnected', this.#onPeerDisconnected);\n this.peer.on('error', this.#onPeerError);\n }\n /**\n * Async factory method that creates the Speaker object, and returns a promise that resolves to the result of the calibration.\n *\n * @param params - The parameters to be passed to the peer object.\n * @param Calibrator - The class that defines the calibration process.\n * @param CalibratorInstance\n * @param timeOut - The amount of time to wait before timing out the connection (in milliseconds).\n * @public\n * @example\n */\n static startCalibration = async (params, CalibratorInstance, timeOut = 180000) => {\n window.speaker = new Speaker(params, CalibratorInstance);\n const {speaker} = window;\n\n // wrap the calibration process in a promise so we can await it\n return new Promise((resolve, reject) => {\n // when a call is received\n speaker.peer.on('call', async call => {\n // Answer the call (one way)\n\n call.answer();\n speaker.#removeUIElems();\n speaker.#showSpinner();\n speaker.ac.createLocalAudio(document.getElementById(speaker.targetElement));\n // when we start receiving audio\n call.on('stream', async stream => {\n window.localStream = stream;\n window.localAudio.srcObject = stream;\n window.localAudio.autoplay = false;\n\n // if the sinkSamplingRate is not set sleep\n while (!speaker.ac.sampleRatesSet()) {\n console.log('SinkSamplingRate is undefined, sleeping');\n await (0,_utils__WEBPACK_IMPORTED_MODULE_2__.sleep)(1);\n }\n\n if (params.displayUpdate) {\n params.displayUpdate.style.display = '';\n }\n\n // resolve when we have a result\n speaker.result = await speaker.ac.startCalibration(\n stream,\n params.gainValues,\n params.ICalib,\n params.knownIR,\n params.microphoneName,\n params.calibrateSoundCheck,\n params.isSmartPhone,\n params.calibrateSoundBurstDb,\n params.calibrateSoundBurstRepeats,\n params.calibrateSoundBurstSec,\n params.calibrateSoundBurstsWarmup,\n params.calibrateSoundHz,\n params.calibrateSoundIRSec,\n params.calibrateSoundIIRSec,\n params.calibrateSound1000HzPreSec,\n params.calibrateSound1000HzSec,\n params.calibrateSound1000HzPostSec,\n params.calibrateSoundBackgroundSecs,\n params.calibrateSoundSmoothOctaves,\n params.calibrateSoundPowerBinDesiredSec,\n params.calibrateSoundPowerDbSDToleratedDb,\n params.micManufacturer,\n params.micSerialNumber,\n params.micModelNumber,\n params.micModelName,\n params.calibrateMicrophonesBool,\n params.authorEmails,\n params.webAudioDeviceNames,\n params.IDsToSaveInSoundProfileLibrary,\n params.restartButton,\n params.calibrateSoundLimit\n );\n speaker.#removeUIElems();\n resolve(speaker.result);\n });\n // if we do not receive a result within the timeout, reject\n setTimeout(() => {\n reject(\n new _peerErrors__WEBPACK_IMPORTED_MODULE_3__.CalibrationTimedOutError(\n `Calibration failed to produce a result after ${\n timeOut / 1000\n } seconds. Please try again.`\n )\n );\n }, timeOut);\n });\n });\n };\n\n static testIIR = async (params, CalibratorInstance, IIR, timeOut = 180000) => {\n window.speaker = new Speaker(params, CalibratorInstance);\n const {speaker} = window;\n\n // wrap the calibration process in a promise so we can await it\n return new Promise((resolve, reject) => {\n // when a call is received\n speaker.peer.on('call', async call => {\n // Answer the call (one way)\n call.answer();\n speaker.#removeUIElems();\n speaker.#showSpinner();\n speaker.ac.createLocalAudio(document.getElementById(speaker.targetElement));\n // when we start receiving audio\n call.on('stream', async stream => {\n window.localStream = stream;\n window.localAudio.srcObject = stream;\n window.localAudio.autoplay = false;\n\n // if the sinkSamplingRate is not set sleep\n while (!speaker.ac.sampleRatesSet()) {\n console.log('SinkSamplingRate is undefined, sleeping');\n await (0,_utils__WEBPACK_IMPORTED_MODULE_2__.sleep)(1);\n }\n // resolve when we have a result\n speaker.result = await speaker.ac.playMLSwithIIR(stream, IIR);\n speaker.#removeUIElems();\n resolve(speaker.result);\n });\n // if we do not receive a result within the timeout, reject\n setTimeout(() => {\n reject(\n new _peerErrors__WEBPACK_IMPORTED_MODULE_3__.CalibrationTimedOutError(\n `Calibration failed to produce a result after ${\n timeOut / 1000\n } seconds. Please try again.`\n )\n );\n }, timeOut);\n });\n });\n };\n\n /**\n * Called after the peer conncection has been opened.\n * Generates a QR code for the connection and displays it.\n *\n * @private\n * @example\n */\n #showQRCode = () => {\n // Get query string, the URL parameters to specify a Listener\n const queryStringParameters = {\n speakerPeerId: this.peer.id,\n isSmartPhone: this.isSmartPhone,\n calibrateSoundHz: this.calibrateSoundHz,\n calibrateSoundSamplingDesiredBits: this.calibrateSoundSamplingDesiredBits,\n lang: this.language,\n };\n const queryString = this.queryStringFromObject(queryStringParameters);\n const uri = this.siteUrl + queryString;\n if (this.isSmartPhone) {\n // Display QR code for the participant to scan\n const qrCanvas = document.createElement('canvas');\n qrCanvas.setAttribute('id', 'qrCanvas');\n console.log(uri);\n qrcode__WEBPACK_IMPORTED_MODULE_0__.toCanvas(qrCanvas, uri, error => {\n if (error) console.error(error);\n });\n const qrImage = new Image(400, 400);\n qrImage.setAttribute('id', 'compatibilityCheckQRImage');\n qrImage.style.zIndex = Infinity;\n qrImage.style.width = 400;\n qrImage.style.height = 400;\n qrImage.style.aspectRatio = 1;\n qrImage.src = qrCanvas.toDataURL();\n qrImage.style.maxHeight = '150px';\n qrImage.style.maxWidth = '150px';\n document.getElementById(this.targetElement).appendChild(qrImage);\n } else {\n // show the link to the user\n // If specified HTML Id is available, show QR code there\n if (document.getElementById(this.targetElement)) {\n // const linkTag = document.createElement('a');\n // linkTag.setAttribute('href', uri);\n // linkTag.innerHTML = 'Click here to start the calibration';\n // linkTag.target = '_blank';\n // document.getElementById(this.targetElement).appendChild(linkTag);\n // document.getElementById(this.targetElement).appendChild(qrCanvas);\n\n const proceedButton = document.createElement('button');\n proceedButton.setAttribute('id', 'calibrationProceedButton');\n proceedButton.setAttribute('class', 'btn btn-success');\n proceedButton.innerHTML = 'Proceed';\n proceedButton.onclick = () => {\n // open the link in a new tab\n window.open(uri, '_blank');\n // remove the button\n document.getElementById('calibrationProceedButton').remove();\n };\n document.getElementById(this.targetElement).appendChild(proceedButton);\n }\n }\n // or just print it to console\n console.log('TEST: Peer reachable at: ', uri);\n };\n\n #showSpinner = () => {\n const spinner = document.createElement('div');\n spinner.className = 'spinner-border ml-auto';\n spinner.role = 'status';\n spinner.ariaHidden = 'true';\n document.getElementById(this.targetElement).appendChild(spinner);\n\n // clear instructionDisplay\n const soundMessage = document.getElementById(this.soundMessageId);\n soundMessage.innerHTML = '';\n soundMessage.style.display = 'none';\n const instructionDisplay = document.getElementById(this.instructionDisplayId);\n const background = document.getElementById('background'); // todo: get background id from params\n const subtitle = document.getElementById(this.soundSubtitleId);\n if (subtitle) {\n subtitle.innerHTML = '';\n }\n if (instructionDisplay) {\n instructionDisplay.innerHTML = '';\n instructionDisplay.style.whiteSpace = 'nowrap';\n instructionDisplay.style.fontWeight = 'bold';\n instructionDisplay.style.width = 'fit-content';\n instructionDisplay.innerHTML = _dist_example_i18n__WEBPACK_IMPORTED_MODULE_4__.phrases.RC_soundRecording[this.language];\n let fontSize = 100;\n instructionDisplay.style.fontSize = fontSize + 'px';\n while (instructionDisplay.scrollWidth > background.scrollWidth * 0.9 && fontSize > 10) {\n fontSize--;\n instructionDisplay.style.fontSize = fontSize + 'px';\n }\n // const p = document.createElement('p');\n // // font size\n // p.style.fontSize = '1.1rem';\n // p.style.fontWeight = 'normal';\n // p.style.paddingTop = '20px';\n // const timeToCalibrateText = phrases.RC_howLongToCalibrate['en-US'];\n // p.innerHTML = timeToCalibrateText.replace('111', this.timeToCalibrate);\n // instructionDisplay.appendChild(p);\n }\n\n const timeToCalibrateDisplay = document.getElementById(this.timeToCalibrateDisplay);\n if (timeToCalibrateDisplay) {\n const timeToCalibrateText = _dist_example_i18n__WEBPACK_IMPORTED_MODULE_4__.phrases.RC_howLongToCalibrate[this.language];\n timeToCalibrateDisplay.innerHTML = timeToCalibrateText.replace('111', this.timeToCalibrate);\n timeToCalibrateDisplay.style.fontWeight = 'normal';\n timeToCalibrateDisplay.style.fontSize = '1rem';\n // timeToCalibrateDisplay.style.paddingTop = '20px';\n }\n\n // Update title - titleDisplayId\n const titleDisplay = document.getElementById(this.titleDisplayId);\n if (titleDisplay) {\n // replace 5 with 6\n titleDisplay.innerHTML = this.isSmartPhone\n ? titleDisplay.innerHTML.replace('2', '3')\n : titleDisplay.innerHTML.replace('4', '5');\n }\n };\n\n #removeUIElems = () => {\n const parent = document.getElementById(this.targetElement);\n while (parent.firstChild) {\n parent.firstChild.remove();\n }\n };\n\n /**\n * Called when the peer connection is opened.\n * Saves the peer id and calls the QR code generator.\n *\n * @param peerId - The peer id of the peer connection.\n * @param id\n * @private\n * @example\n */\n #onPeerOpen = id => {\n // Workaround for peer.reconnect deleting previous id\n if (id === null) {\n console.error('Received null id from peer open');\n this.peer.id = this.lastPeerId;\n } else {\n this.lastPeerId = this.peer.id;\n }\n\n if (id !== this.peer.id) {\n console.warn('DEBUG Check you assumption that id === this.peer.id');\n }\n\n this.#showQRCode();\n };\n\n /**\n * Called when the peer connection is established.\n * Enforces a single connection.\n *\n * @param connection - The connection object.\n * @private\n * @example\n */\n #onPeerConnection = connection => {\n // Allow only a single connection\n if (this.conn && this.conn.open) {\n connection.on('open', () => {\n connection.send('Already connected to another client');\n setTimeout(() => {\n connection.close();\n }, 500);\n });\n return;\n }\n\n this.conn = connection;\n console.log('Connected to: ', this.conn.peer);\n this.#ready();\n };\n\n /**\n * Called when the peer connection is closed.\n *\n * @private\n * @example\n */\n onPeerClose = () => {\n this.conn = null;\n console.log('Connection destroyed');\n };\n\n static closeConnection = () => {\n this.conn = null;\n console.log('Connection destroyed');\n };\n\n /**\n * Called when the peer connection is disconnected.\n * Attempts to reconnect.\n *\n * @private\n * @example\n */\n #onPeerDisconnected = () => {\n console.log('Connection lost. Please reconnect');\n\n // Workaround for peer.reconnect deleting previous id\n this.peer.id = this.lastPeerId;\n // eslint-disable-next-line no-underscore-dangle\n this.peer._lastServerId = this.lastPeerId;\n this.peer.reconnect();\n };\n\n /**\n * Called when the peer connection encounters an error.\n *\n * @param error\n * @private\n * @example\n */\n #onPeerError = error => {\n // TODO: check if this function is needed or not\n console.error(error);\n };\n\n /**\n * Called when data is received from the peer connection.\n *\n * @param data\n * @private\n * @example\n */\n #onIncomingData = data => {\n // enforce object type\n if (\n !Object.prototype.hasOwnProperty.call(data, 'name') ||\n !Object.prototype.hasOwnProperty.call(data, 'payload')\n ) {\n console.error('Received malformed data: ', data);\n return;\n }\n\n switch (data.name) {\n case 'samplingRate':\n this.ac.setSamplingRates(data.payload);\n break;\n case 'sampleSize':\n this.ac.setSampleSize(data.payload);\n break;\n case 'deviceType':\n this.ac.setDeviceType(data.payload);\n break;\n case 'deviceName':\n this.ac.setDeviceName(data.payload);\n break;\n case 'deviceInfo':\n this.ac.setDeviceInfo(data.payload);\n console.log('Received device info from listener: ', data.payload);\n break;\n case _peerErrors__WEBPACK_IMPORTED_MODULE_3__.UnsupportedDeviceError.name:\n case _peerErrors__WEBPACK_IMPORTED_MODULE_3__.MissingSpeakerIdError.name:\n throw data.payload;\n break;\n default:\n break;\n }\n };\n\n /**\n * Called when the peer connection is #ready.\n *\n * @private\n * @example\n */\n #ready = () => {\n // Perform callback with data\n this.conn.on('data', this.#onIncomingData);\n this.conn.on('close', () => {\n console.log('Connection reset<br>Awaiting connection...');\n this.conn = null;\n });\n };\n\n /** .\n * .\n * .\n * Debug method for downloading the recorded audio\n *\n * @public\n * @example\n */\n downloadData = () => {\n this.ac.downloadData();\n };\n}\n\n/* \nReferenced links:\nhttps://stackoverflow.com/questions/28016664/when-you-pass-this-as-an-argument/28016676#28016676\nhttps://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes\nhttps://stackoverflow.com/questions/879152/how-do-i-make-javascript-beep [3]\n*/\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (Speaker);\n\n\n//# sourceURL=webpack://speakerCalibrator/./src/peer-connection/speaker.js?");
|
|
811
811
|
|
|
812
812
|
/***/ }),
|
|
813
813
|
|
|
@@ -840,7 +840,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _aud
|
|
|
840
840
|
/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
|
|
841
841
|
|
|
842
842
|
"use strict";
|
|
843
|
-
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _myEventEmitter__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../myEventEmitter */ \"./src/myEventEmitter.js\");\n\n\n/**\n * @class provides a simple interface for recording audio from a microphone\n * using the Media Recorder API.\n */\nclass AudioRecorder extends _myEventEmitter__WEBPACK_IMPORTED_MODULE_0__[\"default\"] {\n /** @private */\n #mediaRecorder;\n\n /** @private */\n #recordedChunks = [];\n\n /** @private */\n #audioBlob;\n\n /** @private */\n #audioContext;\n\n /** @private */\n #recordedSignals = [];\n\n /**@private */\n #allHzUnfilteredRecordings = [];\n\n /**@private */\n #allBackgroundRecordings = [];\n\n /** @private */\n #allHzFilteredRecordings = [];\n\n /** @private */\n sinkSamplingRate;\n\n /** @private */\n sampleSize;\n\n /** @private */\n #allVolumeRecordings = [];\n\n /**\n * Decode the audio data from the recorded audio blob.\n *\n * @private\n * @example\n */\n #saveRecording = async (mode, checkRec) => {\n const arrayBuffer = await this.#audioBlob.arrayBuffer();\n const audioBuffer = await this.#audioContext.decodeAudioData(arrayBuffer);\n const data = audioBuffer.getChannelData(0);\n const dataArray = Array.from(data);\n\n console.log(`Decoded audio buffer with ${data.length} samples`);\n console.log(`Unfiltered recording should be of length: ${data.length}`);\n if (checkRec == 'loudest') {\n const uniqueSet = new Set(dataArray);\n const numberOfUniqueValues = uniqueSet.size;\n const squaredValues = dataArray.map(value => value * value);\n const sum_of_squares = squaredValues.reduce((total, value) => total + value, 0);\n const squared_mean = sum_of_squares / dataArray.length;\n const dbLevel = 20 * Math.log10(Math.sqrt(squared_mean));\n const roundedDbLevel = Math.round(dbLevel * 10) / 10;\n console.log(\n 'Loudest 1000-Hz recording: ' +\n roundedDbLevel +\n ' dB with ' +\n numberOfUniqueValues +\n ' unique values.'\n );\n } else if (checkRec == 'allhz') {\n const uniqueSet = new Set(dataArray);\n const numberOfUniqueValues = uniqueSet.size;\n const squaredValues = dataArray.map(value => value * value);\n const sum_of_squares = squaredValues.reduce((total, value) => total + value, 0);\n const squared_mean = sum_of_squares / dataArray.length;\n const dbLevel = 20 * Math.log10(Math.sqrt(squared_mean));\n const roundedDbLevel = Math.round(dbLevel * 10) / 10;\n console.log(\n 'All Hz Recording: ' +\n roundedDbLevel +\n ' dB with ' +\n numberOfUniqueValues +\n ' unique values.'\n );\n }\n if (mode === 'volume'){\n console.log('Saving 1000 Hz Recording to #allVolumeRecordings')\n this.#allVolumeRecordings.push(dataArray);\n }else if (mode ==='unfiltered'){\n console.log('Saving unfiltered all Hz recording to #allHzUnfilteredRecordings')\n this.#allHzUnfilteredRecordings.push(dataArray);\n }else if (mode ==='filtered'){\n console.log('Saving filtered all hz recording to #allHzFilteredRecordings')\n this.#allHzFilteredRecordings.push(dataArray);\n }else if (mode ==='background'){\n console.log('Saving background recording to #allBackgroundRecordings')\n this.#allBackgroundRecordings.push(dataArray);\n }\n\n };\n\n #saveFilteredRecording = async () => {\n const arrayBuffer = await this.#audioBlob.arrayBuffer();\n const audioBuffer = await this.#audioContext.decodeAudioData(arrayBuffer);\n const data = audioBuffer.getChannelData(0);\n\n console.log(`Decoded audio buffer with ${data.length} samples`);\n console.log(`Filtered recording should be of length: ${data.length}`);\n this.#allHzFilteredRecordings.push(Array.from(data));\n };\n\n /**\n * Event listener triggered when data is available in the media recorder.\n *\n * @private\n * @param e - The event object.\n * @example\n */\n #onRecorderDataAvailable = e => {\n if (e.data && e.data.size > 0) this.#recordedChunks.push(e.data);\n };\n\n /**\n * Method to create a media recorder object and set up event listeners.\n *\n * @private\n * @param stream - The stream of audio from the Listener.\n * @example\n */\n #setMediaRecorder = stream => {\n // Create a new MediaRecorder object\n this.#mediaRecorder = new MediaRecorder(stream);\n\n // Add event listeners\n this.#mediaRecorder.ondataavailable = e => this.#onRecorderDataAvailable(e);\n };\n\n #setAudioContext = () => {\n this.#audioContext = new (window.AudioContext ||\n window.webkitAudioContext ||\n window.audioContext)({\n sampleRate: this.sinkSamplingRate,\n sampleSize: this.sampleSize,\n //sampleRate: 96000\n });\n };\n\n /**\n * Public method to start the recording process.\n *\n * @param stream - The stream of audio from the Listener.\n * @example\n */\n startRecording = async stream => {\n try {\n // Create a fresh audio context\n this.#setAudioContext();\n // Set up media recorder if needed\n if (!this.#mediaRecorder) this.#setMediaRecorder(stream);\n // clear recorded chunks\n this.#recordedChunks = [];\n // start recording\n this.#mediaRecorder.start();\n } catch (error) {\n console.error('Error in startRecording:', error);\n // Handle the error as needed, e.g., throw it or perform error-specific actions\n }\n };\n\n /**\n * Method to stop the recording process.\n *\n * @public\n * @example\n */\n stopRecording = async (mode, checkRec) => {\n try {\n // Stop the media recorder, and wait for the data to be available\n await new Promise(resolve => {\n this.#mediaRecorder.onstop = () => {\n // when the stop event is triggered, resolve the promise\n this.#audioBlob = new Blob(this.#recordedChunks, {\n type: 'audio/wav; codecs=opus',\n });\n resolve(this.#audioBlob);\n };\n // call stop\n this.#mediaRecorder.stop();\n });\n // Now that we have data, save it\n await this.#saveRecording(mode, checkRec);\n } catch (error) {\n console.error('Error in stopRecording:', error);\n // Handle the error as needed, e.g., throw it or perform error-specific actions\n }\n };\n\n /** .\n * .\n * .\n * Public method to get the last recorded audio signal\n *\n * @returns\n * @example\n */\n getLastRecordedSignal = () => this.#recordedSignals[this.#recordedSignals.length - 1];\n\n /** .\n * .\n * .\n * Public method to get the last 1000hz recorded audio signal\n *\n * @returns\n * @example\n */\n getLastVolumeRecordedSignal = () => Array.from(this.#allVolumeRecordings[this.#allVolumeRecordings.length - 1]);\n\n /** .\n * .\n * .\n * Public method to get all the recorded audio signals\n *\n * @returns\n * @example\n */\n getAllRecordedSignals = () => this.#recordedSignals;\n\n /** .\n * .\n * .\n * Public method to get all the recorded audio signals\n *\n * @returns\n * @example\n */\n getAllVolumeRecordedSignals = () => this.#allVolumeRecordings;\n\n /** .\n * .\n * .\n * Public method to get all the recorded audio signals\n *\n * @returns\n * @example\n */\n getAllFilteredRecordedSignals = () => this.#allHzFilteredRecordings;\n\n /** .\n * .\n * .\n * Public method to get all the recorded audio signals\n *\n * @returns\n * @example\n */\n clearAllFilteredRecordedSignals = () => this.#allHzFilteredRecordings = [];\n\n /** .\n * .\n * .\n * Public method to clear last the recorded audio signals\n *\n * @returns\n * @example\n */\n clearLastFilteredRecordedSignals = () => this.#allHzFilteredRecordings.pop();\n\n /** .\n * .\n * .\n * Public method to get all the recorded audio signals for psd\n *\n * @returns\n * @example\n */\n getAllUnfilteredRecordedSignals = () => this.#allHzUnfilteredRecordings;\n\n /** .\n * .\n * .\n * Public method to get all the recorded audio signals\n *\n * @returns\n * @example\n */\n clearLastUnfilteredRecordedSignals = () => this.#allHzUnfilteredRecordings.pop();\n\n /** .\n * .\n * .\n * Public method to get all the recorded audio signals for psd\n *\n * @returns\n * @example\n */\n getAllBackgroundRecordings = () => this.#allBackgroundRecordings;\n\n /** .\n * .\n * .\n * Public method to set the sampling rate used by the capture device\n *\n * @param {Number} sinkSamplingRate - The sampling rate of the capture device\n * @example\n */\n setSinkSamplingRate = sinkSamplingRate => {\n this.sinkSamplingRate = sinkSamplingRate;\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (AudioRecorder);\n\n\n//# sourceURL=webpack://speakerCalibrator/./src/tasks/audioRecorder.js?");
|
|
843
|
+
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _myEventEmitter__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../myEventEmitter */ \"./src/myEventEmitter.js\");\n\n\n/**\n * @class provides a simple interface for recording audio from a microphone\n * using the Media Recorder API.\n */\nclass AudioRecorder extends _myEventEmitter__WEBPACK_IMPORTED_MODULE_0__[\"default\"] {\n /** @private */\n #mediaRecorder;\n\n /** @private */\n #recordedChunks = [];\n\n /** @private */\n #audioBlob;\n\n /** @private */\n #audioContext;\n\n /** @private */\n #recordedSignals = [];\n\n /**@private */\n #allHzUnfilteredRecordings = [];\n\n /**@private */\n #allBackgroundRecordings = [];\n\n /** @private */\n #allHzFilteredRecordings = [];\n\n /** @private */\n sinkSamplingRate;\n\n /** @private */\n sampleSize;\n\n /** @private */\n #allVolumeRecordings = [];\n\n /**\n * Decode the audio data from the recorded audio blob.\n *\n * @private\n * @example\n */\n #saveRecording = async (mode, checkRec) => {\n const arrayBuffer = await this.#audioBlob.arrayBuffer();\n console.log(arrayBuffer);\n const audioBuffer = await this.#audioContext.decodeAudioData(arrayBuffer);\n const data = audioBuffer.getChannelData(0);\n const dataArray = Array.from(data);\n\n console.log(`Decoded audio buffer with ${data.length} samples`);\n console.log(`Unfiltered recording should be of length: ${data.length}`);\n if (checkRec == 'loudest') {\n const uniqueSet = new Set(dataArray);\n const numberOfUniqueValues = uniqueSet.size;\n const squaredValues = dataArray.map(value => value * value);\n const sum_of_squares = squaredValues.reduce((total, value) => total + value, 0);\n const squared_mean = sum_of_squares / dataArray.length;\n const dbLevel = 20 * Math.log10(Math.sqrt(squared_mean));\n const roundedDbLevel = Math.round(dbLevel * 10) / 10;\n console.log(\n 'Loudest 1000-Hz recording: ' +\n roundedDbLevel +\n ' dB with ' +\n numberOfUniqueValues +\n ' unique values.'\n );\n } else if (checkRec == 'allhz') {\n const uniqueSet = new Set(dataArray);\n const numberOfUniqueValues = uniqueSet.size;\n const squaredValues = dataArray.map(value => value * value);\n const sum_of_squares = squaredValues.reduce((total, value) => total + value, 0);\n const squared_mean = sum_of_squares / dataArray.length;\n const dbLevel = 20 * Math.log10(Math.sqrt(squared_mean));\n const roundedDbLevel = Math.round(dbLevel * 10) / 10;\n console.log(\n 'All Hz Recording: ' +\n roundedDbLevel +\n ' dB with ' +\n numberOfUniqueValues +\n ' unique values.'\n );\n }\n if (mode === 'volume'){\n console.log('Saving 1000 Hz Recording to #allVolumeRecordings')\n this.#allVolumeRecordings.push(dataArray);\n }else if (mode ==='unfiltered'){\n console.log('Saving unfiltered all Hz recording to #allHzUnfilteredRecordings')\n this.#allHzUnfilteredRecordings.push(dataArray);\n }else if (mode ==='filtered'){\n console.log('Saving filtered all hz recording to #allHzFilteredRecordings')\n this.#allHzFilteredRecordings.push(dataArray);\n }else if (mode ==='background'){\n console.log('Saving background recording to #allBackgroundRecordings')\n this.#allBackgroundRecordings.push(dataArray);\n }\n\n };\n\n #saveFilteredRecording = async () => {\n const arrayBuffer = await this.#audioBlob.arrayBuffer();\n const audioBuffer = await this.#audioContext.decodeAudioData(arrayBuffer);\n const data = audioBuffer.getChannelData(0);\n\n console.log(`Decoded audio buffer with ${data.length} samples`);\n console.log(`Filtered recording should be of length: ${data.length}`);\n this.#allHzFilteredRecordings.push(Array.from(data));\n };\n\n /**\n * Event listener triggered when data is available in the media recorder.\n *\n * @private\n * @param e - The event object.\n * @example\n */\n #onRecorderDataAvailable = e => {\n if (e.data && e.data.size > 0) this.#recordedChunks.push(e.data);\n };\n\n /**\n * Method to create a media recorder object and set up event listeners.\n *\n * @private\n * @param stream - The stream of audio from the Listener.\n * @example\n */\n #setMediaRecorder = stream => {\n // Create a new MediaRecorder object\n this.#mediaRecorder = new MediaRecorder(stream);\n\n // Add event listeners\n this.#mediaRecorder.ondataavailable = e => this.#onRecorderDataAvailable(e);\n };\n\n #setAudioContext = () => {\n this.#audioContext = new (window.AudioContext ||\n window.webkitAudioContext ||\n window.audioContext)({\n sampleRate: this.sinkSamplingRate,\n sampleSize: this.sampleSize,\n //sampleRate: 96000\n });\n };\n\n /**\n * Public method to start the recording process.\n *\n * @param stream - The stream of audio from the Listener.\n * @example\n */\n startRecording = async stream => {\n try {\n // Create a fresh audio context\n this.#setAudioContext();\n // Set up media recorder if needed\n if (!this.#mediaRecorder) this.#setMediaRecorder(stream);\n // clear recorded chunks\n this.#recordedChunks = [];\n // start recording\n this.#mediaRecorder.start();\n } catch (error) {\n console.error('Error in startRecording:', error);\n // Handle the error as needed, e.g., throw it or perform error-specific actions\n }\n };\n\n /**\n * Method to stop the recording process.\n *\n * @public\n * @example\n */\n stopRecording = async (mode, checkRec) => {\n try {\n // Stop the media recorder, and wait for the data to be available\n await new Promise(resolve => {\n this.#mediaRecorder.onstop = () => {\n // when the stop event is triggered, resolve the promise\n this.#audioBlob = new Blob(this.#recordedChunks, {\n type: 'audio/wav; codecs=opus',\n });\n resolve(this.#audioBlob);\n };\n // call stop\n this.#mediaRecorder.stop();\n });\n // Now that we have data, save it\n await this.#saveRecording(mode, checkRec);\n } catch (error) {\n console.error('Error in stopRecording:', error);\n // Handle the error as needed, e.g., throw it or perform error-specific actions\n }\n };\n\n /** .\n * .\n * .\n * Public method to get the last recorded audio signal\n *\n * @returns\n * @example\n */\n getLastRecordedSignal = () => this.#recordedSignals[this.#recordedSignals.length - 1];\n\n /** .\n * .\n * .\n * Public method to get the last 1000hz recorded audio signal\n *\n * @returns\n * @example\n */\n getLastVolumeRecordedSignal = () => Array.from(this.#allVolumeRecordings[this.#allVolumeRecordings.length - 1]);\n\n /** .\n * .\n * .\n * Public method to get all the recorded audio signals\n *\n * @returns\n * @example\n */\n getAllRecordedSignals = () => this.#recordedSignals;\n\n /** .\n * .\n * .\n * Public method to get all the recorded audio signals\n *\n * @returns\n * @example\n */\n getAllVolumeRecordedSignals = () => this.#allVolumeRecordings;\n\n /** .\n * .\n * .\n * Public method to get all the recorded audio signals\n *\n * @returns\n * @example\n */\n getAllFilteredRecordedSignals = () => this.#allHzFilteredRecordings;\n\n /** .\n * .\n * .\n * Public method to get all the recorded audio signals\n *\n * @returns\n * @example\n */\n clearAllFilteredRecordedSignals = () => this.#allHzFilteredRecordings = [];\n\n /** .\n * .\n * .\n * Public method to clear last the recorded audio signals\n *\n * @returns\n * @example\n */\n clearLastFilteredRecordedSignals = () => this.#allHzFilteredRecordings.pop();\n\n /** .\n * .\n * .\n * Public method to get all the recorded audio signals for psd\n *\n * @returns\n * @example\n */\n getAllUnfilteredRecordedSignals = () => this.#allHzUnfilteredRecordings;\n\n /** .\n * .\n * .\n * Public method to get all the recorded audio signals\n *\n * @returns\n * @example\n */\n clearLastUnfilteredRecordedSignals = () => this.#allHzUnfilteredRecordings.pop();\n\n /** .\n * .\n * .\n * Public method to get all the recorded audio signals for psd\n *\n * @returns\n * @example\n */\n getAllBackgroundRecordings = () => this.#allBackgroundRecordings;\n\n /** .\n * .\n * .\n * Public method to set the sampling rate used by the capture device\n *\n * @param {Number} sinkSamplingRate - The sampling rate of the capture device\n * @example\n */\n setSinkSamplingRate = sinkSamplingRate => {\n this.sinkSamplingRate = sinkSamplingRate;\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (AudioRecorder);\n\n\n//# sourceURL=webpack://speakerCalibrator/./src/tasks/audioRecorder.js?");
|
|
844
844
|
|
|
845
845
|
/***/ }),
|
|
846
846
|
|
|
@@ -851,7 +851,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _myE
|
|
|
851
851
|
/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
|
|
852
852
|
|
|
853
853
|
"use strict";
|
|
854
|
-
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _audioCalibrator__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../audioCalibrator */ \"./src/tasks/audioCalibrator.js\");\n/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../utils */ \"./src/utils.js\");\n/* harmony import */ var _config_firebase__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../config/firebase */ \"./src/config/firebase.js\");\n/* harmony import */ var firebase_database__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! firebase/database */ \"./node_modules/firebase/database/dist/esm/index.esm.js\");\n/* harmony import */ var firebase_firestore__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! firebase/firestore */ \"./node_modules/firebase/firestore/dist/esm/index.esm.js\");\n\n\n\n\n\n\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 TAPER_SECS = 5;\n\n /** @private */\n offsetGainNode;\n\n /** @private */\n componentConvolution;\n\n /** @private */\n componentIROrigin = {\n Freq: [],\n Gain: [],\n };\n\n /** @private */\n systemConvolution;\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 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 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 recordingChecks = {\n volume: {},\n unfiltered: [],\n system: [],\n component: [],\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 /** @private */\n startTime;\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 /**generate string template that gets reevaluated as variable increases */\n generateTemplate = () => {\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 const reportWebAudioNames = `<br>${this.webAudioDeviceNames.loudspeakerText} <br> ${this.webAudioDeviceNames.microphoneText}`;\n const reportParameters = `<br> Sampling: Loudspeaker ${this.sourceSamplingRate} Hz, Microphone ${this.sinkSamplingRate} Hz, ${this.sampleSize} bits`;\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: center; margin-top:12px;\"><div style=\"width: 800px; height: 20px; border: 2px solid #000; border-radius: 10px;\"><div style=\"width: ${this.percent_complete}%; height: 100%; background-color: #00aaff; border-radius: 8px;\"></div></div></div>`;\n return reportWebAudioNames + reportParameters + MLSsd + systemSD + componentSD + 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('Get 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 const mls = this.#mls;\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 const num_periods = this.numMLSPerCapture + this.num_mls_to_skip;\n this.stepNum += 1;\n console.log('send impulse responses to server: ' + this.stepNum);\n this.status =\n `All Hz Calibration: computing the IIR...`.toString() + this.generateTemplate().toString();\n this.emit('update', {message: this.status});\n return this.pyServerAPI\n .getSystemInverseImpulseResponseWithRetry({\n payload: filteredComputedIRs.slice(0, this.numCaptures),\n mls,\n lowHz,\n highHz,\n iirLength,\n num_periods,\n sampleRate: this.sourceSamplingRate || 96000,\n calibrateSoundBurstDb: this._calibrateSoundBurstDb,\n })\n .then(res => {\n this.stepNum += 1;\n console.log('got impulse response ' + this.stepNum);\n this.incrementStatusBar();\n this.status =\n `All Hz Calibration: done computing the IIR...`.toString() +\n this.generateTemplate().toString();\n this.emit('update', {message: this.status});\n this.systemInvertedImpulseResponse = res['iir'];\n this.systemIR = res['ir'];\n this.systemConvolution = res['convolution'];\n this.systemInvertedImpulseResponseNoBandpass = res['iirNoBandpass'];\n\n // attenuate the system convolution if the amplitude is greater than this.calibrateSoundLimit\n // find max of absolute value of system convolution\n const max = Math.max(...this.systemConvolution.map(Math.abs));\n if (max > this.calibrateSoundLimit) {\n const gain = this.calibrateSoundLimit / max;\n // apply gain to system convolution\n this.systemConvolution = this.systemConvolution.map(value => value * gain);\n this.filteredMLSAttenuation.system = gain;\n }\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('Get component iir');\n const computedIRs = await Promise.all(this.impulseResponses);\n const filteredComputedIRs = computedIRs.filter(element => {\n return element != undefined;\n });\n const componentIRGains = this.componentIR['Gain'];\n const componentIRFreqs = this.componentIR['Freq'];\n const mls = this.#mls;\n const lowHz = this.#lowHz;\n const iirLength = this.iirLength;\n const irLength = this.irLength;\n const num_periods = this.numMLSPerCapture + this.num_mls_to_skip;\n const highHz = this.#highHz;\n this.stepNum += 1;\n console.log('send impulse responses to server: ' + this.stepNum);\n this.status =\n `All Hz Calibration: computing the IIR...`.toString() + this.generateTemplate().toString();\n this.emit('update', {message: this.status});\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 num_periods,\n sampleRate: this.sourceSamplingRate || 96000,\n calibrateSoundBurstDb: this._calibrateSoundBurstDb,\n irLength,\n calibrateSoundSmoothOctaves: this._calibrateSoundSmoothOctaves,\n })\n .then(res => {\n this.stepNum += 1;\n console.log('got impulse response ' + this.stepNum);\n this.incrementStatusBar();\n this.status =\n `All Hz Calibration: done computing the IIR...`.toString() +\n this.generateTemplate().toString();\n this.emit('update', {message: this.status});\n this.componentInvertedImpulseResponse = res['iir'];\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.componentConvolution = res['convolution'];\n this.componentInvertedImpulseResponseNoBandpass = res['iirNoBandpass'];\n this.componentIRInTimeDomain = res['irTime'];\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 = Math.max(...this.componentConvolution.map(Math.abs));\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.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('Get 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;\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 =\n `All Hz Calibration Step: computing the IR of the last recording...`.toString() +\n this.generateTemplate().toString();\n this.emit('update', {message: this.status});\n if (this.isCalibrating) return null;\n console.log(payload);\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,\n })\n .then(result => {\n if (result) {\n if (result['sd'] < this._calibrateSoundPowerDbSDToleratedDb) {\n this.recordingChecks['unfiltered'].push(result);\n this.impulseResponses.push(\n this.pyServerAPI\n .getImpulseResponse({\n sampleRate: this.sourceSamplingRate || 96000,\n payload,\n mls,\n P: this.#P, //get rid of this\n numPeriods: this.numMLSPerCapture,\n })\n .then(res => {\n if (this.numSuccessfulCaptured < this.numCaptures) {\n this.numSuccessfulCaptured += 1;\n console.log('num succ capt: ' + this.numSuccessfulCaptured);\n this.stepNum += 1;\n console.log('got impulse response ' + this.stepNum);\n this.incrementStatusBar();\n this.status =\n `All Hz Calibration: ${this.numSuccessfulCaptured}/${this.numCaptures} IRs computed...`.toString() +\n this.generateTemplate().toString();\n this.emit('update', {\n message: this.status,\n });\n this.autocorrelations.push(res['autocorrelation']);\n return res['ir'];\n }\n })\n .catch(err => {\n console.error(err);\n })\n );\n } else if (result['sd'] > this._calibrateSoundPowerDbSDToleratedDb) {\n this.clearLastUnfilteredRecordedSignals();\n console.log('unfiltered rec', this.getAllUnfilteredRecordedSignals.length);\n }\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 =\n `All Hz Calibration: sampling the calibration signal...`.toString() +\n `\\niteration ${this.stepNum}` +\n this.generateTemplate();\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.length / this.sourceSamplingRate) * this.numMLSPerCapture;\n time_to_wait = time_to_wait * 1.1;\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 * 1.1;\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 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 =\n `All Hz Calibration: waiting for the signal to stabilize...`.toString() +\n this.generateTemplate();\n this.emit('update', {\n message: this.status,\n });\n let number_of_bursts_to_skip = this.num_mls_to_skip;\n let time_to_sleep = 0;\n if (this.mode === 'unfiltered') {\n time_to_sleep = (this.#mls.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 = () => {\n console.log('after record');\n 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 {*} length\n * @param {*} phase\n * @returns\n * @example\n */\n static createSCurveBuffer = (length, phase) => {\n const curve = new Float32Array(length);\n let i;\n for (i = 0; i < length; i += 1) {\n // scale the curve to be between 0-1\n curve[i] = Math.sin((Math.PI * i) / length - phase) / 2 + 0.5;\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]) => {\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.addTimeStamp('Play unfiltered mls');\n this.calibrationNodes[0].start(0);\n this.status = ``;\n if (this.mode === 'unfiltered') {\n console.log('mls', this.#mls); // before multiplied by calibrateSoundBurstDb\n console.log('mls buffer view', this.#mlsBufferView); // after multiplied by calibrateSoundBurstDb\n console.log('play calibration audio ' + this.stepNum);\n this.status =\n `All Hz Calibration: playing the calibration tone...`.toString() +\n this.generateTemplate().toString();\n } else if (this.mode === 'filtered') {\n console.log('play convolved audio ' + this.stepNum);\n this.status =\n `All Hz Calibration: playing the convolved calibration tone...`.toString() +\n this.generateTemplate().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 =\n `All Hz Calibration: stopping the calibration tone...`.toString() +\n this.generateTemplate().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 < 1,\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 this.addTimeStamp('Play MLS with component IIR');\n this.soundCheck = 'component';\n if (this.isCalibrating) return null;\n await this.playMLSwithIIR(stream, this.#currentConvolution);\n this.stopCalibrationAudio();\n let component_conv_recs = this.getAllFilteredRecordedSignals();\n //remove the filteredMLSAttenuation from the recorded signals\n component_conv_recs = component_conv_recs.map(rec => {\n return rec.map(value => value / this.filteredMLSAttenuation.component);\n });\n let return_component_conv_rec = component_conv_recs[component_conv_recs.length - 1];\n this.clearAllFilteredRecordedSignals();\n // await this.checkPowerVariation(return_component_conv_rec);\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 this.addTimeStamp('Play MLS with system IIR');\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 //remove the filteredMLSAttenuation from the recorded signals\n system_conv_recs = system_conv_recs.map(rec => {\n return rec.map(value => value / this.filteredMLSAttenuation.system);\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 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('Get 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 =\n `All Hz Calibration: done computing the PSD graphs...`.toString() +\n this.generateTemplate().toString();\n this.emit('update', {message: this.status});\n return res;\n })\n .catch(err => {\n console.error(err);\n });\n\n this.addTimeStamp('Get 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.#highHz\n );\n\n this.SDofFilteredRange['component'] = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.standardDeviation)(filtered_psd);\n this.incrementStatusBar();\n this.status =\n `All Hz Calibration: done computing the PSD graphs...`.toString() +\n this.generateTemplate().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('Get 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 this.SDofFilteredRange['system'] = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.standardDeviation)(filtered_psd);\n this.incrementStatusBar();\n this.status =\n `All Hz Calibration: done computing the PSD graphs...`.toString() +\n this.generateTemplate().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('Get 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 =\n `All Hz Calibration: done computing the PSD graphs...`.toString() +\n this.generateTemplate().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('Get 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 =\n `All Hz Calibration: done computing the PSD graphs...`.toString() +\n this.generateTemplate().toString();\n this.emit('update', {message: this.status});\n return res;\n })\n .catch(err => {\n console.error(err);\n });\n\n this.addTimeStamp('Get PSD of mls sequence');\n if (this.isCalibrating) return null;\n let mls_psd = await this.pyServerAPI\n .getMLSPSDWithRetry({mls: this.#mlsBufferView, sampleRate: this.sourceSamplingRate || 96000})\n .then(res => {\n this.incrementStatusBar();\n this.status =\n `All Hz Calibration: done computing the PSD graphs...`.toString() +\n this.generateTemplate().toString();\n this.emit('update', {message: this.status});\n return res;\n })\n .catch(err => {\n console.error(err);\n });\n\n this.addTimeStamp('Get PSD of filered 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 =\n `All Hz Calibration: done computing the PSD graphs...`.toString() +\n this.generateTemplate().toString();\n this.emit('update', {message: this.status});\n return res;\n })\n .catch(err => {\n console.error(err);\n });\n\n this.addTimeStamp('Get PSD of filered 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 =\n `All Hz Calibration: done computing the PSD graphs...`.toString() +\n this.generateTemplate().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 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 convolution: this.systemConvolution,\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 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 convolution: this.componentConvolution,\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 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.addTimeStamp('Play MLS with component IIR');\n this.soundCheck = 'component';\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.addTimeStamp('Play MLS with system IIR');\n this.soundCheck = 'system';\n if (this.isCalibrating) return null;\n await this.playMLSwithIIR(stream, this.#currentConvolution);\n this.stopCalibrationAudio();\n }\n let conv_recs = this.getAllFilteredRecordedSignals();\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 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('Get 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 this.incrementStatusBar();\n this.status =\n `All Hz Calibration: done computing the PSD graphs...`.toString() +\n this.generateTemplate().toString();\n this.emit('update', {message: this.status});\n return res;\n })\n .catch(err => {\n console.error(err);\n });\n\n this.addTimeStamp('Get 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 console.log(interpolatedGain);\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 =\n `All Hz Calibration: done computing the PSD graphs...`.toString() +\n this.generateTemplate().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('Get 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 =\n `All Hz Calibration: done computing the PSD graphs...`.toString() +\n this.generateTemplate().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('Get 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 =\n `All Hz Calibration: done computing the PSD graphs...`.toString() +\n this.generateTemplate().toString();\n this.emit('update', {message: this.status});\n return res;\n })\n .catch(err => {\n console.error(err);\n });\n\n this.addTimeStamp('Get PSD of mls sequence');\n if (this.isCalibrating) return null;\n let mls_psd = await this.pyServerAPI\n .getMLSPSDWithRetry({\n mls: this.#mlsBufferView,\n sampleRate: this.sourceSamplingRate || 96000,\n })\n .then(res => {\n this.incrementStatusBar();\n this.status =\n `All Hz Calibration: done computing the PSD graphs...`.toString() +\n this.generateTemplate().toString();\n this.emit('update', {message: this.status});\n return res;\n })\n .catch(err => {\n console.error(err);\n });\n\n this.addTimeStamp('Get 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 =\n `All Hz Calibration: done computing the PSD graphs...`.toString() +\n this.generateTemplate().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 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 convolution: this.systemConvolution,\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 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 convolution: this.componentConvolution,\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('Get 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.#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['system'] = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.standardDeviation)(filtered_psd);\n this.SDofFilteredRange['unfiltered'] = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.standardDeviation)(mls_psd);\n this.incrementStatusBar();\n this.status =\n `All Hz Calibration: done computing the PSD graphs...`.toString() +\n this.generateTemplate().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('Get 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 =\n `All Hz Calibration: done computing the PSD graphs...`.toString() +\n this.generateTemplate().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('Get 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 =\n `All Hz Calibration: done computing the PSD graphs...`.toString() +\n this.generateTemplate().toString();\n this.emit('update', {message: this.status});\n return res;\n })\n .catch(err => {\n console.error(err);\n });\n\n this.addTimeStamp('Get PSD of mls sequence');\n if (this.isCalibrating) return null;\n let mls_psd = await this.pyServerAPI\n .getMLSPSDWithRetry({\n mls: this.#mlsBufferView,\n sampleRate: this.sourceSamplingRate || 96000,\n })\n .then(res => {\n this.incrementStatusBar();\n this.status =\n `All Hz Calibration: done computing the PSD graphs...`.toString() +\n this.generateTemplate().toString();\n this.emit('update', {message: this.status});\n return res;\n })\n .catch(err => {\n console.error(err);\n });\n\n this.addTimeStamp('Get 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 =\n `All Hz Calibration: done computing the PSD graphs...`.toString() +\n this.generateTemplate().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 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 convolution: this.systemConvolution,\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 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 convolution: this.componentConvolution,\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 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 = this._calibrateSoundBurstDb;\n this.addTimeStamp('Get MLS sequence');\n if (this.isCalibrating) return null;\n await this.pyServerAPI\n .getMLSWithRetry({length, calibrateSoundBurstDb})\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 =\n `All Hz Calibration: sampling the background noise...`.toString() +\n this.generateTemplate().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 await this.calibrationSteps(\n stream,\n this.#playCalibrationAudio, // play audio func (required)\n this.#createCalibrationNodeFromBuffer(this.#mlsBufferView), // before play func\n this.#awaitSignalOnset, // before record\n () => this.numSuccessfulCaptured < this.numCaptures, // loop while true\n this.#awaitDesiredMLSLength, // during record\n this.#afterMLSRecord, // after record\n this.mode,\n checkRec\n );\n this.stopCalibrationAudio();\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 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 =\n `All Hz Calibration: done computing the PSD graphs...`.toString() +\n this.generateTemplate().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 =\n `All Hz Calibration: done computing the PSD graphs...`.toString() +\n this.generateTemplate().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 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 psd: {\n unconv: {\n x: [],\n y: [],\n },\n conv: {\n x: [],\n y: [],\n },\n },\n },\n component: {\n iir: this.componentInvertedImpulseResponse,\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 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 = `All Hz Calibration: Finished`.toString() + this.generateTemplate().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 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 #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 }\n if (setItem.size === 0) {\n console.warn('The last capture failed, no recorded signal');\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 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 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 = lCalib => {\n console.log('Sending data to server');\n this.addTimeStamp('Send volume data to server');\n let left = this.calibrateSound1000HzPreSec;\n let right = this.calibrateSound1000HzPreSec + this.calibrateSound1000HzSec;\n console.log(\"truncated signal\",this.#getTruncatedSignal(left, right));\n console.log(\"complete signal\", this.getLastVolumeRecordedSignal());\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\n this.pyServerAPI\n .volumePowerCheck({\n payload: this.getLastVolumeRecordedSignal(),\n sampleRate: this.sourceSamplingRate || 96000,\n binDesiredSec: this._calibrateSoundPowerBinDesiredSec,\n preSec: this.calibrateSound1000HzPreSec,\n Sec: this.calibrateSound1000HzSec,\n })\n .then(res => {\n if (res && res['sd'] < this._calibrateSoundPowerDbSDToleratedDb) {\n this.recordingChecks['volume'][this.inDB] = res;\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\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 =\n `1000 Hz Calibration: Sound Level ${soundLevelToDiscard} dB`.toString() +\n this.generateTemplate().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 );\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 =\n `1000 Hz Calibration: Sound Level ${inDB} dB`.toString() +\n this.generateTemplate().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 );\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('Get Volume 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_4__.doc)(_config_firebase__WEBPACK_IMPORTED_MODULE_2__[\"default\"], 'Microphones', documentID);\n await (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.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_3__.set)((0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.ref)(_config_firebase__WEBPACK_IMPORTED_MODULE_2__[\"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_4__.collection)(_config_firebase__WEBPACK_IMPORTED_MODULE_2__[\"default\"], 'Microphones');\n const q = (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.query)(\n collectionRef,\n (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.where)('ID', '==', speakerID),\n (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.where)('lowercaseOEM', '==', OEM),\n (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.where)('isDefault', '==', isDefault)\n );\n const querySnapshot = await (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.getDocs)(q);\n // if exists return the linear field of the first document\n if (querySnapshot.size > 0) {\n return querySnapshot.docs[0].data().linear;\n }\n return null;\n };\n readFrqGain = async (speakerID, OEM) => {\n const dbRef = (0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.ref)(_config_firebase__WEBPACK_IMPORTED_MODULE_2__[\"default\"]);\n const snapshot = await (0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.get)((0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.child)(dbRef, `Microphone2/${OEM}/${speakerID}/linear`));\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_4__.collection)(_config_firebase__WEBPACK_IMPORTED_MODULE_2__[\"default\"], 'Microphones');\n const q = (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.query)(\n collectionRef,\n (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.where)('ID', '==', speakerID),\n (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.where)('lowercaseOEM', '==', OEM),\n (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.where)('isDefault', '==', isDefault)\n );\n const querySnapshot = await (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.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_3__.ref)(_config_firebase__WEBPACK_IMPORTED_MODULE_2__[\"default\"]);\n const snapshot = await (0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.get)((0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.child)(dbRef, `Microphone2/${OEM}/${speakerID}/Gain1000`));\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_4__.doc)(_config_firebase__WEBPACK_IMPORTED_MODULE_2__[\"default\"], 'Microphones', documentID);\n await (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.updateDoc)(docRef, {\n Gain1000: gain,\n });\n };\n\n writeGainat1000Hz = async (speakerID, gain, OEM) => {\n await (0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.set)((0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.ref)(_config_firebase__WEBPACK_IMPORTED_MODULE_2__[\"default\"], `Microphone2/${OEM}/${speakerID}/Gain1000`), gain);\n };\n\n writeIsSmartPhoneToFirestore = async (speakerID, isSmartPhone, OEM) => {\n const collectionRef = (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.collection)(_config_firebase__WEBPACK_IMPORTED_MODULE_2__[\"default\"], 'Microphones');\n const q = (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.query)(\n collectionRef,\n (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.where)('ID', '==', speakerID),\n (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.where)('lowercaseOEM', '==', OEM),\n (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.where)('isDefault', '==', true)\n );\n const querySnapshot = await (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.getDocs)(q);\n if (querySnapshot.size > 0) {\n const docRef = await (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.addDoc)(collectionRef, {isSmartPhone: isSmartPhone, isDefault: false});\n return docRef.id;\n } else {\n const docRef = await (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.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_3__.set)((0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.ref)(_config_firebase__WEBPACK_IMPORTED_MODULE_2__[\"default\"], `Microphone2/${OEM}/${speakerID}/isSmartPhone`), isSmartPhone);\n };\n\n writeMicrophoneInfoToFirestore = async (speakerID, micInfo, OEM, documentID) => {\n const docRef = (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.doc)(_config_firebase__WEBPACK_IMPORTED_MODULE_2__[\"default\"], 'Microphones', documentID);\n await (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.setDoc)(docRef, micInfo, {merge: true});\n };\n\n doesMicrophoneExistInFirestore = async (speakerID, OEM, documentID) => {\n const docRef = (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.doc)(_config_firebase__WEBPACK_IMPORTED_MODULE_2__[\"default\"], 'Microphone', OEM, speakerID, documentID);\n const docSnap = await (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.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_3__.ref)(_config_firebase__WEBPACK_IMPORTED_MODULE_2__[\"default\"]);\n const snapshot = await (0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.get)((0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.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_3__.ref)(_config_firebase__WEBPACK_IMPORTED_MODULE_2__[\"default\"]);\n const snapshot = await (0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.get)((0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.child)(dbRef, `Microphone2/${OEM}/${speakerID}/info`));\n if (!snapshot.exists()) {\n await (0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.set)((0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.ref)(_config_firebase__WEBPACK_IMPORTED_MODULE_2__[\"default\"], `Microphone2/${OEM}/${speakerID}/info`), micInfo);\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 // add time stamp\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 checkPowerVariation = async () => {\n let recordings = this.getAllFilteredRecordedSignals();\n // remove filteredMLSAttenuation from the recordings\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,\n })\n .then(result => {\n if (result) {\n if (result['sd'] > this._calibrateSoundPowerDbSDToleratedDb) {\n console.log('filtered recording sd too high');\n } else {\n this.recordingChecks[this.soundCheck].push(result);\n if (this.numSuccessfulCaptured < 1) {\n this.numSuccessfulCaptured += 1;\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 =\n `All Hz Calibration: ${this.numSuccessfulCaptured} recording of convolved MLS captured`.toString() +\n this.generateTemplate().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 = 0.1,\n _calibrateSoundBurstRepeats = 3,\n _calibrateSoundBurstSec = 1,\n _calibrateSoundBurstsWarmup = 0,\n _calibrateSoundHz = 48000,\n _calibrateSoundIIRSec = 0.2,\n _calibrateSoundIRSec = 0.2,\n calibrateSound1000HzPreSec = 3.5,\n calibrateSound1000HzSec = 1.0,\n calibrateSound1000HzPostSec = 0.5,\n _calibrateSoundBackgroundSecs = 0,\n _calibrateSoundSmoothOctaves = 0.33,\n _calibrateSoundPowerBinDesiredSec = 0.2,\n _calibrateSoundPowerDbSDToleratedDb = 1,\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 calibrateSoundLimit\n ) => {\n this.calibrateSoundLimit = calibrateSoundLimit;\n this._calibrateSoundBurstDb = _calibrateSoundBurstDb;\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 console.log('device info:', this.deviceInfo);\n this.numMLSPerCapture = _calibrateSoundBurstRepeats + 1;\n this.desired_time_per_mls = _calibrateSoundBurstSec;\n this.num_mls_to_skip = _calibrateSoundBurstsWarmup;\n this.desired_sampling_rate = _calibrateSoundHz;\n this._calibrateSoundBackgroundSecs = _calibrateSoundBackgroundSecs;\n this._calibrateSoundSmoothOctaves = _calibrateSoundSmoothOctaves;\n this._calibrateSoundPowerBinDesiredSec = _calibrateSoundPowerBinDesiredSec;\n this._calibrateSoundPowerDbSDToleratedDb = _calibrateSoundPowerDbSDToleratedDb;\n this.webAudioDeviceNames = webAudioDeviceNames;\n if (isSmartPhone) this.webAudioDeviceNames.microphone = this.deviceInfo.microphoneFromAPI;\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 //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 this.componentIR = await this.readFrqGainFromFirestore(ID, OEM, true).then(data => {\n return data;\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 (restartButton) {\n restartButton.style.display = '';\n restartButton.addEventListener('click', () => {\n this.stopCalibrationAudio();\n this.isCalibrating = true;\n restartButton.style.display = 'none';\n this.emit('update', {message: 'Restarting calibration...'});\n resolve('restart');\n });\n }\n let volumeResults = await this.startCalibrationVolume(\n stream,\n gainValues,\n lCalib,\n this.componentGainDBSPL\n );\n if (!volumeResults) return;\n let impulseResponseResults = await this.startCalibrationImpulseResponse(stream);\n if (!impulseResponseResults) return;\n impulseResponseResults['background_noise'] = this.background_noise;\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['component']['phase'] = this.componentIRPhase;\n total_results['system']['phase'] = this.systemIRPhase;\n total_results['qualityMetrics'] = this.SDofFilteredRange;\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\n//# sourceURL=webpack://speakerCalibrator/./src/tasks/combination/combination.js?");
|
|
854
|
+
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _audioCalibrator__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../audioCalibrator */ \"./src/tasks/audioCalibrator.js\");\n/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../utils */ \"./src/utils.js\");\n/* harmony import */ var _config_firebase__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../config/firebase */ \"./src/config/firebase.js\");\n/* harmony import */ var firebase_database__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! firebase/database */ \"./node_modules/firebase/database/dist/esm/index.esm.js\");\n/* harmony import */ var firebase_firestore__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! firebase/firestore */ \"./node_modules/firebase/firestore/dist/esm/index.esm.js\");\n\n\n\n\n\n\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 TAPER_SECS = 5;\n\n /** @private */\n offsetGainNode;\n\n /** @private */\n componentConvolution;\n\n /** @private */\n componentIROrigin = {\n Freq: [],\n Gain: [],\n };\n\n /** @private */\n systemConvolution;\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 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 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 recordingChecks = {\n volume: {},\n unfiltered: [],\n system: [],\n component: [],\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 /** @private */\n startTime;\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 /**generate string template that gets reevaluated as variable increases */\n generateTemplate = () => {\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 const reportWebAudioNames = `<br>${this.webAudioDeviceNames.loudspeakerText} <br> ${this.webAudioDeviceNames.microphoneText}`;\n const reportParameters = `<br> Sampling: Loudspeaker ${this.sourceSamplingRate} Hz, Microphone ${this.sinkSamplingRate} Hz, ${this.sampleSize} bits`;\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: center; margin-top:12px;\"><div style=\"width: 800px; height: 20px; border: 2px solid #000; border-radius: 10px;\"><div style=\"width: ${this.percent_complete}%; height: 100%; background-color: #00aaff; border-radius: 8px;\"></div></div></div>`;\n return reportWebAudioNames + reportParameters + MLSsd + systemSD + componentSD + 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('Get 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 const mls = this.#mls;\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 const num_periods = this.numMLSPerCapture + this.num_mls_to_skip;\n this.stepNum += 1;\n console.log('send impulse responses to server: ' + this.stepNum);\n this.status =\n `All Hz Calibration: computing the IIR...`.toString() + this.generateTemplate().toString();\n this.emit('update', {message: this.status});\n return this.pyServerAPI\n .getSystemInverseImpulseResponseWithRetry({\n payload: filteredComputedIRs.slice(0, this.numCaptures),\n mls,\n lowHz,\n highHz,\n iirLength,\n num_periods,\n sampleRate: this.sourceSamplingRate || 96000,\n calibrateSoundBurstDb: this._calibrateSoundBurstDb,\n })\n .then(res => {\n this.stepNum += 1;\n console.log('got impulse response ' + this.stepNum);\n this.incrementStatusBar();\n this.status =\n `All Hz Calibration: done computing the IIR...`.toString() +\n this.generateTemplate().toString();\n this.emit('update', {message: this.status});\n this.systemInvertedImpulseResponse = res['iir'];\n this.systemIR = res['ir'];\n this.systemConvolution = res['convolution'];\n this.systemInvertedImpulseResponseNoBandpass = res['iirNoBandpass'];\n\n // attenuate the system convolution if the amplitude is greater than this.calibrateSoundLimit\n // find max of absolute value of system convolution\n const max = Math.max(...this.systemConvolution.map(Math.abs));\n if (max > this.calibrateSoundLimit) {\n const gain = this.calibrateSoundLimit / max;\n // apply gain to system convolution\n this.systemConvolution = this.systemConvolution.map(value => value * gain);\n this.filteredMLSAttenuation.system = gain;\n }\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('Get component iir');\n const computedIRs = await Promise.all(this.impulseResponses);\n const filteredComputedIRs = computedIRs.filter(element => {\n return element != undefined;\n });\n const componentIRGains = this.componentIR['Gain'];\n const componentIRFreqs = this.componentIR['Freq'];\n const mls = this.#mls;\n const lowHz = this.#lowHz;\n const iirLength = this.iirLength;\n const irLength = this.irLength;\n const num_periods = this.numMLSPerCapture + this.num_mls_to_skip;\n const highHz = this.#highHz;\n this.stepNum += 1;\n console.log('send impulse responses to server: ' + this.stepNum);\n this.status =\n `All Hz Calibration: computing the IIR...`.toString() + this.generateTemplate().toString();\n this.emit('update', {message: this.status});\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 num_periods,\n sampleRate: this.sourceSamplingRate || 96000,\n calibrateSoundBurstDb: this._calibrateSoundBurstDb,\n irLength,\n calibrateSoundSmoothOctaves: this._calibrateSoundSmoothOctaves,\n })\n .then(res => {\n this.stepNum += 1;\n console.log('got impulse response ' + this.stepNum);\n this.incrementStatusBar();\n this.status =\n `All Hz Calibration: done computing the IIR...`.toString() +\n this.generateTemplate().toString();\n this.emit('update', {message: this.status});\n this.componentInvertedImpulseResponse = res['iir'];\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.componentConvolution = res['convolution'];\n this.componentInvertedImpulseResponseNoBandpass = res['iirNoBandpass'];\n this.componentIRInTimeDomain = res['irTime'];\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 = Math.max(...this.componentConvolution.map(Math.abs));\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.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('Get 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;\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 =\n `All Hz Calibration Step: computing the IR of the last recording...`.toString() +\n this.generateTemplate().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,\n })\n .then(result => {\n if (result) {\n if (result['sd'] < this._calibrateSoundPowerDbSDToleratedDb) {\n this.recordingChecks['unfiltered'].push(result);\n this.impulseResponses.push(\n this.pyServerAPI\n .getImpulseResponse({\n sampleRate: this.sourceSamplingRate || 96000,\n payload,\n mls,\n P: this.#P, //get rid of this\n numPeriods: this.numMLSPerCapture,\n })\n .then(res => {\n if (this.numSuccessfulCaptured < this.numCaptures) {\n this.numSuccessfulCaptured += 1;\n console.log('num succ capt: ' + this.numSuccessfulCaptured);\n this.stepNum += 1;\n console.log('got impulse response ' + this.stepNum);\n this.incrementStatusBar();\n this.status =\n `All Hz Calibration: ${this.numSuccessfulCaptured}/${this.numCaptures} IRs computed...`.toString() +\n this.generateTemplate().toString();\n this.emit('update', {\n message: this.status,\n });\n this.autocorrelations.push(res['autocorrelation']);\n return res['ir'];\n }\n })\n .catch(err => {\n console.error(err);\n })\n );\n } else if (result['sd'] > this._calibrateSoundPowerDbSDToleratedDb) {\n this.clearLastUnfilteredRecordedSignals();\n console.log('unfiltered rec', this.getAllUnfilteredRecordedSignals.length);\n }\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 =\n `All Hz Calibration: sampling the calibration signal...`.toString() +\n `\\niteration ${this.stepNum}` +\n this.generateTemplate();\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.length / this.sourceSamplingRate) * this.numMLSPerCapture;\n time_to_wait = time_to_wait * 1.1;\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 * 1.1;\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 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 =\n `All Hz Calibration: waiting for the signal to stabilize...`.toString() +\n this.generateTemplate();\n this.emit('update', {\n message: this.status,\n });\n let number_of_bursts_to_skip = this.num_mls_to_skip;\n let time_to_sleep = 0;\n if (this.mode === 'unfiltered') {\n time_to_sleep = (this.#mls.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 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 {*} length\n * @param {*} phase\n * @returns\n * @example\n */\n static createSCurveBuffer = (length, phase) => {\n const curve = new Float32Array(length);\n let i;\n for (i = 0; i < length; i += 1) {\n // scale the curve to be between 0-1\n curve[i] = Math.sin((Math.PI * i) / length - phase) / 2 + 0.5;\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]) => {\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.addTimeStamp('Play unfiltered mls');\n this.calibrationNodes[0].start(0);\n this.status = ``;\n if (this.mode === 'unfiltered') {\n console.log('mls', this.#mls); // before multiplied by calibrateSoundBurstDb\n console.log('mls buffer view', this.#mlsBufferView); // after multiplied by calibrateSoundBurstDb\n console.log('play calibration audio ' + this.stepNum);\n this.status =\n `All Hz Calibration: playing the calibration tone...`.toString() +\n this.generateTemplate().toString();\n } else if (this.mode === 'filtered') {\n console.log('play convolved audio ' + this.stepNum);\n this.status =\n `All Hz Calibration: playing the convolved calibration tone...`.toString() +\n this.generateTemplate().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 =\n `All Hz Calibration: stopping the calibration tone...`.toString() +\n this.generateTemplate().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 < 1,\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 this.addTimeStamp('Play MLS with component IIR');\n this.soundCheck = 'component';\n if (this.isCalibrating) return null;\n await this.playMLSwithIIR(stream, this.#currentConvolution);\n this.stopCalibrationAudio();\n let component_conv_recs = this.getAllFilteredRecordedSignals();\n //remove the filteredMLSAttenuation from the recorded signals\n component_conv_recs = component_conv_recs.map(rec => {\n return rec.map(value => value / this.filteredMLSAttenuation.component);\n });\n let return_component_conv_rec = component_conv_recs[component_conv_recs.length - 1];\n this.clearAllFilteredRecordedSignals();\n // await this.checkPowerVariation(return_component_conv_rec);\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 this.addTimeStamp('Play MLS with system IIR');\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 //remove the filteredMLSAttenuation from the recorded signals\n system_conv_recs = system_conv_recs.map(rec => {\n return rec.map(value => value / this.filteredMLSAttenuation.system);\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 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('Get 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 =\n `All Hz Calibration: done computing the PSD graphs...`.toString() +\n this.generateTemplate().toString();\n this.emit('update', {message: this.status});\n return res;\n })\n .catch(err => {\n console.error(err);\n });\n\n this.addTimeStamp('Get 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.#highHz\n );\n\n this.SDofFilteredRange['component'] = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.standardDeviation)(filtered_psd);\n this.incrementStatusBar();\n this.status =\n `All Hz Calibration: done computing the PSD graphs...`.toString() +\n this.generateTemplate().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('Get 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 this.SDofFilteredRange['system'] = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.standardDeviation)(filtered_psd);\n this.incrementStatusBar();\n this.status =\n `All Hz Calibration: done computing the PSD graphs...`.toString() +\n this.generateTemplate().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('Get 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 =\n `All Hz Calibration: done computing the PSD graphs...`.toString() +\n this.generateTemplate().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('Get 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 =\n `All Hz Calibration: done computing the PSD graphs...`.toString() +\n this.generateTemplate().toString();\n this.emit('update', {message: this.status});\n return res;\n })\n .catch(err => {\n console.error(err);\n });\n\n this.addTimeStamp('Get PSD of mls sequence');\n if (this.isCalibrating) return null;\n let mls_psd = await this.pyServerAPI\n .getMLSPSDWithRetry({mls: this.#mlsBufferView, sampleRate: this.sourceSamplingRate || 96000})\n .then(res => {\n this.incrementStatusBar();\n this.status =\n `All Hz Calibration: done computing the PSD graphs...`.toString() +\n this.generateTemplate().toString();\n this.emit('update', {message: this.status});\n return res;\n })\n .catch(err => {\n console.error(err);\n });\n\n this.addTimeStamp('Get PSD of filered 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 =\n `All Hz Calibration: done computing the PSD graphs...`.toString() +\n this.generateTemplate().toString();\n this.emit('update', {message: this.status});\n return res;\n })\n .catch(err => {\n console.error(err);\n });\n\n this.addTimeStamp('Get PSD of filered 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 =\n `All Hz Calibration: done computing the PSD graphs...`.toString() +\n this.generateTemplate().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 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 convolution: this.systemConvolution,\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 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 convolution: this.componentConvolution,\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 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.addTimeStamp('Play MLS with component IIR');\n this.soundCheck = 'component';\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.addTimeStamp('Play MLS with system IIR');\n this.soundCheck = 'system';\n if (this.isCalibrating) return null;\n await this.playMLSwithIIR(stream, this.#currentConvolution);\n this.stopCalibrationAudio();\n }\n let conv_recs = this.getAllFilteredRecordedSignals();\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 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('Get 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 this.incrementStatusBar();\n this.status =\n `All Hz Calibration: done computing the PSD graphs...`.toString() +\n this.generateTemplate().toString();\n this.emit('update', {message: this.status});\n return res;\n })\n .catch(err => {\n console.error(err);\n });\n\n this.addTimeStamp('Get 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 console.log(interpolatedGain);\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 =\n `All Hz Calibration: done computing the PSD graphs...`.toString() +\n this.generateTemplate().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('Get 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 =\n `All Hz Calibration: done computing the PSD graphs...`.toString() +\n this.generateTemplate().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('Get 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 =\n `All Hz Calibration: done computing the PSD graphs...`.toString() +\n this.generateTemplate().toString();\n this.emit('update', {message: this.status});\n return res;\n })\n .catch(err => {\n console.error(err);\n });\n\n this.addTimeStamp('Get PSD of mls sequence');\n if (this.isCalibrating) return null;\n let mls_psd = await this.pyServerAPI\n .getMLSPSDWithRetry({\n mls: this.#mlsBufferView,\n sampleRate: this.sourceSamplingRate || 96000,\n })\n .then(res => {\n this.incrementStatusBar();\n this.status =\n `All Hz Calibration: done computing the PSD graphs...`.toString() +\n this.generateTemplate().toString();\n this.emit('update', {message: this.status});\n return res;\n })\n .catch(err => {\n console.error(err);\n });\n\n this.addTimeStamp('Get 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 =\n `All Hz Calibration: done computing the PSD graphs...`.toString() +\n this.generateTemplate().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 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 convolution: this.systemConvolution,\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 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 convolution: this.componentConvolution,\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('Get 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.#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['system'] = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.standardDeviation)(filtered_psd);\n this.SDofFilteredRange['unfiltered'] = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.standardDeviation)(mls_psd);\n this.incrementStatusBar();\n this.status =\n `All Hz Calibration: done computing the PSD graphs...`.toString() +\n this.generateTemplate().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('Get 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 =\n `All Hz Calibration: done computing the PSD graphs...`.toString() +\n this.generateTemplate().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('Get 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 =\n `All Hz Calibration: done computing the PSD graphs...`.toString() +\n this.generateTemplate().toString();\n this.emit('update', {message: this.status});\n return res;\n })\n .catch(err => {\n console.error(err);\n });\n\n this.addTimeStamp('Get PSD of mls sequence');\n if (this.isCalibrating) return null;\n let mls_psd = await this.pyServerAPI\n .getMLSPSDWithRetry({\n mls: this.#mlsBufferView,\n sampleRate: this.sourceSamplingRate || 96000,\n })\n .then(res => {\n this.incrementStatusBar();\n this.status =\n `All Hz Calibration: done computing the PSD graphs...`.toString() +\n this.generateTemplate().toString();\n this.emit('update', {message: this.status});\n return res;\n })\n .catch(err => {\n console.error(err);\n });\n\n this.addTimeStamp('Get 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 =\n `All Hz Calibration: done computing the PSD graphs...`.toString() +\n this.generateTemplate().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 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 convolution: this.systemConvolution,\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 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 convolution: this.componentConvolution,\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 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 = this._calibrateSoundBurstDb;\n this.addTimeStamp('Get MLS sequence');\n if (this.isCalibrating) return null;\n await this.pyServerAPI\n .getMLSWithRetry({length, calibrateSoundBurstDb})\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 =\n `All Hz Calibration: sampling the background noise...`.toString() +\n this.generateTemplate().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 await this.calibrationSteps(\n stream,\n this.#playCalibrationAudio, // play audio func (required)\n this.#createCalibrationNodeFromBuffer(this.#mlsBufferView), // before play func\n this.#awaitSignalOnset, // before record\n () => this.numSuccessfulCaptured < this.numCaptures, // loop while true\n this.#awaitDesiredMLSLength, // during record\n this.#afterMLSRecord, // after record\n this.mode,\n checkRec\n );\n this.stopCalibrationAudio();\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 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 =\n `All Hz Calibration: done computing the PSD graphs...`.toString() +\n this.generateTemplate().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 =\n `All Hz Calibration: done computing the PSD graphs...`.toString() +\n this.generateTemplate().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 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 psd: {\n unconv: {\n x: [],\n y: [],\n },\n conv: {\n x: [],\n y: [],\n },\n },\n },\n component: {\n iir: this.componentInvertedImpulseResponse,\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 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 = `All Hz Calibration: Finished`.toString() + this.generateTemplate().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 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 #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 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 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 this.addTimeStamp('Send volume data to server');\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\n await this.pyServerAPI\n .volumePowerCheck({\n payload: this.getLastVolumeRecordedSignal(),\n sampleRate: this.sourceSamplingRate || 96000,\n binDesiredSec: this._calibrateSoundPowerBinDesiredSec,\n preSec: this.calibrateSound1000HzPreSec,\n Sec: this.calibrateSound1000HzSec,\n })\n .then(res => {\n if (res && res['sd'] < this._calibrateSoundPowerDbSDToleratedDb) {\n this.recordingChecks['volume'][this.inDB] = res;\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\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 =\n `1000 Hz Calibration: Sound Level ${soundLevelToDiscard} dB`.toString() +\n this.generateTemplate().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 );\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 =\n `1000 Hz Calibration: Sound Level ${inDB} dB`.toString() +\n this.generateTemplate().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 );\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('Get Volume 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_4__.doc)(_config_firebase__WEBPACK_IMPORTED_MODULE_2__[\"default\"], 'Microphones', documentID);\n await (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.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_3__.set)((0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.ref)(_config_firebase__WEBPACK_IMPORTED_MODULE_2__[\"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_4__.collection)(_config_firebase__WEBPACK_IMPORTED_MODULE_2__[\"default\"], 'Microphones');\n const q = (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.query)(\n collectionRef,\n (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.where)('ID', '==', speakerID),\n (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.where)('lowercaseOEM', '==', OEM),\n (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.where)('isDefault', '==', isDefault)\n );\n const querySnapshot = await (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.getDocs)(q);\n // if exists return the linear field of the first document\n if (querySnapshot.size > 0) {\n return querySnapshot.docs[0].data().linear;\n }\n return null;\n };\n readFrqGain = async (speakerID, OEM) => {\n const dbRef = (0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.ref)(_config_firebase__WEBPACK_IMPORTED_MODULE_2__[\"default\"]);\n const snapshot = await (0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.get)((0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.child)(dbRef, `Microphone2/${OEM}/${speakerID}/linear`));\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_4__.collection)(_config_firebase__WEBPACK_IMPORTED_MODULE_2__[\"default\"], 'Microphones');\n const q = (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.query)(\n collectionRef,\n (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.where)('ID', '==', speakerID),\n (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.where)('lowercaseOEM', '==', OEM),\n (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.where)('isDefault', '==', isDefault)\n );\n const querySnapshot = await (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.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_3__.ref)(_config_firebase__WEBPACK_IMPORTED_MODULE_2__[\"default\"]);\n const snapshot = await (0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.get)((0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.child)(dbRef, `Microphone2/${OEM}/${speakerID}/Gain1000`));\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_4__.doc)(_config_firebase__WEBPACK_IMPORTED_MODULE_2__[\"default\"], 'Microphones', documentID);\n await (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.updateDoc)(docRef, {\n Gain1000: gain,\n });\n };\n\n writeGainat1000Hz = async (speakerID, gain, OEM) => {\n await (0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.set)((0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.ref)(_config_firebase__WEBPACK_IMPORTED_MODULE_2__[\"default\"], `Microphone2/${OEM}/${speakerID}/Gain1000`), gain);\n };\n\n writeIsSmartPhoneToFirestore = async (speakerID, isSmartPhone, OEM) => {\n const collectionRef = (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.collection)(_config_firebase__WEBPACK_IMPORTED_MODULE_2__[\"default\"], 'Microphones');\n const q = (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.query)(\n collectionRef,\n (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.where)('ID', '==', speakerID),\n (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.where)('lowercaseOEM', '==', OEM),\n (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.where)('isDefault', '==', true)\n );\n const querySnapshot = await (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.getDocs)(q);\n if (querySnapshot.size > 0) {\n const docRef = await (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.addDoc)(collectionRef, {isSmartPhone: isSmartPhone, isDefault: false});\n return docRef.id;\n } else {\n const docRef = await (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.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_3__.set)((0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.ref)(_config_firebase__WEBPACK_IMPORTED_MODULE_2__[\"default\"], `Microphone2/${OEM}/${speakerID}/isSmartPhone`), isSmartPhone);\n };\n\n writeMicrophoneInfoToFirestore = async (speakerID, micInfo, OEM, documentID) => {\n const docRef = (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.doc)(_config_firebase__WEBPACK_IMPORTED_MODULE_2__[\"default\"], 'Microphones', documentID);\n await (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.setDoc)(docRef, micInfo, {merge: true});\n };\n\n doesMicrophoneExistInFirestore = async (speakerID, OEM, documentID) => {\n const docRef = (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.doc)(_config_firebase__WEBPACK_IMPORTED_MODULE_2__[\"default\"], 'Microphone', OEM, speakerID, documentID);\n const docSnap = await (0,firebase_firestore__WEBPACK_IMPORTED_MODULE_4__.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_3__.ref)(_config_firebase__WEBPACK_IMPORTED_MODULE_2__[\"default\"]);\n const snapshot = await (0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.get)((0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.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_3__.ref)(_config_firebase__WEBPACK_IMPORTED_MODULE_2__[\"default\"]);\n const snapshot = await (0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.get)((0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.child)(dbRef, `Microphone2/${OEM}/${speakerID}/info`));\n if (!snapshot.exists()) {\n await (0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.set)((0,firebase_database__WEBPACK_IMPORTED_MODULE_3__.ref)(_config_firebase__WEBPACK_IMPORTED_MODULE_2__[\"default\"], `Microphone2/${OEM}/${speakerID}/info`), micInfo);\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 // add time stamp\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 checkPowerVariation = async () => {\n let recordings = this.getAllFilteredRecordedSignals();\n // remove filteredMLSAttenuation from the recordings\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,\n })\n .then(result => {\n if (result) {\n if (result['sd'] > this._calibrateSoundPowerDbSDToleratedDb) {\n console.log('filtered recording sd too high');\n } else {\n this.recordingChecks[this.soundCheck].push(result);\n if (this.numSuccessfulCaptured < 1) {\n this.numSuccessfulCaptured += 1;\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 =\n `All Hz Calibration: ${this.numSuccessfulCaptured} recording of convolved MLS captured`.toString() +\n this.generateTemplate().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 = 0.1,\n _calibrateSoundBurstRepeats = 3,\n _calibrateSoundBurstSec = 1,\n _calibrateSoundBurstsWarmup = 0,\n _calibrateSoundHz = 48000,\n _calibrateSoundIIRSec = 0.2,\n _calibrateSoundIRSec = 0.2,\n calibrateSound1000HzPreSec = 3.5,\n calibrateSound1000HzSec = 1.0,\n calibrateSound1000HzPostSec = 0.5,\n _calibrateSoundBackgroundSecs = 0,\n _calibrateSoundSmoothOctaves = 0.33,\n _calibrateSoundPowerBinDesiredSec = 0.2,\n _calibrateSoundPowerDbSDToleratedDb = 1,\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 calibrateSoundLimit\n ) => {\n this.calibrateSoundLimit = calibrateSoundLimit;\n this._calibrateSoundBurstDb = _calibrateSoundBurstDb;\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.numMLSPerCapture = _calibrateSoundBurstRepeats + 1;\n this.desired_time_per_mls = _calibrateSoundBurstSec;\n this.num_mls_to_skip = _calibrateSoundBurstsWarmup;\n this.desired_sampling_rate = _calibrateSoundHz;\n this._calibrateSoundBackgroundSecs = _calibrateSoundBackgroundSecs;\n this._calibrateSoundSmoothOctaves = _calibrateSoundSmoothOctaves;\n this._calibrateSoundPowerBinDesiredSec = _calibrateSoundPowerBinDesiredSec;\n this._calibrateSoundPowerDbSDToleratedDb = _calibrateSoundPowerDbSDToleratedDb;\n this.webAudioDeviceNames = webAudioDeviceNames;\n if (isSmartPhone) this.webAudioDeviceNames.microphone = this.deviceInfo.microphoneFromAPI;\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 //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 this.componentIR = await this.readFrqGainFromFirestore(ID, OEM, true).then(data => {\n return data;\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 (restartButton) {\n restartButton.style.display = '';\n restartButton.addEventListener('click', () => {\n this.stopCalibrationAudio();\n this.isCalibrating = true;\n restartButton.style.display = 'none';\n this.emit('update', {message: 'Restarting calibration...'});\n resolve('restart');\n });\n }\n let volumeResults = await this.startCalibrationVolume(\n stream,\n gainValues,\n lCalib,\n this.componentGainDBSPL\n );\n if (!volumeResults) return;\n let impulseResponseResults = await this.startCalibrationImpulseResponse(stream);\n if (!impulseResponseResults) return;\n impulseResponseResults['background_noise'] = this.background_noise;\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['component']['phase'] = this.componentIRPhase;\n total_results['system']['phase'] = this.systemIRPhase;\n total_results['qualityMetrics'] = this.SDofFilteredRange;\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\n//# sourceURL=webpack://speakerCalibrator/./src/tasks/combination/combination.js?");
|
|
855
855
|
|
|
856
856
|
/***/ }),
|
|
857
857
|
|
package/package.json
CHANGED
|
@@ -190,8 +190,9 @@ class Listener extends AudioPeer {
|
|
|
190
190
|
|
|
191
191
|
applyHQTrackConstraints = async stream => {
|
|
192
192
|
// Contraint the incoming audio to the sampling rate we want
|
|
193
|
-
|
|
193
|
+
stream.getAudioTracks().forEach(track => {console.log(track, track.enabled)});
|
|
194
194
|
const track = stream.getAudioTracks()[0];
|
|
195
|
+
console.log(track);
|
|
195
196
|
const capabilities = track.getCapabilities();
|
|
196
197
|
|
|
197
198
|
this.displayUpdate(
|
|
@@ -73,7 +73,6 @@ class Speaker extends AudioPeer {
|
|
|
73
73
|
speaker.ac.createLocalAudio(document.getElementById(speaker.targetElement));
|
|
74
74
|
// when we start receiving audio
|
|
75
75
|
call.on('stream', async stream => {
|
|
76
|
-
console.log(stream);
|
|
77
76
|
window.localStream = stream;
|
|
78
77
|
window.localAudio.srcObject = stream;
|
|
79
78
|
window.localAudio.autoplay = false;
|
|
@@ -46,6 +46,7 @@ class AudioRecorder extends MyEventEmitter {
|
|
|
46
46
|
*/
|
|
47
47
|
#saveRecording = async (mode, checkRec) => {
|
|
48
48
|
const arrayBuffer = await this.#audioBlob.arrayBuffer();
|
|
49
|
+
console.log(arrayBuffer);
|
|
49
50
|
const audioBuffer = await this.#audioContext.decodeAudioData(arrayBuffer);
|
|
50
51
|
const data = audioBuffer.getChannelData(0);
|
|
51
52
|
const dataArray = Array.from(data);
|
|
@@ -501,7 +501,6 @@ class Combination extends AudioCalibrator {
|
|
|
501
501
|
this.generateTemplate().toString();
|
|
502
502
|
this.emit('update', {message: this.status});
|
|
503
503
|
if (this.isCalibrating) return null;
|
|
504
|
-
console.log(payload);
|
|
505
504
|
await this.pyServerAPI
|
|
506
505
|
.allHzPowerCheck({
|
|
507
506
|
payload,
|
|
@@ -646,9 +645,9 @@ class Combination extends AudioCalibrator {
|
|
|
646
645
|
*
|
|
647
646
|
* @example
|
|
648
647
|
*/
|
|
649
|
-
#afterMLSRecord = () => {
|
|
648
|
+
#afterMLSRecord = async () => {
|
|
650
649
|
console.log('after record');
|
|
651
|
-
this.sendRecordingToServerForProcessing();
|
|
650
|
+
await this.sendRecordingToServerForProcessing();
|
|
652
651
|
};
|
|
653
652
|
|
|
654
653
|
#afterMLSwIIRRecord = async () => {
|
|
@@ -1859,9 +1858,17 @@ class Combination extends AudioCalibrator {
|
|
|
1859
1858
|
'The last capture failed, all recorded signal is zero',
|
|
1860
1859
|
this.getAllVolumeRecordedSignals()
|
|
1861
1860
|
);
|
|
1861
|
+
this.stopCalibrationAudio();
|
|
1862
|
+
this.isCalibrating = true;
|
|
1863
|
+
// restartButton.style.display = 'none';
|
|
1864
|
+
this.emit('update', {message: 'Connection failed, hit restart button to reconnect'});
|
|
1862
1865
|
}
|
|
1863
1866
|
if (setItem.size === 0) {
|
|
1864
1867
|
console.warn('The last capture failed, no recorded signal');
|
|
1868
|
+
this.stopCalibrationAudio();
|
|
1869
|
+
this.isCalibrating = true;
|
|
1870
|
+
// restartButton.style.display = 'none';
|
|
1871
|
+
this.emit('update', {message: 'Connection failed, hit restart button to reconnect'});
|
|
1865
1872
|
}
|
|
1866
1873
|
};
|
|
1867
1874
|
checkResult(result);
|
|
@@ -1939,13 +1946,11 @@ class Combination extends AudioCalibrator {
|
|
|
1939
1946
|
}
|
|
1940
1947
|
};
|
|
1941
1948
|
|
|
1942
|
-
#sendToServerForProcessing = lCalib => {
|
|
1949
|
+
#sendToServerForProcessing = async (lCalib) => {
|
|
1943
1950
|
console.log('Sending data to server');
|
|
1944
1951
|
this.addTimeStamp('Send volume data to server');
|
|
1945
1952
|
let left = this.calibrateSound1000HzPreSec;
|
|
1946
1953
|
let right = this.calibrateSound1000HzPreSec + this.calibrateSound1000HzSec;
|
|
1947
|
-
console.log("truncated signal",this.#getTruncatedSignal(left, right));
|
|
1948
|
-
console.log("complete signal", this.getLastVolumeRecordedSignal());
|
|
1949
1954
|
if (this.isCalibrating) return null;
|
|
1950
1955
|
this.pyServerAPI
|
|
1951
1956
|
.getVolumeCalibration({
|
|
@@ -1965,7 +1970,7 @@ class Combination extends AudioCalibrator {
|
|
|
1965
1970
|
console.warn(err);
|
|
1966
1971
|
});
|
|
1967
1972
|
|
|
1968
|
-
this.pyServerAPI
|
|
1973
|
+
await this.pyServerAPI
|
|
1969
1974
|
.volumePowerCheck({
|
|
1970
1975
|
payload: this.getLastVolumeRecordedSignal(),
|
|
1971
1976
|
sampleRate: this.sourceSamplingRate || 96000,
|
|
@@ -2408,7 +2413,6 @@ class Combination extends AudioCalibrator {
|
|
|
2408
2413
|
this.calibrateSound1000HzPostSec = calibrateSound1000HzPostSec;
|
|
2409
2414
|
this.iirLength = Math.floor(_calibrateSoundIIRSec * this.sourceSamplingRate);
|
|
2410
2415
|
this.irLength = Math.floor(_calibrateSoundIRSec * this.sourceSamplingRate);
|
|
2411
|
-
console.log('device info:', this.deviceInfo);
|
|
2412
2416
|
this.numMLSPerCapture = _calibrateSoundBurstRepeats + 1;
|
|
2413
2417
|
this.desired_time_per_mls = _calibrateSoundBurstSec;
|
|
2414
2418
|
this.num_mls_to_skip = _calibrateSoundBurstsWarmup;
|