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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "speaker-calibration",
3
- "version": "2.1.8",
3
+ "version": "2.1.10",
4
4
  "description": "Speaker calibration library for auditory testing",
5
5
  "main": "dist/main.js",
6
6
  "directories": {
@@ -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
 
@@ -69,6 +69,7 @@ class AudioRecorder extends MyEventEmitter {
69
69
  window.webkitAudioContext ||
70
70
  window.audioContext)({
71
71
  sampleRate: this.sinkSamplingRate,
72
+ //sampleRate: 96000
72
73
  });
73
74
  };
74
75
 
@@ -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.downloadData();
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
- #createImpulseResponseFilterGraph = (calibrationSignal, iir) => {
280
- const audioCtx = this.makeNewSourceAudioContext();
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
- // ------------------------------------------------------ MLS
304
- const calibrationSignalBuffer = audioCtx.createBuffer(
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
- calibrationSignal.length,
319
+ this.convolution.length,
307
320
  audioCtx.sampleRate // sample rate
308
321
  );
309
322
 
310
- const mlsChannelZeroBuffer = calibrationSignalBuffer.getChannelData(0); // get data
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 < calibrationSignal.length; i += 1) {
314
- mlsChannelZeroBuffer[i] = calibrationSignal[i];
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 sourceNode = audioCtx.createBufferSource();
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
- #createIIRwMLSGraph = () => {
334
- this.#createImpulseResponseFilterGraph(this.impulseResponses, [this.#mlsBufferView][0]);
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
- this.#mlsOrder,
372
- this.sinkSamplingRate,
373
- this.sourceSamplingRate
401
+ this.#mlsOrder,
402
+ this.sinkSamplingRate,
403
+ this.sourceSamplingRate
374
404
  ).then(mlsGenInterface => {
375
- this.#mlsGenInterface = mlsGenInterface;
376
- this.#mlsBufferView = this.#mlsGenInterface.getMLS();
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.#playCalibrationAudio, // play audio func (required)
386
- this.#createImpulseResponseFilterGraph, // before play func
387
- null, // before record
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
- // await this.playMLSwithIIR(stream, this.invertedImpulseResponse);
481
+ await this.playMLSwithIIR(stream, this.invertedImpulseResponse);
482
+ this.#stopCalibrationAudioConvolved();
438
483
 
439
484
  return this.invertedImpulseResponse;
440
485
  };