speaker-calibration 2.2.201 → 2.2.203
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintignore +71 -71
- package/.eslintrc.json +40 -40
- package/.gitignore +81 -0
- package/.prettierignore +69 -69
- package/.prettierrc +14 -14
- package/LICENSE +20 -20
- package/README.md +133 -133
- package/__mocks__/fileMock.js +1 -1
- package/__mocks__/styleMock.js +1 -1
- package/babel.config.js +3 -3
- package/coverage/clover.xml +71 -71
- package/coverage/coverage-final.json +224 -224
- package/coverage/lcov-report/PythonServerInterface.js.html +265 -265
- package/coverage/lcov-report/base.css +354 -354
- package/coverage/lcov-report/block-navigation.js +82 -82
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +123 -123
- package/coverage/lcov-report/prettify.css +101 -101
- package/coverage/lcov-report/prettify.js +937 -937
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +189 -189
- package/coverage/lcov-report/src/index.html +121 -121
- package/coverage/lcov-report/src/server/PythonServerInterface.js.html +268 -268
- package/coverage/lcov-report/src/server/index.html +123 -123
- package/coverage/lcov-report/src/tasks/audioCalibrator.js.html +499 -499
- package/coverage/lcov-report/src/tasks/audioRecorder.js.html +412 -412
- package/coverage/lcov-report/src/tasks/index.html +143 -143
- package/coverage/lcov-report/src/tasks/volume/index.html +123 -123
- package/coverage/lcov-report/src/tasks/volume/volume.js.html +409 -409
- package/coverage/lcov-report/src/utils.js.html +172 -172
- package/coverage/lcov.info +91 -91
- package/dist/Procfile +0 -0
- package/dist/example/NoSleep.min.js +1 -1
- package/dist/example/credentials.json.gpg +0 -0
- package/dist/example/fetch-languages-sheets.js +77 -77
- package/dist/example/i18n.js +27366 -27366
- package/dist/example/index.html +47 -47
- package/dist/example/listener.html +79 -79
- package/dist/example/listener.js +149 -149
- package/dist/example/server.js +51 -51
- package/dist/example/speaker.html +145 -145
- package/dist/example/speakerUI.js +273 -273
- package/dist/example/styles.css +99 -99
- package/dist/main.js +18 -18
- package/dist/main.js.LICENSE.txt +0 -0
- package/dist/mlsGen.js +6814 -6814
- package/dist/mlsGen.wasm +0 -0
- package/dist/package-lock.json +1018 -1018
- package/dist/package.json +18 -18
- package/doc/AudioCalibrator.html +417 -417
- package/doc/AudioPeer.html +251 -251
- package/doc/AudioRecorder.html +195 -195
- package/doc/ImpulseResponse.html +215 -215
- package/doc/Listener.html +308 -308
- package/doc/MlsGenInterface.html +226 -226
- package/doc/MyEventEmitter.html +274 -274
- package/doc/PythonServerAPI.html +109 -109
- package/doc/Speaker-Calibration-UML-Diagram.png +0 -0
- package/doc/Speaker.html +276 -276
- package/doc/Takes%20a%20target%20element%20where%20html%20elements%20will%20be%20appended..html +128 -128
- package/doc/Takes%20the%20url%20of%20the%20current%20site%0Aand%20a%20target%20element%20where%20html%20elements%20will%20be%20appended..html +138 -138
- package/doc/Takes%20the%20url%20of%20the%20current%20site%20and%20a%20target%20element%20where%20html%20elements%20will%20be%20appended..html +137 -137
- package/doc/Volume.html +88 -88
- package/doc/audioCalibrator.js.html +179 -179
- package/doc/audioPeer.js.html +175 -175
- package/doc/audioRecorder.js.html +163 -163
- package/doc/creates%20a%20new%20AudioRecorder%20instance.%20%0ASets%20up%20the%20audio%20context%20and%20file%20reader..html +114 -114
- package/doc/fonts/OpenSans-Bold-webfont.eot +0 -0
- package/doc/fonts/OpenSans-Bold-webfont.svg +1829 -1829
- package/doc/fonts/OpenSans-Bold-webfont.woff +0 -0
- package/doc/fonts/OpenSans-BoldItalic-webfont.eot +0 -0
- package/doc/fonts/OpenSans-BoldItalic-webfont.svg +1829 -1829
- package/doc/fonts/OpenSans-BoldItalic-webfont.woff +0 -0
- package/doc/fonts/OpenSans-Italic-webfont.eot +0 -0
- package/doc/fonts/OpenSans-Italic-webfont.svg +1829 -1829
- package/doc/fonts/OpenSans-Italic-webfont.woff +0 -0
- package/doc/fonts/OpenSans-Light-webfont.eot +0 -0
- package/doc/fonts/OpenSans-Light-webfont.svg +1830 -1830
- package/doc/fonts/OpenSans-Light-webfont.woff +0 -0
- package/doc/fonts/OpenSans-LightItalic-webfont.eot +0 -0
- package/doc/fonts/OpenSans-LightItalic-webfont.svg +1834 -1834
- package/doc/fonts/OpenSans-LightItalic-webfont.woff +0 -0
- package/doc/fonts/OpenSans-Regular-webfont.eot +0 -0
- package/doc/fonts/OpenSans-Regular-webfont.svg +1830 -1830
- package/doc/fonts/OpenSans-Regular-webfont.woff +0 -0
- package/doc/global.html +308 -308
- package/doc/index.html +58 -58
- package/doc/listener.js.html +170 -170
- package/doc/mlsGen_mlsGenInterface.js.html +117 -117
- package/doc/myEventEmitter.js.html +124 -124
- package/doc/peer-connection_audioPeer.js.html +188 -188
- package/doc/peer-connection_listener.js.html +311 -311
- package/doc/peer-connection_speaker.js.html +381 -381
- package/doc/sc-activity-diagram.png +0 -0
- package/doc/scripts/linenumber.js +25 -25
- package/doc/scripts/prettify/Apache-License-2.0.txt +202 -202
- package/doc/scripts/prettify/lang-css.js +24 -24
- package/doc/scripts/prettify/prettify.js +640 -640
- package/doc/server_PythonServerAPI.js.html +160 -160
- package/doc/speaker.js.html +248 -248
- package/doc/styles/jsdoc-default.css +371 -371
- package/doc/styles/prettify-jsdoc.css +111 -111
- package/doc/styles/prettify-tomorrow.css +163 -163
- package/doc/tasks_audioCalibrator.js.html +207 -207
- package/doc/tasks_audioRecorder.js.html +190 -190
- package/doc/tasks_impulse-response_impulseResponse.js.html +442 -442
- package/doc/tasks_impulse-response_mlsGen_mlsGenInterface.js.html +175 -175
- package/doc/tasks_volume_volume.js.html +185 -185
- package/doc/utils.js.html +105 -105
- package/jest.config.js +173 -173
- package/netlify.toml +26 -26
- package/package.json +73 -73
- package/src/config/firebase.js +26 -26
- package/src/index.html +21 -21
- package/src/main.js +23 -23
- package/src/myEventEmitter.js +83 -83
- package/src/peer-connection/audioPeer.js +178 -178
- package/src/peer-connection/listener.js +340 -340
- package/src/peer-connection/peerErrors.js +25 -25
- package/src/peer-connection/speaker.js +731 -731
- package/src/powerCheck.js +98 -98
- package/src/server/PythonServerAPI.js +869 -869
- package/src/tasks/audioCalibrator.js +336 -335
- package/src/tasks/audioRecorder.js +315 -315
- package/src/tasks/combination/combination.js +2996 -2968
- package/src/tasks/combination/mlsGen/mlsGen.cpp +98 -98
- package/src/tasks/combination/mlsGen/mlsGen.hpp +303 -303
- package/src/tasks/combination/mlsGen/mlsGenInterface.js +131 -131
- package/src/tasks/combination/mlsGen/mlsGenTest.cpp +180 -180
- package/src/tasks/impulse-response/impulseResponse.js +610 -610
- package/src/tasks/impulse-response/mlsGen/mlsGen.cpp +98 -98
- package/src/tasks/impulse-response/mlsGen/mlsGen.hpp +303 -303
- package/src/tasks/impulse-response/mlsGen/mlsGenInterface.js +131 -131
- package/src/tasks/impulse-response/mlsGen/mlsGenTest.cpp +180 -180
- package/src/tasks/volume/volume.cpp +2 -2
- package/src/tasks/volume/volume.hpp +22 -22
- package/src/tasks/volume/volume.js +279 -279
- package/src/utils.js +205 -205
- package/webpack.config.js +37 -37
- package/.github/workflows/update-phrases.yml +0 -37
- package/makefile +0 -74
|
@@ -1,315 +1,315 @@
|
|
|
1
|
-
import MyEventEmitter from '../myEventEmitter';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* @class provides a simple interface for recording audio from a microphone
|
|
5
|
-
* using the Media Recorder API.
|
|
6
|
-
*/
|
|
7
|
-
class AudioRecorder extends MyEventEmitter {
|
|
8
|
-
/** @private */
|
|
9
|
-
#mediaRecorder;
|
|
10
|
-
|
|
11
|
-
/** @private */
|
|
12
|
-
#recordedChunks = [];
|
|
13
|
-
|
|
14
|
-
/** @private */
|
|
15
|
-
#audioBlob;
|
|
16
|
-
|
|
17
|
-
/** @private */
|
|
18
|
-
#audioContext;
|
|
19
|
-
|
|
20
|
-
/** @private */
|
|
21
|
-
#recordedSignals = [];
|
|
22
|
-
|
|
23
|
-
/**@private */
|
|
24
|
-
#allHzUnfilteredRecordings = [];
|
|
25
|
-
|
|
26
|
-
/**@private */
|
|
27
|
-
#allBackgroundRecordings = [];
|
|
28
|
-
|
|
29
|
-
/** @private */
|
|
30
|
-
#allHzFilteredRecordings = [];
|
|
31
|
-
|
|
32
|
-
/** @private */
|
|
33
|
-
sinkSamplingRate;
|
|
34
|
-
|
|
35
|
-
/** @private */
|
|
36
|
-
sampleSize;
|
|
37
|
-
|
|
38
|
-
/** @private */
|
|
39
|
-
#allVolumeRecordings = [];
|
|
40
|
-
|
|
41
|
-
/** @private */
|
|
42
|
-
flags = {};
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Decode the audio data from the recorded audio blob.
|
|
46
|
-
*
|
|
47
|
-
* @private
|
|
48
|
-
* @example
|
|
49
|
-
*/
|
|
50
|
-
#saveRecording = async (mode, checkRec) => {
|
|
51
|
-
const arrayBuffer = await this.#audioBlob.arrayBuffer();
|
|
52
|
-
const audioBuffer = await this.#audioContext.decodeAudioData(arrayBuffer);
|
|
53
|
-
console.log(audioBuffer);
|
|
54
|
-
const data = audioBuffer.getChannelData(0);
|
|
55
|
-
const dataArray = Array.from(data);
|
|
56
|
-
|
|
57
|
-
console.log(`Decoded audio buffer with ${data.length} samples`);
|
|
58
|
-
console.log(`Unfiltered recording should be of length: ${data.length}`);
|
|
59
|
-
if (checkRec == 'loudest') {
|
|
60
|
-
const uniqueSet = new Set(dataArray);
|
|
61
|
-
const numberOfUniqueValues = uniqueSet.size;
|
|
62
|
-
const squaredValues = dataArray.map(value => value * value);
|
|
63
|
-
const sum_of_squares = squaredValues.reduce((total, value) => total + value, 0);
|
|
64
|
-
const squared_mean = sum_of_squares / dataArray.length;
|
|
65
|
-
const dbLevel = 20 * Math.log10(Math.sqrt(squared_mean));
|
|
66
|
-
const roundedDbLevel = Math.round(dbLevel * 10) / 10;
|
|
67
|
-
console.log(
|
|
68
|
-
'Loudest 1000-Hz recording: ' +
|
|
69
|
-
roundedDbLevel +
|
|
70
|
-
' dB with ' +
|
|
71
|
-
numberOfUniqueValues +
|
|
72
|
-
' unique values.'
|
|
73
|
-
);
|
|
74
|
-
} else if (checkRec == 'allhz') {
|
|
75
|
-
const uniqueSet = new Set(dataArray);
|
|
76
|
-
const numberOfUniqueValues = uniqueSet.size;
|
|
77
|
-
const squaredValues = dataArray.map(value => value * value);
|
|
78
|
-
const sum_of_squares = squaredValues.reduce((total, value) => total + value, 0);
|
|
79
|
-
const squared_mean = sum_of_squares / dataArray.length;
|
|
80
|
-
const dbLevel = 20 * Math.log10(Math.sqrt(squared_mean));
|
|
81
|
-
const roundedDbLevel = Math.round(dbLevel * 10) / 10;
|
|
82
|
-
console.log(
|
|
83
|
-
'All Hz Recording: ' +
|
|
84
|
-
roundedDbLevel +
|
|
85
|
-
' dB with ' +
|
|
86
|
-
numberOfUniqueValues +
|
|
87
|
-
' unique values.'
|
|
88
|
-
);
|
|
89
|
-
}
|
|
90
|
-
if (mode === 'volume'){
|
|
91
|
-
console.log('Saving 1000 Hz Recording to #allVolumeRecordings')
|
|
92
|
-
this.#allVolumeRecordings.push(dataArray);
|
|
93
|
-
}else if (mode ==='unfiltered'){
|
|
94
|
-
console.log('Saving unfiltered all Hz recording to #allHzUnfilteredRecordings')
|
|
95
|
-
this.#allHzUnfilteredRecordings.push(dataArray);
|
|
96
|
-
}else if (mode ==='filtered'){
|
|
97
|
-
console.log('Saving filtered all hz recording to #allHzFilteredRecordings')
|
|
98
|
-
this.#allHzFilteredRecordings.push(dataArray);
|
|
99
|
-
}else if (mode ==='background'){
|
|
100
|
-
console.log('Saving background recording to #allBackgroundRecordings')
|
|
101
|
-
this.#allBackgroundRecordings.push(dataArray);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
#saveFilteredRecording = async () => {
|
|
107
|
-
const arrayBuffer = await this.#audioBlob.arrayBuffer();
|
|
108
|
-
const audioBuffer = await this.#audioContext.decodeAudioData(arrayBuffer);
|
|
109
|
-
const data = audioBuffer.getChannelData(0);
|
|
110
|
-
|
|
111
|
-
console.log(`Decoded audio buffer with ${data.length} samples`);
|
|
112
|
-
console.log(`Filtered recording should be of length: ${data.length}`);
|
|
113
|
-
this.#allHzFilteredRecordings.push(Array.from(data));
|
|
114
|
-
};
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Event listener triggered when data is available in the media recorder.
|
|
118
|
-
*
|
|
119
|
-
* @private
|
|
120
|
-
* @param e - The event object.
|
|
121
|
-
* @example
|
|
122
|
-
*/
|
|
123
|
-
#onRecorderDataAvailable = e => {
|
|
124
|
-
if (e.data && e.data.size > 0) this.#recordedChunks.push(e.data);
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Method to create a media recorder object and set up event listeners.
|
|
129
|
-
*
|
|
130
|
-
* @private
|
|
131
|
-
* @param stream - The stream of audio from the Listener.
|
|
132
|
-
* @example
|
|
133
|
-
*/
|
|
134
|
-
#setMediaRecorder = stream => {
|
|
135
|
-
// Create a new MediaRecorder object
|
|
136
|
-
this.#mediaRecorder = new MediaRecorder(stream);
|
|
137
|
-
|
|
138
|
-
// Add event listeners
|
|
139
|
-
this.#mediaRecorder.ondataavailable = e => this.#onRecorderDataAvailable(e);
|
|
140
|
-
};
|
|
141
|
-
|
|
142
|
-
#setAudioContext = () => {
|
|
143
|
-
this.#audioContext = new (window.AudioContext ||
|
|
144
|
-
window.webkitAudioContext ||
|
|
145
|
-
window.audioContext)({
|
|
146
|
-
sampleRate: this.sinkSamplingRate,
|
|
147
|
-
sampleSize: this.sampleSize,
|
|
148
|
-
//sampleRate: 96000
|
|
149
|
-
});
|
|
150
|
-
};
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* Public method to start the recording process.
|
|
154
|
-
*
|
|
155
|
-
* @param stream - The stream of audio from the Listener.
|
|
156
|
-
* @example
|
|
157
|
-
*/
|
|
158
|
-
startRecording = async stream => {
|
|
159
|
-
try {
|
|
160
|
-
// Create a fresh audio context
|
|
161
|
-
this.#setAudioContext();
|
|
162
|
-
// Set up media recorder if needed
|
|
163
|
-
if (!this.#mediaRecorder) this.#setMediaRecorder(stream);
|
|
164
|
-
// clear recorded chunks
|
|
165
|
-
this.#recordedChunks = [];
|
|
166
|
-
// start recording
|
|
167
|
-
this.#mediaRecorder.start();
|
|
168
|
-
} catch (error) {
|
|
169
|
-
console.error('Error in startRecording:', error);
|
|
170
|
-
// Handle the error as needed, e.g., throw it or perform error-specific actions
|
|
171
|
-
}
|
|
172
|
-
};
|
|
173
|
-
|
|
174
|
-
/**
|
|
175
|
-
* Method to stop the recording process.
|
|
176
|
-
*
|
|
177
|
-
* @public
|
|
178
|
-
* @example
|
|
179
|
-
*/
|
|
180
|
-
stopRecording = async (mode, checkRec) => {
|
|
181
|
-
try {
|
|
182
|
-
// Stop the media recorder, and wait for the data to be available
|
|
183
|
-
await new Promise(resolve => {
|
|
184
|
-
this.#mediaRecorder.onstop = () => {
|
|
185
|
-
// when the stop event is triggered, resolve the promise
|
|
186
|
-
this.#audioBlob = new Blob(this.#recordedChunks, {
|
|
187
|
-
type: 'audio/wav; codecs=opus',
|
|
188
|
-
});
|
|
189
|
-
resolve(this.#audioBlob);
|
|
190
|
-
};
|
|
191
|
-
// call stop
|
|
192
|
-
this.#mediaRecorder.stop();
|
|
193
|
-
});
|
|
194
|
-
// Now that we have data, save it
|
|
195
|
-
await this.#saveRecording(mode, checkRec);
|
|
196
|
-
} catch (error) {
|
|
197
|
-
console.error('Error in stopRecording:', error);
|
|
198
|
-
// Handle the error as needed, e.g., throw it or perform error-specific actions
|
|
199
|
-
}
|
|
200
|
-
};
|
|
201
|
-
|
|
202
|
-
/** .
|
|
203
|
-
* .
|
|
204
|
-
* .
|
|
205
|
-
* Public method to get the last recorded audio signal
|
|
206
|
-
*
|
|
207
|
-
* @returns
|
|
208
|
-
* @example
|
|
209
|
-
*/
|
|
210
|
-
getLastRecordedSignal = () => this.#recordedSignals[this.#recordedSignals.length - 1];
|
|
211
|
-
|
|
212
|
-
/** .
|
|
213
|
-
* .
|
|
214
|
-
* .
|
|
215
|
-
* Public method to get the last 1000hz recorded audio signal
|
|
216
|
-
*
|
|
217
|
-
* @returns
|
|
218
|
-
* @example
|
|
219
|
-
*/
|
|
220
|
-
getLastVolumeRecordedSignal = () => Array.from(this.#allVolumeRecordings[this.#allVolumeRecordings.length - 1]);
|
|
221
|
-
|
|
222
|
-
/** .
|
|
223
|
-
* .
|
|
224
|
-
* .
|
|
225
|
-
* Public method to get all the recorded audio signals
|
|
226
|
-
*
|
|
227
|
-
* @returns
|
|
228
|
-
* @example
|
|
229
|
-
*/
|
|
230
|
-
getAllRecordedSignals = () => this.#recordedSignals;
|
|
231
|
-
|
|
232
|
-
/** .
|
|
233
|
-
* .
|
|
234
|
-
* .
|
|
235
|
-
* Public method to get all the recorded audio signals
|
|
236
|
-
*
|
|
237
|
-
* @returns
|
|
238
|
-
* @example
|
|
239
|
-
*/
|
|
240
|
-
getAllVolumeRecordedSignals = () => this.#allVolumeRecordings;
|
|
241
|
-
|
|
242
|
-
/** .
|
|
243
|
-
* .
|
|
244
|
-
* .
|
|
245
|
-
* Public method to get all the recorded audio signals
|
|
246
|
-
*
|
|
247
|
-
* @returns
|
|
248
|
-
* @example
|
|
249
|
-
*/
|
|
250
|
-
getAllFilteredRecordedSignals = () => this.#allHzFilteredRecordings;
|
|
251
|
-
|
|
252
|
-
/** .
|
|
253
|
-
* .
|
|
254
|
-
* .
|
|
255
|
-
* Public method to get all the recorded audio signals
|
|
256
|
-
*
|
|
257
|
-
* @returns
|
|
258
|
-
* @example
|
|
259
|
-
*/
|
|
260
|
-
clearAllFilteredRecordedSignals = () => this.#allHzFilteredRecordings = [];
|
|
261
|
-
|
|
262
|
-
/** .
|
|
263
|
-
* .
|
|
264
|
-
* .
|
|
265
|
-
* Public method to clear last the recorded audio signals
|
|
266
|
-
*
|
|
267
|
-
* @returns
|
|
268
|
-
* @example
|
|
269
|
-
*/
|
|
270
|
-
clearLastFilteredRecordedSignals = () => this.#allHzFilteredRecordings.pop();
|
|
271
|
-
|
|
272
|
-
/** .
|
|
273
|
-
* .
|
|
274
|
-
* .
|
|
275
|
-
* Public method to get all the recorded audio signals for psd
|
|
276
|
-
*
|
|
277
|
-
* @returns
|
|
278
|
-
* @example
|
|
279
|
-
*/
|
|
280
|
-
getAllUnfilteredRecordedSignals = () => this.#allHzUnfilteredRecordings;
|
|
281
|
-
|
|
282
|
-
/** .
|
|
283
|
-
* .
|
|
284
|
-
* .
|
|
285
|
-
* Public method to get all the recorded audio signals
|
|
286
|
-
*
|
|
287
|
-
* @returns
|
|
288
|
-
* @example
|
|
289
|
-
*/
|
|
290
|
-
clearLastUnfilteredRecordedSignals = () => this.#allHzUnfilteredRecordings.pop();
|
|
291
|
-
|
|
292
|
-
/** .
|
|
293
|
-
* .
|
|
294
|
-
* .
|
|
295
|
-
* Public method to get all the recorded audio signals for psd
|
|
296
|
-
*
|
|
297
|
-
* @returns
|
|
298
|
-
* @example
|
|
299
|
-
*/
|
|
300
|
-
getAllBackgroundRecordings = () => this.#allBackgroundRecordings;
|
|
301
|
-
|
|
302
|
-
/** .
|
|
303
|
-
* .
|
|
304
|
-
* .
|
|
305
|
-
* Public method to set the sampling rate used by the capture device
|
|
306
|
-
*
|
|
307
|
-
* @param {Number} sinkSamplingRate - The sampling rate of the capture device
|
|
308
|
-
* @example
|
|
309
|
-
*/
|
|
310
|
-
setSinkSamplingRate = sinkSamplingRate => {
|
|
311
|
-
this.sinkSamplingRate = sinkSamplingRate;
|
|
312
|
-
};
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
export default AudioRecorder;
|
|
1
|
+
import MyEventEmitter from '../myEventEmitter';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @class provides a simple interface for recording audio from a microphone
|
|
5
|
+
* using the Media Recorder API.
|
|
6
|
+
*/
|
|
7
|
+
class AudioRecorder extends MyEventEmitter {
|
|
8
|
+
/** @private */
|
|
9
|
+
#mediaRecorder;
|
|
10
|
+
|
|
11
|
+
/** @private */
|
|
12
|
+
#recordedChunks = [];
|
|
13
|
+
|
|
14
|
+
/** @private */
|
|
15
|
+
#audioBlob;
|
|
16
|
+
|
|
17
|
+
/** @private */
|
|
18
|
+
#audioContext;
|
|
19
|
+
|
|
20
|
+
/** @private */
|
|
21
|
+
#recordedSignals = [];
|
|
22
|
+
|
|
23
|
+
/**@private */
|
|
24
|
+
#allHzUnfilteredRecordings = [];
|
|
25
|
+
|
|
26
|
+
/**@private */
|
|
27
|
+
#allBackgroundRecordings = [];
|
|
28
|
+
|
|
29
|
+
/** @private */
|
|
30
|
+
#allHzFilteredRecordings = [];
|
|
31
|
+
|
|
32
|
+
/** @private */
|
|
33
|
+
sinkSamplingRate;
|
|
34
|
+
|
|
35
|
+
/** @private */
|
|
36
|
+
sampleSize;
|
|
37
|
+
|
|
38
|
+
/** @private */
|
|
39
|
+
#allVolumeRecordings = [];
|
|
40
|
+
|
|
41
|
+
/** @private */
|
|
42
|
+
flags = {};
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Decode the audio data from the recorded audio blob.
|
|
46
|
+
*
|
|
47
|
+
* @private
|
|
48
|
+
* @example
|
|
49
|
+
*/
|
|
50
|
+
#saveRecording = async (mode, checkRec) => {
|
|
51
|
+
const arrayBuffer = await this.#audioBlob.arrayBuffer();
|
|
52
|
+
const audioBuffer = await this.#audioContext.decodeAudioData(arrayBuffer);
|
|
53
|
+
console.log(audioBuffer);
|
|
54
|
+
const data = audioBuffer.getChannelData(0);
|
|
55
|
+
const dataArray = Array.from(data);
|
|
56
|
+
|
|
57
|
+
console.log(`Decoded audio buffer with ${data.length} samples`);
|
|
58
|
+
console.log(`Unfiltered recording should be of length: ${data.length}`);
|
|
59
|
+
if (checkRec == 'loudest') {
|
|
60
|
+
const uniqueSet = new Set(dataArray);
|
|
61
|
+
const numberOfUniqueValues = uniqueSet.size;
|
|
62
|
+
const squaredValues = dataArray.map(value => value * value);
|
|
63
|
+
const sum_of_squares = squaredValues.reduce((total, value) => total + value, 0);
|
|
64
|
+
const squared_mean = sum_of_squares / dataArray.length;
|
|
65
|
+
const dbLevel = 20 * Math.log10(Math.sqrt(squared_mean));
|
|
66
|
+
const roundedDbLevel = Math.round(dbLevel * 10) / 10;
|
|
67
|
+
console.log(
|
|
68
|
+
'Loudest 1000-Hz recording: ' +
|
|
69
|
+
roundedDbLevel +
|
|
70
|
+
' dB with ' +
|
|
71
|
+
numberOfUniqueValues +
|
|
72
|
+
' unique values.'
|
|
73
|
+
);
|
|
74
|
+
} else if (checkRec == 'allhz') {
|
|
75
|
+
const uniqueSet = new Set(dataArray);
|
|
76
|
+
const numberOfUniqueValues = uniqueSet.size;
|
|
77
|
+
const squaredValues = dataArray.map(value => value * value);
|
|
78
|
+
const sum_of_squares = squaredValues.reduce((total, value) => total + value, 0);
|
|
79
|
+
const squared_mean = sum_of_squares / dataArray.length;
|
|
80
|
+
const dbLevel = 20 * Math.log10(Math.sqrt(squared_mean));
|
|
81
|
+
const roundedDbLevel = Math.round(dbLevel * 10) / 10;
|
|
82
|
+
console.log(
|
|
83
|
+
'All Hz Recording: ' +
|
|
84
|
+
roundedDbLevel +
|
|
85
|
+
' dB with ' +
|
|
86
|
+
numberOfUniqueValues +
|
|
87
|
+
' unique values.'
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
if (mode === 'volume'){
|
|
91
|
+
console.log('Saving 1000 Hz Recording to #allVolumeRecordings')
|
|
92
|
+
this.#allVolumeRecordings.push(dataArray);
|
|
93
|
+
}else if (mode ==='unfiltered'){
|
|
94
|
+
console.log('Saving unfiltered all Hz recording to #allHzUnfilteredRecordings')
|
|
95
|
+
this.#allHzUnfilteredRecordings.push(dataArray);
|
|
96
|
+
}else if (mode ==='filtered'){
|
|
97
|
+
console.log('Saving filtered all hz recording to #allHzFilteredRecordings')
|
|
98
|
+
this.#allHzFilteredRecordings.push(dataArray);
|
|
99
|
+
}else if (mode ==='background'){
|
|
100
|
+
console.log('Saving background recording to #allBackgroundRecordings')
|
|
101
|
+
this.#allBackgroundRecordings.push(dataArray);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
#saveFilteredRecording = async () => {
|
|
107
|
+
const arrayBuffer = await this.#audioBlob.arrayBuffer();
|
|
108
|
+
const audioBuffer = await this.#audioContext.decodeAudioData(arrayBuffer);
|
|
109
|
+
const data = audioBuffer.getChannelData(0);
|
|
110
|
+
|
|
111
|
+
console.log(`Decoded audio buffer with ${data.length} samples`);
|
|
112
|
+
console.log(`Filtered recording should be of length: ${data.length}`);
|
|
113
|
+
this.#allHzFilteredRecordings.push(Array.from(data));
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Event listener triggered when data is available in the media recorder.
|
|
118
|
+
*
|
|
119
|
+
* @private
|
|
120
|
+
* @param e - The event object.
|
|
121
|
+
* @example
|
|
122
|
+
*/
|
|
123
|
+
#onRecorderDataAvailable = e => {
|
|
124
|
+
if (e.data && e.data.size > 0) this.#recordedChunks.push(e.data);
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Method to create a media recorder object and set up event listeners.
|
|
129
|
+
*
|
|
130
|
+
* @private
|
|
131
|
+
* @param stream - The stream of audio from the Listener.
|
|
132
|
+
* @example
|
|
133
|
+
*/
|
|
134
|
+
#setMediaRecorder = stream => {
|
|
135
|
+
// Create a new MediaRecorder object
|
|
136
|
+
this.#mediaRecorder = new MediaRecorder(stream);
|
|
137
|
+
|
|
138
|
+
// Add event listeners
|
|
139
|
+
this.#mediaRecorder.ondataavailable = e => this.#onRecorderDataAvailable(e);
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
#setAudioContext = () => {
|
|
143
|
+
this.#audioContext = new (window.AudioContext ||
|
|
144
|
+
window.webkitAudioContext ||
|
|
145
|
+
window.audioContext)({
|
|
146
|
+
sampleRate: this.sinkSamplingRate,
|
|
147
|
+
sampleSize: this.sampleSize,
|
|
148
|
+
//sampleRate: 96000
|
|
149
|
+
});
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Public method to start the recording process.
|
|
154
|
+
*
|
|
155
|
+
* @param stream - The stream of audio from the Listener.
|
|
156
|
+
* @example
|
|
157
|
+
*/
|
|
158
|
+
startRecording = async stream => {
|
|
159
|
+
try {
|
|
160
|
+
// Create a fresh audio context
|
|
161
|
+
this.#setAudioContext();
|
|
162
|
+
// Set up media recorder if needed
|
|
163
|
+
if (!this.#mediaRecorder) this.#setMediaRecorder(stream);
|
|
164
|
+
// clear recorded chunks
|
|
165
|
+
this.#recordedChunks = [];
|
|
166
|
+
// start recording
|
|
167
|
+
this.#mediaRecorder.start();
|
|
168
|
+
} catch (error) {
|
|
169
|
+
console.error('Error in startRecording:', error);
|
|
170
|
+
// Handle the error as needed, e.g., throw it or perform error-specific actions
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Method to stop the recording process.
|
|
176
|
+
*
|
|
177
|
+
* @public
|
|
178
|
+
* @example
|
|
179
|
+
*/
|
|
180
|
+
stopRecording = async (mode, checkRec) => {
|
|
181
|
+
try {
|
|
182
|
+
// Stop the media recorder, and wait for the data to be available
|
|
183
|
+
await new Promise(resolve => {
|
|
184
|
+
this.#mediaRecorder.onstop = () => {
|
|
185
|
+
// when the stop event is triggered, resolve the promise
|
|
186
|
+
this.#audioBlob = new Blob(this.#recordedChunks, {
|
|
187
|
+
type: 'audio/wav; codecs=opus',
|
|
188
|
+
});
|
|
189
|
+
resolve(this.#audioBlob);
|
|
190
|
+
};
|
|
191
|
+
// call stop
|
|
192
|
+
this.#mediaRecorder.stop();
|
|
193
|
+
});
|
|
194
|
+
// Now that we have data, save it
|
|
195
|
+
await this.#saveRecording(mode, checkRec);
|
|
196
|
+
} catch (error) {
|
|
197
|
+
console.error('Error in stopRecording:', error);
|
|
198
|
+
// Handle the error as needed, e.g., throw it or perform error-specific actions
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
/** .
|
|
203
|
+
* .
|
|
204
|
+
* .
|
|
205
|
+
* Public method to get the last recorded audio signal
|
|
206
|
+
*
|
|
207
|
+
* @returns
|
|
208
|
+
* @example
|
|
209
|
+
*/
|
|
210
|
+
getLastRecordedSignal = () => this.#recordedSignals[this.#recordedSignals.length - 1];
|
|
211
|
+
|
|
212
|
+
/** .
|
|
213
|
+
* .
|
|
214
|
+
* .
|
|
215
|
+
* Public method to get the last 1000hz recorded audio signal
|
|
216
|
+
*
|
|
217
|
+
* @returns
|
|
218
|
+
* @example
|
|
219
|
+
*/
|
|
220
|
+
getLastVolumeRecordedSignal = () => Array.from(this.#allVolumeRecordings[this.#allVolumeRecordings.length - 1]);
|
|
221
|
+
|
|
222
|
+
/** .
|
|
223
|
+
* .
|
|
224
|
+
* .
|
|
225
|
+
* Public method to get all the recorded audio signals
|
|
226
|
+
*
|
|
227
|
+
* @returns
|
|
228
|
+
* @example
|
|
229
|
+
*/
|
|
230
|
+
getAllRecordedSignals = () => this.#recordedSignals;
|
|
231
|
+
|
|
232
|
+
/** .
|
|
233
|
+
* .
|
|
234
|
+
* .
|
|
235
|
+
* Public method to get all the recorded audio signals
|
|
236
|
+
*
|
|
237
|
+
* @returns
|
|
238
|
+
* @example
|
|
239
|
+
*/
|
|
240
|
+
getAllVolumeRecordedSignals = () => this.#allVolumeRecordings;
|
|
241
|
+
|
|
242
|
+
/** .
|
|
243
|
+
* .
|
|
244
|
+
* .
|
|
245
|
+
* Public method to get all the recorded audio signals
|
|
246
|
+
*
|
|
247
|
+
* @returns
|
|
248
|
+
* @example
|
|
249
|
+
*/
|
|
250
|
+
getAllFilteredRecordedSignals = () => this.#allHzFilteredRecordings;
|
|
251
|
+
|
|
252
|
+
/** .
|
|
253
|
+
* .
|
|
254
|
+
* .
|
|
255
|
+
* Public method to get all the recorded audio signals
|
|
256
|
+
*
|
|
257
|
+
* @returns
|
|
258
|
+
* @example
|
|
259
|
+
*/
|
|
260
|
+
clearAllFilteredRecordedSignals = () => this.#allHzFilteredRecordings = [];
|
|
261
|
+
|
|
262
|
+
/** .
|
|
263
|
+
* .
|
|
264
|
+
* .
|
|
265
|
+
* Public method to clear last the recorded audio signals
|
|
266
|
+
*
|
|
267
|
+
* @returns
|
|
268
|
+
* @example
|
|
269
|
+
*/
|
|
270
|
+
clearLastFilteredRecordedSignals = () => this.#allHzFilteredRecordings.pop();
|
|
271
|
+
|
|
272
|
+
/** .
|
|
273
|
+
* .
|
|
274
|
+
* .
|
|
275
|
+
* Public method to get all the recorded audio signals for psd
|
|
276
|
+
*
|
|
277
|
+
* @returns
|
|
278
|
+
* @example
|
|
279
|
+
*/
|
|
280
|
+
getAllUnfilteredRecordedSignals = () => this.#allHzUnfilteredRecordings;
|
|
281
|
+
|
|
282
|
+
/** .
|
|
283
|
+
* .
|
|
284
|
+
* .
|
|
285
|
+
* Public method to get all the recorded audio signals
|
|
286
|
+
*
|
|
287
|
+
* @returns
|
|
288
|
+
* @example
|
|
289
|
+
*/
|
|
290
|
+
clearLastUnfilteredRecordedSignals = () => this.#allHzUnfilteredRecordings.pop();
|
|
291
|
+
|
|
292
|
+
/** .
|
|
293
|
+
* .
|
|
294
|
+
* .
|
|
295
|
+
* Public method to get all the recorded audio signals for psd
|
|
296
|
+
*
|
|
297
|
+
* @returns
|
|
298
|
+
* @example
|
|
299
|
+
*/
|
|
300
|
+
getAllBackgroundRecordings = () => this.#allBackgroundRecordings;
|
|
301
|
+
|
|
302
|
+
/** .
|
|
303
|
+
* .
|
|
304
|
+
* .
|
|
305
|
+
* Public method to set the sampling rate used by the capture device
|
|
306
|
+
*
|
|
307
|
+
* @param {Number} sinkSamplingRate - The sampling rate of the capture device
|
|
308
|
+
* @example
|
|
309
|
+
*/
|
|
310
|
+
setSinkSamplingRate = sinkSamplingRate => {
|
|
311
|
+
this.sinkSamplingRate = sinkSamplingRate;
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
export default AudioRecorder;
|