speaker-calibration 2.1.7 → 2.1.9

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.7",
3
+ "version": "2.1.9",
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
@@ -20,7 +19,7 @@ class PythonServerAPI {
20
19
  * @returns
21
20
  * @example
22
21
  */
23
- getImpulseResponse = async ({payload, sampleRate, P}) => {
22
+ getImpulseResponse = async ({mls, payload, sampleRate, P}) => {
24
23
  const task = 'impulse-response';
25
24
  let res = null;
26
25
 
@@ -30,6 +29,7 @@ class PythonServerAPI {
30
29
  task,
31
30
  payload,
32
31
  'sample-rate': sampleRate,
32
+ mls,
33
33
  P,
34
34
  });
35
35
 
@@ -48,11 +48,10 @@ class PythonServerAPI {
48
48
  .catch(error => {
49
49
  throw error;
50
50
  });
51
-
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,11 +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 = 3}) {
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
+ this.#mls = [];
25
+ this.#lowHz = lowHz;
26
+ this.#highHz = highHz;
24
27
  }
25
28
 
26
29
  /** @private */
@@ -41,15 +44,30 @@ class ImpulseResponse extends AudioCalibrator {
41
44
  /** @private */
42
45
  #mlsOrder;
43
46
 
47
+ /** @private */
48
+ #lowHz;
49
+
50
+ /** @private */
51
+ #highHz;
52
+
53
+ /** @private */
54
+ #mls;
55
+
44
56
  /** @private */
45
57
  #P;
46
58
 
59
+ /** @private */
60
+ #audioContext;
61
+
47
62
  /** @private */
48
63
  TAPER_SECS = 5;
49
64
 
50
65
  /** @private */
51
66
  offsetGainNode;
52
67
 
68
+ /** @private */
69
+ convolution;
70
+
53
71
  /** .
54
72
  * .
55
73
  * .
@@ -60,14 +78,22 @@ class ImpulseResponse extends AudioCalibrator {
60
78
  */
61
79
  sendImpulseResponsesToServerForProcessing = async () => {
62
80
  const computedIRs = await Promise.all(this.impulseResponses);
81
+ const mls = this.#mls;
82
+ const lowHz = this.#lowHz;
83
+ const highHz = this.#highHz;
63
84
  this.emit('update', {message: `computing the IIR...`});
64
85
  return this.pyServerAPI
65
86
  .getInverseImpulseResponse({
66
87
  payload: computedIRs.slice(0, this.numCaptures),
88
+ mls,
89
+ lowHz,
90
+ highHz
67
91
  })
68
92
  .then(res => {
93
+ console.log(res);
69
94
  this.emit('update', {message: `done computing the IIR...`});
70
- this.invertedImpulseResponse = res;
95
+ this.invertedImpulseResponse = res["iir"];
96
+ this.convolution = res["convolution"];
71
97
  })
72
98
  .catch(err => {
73
99
  // this.emit('InvertedImpulseResponse', {res: false});
@@ -86,6 +112,7 @@ class ImpulseResponse extends AudioCalibrator {
86
112
  sendRecordingToServerForProcessing = signalCsv => {
87
113
  const allSignals = this.getAllRecordedSignals();
88
114
  const numSignals = allSignals.length;
115
+ const mls = this.#mls;
89
116
  const payload =
90
117
  signalCsv && signalCsv.length > 0 ? csvToArray(signalCsv) : allSignals[numSignals - 1];
91
118
 
@@ -95,11 +122,13 @@ class ImpulseResponse extends AudioCalibrator {
95
122
  .getImpulseResponse({
96
123
  sampleRate: this.sourceSamplingRate || 96000,
97
124
  payload,
125
+ mls,
98
126
  P: this.#P,
99
127
  })
100
128
  .then(res => {
101
129
  if (this.numSuccessfulCaptured < this.numCaptures) {
102
130
  this.numSuccessfulCaptured += 1;
131
+ console.log("num succ capt: " + this.numSuccessfulCaptured);
103
132
  this.emit('update', {
104
133
  message: `${this.numSuccessfulCaptured}/${this.numCaptures} IRs computed...`,
105
134
  });
@@ -156,9 +185,14 @@ class ImpulseResponse extends AudioCalibrator {
156
185
 
157
186
  #afterMLSwIIRRecord = () => {
158
187
  if (this.#download) {
159
- 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
+ });
160
195
  }
161
- this.#stopCalibrationAudio();
162
196
  };
163
197
 
164
198
  /** .
@@ -234,12 +268,13 @@ class ImpulseResponse extends AudioCalibrator {
234
268
  // fill the buffer with our data
235
269
  try {
236
270
  for (let i = 0; i < dataBuffer.length; i += 1) {
237
- data[i] = dataBuffer[i];
271
+ data[i] = dataBuffer[i]*.1;
238
272
  }
239
273
  } catch (error) {
240
274
  console.error(error);
241
275
  }
242
-
276
+ console.log("mls second, same?");
277
+ console.log(data);
243
278
  const onsetGainNode = audioContext.createGain();
244
279
  this.offsetGainNode = audioContext.createGain();
245
280
  const source = audioContext.createBufferSource();
@@ -252,7 +287,6 @@ class ImpulseResponse extends AudioCalibrator {
252
287
 
253
288
  const onsetCurve = ImpulseResponse.createSCurveBuffer(this.sourceSamplingRate, Math.PI / 2);
254
289
  onsetGainNode.gain.setValueCurveAtTime(onsetCurve, 0, this.TAPER_SECS);
255
-
256
290
  this.addCalibrationNode(source);
257
291
  };
258
292
 
@@ -264,70 +298,47 @@ class ImpulseResponse extends AudioCalibrator {
264
298
  */
265
299
  #setCalibrationNodesFromBuffer = (dataBufferArray = [this.#mlsBufferView]) => {
266
300
  if (dataBufferArray.length === 1) {
301
+ console.log('data buffer aray');
302
+ console.log(dataBufferArray);
267
303
  this.#createCalibrationNodeFromBuffer(dataBufferArray[0]);
268
304
  } else {
269
305
  throw new Error('The length of the data buffer array must be 1');
270
306
  }
271
- };
272
-
273
- #createImpulseResponseFilterGraph = (calibrationSignal, iir) => {
274
- const audioCtx = this.makeNewSourceAudioContext();
275
-
276
- // -------------------------------------------------------- IIR
277
- const iirBuffer = audioCtx.createBuffer(
278
- 1,
279
- // TODO: quality check this
280
- iir.length - 1,
281
- audioCtx.sampleRate
282
- );
283
307
 
284
- // Fill the buffer with the inverted impulse response
285
- const iirChannelZeroBuffer = iirBuffer.getChannelData(0);
286
- for (let i = 0; i < iirBuffer.length; i += 1) {
287
- // audio needs to be in [-1.0; 1.0]
288
- iirChannelZeroBuffer[i] = iir[i];
289
- }
290
-
291
- const convolverNode = audioCtx.createConvolver();
292
-
293
- convolverNode.normalize = false;
294
- convolverNode.channelCount = 1;
295
- convolverNode.buffer = iirBuffer;
308
+
309
+ };
296
310
 
297
- // ------------------------------------------------------ MLS
298
- 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(
299
318
  1, // number of channels
300
- calibrationSignal.length,
319
+ this.convolution.length,
301
320
  audioCtx.sampleRate // sample rate
302
321
  );
303
322
 
304
- const mlsChannelZeroBuffer = calibrationSignalBuffer.getChannelData(0); // get data
323
+ const data = buffer.getChannelData(0); // get data
305
324
  // fill the buffer with our data
306
325
  try {
307
- for (let i = 0; i < calibrationSignal.length; i += 1) {
308
- mlsChannelZeroBuffer[i] = calibrationSignal[i];
326
+ for (let i = 0; i < this.convolution.length; i += 1) {
327
+ data[i] = this.convolution[i];
309
328
  }
310
329
  } catch (error) {
311
330
  console.error(error);
312
331
  }
313
332
 
314
- const sourceNode = audioCtx.createBufferSource();
315
-
316
- sourceNode.buffer = calibrationSignalBuffer;
317
- sourceNode.loop = true;
318
- sourceNode.connect(convolverNode);
319
-
320
- convolverNode.connect(audioCtx.destination);
321
-
322
- console.log({convolverNode, sourceNode});
323
-
324
- this.addCalibrationNode(sourceNode);
325
- };
333
+ const source = audioCtx.createBufferSource();
326
334
 
327
- #createIIRwMLSGraph = () => {
328
- this.#createImpulseResponseFilterGraph(this.impulseResponses, [this.#mlsBufferView][0]);
329
- };
335
+ source.buffer = buffer;
336
+ source.loop = true;
337
+ source.connect(audioCtx.destination);
330
338
 
339
+ this.addCalibrationNodeConvolved(source);
340
+ }
341
+
331
342
  /**
332
343
  * Creates an audio context and plays it for a few seconds.
333
344
  *
@@ -337,8 +348,16 @@ class ImpulseResponse extends AudioCalibrator {
337
348
  */
338
349
  #playCalibrationAudio = () => {
339
350
  this.calibrationNodes[0].start(0);
351
+ this.#mls = this.calibrationNodes[0].buffer.getChannelData(0);
352
+ console.log(this.#mls);
340
353
  this.emit('update', {message: 'playing the calibration tone...'});
341
- };
354
+ };
355
+
356
+
357
+ #playCalibrationAudioConvolved = () => {
358
+ this.calibrationNodesConvolved[0].start(0);
359
+ this.emit('update',{message: 'playing the convolved calibration tone...'})
360
+ }
342
361
 
343
362
  /** .
344
363
  * .
@@ -354,34 +373,53 @@ class ImpulseResponse extends AudioCalibrator {
354
373
  );
355
374
 
356
375
  this.offsetGainNode.gain.setTargetAtTime(0, this.sourceAudioContext.currentTime, 0.5);
376
+ this.calibrationNodes[0].stop(0);
377
+ this.sourceAudioContext.close();
357
378
  this.emit('update', {message: 'stopping the calibration tone...'});
358
379
  };
359
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
+
360
395
  playMLSwithIIR = async (stream, iir) => {
396
+ console.log('play mls with iir');
361
397
  this.invertedImpulseResponse = iir;
362
398
  // initialize the MLSGenInterface object with it's factory method
399
+
363
400
  await MlsGenInterface.factory(
364
- this.#mlsOrder,
365
- this.sinkSamplingRate,
366
- this.sourceSamplingRate
401
+ this.#mlsOrder,
402
+ this.sinkSamplingRate,
403
+ this.sourceSamplingRate
367
404
  ).then(mlsGenInterface => {
368
- this.#mlsGenInterface = mlsGenInterface;
369
- this.#mlsBufferView = this.#mlsGenInterface.getMLS();
405
+ this.#mlsGenInterface = mlsGenInterface;
406
+ this.#mlsBufferView = this.#mlsGenInterface.getMLS();
370
407
  });
371
408
 
409
+ console.log('after mls factory'); //works up to here.
410
+ console.log(this.#mls);
372
411
  // after intializating, start the calibration steps with garbage collection
373
412
  await this.#mlsGenInterface.withGarbageCollection([
374
- [
375
- this.calibrationSteps,
376
- [
413
+ () =>
414
+ this.calibrationSteps(
377
415
  stream,
378
- this.#playCalibrationAudio, // play audio func (required)
379
- this.#createImpulseResponseFilterGraph, // before play func
380
- 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,
381
420
  this.#awaitDesiredMLSLength, // during record
382
421
  this.#afterMLSwIIRRecord, // after record
383
- ],
384
- ],
422
+ ),
385
423
  ]);
386
424
  };
387
425
 
@@ -426,8 +464,22 @@ class ImpulseResponse extends AudioCalibrator {
426
464
  // so let's send all the IRs to the server to be converted to a single IIR
427
465
  await this.sendImpulseResponsesToServerForProcessing();
428
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;
429
480
  // debugging function, use to test the result of the IIR
430
- // await this.playMLSwithIIR(stream, this.invertedImpulseResponse);
481
+ await this.playMLSwithIIR(stream, this.invertedImpulseResponse);
482
+ this.#stopCalibrationAudioConvolved();
431
483
 
432
484
  return this.invertedImpulseResponse;
433
485
  };