speaker-calibration 2.2.215 → 2.2.217

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 (142) 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/favicon.png +0 -0
  17. package/coverage/lcov-report/index.html +123 -123
  18. package/coverage/lcov-report/prettify.css +101 -101
  19. package/coverage/lcov-report/prettify.js +937 -937
  20. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  21. package/coverage/lcov-report/sorter.js +189 -189
  22. package/coverage/lcov-report/src/index.html +121 -121
  23. package/coverage/lcov-report/src/server/PythonServerInterface.js.html +268 -268
  24. package/coverage/lcov-report/src/server/index.html +123 -123
  25. package/coverage/lcov-report/src/tasks/audioCalibrator.js.html +499 -499
  26. package/coverage/lcov-report/src/tasks/audioRecorder.js.html +412 -412
  27. package/coverage/lcov-report/src/tasks/index.html +143 -143
  28. package/coverage/lcov-report/src/tasks/volume/index.html +123 -123
  29. package/coverage/lcov-report/src/tasks/volume/volume.js.html +409 -409
  30. package/coverage/lcov-report/src/utils.js.html +172 -172
  31. package/coverage/lcov.info +91 -91
  32. package/dist/Procfile +0 -0
  33. package/dist/example/NoSleep.min.js +1 -1
  34. package/dist/example/credentials.json.gpg +0 -0
  35. package/dist/example/fetch-languages-sheets.js +77 -77
  36. package/dist/example/i18n.js +28705 -28702
  37. package/dist/example/index.html +47 -47
  38. package/dist/example/listener.html +79 -79
  39. package/dist/example/server.js +51 -51
  40. package/dist/example/speaker.html +145 -145
  41. package/dist/example/speakerUI.js +273 -273
  42. package/dist/example/styles.css +99 -99
  43. package/dist/listener.js +13 -13
  44. package/dist/main.js +12 -12
  45. package/dist/main.js.LICENSE.txt +0 -0
  46. package/dist/mlsGen.js +6814 -6814
  47. package/dist/mlsGen.wasm +0 -0
  48. package/dist/package-lock.json +1018 -1018
  49. package/dist/package.json +18 -18
  50. package/doc/AudioCalibrator.html +417 -417
  51. package/doc/AudioPeer.html +251 -251
  52. package/doc/AudioRecorder.html +195 -195
  53. package/doc/ImpulseResponse.html +215 -215
  54. package/doc/Listener.html +308 -308
  55. package/doc/MlsGenInterface.html +226 -226
  56. package/doc/MyEventEmitter.html +274 -274
  57. package/doc/PythonServerAPI.html +109 -109
  58. package/doc/Speaker-Calibration-UML-Diagram.png +0 -0
  59. package/doc/Speaker.html +276 -276
  60. package/doc/Takes%20a%20target%20element%20where%20html%20elements%20will%20be%20appended..html +128 -128
  61. package/doc/Takes%20the%20url%20of%20the%20current%20site%0Aand%20a%20target%20element%20where%20html%20elements%20will%20be%20appended..html +138 -138
  62. package/doc/Takes%20the%20url%20of%20the%20current%20site%20and%20a%20target%20element%20where%20html%20elements%20will%20be%20appended..html +137 -137
  63. package/doc/Volume.html +88 -88
  64. package/doc/audioCalibrator.js.html +179 -179
  65. package/doc/audioPeer.js.html +175 -175
  66. package/doc/audioRecorder.js.html +163 -163
  67. package/doc/creates%20a%20new%20AudioRecorder%20instance.%20%0ASets%20up%20the%20audio%20context%20and%20file%20reader..html +114 -114
  68. package/doc/fonts/OpenSans-Bold-webfont.eot +0 -0
  69. package/doc/fonts/OpenSans-Bold-webfont.svg +1829 -1829
  70. package/doc/fonts/OpenSans-Bold-webfont.woff +0 -0
  71. package/doc/fonts/OpenSans-BoldItalic-webfont.eot +0 -0
  72. package/doc/fonts/OpenSans-BoldItalic-webfont.svg +1829 -1829
  73. package/doc/fonts/OpenSans-BoldItalic-webfont.woff +0 -0
  74. package/doc/fonts/OpenSans-Italic-webfont.eot +0 -0
  75. package/doc/fonts/OpenSans-Italic-webfont.svg +1829 -1829
  76. package/doc/fonts/OpenSans-Italic-webfont.woff +0 -0
  77. package/doc/fonts/OpenSans-Light-webfont.eot +0 -0
  78. package/doc/fonts/OpenSans-Light-webfont.svg +1830 -1830
  79. package/doc/fonts/OpenSans-Light-webfont.woff +0 -0
  80. package/doc/fonts/OpenSans-LightItalic-webfont.eot +0 -0
  81. package/doc/fonts/OpenSans-LightItalic-webfont.svg +1834 -1834
  82. package/doc/fonts/OpenSans-LightItalic-webfont.woff +0 -0
  83. package/doc/fonts/OpenSans-Regular-webfont.eot +0 -0
  84. package/doc/fonts/OpenSans-Regular-webfont.svg +1830 -1830
  85. package/doc/fonts/OpenSans-Regular-webfont.woff +0 -0
  86. package/doc/global.html +308 -308
  87. package/doc/index.html +58 -58
  88. package/doc/listener.js.html +170 -170
  89. package/doc/mlsGen_mlsGenInterface.js.html +117 -117
  90. package/doc/myEventEmitter.js.html +124 -124
  91. package/doc/peer-connection_audioPeer.js.html +188 -188
  92. package/doc/peer-connection_listener.js.html +311 -311
  93. package/doc/peer-connection_speaker.js.html +381 -381
  94. package/doc/sc-activity-diagram.png +0 -0
  95. package/doc/scripts/linenumber.js +25 -25
  96. package/doc/scripts/prettify/Apache-License-2.0.txt +202 -202
  97. package/doc/scripts/prettify/lang-css.js +24 -24
  98. package/doc/scripts/prettify/prettify.js +640 -640
  99. package/doc/server_PythonServerAPI.js.html +160 -160
  100. package/doc/speaker.js.html +248 -248
  101. package/doc/styles/jsdoc-default.css +371 -371
  102. package/doc/styles/prettify-jsdoc.css +111 -111
  103. package/doc/styles/prettify-tomorrow.css +163 -163
  104. package/doc/tasks_audioCalibrator.js.html +207 -207
  105. package/doc/tasks_audioRecorder.js.html +190 -190
  106. package/doc/tasks_impulse-response_impulseResponse.js.html +442 -442
  107. package/doc/tasks_impulse-response_mlsGen_mlsGenInterface.js.html +175 -175
  108. package/doc/tasks_volume_volume.js.html +185 -185
  109. package/doc/utils.js.html +105 -105
  110. package/jest.config.js +173 -173
  111. package/netlify.toml +26 -26
  112. package/package.json +78 -78
  113. package/src/config/firebase.js +26 -26
  114. package/src/index.html +21 -21
  115. package/src/listener-app/listener.js +153 -153
  116. package/src/main.js +23 -23
  117. package/src/myEventEmitter.js +83 -83
  118. package/src/peer-connection/audioPeer.js +183 -183
  119. package/src/peer-connection/listener.js +369 -366
  120. package/src/peer-connection/peerErrors.js +25 -25
  121. package/src/peer-connection/speaker.js +765 -744
  122. package/src/powerCheck.js +98 -98
  123. package/src/server/PythonServerAPI.js +869 -869
  124. package/src/tasks/audioCalibrator.js +360 -351
  125. package/src/tasks/audioRecorder.js +315 -315
  126. package/src/tasks/combination/combination.js +3160 -3160
  127. package/src/tasks/combination/mlsGen/mlsGen.cpp +98 -98
  128. package/src/tasks/combination/mlsGen/mlsGen.hpp +303 -303
  129. package/src/tasks/combination/mlsGen/mlsGenInterface.js +131 -131
  130. package/src/tasks/combination/mlsGen/mlsGenTest.cpp +180 -180
  131. package/src/tasks/impulse-response/impulseResponse.js +610 -610
  132. package/src/tasks/impulse-response/mlsGen/mlsGen.cpp +98 -98
  133. package/src/tasks/impulse-response/mlsGen/mlsGen.hpp +303 -303
  134. package/src/tasks/impulse-response/mlsGen/mlsGenInterface.js +131 -131
  135. package/src/tasks/impulse-response/mlsGen/mlsGenTest.cpp +180 -180
  136. package/src/tasks/volume/volume.cpp +2 -2
  137. package/src/tasks/volume/volume.hpp +22 -22
  138. package/src/tasks/volume/volume.js +279 -279
  139. package/src/utils.js +205 -205
  140. package/webpack.config.js +64 -64
  141. package/.github/workflows/update-phrases.yml +0 -37
  142. package/makefile +0 -74
