speaker-calibration 2.2.43 → 2.2.45

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.
Files changed (119) hide show
  1. package/.eslintignore +71 -71
  2. package/.eslintrc.json +40 -40
  3. package/.gitignore +81 -0
  4. package/.prettierignore +69 -69
  5. package/.prettierrc +14 -14
  6. package/LICENSE +20 -20
  7. package/README.md +133 -133
  8. package/__mocks__/fileMock.js +1 -1
  9. package/__mocks__/styleMock.js +1 -1
  10. package/babel.config.js +3 -3
  11. package/coverage/clover.xml +71 -71
  12. package/coverage/coverage-final.json +224 -224
  13. package/coverage/lcov-report/PythonServerInterface.js.html +265 -265
  14. package/coverage/lcov-report/base.css +354 -354
  15. package/coverage/lcov-report/block-navigation.js +82 -82
  16. package/coverage/lcov-report/index.html +123 -123
  17. package/coverage/lcov-report/prettify.css +101 -101
  18. package/coverage/lcov-report/prettify.js +937 -937
  19. package/coverage/lcov-report/sorter.js +189 -189
  20. package/coverage/lcov-report/src/index.html +121 -121
  21. package/coverage/lcov-report/src/server/PythonServerInterface.js.html +268 -268
  22. package/coverage/lcov-report/src/server/index.html +123 -123
  23. package/coverage/lcov-report/src/tasks/audioCalibrator.js.html +499 -499
  24. package/coverage/lcov-report/src/tasks/audioRecorder.js.html +412 -412
  25. package/coverage/lcov-report/src/tasks/index.html +143 -143
  26. package/coverage/lcov-report/src/tasks/volume/index.html +123 -123
  27. package/coverage/lcov-report/src/tasks/volume/volume.js.html +409 -409
  28. package/coverage/lcov-report/src/utils.js.html +172 -172
  29. package/coverage/lcov.info +91 -91
  30. package/dist/example/fetch-languages-sheets.js +77 -77
  31. package/dist/example/i18n.js +6822 -6822
  32. package/dist/example/index.html +47 -47
  33. package/dist/example/listener.html +61 -61
  34. package/dist/example/listener.js +97 -97
  35. package/dist/example/server.js +51 -51
  36. package/dist/example/speaker.html +139 -139
  37. package/dist/example/speakerUI.js +270 -270
  38. package/dist/example/styles.css +63 -63
  39. package/dist/main.js +17 -17
  40. package/dist/mlsGen.js +6814 -6814
  41. package/dist/mlsGen.wasm +0 -0
  42. package/dist/package-lock.json +1018 -1018
  43. package/dist/package.json +18 -18
  44. package/doc/AudioCalibrator.html +417 -417
  45. package/doc/AudioPeer.html +251 -251
  46. package/doc/AudioRecorder.html +195 -195
  47. package/doc/ImpulseResponse.html +215 -215
  48. package/doc/Listener.html +308 -308
  49. package/doc/MlsGenInterface.html +226 -226
  50. package/doc/MyEventEmitter.html +274 -274
  51. package/doc/PythonServerAPI.html +109 -109
  52. package/doc/Speaker.html +276 -276
  53. package/doc/Takes%20a%20target%20element%20where%20html%20elements%20will%20be%20appended..html +128 -128
  54. package/doc/Takes%20the%20url%20of%20the%20current%20site%0Aand%20a%20target%20element%20where%20html%20elements%20will%20be%20appended..html +138 -138
  55. package/doc/Takes%20the%20url%20of%20the%20current%20site%20and%20a%20target%20element%20where%20html%20elements%20will%20be%20appended..html +137 -137
  56. package/doc/Volume.html +88 -88
  57. package/doc/audioCalibrator.js.html +179 -179
  58. package/doc/audioPeer.js.html +175 -175
  59. package/doc/audioRecorder.js.html +163 -163
  60. package/doc/creates%20a%20new%20AudioRecorder%20instance.%20%0ASets%20up%20the%20audio%20context%20and%20file%20reader..html +114 -114
  61. package/doc/fonts/OpenSans-Bold-webfont.svg +1829 -1829
  62. package/doc/fonts/OpenSans-BoldItalic-webfont.svg +1829 -1829
  63. package/doc/fonts/OpenSans-Italic-webfont.svg +1829 -1829
  64. package/doc/fonts/OpenSans-Light-webfont.svg +1830 -1830
  65. package/doc/fonts/OpenSans-LightItalic-webfont.svg +1834 -1834
  66. package/doc/fonts/OpenSans-Regular-webfont.svg +1830 -1830
  67. package/doc/global.html +308 -308
  68. package/doc/index.html +58 -58
  69. package/doc/listener.js.html +170 -170
  70. package/doc/mlsGen_mlsGenInterface.js.html +117 -117
  71. package/doc/myEventEmitter.js.html +124 -124
  72. package/doc/peer-connection_audioPeer.js.html +188 -188
  73. package/doc/peer-connection_listener.js.html +311 -311
  74. package/doc/peer-connection_speaker.js.html +381 -381
  75. package/doc/scripts/linenumber.js +25 -25
  76. package/doc/scripts/prettify/Apache-License-2.0.txt +202 -202
  77. package/doc/scripts/prettify/lang-css.js +24 -24
  78. package/doc/scripts/prettify/prettify.js +640 -640
  79. package/doc/server_PythonServerAPI.js.html +160 -160
  80. package/doc/speaker.js.html +248 -248
  81. package/doc/styles/jsdoc-default.css +371 -371
  82. package/doc/styles/prettify-jsdoc.css +111 -111
  83. package/doc/styles/prettify-tomorrow.css +163 -163
  84. package/doc/tasks_audioCalibrator.js.html +207 -207
  85. package/doc/tasks_audioRecorder.js.html +190 -190
  86. package/doc/tasks_impulse-response_impulseResponse.js.html +442 -442
  87. package/doc/tasks_impulse-response_mlsGen_mlsGenInterface.js.html +175 -175
  88. package/doc/tasks_volume_volume.js.html +185 -185
  89. package/doc/utils.js.html +105 -105
  90. package/jest.config.js +173 -173
  91. package/netlify.toml +26 -26
  92. package/package.json +69 -69
  93. package/src/config/firebase.js +25 -25
  94. package/src/index.html +21 -21
  95. package/src/main.js +23 -23
  96. package/src/myEventEmitter.js +83 -83
  97. package/src/peer-connection/audioPeer.js +151 -151
  98. package/src/peer-connection/listener.js +288 -288
  99. package/src/peer-connection/peerErrors.js +25 -25
  100. package/src/peer-connection/speaker.js +454 -453
  101. package/src/server/PythonServerAPI.js +468 -468
  102. package/src/tasks/audioCalibrator.js +304 -304
  103. package/src/tasks/audioRecorder.js +277 -277
  104. package/src/tasks/combination/combination.js +1781 -1775
  105. package/src/tasks/combination/mlsGen/mlsGen.cpp +98 -98
  106. package/src/tasks/combination/mlsGen/mlsGen.hpp +303 -303
  107. package/src/tasks/combination/mlsGen/mlsGenInterface.js +131 -131
  108. package/src/tasks/combination/mlsGen/mlsGenTest.cpp +180 -180
  109. package/src/tasks/impulse-response/impulseResponse.js +610 -610
  110. package/src/tasks/impulse-response/mlsGen/mlsGen.cpp +98 -98
  111. package/src/tasks/impulse-response/mlsGen/mlsGen.hpp +303 -303
  112. package/src/tasks/impulse-response/mlsGen/mlsGenInterface.js +131 -131
  113. package/src/tasks/impulse-response/mlsGen/mlsGenTest.cpp +180 -180
  114. package/src/tasks/volume/volume.cpp +2 -2
  115. package/src/tasks/volume/volume.hpp +22 -22
  116. package/src/tasks/volume/volume.js +279 -279
  117. package/src/utils.js +55 -55
  118. package/webpack.config.js +37 -37
  119. package/makefile +0 -74
