speaker-calibration 2.1.8 → 2.1.10
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/package.json
CHANGED
|
@@ -6,7 +6,6 @@ class PythonServerAPI {
|
|
|
6
6
|
static PYTHON_SERVER_URL = 'https://easyeyes-python-flask-server.herokuapp.com';
|
|
7
7
|
|
|
8
8
|
static TEST_SERVER_URL = 'http://127.0.0.1:5000';
|
|
9
|
-
|
|
10
9
|
/**
|
|
11
10
|
* @param data- -
|
|
12
11
|
* g = inverted impulse response, when convolved with the impulse
|
|
@@ -52,7 +51,7 @@ class PythonServerAPI {
|
|
|
52
51
|
return res.data[task];
|
|
53
52
|
};
|
|
54
53
|
|
|
55
|
-
getInverseImpulseResponse = async ({payload}) => {
|
|
54
|
+
getInverseImpulseResponse = async ({payload,mls,lowHz,highHz}) => {
|
|
56
55
|
const task = 'inverse-impulse-response';
|
|
57
56
|
let res = null;
|
|
58
57
|
|
|
@@ -61,6 +60,9 @@ class PythonServerAPI {
|
|
|
61
60
|
const data = JSON.stringify({
|
|
62
61
|
task,
|
|
63
62
|
payload,
|
|
63
|
+
mls,
|
|
64
|
+
lowHz,
|
|
65
|
+
highHz
|
|
64
66
|
});
|
|
65
67
|
|
|
66
68
|
await axios({
|
|
@@ -31,6 +31,9 @@ class AudioCalibrator extends AudioRecorder {
|
|
|
31
31
|
/** @private */
|
|
32
32
|
sourceAudioContext;
|
|
33
33
|
|
|
34
|
+
/** @private */
|
|
35
|
+
sourceAudioContextConvolved;
|
|
36
|
+
|
|
34
37
|
/** @protected */
|
|
35
38
|
numCalibratingRounds = 1;
|
|
36
39
|
|
|
@@ -43,6 +46,9 @@ class AudioCalibrator extends AudioRecorder {
|
|
|
43
46
|
/** @protected */
|
|
44
47
|
calibrationNodes = [];
|
|
45
48
|
|
|
49
|
+
/** @protected */
|
|
50
|
+
calibrationNodesConvolved = [];
|
|
51
|
+
|
|
46
52
|
/** @protected */
|
|
47
53
|
localAudio;
|
|
48
54
|
|
|
@@ -181,6 +187,7 @@ class AudioCalibrator extends AudioRecorder {
|
|
|
181
187
|
setSamplingRates = samplingRate => {
|
|
182
188
|
this.sinkSamplingRate = samplingRate;
|
|
183
189
|
this.sourceSamplingRate = samplingRate;
|
|
190
|
+
|
|
184
191
|
// this.emit('update', {message: `sampling at ${samplingRate}Hz...`});
|
|
185
192
|
};
|
|
186
193
|
|
|
@@ -190,6 +197,11 @@ class AudioCalibrator extends AudioRecorder {
|
|
|
190
197
|
this.calibrationNodes.push(node);
|
|
191
198
|
};
|
|
192
199
|
|
|
200
|
+
addCalibrationNodeConvolved = node => {
|
|
201
|
+
|
|
202
|
+
this.calibrationNodesConvolved.push(node);
|
|
203
|
+
}
|
|
204
|
+
|
|
193
205
|
makeNewSourceAudioContext = () => {
|
|
194
206
|
const options = {
|
|
195
207
|
sampleRate: this.sourceSamplingRate,
|
|
@@ -202,6 +214,20 @@ class AudioCalibrator extends AudioRecorder {
|
|
|
202
214
|
return this.sourceAudioContext;
|
|
203
215
|
};
|
|
204
216
|
|
|
217
|
+
makeNewSourceAudioContextConvolved = () => {
|
|
218
|
+
const options = {
|
|
219
|
+
sampleRate: this.sourceSamplingRate,
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
this.sourceAudioContextConvolved = new (window.AudioContext ||
|
|
223
|
+
window.webkitAudioContext ||
|
|
224
|
+
window.audioContext)(options);
|
|
225
|
+
|
|
226
|
+
return this.sourceAudioContextConvolved;
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
|
|
205
231
|
/** .
|
|
206
232
|
* .
|
|
207
233
|
* .
|
|
@@ -212,7 +238,12 @@ class AudioCalibrator extends AudioRecorder {
|
|
|
212
238
|
downloadData = () => {
|
|
213
239
|
const recordings = this.getAllRecordedSignals();
|
|
214
240
|
const i = recordings.length - 1;
|
|
215
|
-
saveToCSV(recordings[i], `recordedMLSignal_${i}.csv`);
|
|
241
|
+
saveToCSV(recordings[i], `recordedMLSignal_${i}_unconvolved.csv`);
|
|
242
|
+
};
|
|
243
|
+
downloadConvolvedData = () => {
|
|
244
|
+
const recordings = this.getAllRecordedSignals();
|
|
245
|
+
const i = recordings.length - 1;
|
|
246
|
+
saveToCSV(recordings[i], `recordedMLSignal_${i}_convolved.csv`);
|
|
216
247
|
};
|
|
217
248
|
}
|
|
218
249
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import AudioCalibrator from '../audioCalibrator';
|
|
2
2
|
import MlsGenInterface from './mlsGen/mlsGenInterface';
|
|
3
3
|
|
|
4
|
-
import {sleep, csvToArray} from '../../utils';
|
|
4
|
+
import {sleep, csvToArray, saveToCSV} from '../../utils';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
*
|
|
@@ -16,12 +16,14 @@ class ImpulseResponse extends AudioCalibrator {
|
|
|
16
16
|
* @param {number} [calibratorParams.numCaptures = 5] - number of captures to perform
|
|
17
17
|
* @param {number} [calibratorParams.numMLSPerCapture = 4] - number of bursts of MLS per capture
|
|
18
18
|
*/
|
|
19
|
-
constructor({download = false, mlsOrder = 18, numCaptures = 3, numMLSPerCapture = 4}) {
|
|
19
|
+
constructor({download = false, mlsOrder = 18, numCaptures = 3, numMLSPerCapture = 4, lowHz = 20, highHz = 10000}) {
|
|
20
20
|
super(numCaptures, numMLSPerCapture);
|
|
21
21
|
this.#mlsOrder = parseInt(mlsOrder, 10);
|
|
22
22
|
this.#P = 2 ** mlsOrder - 1;
|
|
23
23
|
this.#download = download;
|
|
24
24
|
this.#mls = [];
|
|
25
|
+
this.#lowHz = lowHz;
|
|
26
|
+
this.#highHz = highHz;
|
|
25
27
|
}
|
|
26
28
|
|
|
27
29
|
/** @private */
|
|
@@ -42,18 +44,30 @@ class ImpulseResponse extends AudioCalibrator {
|
|
|
42
44
|
/** @private */
|
|
43
45
|
#mlsOrder;
|
|
44
46
|
|
|
47
|
+
/** @private */
|
|
48
|
+
#lowHz;
|
|
49
|
+
|
|
50
|
+
/** @private */
|
|
51
|
+
#highHz;
|
|
52
|
+
|
|
45
53
|
/** @private */
|
|
46
54
|
#mls;
|
|
47
55
|
|
|
48
56
|
/** @private */
|
|
49
57
|
#P;
|
|
50
58
|
|
|
59
|
+
/** @private */
|
|
60
|
+
#audioContext;
|
|
61
|
+
|
|
51
62
|
/** @private */
|
|
52
63
|
TAPER_SECS = 5;
|
|
53
64
|
|
|
54
65
|
/** @private */
|
|
55
66
|
offsetGainNode;
|
|
56
67
|
|
|
68
|
+
/** @private */
|
|
69
|
+
convolution;
|
|
70
|
+
|
|
57
71
|
/** .
|
|
58
72
|
* .
|
|
59
73
|
* .
|
|
@@ -64,14 +78,22 @@ class ImpulseResponse extends AudioCalibrator {
|
|
|
64
78
|
*/
|
|
65
79
|
sendImpulseResponsesToServerForProcessing = async () => {
|
|
66
80
|
const computedIRs = await Promise.all(this.impulseResponses);
|
|
81
|
+
const mls = this.#mls;
|
|
82
|
+
const lowHz = this.#lowHz;
|
|
83
|
+
const highHz = this.#highHz;
|
|
67
84
|
this.emit('update', {message: `computing the IIR...`});
|
|
68
85
|
return this.pyServerAPI
|
|
69
86
|
.getInverseImpulseResponse({
|
|
70
87
|
payload: computedIRs.slice(0, this.numCaptures),
|
|
88
|
+
mls,
|
|
89
|
+
lowHz,
|
|
90
|
+
highHz
|
|
71
91
|
})
|
|
72
92
|
.then(res => {
|
|
93
|
+
console.log(res);
|
|
73
94
|
this.emit('update', {message: `done computing the IIR...`});
|
|
74
|
-
this.invertedImpulseResponse = res;
|
|
95
|
+
this.invertedImpulseResponse = res["iir"];
|
|
96
|
+
this.convolution = res["convolution"];
|
|
75
97
|
})
|
|
76
98
|
.catch(err => {
|
|
77
99
|
// this.emit('InvertedImpulseResponse', {res: false});
|
|
@@ -106,6 +128,7 @@ class ImpulseResponse extends AudioCalibrator {
|
|
|
106
128
|
.then(res => {
|
|
107
129
|
if (this.numSuccessfulCaptured < this.numCaptures) {
|
|
108
130
|
this.numSuccessfulCaptured += 1;
|
|
131
|
+
console.log("num succ capt: " + this.numSuccessfulCaptured);
|
|
109
132
|
this.emit('update', {
|
|
110
133
|
message: `${this.numSuccessfulCaptured}/${this.numCaptures} IRs computed...`,
|
|
111
134
|
});
|
|
@@ -162,9 +185,14 @@ class ImpulseResponse extends AudioCalibrator {
|
|
|
162
185
|
|
|
163
186
|
#afterMLSwIIRRecord = () => {
|
|
164
187
|
if (this.#download) {
|
|
165
|
-
this.
|
|
188
|
+
this.downloadConvolvedData();
|
|
189
|
+
}
|
|
190
|
+
if (this.numSuccessfulCaptured < this.numCaptures) {
|
|
191
|
+
this.numSuccessfulCaptured += 1;
|
|
192
|
+
this.emit('update', {
|
|
193
|
+
message: `${this.numSuccessfulCaptured}/${this.numCaptures} IRs computed...`,
|
|
194
|
+
});
|
|
166
195
|
}
|
|
167
|
-
this.#stopCalibrationAudio();
|
|
168
196
|
};
|
|
169
197
|
|
|
170
198
|
/** .
|
|
@@ -240,12 +268,13 @@ class ImpulseResponse extends AudioCalibrator {
|
|
|
240
268
|
// fill the buffer with our data
|
|
241
269
|
try {
|
|
242
270
|
for (let i = 0; i < dataBuffer.length; i += 1) {
|
|
243
|
-
data[i] = dataBuffer[i];
|
|
271
|
+
data[i] = dataBuffer[i]*.1;
|
|
244
272
|
}
|
|
245
273
|
} catch (error) {
|
|
246
274
|
console.error(error);
|
|
247
275
|
}
|
|
248
|
-
|
|
276
|
+
console.log("mls second, same?");
|
|
277
|
+
console.log(data);
|
|
249
278
|
const onsetGainNode = audioContext.createGain();
|
|
250
279
|
this.offsetGainNode = audioContext.createGain();
|
|
251
280
|
const source = audioContext.createBufferSource();
|
|
@@ -258,7 +287,6 @@ class ImpulseResponse extends AudioCalibrator {
|
|
|
258
287
|
|
|
259
288
|
const onsetCurve = ImpulseResponse.createSCurveBuffer(this.sourceSamplingRate, Math.PI / 2);
|
|
260
289
|
onsetGainNode.gain.setValueCurveAtTime(onsetCurve, 0, this.TAPER_SECS);
|
|
261
|
-
|
|
262
290
|
this.addCalibrationNode(source);
|
|
263
291
|
};
|
|
264
292
|
|
|
@@ -270,70 +298,47 @@ class ImpulseResponse extends AudioCalibrator {
|
|
|
270
298
|
*/
|
|
271
299
|
#setCalibrationNodesFromBuffer = (dataBufferArray = [this.#mlsBufferView]) => {
|
|
272
300
|
if (dataBufferArray.length === 1) {
|
|
301
|
+
console.log('data buffer aray');
|
|
302
|
+
console.log(dataBufferArray);
|
|
273
303
|
this.#createCalibrationNodeFromBuffer(dataBufferArray[0]);
|
|
274
304
|
} else {
|
|
275
305
|
throw new Error('The length of the data buffer array must be 1');
|
|
276
306
|
}
|
|
277
|
-
};
|
|
278
307
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
// -------------------------------------------------------- IIR
|
|
283
|
-
const iirBuffer = audioCtx.createBuffer(
|
|
284
|
-
1,
|
|
285
|
-
// TODO: quality check this
|
|
286
|
-
iir.length - 1,
|
|
287
|
-
audioCtx.sampleRate
|
|
288
|
-
);
|
|
289
|
-
|
|
290
|
-
// Fill the buffer with the inverted impulse response
|
|
291
|
-
const iirChannelZeroBuffer = iirBuffer.getChannelData(0);
|
|
292
|
-
for (let i = 0; i < iirBuffer.length; i += 1) {
|
|
293
|
-
// audio needs to be in [-1.0; 1.0]
|
|
294
|
-
iirChannelZeroBuffer[i] = iir[i];
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
const convolverNode = audioCtx.createConvolver();
|
|
298
|
-
|
|
299
|
-
convolverNode.normalize = false;
|
|
300
|
-
convolverNode.channelCount = 1;
|
|
301
|
-
convolverNode.buffer = iirBuffer;
|
|
308
|
+
|
|
309
|
+
};
|
|
302
310
|
|
|
303
|
-
|
|
304
|
-
|
|
311
|
+
/**
|
|
312
|
+
* function to put MLS filtered IIR data obtained from
|
|
313
|
+
* python server into our audio buffer to be played aloud
|
|
314
|
+
*/
|
|
315
|
+
#putInPythonConv = () => {
|
|
316
|
+
const audioCtx = this.makeNewSourceAudioContextConvolved();
|
|
317
|
+
const buffer = audioCtx.createBuffer(
|
|
305
318
|
1, // number of channels
|
|
306
|
-
|
|
319
|
+
this.convolution.length,
|
|
307
320
|
audioCtx.sampleRate // sample rate
|
|
308
321
|
);
|
|
309
322
|
|
|
310
|
-
const
|
|
323
|
+
const data = buffer.getChannelData(0); // get data
|
|
311
324
|
// fill the buffer with our data
|
|
312
325
|
try {
|
|
313
|
-
for (let i = 0; i <
|
|
314
|
-
|
|
326
|
+
for (let i = 0; i < this.convolution.length; i += 1) {
|
|
327
|
+
data[i] = this.convolution[i];
|
|
315
328
|
}
|
|
316
329
|
} catch (error) {
|
|
317
330
|
console.error(error);
|
|
318
331
|
}
|
|
319
332
|
|
|
320
|
-
const
|
|
321
|
-
|
|
322
|
-
sourceNode.buffer = calibrationSignalBuffer;
|
|
323
|
-
sourceNode.loop = true;
|
|
324
|
-
sourceNode.connect(convolverNode);
|
|
325
|
-
|
|
326
|
-
convolverNode.connect(audioCtx.destination);
|
|
327
|
-
|
|
328
|
-
console.log({convolverNode, sourceNode});
|
|
329
|
-
|
|
330
|
-
this.addCalibrationNode(sourceNode);
|
|
331
|
-
};
|
|
333
|
+
const source = audioCtx.createBufferSource();
|
|
332
334
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
335
|
+
source.buffer = buffer;
|
|
336
|
+
source.loop = true;
|
|
337
|
+
source.connect(audioCtx.destination);
|
|
336
338
|
|
|
339
|
+
this.addCalibrationNodeConvolved(source);
|
|
340
|
+
}
|
|
341
|
+
|
|
337
342
|
/**
|
|
338
343
|
* Creates an audio context and plays it for a few seconds.
|
|
339
344
|
*
|
|
@@ -344,8 +349,15 @@ class ImpulseResponse extends AudioCalibrator {
|
|
|
344
349
|
#playCalibrationAudio = () => {
|
|
345
350
|
this.calibrationNodes[0].start(0);
|
|
346
351
|
this.#mls = this.calibrationNodes[0].buffer.getChannelData(0);
|
|
352
|
+
console.log(this.#mls);
|
|
347
353
|
this.emit('update', {message: 'playing the calibration tone...'});
|
|
348
|
-
};
|
|
354
|
+
};
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
#playCalibrationAudioConvolved = () => {
|
|
358
|
+
this.calibrationNodesConvolved[0].start(0);
|
|
359
|
+
this.emit('update',{message: 'playing the convolved calibration tone...'})
|
|
360
|
+
}
|
|
349
361
|
|
|
350
362
|
/** .
|
|
351
363
|
* .
|
|
@@ -361,34 +373,53 @@ class ImpulseResponse extends AudioCalibrator {
|
|
|
361
373
|
);
|
|
362
374
|
|
|
363
375
|
this.offsetGainNode.gain.setTargetAtTime(0, this.sourceAudioContext.currentTime, 0.5);
|
|
376
|
+
this.calibrationNodes[0].stop(0);
|
|
377
|
+
this.sourceAudioContext.close();
|
|
364
378
|
this.emit('update', {message: 'stopping the calibration tone...'});
|
|
365
379
|
};
|
|
366
380
|
|
|
381
|
+
#stopCalibrationAudioConvolved = () => {
|
|
382
|
+
this.offsetGainNode.gain.setValueAtTime(
|
|
383
|
+
this.offsetGainNode.gain.value,
|
|
384
|
+
this.sourceAudioContextConvolved.currentTime
|
|
385
|
+
);
|
|
386
|
+
|
|
387
|
+
this.offsetGainNode.gain.setTargetAtTime(0, this.sourceAudioContextConvolved.currentTime, 0.5);
|
|
388
|
+
//this.calibrationNodesConvolved[0].stop(0);
|
|
389
|
+
console.log("right before closing volved audio context");
|
|
390
|
+
this.sourceAudioContextConvolved.close();
|
|
391
|
+
this.emit('update', {message: 'stopping the convolved calibration tone...'});
|
|
392
|
+
|
|
393
|
+
}
|
|
394
|
+
|
|
367
395
|
playMLSwithIIR = async (stream, iir) => {
|
|
396
|
+
console.log('play mls with iir');
|
|
368
397
|
this.invertedImpulseResponse = iir;
|
|
369
398
|
// initialize the MLSGenInterface object with it's factory method
|
|
399
|
+
|
|
370
400
|
await MlsGenInterface.factory(
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
401
|
+
this.#mlsOrder,
|
|
402
|
+
this.sinkSamplingRate,
|
|
403
|
+
this.sourceSamplingRate
|
|
374
404
|
).then(mlsGenInterface => {
|
|
375
|
-
|
|
376
|
-
|
|
405
|
+
this.#mlsGenInterface = mlsGenInterface;
|
|
406
|
+
this.#mlsBufferView = this.#mlsGenInterface.getMLS();
|
|
377
407
|
});
|
|
378
408
|
|
|
409
|
+
console.log('after mls factory'); //works up to here.
|
|
410
|
+
console.log(this.#mls);
|
|
379
411
|
// after intializating, start the calibration steps with garbage collection
|
|
380
412
|
await this.#mlsGenInterface.withGarbageCollection([
|
|
381
|
-
|
|
382
|
-
this.calibrationSteps
|
|
383
|
-
[
|
|
413
|
+
() =>
|
|
414
|
+
this.calibrationSteps(
|
|
384
415
|
stream,
|
|
385
|
-
this.#
|
|
386
|
-
this.#
|
|
387
|
-
|
|
416
|
+
this.#playCalibrationAudioConvolved, // play audio func (required)
|
|
417
|
+
this.#putInPythonConv, // before play func
|
|
418
|
+
this.#awaitSignalOnset, // before record
|
|
419
|
+
() => this.numSuccessfulCaptured < this.numCaptures,
|
|
388
420
|
this.#awaitDesiredMLSLength, // during record
|
|
389
421
|
this.#afterMLSwIIRRecord, // after record
|
|
390
|
-
|
|
391
|
-
],
|
|
422
|
+
),
|
|
392
423
|
]);
|
|
393
424
|
};
|
|
394
425
|
|
|
@@ -433,8 +464,22 @@ class ImpulseResponse extends AudioCalibrator {
|
|
|
433
464
|
// so let's send all the IRs to the server to be converted to a single IIR
|
|
434
465
|
await this.sendImpulseResponsesToServerForProcessing();
|
|
435
466
|
|
|
467
|
+
if (this.#download) {
|
|
468
|
+
saveToCSV(this.invertedImpulseResponse,'IIR.csv');
|
|
469
|
+
const computedIRagain = await Promise.all(this.impulseResponses)
|
|
470
|
+
.then(res => {
|
|
471
|
+
for (let i = 0; i < res.length; i++){
|
|
472
|
+
saveToCSV(res[i], `IR_${i}`);
|
|
473
|
+
}
|
|
474
|
+
})
|
|
475
|
+
saveToCSV(this.#mls,"MLS.csv");
|
|
476
|
+
saveToCSV(this.convolution,'python_convolution_mls_iir.csv');
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
this.numSuccessfulCaptured = 0;
|
|
436
480
|
// debugging function, use to test the result of the IIR
|
|
437
|
-
|
|
481
|
+
await this.playMLSwithIIR(stream, this.invertedImpulseResponse);
|
|
482
|
+
this.#stopCalibrationAudioConvolved();
|
|
438
483
|
|
|
439
484
|
return this.invertedImpulseResponse;
|
|
440
485
|
};
|