@@ -1,279 +1,279 @@
1
- import AudioCalibrator from '../audioCalibrator';
2
- import axios from 'axios';
3
- import {sleep} from '../../utils';
4
-
5
- /**
6
- *
7
- */
8
- class Volume extends AudioCalibrator {
9
- /**
10
- *
11
- * @param root0
12
- * @param root0.download
13
- * @param root0.numCalibrationRounds
14
- * @param root0.numCalibrationNodes
15
- * @example
16
- */
17
-
18
- /** @private */
19
- #CALIBRATION_TONE_FREQUENCY = 1000; // Hz
20
-
21
- /** @private */
22
- #CALIBRATION_TONE_TYPE = 'sine';
23
-
24
- /** @private */
25
- #CALIBRATION_TONE_DURATION = 5; // seconds
26
-
27
- /** @private */
28
- outDBSPL = null;
29
- THD = null;
30
- outDBSPL1000 = null;
31
-
32
- /** @private */
33
- TAPER_SECS = 0.010; // seconds
34
-
35
- /** @private */
36
- status_denominator = 2;
37
-
38
- /** @private */
39
- status_numerator = 0;
40
-
41
- /** @private */
42
- percent_complete = 0;
43
-
44
- /** @private */
45
- status = ``;
46
-
47
- /**generate string template that gets reevaluated as variable increases */
48
- generateTemplate = () => {
49
- if (this.percent_complete > 100){
50
- this.percent_complete = 100;
51
- }
52
- const template = `<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>`;
53
- return template;
54
- }
55
-
56
- /** increment numerator and percent for status bar */
57
- incrementStatusBar = () => {
58
- this.status_numerator += 1;
59
- this.percent_complete = (this.status_numerator/this.status_denominator)*100;
60
- }
61
-
62
- handleIncomingData = data => {
63
- console.log('Received data: ', data);
64
- if (data.type === 'soundGainDBSPL') {
65
- this.soundGainDBSPL = data.value;
66
- } else {
67
- throw new Error(`Unknown data type: ${data.type}`);
68
- }
69
- };
70
-
71
- createSCurveBuffer = (onSetBool=true) => {
72
-
73
- const curve = new Float32Array(this.TAPER_SECS*this.sourceSamplingRate+1);
74
- const frequency = 1 / (4 * this.TAPER_SECS);
75
- let j = 0;
76
- for (let i = 0; i < this.TAPER_SECS*this.sourceSamplingRate+1; i += 1) {
77
- const phase = 2 * Math.PI * frequency * j;
78
- const onsetTaper = Math.pow(Math.sin(phase) , 2);
79
- const offsetTaper = Math.pow(Math.cos(phase) , 2);
80
- curve[i] = onSetBool? onsetTaper : offsetTaper;
81
- j += (1 / this.sourceSamplingRate);
82
- }
83
- return curve;
84
- };
85
-
86
- #getTruncatedSignal = (left = 3.5, right = 4.5) => {
87
- const start = Math.floor(left * this.sourceSamplingRate);
88
- const end = Math.floor(right * this.sourceSamplingRate);
89
- const result = Array.from(this.getLastRecordedSignal().slice(start, end));
90
-
91
- /**
92
- * function to check that capture was properly made
93
- * @param {*} list
94
- */
95
- const checkResult = list => {
96
- const setItem = new Set(list);
97
- if (setItem.size === 1 && setItem.has(0)) {
98
- console.warn(
99
- 'The last capture failed, all recorded signal is zero',
100
- this.getAllRecordedSignals()
101
- );
102
- }
103
- if (setItem.size === 0) {
104
- console.warn('The last capture failed, no recorded signal');
105
- }
106
- };
107
- checkResult(result);
108
- return result;
109
- };
110
-
111
- /**
112
- *
113
- *
114
- Construct a calibration Node with the calibration parameters and given gain value
115
- * @param {*} gainValue
116
- * */
117
- #createCalibrationToneWithGainValue = gainValue => {
118
- const audioContext = this.makeNewSourceAudioContext();
119
- const oscilator = audioContext.createOscillator();
120
- const gainNode = audioContext.createGain();
121
- const taperGainNode = audioContext.createGain();
122
- const offsetGainNode = audioContext.createGain();
123
- const totalDuration = this.#CALIBRATION_TONE_DURATION * 1.2;
124
-
125
- oscilator.frequency.value = this.#CALIBRATION_TONE_FREQUENCY;
126
- oscilator.type = this.#CALIBRATION_TONE_TYPE;
127
- gainNode.gain.value = gainValue;
128
-
129
- oscilator.connect(gainNode);
130
- gainNode.connect(taperGainNode);
131
- const onsetCurve = this.createSCurveBuffer();
132
- taperGainNode.gain.setValueCurveAtTime(onsetCurve, 0, this.TAPER_SECS);
133
- taperGainNode.connect(offsetGainNode);
134
- const offsetCurve = this.createSCurveBuffer(false);
135
- offsetGainNode.gain.setValueCurveAtTime(offsetCurve, (totalDuration-this.TAPER_SECS), this.TAPER_SECS);
136
- offsetGainNode.connect(audioContext.destination);
137
-
138
- this.addCalibrationNode(oscilator);
139
- };
140
-
141
- /**
142
- * Construct a Calibration Node with the calibration parameters.
143
- *
144
- * @private
145
- * @example
146
- */
147
- #createCalibrationNode = () => {
148
- const audioContext = this.makeNewSourceAudioContext();
149
- const oscilator = audioContext.createOscillator();
150
- const gainNode = audioContext.createGain();
151
-
152
- oscilator.frequency.value = this.#CALIBRATION_TONE_FREQUENCY;
153
- oscilator.type = this.#CALIBRATION_TONE_TYPE;
154
- gainNode.gain.value = 0.04;
155
-
156
- oscilator.connect(gainNode);
157
- gainNode.connect(audioContext.destination);
158
-
159
- this.addCalibrationNode(oscilator);
160
- };
161
-
162
- #playCalibrationAudio = async () => {
163
- const totalDuration = this.#CALIBRATION_TONE_DURATION * 1.2;
164
-
165
- this.calibrationNodes[0].start(0);
166
- this.calibrationNodes[0].stop(totalDuration);
167
- console.log(`Playing a buffer of ${this.#CALIBRATION_TONE_DURATION} seconds of audio`);
168
- console.log(`Waiting a total of ${totalDuration} seconds`);
169
- await sleep(totalDuration);
170
- };
171
-
172
- #sendToServerForProcessing = (lCalib = 104.92978421490648) => {
173
- console.log('Sending data to server');
174
- this.pyServerAPI
175
- .getVolumeCalibration({
176
- sampleRate: this.sourceSamplingRate,
177
- payload: this.#getTruncatedSignal(),
178
- lCalib: lCalib,
179
- })
180
- .then(res => {
181
- if (this.outDBSPL === null) {
182
- this.incrementStatusBar();
183
- this.outDBSPL = res['outDbSPL'];
184
- this.outDBSPL1000 = res['outDbSPL1000'];
185
- this.THD = res['thd'];
186
- }
187
- })
188
- .catch(err => {
189
- console.warn(err);
190
- });
191
- };
192
-
193
- startCalibration = async (stream, gainValues, lCalib = 104.92978421490648) => {
194
- const trialIterations = gainValues.length;
195
- this.status_denominator += trialIterations;
196
- const thdValues = [];
197
- const inDBValues = [];
198
- let inDB = 0;
199
- const outDBSPLValues = [];
200
- const outDBSPL1000Values = [];
201
-
202
- // do one calibration that will be discarded
203
- const soundLevelToDiscard = -60;
204
- const gainToDiscard = Math.pow(10, soundLevelToDiscard / 20);
205
- this.status = `Sound Level: ${soundLevelToDiscard} dB`.toString() + this.generateTemplate().toString();
206
- this.emit('update', {message: this.status});
207
- do {
208
- // eslint-disable-next-line no-await-in-loop
209
- await this.volumeCalibrationSteps(
210
- stream,
211
- this.#playCalibrationAudio,
212
- this.#createCalibrationToneWithGainValue,
213
- this.#sendToServerForProcessing,
214
- gainToDiscard,
215
- lCalib //todo make this a class parameter
216
- );
217
- } while (this.outDBSPL === null);
218
- //reset the values
219
- this.outDBSPL = null;
220
- this.outDBSPL = null;
221
- this.outDBSPL1000 = null;
222
- this.THD = null;
223
-
224
- // run the calibration at different gain values provided by the user
225
- for (let i = 0; i < trialIterations; i++) {
226
- //convert gain to DB and add to inDB
227
- inDB = Math.log10(gainValues[i]) * 20;
228
- // precision to 1 decimal place
229
- inDB = Math.round(inDB * 10) / 10;
230
- inDBValues.push(inDB);
231
- this.status = `Sound Level: ${inDB} dB`.toString() + this.generateTemplate().toString();
232
- this.emit('update', {message: this.status});
233
- do {
234
- // eslint-disable-next-line no-await-in-loop
235
- await this.volumeCalibrationSteps(
236
- stream,
237
- this.#playCalibrationAudio,
238
- this.#createCalibrationToneWithGainValue,
239
- this.#sendToServerForProcessing,
240
- gainValues[i],
241
- lCalib //todo make this a class parameter
242
- );
243
- } while (this.outDBSPL === null);
244
- outDBSPL1000Values.push(this.outDBSPL1000);
245
- thdValues.push(this.THD);
246
- outDBSPLValues.push(this.outDBSPL);
247
-
248
- this.outDBSPL = null;
249
- this.outDBSPL1000 = null;
250
- this.THD = null;
251
- }
252
-
253
- // get the volume calibration parameters from the server
254
-
255
- const parameters = await this.pyServerAPI
256
- .getVolumeCalibrationParameters({
257
- inDBValues: inDBValues,
258
- outDBSPLValues: outDBSPL1000Values,
259
- lCalib: lCalib,
260
- })
261
- .then(res => {
262
- this.incrementStatusBar();
263
- this.status = `done with 1000 Hz calibration`.toString() + this.generateTemplate().toString();
264
- this.emit('update', {message: this.status});
265
- return res;
266
- });
267
- const result = {
268
- parameters: parameters,
269
- inDBValues: inDBValues,
270
- outDBSPLValues: outDBSPLValues,
271
- outDBSPL1000Values: outDBSPL1000Values,
272
- thdValues: thdValues,
273
- };
274
-
275
- return result;
276
- };
277
- }
278
-
279
- export default Volume;
1
+ import AudioCalibrator from '../audioCalibrator';
2
+ import axios from 'axios';
3
+ import {sleep} from '../../utils';
4
+
5
+ /**
6
+ *
7
+ */
8
+ class Volume extends AudioCalibrator {
9
+ /**
10
+ *
11
+ * @param root0
12
+ * @param root0.download
13
+ * @param root0.numCalibrationRounds
14
+ * @param root0.numCalibrationNodes
15
+ * @example
16
+ */
17
+
18
+ /** @private */
19
+ #CALIBRATION_TONE_FREQUENCY = 1000; // Hz
20
+
21
+ /** @private */
22
+ #CALIBRATION_TONE_TYPE = 'sine';
23
+
24
+ /** @private */
25
+ #CALIBRATION_TONE_DURATION = 5; // seconds
26
+
27
+ /** @private */
28
+ outDBSPL = null;
29
+ THD = null;
30
+ outDBSPL1000 = null;
31
+
32
+ /** @private */
33
+ TAPER_SECS = 0.010; // seconds
34
+
35
+ /** @private */
36
+ status_denominator = 2;
37
+
38
+ /** @private */
39
+ status_numerator = 0;
40
+
41
+ /** @private */
42
+ percent_complete = 0;
43
+
44
+ /** @private */
45
+ status = ``;
46
+
47
+ /**generate string template that gets reevaluated as variable increases */
48
+ generateTemplate = () => {
49
+ if (this.percent_complete > 100){
50
+ this.percent_complete = 100;
51
+ }
52
+ const template = `<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>`;
53
+ return template;
54
+ }
55
+
56
+ /** increment numerator and percent for status bar */
57
+ incrementStatusBar = () => {
58
+ this.status_numerator += 1;
59
+ this.percent_complete = (this.status_numerator/this.status_denominator)*100;
60
+ }
61
+
62
+ handleIncomingData = data => {
63
+ console.log('Received data: ', data);
64
+ if (data.type === 'soundGainDBSPL') {
65
+ this.soundGainDBSPL = data.value;
66
+ } else {
67
+ throw new Error(`Unknown data type: ${data.type}`);
68
+ }
69
+ };
70
+
71
+ createSCurveBuffer = (onSetBool=true) => {
72
+
73
+ const curve = new Float32Array(this.TAPER_SECS*this.sourceSamplingRate+1);
74
+ const frequency = 1 / (4 * this.TAPER_SECS);
75
+ let j = 0;
76
+ for (let i = 0; i < this.TAPER_SECS*this.sourceSamplingRate+1; i += 1) {
77
+ const phase = 2 * Math.PI * frequency * j;
78
+ const onsetTaper = Math.pow(Math.sin(phase) , 2);
79
+ const offsetTaper = Math.pow(Math.cos(phase) , 2);
80
+ curve[i] = onSetBool? onsetTaper : offsetTaper;
81
+ j += (1 / this.sourceSamplingRate);
82
+ }
83
+ return curve;
84
+ };
85
+
86
+ #getTruncatedSignal = (left = 3.5, right = 4.5) => {
87
+ const start = Math.floor(left * this.sourceSamplingRate);
88
+ const end = Math.floor(right * this.sourceSamplingRate);
89
+ const result = Array.from(this.getLastRecordedSignal().slice(start, end));
90
+
91
+ /**
92
+ * function to check that capture was properly made
93
+ * @param {*} list
94
+ */
95
+ const checkResult = list => {
96
+ const setItem = new Set(list);
97
+ if (setItem.size === 1 && setItem.has(0)) {
98
+ console.warn(
99
+ 'The last capture failed, all recorded signal is zero',
100
+ this.getAllRecordedSignals()
101
+ );
102
+ }
103
+ if (setItem.size === 0) {
104
+ console.warn('The last capture failed, no recorded signal');
105
+ }
106
+ };
107
+ checkResult(result);
108
+ return result;
109
+ };
110
+
111
+ /**
112
+ *
113
+ *
114
+ Construct a calibration Node with the calibration parameters and given gain value
115
+ * @param {*} gainValue
116
+ * */
117
+ #createCalibrationToneWithGainValue = gainValue => {
118
+ const audioContext = this.makeNewSourceAudioContext();
119
+ const oscilator = audioContext.createOscillator();
120
+ const gainNode = audioContext.createGain();
121
+ const taperGainNode = audioContext.createGain();
122
+ const offsetGainNode = audioContext.createGain();
123
+ const totalDuration = this.#CALIBRATION_TONE_DURATION * 1.2;
124
+
125
+ oscilator.frequency.value = this.#CALIBRATION_TONE_FREQUENCY;
126
+ oscilator.type = this.#CALIBRATION_TONE_TYPE;
127
+ gainNode.gain.value = gainValue;
128
+
129
+ oscilator.connect(gainNode);
130
+ gainNode.connect(taperGainNode);
131
+ const onsetCurve = this.createSCurveBuffer();
132
+ taperGainNode.gain.setValueCurveAtTime(onsetCurve, 0, this.TAPER_SECS);
133
+ taperGainNode.connect(offsetGainNode);
134
+ const offsetCurve = this.createSCurveBuffer(false);
135
+ offsetGainNode.gain.setValueCurveAtTime(offsetCurve, (totalDuration-this.TAPER_SECS), this.TAPER_SECS);
136
+ offsetGainNode.connect(audioContext.destination);
137
+
138
+ this.addCalibrationNode(oscilator);
139
+ };
140
+
141
+ /**
142
+ * Construct a Calibration Node with the calibration parameters.
143
+ *
144
+ * @private
145
+ * @example
146
+ */
147
+ #createCalibrationNode = () => {
148
+ const audioContext = this.makeNewSourceAudioContext();
149
+ const oscilator = audioContext.createOscillator();
150
+ const gainNode = audioContext.createGain();
151
+
152
+ oscilator.frequency.value = this.#CALIBRATION_TONE_FREQUENCY;
153
+ oscilator.type = this.#CALIBRATION_TONE_TYPE;
154
+ gainNode.gain.value = 0.04;
155
+
156
+ oscilator.connect(gainNode);
157
+ gainNode.connect(audioContext.destination);
158
+
159
+ this.addCalibrationNode(oscilator);
160
+ };
161
+
162
+ #playCalibrationAudio = async () => {
163
+ const totalDuration = this.#CALIBRATION_TONE_DURATION * 1.2;
164
+
165
+ this.calibrationNodes[0].start(0);
166
+ this.calibrationNodes[0].stop(totalDuration);
167
+ console.log(`Playing a buffer of ${this.#CALIBRATION_TONE_DURATION} seconds of audio`);
168
+ console.log(`Waiting a total of ${totalDuration} seconds`);
169
+ await sleep(totalDuration);
170
+ };
171
+
172
+ #sendToServerForProcessing = (lCalib = 104.92978421490648) => {
173
+ console.log('Sending data to server');
174
+ this.pyServerAPI
175
+ .getVolumeCalibration({
176
+ sampleRate: this.sourceSamplingRate,
177
+ payload: this.#getTruncatedSignal(),
178
+ lCalib: lCalib,
179
+ })
180
+ .then(res => {
181
+ if (this.outDBSPL === null) {
182
+ this.incrementStatusBar();
183
+ this.outDBSPL = res['outDbSPL'];
184
+ this.outDBSPL1000 = res['outDbSPL1000'];
185
+ this.THD = res['thd'];
186
+ }
187
+ })
188
+ .catch(err => {
189
+ console.warn(err);
190
+ });
191
+ };
192
+
193
+ startCalibration = async (stream, gainValues, lCalib = 104.92978421490648) => {
194
+ const trialIterations = gainValues.length;
195
+ this.status_denominator += trialIterations;
196
+ const thdValues = [];
197
+ const inDBValues = [];
198
+ let inDB = 0;
199
+ const outDBSPLValues = [];
200
+ const outDBSPL1000Values = [];
201
+
202
+ // do one calibration that will be discarded
203
+ const soundLevelToDiscard = -60;
204
+ const gainToDiscard = Math.pow(10, soundLevelToDiscard / 20);
205
+ this.status = `Sound Level: ${soundLevelToDiscard} dB`.toString() + this.generateTemplate().toString();
206
+ this.emit('update', {message: this.status});
207
+ do {
208
+ // eslint-disable-next-line no-await-in-loop
209
+ await this.volumeCalibrationSteps(
210
+ stream,
211
+ this.#playCalibrationAudio,
212
+ this.#createCalibrationToneWithGainValue,
213
+ this.#sendToServerForProcessing,
214
+ gainToDiscard,
215
+ lCalib //todo make this a class parameter
216
+ );
217
+ } while (this.outDBSPL === null);
218
+ //reset the values
219
+ this.outDBSPL = null;
220
+ this.outDBSPL = null;
221
+ this.outDBSPL1000 = null;
222
+ this.THD = null;
223
+
224
+ // run the calibration at different gain values provided by the user
225
+ for (let i = 0; i < trialIterations; i++) {
226
+ //convert gain to DB and add to inDB
227
+ inDB = Math.log10(gainValues[i]) * 20;
228
+ // precision to 1 decimal place
229
+ inDB = Math.round(inDB * 10) / 10;
230
+ inDBValues.push(inDB);
231
+ this.status = `Sound Level: ${inDB} dB`.toString() + this.generateTemplate().toString();
232
+ this.emit('update', {message: this.status});
233
+ do {
234
+ // eslint-disable-next-line no-await-in-loop
235
+ await this.volumeCalibrationSteps(
236
+ stream,
237
+ this.#playCalibrationAudio,
238
+ this.#createCalibrationToneWithGainValue,
239
+ this.#sendToServerForProcessing,
240
+ gainValues[i],
241
+ lCalib //todo make this a class parameter
242
+ );
243
+ } while (this.outDBSPL === null);
244
+ outDBSPL1000Values.push(this.outDBSPL1000);
245
+ thdValues.push(this.THD);
246
+ outDBSPLValues.push(this.outDBSPL);
247
+
248
+ this.outDBSPL = null;
249
+ this.outDBSPL1000 = null;
250
+ this.THD = null;
251
+ }
252
+
253
+ // get the volume calibration parameters from the server
254
+
255
+ const parameters = await this.pyServerAPI
256
+ .getVolumeCalibrationParameters({
257
+ inDBValues: inDBValues,
258
+ outDBSPLValues: outDBSPL1000Values,
259
+ lCalib: lCalib,
260
+ })
261
+ .then(res => {
262
+ this.incrementStatusBar();
263
+ this.status = `done with 1000 Hz calibration`.toString() + this.generateTemplate().toString();
264
+ this.emit('update', {message: this.status});
265
+ return res;
266
+ });
267
+ const result = {
268
+ parameters: parameters,
269
+ inDBValues: inDBValues,
270
+ outDBSPLValues: outDBSPLValues,
271
+ outDBSPL1000Values: outDBSPL1000Values,
272
+ thdValues: thdValues,
273
+ };
274
+
275
+ return result;
276
+ };
277
+ }
278
+
279
+ export default Volume;