speaker-calibration 2.2.218 → 2.2.220
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 +1704 -1495
- package/dist/example/styles.css +75 -22
- package/dist/listener.js +954 -2423
- package/dist/main.js +4 -15
- package/package.json +1 -1
- package/src/listener-app/listener.js +282 -91
- package/src/main.js +1 -2
- package/src/peer-connection/audioPeer.js +4 -39
- package/src/peer-connection/listener.js +123 -25
- package/src/peer-connection/speaker.js +98 -55
- package/src/tasks/combination/combination.js +7 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import AudioPeer from './audioPeer';
|
|
2
2
|
import {UnsupportedDeviceError, MissingSpeakerIdError} from './peerErrors';
|
|
3
3
|
import axios from 'axios';
|
|
4
|
+
import Peer from 'peerjs';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* @class Handles the listener's side of the connection. Responsible for getting access to user's microphone,
|
|
@@ -16,6 +17,7 @@ class Listener extends AudioPeer {
|
|
|
16
17
|
*/
|
|
17
18
|
constructor(params) {
|
|
18
19
|
super(params);
|
|
20
|
+
console.log('Listener constructor', this.peer);
|
|
19
21
|
this.microphoneFromAPI = params.microphoneFromAPI ? params.microphoneFromAPI : '';
|
|
20
22
|
this.microphoneDeviceId = params.microphoneDeviceId ? params.microphoneDeviceId : '';
|
|
21
23
|
// this.deviceInfoFromUser = params.deviceInfoFromUser
|
|
@@ -32,16 +34,92 @@ class Listener extends AudioPeer {
|
|
|
32
34
|
// previous calibrateSoundSamplingDesiredBits
|
|
33
35
|
urlParameters.bits !== null && urlParameters.bits !== undefined ? urlParameters.bits : 24;
|
|
34
36
|
this.speakerPeerId = urlParameters.speakerPeerId;
|
|
35
|
-
|
|
36
|
-
this.
|
|
37
|
-
this.peer.on('connection', this.onPeerConnection);
|
|
38
|
-
this.peer.on('disconnected', this.onPeerDisconnected);
|
|
39
|
-
this.peer.on('close', this.onPeerClose);
|
|
40
|
-
this.peer.on('error', this.onPeerError);
|
|
37
|
+
this.lastPeerId = this.speakerPeerId;
|
|
38
|
+
this.connOpen = false;
|
|
41
39
|
}
|
|
40
|
+
generateTimeBasedPeerID = async () => {
|
|
41
|
+
const now = new Date().getTime();
|
|
42
|
+
const randomBuffer = new Uint8Array(10);
|
|
43
|
+
crypto.getRandomValues(randomBuffer);
|
|
44
|
+
const randomPart = Array.from(randomBuffer)
|
|
45
|
+
.map(b => b.toString(36))
|
|
46
|
+
.join('');
|
|
47
|
+
const toHash = `${now}-${randomPart}`;
|
|
48
|
+
const encoder = new TextEncoder();
|
|
49
|
+
const data = encoder.encode(toHash);
|
|
50
|
+
const hash = await crypto.subtle.digest('SHA-256', data);
|
|
51
|
+
const hashArray = Array.from(new Uint8Array(hash));
|
|
52
|
+
const hashString = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
|
|
53
|
+
const shortHash = hashString.substring(0, 12);
|
|
54
|
+
return this.encodeBase62(parseInt(shortHash, 16));
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
encodeBase62 = num => {
|
|
58
|
+
const base = 26;
|
|
59
|
+
const characters = 'abcdefghijklmnopqrstuvwxyz';
|
|
60
|
+
let result = '';
|
|
61
|
+
while (num > 0) {
|
|
62
|
+
result = characters[num % base] + result;
|
|
63
|
+
num = Math.floor(num / base);
|
|
64
|
+
}
|
|
65
|
+
return result || 'a';
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
initializePeer = async () => {
|
|
69
|
+
console.log('Initializing PeerJS connection...');
|
|
70
|
+
const id = await this.generateTimeBasedPeerID();
|
|
71
|
+
console.log('Generated Peer ID:', id);
|
|
72
|
+
|
|
73
|
+
try {
|
|
74
|
+
this.peer = new Peer(id, {
|
|
75
|
+
debug: 2,
|
|
76
|
+
host: 'easyeyes-peer-server.herokuapp.com',
|
|
77
|
+
port: 443,
|
|
78
|
+
secure: true,
|
|
79
|
+
config: {
|
|
80
|
+
iceServers: [
|
|
81
|
+
{
|
|
82
|
+
urls: 'stun:stun.relay.metered.ca:80',
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
urls: 'turn:global.relay.metered.ca:80',
|
|
86
|
+
username: 'de884cfc34189cdf1a5dd616',
|
|
87
|
+
credential: 'IcOpouU9/TYBmpHU',
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
urls: 'turn:global.relay.metered.ca:80?transport=tcp',
|
|
91
|
+
username: 'de884cfc34189cdf1a5dd616',
|
|
92
|
+
credential: 'IcOpouU9/TYBmpHU',
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
urls: 'turn:global.relay.metered.ca:443',
|
|
96
|
+
username: 'de884cfc34189cdf1a5dd616',
|
|
97
|
+
credential: 'IcOpouU9/TYBmpHU',
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
urls: 'turns:global.relay.metered.ca:443?transport=tcp',
|
|
101
|
+
username: 'de884cfc34189cdf1a5dd616',
|
|
102
|
+
credential: 'IcOpouU9/TYBmpHU',
|
|
103
|
+
},
|
|
104
|
+
],
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
this.peer.on('open', this.onPeerOpen);
|
|
109
|
+
this.peer.on('connection', this.onPeerConnection);
|
|
110
|
+
this.peer.on('disconnected', this.onPeerDisconnected);
|
|
111
|
+
this.peer.on('close', this.onPeerClose);
|
|
112
|
+
this.peer.on('error', this.onPeerError);
|
|
113
|
+
|
|
114
|
+
console.log('Peer object created:', this.peer);
|
|
115
|
+
} catch (error) {
|
|
116
|
+
console.error('Failed to initialize PeerJS:', error);
|
|
117
|
+
}
|
|
118
|
+
};
|
|
42
119
|
|
|
43
120
|
onPeerOpen = id => {
|
|
44
121
|
this.displayUpdate('Listener - onPeerOpen');
|
|
122
|
+
console.log('onPeerOpen: ', id);
|
|
45
123
|
// Workaround for peer.reconnect deleting previous id
|
|
46
124
|
try {
|
|
47
125
|
if (id === null) {
|
|
@@ -53,12 +131,12 @@ class Listener extends AudioPeer {
|
|
|
53
131
|
} catch (error) {
|
|
54
132
|
console.error('Error in onPeerOpen: ', error);
|
|
55
133
|
}
|
|
56
|
-
|
|
57
134
|
this.join();
|
|
58
135
|
};
|
|
59
136
|
|
|
60
137
|
onPeerConnection = connection => {
|
|
61
138
|
this.displayUpdate('Listener - onPeerConnection');
|
|
139
|
+
console.log('onPeerConnection: ', connection);
|
|
62
140
|
// Disallow incoming connections
|
|
63
141
|
connection.on('open', () => {
|
|
64
142
|
connection.send('Sender does not accept incoming connections');
|
|
@@ -71,26 +149,27 @@ class Listener extends AudioPeer {
|
|
|
71
149
|
onConnData = data => {
|
|
72
150
|
this.displayUpdate('Listener - onConnData');
|
|
73
151
|
const hasSpeakerID = Object.prototype.hasOwnProperty.call(data, 'speakerPeerId');
|
|
74
|
-
if (!hasSpeakerID) {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
} else {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
}
|
|
152
|
+
// if (!hasSpeakerID) {
|
|
153
|
+
// this.displayUpdate('Error in parsing data received! Must set "speakerPeerId" property');
|
|
154
|
+
// throw new MissingSpeakerIdError('Must set "speakerPeerId" property');
|
|
155
|
+
// } else {
|
|
156
|
+
// // this.conn.close();
|
|
157
|
+
// this.displayUpdate(this.speakerPeerId);
|
|
158
|
+
// this.speakerPeerId = data.speakerPeerId;
|
|
159
|
+
// const newParams = {
|
|
160
|
+
// speakerPeerId: this.speakerPeerId,
|
|
161
|
+
// };
|
|
162
|
+
// /*
|
|
163
|
+
// FUTURE does this limit usable environments?
|
|
164
|
+
// ie does this work if internet is lost after initial page load?
|
|
165
|
+
// */
|
|
166
|
+
// window.location.search = this.queryStringFromObject(newParams); // Redirect to correctly constructed keypad page
|
|
167
|
+
// }
|
|
90
168
|
};
|
|
91
169
|
|
|
92
170
|
join = async () => {
|
|
93
171
|
this.displayUpdate('Listener - join');
|
|
172
|
+
console.log(' Creating connection to: ', this.speakerPeerId);
|
|
94
173
|
/**
|
|
95
174
|
* Create the connection between the two Peers.
|
|
96
175
|
*
|
|
@@ -112,18 +191,23 @@ class Listener extends AudioPeer {
|
|
|
112
191
|
this.displayUpdate('Created connection');
|
|
113
192
|
this.conn.on('open', async () => {
|
|
114
193
|
this.displayUpdate('Listener - conn open');
|
|
115
|
-
|
|
194
|
+
this.connOpen = true;
|
|
116
195
|
// this.sendSamplingRate();
|
|
117
|
-
await this.openAudioStream();
|
|
118
196
|
});
|
|
119
197
|
|
|
120
198
|
// Handle incoming data (messages only since this is the signal sender)
|
|
121
199
|
this.conn.on('data', this.onConnData);
|
|
122
200
|
this.conn.on('close', () => {
|
|
123
201
|
console.log('Connection closed');
|
|
202
|
+
this.connOpen = false;
|
|
124
203
|
});
|
|
125
204
|
};
|
|
126
205
|
|
|
206
|
+
startCalibration = async () => {
|
|
207
|
+
await this.getDeviceInfo();
|
|
208
|
+
await this.openAudioStream();
|
|
209
|
+
};
|
|
210
|
+
|
|
127
211
|
getMobileOS = () => {
|
|
128
212
|
const ua = navigator.userAgent;
|
|
129
213
|
if (/android/i.test(ua)) {
|
|
@@ -163,6 +247,14 @@ class Listener extends AudioPeer {
|
|
|
163
247
|
});
|
|
164
248
|
};
|
|
165
249
|
|
|
250
|
+
sendPermissionStatus = status => {
|
|
251
|
+
// this.displayUpdate('Listener - sendPermissionStatus');
|
|
252
|
+
this.conn.send({
|
|
253
|
+
name: 'permissionStatus',
|
|
254
|
+
payload: status,
|
|
255
|
+
});
|
|
256
|
+
};
|
|
257
|
+
|
|
166
258
|
getDeviceInfo = async () => {
|
|
167
259
|
const deviceInfo = {};
|
|
168
260
|
try {
|
|
@@ -271,6 +363,12 @@ class Listener extends AudioPeer {
|
|
|
271
363
|
|
|
272
364
|
return contraints;
|
|
273
365
|
};
|
|
366
|
+
setMicrophoneFromAPI = microphoneFromAPI => {
|
|
367
|
+
this.microphoneFromAPI = microphoneFromAPI;
|
|
368
|
+
};
|
|
369
|
+
setMicrophoneDeviceId = microphoneDeviceId => {
|
|
370
|
+
this.microphoneDeviceId = microphoneDeviceId;
|
|
371
|
+
};
|
|
274
372
|
getDeviceIdByLabel = async targetLabel => {
|
|
275
373
|
try {
|
|
276
374
|
//get permission to use audio first. (Returns empty labels on some computers if not done first)
|
|
@@ -45,6 +45,7 @@ class Speaker extends AudioPeer {
|
|
|
45
45
|
this.deviceId = params?.micrpohoneIdFromWebAudioApi ?? '';
|
|
46
46
|
this.buttonsContainer = params?.buttonsContainer ?? document.createElement('div');
|
|
47
47
|
this.phrases = params?.phrases ?? {};
|
|
48
|
+
this.permissionStatus = 'pending';
|
|
48
49
|
|
|
49
50
|
/* Set up callbacks that handle any events related to our peer object. */
|
|
50
51
|
}
|
|
@@ -138,10 +139,19 @@ class Speaker extends AudioPeer {
|
|
|
138
139
|
await speaker.initPeer();
|
|
139
140
|
// wrap the calibration process in a promise so we can await it
|
|
140
141
|
return new Promise((resolve, reject) => {
|
|
142
|
+
// Add a permission check handler
|
|
143
|
+
const permissionCheckInterval = setInterval(() => {
|
|
144
|
+
if (speaker.permissionStatus === 'error' || speaker.permissionStatus === 'denied') {
|
|
145
|
+
clearInterval(permissionCheckInterval);
|
|
146
|
+
speaker.#removeUIElems();
|
|
147
|
+
resolve('permission denied');
|
|
148
|
+
}
|
|
149
|
+
}, 100);
|
|
150
|
+
|
|
141
151
|
// when a call is received
|
|
142
152
|
speaker.peer.on('call', async call => {
|
|
143
|
-
//
|
|
144
|
-
|
|
153
|
+
clearInterval(permissionCheckInterval); // Clear interval when call is received
|
|
154
|
+
// Rest of the existing call handling code...
|
|
145
155
|
call.answer();
|
|
146
156
|
speaker.#removeUIElems();
|
|
147
157
|
speaker.#showSpinner();
|
|
@@ -218,6 +228,7 @@ class Speaker extends AudioPeer {
|
|
|
218
228
|
});
|
|
219
229
|
// if we do not receive a result within the timeout, reject
|
|
220
230
|
setTimeout(() => {
|
|
231
|
+
clearInterval(permissionCheckInterval);
|
|
221
232
|
reject(
|
|
222
233
|
new CalibrationTimedOutError(
|
|
223
234
|
`Calibration failed to produce a result after ${
|
|
@@ -657,6 +668,20 @@ class Speaker extends AudioPeer {
|
|
|
657
668
|
this.ac.setDeviceInfo(data.payload);
|
|
658
669
|
console.log('Received device info from listener: ', data.payload);
|
|
659
670
|
break;
|
|
671
|
+
case 'permissionStatus':
|
|
672
|
+
console.log('Received permission status from listener: ', data.payload);
|
|
673
|
+
if (data.payload.type === 'error') {
|
|
674
|
+
this.permissionStatus = 'error';
|
|
675
|
+
this.ac.setPermissionStatus('error');
|
|
676
|
+
} else if (data.payload.type === 'denied') {
|
|
677
|
+
this.permissionStatus = 'denied';
|
|
678
|
+
this.ac.setPermissionStatus('denied');
|
|
679
|
+
} else if (data.payload.type === 'granted') {
|
|
680
|
+
this.permissionStatus = 'granted';
|
|
681
|
+
this.ac.setPermissionStatus('granted');
|
|
682
|
+
console.log('Permission granted');
|
|
683
|
+
}
|
|
684
|
+
break;
|
|
660
685
|
case UnsupportedDeviceError.name:
|
|
661
686
|
case MissingSpeakerIdError.name:
|
|
662
687
|
throw data.payload;
|
|
@@ -700,59 +725,77 @@ class Speaker extends AudioPeer {
|
|
|
700
725
|
|
|
701
726
|
console.log('This is a repeat');
|
|
702
727
|
// wrap the calibration process in a promise so we can await it
|
|
703
|
-
return new Promise(
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
728
|
+
return new Promise((resolve, reject) => {
|
|
729
|
+
// Add a permission check handler
|
|
730
|
+
const permissionCheckInterval = setInterval(() => {
|
|
731
|
+
if (this.permissionStatus === 'error' || this.permissionStatus === 'denied') {
|
|
732
|
+
clearInterval(permissionCheckInterval);
|
|
733
|
+
this.#removeUIElems();
|
|
734
|
+
resolve('permission denied');
|
|
735
|
+
}
|
|
736
|
+
}, 100);
|
|
737
|
+
|
|
738
|
+
// Start calibration process
|
|
739
|
+
(async () => {
|
|
740
|
+
try {
|
|
741
|
+
const result = await this.ac.startCalibration(
|
|
742
|
+
stream,
|
|
743
|
+
params.gainValues,
|
|
744
|
+
params.ICalib,
|
|
745
|
+
params.knownIR,
|
|
746
|
+
params.microphoneName,
|
|
747
|
+
params.calibrateSoundCheck,
|
|
748
|
+
params.isSmartPhone,
|
|
749
|
+
params.calibrateSoundBurstDb,
|
|
750
|
+
params.calibrateSoundBurstFilteredExtraDb,
|
|
751
|
+
params.calibrateSoundBurstLevelReTBool,
|
|
752
|
+
params.calibrateSoundBurstUses1000HzGainBool,
|
|
753
|
+
params.calibrateSoundBurstRepeats,
|
|
754
|
+
params.calibrateSoundBurstSec,
|
|
755
|
+
params._calibrateSoundBurstPreSec,
|
|
756
|
+
params._calibrateSoundBurstPostSec,
|
|
757
|
+
params.calibrateSoundHz,
|
|
758
|
+
params.calibrateSoundIRSec,
|
|
759
|
+
params.calibrateSoundIIRSec,
|
|
760
|
+
params.calibrateSoundIIRPhase,
|
|
761
|
+
params.calibrateSound1000HzPreSec,
|
|
762
|
+
params.calibrateSound1000HzSec,
|
|
763
|
+
params.calibrateSound1000HzPostSec,
|
|
764
|
+
params.calibrateSoundBackgroundSecs,
|
|
765
|
+
params.calibrateSoundSmoothOctaves,
|
|
766
|
+
params.calibrateSoundSmoothMinBandwidthHz,
|
|
767
|
+
params.calibrateSoundPowerBinDesiredSec,
|
|
768
|
+
params.calibrateSoundPowerDbSDToleratedDb,
|
|
769
|
+
params.calibrateSoundTaperSec,
|
|
770
|
+
params.micManufacturer,
|
|
771
|
+
params.micSerialNumber,
|
|
772
|
+
params.micModelNumber,
|
|
773
|
+
params.micModelName,
|
|
774
|
+
params.calibrateMicrophonesBool,
|
|
775
|
+
params.authorEmails,
|
|
776
|
+
params.webAudioDeviceNames,
|
|
777
|
+
params.IDsToSaveInSoundProfileLibrary,
|
|
778
|
+
params.restartButton,
|
|
779
|
+
params.reminder,
|
|
780
|
+
params.calibrateSoundLimit,
|
|
781
|
+
params.calibrateSoundBurstNormalizeBy1000HzGainBool,
|
|
782
|
+
params.calibrateSoundBurstScalarDB,
|
|
783
|
+
params.calibrateSound1000HzMaxSD_dB,
|
|
784
|
+
params._calibrateSoundBurstMaxSD_dB,
|
|
785
|
+
params.calibrateSoundSamplingDesiredBits,
|
|
786
|
+
params.language,
|
|
787
|
+
params.loudspeakerModelName,
|
|
788
|
+
params.phrases,
|
|
789
|
+
params.soundSubtitleId
|
|
790
|
+
);
|
|
791
|
+
clearInterval(permissionCheckInterval);
|
|
792
|
+
this.#removeUIElems();
|
|
793
|
+
resolve(result);
|
|
794
|
+
} catch (error) {
|
|
795
|
+
clearInterval(permissionCheckInterval);
|
|
796
|
+
reject(error);
|
|
797
|
+
}
|
|
798
|
+
})();
|
|
756
799
|
});
|
|
757
800
|
};
|
|
758
801
|
}
|
|
@@ -296,6 +296,8 @@ class Combination extends AudioCalibrator {
|
|
|
296
296
|
fs2;
|
|
297
297
|
icapture = 0;
|
|
298
298
|
|
|
299
|
+
permissionStatus = null;
|
|
300
|
+
|
|
299
301
|
/**generate string template that gets reevaluated as variable increases */
|
|
300
302
|
generateTemplate = status => {
|
|
301
303
|
if (this.isCalibrating) {
|
|
@@ -362,6 +364,10 @@ class Combination extends AudioCalibrator {
|
|
|
362
364
|
this.deviceInfo = deviceInfo;
|
|
363
365
|
};
|
|
364
366
|
|
|
367
|
+
setPermissionStatus = permissionStatus => {
|
|
368
|
+
this.permissionStatus = permissionStatus;
|
|
369
|
+
};
|
|
370
|
+
|
|
365
371
|
/** .
|
|
366
372
|
* .
|
|
367
373
|
* .
|
|
@@ -3147,6 +3153,7 @@ class Combination extends AudioCalibrator {
|
|
|
3147
3153
|
total_results['system']['phase'] = this.systemIRPhase;
|
|
3148
3154
|
total_results['qualityMetrics'] = this.SDofFilteredRange;
|
|
3149
3155
|
total_results['flags'] = this.flags;
|
|
3156
|
+
total_results['permissionStatus'] = this.permissionStatus;
|
|
3150
3157
|
console.log('total results');
|
|
3151
3158
|
console.log(total_results);
|
|
3152
3159
|
console.log('Time Stamps');
|