@@ -1,1775 +1,1781 @@
1
- import AudioCalibrator from '../audioCalibrator';
2
-
3
- import {sleep, csvToArray, saveToCSV} from '../../utils';
4
- import database from '../../config/firebase';
5
- import {ref, set, get, child} from 'firebase/database';
6
-
7
- /**
8
- *
9
- */
10
- class Combination extends AudioCalibrator {
11
- /**
12
- * Default constructor. Creates an instance with any number of paramters passed or the default parameters defined here.
13
- *
14
- * @param {Object<boolean, number, number, number>} calibratorParams - paramter object
15
- * @param {boolean} [calibratorParams.download = false] - boolean flag to download captures
16
- * @param {number} [calibratorParams.mlsOrder = 18] - order of the MLS to be generated
17
- * @param {number} [calibratorParams.numCaptures = 5] - number of captures to perform
18
- * @param {number} [calibratorParams.numMLSPerCapture = 4] - number of bursts of MLS per capture
19
- */
20
- constructor({
21
- download = false,
22
- mlsOrder = 18,
23
- numCaptures = 3,
24
- numMLSPerCapture = 4,
25
- lowHz = 20,
26
- highHz = 10000,
27
- }) {
28
- super(numCaptures, numMLSPerCapture);
29
- this.#mlsOrder = parseInt(mlsOrder, 10);
30
- this.#P = 2 ** mlsOrder - 1;
31
- this.#download = download;
32
- this.#mls = [];
33
- this.#lowHz = lowHz;
34
- this.#highHz = highHz;
35
- }
36
-
37
- /** @private */
38
- stepNum = 0;
39
-
40
- /** @private */
41
- totalSteps = 25;
42
-
43
- /** @private */
44
- #download;
45
-
46
- /** @private */
47
- #mlsGenInterface;
48
-
49
- /** @private */
50
- #mlsBufferView;
51
-
52
- /** @private */
53
- componentInvertedImpulseResponse = null;
54
-
55
- /** @private */
56
- systemInvertedImpulseResponse = null;
57
-
58
- //averaged and subtracted ir returned from calibration used to calculated iir
59
- /** @private */
60
- ir = null;
61
-
62
- /** @private */
63
- impulseResponses = [];
64
-
65
- /** @private */
66
- #mlsOrder;
67
-
68
- /** @private */
69
- #lowHz;
70
-
71
- /** @private */
72
- #highHz;
73
-
74
- /** @private */
75
- #mls;
76
-
77
- /** @private */
78
- #P;
79
-
80
- /** @private */
81
- #audioContext;
82
-
83
- /** @private */
84
- TAPER_SECS = 5;
85
-
86
- /** @private */
87
- offsetGainNode;
88
-
89
- /** @private */
90
- componentConvolution;
91
-
92
- /** @private */
93
- systemConvolution;
94
-
95
- ////////////////////////volume
96
- /** @private */
97
- #CALIBRATION_TONE_FREQUENCY = 1000; // Hz
98
-
99
- /** @private */
100
- #CALIBRATION_TONE_TYPE = 'sine';
101
-
102
- CALIBRATION_TONE_DURATION = 5; // seconds
103
- calibrateSound1000HzPreSec = 3.5;
104
- calibrateSound1000HzSec = 1.0;
105
- calibrateSound1000HzPostSec = 0.5;
106
-
107
- /** @private */
108
- outDBSPL = null;
109
- THD = null;
110
- outDBSPL1000 = null;
111
-
112
- /** @private */
113
- TAPER_SECS = 0.01; // seconds
114
-
115
- /** @private */
116
- status_denominator = 8;
117
-
118
- /** @private */
119
- status_numerator = 0;
120
-
121
- /** @private */
122
- percent_complete = 0;
123
-
124
- /** @private */
125
- status = ``;
126
-
127
- /**@private */
128
- 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>`;
129
-
130
- /**@private */
131
- componentIR = null;
132
-
133
- /**@private */
134
- oldComponentIR = null;
135
-
136
- /**@private */
137
- systemIR = null;
138
-
139
- /**@private */
140
- _calibrateSoundCheck = '';
141
-
142
- deviceType = null;
143
-
144
- deviceName = null;
145
-
146
- deviceInfo = null;
147
-
148
- desired_time_per_mls = 0;
149
-
150
- num_mls_to_skip = 0;
151
-
152
- desired_sampling_rate = 0;
153
-
154
- #currentConvolution = [];
155
-
156
- mode = 'unfiltered';
157
-
158
- sourceNode;
159
-
160
- autocorrelations = [];
161
-
162
- iirLength = 0;
163
-
164
- componentInvertedImpulseResponseNoBandpass = [];
165
-
166
- systemInvertedImpulseResponseNoBandpass = [];
167
-
168
- _calibrateSoundBackgroundSecs;
169
-
170
- background_noise = {};
171
-
172
- numSuccessfulBackgroundCaptured;
173
-
174
- /**generate string template that gets reevaluated as variable increases */
175
- generateTemplate = () => {
176
- if (this.percent_complete > 100) {
177
- this.percent_complete = 100;
178
- }
179
- 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>`;
180
- return template;
181
- };
182
-
183
- /** increment numerator and percent for status bar */
184
- incrementStatusBar = () => {
185
- this.status_numerator += 1;
186
- this.percent_complete = (this.status_numerator / this.status_denominator) * 100;
187
- };
188
-
189
- setDeviceType = deviceType => {
190
- this.deviceType = deviceType;
191
- };
192
-
193
- setDeviceName = deviceName => {
194
- this.deviceName = deviceName;
195
- };
196
-
197
- setDeviceInfo = deviceInfo => {
198
- this.deviceInfo = deviceInfo;
199
- };
200
-
201
- /** .
202
- * .
203
- * .
204
- * Sends all the computed impulse responses to the backend server for processing
205
- *
206
- * @returns sets the resulting inverted impulse response to the class property
207
- * @example
208
- */
209
- sendSystemImpulseResponsesToServerForProcessing = async () => {
210
- const computedIRs = await Promise.all(this.impulseResponses);
211
- const filteredComputedIRs = computedIRs.filter(element => {
212
- return element != undefined;
213
- }); //log any errors that are found in this step
214
- const mls = this.#mls;
215
- const lowHz = this.#lowHz; //gain of 1 below cutoff, need gain of 0
216
- const highHz = this.#highHz; //check error for anything other than 10 kHz
217
- const iirLength = this.iirLength;
218
- const num_periods = this.numMLSPerCapture + this.num_mls_to_skip;
219
- this.stepNum += 1;
220
- console.log('send impulse responses to server: ' + this.stepNum);
221
- this.status =
222
- `All Hz Calibration: computing the IIR...`.toString() + this.generateTemplate().toString();
223
- this.emit('update', {message: this.status});
224
- return this.pyServerAPI
225
- .getSystemInverseImpulseResponseWithRetry({
226
- payload: filteredComputedIRs.slice(0, this.numCaptures),
227
- mls,
228
- lowHz,
229
- highHz,
230
- iirLength,
231
- num_periods,
232
- sampleRate: this.sourceSamplingRate || 96000,
233
- })
234
- .then(res => {
235
- console.log(res);
236
- this.stepNum += 1;
237
- console.log('got impulse response ' + this.stepNum);
238
- this.incrementStatusBar();
239
- this.status =
240
- `All Hz Calibration: done computing the IIR...`.toString() +
241
- this.generateTemplate().toString();
242
- this.emit('update', {message: this.status});
243
- this.systemInvertedImpulseResponse = res['iir'];
244
- this.systemIR = res['ir'];
245
- this.systemConvolution = res['convolution'];
246
- this.systemInvertedImpulseResponseNoBandpass = res['iirNoBandpass'];
247
- })
248
- .catch(err => {
249
- console.error(err);
250
- });
251
- };
252
-
253
- /** .
254
- * .
255
- * .
256
- * Sends all the computed impulse responses to the backend server for processing
257
- *
258
- * @returns sets the resulting inverted impulse response to the class property
259
- * @example
260
- */
261
- sendComponentImpulseResponsesToServerForProcessing = async () => {
262
- const computedIRs = await Promise.all(this.impulseResponses);
263
- const filteredComputedIRs = computedIRs.filter(element => {
264
- return element != undefined;
265
- });
266
- const componentIRGains = this.componentIR['Gain'];
267
- const componentIRFreqs = this.componentIR['Freq'];
268
- const mls = this.#mls;
269
- const lowHz = this.#lowHz;
270
- const iirLength = this.iirLength;
271
- const num_periods = this.numMLSPerCapture + this.num_mls_to_skip;
272
- const highHz = this.#highHz;
273
- this.stepNum += 1;
274
- console.log('send impulse responses to server: ' + this.stepNum);
275
- this.status =
276
- `All Hz Calibration: computing the IIR...`.toString() + this.generateTemplate().toString();
277
- this.emit('update', {message: this.status});
278
- return this.pyServerAPI
279
- .getComponentInverseImpulseResponseWithRetry({
280
- payload: filteredComputedIRs.slice(0, this.numCaptures),
281
- mls,
282
- lowHz,
283
- highHz,
284
- iirLength,
285
- componentIRGains,
286
- componentIRFreqs,
287
- num_periods,
288
- sampleRate: this.sourceSamplingRate || 96000,
289
- })
290
- .then(res => {
291
- console.log(res);
292
- this.stepNum += 1;
293
- console.log('got impulse response ' + this.stepNum);
294
- this.incrementStatusBar();
295
- this.status =
296
- `All Hz Calibration: done computing the IIR...`.toString() +
297
- this.generateTemplate().toString();
298
- this.emit('update', {message: this.status});
299
- this.componentInvertedImpulseResponse = res['iir'];
300
- this.componentIR['Gain'] = res['ir'];
301
- this.componentIR['Freq'] = res['frequencies'];
302
- this.componentConvolution = res['convolution'];
303
- this.componentInvertedImpulseResponseNoBandpass = res['iirNoBandpass'];
304
- })
305
- .catch(err => {
306
- // this.emit('InvertedImpulseResponse', {res: false});
307
- console.error(err);
308
- });
309
- };
310
-
311
- sendBackgroundRecording = () => {
312
- const allSignals = this.getAllBackgroundRecordings();
313
- const numSignals = allSignals.length;
314
- const background_rec_whole = allSignals[numSignals - 1];
315
- const fraction = 0.5 / (this._calibrateSoundBackgroundSecs + 0.5);
316
- // Calculate the starting index for slicing the array
317
- const startIndex = Math.round(fraction * background_rec_whole.length);
318
- // Slice the array from the calculated start index to the end of the array
319
- const background_rec = background_rec_whole.slice(startIndex);
320
- console.log('Sending background recording to server for processing');
321
- this.pyServerAPI
322
- .getBackgroundNoisePSDWithRetry({
323
- background_rec,
324
- sampleRate: this.sourceSamplingRate || 96000,
325
- })
326
- .then(res => {
327
- if (this.numSuccessfulBackgroundCaptured < 1) {
328
- this.numSuccessfulBackgroundCaptured += 1;
329
- //storing all background data in background_psd object
330
- this.background_noise['x_background'] = res['x_background'];
331
- this.background_noise['y_background'] = res['y_background'];
332
- this.background_noise['recording'] = background_rec;
333
- }
334
- })
335
- .catch(err => {
336
- console.error(err);
337
- });
338
- };
339
-
340
- /** .
341
- * .
342
- * .
343
- * Sends the recorded signal, or a given csv string of a signal, to the back end server for processing
344
- *
345
- * @param {<array>String} signalCsv - Optional csv string of a previously recorded signal, if given, this signal will be processed
346
- * @example
347
- */
348
- sendRecordingToServerForProcessing = signalCsv => {
349
- const allSignals = this.getAllUnfilteredRecordedSignals();
350
- console.log(
351
- 'Obtaining last all hz unfiltered recording from #allHzUnfilteredRecordings to send to server for processing'
352
- );
353
- const numSignals = allSignals.length;
354
- const mls = this.#mls;
355
- const payload =
356
- signalCsv && signalCsv.length > 0 ? csvToArray(signalCsv) : allSignals[numSignals - 1];
357
- console.log('sending rec');
358
- this.stepNum += 1;
359
- console.log('send rec ' + this.stepNum);
360
- this.status =
361
- `All Hz Calibration Step: computing the IR of the last recording...`.toString() +
362
- this.generateTemplate().toString();
363
- this.emit('update', {message: this.status});
364
- this.impulseResponses.push(
365
- this.pyServerAPI
366
- .getImpulseResponse({
367
- sampleRate: this.sourceSamplingRate || 96000,
368
- payload,
369
- mls,
370
- P: this.#P, //get rid of this
371
- numPeriods: this.numMLSPerCapture,
372
- })
373
- .then(res => {
374
- if (this.numSuccessfulCaptured < this.numCaptures) {
375
- this.numSuccessfulCaptured += 1;
376
- console.log('num succ capt: ' + this.numSuccessfulCaptured);
377
- this.stepNum += 1;
378
- console.log('got impulse response ' + this.stepNum);
379
- this.incrementStatusBar();
380
- this.status =
381
- `All Hz Calibration: ${this.numSuccessfulCaptured}/${this.numCaptures} IRs computed...`.toString() +
382
- this.generateTemplate().toString();
383
- this.emit('update', {
384
- message: this.status,
385
- });
386
- this.autocorrelations.push(res['autocorrelation']);
387
- return res['ir'];
388
- }
389
- })
390
- .catch(err => {
391
- console.error(err);
392
- })
393
- );
394
- };
395
-
396
- /**
397
- * Passed to the calibration steps function, awaits the desired amount of seconds to capture the desired number
398
- * of MLS periods defined in the constructor.
399
- *
400
- * @example
401
- */
402
- #awaitDesiredMLSLength = async () => {
403
- // seconds per MLS = P / SR
404
- // await N * P / SR
405
- this.stepNum += 1;
406
- console.log('await desired length ' + this.stepNum);
407
- this.status =
408
- `All Hz Calibration: sampling the calibration signal...`.toString() +
409
- `\niteration ${this.stepNum}` +
410
- this.generateTemplate();
411
- this.emit('update', {
412
- message: this.status,
413
- });
414
- let time_to_wait = 0;
415
- if (this.mode === 'unfiltered') {
416
- //unfiltered
417
- time_to_wait = (this.#mls.length / this.sourceSamplingRate) * this.numMLSPerCapture;
418
- time_to_wait = time_to_wait * 1.1;
419
- } else if (this.mode === 'filtered') {
420
- //filtered
421
- // time_to_wait =
422
- // (this.#currentConvolution.length / this.sourceSamplingRate) *
423
- // (this.numMLSPerCapture / (this.num_mls_to_skip + this.numMLSPerCapture));
424
- time_to_wait =
425
- (this.#currentConvolution.length / this.sourceSamplingRate) * this.numMLSPerCapture;
426
- time_to_wait = time_to_wait * 1.1;
427
- } else {
428
- throw new Error('Mode broke in awaitDesiredMLSLength');
429
- }
430
-
431
- await sleep(time_to_wait);
432
- };
433
-
434
- /**
435
- * Passed to the background noise recording function, awaits the desired amount of seconds to capture the desired number
436
- * of seconds of background noise
437
- *
438
- * @example
439
- */
440
- #awaitBackgroundNoiseRecording = async () => {
441
- console.log(
442
- 'Waiting ' + this._calibrateSoundBackgroundSecs + ' second(s) to record background noise'
443
- );
444
- let time_to_wait = this._calibrateSoundBackgroundSecs + 0.5;
445
- await sleep(time_to_wait);
446
- };
447
-
448
- /** .
449
- * .
450
- * .
451
- * Passed to the calibration steps function, awaits the onset of the signal to ensure a steady state
452
- *
453
- * @example
454
- */
455
- #awaitSignalOnset = async () => {
456
- this.stepNum += 1;
457
- console.log('await signal onset ' + this.stepNum);
458
- this.status =
459
- `All Hz Calibration: waiting for the signal to stabilize...`.toString() +
460
- this.generateTemplate();
461
- this.emit('update', {
462
- message: this.status,
463
- });
464
- let number_of_bursts_to_skip = this.num_mls_to_skip;
465
- let time_to_sleep = 0;
466
- if (this.mode === 'unfiltered') {
467
- time_to_sleep = (this.#mls.length / this.sourceSamplingRate) * number_of_bursts_to_skip;
468
- } else if (this.mode === 'filtered') {
469
- console.log(this.#currentConvolution.length);
470
- // time_to_sleep =
471
- // (this.#currentConvolution.length / this.sourceSamplingRate) *
472
- // (number_of_bursts_to_skip / (number_of_bursts_to_skip + this.numMLSPerCapture));
473
- time_to_sleep =
474
- (this.#currentConvolution.length / this.sourceSamplingRate) * number_of_bursts_to_skip;
475
- } else {
476
- throw new Error('Mode broke in awaitSignalOnset');
477
- }
478
- await sleep(time_to_sleep);
479
- };
480
-
481
- /**
482
- * Called immediately after a recording is captured. Used to process the resulting signal
483
- * whether by sending the result to a server or by computing a result locally.
484
- *
485
- * @example
486
- */
487
- #afterMLSRecord = () => {
488
- console.log('after record');
489
- this.sendRecordingToServerForProcessing();
490
- };
491
-
492
- #afterMLSwIIRRecord = () => {
493
- if (this.numSuccessfulCaptured < 1) {
494
- this.numSuccessfulCaptured += 1;
495
- this.stepNum += 1;
496
- this.incrementStatusBar();
497
- console.log('after mls w iir record for some reason add numSucc capt ' + this.stepNum);
498
- this.status =
499
- `All Hz Calibration: ${this.numSuccessfulCaptured} recording of convolved MLS captured`.toString() +
500
- this.generateTemplate().toString();
501
- this.emit('update', {
502
- message: this.status,
503
- });
504
- }
505
- };
506
-
507
- /** .
508
- * .
509
- * .
510
- * Created an S Curver Buffer to taper the signal onset
511
- *
512
- * @param {*} length
513
- * @param {*} phase
514
- * @returns
515
- * @example
516
- */
517
- static createSCurveBuffer = (length, phase) => {
518
- const curve = new Float32Array(length);
519
- let i;
520
- for (i = 0; i < length; i += 1) {
521
- // scale the curve to be between 0-1
522
- curve[i] = Math.sin((Math.PI * i) / length - phase) / 2 + 0.5;
523
- }
524
- return curve;
525
- };
526
-
527
- static createInverseSCurveBuffer = (length, phase) => {
528
- const curve = new Float32Array(length);
529
- let i;
530
- let j = length - 1;
531
- for (i = 0; i < length; i += 1) {
532
- // scale the curve to be between 0-1
533
- curve[i] = Math.sin((Math.PI * j) / length - phase) / 2 + 0.5;
534
- j -= 1;
535
- }
536
- return curve;
537
- };
538
-
539
- /**
540
- * Construct a Calibration Node with the calibration parameters.
541
- *
542
- * @param dataBuffer
543
- * @private
544
- * @example
545
- */
546
- #createCalibrationNodeFromBuffer = dataBuffer => {
547
- console.log('length databuffer');
548
- console.log(dataBuffer.length);
549
- if (!this.sourceAudioContext) {
550
- this.makeNewSourceAudioContext();
551
- }
552
-
553
- const buffer = this.sourceAudioContext.createBuffer(
554
- 1, // number of channels
555
- dataBuffer.length,
556
- this.sourceAudioContext.sampleRate // sample rate
557
- );
558
-
559
- const data = buffer.getChannelData(0); // get data
560
- // fill the buffer with our data
561
- try {
562
- for (let i = 0; i < dataBuffer.length; i += 1) {
563
- data[i] = dataBuffer[i] * 0.33;
564
- }
565
- } catch (error) {
566
- console.error(error);
567
- }
568
-
569
- this.sourceNode = this.sourceAudioContext.createBufferSource();
570
-
571
- this.sourceNode.buffer = buffer;
572
- if (this.mode === 'filtered') {
573
- //used to not loop filtered
574
- this.sourceNode.loop = true;
575
- } else {
576
- this.sourceNode.loop = true;
577
- }
578
-
579
- this.sourceNode.connect(this.sourceAudioContext.destination);
580
-
581
- this.addCalibrationNode(this.sourceNode);
582
- };
583
-
584
- /**
585
- * Given a data buffer, creates the required calibration node
586
- *
587
- * @param {*} dataBufferArray
588
- * @example
589
- */
590
- #setCalibrationNodesFromBuffer = (dataBufferArray = [this.#mlsBufferView]) => {
591
- if (dataBufferArray.length === 1) {
592
- this.#createCalibrationNodeFromBuffer(dataBufferArray[0]);
593
- } else {
594
- throw new Error('The length of the data buffer array must be 1');
595
- }
596
- };
597
-
598
- /**
599
- * Creates an audio context and plays it for a few seconds.
600
- *
601
- * @private
602
- * @returns - Resolves when the audio is done playing.
603
- * @example
604
- */
605
- #playCalibrationAudio = () => {
606
- this.calibrationNodes[0].start(0);
607
- this.status = ``;
608
- if (this.mode === 'unfiltered') {
609
- this.#mls = this.calibrationNodes[0].buffer.getChannelData(0);
610
- console.log('play calibration audio ' + this.stepNum);
611
- this.status =
612
- `All Hz Calibration: playing the calibration tone...`.toString() +
613
- this.generateTemplate().toString();
614
- } else if (this.mode === 'filtered') {
615
- console.log('play convolved audio ' + this.stepNum);
616
- this.status =
617
- `All Hz Calibration: playing the convolved calibration tone...`.toString() +
618
- this.generateTemplate().toString();
619
- } else {
620
- throw new Error('Mode is incorrect');
621
- }
622
- this.emit('update', {message: this.status});
623
- this.stepNum += 1;
624
- console.log('sink sampling rate');
625
- console.log(this.sinkSamplingRate);
626
- console.log('source sampling rate');
627
- console.log(this.sourceSamplingRate);
628
- };
629
-
630
- /** .
631
- * .
632
- * .
633
- * Stops the audio with tapered offset
634
- *
635
- * @example
636
- */
637
- #stopCalibrationAudio = () => {
638
- this.calibrationNodes[0].stop(0);
639
- this.calibrationNodes = [];
640
- this.sourceNode.disconnect();
641
- this.stepNum += 1;
642
- console.log('stop calibration audio ' + this.stepNum);
643
- this.status =
644
- `All Hz Calibration: stopping the calibration tone...`.toString() +
645
- this.generateTemplate().toString();
646
- this.emit('update', {message: this.status});
647
- };
648
-
649
- playMLSwithIIR = async (stream, convolution) => {
650
- let checkRec = false;
651
- this.mode = 'filtered';
652
- console.log('play mls with iir');
653
- //this.invertedImpulseResponse = iir;
654
-
655
- await this.calibrationSteps(
656
- stream,
657
- this.#playCalibrationAudio, // play audio func (required)
658
- this.#createCalibrationNodeFromBuffer(convolution), // before play func
659
- this.#awaitSignalOnset, // before record
660
- () => this.numSuccessfulCaptured < 1,
661
- this.#awaitDesiredMLSLength, // during record
662
- this.#afterMLSwIIRRecord, // after record
663
- this.mode,
664
- checkRec
665
- );
666
- };
667
-
668
- bothSoundCheck = async stream => {
669
- let iir_ir_and_plots;
670
- this.#currentConvolution = this.componentConvolution;
671
- await this.playMLSwithIIR(stream, this.#currentConvolution);
672
- this.#stopCalibrationAudio();
673
- let component_conv_recs = this.getAllFilteredRecordedSignals();
674
- let return_component_conv_rec = component_conv_recs[0];
675
- this.clearAllFilteredRecordedSignals();
676
- this.#currentConvolution = this.systemConvolution;
677
- await this.playMLSwithIIR(stream, this.#currentConvolution);
678
- this.#stopCalibrationAudio();
679
- let system_conv_recs = this.getAllFilteredRecordedSignals();
680
- let return_system_conv_rec = system_conv_recs[0];
681
- this.sourceAudioContext.close();
682
- let recs = this.getAllUnfilteredRecordedSignals();
683
- let unconv_rec = recs[0];
684
- let return_unconv_rec = unconv_rec;
685
- let conv_rec = component_conv_recs[0];
686
-
687
- //psd of component
688
- let knownGain = this.oldComponentIR.Gain;
689
- let knownFreq = this.oldComponentIR.Freq;
690
- let sampleRate = this.sourceSamplingRate || 96000;
691
- let component_unconv_rec_psd = await this.pyServerAPI
692
- .getSubtractedPSDWithRetry(unconv_rec, knownGain, knownFreq, sampleRate)
693
- .then(res => {
694
- this.incrementStatusBar();
695
- this.status =
696
- `All Hz Calibration: done computing the PSD graphs...`.toString() +
697
- this.generateTemplate().toString();
698
- this.emit('update', {message: this.status});
699
- return res;
700
- })
701
- .catch(err => {
702
- console.error(err);
703
- });
704
-
705
- let component_conv_rec_psd = await this.pyServerAPI
706
- .getSubtractedPSDWithRetry(conv_rec, knownGain, knownFreq, sampleRate)
707
- .then(res => {
708
- this.incrementStatusBar();
709
- this.status =
710
- `All Hz Calibration: done computing the PSD graphs...`.toString() +
711
- this.generateTemplate().toString();
712
- this.emit('update', {message: this.status});
713
- return res;
714
- })
715
- .catch(err => {
716
- console.error(err);
717
- });
718
-
719
- conv_rec = system_conv_recs[0];
720
- //psd of system
721
- let system_recs_psd = await this.pyServerAPI
722
- .getPSDWithRetry({
723
- unconv_rec,
724
- conv_rec,
725
- sampleRate: this.sourceSamplingRate || 96000,
726
- })
727
- .then(res => {
728
- this.incrementStatusBar();
729
- this.status =
730
- `All Hz Calibration: done computing the PSD graphs...`.toString() +
731
- this.generateTemplate().toString();
732
- this.emit('update', {message: this.status});
733
- return res;
734
- })
735
- .catch(err => {
736
- console.error(err);
737
- });
738
-
739
- //iir w/ and without bandpass psd. done
740
- unconv_rec = this.componentInvertedImpulseResponseNoBandpass;
741
- conv_rec = this.componentInvertedImpulseResponse;
742
- let component_iir_psd = await this.pyServerAPI
743
- .getPSDWithRetry({
744
- unconv_rec,
745
- conv_rec,
746
- sampleRate: this.sourceSamplingRate || 96000,
747
- })
748
- .then(res => {
749
- this.incrementStatusBar();
750
- this.status =
751
- `All Hz Calibration: done computing the PSD graphs...`.toString() +
752
- this.generateTemplate().toString();
753
- this.emit('update', {message: this.status});
754
- return res;
755
- })
756
- .catch(err => {
757
- console.error(err);
758
- });
759
- unconv_rec = this.systemInvertedImpulseResponseNoBandpass;
760
- conv_rec = this.systemInvertedImpulseResponse;
761
- let system_iir_psd = await this.pyServerAPI
762
- .getPSDWithRetry({
763
- unconv_rec,
764
- conv_rec,
765
- sampleRate: this.sourceSamplingRate || 96000,
766
- })
767
- .then(res => {
768
- this.incrementStatusBar();
769
- this.status =
770
- `All Hz Calibration: done computing the PSD graphs...`.toString() +
771
- this.generateTemplate().toString();
772
- this.emit('update', {message: this.status});
773
- return res;
774
- })
775
- .catch(err => {
776
- console.error(err);
777
- });
778
-
779
- iir_ir_and_plots = {
780
- filtered_recording: {
781
- component: return_component_conv_rec,
782
- system: return_system_conv_rec,
783
- },
784
- unfiltered_recording: this.getAllUnfilteredRecordedSignals()[0],
785
- system: {
786
- iir: this.systemInvertedImpulseResponse,
787
- ir: this.systemIR,
788
- iir_psd: {
789
- y: system_iir_psd['y_conv'],
790
- x: system_iir_psd['x_conv'],
791
- y_no_bandpass: system_iir_psd['y_unconv'],
792
- x_no_bandpass: system_iir_psd['x_unconv'],
793
- },
794
- convolution: this.systemConvolution,
795
- psd: {
796
- unconv: {
797
- x: system_recs_psd['x_unconv'],
798
- y: system_recs_psd['y_unconv'],
799
- },
800
- conv: {
801
- x: system_recs_psd['x_conv'],
802
- y: system_recs_psd['y_conv'],
803
- },
804
- },
805
- },
806
- component: {
807
- iir: this.componentInvertedImpulseResponse,
808
- ir: this.componentIR,
809
- iir_psd: {
810
- y: component_iir_psd['y_conv'],
811
- x: component_iir_psd['x_conv'],
812
- y_no_bandpass: component_iir_psd['y_unconv'],
813
- x_no_bandpass: component_iir_psd['x_unconv'],
814
- },
815
- convolution: this.componentConvolution,
816
- psd: {
817
- unconv: {
818
- x: component_unconv_rec_psd['x'],
819
- y: component_unconv_rec_psd['y'],
820
- },
821
- conv: {
822
- x: component_conv_rec_psd['x'],
823
- y: component_conv_rec_psd['y'],
824
- },
825
- },
826
- },
827
- mls: this.#mls,
828
- autocorrelations: this.autocorrelations,
829
- impulseResponses: [],
830
- };
831
-
832
- return iir_ir_and_plots;
833
- };
834
-
835
- singleSoundCheck = async stream => {
836
- let iir_ir_and_plots;
837
- if (this._calibrateSoundCheck != 'system') {
838
- this.#currentConvolution = this.componentConvolution;
839
- } else {
840
- this.#currentConvolution = this.systemConvolution;
841
- }
842
- await this.playMLSwithIIR(stream, this.#currentConvolution);
843
- this.#stopCalibrationAudio();
844
- this.sourceAudioContext.close();
845
- let conv_recs = this.getAllFilteredRecordedSignals();
846
- let recs = this.getAllUnfilteredRecordedSignals();
847
- console.log('Obtaining unfiltered recording from #allHzUnfilteredRecordings to calculate PSD');
848
- console.log('Obtaining filtered recording from #allHzFilteredRecordings to calculate PSD');
849
- let unconv_rec = recs[0];
850
- let return_unconv_rec = unconv_rec;
851
- let conv_rec = conv_recs[0];
852
- let return_conv_rec = conv_rec;
853
- if (this._calibrateSoundCheck != 'system') {
854
- let knownGain = this.oldComponentIR.Gain;
855
- let knownFreq = this.oldComponentIR.Freq;
856
- let sampleRate = this.sourceSamplingRate || 96000;
857
- let unconv_results = await this.pyServerAPI
858
- .getSubtractedPSDWithRetry(unconv_rec, knownGain, knownFreq, sampleRate)
859
- .then(res => {
860
- this.incrementStatusBar();
861
- this.status =
862
- `All Hz Calibration: done computing the PSD graphs...`.toString() +
863
- this.generateTemplate().toString();
864
- this.emit('update', {message: this.status});
865
- return res;
866
- })
867
- .catch(err => {
868
- console.error(err);
869
- });
870
-
871
- let conv_results = await this.pyServerAPI
872
- .getSubtractedPSDWithRetry(conv_rec, knownGain, knownFreq, sampleRate)
873
- .then(res => {
874
- this.incrementStatusBar();
875
- this.status =
876
- `All Hz Calibration: done computing the PSD graphs...`.toString() +
877
- this.generateTemplate().toString();
878
- this.emit('update', {message: this.status});
879
- return res;
880
- })
881
- .catch(err => {
882
- console.error(err);
883
- });
884
- unconv_rec = this.componentInvertedImpulseResponseNoBandpass;
885
- conv_rec = this.componentInvertedImpulseResponse;
886
- let component_iir_psd = await this.pyServerAPI
887
- .getPSDWithRetry({
888
- unconv_rec,
889
- conv_rec,
890
- sampleRate: this.sourceSamplingRate || 96000,
891
- })
892
- .then(res => {
893
- this.incrementStatusBar();
894
- this.status =
895
- `All Hz Calibration: done computing the PSD graphs...`.toString() +
896
- this.generateTemplate().toString();
897
- this.emit('update', {message: this.status});
898
- return res;
899
- })
900
- .catch(err => {
901
- console.error(err);
902
- });
903
- unconv_rec = this.systemInvertedImpulseResponseNoBandpass;
904
- conv_rec = this.systemInvertedImpulseResponse;
905
- let system_iir_psd = await this.pyServerAPI
906
- .getPSDWithRetry({
907
- unconv_rec,
908
- conv_rec,
909
- sampleRate: this.sourceSamplingRate || 96000,
910
- })
911
- .then(res => {
912
- this.incrementStatusBar();
913
- this.status =
914
- `All Hz Calibration: done computing the PSD graphs...`.toString() +
915
- this.generateTemplate().toString();
916
- this.emit('update', {message: this.status});
917
- return res;
918
- })
919
- .catch(err => {
920
- console.error(err);
921
- });
922
-
923
- iir_ir_and_plots = {
924
- unfiltered_recording: return_unconv_rec,
925
- filtered_recording: return_conv_rec,
926
- system: {
927
- iir: this.systemInvertedImpulseResponse,
928
- ir: this.systemIR,
929
- iir_psd: {
930
- y: system_iir_psd['y_conv'],
931
- x: system_iir_psd['y_conv'],
932
- y_no_bandpass: system_iir_psd['y_unconv'],
933
- x_no_bandpass: system_iir_psd['x_unconv'],
934
- },
935
- filtered_recording: [],
936
- convolution: this.systemConvolution,
937
- psd: {
938
- unconv: {
939
- x: [],
940
- y: [],
941
- },
942
- conv: {
943
- x: [],
944
- y: [],
945
- },
946
- },
947
- },
948
- component: {
949
- iir: this.componentInvertedImpulseResponse,
950
- ir: this.componentIR,
951
- iir_psd: {
952
- y: component_iir_psd['y_conv'],
953
- x: component_iir_psd['x_conv'],
954
- y_no_bandpass: component_iir_psd['y_unconv'],
955
- x_no_bandpass: component_iir_psd['x_unconv'],
956
- },
957
- convolution: this.componentConvolution,
958
- psd: {
959
- unconv: {
960
- x: unconv_results['x'],
961
- y: unconv_results['y'],
962
- },
963
- conv: {
964
- x: conv_results['x'],
965
- y: conv_results['y'],
966
- },
967
- },
968
- },
969
- mls: this.#mls,
970
- autocorrelations: this.autocorrelations,
971
- impulseResponses: [],
972
- };
973
- } else {
974
- let results = await this.pyServerAPI
975
- .getPSDWithRetry({
976
- unconv_rec,
977
- conv_rec,
978
- sampleRate: this.sourceSamplingRate || 96000,
979
- })
980
- .then(res => {
981
- this.incrementStatusBar();
982
- this.status =
983
- `All Hz Calibration: done computing the PSD graphs...`.toString() +
984
- this.generateTemplate().toString();
985
- this.emit('update', {message: this.status});
986
- return res;
987
- })
988
- .catch(err => {
989
- console.error(err);
990
- });
991
-
992
- //iir w/ and without bandpass psd
993
- unconv_rec = this.componentInvertedImpulseResponseNoBandpass;
994
- conv_rec = this.componentInvertedImpulseResponse;
995
- let component_iir_psd = await this.pyServerAPI
996
- .getPSDWithRetry({
997
- unconv_rec,
998
- conv_rec,
999
- sampleRate: this.sourceSamplingRate || 96000,
1000
- })
1001
- .then(res => {
1002
- this.incrementStatusBar();
1003
- this.status =
1004
- `All Hz Calibration: done computing the PSD graphs...`.toString() +
1005
- this.generateTemplate().toString();
1006
- this.emit('update', {message: this.status});
1007
- return res;
1008
- })
1009
- .catch(err => {
1010
- console.error(err);
1011
- });
1012
- unconv_rec = this.systemInvertedImpulseResponseNoBandpass;
1013
- conv_rec = this.systemInvertedImpulseResponse;
1014
- let system_iir_psd = await this.pyServerAPI
1015
- .getPSDWithRetry({
1016
- unconv_rec,
1017
- conv_rec,
1018
- sampleRate: this.sourceSamplingRate || 96000,
1019
- })
1020
- .then(res => {
1021
- this.incrementStatusBar();
1022
- this.status =
1023
- `All Hz Calibration: done computing the PSD graphs...`.toString() +
1024
- this.generateTemplate().toString();
1025
- this.emit('update', {message: this.status});
1026
- return res;
1027
- })
1028
- .catch(err => {
1029
- console.error(err);
1030
- });
1031
-
1032
- iir_ir_and_plots = {
1033
- unfiltered_recording: return_unconv_rec,
1034
- filtered_recording: return_conv_rec,
1035
- system: {
1036
- iir: this.systemInvertedImpulseResponse,
1037
- ir: this.systemIR,
1038
- iir_psd: {
1039
- y: system_iir_psd['y_conv'],
1040
- x: system_iir_psd['y_conv'],
1041
- y_no_bandpass: system_iir_psd['y_unconv'],
1042
- x_no_bandpass: system_iir_psd['x_unconv'],
1043
- },
1044
- filtered_recording: [],
1045
- convolution: this.systemConvolution,
1046
- psd: {
1047
- unconv: {
1048
- x: results['x_unconv'],
1049
- y: results['y_unconv'],
1050
- },
1051
- conv: {
1052
- x: results['x_conv'],
1053
- y: results['y_conv'],
1054
- },
1055
- },
1056
- },
1057
- component: {
1058
- iir: this.componentInvertedImpulseResponse,
1059
- ir: this.componentIR,
1060
- iir_psd: {
1061
- y: component_iir_psd['y_conv'],
1062
- x: component_iir_psd['x_conv'],
1063
- y_no_bandpass: component_iir_psd['y_unconv'],
1064
- x_no_bandpass: component_iir_psd['x_unconv'],
1065
- },
1066
- convolution: this.componentConvolution,
1067
- psd: {
1068
- unconv: {
1069
- x: [],
1070
- y: [],
1071
- },
1072
- conv: {
1073
- x: [],
1074
- y: [],
1075
- },
1076
- },
1077
- },
1078
- mls: this.#mls,
1079
- autocorrelations: this.autocorrelations,
1080
- impulseResponses: [],
1081
- };
1082
- }
1083
- await Promise.all(this.impulseResponses).then(res => {
1084
- for (let i = 0; i < res.length; i++) {
1085
- if (res[i] != undefined) {
1086
- iir_ir_and_plots['impulseResponses'].push(res[i]);
1087
- }
1088
- }
1089
- });
1090
-
1091
- if (this.#download) {
1092
- this.downloadSingleUnfilteredRecording();
1093
- this.downloadSingleFilteredRecording();
1094
- saveToCSV(this.#mls, 'MLS.csv');
1095
- saveToCSV(this.componentConvolution, 'python_component_convolution_mls_iir.csv');
1096
- saveToCSV(this.systemConvolution, 'python_system_convolution_mls_iir.csv');
1097
- saveToCSV(this.componentInvertedImpulseResponse, 'componentIIR.csv');
1098
- saveToCSV(this.systemInvertedImpulseResponse, 'systemIIR.csv');
1099
- for (let i = 0; i < this.autocorrelations.length; i++) {
1100
- saveToCSV(this.autocorrelations[i], `autocorrelation_${i}`);
1101
- }
1102
- const computedIRagain = await Promise.all(this.impulseResponses).then(res => {
1103
- for (let i = 0; i < res.length; i++) {
1104
- if (res[i] != undefined) {
1105
- saveToCSV(res[i], `IR_${i}`);
1106
- }
1107
- }
1108
- });
1109
- }
1110
-
1111
- return iir_ir_and_plots;
1112
- };
1113
-
1114
- /**
1115
- * Public method to start the calibration process. Objects intialized from webassembly allocate new memory
1116
- * and must be manually freed. This function is responsible for intializing the MlsGenInterface,
1117
- * and wrapping the calibration steps with a garbage collection safe gaurd.
1118
- *
1119
- * @public
1120
- * @param stream - The stream of audio from the Listener.
1121
- * @example
1122
- */
1123
- startCalibrationImpulseResponse = async stream => {
1124
- let desired_time = this.desired_time_per_mls;
1125
- let checkRec = 'allhz';
1126
-
1127
- console.log('MLS sequence should be of length: ' + this.sourceSamplingRate * desired_time);
1128
-
1129
- length = this.sourceSamplingRate * desired_time;
1130
- //get mls here
1131
- await this.pyServerAPI
1132
- .getMLSWithRetry(length)
1133
- .then(res => {
1134
- console.log(res);
1135
- this.#mlsBufferView = res['mls'];
1136
- })
1137
- .catch(err => {
1138
- // this.emit('InvertedImpulseResponse', {res: false});
1139
- console.error(err);
1140
- });
1141
- this.numSuccessfulBackgroundCaptured = 0;
1142
- if (this._calibrateSoundBackgroundSecs > 0) {
1143
- this.mode = 'background';
1144
- this.status =
1145
- `All Hz Calibration: sampling the background noise...`.toString() +
1146
- this.generateTemplate().toString();
1147
- this.emit('update', {message: this.status});
1148
- await this.recordBackground(
1149
- stream, //stream
1150
- () => this.numSuccessfulBackgroundCaptured < 1, //loop condition
1151
- this.#awaitBackgroundNoiseRecording, //sleep to record
1152
- this.sendBackgroundRecording, //send to get PSD
1153
- this.mode,
1154
- checkRec
1155
- );
1156
- this.incrementStatusBar();
1157
- }
1158
- this.mode = 'unfiltered';
1159
- this.numSuccessfulCaptured = 0;
1160
-
1161
- await this.calibrationSteps(
1162
- stream,
1163
- this.#playCalibrationAudio, // play audio func (required)
1164
- this.#createCalibrationNodeFromBuffer(this.#mlsBufferView), // before play func
1165
- this.#awaitSignalOnset, // before record
1166
- () => this.numSuccessfulCaptured < this.numCaptures, // loop while true
1167
- this.#awaitDesiredMLSLength, // during record
1168
- this.#afterMLSRecord, // after record
1169
- this.mode,
1170
- checkRec
1171
- ),
1172
- this.#stopCalibrationAudio();
1173
- checkRec = false;
1174
-
1175
- // at this stage we've captured all the required signals,
1176
- // and have received IRs for each one
1177
- // so let's send all the IRs to the server to be converted to a single IIR
1178
- await this.sendSystemImpulseResponsesToServerForProcessing();
1179
- await this.sendComponentImpulseResponsesToServerForProcessing();
1180
-
1181
- this.numSuccessfulCaptured = 0;
1182
-
1183
- let iir_ir_and_plots;
1184
- if (this._calibrateSoundCheck != 'none') {
1185
- //do single check
1186
- if (this._calibrateSoundCheck == 'goal' || this._calibrateSoundCheck == 'system') {
1187
- iir_ir_and_plots = await this.singleSoundCheck(stream);
1188
- } else {
1189
- //both
1190
- iir_ir_and_plots = await this.bothSoundCheck(stream);
1191
- }
1192
- } else {
1193
- let unconv_rec = this.componentInvertedImpulseResponseNoBandpass;
1194
- let conv_rec = this.componentInvertedImpulseResponse;
1195
- let component_iir_psd = await this.pyServerAPI
1196
- .getPSDWithRetry({
1197
- unconv_rec,
1198
- conv_rec,
1199
- sampleRate: this.sourceSamplingRate || 96000,
1200
- })
1201
- .then(res => {
1202
- this.incrementStatusBar();
1203
- this.status =
1204
- `All Hz Calibration: done computing the PSD graphs...`.toString() +
1205
- this.generateTemplate().toString();
1206
- this.emit('update', {message: this.status});
1207
- return res;
1208
- })
1209
- .catch(err => {
1210
- console.error(err);
1211
- });
1212
- unconv_rec = this.systemInvertedImpulseResponseNoBandpass;
1213
- conv_rec = this.systemInvertedImpulseResponse;
1214
- let system_iir_psd = await this.pyServerAPI
1215
- .getPSDWithRetry({
1216
- unconv_rec,
1217
- conv_rec,
1218
- sampleRate: this.sourceSamplingRate || 96000,
1219
- })
1220
- .then(res => {
1221
- this.incrementStatusBar();
1222
- this.status =
1223
- `All Hz Calibration: done computing the PSD graphs...`.toString() +
1224
- this.generateTemplate().toString();
1225
- this.emit('update', {message: this.status});
1226
- return res;
1227
- })
1228
- .catch(err => {
1229
- console.error(err);
1230
- });
1231
-
1232
- iir_ir_and_plots = {
1233
- unfiltered_recording: return_unconv_rec,
1234
- filtered_recording: return_conv_rec,
1235
- system: {
1236
- iir: this.systemInvertedImpulseResponse,
1237
- ir: this.systemIR,
1238
- iir_psd: {
1239
- y: system_iir_psd['y_conv'],
1240
- x: system_iir_psd['y_conv'],
1241
- y_no_bandpass: system_iir_psd['y_unconv'],
1242
- x_no_bandpass: system_iir_psd['x_unconv'],
1243
- },
1244
- filtered_recording: [],
1245
- convolution: this.systemConvolution,
1246
- psd: {
1247
- unconv: {
1248
- x: [],
1249
- y: [],
1250
- },
1251
- conv: {
1252
- x: [],
1253
- y: [],
1254
- },
1255
- },
1256
- },
1257
- component: {
1258
- iir: this.componentInvertedImpulseResponse,
1259
- ir: this.componentIR,
1260
- iir_psd: {
1261
- y: component_iir_psd['y_conv'],
1262
- x: component_iir_psd['x_conv'],
1263
- y_no_bandpass: component_iir_psd['y_unconv'],
1264
- x_no_bandpass: component_iir_psd['x_unconv'],
1265
- },
1266
- convolution: this.componentConvolution,
1267
- psd: {
1268
- unconv: {
1269
- x: [],
1270
- y: [],
1271
- },
1272
- conv: {
1273
- x: [],
1274
- y: [],
1275
- },
1276
- },
1277
- },
1278
- mls: this.#mls,
1279
- autocorrelations: this.autocorrelations,
1280
- impulseResponses: [],
1281
- };
1282
- await Promise.all(this.impulseResponses).then(res => {
1283
- for (let i = 0; i < res.length; i++) {
1284
- if (res[i] != undefined) {
1285
- iir_ir_and_plots['impulseResponses'].push(res[i]);
1286
- }
1287
- }
1288
- });
1289
-
1290
- if (this.#download) {
1291
- saveToCSV(this.#mls, 'MLS.csv');
1292
- saveToCSV(this.componentConvolution, 'python_component_convolution_mls_iir.csv');
1293
- saveToCSV(this.systemConvolution, 'python_system_convolution_mls_iir.csv');
1294
- saveToCSV(this.componentInvertedImpulseResponse, 'componentIIR.csv');
1295
- saveToCSV(this.systemInvertedImpulseResponse, 'systemIIR.csv');
1296
- for (let i = 0; i < this.autocorrelations.length; i++) {
1297
- saveToCSV(this.autocorrelations[i], `autocorrelation_${i}`);
1298
- }
1299
- const computedIRagain = await Promise.all(this.impulseResponses).then(res => {
1300
- for (let i = 0; i < res.length; i++) {
1301
- if (res[i] != undefined) {
1302
- saveToCSV(res[i], `IR_${i}`);
1303
- }
1304
- }
1305
- });
1306
- }
1307
- }
1308
-
1309
- this.percent_complete = 100;
1310
-
1311
- this.status = `All Hz Calibration: Finished`.toString() + this.generateTemplate().toString();
1312
- this.emit('update', {message: this.status});
1313
-
1314
- //here after calibration we have the component calibration (either loudspeaker or microphone) in the same form as the componentIR
1315
- //that was used to calibrate
1316
-
1317
- return iir_ir_and_plots;
1318
- };
1319
-
1320
- //////////////////////volume
1321
-
1322
- handleIncomingData = data => {
1323
- console.log('Received data: ', data);
1324
- if (data.type === 'soundGainDBSPL') {
1325
- this.soundGainDBSPL = data.value;
1326
- } else {
1327
- throw new Error(`Unknown data type: ${data.type}`);
1328
- }
1329
- };
1330
- createSCurveBuffer = (onSetBool = true) => {
1331
- const curve = new Float32Array(this.TAPER_SECS * this.sourceSamplingRate + 1);
1332
- const frequency = 1 / (4 * this.TAPER_SECS);
1333
- let j = 0;
1334
- for (let i = 0; i < this.TAPER_SECS * this.sourceSamplingRate + 1; i += 1) {
1335
- const phase = 2 * Math.PI * frequency * j;
1336
- const onsetTaper = Math.pow(Math.sin(phase), 2);
1337
- const offsetTaper = Math.pow(Math.cos(phase), 2);
1338
- curve[i] = onSetBool ? onsetTaper : offsetTaper;
1339
- j += 1 / this.sourceSamplingRate;
1340
- }
1341
- return curve;
1342
- };
1343
-
1344
- #getTruncatedSignal = (left = 3.5, right = 4.5) => {
1345
- const start = Math.floor(left * this.sourceSamplingRate);
1346
- const end = Math.floor(right * this.sourceSamplingRate);
1347
- const result = Array.from(this.getLastVolumeRecordedSignal().slice(start, end));
1348
- console.log(
1349
- 'Obtaining last 1000 hz recording from #allVolumeRecordings to send for processing'
1350
- );
1351
- /**
1352
- * function to check that capture was properly made
1353
- * @param {*} list
1354
- */
1355
- const checkResult = list => {
1356
- const setItem = new Set(list);
1357
- if (setItem.size === 1 && setItem.has(0)) {
1358
- console.warn(
1359
- 'The last capture failed, all recorded signal is zero',
1360
- this.getAllVolumeRecordedSignals()
1361
- );
1362
- }
1363
- if (setItem.size === 0) {
1364
- console.warn('The last capture failed, no recorded signal');
1365
- }
1366
- };
1367
- checkResult(result);
1368
- return result;
1369
- };
1370
-
1371
- /**
1372
- *
1373
- *
1374
- Construct a calibration Node with the calibration parameters and given gain value
1375
- * @param {*} gainValue
1376
- * */
1377
- #createCalibrationToneWithGainValue = gainValue => {
1378
- const audioContext = this.makeNewSourceAudioContext();
1379
- const oscilator = audioContext.createOscillator();
1380
- const gainNode = audioContext.createGain();
1381
- const taperGainNode = audioContext.createGain();
1382
- const offsetGainNode = audioContext.createGain();
1383
- const totalDuration = this.CALIBRATION_TONE_DURATION * 1.2;
1384
-
1385
- oscilator.frequency.value = this.#CALIBRATION_TONE_FREQUENCY;
1386
- oscilator.type = this.#CALIBRATION_TONE_TYPE;
1387
- gainNode.gain.value = gainValue;
1388
-
1389
- oscilator.connect(gainNode);
1390
- gainNode.connect(taperGainNode);
1391
- const onsetCurve = this.createSCurveBuffer();
1392
- taperGainNode.gain.setValueCurveAtTime(onsetCurve, 0, this.TAPER_SECS);
1393
- taperGainNode.connect(offsetGainNode);
1394
- const offsetCurve = this.createSCurveBuffer(false);
1395
- offsetGainNode.gain.setValueCurveAtTime(
1396
- offsetCurve,
1397
- totalDuration - this.TAPER_SECS,
1398
- this.TAPER_SECS
1399
- );
1400
- offsetGainNode.connect(audioContext.destination);
1401
-
1402
- this.addCalibrationNode(oscilator);
1403
- };
1404
-
1405
- /**
1406
- * Construct a Calibration Node with the calibration parameters.
1407
- *
1408
- * @private
1409
- * @example
1410
- */
1411
- #createCalibrationNode = () => {
1412
- const audioContext = this.makeNewSourceAudioContext();
1413
- const oscilator = audioContext.createOscillator();
1414
- const gainNode = audioContext.createGain();
1415
-
1416
- oscilator.frequency.value = this.#CALIBRATION_TONE_FREQUENCY;
1417
- oscilator.type = this.#CALIBRATION_TONE_TYPE;
1418
- gainNode.gain.value = 0.04;
1419
-
1420
- oscilator.connect(gainNode);
1421
- gainNode.connect(audioContext.destination);
1422
-
1423
- this.addCalibrationNode(oscilator);
1424
- };
1425
-
1426
- #playCalibrationAudioVolume = async () => {
1427
- const totalDuration = this.CALIBRATION_TONE_DURATION * 1.2;
1428
-
1429
- this.calibrationNodes[0].start(0);
1430
- this.calibrationNodes[0].stop(totalDuration);
1431
- console.log(`Playing a buffer of ${this.CALIBRATION_TONE_DURATION} seconds of audio`);
1432
- console.log(`Waiting a total of ${totalDuration} seconds`);
1433
- await sleep(totalDuration);
1434
- };
1435
-
1436
- #sendToServerForProcessing = lCalib => {
1437
- console.log('Sending data to server');
1438
- let left = this.calibrateSound1000HzPreSec;
1439
- let right = this.calibrateSound1000HzPreSec + this.calibrateSound1000HzSec;
1440
- this.pyServerAPI
1441
- .getVolumeCalibration({
1442
- sampleRate: this.sourceSamplingRate,
1443
- payload: this.#getTruncatedSignal(left, right),
1444
- lCalib: lCalib,
1445
- })
1446
- .then(res => {
1447
- if (this.outDBSPL === null) {
1448
- this.incrementStatusBar();
1449
- this.outDBSPL = res['outDbSPL'];
1450
- this.outDBSPL1000 = res['outDbSPL1000'];
1451
- this.THD = res['thd'];
1452
- }
1453
- })
1454
- .catch(err => {
1455
- console.warn(err);
1456
- });
1457
- };
1458
-
1459
- startCalibrationVolume = async (stream, gainValues, lCalib, componentGainDBSPL) => {
1460
- const trialIterations = gainValues.length;
1461
- this.status_denominator += trialIterations;
1462
- const thdValues = [];
1463
- const inDBValues = [];
1464
- let inDB = 0;
1465
- const outDBSPLValues = [];
1466
- const outDBSPL1000Values = [];
1467
- let checkRec = false;
1468
-
1469
- // do one calibration that will be discarded
1470
- const soundLevelToDiscard = -60;
1471
- const gainToDiscard = Math.pow(10, soundLevelToDiscard / 20);
1472
- this.status =
1473
- `1000 Hz Calibration: Sound Level ${soundLevelToDiscard} dB`.toString() +
1474
- this.generateTemplate().toString();
1475
- //this.emit('update', {message: `1000 Hz Calibration: Sound Level ${soundLevelToDiscard} dB`});
1476
- this.emit('update', {message: this.status});
1477
-
1478
- do {
1479
- // eslint-disable-next-line no-await-in-loop
1480
- await this.volumeCalibrationSteps(
1481
- stream,
1482
- this.#playCalibrationAudioVolume,
1483
- this.#createCalibrationToneWithGainValue,
1484
- this.#sendToServerForProcessing,
1485
- gainToDiscard,
1486
- lCalib, //todo make this a class parameter
1487
- checkRec
1488
- );
1489
- } while (this.outDBSPL === null);
1490
- //reset the values
1491
- //this.incrementStatusBar();
1492
-
1493
- this.outDBSPL = null;
1494
- this.outDBSPL = null;
1495
- this.outDBSPL1000 = null;
1496
- this.THD = null;
1497
-
1498
- // run the calibration at different gain values provided by the user
1499
- for (let i = 0; i < trialIterations; i++) {
1500
- //convert gain to DB and add to inDB
1501
- if (i == trialIterations - 1) {
1502
- checkRec = 'loudest';
1503
- }
1504
- inDB = Math.log10(gainValues[i]) * 20;
1505
- // precision to 1 decimal place
1506
- inDB = Math.round(inDB * 10) / 10;
1507
- inDBValues.push(inDB);
1508
- console.log('next update');
1509
- this.status =
1510
- `1000 Hz Calibration: Sound Level ${inDB} dB`.toString() +
1511
- this.generateTemplate().toString();
1512
- this.emit('update', {message: this.status});
1513
- do {
1514
- // eslint-disable-next-line no-await-in-loop
1515
- await this.volumeCalibrationSteps(
1516
- stream,
1517
- this.#playCalibrationAudioVolume,
1518
- this.#createCalibrationToneWithGainValue,
1519
- this.#sendToServerForProcessing,
1520
- gainValues[i],
1521
- lCalib, //todo make this a class parameter
1522
- checkRec
1523
- );
1524
- } while (this.outDBSPL === null);
1525
- outDBSPL1000Values.push(this.outDBSPL1000);
1526
- thdValues.push(this.THD);
1527
- outDBSPLValues.push(this.outDBSPL);
1528
-
1529
- this.outDBSPL = null;
1530
- this.outDBSPL1000 = null;
1531
- this.THD = null;
1532
- }
1533
-
1534
- // get the volume calibration parameters from the server
1535
- const parameters = await this.pyServerAPI
1536
- .getVolumeCalibrationParameters({
1537
- inDBValues: inDBValues,
1538
- outDBSPLValues: outDBSPL1000Values,
1539
- lCalib: lCalib,
1540
- componentGainDBSPL,
1541
- })
1542
- .then(res => {
1543
- this.incrementStatusBar();
1544
- return res;
1545
- });
1546
- const result = {
1547
- parameters: parameters,
1548
- inDBValues: inDBValues,
1549
- outDBSPLValues: outDBSPLValues,
1550
- outDBSPL1000Values: outDBSPL1000Values,
1551
- thdValues: thdValues,
1552
- };
1553
-
1554
- return result;
1555
- };
1556
-
1557
- // function to write frq and gain to firebase database given speakerID
1558
- writeFrqGain = async (speakerID, frq, gain, OEM) => {
1559
- // freq and gain are too large to take samples 1 in every 100 samples
1560
-
1561
- const sampledFrq = [];
1562
- const sampledGain = [];
1563
- for (let i = 0; i < frq.length; i += 100) {
1564
- sampledFrq.push(frq[i]);
1565
- sampledGain.push(gain[i]);
1566
- }
1567
-
1568
- const data = {Freq: sampledFrq, Gain: sampledGain};
1569
-
1570
- await set(ref(database, `Microphone2/${OEM}/${speakerID}/linear`), data);
1571
- };
1572
-
1573
- // Function to Read frq and gain from firebase database given speakerID
1574
- // returns an array of frq and gain if speakerID exists, returns null otherwise
1575
-
1576
- readFrqGain = async (speakerID, OEM) => {
1577
- const dbRef = ref(database);
1578
- const snapshot = await get(child(dbRef, `Microphone2/${OEM}/${speakerID}/linear`));
1579
- if (snapshot.exists()) {
1580
- return snapshot.val();
1581
- }
1582
- return null;
1583
- };
1584
-
1585
- readGainat1000Hz = async (speakerID, OEM) => {
1586
- const dbRef = ref(database);
1587
- const snapshot = await get(child(dbRef, `Microphone2/${OEM}/${speakerID}/Gain1000`));
1588
- if (snapshot.exists()) {
1589
- return snapshot.val();
1590
- }
1591
- return null;
1592
- };
1593
-
1594
- writeGainat1000Hz = async (speakerID, gain, OEM) => {
1595
- const data = {Gain: gain};
1596
- await set(ref(database, `Microphone2/${OEM}/${speakerID}/Gain1000`), gain);
1597
- };
1598
-
1599
- writeIsSmartPhone = async (speakerID, isSmartPhone, OEM) => {
1600
- const data = {isSmartPhone: isSmartPhone};
1601
- await set(ref(database, `Microphone2/${OEM}/${speakerID}/isSmartPhone`), isSmartPhone);
1602
- };
1603
-
1604
- doesMicrophoneExist = async (speakerID, OEM) => {
1605
- const dbRef = ref(database);
1606
- const snapshot = await get(child(dbRef, `Microphone2/${OEM}/${speakerID}`));
1607
- if (snapshot.exists()) {
1608
- return true;
1609
- }
1610
- return false;
1611
- };
1612
-
1613
- addMicrophoneInfo = async (speakerID, OEM, micInfo) => {
1614
- // add to database if /info does not exist
1615
- const dbRef = ref(database);
1616
- const snapshot = await get(child(dbRef, `Microphone2/${OEM}/${speakerID}/info`));
1617
- if (!snapshot.exists()) {
1618
- await set(ref(database, `Microphone2/${OEM}/${speakerID}/info`), micInfo);
1619
- }
1620
- };
1621
-
1622
- convertToDB = gain => {
1623
- return Math.log10(gain) * 20;
1624
- };
1625
-
1626
- // Function to perform linear interpolation between two points
1627
- interpolate(x, x0, y0, x1, y1) {
1628
- return y0 + ((x - x0) * (y1 - y0)) / (x1 - x0);
1629
- }
1630
-
1631
- findGainatFrequency = (frequencies, gains, targetFrequency) => {
1632
- // Find the index of the first frequency in the array greater than the target frequency
1633
- let index = 0;
1634
- while (index < frequencies.length && frequencies[index] < targetFrequency) {
1635
- index++;
1636
- }
1637
-
1638
- // Handle cases when the target frequency is outside the range of the given data
1639
- if (index === 0) {
1640
- return gains[0];
1641
- } else if (index === frequencies.length) {
1642
- return gains[gains.length - 1];
1643
- } else {
1644
- // Interpolate the gain based on the surrounding frequencies
1645
- const x0 = frequencies[index - 1];
1646
- const y0 = gains[index - 1];
1647
- const x1 = frequencies[index];
1648
- const y1 = gains[index];
1649
- return this.interpolate(targetFrequency, x0, y0, x1, y1);
1650
- }
1651
- };
1652
-
1653
- // Example of how to use the writeFrqGain and readFrqGain functions
1654
- // writeFrqGain('speaker1', [1, 2, 3], [4, 5, 6]);
1655
- // Speaker1 is the speakerID you want to write to in the database
1656
- // readFrqGain('MiniDSPUMIK_1').then(data => console.log(data));
1657
- // MiniDSPUMIK_1 is the speakerID with some Data in the database
1658
- //adding gainDBSPL
1659
- startCalibration = async (
1660
- stream,
1661
- gainValues,
1662
- lCalib = 104.92978421490648,
1663
- componentIR = null,
1664
- microphoneName = 'MiniDSP-UMIK1-711-4754-vertical',
1665
- _calibrateSoundCheck = 'goal', //GOAL PASSed in by default
1666
- isSmartPhone = false,
1667
- _calibrateSoundBurstRepeats = 4,
1668
- _calibrateSoundBurstSec = 1,
1669
- _calibrateSoundBurstsWarmup = 1,
1670
- _calibrateSoundHz = 48000,
1671
- _calibrateSoundIIRSec = 0.2,
1672
- calibrateSound1000HzPreSec = 3.5,
1673
- calibrateSound1000HzSec = 1.0,
1674
- calibrateSound1000HzPostSec = 0.5,
1675
- _calibrateSoundBackgroundSecs = 0,
1676
- micManufacturer = '',
1677
- micSerialNumber = '',
1678
- micModelNumber = '',
1679
- micModelName = ''
1680
- ) => {
1681
- this.CALIBRATION_TONE_DURATION =
1682
- calibrateSound1000HzPreSec + calibrateSound1000HzSec + calibrateSound1000HzPostSec;
1683
- this.calibrateSound1000HzPreSec = calibrateSound1000HzPreSec;
1684
- this.calibrateSound1000HzSec = calibrateSound1000HzSec;
1685
- this.calibrateSound1000HzPostSec = calibrateSound1000HzPostSec;
1686
- this.iirLength = Math.floor(_calibrateSoundIIRSec * this.sourceSamplingRate);
1687
- console.log('device info:', this.deviceInfo);
1688
- this.numMLSPerCapture = _calibrateSoundBurstRepeats;
1689
- this.desired_time_per_mls = _calibrateSoundBurstSec;
1690
- this.num_mls_to_skip = _calibrateSoundBurstsWarmup;
1691
- this.desired_sampling_rate = _calibrateSoundHz;
1692
- this._calibrateSoundBackgroundSecs = _calibrateSoundBackgroundSecs;
1693
-
1694
- //feed calibration goal here
1695
- this._calibrateSoundCheck = _calibrateSoundCheck;
1696
- //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
1697
- //check the db based on the microphone currently connected
1698
-
1699
- //new lCalib found at top of calibration files *1000hz, make sure to correct
1700
- //based on zeroing of 1000hz, search for "*1000Hz"
1701
- const ID = isSmartPhone ? micModelNumber : micSerialNumber;
1702
- const OEM = isSmartPhone ? this.deviceInfo.OEM : micManufacturer;
1703
- // const ID = "711-4754";
1704
- // const OEM = "MiniDSP";
1705
- const micInfo = {
1706
- micModelName: isSmartPhone ? micModelName : microphoneName,
1707
- OEM: OEM,
1708
- ID: ID,
1709
- HardwareName: isSmartPhone ? this.deviceInfo.hardwarename : microphoneName,
1710
- hardwareFamily: isSmartPhone ? this.deviceInfo.hardwarefamily : microphoneName,
1711
- HardwareModel: isSmartPhone ? this.deviceInfo.hardwaremodel : microphoneName,
1712
- PlatformName: isSmartPhone ? this.deviceInfo.platformname : 'N/A',
1713
- PlatformVersion: isSmartPhone ? this.deviceInfo.platformversion : 'N/A',
1714
- DeviceType: isSmartPhone ? this.deviceInfo.devicetype : 'N/A',
1715
- };
1716
- this.addMicrophoneInfo(ID, OEM, micInfo);
1717
- if (componentIR == null) {
1718
- //mode 'ir'
1719
- //global variable this.componentIR must be set
1720
- this.componentIR = await this.readFrqGain(ID, OEM).then(data => {
1721
- return data;
1722
- });
1723
-
1724
- lCalib = await this.readGainat1000Hz(ID, OEM);
1725
- micInfo['gainDBSPL'] = lCalib;
1726
- // this.componentGainDBSPL = this.convertToDB(lCalib);
1727
- this.componentGainDBSPL = lCalib;
1728
- //TODO: if this call to database is unknown, cannot perform experiment => return false
1729
- if (this.componentIR == null) {
1730
- this.status =
1731
- `Microphone (${OEM},${ID}) is not found in the database. Please add it to the database.`.toString();
1732
- this.emit('update', {message: this.status});
1733
- return false;
1734
- }
1735
- } else {
1736
- this.componentIR = componentIR;
1737
- lCalib = this.findGainatFrequency(this.componentIR.Freq, this.componentIR.Gain, 1000);
1738
- // this.componentGainDBSPL = this.convertToDB(lCalib);
1739
- this.componentGainDBSPL = lCalib;
1740
- await this.writeIsSmartPhone(ID, isSmartPhone, OEM);
1741
- }
1742
-
1743
- this.oldComponentIR = this.componentIR;
1744
-
1745
- let volumeResults = await this.startCalibrationVolume(
1746
- stream,
1747
- gainValues,
1748
- lCalib,
1749
- this.componentGainDBSPL
1750
- );
1751
-
1752
- let impulseResponseResults = await this.startCalibrationImpulseResponse(stream);
1753
- impulseResponseResults['background_noise'] = this.background_noise;
1754
- if (componentIR != null) {
1755
- //insert Freq and Gain from this.componentIR into db
1756
- await this.writeFrqGain(
1757
- ID,
1758
- impulseResponseResults.componentIR.Freq,
1759
- impulseResponseResults.componentIR.Gain,
1760
- OEM
1761
- );
1762
- micInfo['gainDBSPL'] = impulseResponseResults.parameters.gainDBSPL;
1763
- await this.writeGainat1000Hz(ID, micInfo['gainDBSPL'], OEM);
1764
- }
1765
-
1766
- const total_results = {...volumeResults, ...impulseResponseResults};
1767
-
1768
- total_results['micInfo'] = micInfo;
1769
- console.log('total results');
1770
- console.log(total_results);
1771
- return total_results;
1772
- };
1773
- }
1774
-
1775
- export default Combination;
1
+ import AudioCalibrator from '../audioCalibrator';
2
+
3
+ import {sleep, csvToArray, saveToCSV} from '../../utils';
4
+ import database from '../../config/firebase';
5
+ import {ref, set, get, child} from 'firebase/database';
6
+
7
+ /**
8
+ *
9
+ */
10
+ class Combination extends AudioCalibrator {
11
+ /**
12
+ * Default constructor. Creates an instance with any number of paramters passed or the default parameters defined here.
13
+ *
14
+ * @param {Object<boolean, number, number, number>} calibratorParams - paramter object
15
+ * @param {boolean} [calibratorParams.download = false] - boolean flag to download captures
16
+ * @param {number} [calibratorParams.mlsOrder = 18] - order of the MLS to be generated
17
+ * @param {number} [calibratorParams.numCaptures = 5] - number of captures to perform
18
+ * @param {number} [calibratorParams.numMLSPerCapture = 4] - number of bursts of MLS per capture
19
+ */
20
+ constructor({
21
+ download = false,
22
+ mlsOrder = 18,
23
+ numCaptures = 3,
24
+ numMLSPerCapture = 4,
25
+ lowHz = 20,
26
+ highHz = 10000,
27
+ }) {
28
+ super(numCaptures, numMLSPerCapture);
29
+ this.#mlsOrder = parseInt(mlsOrder, 10);
30
+ this.#P = 2 ** mlsOrder - 1;
31
+ this.#download = download;
32
+ this.#mls = [];
33
+ this.#lowHz = lowHz;
34
+ this.#highHz = highHz;
35
+ }
36
+
37
+ /** @private */
38
+ stepNum = 0;
39
+
40
+ /** @private */
41
+ totalSteps = 25;
42
+
43
+ /** @private */
44
+ #download;
45
+
46
+ /** @private */
47
+ #mlsGenInterface;
48
+
49
+ /** @private */
50
+ #mlsBufferView;
51
+
52
+ /** @private */
53
+ componentInvertedImpulseResponse = null;
54
+
55
+ /** @private */
56
+ systemInvertedImpulseResponse = null;
57
+
58
+ //averaged and subtracted ir returned from calibration used to calculated iir
59
+ /** @private */
60
+ ir = null;
61
+
62
+ /** @private */
63
+ impulseResponses = [];
64
+
65
+ /** @private */
66
+ #mlsOrder;
67
+
68
+ /** @private */
69
+ #lowHz;
70
+
71
+ /** @private */
72
+ #highHz;
73
+
74
+ /** @private */
75
+ #mls;
76
+
77
+ /** @private */
78
+ #P;
79
+
80
+ /** @private */
81
+ #audioContext;
82
+
83
+ /** @private */
84
+ TAPER_SECS = 5;
85
+
86
+ /** @private */
87
+ offsetGainNode;
88
+
89
+ /** @private */
90
+ componentConvolution;
91
+
92
+ /** @private */
93
+ systemConvolution;
94
+
95
+ ////////////////////////volume
96
+ /** @private */
97
+ #CALIBRATION_TONE_FREQUENCY = 1000; // Hz
98
+
99
+ /** @private */
100
+ #CALIBRATION_TONE_TYPE = 'sine';
101
+
102
+ CALIBRATION_TONE_DURATION = 5; // seconds
103
+ calibrateSound1000HzPreSec = 3.5;
104
+ calibrateSound1000HzSec = 1.0;
105
+ calibrateSound1000HzPostSec = 0.5;
106
+
107
+ /** @private */
108
+ outDBSPL = null;
109
+ THD = null;
110
+ outDBSPL1000 = null;
111
+
112
+ /** @private */
113
+ TAPER_SECS = 0.01; // seconds
114
+
115
+ /** @private */
116
+ status_denominator = 8;
117
+
118
+ /** @private */
119
+ status_numerator = 0;
120
+
121
+ /** @private */
122
+ percent_complete = 0;
123
+
124
+ /** @private */
125
+ status = ``;
126
+
127
+ /**@private */
128
+ 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>`;
129
+
130
+ /**@private */
131
+ componentIR = null;
132
+
133
+ /**@private */
134
+ oldComponentIR = null;
135
+
136
+ /**@private */
137
+ systemIR = null;
138
+
139
+ /**@private */
140
+ _calibrateSoundCheck = '';
141
+
142
+ deviceType = null;
143
+
144
+ deviceName = null;
145
+
146
+ deviceInfo = null;
147
+
148
+ desired_time_per_mls = 0;
149
+
150
+ num_mls_to_skip = 0;
151
+
152
+ desired_sampling_rate = 0;
153
+
154
+ #currentConvolution = [];
155
+
156
+ mode = 'unfiltered';
157
+
158
+ sourceNode;
159
+
160
+ autocorrelations = [];
161
+
162
+ iirLength = 0;
163
+
164
+ componentInvertedImpulseResponseNoBandpass = [];
165
+
166
+ systemInvertedImpulseResponseNoBandpass = [];
167
+
168
+ _calibrateSoundBackgroundSecs;
169
+
170
+ background_noise = {};
171
+
172
+ numSuccessfulBackgroundCaptured;
173
+
174
+ _calibrateSoundBurstDb;
175
+
176
+ /**generate string template that gets reevaluated as variable increases */
177
+ generateTemplate = () => {
178
+ if (this.percent_complete > 100) {
179
+ this.percent_complete = 100;
180
+ }
181
+ 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>`;
182
+ return template;
183
+ };
184
+
185
+ /** increment numerator and percent for status bar */
186
+ incrementStatusBar = () => {
187
+ this.status_numerator += 1;
188
+ this.percent_complete = (this.status_numerator / this.status_denominator) * 100;
189
+ };
190
+
191
+ setDeviceType = deviceType => {
192
+ this.deviceType = deviceType;
193
+ };
194
+
195
+ setDeviceName = deviceName => {
196
+ this.deviceName = deviceName;
197
+ };
198
+
199
+ setDeviceInfo = deviceInfo => {
200
+ this.deviceInfo = deviceInfo;
201
+ };
202
+
203
+ /** .
204
+ * .
205
+ * .
206
+ * Sends all the computed impulse responses to the backend server for processing
207
+ *
208
+ * @returns sets the resulting inverted impulse response to the class property
209
+ * @example
210
+ */
211
+ sendSystemImpulseResponsesToServerForProcessing = async () => {
212
+ const computedIRs = await Promise.all(this.impulseResponses);
213
+ const filteredComputedIRs = computedIRs.filter(element => {
214
+ return element != undefined;
215
+ }); //log any errors that are found in this step
216
+ const mls = this.#mlsBufferView;
217
+ const lowHz = this.#lowHz; //gain of 1 below cutoff, need gain of 0
218
+ const highHz = this.#highHz; //check error for anything other than 10 kHz
219
+ const iirLength = this.iirLength;
220
+ const num_periods = this.numMLSPerCapture + this.num_mls_to_skip;
221
+ this.stepNum += 1;
222
+ console.log('send impulse responses to server: ' + this.stepNum);
223
+ this.status =
224
+ `All Hz Calibration: computing the IIR...`.toString() + this.generateTemplate().toString();
225
+ this.emit('update', {message: this.status});
226
+ return this.pyServerAPI
227
+ .getSystemInverseImpulseResponseWithRetry({
228
+ payload: filteredComputedIRs.slice(0, this.numCaptures),
229
+ mls,
230
+ lowHz,
231
+ highHz,
232
+ iirLength,
233
+ num_periods,
234
+ sampleRate: this.sourceSamplingRate || 96000,
235
+ })
236
+ .then(res => {
237
+ console.log(res);
238
+ this.stepNum += 1;
239
+ console.log('got impulse response ' + this.stepNum);
240
+ this.incrementStatusBar();
241
+ this.status =
242
+ `All Hz Calibration: done computing the IIR...`.toString() +
243
+ this.generateTemplate().toString();
244
+ this.emit('update', {message: this.status});
245
+ this.systemInvertedImpulseResponse = res['iir'];
246
+ this.systemIR = res['ir'];
247
+ this.systemConvolution = res['convolution'];
248
+ this.systemInvertedImpulseResponseNoBandpass = res['iirNoBandpass'];
249
+ })
250
+ .catch(err => {
251
+ console.error(err);
252
+ });
253
+ };
254
+
255
+ /** .
256
+ * .
257
+ * .
258
+ * Sends all the computed impulse responses to the backend server for processing
259
+ *
260
+ * @returns sets the resulting inverted impulse response to the class property
261
+ * @example
262
+ */
263
+ sendComponentImpulseResponsesToServerForProcessing = async () => {
264
+ const computedIRs = await Promise.all(this.impulseResponses);
265
+ const filteredComputedIRs = computedIRs.filter(element => {
266
+ return element != undefined;
267
+ });
268
+ const componentIRGains = this.componentIR['Gain'];
269
+ const componentIRFreqs = this.componentIR['Freq'];
270
+ const mls = this.#mlsBufferView;
271
+ const lowHz = this.#lowHz;
272
+ const iirLength = this.iirLength;
273
+ const num_periods = this.numMLSPerCapture + this.num_mls_to_skip;
274
+ const highHz = this.#highHz;
275
+ this.stepNum += 1;
276
+ console.log('send impulse responses to server: ' + this.stepNum);
277
+ this.status =
278
+ `All Hz Calibration: computing the IIR...`.toString() + this.generateTemplate().toString();
279
+ this.emit('update', {message: this.status});
280
+ return this.pyServerAPI
281
+ .getComponentInverseImpulseResponseWithRetry({
282
+ payload: filteredComputedIRs.slice(0, this.numCaptures),
283
+ mls,
284
+ lowHz,
285
+ highHz,
286
+ iirLength,
287
+ componentIRGains,
288
+ componentIRFreqs,
289
+ num_periods,
290
+ sampleRate: this.sourceSamplingRate || 96000,
291
+ })
292
+ .then(res => {
293
+ console.log(res);
294
+ this.stepNum += 1;
295
+ console.log('got impulse response ' + this.stepNum);
296
+ this.incrementStatusBar();
297
+ this.status =
298
+ `All Hz Calibration: done computing the IIR...`.toString() +
299
+ this.generateTemplate().toString();
300
+ this.emit('update', {message: this.status});
301
+ this.componentInvertedImpulseResponse = res['iir'];
302
+ this.componentIR['Gain'] = res['ir'];
303
+ this.componentIR['Freq'] = res['frequencies'];
304
+ this.componentConvolution = res['convolution'];
305
+ this.componentInvertedImpulseResponseNoBandpass = res['iirNoBandpass'];
306
+ })
307
+ .catch(err => {
308
+ // this.emit('InvertedImpulseResponse', {res: false});
309
+ console.error(err);
310
+ });
311
+ };
312
+
313
+ sendBackgroundRecording = () => {
314
+ const allSignals = this.getAllBackgroundRecordings();
315
+ const numSignals = allSignals.length;
316
+ const background_rec_whole = allSignals[numSignals - 1];
317
+ const fraction = 0.5 / (this._calibrateSoundBackgroundSecs + 0.5);
318
+ // Calculate the starting index for slicing the array
319
+ const startIndex = Math.round(fraction * background_rec_whole.length);
320
+ // Slice the array from the calculated start index to the end of the array
321
+ const background_rec = background_rec_whole.slice(startIndex);
322
+ console.log('Sending background recording to server for processing');
323
+ this.pyServerAPI
324
+ .getBackgroundNoisePSDWithRetry({
325
+ background_rec,
326
+ sampleRate: this.sourceSamplingRate || 96000,
327
+ })
328
+ .then(res => {
329
+ if (this.numSuccessfulBackgroundCaptured < 1) {
330
+ this.numSuccessfulBackgroundCaptured += 1;
331
+ //storing all background data in background_psd object
332
+ this.background_noise['x_background'] = res['x_background'];
333
+ this.background_noise['y_background'] = res['y_background'];
334
+ this.background_noise['recording'] = background_rec;
335
+ }
336
+ })
337
+ .catch(err => {
338
+ console.error(err);
339
+ });
340
+ };
341
+
342
+ /** .
343
+ * .
344
+ * .
345
+ * Sends the recorded signal, or a given csv string of a signal, to the back end server for processing
346
+ *
347
+ * @param {<array>String} signalCsv - Optional csv string of a previously recorded signal, if given, this signal will be processed
348
+ * @example
349
+ */
350
+ sendRecordingToServerForProcessing = signalCsv => {
351
+ const allSignals = this.getAllUnfilteredRecordedSignals();
352
+ console.log(
353
+ 'Obtaining last all hz unfiltered recording from #allHzUnfilteredRecordings to send to server for processing'
354
+ );
355
+ const numSignals = allSignals.length;
356
+ const mls = this.#mls;
357
+ const payload =
358
+ signalCsv && signalCsv.length > 0 ? csvToArray(signalCsv) : allSignals[numSignals - 1];
359
+ console.log('sending rec');
360
+ this.stepNum += 1;
361
+ console.log('send rec ' + this.stepNum);
362
+ this.status =
363
+ `All Hz Calibration Step: computing the IR of the last recording...`.toString() +
364
+ this.generateTemplate().toString();
365
+ this.emit('update', {message: this.status});
366
+ this.impulseResponses.push(
367
+ this.pyServerAPI
368
+ .getImpulseResponse({
369
+ sampleRate: this.sourceSamplingRate || 96000,
370
+ payload,
371
+ mls,
372
+ P: this.#P, //get rid of this
373
+ numPeriods: this.numMLSPerCapture,
374
+ })
375
+ .then(res => {
376
+ if (this.numSuccessfulCaptured < this.numCaptures) {
377
+ this.numSuccessfulCaptured += 1;
378
+ console.log('num succ capt: ' + this.numSuccessfulCaptured);
379
+ this.stepNum += 1;
380
+ console.log('got impulse response ' + this.stepNum);
381
+ this.incrementStatusBar();
382
+ this.status =
383
+ `All Hz Calibration: ${this.numSuccessfulCaptured}/${this.numCaptures} IRs computed...`.toString() +
384
+ this.generateTemplate().toString();
385
+ this.emit('update', {
386
+ message: this.status,
387
+ });
388
+ this.autocorrelations.push(res['autocorrelation']);
389
+ return res['ir'];
390
+ }
391
+ })
392
+ .catch(err => {
393
+ console.error(err);
394
+ })
395
+ );
396
+ };
397
+
398
+ /**
399
+ * Passed to the calibration steps function, awaits the desired amount of seconds to capture the desired number
400
+ * of MLS periods defined in the constructor.
401
+ *
402
+ * @example
403
+ */
404
+ #awaitDesiredMLSLength = async () => {
405
+ // seconds per MLS = P / SR
406
+ // await N * P / SR
407
+ this.stepNum += 1;
408
+ console.log('await desired length ' + this.stepNum);
409
+ this.status =
410
+ `All Hz Calibration: sampling the calibration signal...`.toString() +
411
+ `\niteration ${this.stepNum}` +
412
+ this.generateTemplate();
413
+ this.emit('update', {
414
+ message: this.status,
415
+ });
416
+ let time_to_wait = 0;
417
+ if (this.mode === 'unfiltered') {
418
+ //unfiltered
419
+ time_to_wait = (this.#mls.length / this.sourceSamplingRate) * this.numMLSPerCapture;
420
+ time_to_wait = time_to_wait * 1.1;
421
+ } else if (this.mode === 'filtered') {
422
+ //filtered
423
+ // time_to_wait =
424
+ // (this.#currentConvolution.length / this.sourceSamplingRate) *
425
+ // (this.numMLSPerCapture / (this.num_mls_to_skip + this.numMLSPerCapture));
426
+ time_to_wait =
427
+ (this.#currentConvolution.length / this.sourceSamplingRate) * this.numMLSPerCapture;
428
+ time_to_wait = time_to_wait * 1.1;
429
+ } else {
430
+ throw new Error('Mode broke in awaitDesiredMLSLength');
431
+ }
432
+
433
+ await sleep(time_to_wait);
434
+ };
435
+
436
+ /**
437
+ * Passed to the background noise recording function, awaits the desired amount of seconds to capture the desired number
438
+ * of seconds of background noise
439
+ *
440
+ * @example
441
+ */
442
+ #awaitBackgroundNoiseRecording = async () => {
443
+ console.log(
444
+ 'Waiting ' + this._calibrateSoundBackgroundSecs + ' second(s) to record background noise'
445
+ );
446
+ let time_to_wait = this._calibrateSoundBackgroundSecs + 0.5;
447
+ await sleep(time_to_wait);
448
+ };
449
+
450
+ /** .
451
+ * .
452
+ * .
453
+ * Passed to the calibration steps function, awaits the onset of the signal to ensure a steady state
454
+ *
455
+ * @example
456
+ */
457
+ #awaitSignalOnset = async () => {
458
+ this.stepNum += 1;
459
+ console.log('await signal onset ' + this.stepNum);
460
+ this.status =
461
+ `All Hz Calibration: waiting for the signal to stabilize...`.toString() +
462
+ this.generateTemplate();
463
+ this.emit('update', {
464
+ message: this.status,
465
+ });
466
+ let number_of_bursts_to_skip = this.num_mls_to_skip;
467
+ let time_to_sleep = 0;
468
+ if (this.mode === 'unfiltered') {
469
+ time_to_sleep = (this.#mls.length / this.sourceSamplingRate) * number_of_bursts_to_skip;
470
+ } else if (this.mode === 'filtered') {
471
+ console.log(this.#currentConvolution.length);
472
+ // time_to_sleep =
473
+ // (this.#currentConvolution.length / this.sourceSamplingRate) *
474
+ // (number_of_bursts_to_skip / (number_of_bursts_to_skip + this.numMLSPerCapture));
475
+ time_to_sleep =
476
+ (this.#currentConvolution.length / this.sourceSamplingRate) * number_of_bursts_to_skip;
477
+ } else {
478
+ throw new Error('Mode broke in awaitSignalOnset');
479
+ }
480
+ await sleep(time_to_sleep);
481
+ };
482
+
483
+ /**
484
+ * Called immediately after a recording is captured. Used to process the resulting signal
485
+ * whether by sending the result to a server or by computing a result locally.
486
+ *
487
+ * @example
488
+ */
489
+ #afterMLSRecord = () => {
490
+ console.log('after record');
491
+ this.sendRecordingToServerForProcessing();
492
+ };
493
+
494
+ #afterMLSwIIRRecord = () => {
495
+ if (this.numSuccessfulCaptured < 1) {
496
+ this.numSuccessfulCaptured += 1;
497
+ this.stepNum += 1;
498
+ this.incrementStatusBar();
499
+ console.log('after mls w iir record for some reason add numSucc capt ' + this.stepNum);
500
+ this.status =
501
+ `All Hz Calibration: ${this.numSuccessfulCaptured} recording of convolved MLS captured`.toString() +
502
+ this.generateTemplate().toString();
503
+ this.emit('update', {
504
+ message: this.status,
505
+ });
506
+ }
507
+ };
508
+
509
+ /** .
510
+ * .
511
+ * .
512
+ * Created an S Curver Buffer to taper the signal onset
513
+ *
514
+ * @param {*} length
515
+ * @param {*} phase
516
+ * @returns
517
+ * @example
518
+ */
519
+ static createSCurveBuffer = (length, phase) => {
520
+ const curve = new Float32Array(length);
521
+ let i;
522
+ for (i = 0; i < length; i += 1) {
523
+ // scale the curve to be between 0-1
524
+ curve[i] = Math.sin((Math.PI * i) / length - phase) / 2 + 0.5;
525
+ }
526
+ return curve;
527
+ };
528
+
529
+ static createInverseSCurveBuffer = (length, phase) => {
530
+ const curve = new Float32Array(length);
531
+ let i;
532
+ let j = length - 1;
533
+ for (i = 0; i < length; i += 1) {
534
+ // scale the curve to be between 0-1
535
+ curve[i] = Math.sin((Math.PI * j) / length - phase) / 2 + 0.5;
536
+ j -= 1;
537
+ }
538
+ return curve;
539
+ };
540
+
541
+ /**
542
+ * Construct a Calibration Node with the calibration parameters.
543
+ *
544
+ * @param dataBuffer
545
+ * @private
546
+ * @example
547
+ */
548
+ #createCalibrationNodeFromBuffer = dataBuffer => {
549
+ console.log('length databuffer');
550
+ console.log(dataBuffer.length);
551
+ if (!this.sourceAudioContext) {
552
+ this.makeNewSourceAudioContext();
553
+ }
554
+
555
+ const buffer = this.sourceAudioContext.createBuffer(
556
+ 1, // number of channels
557
+ dataBuffer.length,
558
+ this.sourceAudioContext.sampleRate // sample rate
559
+ );
560
+
561
+ const data = buffer.getChannelData(0); // get data
562
+ // fill the buffer with our data
563
+ try {
564
+ for (let i = 0; i < dataBuffer.length; i += 1) {
565
+ data[i] = dataBuffer[i] * this._calibrateSoundBurstDb;
566
+ }
567
+ } catch (error) {
568
+ console.error(error);
569
+ }
570
+
571
+ this.sourceNode = this.sourceAudioContext.createBufferSource();
572
+
573
+ this.sourceNode.buffer = buffer;
574
+ if (this.mode === 'filtered') {
575
+ //used to not loop filtered
576
+ this.sourceNode.loop = true;
577
+ } else {
578
+ this.sourceNode.loop = true;
579
+ }
580
+
581
+ this.sourceNode.connect(this.sourceAudioContext.destination);
582
+
583
+ this.addCalibrationNode(this.sourceNode);
584
+ };
585
+
586
+ /**
587
+ * Given a data buffer, creates the required calibration node
588
+ *
589
+ * @param {*} dataBufferArray
590
+ * @example
591
+ */
592
+ #setCalibrationNodesFromBuffer = (dataBufferArray = [this.#mlsBufferView]) => {
593
+ if (dataBufferArray.length === 1) {
594
+ this.#createCalibrationNodeFromBuffer(dataBufferArray[0]);
595
+ } else {
596
+ throw new Error('The length of the data buffer array must be 1');
597
+ }
598
+ };
599
+
600
+ /**
601
+ * Creates an audio context and plays it for a few seconds.
602
+ *
603
+ * @private
604
+ * @returns - Resolves when the audio is done playing.
605
+ * @example
606
+ */
607
+ #playCalibrationAudio = () => {
608
+ this.calibrationNodes[0].start(0);
609
+ this.status = ``;
610
+ if (this.mode === 'unfiltered') {
611
+ this.#mls = this.calibrationNodes[0].buffer.getChannelData(0);
612
+ console.log("mls", this.#mls);
613
+ console.log("mls buffer view", this.#mlsBufferView);
614
+ console.log('play calibration audio ' + this.stepNum);
615
+ this.status =
616
+ `All Hz Calibration: playing the calibration tone...`.toString() +
617
+ this.generateTemplate().toString();
618
+ } else if (this.mode === 'filtered') {
619
+ console.log('play convolved audio ' + this.stepNum);
620
+ this.status =
621
+ `All Hz Calibration: playing the convolved calibration tone...`.toString() +
622
+ this.generateTemplate().toString();
623
+ } else {
624
+ throw new Error('Mode is incorrect');
625
+ }
626
+ this.emit('update', {message: this.status});
627
+ this.stepNum += 1;
628
+ console.log('sink sampling rate');
629
+ console.log(this.sinkSamplingRate);
630
+ console.log('source sampling rate');
631
+ console.log(this.sourceSamplingRate);
632
+ };
633
+
634
+ /** .
635
+ * .
636
+ * .
637
+ * Stops the audio with tapered offset
638
+ *
639
+ * @example
640
+ */
641
+ #stopCalibrationAudio = () => {
642
+ this.calibrationNodes[0].stop(0);
643
+ this.calibrationNodes = [];
644
+ this.sourceNode.disconnect();
645
+ this.stepNum += 1;
646
+ console.log('stop calibration audio ' + this.stepNum);
647
+ this.status =
648
+ `All Hz Calibration: stopping the calibration tone...`.toString() +
649
+ this.generateTemplate().toString();
650
+ this.emit('update', {message: this.status});
651
+ };
652
+
653
+ playMLSwithIIR = async (stream, convolution) => {
654
+ let checkRec = false;
655
+ this.mode = 'filtered';
656
+ console.log('play mls with iir');
657
+ //this.invertedImpulseResponse = iir;
658
+
659
+ await this.calibrationSteps(
660
+ stream,
661
+ this.#playCalibrationAudio, // play audio func (required)
662
+ this.#createCalibrationNodeFromBuffer(convolution), // before play func
663
+ this.#awaitSignalOnset, // before record
664
+ () => this.numSuccessfulCaptured < 1,
665
+ this.#awaitDesiredMLSLength, // during record
666
+ this.#afterMLSwIIRRecord, // after record
667
+ this.mode,
668
+ checkRec
669
+ );
670
+ };
671
+
672
+ bothSoundCheck = async stream => {
673
+ let iir_ir_and_plots;
674
+ this.#currentConvolution = this.componentConvolution;
675
+ await this.playMLSwithIIR(stream, this.#currentConvolution);
676
+ this.#stopCalibrationAudio();
677
+ let component_conv_recs = this.getAllFilteredRecordedSignals();
678
+ let return_component_conv_rec = component_conv_recs[0];
679
+ this.clearAllFilteredRecordedSignals();
680
+ this.#currentConvolution = this.systemConvolution;
681
+ await this.playMLSwithIIR(stream, this.#currentConvolution);
682
+ this.#stopCalibrationAudio();
683
+ let system_conv_recs = this.getAllFilteredRecordedSignals();
684
+ let return_system_conv_rec = system_conv_recs[0];
685
+ this.sourceAudioContext.close();
686
+ let recs = this.getAllUnfilteredRecordedSignals();
687
+ let unconv_rec = recs[0];
688
+ let return_unconv_rec = unconv_rec;
689
+ let conv_rec = component_conv_recs[0];
690
+
691
+ //psd of component
692
+ let knownGain = this.oldComponentIR.Gain;
693
+ let knownFreq = this.oldComponentIR.Freq;
694
+ let sampleRate = this.sourceSamplingRate || 96000;
695
+ let component_unconv_rec_psd = await this.pyServerAPI
696
+ .getSubtractedPSDWithRetry(unconv_rec, knownGain, knownFreq, sampleRate)
697
+ .then(res => {
698
+ this.incrementStatusBar();
699
+ this.status =
700
+ `All Hz Calibration: done computing the PSD graphs...`.toString() +
701
+ this.generateTemplate().toString();
702
+ this.emit('update', {message: this.status});
703
+ return res;
704
+ })
705
+ .catch(err => {
706
+ console.error(err);
707
+ });
708
+
709
+ let component_conv_rec_psd = await this.pyServerAPI
710
+ .getSubtractedPSDWithRetry(conv_rec, knownGain, knownFreq, sampleRate)
711
+ .then(res => {
712
+ this.incrementStatusBar();
713
+ this.status =
714
+ `All Hz Calibration: done computing the PSD graphs...`.toString() +
715
+ this.generateTemplate().toString();
716
+ this.emit('update', {message: this.status});
717
+ return res;
718
+ })
719
+ .catch(err => {
720
+ console.error(err);
721
+ });
722
+
723
+ conv_rec = system_conv_recs[0];
724
+ //psd of system
725
+ let system_recs_psd = await this.pyServerAPI
726
+ .getPSDWithRetry({
727
+ unconv_rec,
728
+ conv_rec,
729
+ sampleRate: this.sourceSamplingRate || 96000,
730
+ })
731
+ .then(res => {
732
+ this.incrementStatusBar();
733
+ this.status =
734
+ `All Hz Calibration: done computing the PSD graphs...`.toString() +
735
+ this.generateTemplate().toString();
736
+ this.emit('update', {message: this.status});
737
+ return res;
738
+ })
739
+ .catch(err => {
740
+ console.error(err);
741
+ });
742
+
743
+ //iir w/ and without bandpass psd. done
744
+ unconv_rec = this.componentInvertedImpulseResponseNoBandpass;
745
+ conv_rec = this.componentInvertedImpulseResponse;
746
+ let component_iir_psd = await this.pyServerAPI
747
+ .getPSDWithRetry({
748
+ unconv_rec,
749
+ conv_rec,
750
+ sampleRate: this.sourceSamplingRate || 96000,
751
+ })
752
+ .then(res => {
753
+ this.incrementStatusBar();
754
+ this.status =
755
+ `All Hz Calibration: done computing the PSD graphs...`.toString() +
756
+ this.generateTemplate().toString();
757
+ this.emit('update', {message: this.status});
758
+ return res;
759
+ })
760
+ .catch(err => {
761
+ console.error(err);
762
+ });
763
+ unconv_rec = this.systemInvertedImpulseResponseNoBandpass;
764
+ conv_rec = this.systemInvertedImpulseResponse;
765
+ let system_iir_psd = await this.pyServerAPI
766
+ .getPSDWithRetry({
767
+ unconv_rec,
768
+ conv_rec,
769
+ sampleRate: this.sourceSamplingRate || 96000,
770
+ })
771
+ .then(res => {
772
+ this.incrementStatusBar();
773
+ this.status =
774
+ `All Hz Calibration: done computing the PSD graphs...`.toString() +
775
+ this.generateTemplate().toString();
776
+ this.emit('update', {message: this.status});
777
+ return res;
778
+ })
779
+ .catch(err => {
780
+ console.error(err);
781
+ });
782
+
783
+ iir_ir_and_plots = {
784
+ filtered_recording: {
785
+ component: return_component_conv_rec,
786
+ system: return_system_conv_rec,
787
+ },
788
+ unfiltered_recording: this.getAllUnfilteredRecordedSignals()[0],
789
+ system: {
790
+ iir: this.systemInvertedImpulseResponse,
791
+ ir: this.systemIR,
792
+ iir_psd: {
793
+ y: system_iir_psd['y_conv'],
794
+ x: system_iir_psd['x_conv'],
795
+ y_no_bandpass: system_iir_psd['y_unconv'],
796
+ x_no_bandpass: system_iir_psd['x_unconv'],
797
+ },
798
+ convolution: this.systemConvolution,
799
+ psd: {
800
+ unconv: {
801
+ x: system_recs_psd['x_unconv'],
802
+ y: system_recs_psd['y_unconv'],
803
+ },
804
+ conv: {
805
+ x: system_recs_psd['x_conv'],
806
+ y: system_recs_psd['y_conv'],
807
+ },
808
+ },
809
+ },
810
+ component: {
811
+ iir: this.componentInvertedImpulseResponse,
812
+ ir: this.componentIR,
813
+ iir_psd: {
814
+ y: component_iir_psd['y_conv'],
815
+ x: component_iir_psd['x_conv'],
816
+ y_no_bandpass: component_iir_psd['y_unconv'],
817
+ x_no_bandpass: component_iir_psd['x_unconv'],
818
+ },
819
+ convolution: this.componentConvolution,
820
+ psd: {
821
+ unconv: {
822
+ x: component_unconv_rec_psd['x'],
823
+ y: component_unconv_rec_psd['y'],
824
+ },
825
+ conv: {
826
+ x: component_conv_rec_psd['x'],
827
+ y: component_conv_rec_psd['y'],
828
+ },
829
+ },
830
+ },
831
+ mls: this.#mls,
832
+ autocorrelations: this.autocorrelations,
833
+ impulseResponses: [],
834
+ };
835
+
836
+ return iir_ir_and_plots;
837
+ };
838
+
839
+ singleSoundCheck = async stream => {
840
+ let iir_ir_and_plots;
841
+ if (this._calibrateSoundCheck != 'system') {
842
+ this.#currentConvolution = this.componentConvolution;
843
+ } else {
844
+ this.#currentConvolution = this.systemConvolution;
845
+ }
846
+ await this.playMLSwithIIR(stream, this.#currentConvolution);
847
+ this.#stopCalibrationAudio();
848
+ this.sourceAudioContext.close();
849
+ let conv_recs = this.getAllFilteredRecordedSignals();
850
+ let recs = this.getAllUnfilteredRecordedSignals();
851
+ console.log('Obtaining unfiltered recording from #allHzUnfilteredRecordings to calculate PSD');
852
+ console.log('Obtaining filtered recording from #allHzFilteredRecordings to calculate PSD');
853
+ let unconv_rec = recs[0];
854
+ let return_unconv_rec = unconv_rec;
855
+ let conv_rec = conv_recs[0];
856
+ let return_conv_rec = conv_rec;
857
+ if (this._calibrateSoundCheck != 'system') {
858
+ let knownGain = this.oldComponentIR.Gain;
859
+ let knownFreq = this.oldComponentIR.Freq;
860
+ let sampleRate = this.sourceSamplingRate || 96000;
861
+ let unconv_results = await this.pyServerAPI
862
+ .getSubtractedPSDWithRetry(unconv_rec, knownGain, knownFreq, sampleRate)
863
+ .then(res => {
864
+ this.incrementStatusBar();
865
+ this.status =
866
+ `All Hz Calibration: done computing the PSD graphs...`.toString() +
867
+ this.generateTemplate().toString();
868
+ this.emit('update', {message: this.status});
869
+ return res;
870
+ })
871
+ .catch(err => {
872
+ console.error(err);
873
+ });
874
+
875
+ let conv_results = await this.pyServerAPI
876
+ .getSubtractedPSDWithRetry(conv_rec, knownGain, knownFreq, sampleRate)
877
+ .then(res => {
878
+ this.incrementStatusBar();
879
+ this.status =
880
+ `All Hz Calibration: done computing the PSD graphs...`.toString() +
881
+ this.generateTemplate().toString();
882
+ this.emit('update', {message: this.status});
883
+ return res;
884
+ })
885
+ .catch(err => {
886
+ console.error(err);
887
+ });
888
+ unconv_rec = this.componentInvertedImpulseResponseNoBandpass;
889
+ conv_rec = this.componentInvertedImpulseResponse;
890
+ let component_iir_psd = await this.pyServerAPI
891
+ .getPSDWithRetry({
892
+ unconv_rec,
893
+ conv_rec,
894
+ sampleRate: this.sourceSamplingRate || 96000,
895
+ })
896
+ .then(res => {
897
+ this.incrementStatusBar();
898
+ this.status =
899
+ `All Hz Calibration: done computing the PSD graphs...`.toString() +
900
+ this.generateTemplate().toString();
901
+ this.emit('update', {message: this.status});
902
+ return res;
903
+ })
904
+ .catch(err => {
905
+ console.error(err);
906
+ });
907
+ unconv_rec = this.systemInvertedImpulseResponseNoBandpass;
908
+ conv_rec = this.systemInvertedImpulseResponse;
909
+ let system_iir_psd = await this.pyServerAPI
910
+ .getPSDWithRetry({
911
+ unconv_rec,
912
+ conv_rec,
913
+ sampleRate: this.sourceSamplingRate || 96000,
914
+ })
915
+ .then(res => {
916
+ this.incrementStatusBar();
917
+ this.status =
918
+ `All Hz Calibration: done computing the PSD graphs...`.toString() +
919
+ this.generateTemplate().toString();
920
+ this.emit('update', {message: this.status});
921
+ return res;
922
+ })
923
+ .catch(err => {
924
+ console.error(err);
925
+ });
926
+
927
+ iir_ir_and_plots = {
928
+ unfiltered_recording: return_unconv_rec,
929
+ filtered_recording: return_conv_rec,
930
+ system: {
931
+ iir: this.systemInvertedImpulseResponse,
932
+ ir: this.systemIR,
933
+ iir_psd: {
934
+ y: system_iir_psd['y_conv'],
935
+ x: system_iir_psd['y_conv'],
936
+ y_no_bandpass: system_iir_psd['y_unconv'],
937
+ x_no_bandpass: system_iir_psd['x_unconv'],
938
+ },
939
+ filtered_recording: [],
940
+ convolution: this.systemConvolution,
941
+ psd: {
942
+ unconv: {
943
+ x: [],
944
+ y: [],
945
+ },
946
+ conv: {
947
+ x: [],
948
+ y: [],
949
+ },
950
+ },
951
+ },
952
+ component: {
953
+ iir: this.componentInvertedImpulseResponse,
954
+ ir: this.componentIR,
955
+ iir_psd: {
956
+ y: component_iir_psd['y_conv'],
957
+ x: component_iir_psd['x_conv'],
958
+ y_no_bandpass: component_iir_psd['y_unconv'],
959
+ x_no_bandpass: component_iir_psd['x_unconv'],
960
+ },
961
+ convolution: this.componentConvolution,
962
+ psd: {
963
+ unconv: {
964
+ x: unconv_results['x'],
965
+ y: unconv_results['y'],
966
+ },
967
+ conv: {
968
+ x: conv_results['x'],
969
+ y: conv_results['y'],
970
+ },
971
+ },
972
+ },
973
+ mls: this.#mls,
974
+ autocorrelations: this.autocorrelations,
975
+ impulseResponses: [],
976
+ };
977
+ } else {
978
+ let results = await this.pyServerAPI
979
+ .getPSDWithRetry({
980
+ unconv_rec,
981
+ conv_rec,
982
+ sampleRate: this.sourceSamplingRate || 96000,
983
+ })
984
+ .then(res => {
985
+ this.incrementStatusBar();
986
+ this.status =
987
+ `All Hz Calibration: done computing the PSD graphs...`.toString() +
988
+ this.generateTemplate().toString();
989
+ this.emit('update', {message: this.status});
990
+ return res;
991
+ })
992
+ .catch(err => {
993
+ console.error(err);
994
+ });
995
+
996
+ //iir w/ and without bandpass psd
997
+ unconv_rec = this.componentInvertedImpulseResponseNoBandpass;
998
+ conv_rec = this.componentInvertedImpulseResponse;
999
+ let component_iir_psd = await this.pyServerAPI
1000
+ .getPSDWithRetry({
1001
+ unconv_rec,
1002
+ conv_rec,
1003
+ sampleRate: this.sourceSamplingRate || 96000,
1004
+ })
1005
+ .then(res => {
1006
+ this.incrementStatusBar();
1007
+ this.status =
1008
+ `All Hz Calibration: done computing the PSD graphs...`.toString() +
1009
+ this.generateTemplate().toString();
1010
+ this.emit('update', {message: this.status});
1011
+ return res;
1012
+ })
1013
+ .catch(err => {
1014
+ console.error(err);
1015
+ });
1016
+ unconv_rec = this.systemInvertedImpulseResponseNoBandpass;
1017
+ conv_rec = this.systemInvertedImpulseResponse;
1018
+ let system_iir_psd = await this.pyServerAPI
1019
+ .getPSDWithRetry({
1020
+ unconv_rec,
1021
+ conv_rec,
1022
+ sampleRate: this.sourceSamplingRate || 96000,
1023
+ })
1024
+ .then(res => {
1025
+ this.incrementStatusBar();
1026
+ this.status =
1027
+ `All Hz Calibration: done computing the PSD graphs...`.toString() +
1028
+ this.generateTemplate().toString();
1029
+ this.emit('update', {message: this.status});
1030
+ return res;
1031
+ })
1032
+ .catch(err => {
1033
+ console.error(err);
1034
+ });
1035
+
1036
+ iir_ir_and_plots = {
1037
+ unfiltered_recording: return_unconv_rec,
1038
+ filtered_recording: return_conv_rec,
1039
+ system: {
1040
+ iir: this.systemInvertedImpulseResponse,
1041
+ ir: this.systemIR,
1042
+ iir_psd: {
1043
+ y: system_iir_psd['y_conv'],
1044
+ x: system_iir_psd['y_conv'],
1045
+ y_no_bandpass: system_iir_psd['y_unconv'],
1046
+ x_no_bandpass: system_iir_psd['x_unconv'],
1047
+ },
1048
+ filtered_recording: [],
1049
+ convolution: this.systemConvolution,
1050
+ psd: {
1051
+ unconv: {
1052
+ x: results['x_unconv'],
1053
+ y: results['y_unconv'],
1054
+ },
1055
+ conv: {
1056
+ x: results['x_conv'],
1057
+ y: results['y_conv'],
1058
+ },
1059
+ },
1060
+ },
1061
+ component: {
1062
+ iir: this.componentInvertedImpulseResponse,
1063
+ ir: this.componentIR,
1064
+ iir_psd: {
1065
+ y: component_iir_psd['y_conv'],
1066
+ x: component_iir_psd['x_conv'],
1067
+ y_no_bandpass: component_iir_psd['y_unconv'],
1068
+ x_no_bandpass: component_iir_psd['x_unconv'],
1069
+ },
1070
+ convolution: this.componentConvolution,
1071
+ psd: {
1072
+ unconv: {
1073
+ x: [],
1074
+ y: [],
1075
+ },
1076
+ conv: {
1077
+ x: [],
1078
+ y: [],
1079
+ },
1080
+ },
1081
+ },
1082
+ mls: this.#mls,
1083
+ autocorrelations: this.autocorrelations,
1084
+ impulseResponses: [],
1085
+ };
1086
+ }
1087
+ await Promise.all(this.impulseResponses).then(res => {
1088
+ for (let i = 0; i < res.length; i++) {
1089
+ if (res[i] != undefined) {
1090
+ iir_ir_and_plots['impulseResponses'].push(res[i]);
1091
+ }
1092
+ }
1093
+ });
1094
+
1095
+ if (this.#download) {
1096
+ this.downloadSingleUnfilteredRecording();
1097
+ this.downloadSingleFilteredRecording();
1098
+ saveToCSV(this.#mls, 'MLS.csv');
1099
+ saveToCSV(this.componentConvolution, 'python_component_convolution_mls_iir.csv');
1100
+ saveToCSV(this.systemConvolution, 'python_system_convolution_mls_iir.csv');
1101
+ saveToCSV(this.componentInvertedImpulseResponse, 'componentIIR.csv');
1102
+ saveToCSV(this.systemInvertedImpulseResponse, 'systemIIR.csv');
1103
+ for (let i = 0; i < this.autocorrelations.length; i++) {
1104
+ saveToCSV(this.autocorrelations[i], `autocorrelation_${i}`);
1105
+ }
1106
+ const computedIRagain = await Promise.all(this.impulseResponses).then(res => {
1107
+ for (let i = 0; i < res.length; i++) {
1108
+ if (res[i] != undefined) {
1109
+ saveToCSV(res[i], `IR_${i}`);
1110
+ }
1111
+ }
1112
+ });
1113
+ }
1114
+
1115
+ return iir_ir_and_plots;
1116
+ };
1117
+
1118
+ /**
1119
+ * Public method to start the calibration process. Objects intialized from webassembly allocate new memory
1120
+ * and must be manually freed. This function is responsible for intializing the MlsGenInterface,
1121
+ * and wrapping the calibration steps with a garbage collection safe gaurd.
1122
+ *
1123
+ * @public
1124
+ * @param stream - The stream of audio from the Listener.
1125
+ * @example
1126
+ */
1127
+ startCalibrationImpulseResponse = async stream => {
1128
+ let desired_time = this.desired_time_per_mls;
1129
+ let checkRec = 'allhz';
1130
+
1131
+ console.log('MLS sequence should be of length: ' + this.sourceSamplingRate * desired_time);
1132
+
1133
+ length = this.sourceSamplingRate * desired_time;
1134
+ //get mls here
1135
+ await this.pyServerAPI
1136
+ .getMLSWithRetry(length)
1137
+ .then(res => {
1138
+ console.log(res);
1139
+ this.#mlsBufferView = res['mls'];
1140
+ })
1141
+ .catch(err => {
1142
+ // this.emit('InvertedImpulseResponse', {res: false});
1143
+ console.error(err);
1144
+ });
1145
+ this.numSuccessfulBackgroundCaptured = 0;
1146
+ if (this._calibrateSoundBackgroundSecs > 0) {
1147
+ this.mode = 'background';
1148
+ this.status =
1149
+ `All Hz Calibration: sampling the background noise...`.toString() +
1150
+ this.generateTemplate().toString();
1151
+ this.emit('update', {message: this.status});
1152
+ await this.recordBackground(
1153
+ stream, //stream
1154
+ () => this.numSuccessfulBackgroundCaptured < 1, //loop condition
1155
+ this.#awaitBackgroundNoiseRecording, //sleep to record
1156
+ this.sendBackgroundRecording, //send to get PSD
1157
+ this.mode,
1158
+ checkRec
1159
+ );
1160
+ this.incrementStatusBar();
1161
+ }
1162
+ this.mode = 'unfiltered';
1163
+ this.numSuccessfulCaptured = 0;
1164
+
1165
+ await this.calibrationSteps(
1166
+ stream,
1167
+ this.#playCalibrationAudio, // play audio func (required)
1168
+ this.#createCalibrationNodeFromBuffer(this.#mlsBufferView), // before play func
1169
+ this.#awaitSignalOnset, // before record
1170
+ () => this.numSuccessfulCaptured < this.numCaptures, // loop while true
1171
+ this.#awaitDesiredMLSLength, // during record
1172
+ this.#afterMLSRecord, // after record
1173
+ this.mode,
1174
+ checkRec
1175
+ ),
1176
+ this.#stopCalibrationAudio();
1177
+ checkRec = false;
1178
+
1179
+ // at this stage we've captured all the required signals,
1180
+ // and have received IRs for each one
1181
+ // so let's send all the IRs to the server to be converted to a single IIR
1182
+ await this.sendSystemImpulseResponsesToServerForProcessing();
1183
+ await this.sendComponentImpulseResponsesToServerForProcessing();
1184
+
1185
+ this.numSuccessfulCaptured = 0;
1186
+
1187
+ let iir_ir_and_plots;
1188
+ if (this._calibrateSoundCheck != 'none') {
1189
+ //do single check
1190
+ if (this._calibrateSoundCheck == 'goal' || this._calibrateSoundCheck == 'system') {
1191
+ iir_ir_and_plots = await this.singleSoundCheck(stream);
1192
+ } else {
1193
+ //both
1194
+ iir_ir_and_plots = await this.bothSoundCheck(stream);
1195
+ }
1196
+ } else {
1197
+ let unconv_rec = this.componentInvertedImpulseResponseNoBandpass;
1198
+ let conv_rec = this.componentInvertedImpulseResponse;
1199
+ let component_iir_psd = await this.pyServerAPI
1200
+ .getPSDWithRetry({
1201
+ unconv_rec,
1202
+ conv_rec,
1203
+ sampleRate: this.sourceSamplingRate || 96000,
1204
+ })
1205
+ .then(res => {
1206
+ this.incrementStatusBar();
1207
+ this.status =
1208
+ `All Hz Calibration: done computing the PSD graphs...`.toString() +
1209
+ this.generateTemplate().toString();
1210
+ this.emit('update', {message: this.status});
1211
+ return res;
1212
+ })
1213
+ .catch(err => {
1214
+ console.error(err);
1215
+ });
1216
+ unconv_rec = this.systemInvertedImpulseResponseNoBandpass;
1217
+ conv_rec = this.systemInvertedImpulseResponse;
1218
+ let system_iir_psd = await this.pyServerAPI
1219
+ .getPSDWithRetry({
1220
+ unconv_rec,
1221
+ conv_rec,
1222
+ sampleRate: this.sourceSamplingRate || 96000,
1223
+ })
1224
+ .then(res => {
1225
+ this.incrementStatusBar();
1226
+ this.status =
1227
+ `All Hz Calibration: done computing the PSD graphs...`.toString() +
1228
+ this.generateTemplate().toString();
1229
+ this.emit('update', {message: this.status});
1230
+ return res;
1231
+ })
1232
+ .catch(err => {
1233
+ console.error(err);
1234
+ });
1235
+
1236
+ iir_ir_and_plots = {
1237
+ unfiltered_recording: return_unconv_rec,
1238
+ filtered_recording: return_conv_rec,
1239
+ system: {
1240
+ iir: this.systemInvertedImpulseResponse,
1241
+ ir: this.systemIR,
1242
+ iir_psd: {
1243
+ y: system_iir_psd['y_conv'],
1244
+ x: system_iir_psd['y_conv'],
1245
+ y_no_bandpass: system_iir_psd['y_unconv'],
1246
+ x_no_bandpass: system_iir_psd['x_unconv'],
1247
+ },
1248
+ filtered_recording: [],
1249
+ convolution: this.systemConvolution,
1250
+ psd: {
1251
+ unconv: {
1252
+ x: [],
1253
+ y: [],
1254
+ },
1255
+ conv: {
1256
+ x: [],
1257
+ y: [],
1258
+ },
1259
+ },
1260
+ },
1261
+ component: {
1262
+ iir: this.componentInvertedImpulseResponse,
1263
+ ir: this.componentIR,
1264
+ iir_psd: {
1265
+ y: component_iir_psd['y_conv'],
1266
+ x: component_iir_psd['x_conv'],
1267
+ y_no_bandpass: component_iir_psd['y_unconv'],
1268
+ x_no_bandpass: component_iir_psd['x_unconv'],
1269
+ },
1270
+ convolution: this.componentConvolution,
1271
+ psd: {
1272
+ unconv: {
1273
+ x: [],
1274
+ y: [],
1275
+ },
1276
+ conv: {
1277
+ x: [],
1278
+ y: [],
1279
+ },
1280
+ },
1281
+ },
1282
+ mls: this.#mls,
1283
+ autocorrelations: this.autocorrelations,
1284
+ impulseResponses: [],
1285
+ };
1286
+ await Promise.all(this.impulseResponses).then(res => {
1287
+ for (let i = 0; i < res.length; i++) {
1288
+ if (res[i] != undefined) {
1289
+ iir_ir_and_plots['impulseResponses'].push(res[i]);
1290
+ }
1291
+ }
1292
+ });
1293
+
1294
+ if (this.#download) {
1295
+ saveToCSV(this.#mls, 'MLS.csv');
1296
+ saveToCSV(this.componentConvolution, 'python_component_convolution_mls_iir.csv');
1297
+ saveToCSV(this.systemConvolution, 'python_system_convolution_mls_iir.csv');
1298
+ saveToCSV(this.componentInvertedImpulseResponse, 'componentIIR.csv');
1299
+ saveToCSV(this.systemInvertedImpulseResponse, 'systemIIR.csv');
1300
+ for (let i = 0; i < this.autocorrelations.length; i++) {
1301
+ saveToCSV(this.autocorrelations[i], `autocorrelation_${i}`);
1302
+ }
1303
+ const computedIRagain = await Promise.all(this.impulseResponses).then(res => {
1304
+ for (let i = 0; i < res.length; i++) {
1305
+ if (res[i] != undefined) {
1306
+ saveToCSV(res[i], `IR_${i}`);
1307
+ }
1308
+ }
1309
+ });
1310
+ }
1311
+ }
1312
+
1313
+ this.percent_complete = 100;
1314
+
1315
+ this.status = `All Hz Calibration: Finished`.toString() + this.generateTemplate().toString();
1316
+ this.emit('update', {message: this.status});
1317
+
1318
+ //here after calibration we have the component calibration (either loudspeaker or microphone) in the same form as the componentIR
1319
+ //that was used to calibrate
1320
+
1321
+ return iir_ir_and_plots;
1322
+ };
1323
+
1324
+ //////////////////////volume
1325
+
1326
+ handleIncomingData = data => {
1327
+ console.log('Received data: ', data);
1328
+ if (data.type === 'soundGainDBSPL') {
1329
+ this.soundGainDBSPL = data.value;
1330
+ } else {
1331
+ throw new Error(`Unknown data type: ${data.type}`);
1332
+ }
1333
+ };
1334
+ createSCurveBuffer = (onSetBool = true) => {
1335
+ const curve = new Float32Array(this.TAPER_SECS * this.sourceSamplingRate + 1);
1336
+ const frequency = 1 / (4 * this.TAPER_SECS);
1337
+ let j = 0;
1338
+ for (let i = 0; i < this.TAPER_SECS * this.sourceSamplingRate + 1; i += 1) {
1339
+ const phase = 2 * Math.PI * frequency * j;
1340
+ const onsetTaper = Math.pow(Math.sin(phase), 2);
1341
+ const offsetTaper = Math.pow(Math.cos(phase), 2);
1342
+ curve[i] = onSetBool ? onsetTaper : offsetTaper;
1343
+ j += 1 / this.sourceSamplingRate;
1344
+ }
1345
+ return curve;
1346
+ };
1347
+
1348
+ #getTruncatedSignal = (left = 3.5, right = 4.5) => {
1349
+ const start = Math.floor(left * this.sourceSamplingRate);
1350
+ const end = Math.floor(right * this.sourceSamplingRate);
1351
+ const result = Array.from(this.getLastVolumeRecordedSignal().slice(start, end));
1352
+ console.log(
1353
+ 'Obtaining last 1000 hz recording from #allVolumeRecordings to send for processing'
1354
+ );
1355
+ /**
1356
+ * function to check that capture was properly made
1357
+ * @param {*} list
1358
+ */
1359
+ const checkResult = list => {
1360
+ const setItem = new Set(list);
1361
+ if (setItem.size === 1 && setItem.has(0)) {
1362
+ console.warn(
1363
+ 'The last capture failed, all recorded signal is zero',
1364
+ this.getAllVolumeRecordedSignals()
1365
+ );
1366
+ }
1367
+ if (setItem.size === 0) {
1368
+ console.warn('The last capture failed, no recorded signal');
1369
+ }
1370
+ };
1371
+ checkResult(result);
1372
+ return result;
1373
+ };
1374
+
1375
+ /**
1376
+ *
1377
+ *
1378
+ Construct a calibration Node with the calibration parameters and given gain value
1379
+ * @param {*} gainValue
1380
+ * */
1381
+ #createCalibrationToneWithGainValue = gainValue => {
1382
+ const audioContext = this.makeNewSourceAudioContext();
1383
+ const oscilator = audioContext.createOscillator();
1384
+ const gainNode = audioContext.createGain();
1385
+ const taperGainNode = audioContext.createGain();
1386
+ const offsetGainNode = audioContext.createGain();
1387
+ const totalDuration = this.CALIBRATION_TONE_DURATION * 1.2;
1388
+
1389
+ oscilator.frequency.value = this.#CALIBRATION_TONE_FREQUENCY;
1390
+ oscilator.type = this.#CALIBRATION_TONE_TYPE;
1391
+ gainNode.gain.value = gainValue;
1392
+
1393
+ oscilator.connect(gainNode);
1394
+ gainNode.connect(taperGainNode);
1395
+ const onsetCurve = this.createSCurveBuffer();
1396
+ taperGainNode.gain.setValueCurveAtTime(onsetCurve, 0, this.TAPER_SECS);
1397
+ taperGainNode.connect(offsetGainNode);
1398
+ const offsetCurve = this.createSCurveBuffer(false);
1399
+ offsetGainNode.gain.setValueCurveAtTime(
1400
+ offsetCurve,
1401
+ totalDuration - this.TAPER_SECS,
1402
+ this.TAPER_SECS
1403
+ );
1404
+ offsetGainNode.connect(audioContext.destination);
1405
+
1406
+ this.addCalibrationNode(oscilator);
1407
+ };
1408
+
1409
+ /**
1410
+ * Construct a Calibration Node with the calibration parameters.
1411
+ *
1412
+ * @private
1413
+ * @example
1414
+ */
1415
+ #createCalibrationNode = () => {
1416
+ const audioContext = this.makeNewSourceAudioContext();
1417
+ const oscilator = audioContext.createOscillator();
1418
+ const gainNode = audioContext.createGain();
1419
+
1420
+ oscilator.frequency.value = this.#CALIBRATION_TONE_FREQUENCY;
1421
+ oscilator.type = this.#CALIBRATION_TONE_TYPE;
1422
+ gainNode.gain.value = 0.04;
1423
+
1424
+ oscilator.connect(gainNode);
1425
+ gainNode.connect(audioContext.destination);
1426
+
1427
+ this.addCalibrationNode(oscilator);
1428
+ };
1429
+
1430
+ #playCalibrationAudioVolume = async () => {
1431
+ const totalDuration = this.CALIBRATION_TONE_DURATION * 1.2;
1432
+
1433
+ this.calibrationNodes[0].start(0);
1434
+ this.calibrationNodes[0].stop(totalDuration);
1435
+ console.log(`Playing a buffer of ${this.CALIBRATION_TONE_DURATION} seconds of audio`);
1436
+ console.log(`Waiting a total of ${totalDuration} seconds`);
1437
+ await sleep(totalDuration);
1438
+ };
1439
+
1440
+ #sendToServerForProcessing = lCalib => {
1441
+ console.log('Sending data to server');
1442
+ let left = this.calibrateSound1000HzPreSec;
1443
+ let right = this.calibrateSound1000HzPreSec + this.calibrateSound1000HzSec;
1444
+ this.pyServerAPI
1445
+ .getVolumeCalibration({
1446
+ sampleRate: this.sourceSamplingRate,
1447
+ payload: this.#getTruncatedSignal(left, right),
1448
+ lCalib: lCalib,
1449
+ })
1450
+ .then(res => {
1451
+ if (this.outDBSPL === null) {
1452
+ this.incrementStatusBar();
1453
+ this.outDBSPL = res['outDbSPL'];
1454
+ this.outDBSPL1000 = res['outDbSPL1000'];
1455
+ this.THD = res['thd'];
1456
+ }
1457
+ })
1458
+ .catch(err => {
1459
+ console.warn(err);
1460
+ });
1461
+ };
1462
+
1463
+ startCalibrationVolume = async (stream, gainValues, lCalib, componentGainDBSPL) => {
1464
+ const trialIterations = gainValues.length;
1465
+ this.status_denominator += trialIterations;
1466
+ const thdValues = [];
1467
+ const inDBValues = [];
1468
+ let inDB = 0;
1469
+ const outDBSPLValues = [];
1470
+ const outDBSPL1000Values = [];
1471
+ let checkRec = false;
1472
+
1473
+ // do one calibration that will be discarded
1474
+ const soundLevelToDiscard = -60;
1475
+ const gainToDiscard = Math.pow(10, soundLevelToDiscard / 20);
1476
+ this.status =
1477
+ `1000 Hz Calibration: Sound Level ${soundLevelToDiscard} dB`.toString() +
1478
+ this.generateTemplate().toString();
1479
+ //this.emit('update', {message: `1000 Hz Calibration: Sound Level ${soundLevelToDiscard} dB`});
1480
+ this.emit('update', {message: this.status});
1481
+
1482
+ do {
1483
+ // eslint-disable-next-line no-await-in-loop
1484
+ await this.volumeCalibrationSteps(
1485
+ stream,
1486
+ this.#playCalibrationAudioVolume,
1487
+ this.#createCalibrationToneWithGainValue,
1488
+ this.#sendToServerForProcessing,
1489
+ gainToDiscard,
1490
+ lCalib, //todo make this a class parameter
1491
+ checkRec
1492
+ );
1493
+ } while (this.outDBSPL === null);
1494
+ //reset the values
1495
+ //this.incrementStatusBar();
1496
+
1497
+ this.outDBSPL = null;
1498
+ this.outDBSPL = null;
1499
+ this.outDBSPL1000 = null;
1500
+ this.THD = null;
1501
+
1502
+ // run the calibration at different gain values provided by the user
1503
+ for (let i = 0; i < trialIterations; i++) {
1504
+ //convert gain to DB and add to inDB
1505
+ if (i == trialIterations - 1) {
1506
+ checkRec = 'loudest';
1507
+ }
1508
+ inDB = Math.log10(gainValues[i]) * 20;
1509
+ // precision to 1 decimal place
1510
+ inDB = Math.round(inDB * 10) / 10;
1511
+ inDBValues.push(inDB);
1512
+ console.log('next update');
1513
+ this.status =
1514
+ `1000 Hz Calibration: Sound Level ${inDB} dB`.toString() +
1515
+ this.generateTemplate().toString();
1516
+ this.emit('update', {message: this.status});
1517
+ do {
1518
+ // eslint-disable-next-line no-await-in-loop
1519
+ await this.volumeCalibrationSteps(
1520
+ stream,
1521
+ this.#playCalibrationAudioVolume,
1522
+ this.#createCalibrationToneWithGainValue,
1523
+ this.#sendToServerForProcessing,
1524
+ gainValues[i],
1525
+ lCalib, //todo make this a class parameter
1526
+ checkRec
1527
+ );
1528
+ } while (this.outDBSPL === null);
1529
+ outDBSPL1000Values.push(this.outDBSPL1000);
1530
+ thdValues.push(this.THD);
1531
+ outDBSPLValues.push(this.outDBSPL);
1532
+
1533
+ this.outDBSPL = null;
1534
+ this.outDBSPL1000 = null;
1535
+ this.THD = null;
1536
+ }
1537
+
1538
+ // get the volume calibration parameters from the server
1539
+ const parameters = await this.pyServerAPI
1540
+ .getVolumeCalibrationParameters({
1541
+ inDBValues: inDBValues,
1542
+ outDBSPLValues: outDBSPL1000Values,
1543
+ lCalib: lCalib,
1544
+ componentGainDBSPL,
1545
+ })
1546
+ .then(res => {
1547
+ this.incrementStatusBar();
1548
+ return res;
1549
+ });
1550
+ const result = {
1551
+ parameters: parameters,
1552
+ inDBValues: inDBValues,
1553
+ outDBSPLValues: outDBSPLValues,
1554
+ outDBSPL1000Values: outDBSPL1000Values,
1555
+ thdValues: thdValues,
1556
+ };
1557
+
1558
+ return result;
1559
+ };
1560
+
1561
+ // function to write frq and gain to firebase database given speakerID
1562
+ writeFrqGain = async (speakerID, frq, gain, OEM) => {
1563
+ // freq and gain are too large to take samples 1 in every 100 samples
1564
+
1565
+ const sampledFrq = [];
1566
+ const sampledGain = [];
1567
+ for (let i = 0; i < frq.length; i += 100) {
1568
+ sampledFrq.push(frq[i]);
1569
+ sampledGain.push(gain[i]);
1570
+ }
1571
+
1572
+ const data = {Freq: sampledFrq, Gain: sampledGain};
1573
+
1574
+ await set(ref(database, `Microphone2/${OEM}/${speakerID}/linear`), data);
1575
+ };
1576
+
1577
+ // Function to Read frq and gain from firebase database given speakerID
1578
+ // returns an array of frq and gain if speakerID exists, returns null otherwise
1579
+
1580
+ readFrqGain = async (speakerID, OEM) => {
1581
+ const dbRef = ref(database);
1582
+ const snapshot = await get(child(dbRef, `Microphone2/${OEM}/${speakerID}/linear`));
1583
+ if (snapshot.exists()) {
1584
+ return snapshot.val();
1585
+ }
1586
+ return null;
1587
+ };
1588
+
1589
+ readGainat1000Hz = async (speakerID, OEM) => {
1590
+ const dbRef = ref(database);
1591
+ const snapshot = await get(child(dbRef, `Microphone2/${OEM}/${speakerID}/Gain1000`));
1592
+ if (snapshot.exists()) {
1593
+ return snapshot.val();
1594
+ }
1595
+ return null;
1596
+ };
1597
+
1598
+ writeGainat1000Hz = async (speakerID, gain, OEM) => {
1599
+ const data = {Gain: gain};
1600
+ await set(ref(database, `Microphone2/${OEM}/${speakerID}/Gain1000`), gain);
1601
+ };
1602
+
1603
+ writeIsSmartPhone = async (speakerID, isSmartPhone, OEM) => {
1604
+ const data = {isSmartPhone: isSmartPhone};
1605
+ await set(ref(database, `Microphone2/${OEM}/${speakerID}/isSmartPhone`), isSmartPhone);
1606
+ };
1607
+
1608
+ doesMicrophoneExist = async (speakerID, OEM) => {
1609
+ const dbRef = ref(database);
1610
+ const snapshot = await get(child(dbRef, `Microphone2/${OEM}/${speakerID}`));
1611
+ if (snapshot.exists()) {
1612
+ return true;
1613
+ }
1614
+ return false;
1615
+ };
1616
+
1617
+ addMicrophoneInfo = async (speakerID, OEM, micInfo) => {
1618
+ // add to database if /info does not exist
1619
+ const dbRef = ref(database);
1620
+ const snapshot = await get(child(dbRef, `Microphone2/${OEM}/${speakerID}/info`));
1621
+ if (!snapshot.exists()) {
1622
+ await set(ref(database, `Microphone2/${OEM}/${speakerID}/info`), micInfo);
1623
+ }
1624
+ };
1625
+
1626
+ convertToDB = gain => {
1627
+ return Math.log10(gain) * 20;
1628
+ };
1629
+
1630
+ // Function to perform linear interpolation between two points
1631
+ interpolate(x, x0, y0, x1, y1) {
1632
+ return y0 + ((x - x0) * (y1 - y0)) / (x1 - x0);
1633
+ }
1634
+
1635
+ findGainatFrequency = (frequencies, gains, targetFrequency) => {
1636
+ // Find the index of the first frequency in the array greater than the target frequency
1637
+ let index = 0;
1638
+ while (index < frequencies.length && frequencies[index] < targetFrequency) {
1639
+ index++;
1640
+ }
1641
+
1642
+ // Handle cases when the target frequency is outside the range of the given data
1643
+ if (index === 0) {
1644
+ return gains[0];
1645
+ } else if (index === frequencies.length) {
1646
+ return gains[gains.length - 1];
1647
+ } else {
1648
+ // Interpolate the gain based on the surrounding frequencies
1649
+ const x0 = frequencies[index - 1];
1650
+ const y0 = gains[index - 1];
1651
+ const x1 = frequencies[index];
1652
+ const y1 = gains[index];
1653
+ return this.interpolate(targetFrequency, x0, y0, x1, y1);
1654
+ }
1655
+ };
1656
+
1657
+ // Example of how to use the writeFrqGain and readFrqGain functions
1658
+ // writeFrqGain('speaker1', [1, 2, 3], [4, 5, 6]);
1659
+ // Speaker1 is the speakerID you want to write to in the database
1660
+ // readFrqGain('MiniDSPUMIK_1').then(data => console.log(data));
1661
+ // MiniDSPUMIK_1 is the speakerID with some Data in the database
1662
+ //adding gainDBSPL
1663
+ startCalibration = async (
1664
+ stream,
1665
+ gainValues,
1666
+ lCalib = 104.92978421490648,
1667
+ componentIR = null,
1668
+ microphoneName = 'MiniDSP-UMIK1-711-4754-vertical',
1669
+ _calibrateSoundCheck = 'goal', //GOAL PASSed in by default
1670
+ isSmartPhone = false,
1671
+ _calibrateSoundBurstDb = .33,
1672
+ _calibrateSoundBurstRepeats = 4,
1673
+ _calibrateSoundBurstSec = 1,
1674
+ _calibrateSoundBurstsWarmup = 1,
1675
+ _calibrateSoundHz = 48000,
1676
+ _calibrateSoundIIRSec = 0.2,
1677
+ calibrateSound1000HzPreSec = 3.5,
1678
+ calibrateSound1000HzSec = 1.0,
1679
+ calibrateSound1000HzPostSec = 0.5,
1680
+ _calibrateSoundBackgroundSecs = 0,
1681
+ micManufacturer = '',
1682
+ micSerialNumber = '',
1683
+ micModelNumber = '',
1684
+ micModelName = ''
1685
+ ) => {
1686
+ this._calibrateSoundBurstDb = _calibrateSoundBurstDb;
1687
+ this.CALIBRATION_TONE_DURATION =
1688
+ calibrateSound1000HzPreSec + calibrateSound1000HzSec + calibrateSound1000HzPostSec;
1689
+ this.calibrateSound1000HzPreSec = calibrateSound1000HzPreSec;
1690
+ this.calibrateSound1000HzSec = calibrateSound1000HzSec;
1691
+ this.calibrateSound1000HzPostSec = calibrateSound1000HzPostSec;
1692
+ this.iirLength = Math.floor(_calibrateSoundIIRSec * this.sourceSamplingRate);
1693
+ console.log('device info:', this.deviceInfo);
1694
+ this.numMLSPerCapture = _calibrateSoundBurstRepeats;
1695
+ this.desired_time_per_mls = _calibrateSoundBurstSec;
1696
+ this.num_mls_to_skip = _calibrateSoundBurstsWarmup;
1697
+ this.desired_sampling_rate = _calibrateSoundHz;
1698
+ this._calibrateSoundBackgroundSecs = _calibrateSoundBackgroundSecs;
1699
+
1700
+ //feed calibration goal here
1701
+ this._calibrateSoundCheck = _calibrateSoundCheck;
1702
+ //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
1703
+ //check the db based on the microphone currently connected
1704
+
1705
+ //new lCalib found at top of calibration files *1000hz, make sure to correct
1706
+ //based on zeroing of 1000hz, search for "*1000Hz"
1707
+ const ID = isSmartPhone ? micModelNumber : micSerialNumber;
1708
+ const OEM = isSmartPhone ? this.deviceInfo.OEM : micManufacturer;
1709
+ // const ID = "711-4754";
1710
+ // const OEM = "MiniDSP";
1711
+ const micInfo = {
1712
+ micModelName: isSmartPhone ? micModelName : microphoneName,
1713
+ OEM: OEM,
1714
+ ID: ID,
1715
+ HardwareName: isSmartPhone ? this.deviceInfo.hardwarename : microphoneName,
1716
+ hardwareFamily: isSmartPhone ? this.deviceInfo.hardwarefamily : microphoneName,
1717
+ HardwareModel: isSmartPhone ? this.deviceInfo.hardwaremodel : microphoneName,
1718
+ PlatformName: isSmartPhone ? this.deviceInfo.platformname : 'N/A',
1719
+ PlatformVersion: isSmartPhone ? this.deviceInfo.platformversion : 'N/A',
1720
+ DeviceType: isSmartPhone ? this.deviceInfo.devicetype : 'N/A',
1721
+ };
1722
+ this.addMicrophoneInfo(ID, OEM, micInfo);
1723
+ if (componentIR == null) {
1724
+ //mode 'ir'
1725
+ //global variable this.componentIR must be set
1726
+ this.componentIR = await this.readFrqGain(ID, OEM).then(data => {
1727
+ return data;
1728
+ });
1729
+
1730
+ lCalib = await this.readGainat1000Hz(ID, OEM);
1731
+ micInfo['gainDBSPL'] = lCalib;
1732
+ // this.componentGainDBSPL = this.convertToDB(lCalib);
1733
+ this.componentGainDBSPL = lCalib;
1734
+ //TODO: if this call to database is unknown, cannot perform experiment => return false
1735
+ if (this.componentIR == null) {
1736
+ this.status =
1737
+ `Microphone (${OEM},${ID}) is not found in the database. Please add it to the database.`.toString();
1738
+ this.emit('update', {message: this.status});
1739
+ return false;
1740
+ }
1741
+ } else {
1742
+ this.componentIR = componentIR;
1743
+ lCalib = this.findGainatFrequency(this.componentIR.Freq, this.componentIR.Gain, 1000);
1744
+ // this.componentGainDBSPL = this.convertToDB(lCalib);
1745
+ this.componentGainDBSPL = lCalib;
1746
+ await this.writeIsSmartPhone(ID, isSmartPhone, OEM);
1747
+ }
1748
+
1749
+ this.oldComponentIR = this.componentIR;
1750
+
1751
+ let volumeResults = await this.startCalibrationVolume(
1752
+ stream,
1753
+ gainValues,
1754
+ lCalib,
1755
+ this.componentGainDBSPL
1756
+ );
1757
+
1758
+ let impulseResponseResults = await this.startCalibrationImpulseResponse(stream);
1759
+ impulseResponseResults['background_noise'] = this.background_noise;
1760
+ if (componentIR != null) {
1761
+ //insert Freq and Gain from this.componentIR into db
1762
+ await this.writeFrqGain(
1763
+ ID,
1764
+ impulseResponseResults.componentIR.Freq,
1765
+ impulseResponseResults.componentIR.Gain,
1766
+ OEM
1767
+ );
1768
+ micInfo['gainDBSPL'] = impulseResponseResults.parameters.gainDBSPL;
1769
+ await this.writeGainat1000Hz(ID, micInfo['gainDBSPL'], OEM);
1770
+ }
1771
+
1772
+ const total_results = {...volumeResults, ...impulseResponseResults};
1773
+
1774
+ total_results['micInfo'] = micInfo;
1775
+ console.log('total results');
1776
+ console.log(total_results);
1777
+ return total_results;
1778
+ };
1779
+ }
1780
+
1781
+ export default Combination;