speaker-calibration 2.2.10 → 2.2.12
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/dist/example/i18n.js +56 -54
- package/dist/example/listener.html +5 -1
- package/dist/example/speaker.html +6 -0
- package/dist/example/speakerUI.js +1 -0
- package/dist/main.js +4 -4
- package/package.json +1 -1
- package/src/peer-connection/listener.js +34 -22
- package/src/peer-connection/speaker.js +9 -1
- package/src/tasks/combination/combination.js +48 -36
package/package.json
CHANGED
|
@@ -21,6 +21,10 @@ class Listener extends AudioPeer {
|
|
|
21
21
|
this.receiverPeerId = null;
|
|
22
22
|
|
|
23
23
|
const urlParameters = this.parseURLSearchParams();
|
|
24
|
+
this.calibrateSoundHz =
|
|
25
|
+
urlParameters.calibrateSoundHz !== null && urlParameters.calibrateSoundHz !== undefined
|
|
26
|
+
? urlParameters.calibrateSoundHz
|
|
27
|
+
: 48000;
|
|
24
28
|
this.speakerPeerId = urlParameters.speakerPeerId;
|
|
25
29
|
|
|
26
30
|
this.peer.on('open', this.onPeerOpen);
|
|
@@ -99,7 +103,7 @@ class Listener extends AudioPeer {
|
|
|
99
103
|
this.displayUpdate('Created connection');
|
|
100
104
|
this.conn.on('open', async () => {
|
|
101
105
|
this.displayUpdate('Listener - conn open');
|
|
102
|
-
|
|
106
|
+
await this.getDeviceInfo();
|
|
103
107
|
// this.sendSamplingRate();
|
|
104
108
|
await this.openAudioStream();
|
|
105
109
|
});
|
|
@@ -134,27 +138,35 @@ class Listener extends AudioPeer {
|
|
|
134
138
|
});
|
|
135
139
|
};
|
|
136
140
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
141
|
+
getDeviceInfo = async () => {
|
|
142
|
+
try {
|
|
143
|
+
const deviceInfo = {};
|
|
144
|
+
fod.complete(function (data) {
|
|
145
|
+
deviceInfo['IsMobile'] = data.device['ismobile'];
|
|
146
|
+
deviceInfo['HardwareName'] = data.device['hardwarename'];
|
|
147
|
+
deviceInfo['HardwareFamily'] = data.device['hardwarefamily'];
|
|
148
|
+
deviceInfo['HardwareModel'] = data.device['hardwaremodel'];
|
|
149
|
+
deviceInfo['OEM'] = data.device['oem'];
|
|
150
|
+
deviceInfo['HardwareModelVariants'] = data.device['hardwaremodelvariants'];
|
|
151
|
+
deviceInfo['DeviceId'] = data.device['deviceid'];
|
|
152
|
+
deviceInfo['PlatformName'] = data.device['platformname'];
|
|
153
|
+
deviceInfo['PlatformVersion'] = data.device['platformversion'];
|
|
154
|
+
deviceInfo['DeviceType'] = data.device['devicetype'];
|
|
155
|
+
});
|
|
156
|
+
this.conn.send({
|
|
157
|
+
name: 'deviceInfo',
|
|
158
|
+
payload: deviceInfo,
|
|
159
|
+
});
|
|
160
|
+
return deviceInfo;
|
|
161
|
+
} catch (error) {
|
|
162
|
+
console.error('Error fetching or executing script:', error.message);
|
|
163
|
+
return null;
|
|
144
164
|
}
|
|
145
|
-
|
|
146
|
-
this.conn.send({
|
|
147
|
-
name: 'deviceType',
|
|
148
|
-
payload: deviceType,
|
|
149
|
-
});
|
|
150
|
-
this.conn.send({
|
|
151
|
-
name: 'deviceName',
|
|
152
|
-
payload: deviceName,
|
|
153
|
-
});
|
|
154
165
|
};
|
|
155
166
|
|
|
156
167
|
applyHQTrackConstraints = async stream => {
|
|
157
168
|
// Contraint the incoming audio to the sampling rate we want
|
|
169
|
+
|
|
158
170
|
const track = stream.getAudioTracks()[0];
|
|
159
171
|
const capabilities = track.getCapabilities();
|
|
160
172
|
|
|
@@ -169,7 +181,7 @@ class Listener extends AudioPeer {
|
|
|
169
181
|
}
|
|
170
182
|
|
|
171
183
|
if (capabilities.sampleRate) {
|
|
172
|
-
constraints.sampleRate =
|
|
184
|
+
constraints.sampleRate = this.calibrateSoundHz;
|
|
173
185
|
}
|
|
174
186
|
|
|
175
187
|
if (capabilities.sampleSize) {
|
|
@@ -211,7 +223,7 @@ class Listener extends AudioPeer {
|
|
|
211
223
|
// ? {echoCancellation: {exact: false}}
|
|
212
224
|
// : {}),
|
|
213
225
|
...(availableConstraints.sampleRate && availableConstraints.sampleRate == true
|
|
214
|
-
? {sampleRate: {ideal:
|
|
226
|
+
? {sampleRate: {ideal: this.calibrateSoundHz}}
|
|
215
227
|
: {}),
|
|
216
228
|
...(availableConstraints.sampleSize && availableConstraints.sampleSize == true
|
|
217
229
|
? {sampleSize: {ideal: 24}}
|
|
@@ -219,9 +231,9 @@ class Listener extends AudioPeer {
|
|
|
219
231
|
...(availableConstraints.channelCount && availableConstraints.channelCount == true
|
|
220
232
|
? {channelCount: {exact: 1}}
|
|
221
233
|
: {}),
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
234
|
+
echoCancellation: false,
|
|
235
|
+
noiseSuppression: false,
|
|
236
|
+
autoGainControl: false,
|
|
225
237
|
};
|
|
226
238
|
|
|
227
239
|
console.log(contraints);
|
|
@@ -32,6 +32,7 @@ class Speaker extends AudioPeer {
|
|
|
32
32
|
this.result = null;
|
|
33
33
|
this.debug = params?.debug ?? false;
|
|
34
34
|
this.isSmartPhone = params?.isSmartPhone ?? false;
|
|
35
|
+
this.calibrateSoundHz = params?.calibrateSoundHz ?? 48000;
|
|
35
36
|
this.instructionDisplayId = params?.instructionDisplayId ?? '';
|
|
36
37
|
this.titleDisplayId = params?.titleDisplayId ?? '';
|
|
37
38
|
this.timeToCalibrate = params?.timeToCalibrate ?? 10;
|
|
@@ -99,7 +100,11 @@ class Speaker extends AudioPeer {
|
|
|
99
100
|
params.calibrateSoundBurstRepeats,
|
|
100
101
|
params.calibrateSoundBurstSec,
|
|
101
102
|
params.calibrateSoundBurstsWarmup,
|
|
102
|
-
params.calibrateSoundHz
|
|
103
|
+
params.calibrateSoundHz,
|
|
104
|
+
params.micManufacturer,
|
|
105
|
+
params.micSerialNumber,
|
|
106
|
+
params.micModelNumber,
|
|
107
|
+
params.micModelName
|
|
103
108
|
);
|
|
104
109
|
speaker.#removeUIElems();
|
|
105
110
|
resolve(speaker.result);
|
|
@@ -173,6 +178,7 @@ class Speaker extends AudioPeer {
|
|
|
173
178
|
const queryStringParameters = {
|
|
174
179
|
speakerPeerId: this.peer.id,
|
|
175
180
|
isSmartPhone: this.isSmartPhone,
|
|
181
|
+
calibrateSoundHz: this.calibrateSoundHz,
|
|
176
182
|
};
|
|
177
183
|
const queryString = this.queryStringFromObject(queryStringParameters);
|
|
178
184
|
const uri = this.siteUrl + queryString;
|
|
@@ -379,6 +385,8 @@ class Speaker extends AudioPeer {
|
|
|
379
385
|
case 'deviceName':
|
|
380
386
|
this.ac.setDeviceName(data.payload);
|
|
381
387
|
break;
|
|
388
|
+
case 'deviceInfo':
|
|
389
|
+
this.ac.setDeviceInfo(data.payload);
|
|
382
390
|
case UnsupportedDeviceError.name:
|
|
383
391
|
case MissingSpeakerIdError.name:
|
|
384
392
|
throw data.payload;
|
|
@@ -141,6 +141,8 @@ class Combination extends AudioCalibrator {
|
|
|
141
141
|
|
|
142
142
|
deviceName = null;
|
|
143
143
|
|
|
144
|
+
deviceInfo = null;
|
|
145
|
+
|
|
144
146
|
desired_time_per_mls = 0;
|
|
145
147
|
|
|
146
148
|
num_mls_to_skip = 0;
|
|
@@ -153,6 +155,8 @@ class Combination extends AudioCalibrator {
|
|
|
153
155
|
|
|
154
156
|
sourceNode;
|
|
155
157
|
|
|
158
|
+
autocorrelations = [];
|
|
159
|
+
|
|
156
160
|
/**generate string template that gets reevaluated as variable increases */
|
|
157
161
|
generateTemplate = () => {
|
|
158
162
|
if (this.percent_complete > 100) {
|
|
@@ -176,6 +180,10 @@ class Combination extends AudioCalibrator {
|
|
|
176
180
|
this.deviceName = deviceName;
|
|
177
181
|
};
|
|
178
182
|
|
|
183
|
+
setDeviceInfo = deviceInfo => {
|
|
184
|
+
this.deviceInfo = deviceInfo;
|
|
185
|
+
};
|
|
186
|
+
|
|
179
187
|
/** .
|
|
180
188
|
* .
|
|
181
189
|
* .
|
|
@@ -319,7 +327,8 @@ class Combination extends AudioCalibrator {
|
|
|
319
327
|
this.emit('update', {
|
|
320
328
|
message: this.status,
|
|
321
329
|
});
|
|
322
|
-
|
|
330
|
+
this.autocorrelations.push(res['autocorrelation']);
|
|
331
|
+
return res['ir'];
|
|
323
332
|
}
|
|
324
333
|
})
|
|
325
334
|
.catch(err => {
|
|
@@ -347,16 +356,16 @@ class Combination extends AudioCalibrator {
|
|
|
347
356
|
message: this.status,
|
|
348
357
|
});
|
|
349
358
|
let time_to_wait = 0;
|
|
350
|
-
if (this.mode === 'unfiltered'){
|
|
359
|
+
if (this.mode === 'unfiltered') {
|
|
351
360
|
time_to_wait = (this.#mls.length / this.sourceSamplingRate) * this.numMLSPerCapture;
|
|
352
|
-
}else if (this.mode === 'filtered'){
|
|
361
|
+
} else if (this.mode === 'filtered') {
|
|
353
362
|
time_to_wait =
|
|
354
|
-
|
|
355
|
-
}else{
|
|
356
|
-
throw new Error(
|
|
363
|
+
(this.#currentConvolution.length / this.sourceSamplingRate) * this.numMLSPerCapture;
|
|
364
|
+
} else {
|
|
365
|
+
throw new Error('Mode broke in awaitDesiredMLSLength');
|
|
357
366
|
}
|
|
358
|
-
|
|
359
|
-
await sleep(time_to_wait*1.1);
|
|
367
|
+
|
|
368
|
+
await sleep(time_to_wait * 1.1);
|
|
360
369
|
};
|
|
361
370
|
|
|
362
371
|
/** .
|
|
@@ -377,12 +386,12 @@ class Combination extends AudioCalibrator {
|
|
|
377
386
|
});
|
|
378
387
|
let number_of_bursts_to_skip = this.num_mls_to_skip;
|
|
379
388
|
let time_to_sleep = 0;
|
|
380
|
-
if (this.mode === 'unfiltered'){
|
|
389
|
+
if (this.mode === 'unfiltered') {
|
|
381
390
|
time_to_sleep = this.#mls.length / this.sourceSamplingRate;
|
|
382
|
-
}else if (this.mode === 'filtered'){
|
|
391
|
+
} else if (this.mode === 'filtered') {
|
|
383
392
|
time_to_sleep = this.#currentConvolution.length / this.sourceSamplingRate;
|
|
384
|
-
}else{
|
|
385
|
-
throw new Error(
|
|
393
|
+
} else {
|
|
394
|
+
throw new Error('Mode broke in awaitSignalOnset');
|
|
386
395
|
}
|
|
387
396
|
await sleep(time_to_sleep);
|
|
388
397
|
};
|
|
@@ -455,10 +464,10 @@ class Combination extends AudioCalibrator {
|
|
|
455
464
|
#createCalibrationNodeFromBuffer = dataBuffer => {
|
|
456
465
|
console.log('databuffer');
|
|
457
466
|
console.log(dataBuffer);
|
|
458
|
-
if (!this.sourceAudioContext){
|
|
467
|
+
if (!this.sourceAudioContext) {
|
|
459
468
|
this.makeNewSourceAudioContext();
|
|
460
469
|
}
|
|
461
|
-
|
|
470
|
+
|
|
462
471
|
const buffer = this.sourceAudioContext.createBuffer(
|
|
463
472
|
1, // number of channels
|
|
464
473
|
dataBuffer.length,
|
|
@@ -474,10 +483,8 @@ class Combination extends AudioCalibrator {
|
|
|
474
483
|
} catch (error) {
|
|
475
484
|
console.error(error);
|
|
476
485
|
}
|
|
477
|
-
|
|
486
|
+
|
|
478
487
|
this.sourceNode = this.sourceAudioContext.createBufferSource();
|
|
479
|
-
|
|
480
|
-
|
|
481
488
|
|
|
482
489
|
this.sourceNode.buffer = buffer;
|
|
483
490
|
this.sourceNode.loop = true;
|
|
@@ -568,23 +575,23 @@ class Combination extends AudioCalibrator {
|
|
|
568
575
|
* @example
|
|
569
576
|
*/
|
|
570
577
|
#playCalibrationAudio = () => {
|
|
571
|
-
if (this.mode === 'unfiltered'){
|
|
578
|
+
if (this.mode === 'unfiltered') {
|
|
572
579
|
this.calibrationNodes[0].start(0);
|
|
573
580
|
this.#mls = this.calibrationNodes[0].buffer.getChannelData(0);
|
|
574
581
|
console.log('play calibration audio ' + this.stepNum);
|
|
575
582
|
this.status =
|
|
576
|
-
|
|
577
|
-
|
|
583
|
+
`All Hz Calibration: playing the calibration tone...`.toString() +
|
|
584
|
+
this.generateTemplate().toString();
|
|
578
585
|
this.emit('update', {message: this.status});
|
|
579
|
-
} else if (this.mode === 'filtered'){
|
|
586
|
+
} else if (this.mode === 'filtered') {
|
|
580
587
|
this.calibrationNodes[0].start(0);
|
|
581
588
|
console.log('play convolved audio ' + this.stepNum);
|
|
582
589
|
this.status =
|
|
583
590
|
`All Hz Calibration: playing the convolved calibration tone...`.toString() +
|
|
584
|
-
|
|
591
|
+
this.generateTemplate().toString();
|
|
585
592
|
this.emit('update', {message: this.status});
|
|
586
|
-
} else{
|
|
587
|
-
throw new Error(
|
|
593
|
+
} else {
|
|
594
|
+
throw new Error('Mode is incorrect');
|
|
588
595
|
}
|
|
589
596
|
this.stepNum += 1;
|
|
590
597
|
console.log('sink sampling rate');
|
|
@@ -593,7 +600,6 @@ class Combination extends AudioCalibrator {
|
|
|
593
600
|
console.log(this.sourceSamplingRate);
|
|
594
601
|
};
|
|
595
602
|
|
|
596
|
-
|
|
597
603
|
/** .
|
|
598
604
|
* .
|
|
599
605
|
* .
|
|
@@ -602,7 +608,6 @@ class Combination extends AudioCalibrator {
|
|
|
602
608
|
* @example
|
|
603
609
|
*/
|
|
604
610
|
#stopCalibrationAudio = () => {
|
|
605
|
-
|
|
606
611
|
this.calibrationNodes[0].stop(0);
|
|
607
612
|
this.calibrationNodes = [];
|
|
608
613
|
this.sourceNode.disconnect();
|
|
@@ -628,7 +633,7 @@ class Combination extends AudioCalibrator {
|
|
|
628
633
|
this.#awaitDesiredMLSLength, // during record
|
|
629
634
|
this.#afterMLSwIIRRecord, // after record
|
|
630
635
|
this.mode
|
|
631
|
-
)
|
|
636
|
+
);
|
|
632
637
|
};
|
|
633
638
|
|
|
634
639
|
/**
|
|
@@ -641,11 +646,9 @@ class Combination extends AudioCalibrator {
|
|
|
641
646
|
* @example
|
|
642
647
|
*/
|
|
643
648
|
startCalibrationImpulseResponse = async stream => {
|
|
644
|
-
|
|
645
649
|
let desired_time = this.desired_time_per_mls;
|
|
646
650
|
|
|
647
|
-
console.log(
|
|
648
|
-
|
|
651
|
+
console.log('MLS sequence should be of length: ' + this.sourceSamplingRate * desired_time);
|
|
649
652
|
|
|
650
653
|
length = this.sourceSamplingRate * desired_time;
|
|
651
654
|
//get mls here
|
|
@@ -669,8 +672,7 @@ class Combination extends AudioCalibrator {
|
|
|
669
672
|
this.#afterMLSRecord, // after record
|
|
670
673
|
this.mode
|
|
671
674
|
),
|
|
672
|
-
|
|
673
|
-
this.#stopCalibrationAudio();
|
|
675
|
+
this.#stopCalibrationAudio();
|
|
674
676
|
|
|
675
677
|
// at this stage we've captured all the required signals,
|
|
676
678
|
// and have received IRs for each one
|
|
@@ -684,7 +686,7 @@ class Combination extends AudioCalibrator {
|
|
|
684
686
|
if (this._calibrateSoundCheck != 'none') {
|
|
685
687
|
if (this._calibrateSoundCheck != 'system') {
|
|
686
688
|
this.#currentConvolution = this.componentConvolution;
|
|
687
|
-
}else{
|
|
689
|
+
} else {
|
|
688
690
|
this.#currentConvolution = this.systemConvolution;
|
|
689
691
|
}
|
|
690
692
|
await this.playMLSwithIIR(stream, this.invertedImpulseResponse);
|
|
@@ -773,6 +775,9 @@ class Combination extends AudioCalibrator {
|
|
|
773
775
|
saveToCSV(this.systemConvolution, 'python_system_convolution_mls_iir.csv');
|
|
774
776
|
saveToCSV(this.componentInvertedImpulseResponse, 'componentIIR.csv');
|
|
775
777
|
saveToCSV(this.systemInvertedImpulseResponse, 'systemIIR.csv');
|
|
778
|
+
for (let i = 0; i < this.autocorrelations.length; i++){
|
|
779
|
+
saveToCSV(this.autocorrelations[i],`autocorrelation_${i}`);
|
|
780
|
+
}
|
|
776
781
|
const computedIRagain = await Promise.all(this.impulseResponses).then(res => {
|
|
777
782
|
for (let i = 0; i < res.length; i++) {
|
|
778
783
|
if (res[i] != undefined) {
|
|
@@ -798,6 +803,9 @@ class Combination extends AudioCalibrator {
|
|
|
798
803
|
saveToCSV(this.systemConvolution, 'python_system_convolution_mls_iir.csv');
|
|
799
804
|
saveToCSV(this.componentInvertedImpulseResponse, 'componentIIR.csv');
|
|
800
805
|
saveToCSV(this.systemInvertedImpulseResponse, 'systemIIR.csv');
|
|
806
|
+
for (let i = 0; i < this.autocorrelations.length; i++){
|
|
807
|
+
saveToCSV(this.autocorrelations[i],`autocorrelation_${i}`);
|
|
808
|
+
}
|
|
801
809
|
const computedIRagain = await Promise.all(this.impulseResponses).then(res => {
|
|
802
810
|
for (let i = 0; i < res.length; i++) {
|
|
803
811
|
if (res[i] != undefined) {
|
|
@@ -841,8 +849,8 @@ class Combination extends AudioCalibrator {
|
|
|
841
849
|
j += 1 / this.sourceSamplingRate;
|
|
842
850
|
}
|
|
843
851
|
return curve;
|
|
844
|
-
|
|
845
|
-
|
|
852
|
+
};
|
|
853
|
+
|
|
846
854
|
#getTruncatedSignal = (left = 3.5, right = 4.5) => {
|
|
847
855
|
const start = Math.floor(left * this.sourceSamplingRate);
|
|
848
856
|
const end = Math.floor(right * this.sourceSamplingRate);
|
|
@@ -1141,7 +1149,11 @@ class Combination extends AudioCalibrator {
|
|
|
1141
1149
|
_calibrateSoundBurstRepeats = 4,
|
|
1142
1150
|
_calibrateSoundBurstSec = 1,
|
|
1143
1151
|
_calibrateSoundBurstsWarmup = 1,
|
|
1144
|
-
_calibrateSoundHz = 48000
|
|
1152
|
+
_calibrateSoundHz = 48000,
|
|
1153
|
+
micManufacturer = '',
|
|
1154
|
+
micSerialNumber = '',
|
|
1155
|
+
micModelNumber = '',
|
|
1156
|
+
micModelName = ''
|
|
1145
1157
|
) => {
|
|
1146
1158
|
this.numMLSPerCapture = _calibrateSoundBurstRepeats;
|
|
1147
1159
|
this.desired_time_per_mls = _calibrateSoundBurstSec;
|