speaker-calibration 2.2.78 → 2.2.79

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