speaker-calibration 2.2.217 → 2.2.219
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/.eslintignore +71 -71
- package/.eslintrc.json +40 -40
- package/.github/workflows/update-phrases.yml +37 -0
- package/.prettierignore +69 -69
- package/.prettierrc +14 -14
- package/LICENSE +20 -20
- package/README.md +133 -133
- package/__mocks__/fileMock.js +1 -1
- package/__mocks__/styleMock.js +1 -1
- package/babel.config.js +3 -3
- package/coverage/clover.xml +71 -71
- package/coverage/coverage-final.json +224 -224
- package/coverage/lcov-report/PythonServerInterface.js.html +265 -265
- package/coverage/lcov-report/base.css +354 -354
- package/coverage/lcov-report/block-navigation.js +82 -82
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +123 -123
- package/coverage/lcov-report/prettify.css +101 -101
- package/coverage/lcov-report/prettify.js +937 -937
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +189 -189
- package/coverage/lcov-report/src/index.html +121 -121
- package/coverage/lcov-report/src/server/PythonServerInterface.js.html +268 -268
- package/coverage/lcov-report/src/server/index.html +123 -123
- package/coverage/lcov-report/src/tasks/audioCalibrator.js.html +499 -499
- package/coverage/lcov-report/src/tasks/audioRecorder.js.html +412 -412
- package/coverage/lcov-report/src/tasks/index.html +143 -143
- package/coverage/lcov-report/src/tasks/volume/index.html +123 -123
- package/coverage/lcov-report/src/tasks/volume/volume.js.html +409 -409
- package/coverage/lcov-report/src/utils.js.html +172 -172
- package/coverage/lcov.info +91 -91
- package/dist/Procfile +0 -0
- package/dist/example/NoSleep.min.js +1 -1
- package/dist/example/credentials.json.gpg +0 -0
- package/dist/example/fetch-languages-sheets.js +77 -77
- package/dist/example/i18n.js +28681 -28705
- package/dist/example/index.html +47 -47
- package/dist/example/listener.html +79 -79
- package/dist/example/server.js +51 -51
- package/dist/example/speaker.html +145 -145
- package/dist/example/speakerUI.js +273 -273
- package/dist/example/styles.css +152 -99
- package/dist/listener.js +745 -2621
- package/dist/main.js +12 -23
- package/dist/main.js.LICENSE.txt +0 -0
- package/dist/mlsGen.js +6814 -6814
- package/dist/mlsGen.wasm +0 -0
- package/dist/package-lock.json +1018 -1018
- package/dist/package.json +18 -18
- package/doc/AudioCalibrator.html +417 -417
- package/doc/AudioPeer.html +251 -251
- package/doc/AudioRecorder.html +195 -195
- package/doc/ImpulseResponse.html +215 -215
- package/doc/Listener.html +308 -308
- package/doc/MlsGenInterface.html +226 -226
- package/doc/MyEventEmitter.html +274 -274
- package/doc/PythonServerAPI.html +109 -109
- package/doc/Speaker-Calibration-UML-Diagram.png +0 -0
- package/doc/Speaker.html +276 -276
- package/doc/Takes%20a%20target%20element%20where%20html%20elements%20will%20be%20appended..html +128 -128
- package/doc/Takes%20the%20url%20of%20the%20current%20site%0Aand%20a%20target%20element%20where%20html%20elements%20will%20be%20appended..html +138 -138
- package/doc/Takes%20the%20url%20of%20the%20current%20site%20and%20a%20target%20element%20where%20html%20elements%20will%20be%20appended..html +137 -137
- package/doc/Volume.html +88 -88
- package/doc/audioCalibrator.js.html +179 -179
- package/doc/audioPeer.js.html +175 -175
- package/doc/audioRecorder.js.html +163 -163
- package/doc/creates%20a%20new%20AudioRecorder%20instance.%20%0ASets%20up%20the%20audio%20context%20and%20file%20reader..html +114 -114
- package/doc/fonts/OpenSans-Bold-webfont.eot +0 -0
- package/doc/fonts/OpenSans-Bold-webfont.svg +1829 -1829
- package/doc/fonts/OpenSans-Bold-webfont.woff +0 -0
- package/doc/fonts/OpenSans-BoldItalic-webfont.eot +0 -0
- package/doc/fonts/OpenSans-BoldItalic-webfont.svg +1829 -1829
- package/doc/fonts/OpenSans-BoldItalic-webfont.woff +0 -0
- package/doc/fonts/OpenSans-Italic-webfont.eot +0 -0
- package/doc/fonts/OpenSans-Italic-webfont.svg +1829 -1829
- package/doc/fonts/OpenSans-Italic-webfont.woff +0 -0
- package/doc/fonts/OpenSans-Light-webfont.eot +0 -0
- package/doc/fonts/OpenSans-Light-webfont.svg +1830 -1830
- package/doc/fonts/OpenSans-Light-webfont.woff +0 -0
- package/doc/fonts/OpenSans-LightItalic-webfont.eot +0 -0
- package/doc/fonts/OpenSans-LightItalic-webfont.svg +1834 -1834
- package/doc/fonts/OpenSans-LightItalic-webfont.woff +0 -0
- package/doc/fonts/OpenSans-Regular-webfont.eot +0 -0
- package/doc/fonts/OpenSans-Regular-webfont.svg +1830 -1830
- package/doc/fonts/OpenSans-Regular-webfont.woff +0 -0
- package/doc/global.html +308 -308
- package/doc/index.html +58 -58
- package/doc/listener.js.html +170 -170
- package/doc/mlsGen_mlsGenInterface.js.html +117 -117
- package/doc/myEventEmitter.js.html +124 -124
- package/doc/peer-connection_audioPeer.js.html +188 -188
- package/doc/peer-connection_listener.js.html +311 -311
- package/doc/peer-connection_speaker.js.html +381 -381
- package/doc/sc-activity-diagram.png +0 -0
- package/doc/scripts/linenumber.js +25 -25
- package/doc/scripts/prettify/Apache-License-2.0.txt +202 -202
- package/doc/scripts/prettify/lang-css.js +24 -24
- package/doc/scripts/prettify/prettify.js +640 -640
- package/doc/server_PythonServerAPI.js.html +160 -160
- package/doc/speaker.js.html +248 -248
- package/doc/styles/jsdoc-default.css +371 -371
- package/doc/styles/prettify-jsdoc.css +111 -111
- package/doc/styles/prettify-tomorrow.css +163 -163
- package/doc/tasks_audioCalibrator.js.html +207 -207
- package/doc/tasks_audioRecorder.js.html +190 -190
- package/doc/tasks_impulse-response_impulseResponse.js.html +442 -442
- package/doc/tasks_impulse-response_mlsGen_mlsGenInterface.js.html +175 -175
- package/doc/tasks_volume_volume.js.html +185 -185
- package/doc/utils.js.html +105 -105
- package/jest.config.js +173 -173
- package/makefile +74 -0
- package/netlify.toml +26 -26
- package/package.json +78 -78
- package/src/config/firebase.js +26 -26
- package/src/index.html +21 -21
- package/src/listener-app/listener.js +351 -153
- package/src/main.js +22 -23
- package/src/myEventEmitter.js +83 -83
- package/src/peer-connection/audioPeer.js +183 -183
- package/src/peer-connection/listener.js +389 -369
- package/src/peer-connection/peerErrors.js +25 -25
- package/src/peer-connection/speaker.js +810 -765
- package/src/powerCheck.js +98 -98
- package/src/server/PythonServerAPI.js +869 -869
- package/src/tasks/audioCalibrator.js +360 -360
- package/src/tasks/audioRecorder.js +315 -315
- package/src/tasks/combination/combination.js +3167 -3160
- package/src/tasks/combination/mlsGen/mlsGen.cpp +98 -98
- package/src/tasks/combination/mlsGen/mlsGen.hpp +303 -303
- package/src/tasks/combination/mlsGen/mlsGenInterface.js +131 -131
- package/src/tasks/combination/mlsGen/mlsGenTest.cpp +180 -180
- package/src/tasks/impulse-response/impulseResponse.js +610 -610
- package/src/tasks/impulse-response/mlsGen/mlsGen.cpp +98 -98
- package/src/tasks/impulse-response/mlsGen/mlsGen.hpp +303 -303
- package/src/tasks/impulse-response/mlsGen/mlsGenInterface.js +131 -131
- package/src/tasks/impulse-response/mlsGen/mlsGenTest.cpp +180 -180
- package/src/tasks/volume/volume.cpp +2 -2
- package/src/tasks/volume/volume.hpp +22 -22
- package/src/tasks/volume/volume.js +279 -279
- package/src/utils.js +205 -205
- package/webpack.config.js +64 -64
- package/.gitignore +0 -81
package/src/config/firebase.js
CHANGED
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
// Import the functions you need from the SDKs you need
|
|
2
|
-
import {initializeApp} from 'firebase/app';
|
|
3
|
-
import {getDatabase} from 'firebase/database';
|
|
4
|
-
import {getFirestore} from 'firebase/firestore';
|
|
5
|
-
// TODO: Add SDKs for Firebase products that you want to use
|
|
6
|
-
// https://firebase.google.com/docs/web/setup#available-libraries
|
|
7
|
-
|
|
8
|
-
// Your web app's Firebase configuration
|
|
9
|
-
// For Firebase JS SDK v7.20.0 and later, measurementId is optional
|
|
10
|
-
const firebaseConfig = {
|
|
11
|
-
apiKey: 'AIzaSyDZopCl6jqND4sFYCSiB1GpCXreXd6-Q9s',
|
|
12
|
-
authDomain: 'speaker-calibration.firebaseapp.com',
|
|
13
|
-
databaseURL: 'https://speaker-calibration-default-rtdb.firebaseio.com',
|
|
14
|
-
projectId: 'speaker-calibration',
|
|
15
|
-
storageBucket: 'speaker-calibration.appspot.com',
|
|
16
|
-
messagingSenderId: '322038930574',
|
|
17
|
-
appId: '1:322038930574:web:d10ca9e7d60b6da9bafddf',
|
|
18
|
-
measurementId: 'G-3724GD92R6',
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
// Initialize Firebase
|
|
22
|
-
const app = initializeApp(firebaseConfig);
|
|
23
|
-
// const database = getDatabase();
|
|
24
|
-
const database = getFirestore(app);
|
|
25
|
-
|
|
26
|
-
export default database;
|
|
1
|
+
// Import the functions you need from the SDKs you need
|
|
2
|
+
import {initializeApp} from 'firebase/app';
|
|
3
|
+
import {getDatabase} from 'firebase/database';
|
|
4
|
+
import {getFirestore} from 'firebase/firestore';
|
|
5
|
+
// TODO: Add SDKs for Firebase products that you want to use
|
|
6
|
+
// https://firebase.google.com/docs/web/setup#available-libraries
|
|
7
|
+
|
|
8
|
+
// Your web app's Firebase configuration
|
|
9
|
+
// For Firebase JS SDK v7.20.0 and later, measurementId is optional
|
|
10
|
+
const firebaseConfig = {
|
|
11
|
+
apiKey: 'AIzaSyDZopCl6jqND4sFYCSiB1GpCXreXd6-Q9s',
|
|
12
|
+
authDomain: 'speaker-calibration.firebaseapp.com',
|
|
13
|
+
databaseURL: 'https://speaker-calibration-default-rtdb.firebaseio.com',
|
|
14
|
+
projectId: 'speaker-calibration',
|
|
15
|
+
storageBucket: 'speaker-calibration.appspot.com',
|
|
16
|
+
messagingSenderId: '322038930574',
|
|
17
|
+
appId: '1:322038930574:web:d10ca9e7d60b6da9bafddf',
|
|
18
|
+
measurementId: 'G-3724GD92R6',
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
// Initialize Firebase
|
|
22
|
+
const app = initializeApp(firebaseConfig);
|
|
23
|
+
// const database = getDatabase();
|
|
24
|
+
const database = getFirestore(app);
|
|
25
|
+
|
|
26
|
+
export default database;
|
package/src/index.html
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html class="no-js" lang="">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="utf-8" />
|
|
5
|
-
<meta http-equiv="x-ua-compatible" content="ie=edge" />
|
|
6
|
-
<title>Sound Check</title>
|
|
7
|
-
<meta name="description" content="" />
|
|
8
|
-
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
9
|
-
|
|
10
|
-
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
|
|
11
|
-
<!-- Place favicon.ico in the root directory -->
|
|
12
|
-
</head>
|
|
13
|
-
<body>
|
|
14
|
-
<!--[if lt IE 8]>
|
|
15
|
-
<p class="browserupgrade">
|
|
16
|
-
You are using an <strong>outdated</strong> browser. Please
|
|
17
|
-
<a href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.
|
|
18
|
-
</p>
|
|
19
|
-
<![endif]-->
|
|
20
|
-
</body>
|
|
21
|
-
</html>
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html class="no-js" lang="">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta http-equiv="x-ua-compatible" content="ie=edge" />
|
|
6
|
+
<title>Sound Check</title>
|
|
7
|
+
<meta name="description" content="" />
|
|
8
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
9
|
+
|
|
10
|
+
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
|
|
11
|
+
<!-- Place favicon.ico in the root directory -->
|
|
12
|
+
</head>
|
|
13
|
+
<body>
|
|
14
|
+
<!--[if lt IE 8]>
|
|
15
|
+
<p class="browserupgrade">
|
|
16
|
+
You are using an <strong>outdated</strong> browser. Please
|
|
17
|
+
<a href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.
|
|
18
|
+
</p>
|
|
19
|
+
<![endif]-->
|
|
20
|
+
</body>
|
|
21
|
+
</html>
|
|
@@ -1,153 +1,351 @@
|
|
|
1
|
-
// get element with id message
|
|
2
|
-
import {phrases} from '../../dist/example/i18n.js';
|
|
3
|
-
import
|
|
4
|
-
// get url query parameters
|
|
5
|
-
const urlParams = new URLSearchParams(window.location.search);
|
|
6
|
-
|
|
7
|
-
// get isSmartPhone query parameter
|
|
8
|
-
const isSmartPhone = urlParams.get('sp'); // previous isSmartPhone
|
|
9
|
-
|
|
10
|
-
const listenerParameters = {
|
|
11
|
-
targetElementId: 'display',
|
|
12
|
-
microphoneFromAPI: '',
|
|
13
|
-
microphoneDeviceId: '',
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
const container = document.getElementById('listenerContainer');
|
|
17
|
-
const recordingInProgress = phrases.RC_soundRecording['en-US'];
|
|
18
|
-
const backToExperimentWindow = phrases.RC_backToExperimentWindow['en-US'];
|
|
19
|
-
const allowMicrophone = phrases.RC_allowMicrophoneUse['en-US']
|
|
20
|
-
const placeSmartphoneMicrophone = phrases.RC_placeSmartphoneMicrophone['en-US']
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
);
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
1
|
+
// get element with id message
|
|
2
|
+
import {phrases} from '../../dist/example/i18n.js';
|
|
3
|
+
import Listener from '../peer-connection/listener.js';
|
|
4
|
+
// get url query parameters
|
|
5
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
6
|
+
|
|
7
|
+
// get isSmartPhone query parameter
|
|
8
|
+
const isSmartPhone = urlParams.get('sp'); // previous isSmartPhone
|
|
9
|
+
|
|
10
|
+
const listenerParameters = {
|
|
11
|
+
targetElementId: 'display',
|
|
12
|
+
microphoneFromAPI: '',
|
|
13
|
+
microphoneDeviceId: '',
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const container = document.getElementById('listenerContainer');
|
|
17
|
+
const recordingInProgress = phrases.RC_soundRecording['en-US'];
|
|
18
|
+
const backToExperimentWindow = phrases.RC_backToExperimentWindow['en-US'];
|
|
19
|
+
const allowMicrophone = phrases.RC_allowMicrophoneUse['en-US'];
|
|
20
|
+
const placeSmartphoneMicrophone = phrases.RC_placeSmartphoneMicrophone['en-US'];
|
|
21
|
+
const turnMeToReadBelow = phrases.RC_turnMeToReadBelow['en-US'];
|
|
22
|
+
const recordingInProgressElement = document.getElementById('recordingInProgress');
|
|
23
|
+
const allowMicrophoneElement = document.getElementById('allowMicrophone');
|
|
24
|
+
const turnMessageElement = document.getElementById('turnMeToReadBelow');
|
|
25
|
+
|
|
26
|
+
switch (isSmartPhone) {
|
|
27
|
+
case 'true':
|
|
28
|
+
//hide target element
|
|
29
|
+
const targetElement = document.getElementById('display');
|
|
30
|
+
targetElement.style.display = 'none';
|
|
31
|
+
// Initialize Listener early
|
|
32
|
+
const initializeListener = async () => {
|
|
33
|
+
window.listener = new Listener(listenerParameters);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
// Check microphone permission first
|
|
37
|
+
async function checkAndRequestMicrophonePermission() {
|
|
38
|
+
try {
|
|
39
|
+
const permissionStatus = await navigator.permissions.query({name: 'microphone'});
|
|
40
|
+
|
|
41
|
+
if (permissionStatus.state === 'granted') {
|
|
42
|
+
// Permission already granted, proceed to normal flow
|
|
43
|
+
initializeSmartPhoneDisplay();
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Show permission request message
|
|
48
|
+
allowMicrophoneElement.innerText = phrases.RC_microphonePermission['en-US'];
|
|
49
|
+
container.style.display = 'block';
|
|
50
|
+
|
|
51
|
+
// Function to request microphone access
|
|
52
|
+
async function requestMicAccess(attempt = 1) {
|
|
53
|
+
try {
|
|
54
|
+
await navigator.mediaDevices.getUserMedia({audio: true});
|
|
55
|
+
// Permission granted, proceed to normal flow
|
|
56
|
+
initializeSmartPhoneDisplay();
|
|
57
|
+
} catch (err) {
|
|
58
|
+
if (err.name === 'NotAllowedError') {
|
|
59
|
+
console.log('Permission explicitly denied');
|
|
60
|
+
// Permission explicitly denied
|
|
61
|
+
allowMicrophoneElement.innerText = phrases.RC_microphonePermissionDenied['en-US'];
|
|
62
|
+
// Send denied status and end study
|
|
63
|
+
let error = JSON.stringify(err);
|
|
64
|
+
await window.listener.sendPermissionStatus({type: 'denied', error: error});
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// If 10 seconds passed, try again
|
|
69
|
+
if (attempt < 3) {
|
|
70
|
+
console.log('Retrying microphone access');
|
|
71
|
+
// Limit retries
|
|
72
|
+
setTimeout(() => requestMicAccess(attempt + 1), 10000);
|
|
73
|
+
} else {
|
|
74
|
+
console.log('All retries failed, treating as denied');
|
|
75
|
+
// After all retries failed, treat as denied
|
|
76
|
+
allowMicrophoneElement.innerText = phrases.RC_microphonePermissionDenied['en-US'];
|
|
77
|
+
let error = JSON.stringify(err);
|
|
78
|
+
await window.listener.sendPermissionStatus({type: 'error', error: error});
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
requestMicAccess();
|
|
84
|
+
} catch (err) {
|
|
85
|
+
console.error('Error checking microphone permission:', err);
|
|
86
|
+
allowMicrophoneElement.innerText = phrases.RC_microphonePermissionDenied['en-US'];
|
|
87
|
+
let error = JSON.stringify(err);
|
|
88
|
+
await window.listener.sendPermissionStatus({type: 'error', error: error});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function initializeSmartPhoneDisplay() {
|
|
93
|
+
allowMicrophoneElement.innerText = placeSmartphoneMicrophone;
|
|
94
|
+
allowMicrophoneElement.style.lineHeight = '1.2rem';
|
|
95
|
+
allowMicrophoneElement.style.fontSize = '14px';
|
|
96
|
+
turnMessageElement.innerText = turnMeToReadBelow;
|
|
97
|
+
turnMessageElement.style.lineHeight = '1.2rem';
|
|
98
|
+
turnMessageElement.style.fontSize = '14px';
|
|
99
|
+
|
|
100
|
+
// Show the html upsidedown and adjust layout
|
|
101
|
+
const phrasesContainer = document.getElementById('phrases');
|
|
102
|
+
phrasesContainer.classList.add('phrases');
|
|
103
|
+
|
|
104
|
+
// Hide all elements except what's needed for calibration
|
|
105
|
+
const html = document.querySelector('html');
|
|
106
|
+
html.style.overflow = 'hidden';
|
|
107
|
+
|
|
108
|
+
// Adjust the display container
|
|
109
|
+
const display = document.getElementById('updateDisplay');
|
|
110
|
+
display.classList.add('updateDisplay');
|
|
111
|
+
display.style.position = 'absolute';
|
|
112
|
+
display.style.top = '50%';
|
|
113
|
+
display.style.left = '50%';
|
|
114
|
+
display.style.transform = 'translate(-50%, -50%) rotate(180deg)';
|
|
115
|
+
display.style.width = '100%';
|
|
116
|
+
display.style.textAlign = 'center';
|
|
117
|
+
|
|
118
|
+
container.style.display = 'block';
|
|
119
|
+
|
|
120
|
+
// event listener for id calibrationBeginButton
|
|
121
|
+
const calibrationBeginButton = document.getElementById('calibrationBeginButton');
|
|
122
|
+
console.log('Waiting for proceed button click');
|
|
123
|
+
|
|
124
|
+
calibrationBeginButton.addEventListener('click', async () => {
|
|
125
|
+
console.log('Proceed button clicked');
|
|
126
|
+
|
|
127
|
+
// Clear unnecessary elements
|
|
128
|
+
calibrationBeginButton.remove();
|
|
129
|
+
turnMessageElement.remove();
|
|
130
|
+
|
|
131
|
+
// Create a header container for fixed elements
|
|
132
|
+
const headerContainer = document.createElement('div');
|
|
133
|
+
headerContainer.id = 'headerContainer';
|
|
134
|
+
headerContainer.style.position = 'fixed';
|
|
135
|
+
headerContainer.style.bottom = '0';
|
|
136
|
+
headerContainer.style.left = '0';
|
|
137
|
+
headerContainer.style.width = '100%';
|
|
138
|
+
headerContainer.style.background = 'white';
|
|
139
|
+
headerContainer.style.padding = '10px';
|
|
140
|
+
headerContainer.style.zIndex = '1000';
|
|
141
|
+
headerContainer.style.transform = 'rotate(180deg)';
|
|
142
|
+
container.appendChild(headerContainer);
|
|
143
|
+
|
|
144
|
+
// Set title based on screen width
|
|
145
|
+
const title = document.createElement('h1');
|
|
146
|
+
const titleText =
|
|
147
|
+
window.innerWidth >= 1366
|
|
148
|
+
? phrases.RC_soundRecording['en-US']
|
|
149
|
+
: phrases.RC_soundRecordingSmallScreen['en-US'];
|
|
150
|
+
|
|
151
|
+
// Split small screen title into lines if needed
|
|
152
|
+
if (window.innerWidth < 1366 && titleText.includes('\n')) {
|
|
153
|
+
const lines = titleText.split('\n');
|
|
154
|
+
|
|
155
|
+
// Create container for title lines
|
|
156
|
+
const titleContainer = document.createElement('div');
|
|
157
|
+
titleContainer.style.display = 'flex';
|
|
158
|
+
titleContainer.style.flexDirection = 'column';
|
|
159
|
+
titleContainer.style.alignItems = 'left';
|
|
160
|
+
titleContainer.style.lineHeight = '1.2';
|
|
161
|
+
|
|
162
|
+
// Add each line
|
|
163
|
+
lines.forEach(line => {
|
|
164
|
+
const lineDiv = document.createElement('p');
|
|
165
|
+
lineDiv.textContent = line;
|
|
166
|
+
lineDiv.style.width = 'fit-content';
|
|
167
|
+
titleContainer.appendChild(lineDiv);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
title.appendChild(titleContainer);
|
|
171
|
+
} else {
|
|
172
|
+
title.textContent = titleText;
|
|
173
|
+
title.style.lineHeight = '1.2';
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
title.style.margin = '0';
|
|
177
|
+
title.style.whiteSpace = 'pre-line'; // Preserve line breaks
|
|
178
|
+
headerContainer.appendChild(title);
|
|
179
|
+
|
|
180
|
+
// Function to adjust font size to fill width
|
|
181
|
+
const adjustFontSize = (element, maxWidth) => {
|
|
182
|
+
let fontSize = 20; // Start with a reasonable minimum size
|
|
183
|
+
element.style.fontSize = fontSize + 'px';
|
|
184
|
+
// Increase font size until text fills width (minus margins)
|
|
185
|
+
while (element.scrollWidth < maxWidth - 40 && fontSize < 200) {
|
|
186
|
+
fontSize++;
|
|
187
|
+
element.style.fontSize = fontSize + 'px';
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Step back one to ensure we don't overflow
|
|
191
|
+
fontSize--;
|
|
192
|
+
element.style.fontSize = fontSize + 'px';
|
|
193
|
+
return fontSize;
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
// For small screen, ensure all lines use same font size
|
|
197
|
+
if (window.innerWidth < 1366 && titleText.includes('\n')) {
|
|
198
|
+
const lines = title.querySelectorAll('p');
|
|
199
|
+
let minFontSize = Infinity;
|
|
200
|
+
|
|
201
|
+
// First pass: find the smallest font size that fits for any line
|
|
202
|
+
lines.forEach(line => {
|
|
203
|
+
const fontSize = adjustFontSize(line, window.innerWidth);
|
|
204
|
+
minFontSize = Math.min(minFontSize, fontSize);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
// Second pass: apply the smallest font size to all lines
|
|
208
|
+
lines.forEach(line => {
|
|
209
|
+
line.style.fontSize = minFontSize + 'px';
|
|
210
|
+
});
|
|
211
|
+
} else {
|
|
212
|
+
// For single line title, just adjust to fill width
|
|
213
|
+
adjustFontSize(title, window.innerWidth);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Get the header height after text is added and sized
|
|
217
|
+
const headerHeight = headerContainer.getBoundingClientRect().height;
|
|
218
|
+
|
|
219
|
+
// Adjust the display container to start after header
|
|
220
|
+
const display = document.getElementById('updateDisplay');
|
|
221
|
+
display.classList.add('updateDisplay');
|
|
222
|
+
display.style.position = 'fixed';
|
|
223
|
+
display.style.bottom = `${headerHeight}px`; // Start after header
|
|
224
|
+
display.style.left = '0';
|
|
225
|
+
display.style.right = '0';
|
|
226
|
+
display.style.top = '0';
|
|
227
|
+
display.style.transform = 'rotate(180deg)';
|
|
228
|
+
display.style.overflowY = 'auto';
|
|
229
|
+
display.style.padding = '20px';
|
|
230
|
+
display.style.background = 'white';
|
|
231
|
+
|
|
232
|
+
// Position microphone instruction at the top (appears at bottom due to rotation)
|
|
233
|
+
allowMicrophoneElement.innerText = '';
|
|
234
|
+
allowMicrophoneElement.style.position = 'fixed';
|
|
235
|
+
allowMicrophoneElement.style.top = '20px';
|
|
236
|
+
allowMicrophoneElement.style.left = '50%';
|
|
237
|
+
allowMicrophoneElement.style.transform = 'translateX(-50%) rotate(180deg)';
|
|
238
|
+
allowMicrophoneElement.style.width = '90%';
|
|
239
|
+
allowMicrophoneElement.style.textAlign = 'center';
|
|
240
|
+
allowMicrophoneElement.style.zIndex = '1000';
|
|
241
|
+
|
|
242
|
+
let lock = null;
|
|
243
|
+
try {
|
|
244
|
+
if ('wakeLock' in navigator) {
|
|
245
|
+
lock = await navigator.wakeLock.request('screen');
|
|
246
|
+
}
|
|
247
|
+
} catch (err) {
|
|
248
|
+
console.log(err);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const webAudioDeviceNames = {microphone: '', deviceID: ''};
|
|
252
|
+
const externalMicList = ['UMIK', 'Airpods', 'Bluetooth'];
|
|
253
|
+
try {
|
|
254
|
+
const stream = await navigator.mediaDevices.getUserMedia({audio: true});
|
|
255
|
+
if (stream) {
|
|
256
|
+
const devices = await navigator.mediaDevices.enumerateDevices();
|
|
257
|
+
const mics = devices.filter(device => device.kind === 'audioinput');
|
|
258
|
+
mics.forEach(mic => {
|
|
259
|
+
if (externalMicList.some(externalMic => mic.label.includes(externalMic))) {
|
|
260
|
+
webAudioDeviceNames.microphone = mic.label;
|
|
261
|
+
webAudioDeviceNames.deviceID = mic.deviceId;
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
if (webAudioDeviceNames.microphone === '') {
|
|
265
|
+
webAudioDeviceNames.microphone = mics[0].label;
|
|
266
|
+
webAudioDeviceNames.deviceID = mics[0].deviceId;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
} catch (err) {
|
|
270
|
+
console.log(err);
|
|
271
|
+
}
|
|
272
|
+
window.listener.setMicrophoneFromAPI(webAudioDeviceNames.microphone);
|
|
273
|
+
window.listener.setMicrophoneDeviceId(webAudioDeviceNames.microphone);
|
|
274
|
+
// show target element
|
|
275
|
+
targetElement.style.display = 'block';
|
|
276
|
+
await window.listener.startCalibration();
|
|
277
|
+
if (lock) {
|
|
278
|
+
lock.release();
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Wrap the initialization in an IIFE
|
|
284
|
+
(async function initializeSmartPhoneMode() {
|
|
285
|
+
await initializeListener();
|
|
286
|
+
|
|
287
|
+
const timeout = 30000; // 30 seconds timeout
|
|
288
|
+
const startTime = Date.now();
|
|
289
|
+
|
|
290
|
+
// Wait for peer connection setup with timeout
|
|
291
|
+
while (Date.now() - startTime < timeout) {
|
|
292
|
+
if (
|
|
293
|
+
window.listener.peer.id !== null &&
|
|
294
|
+
window.listener.conn !== null &&
|
|
295
|
+
window.listener.connOpen
|
|
296
|
+
) {
|
|
297
|
+
console.log('Connection established successfully');
|
|
298
|
+
await checkAndRequestMicrophonePermission();
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
console.log('Waiting for connection setup...');
|
|
302
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// If we get here, we've timed out
|
|
306
|
+
console.error('Connection setup timed out after 30 seconds');
|
|
307
|
+
allowMicrophoneElement.innerText = phrases.RC_microphonePermissionDenied['en-US'];
|
|
308
|
+
await window.listener.sendPermissionStatus({
|
|
309
|
+
type: 'error',
|
|
310
|
+
error: 'Connection setup timed out after 30 seconds',
|
|
311
|
+
});
|
|
312
|
+
})().catch(console.error);
|
|
313
|
+
break;
|
|
314
|
+
case 'false':
|
|
315
|
+
// Initialize listener immediately
|
|
316
|
+
listenerParameters.microphoneDeviceId = urlParams.get('deviceId');
|
|
317
|
+
window.listener = new Listener(listenerParameters);
|
|
318
|
+
|
|
319
|
+
// remove the button
|
|
320
|
+
const calibrationBeginButton2 = document.getElementById('calibrationBeginButton');
|
|
321
|
+
calibrationBeginButton2.remove();
|
|
322
|
+
container.style.display = 'block';
|
|
323
|
+
|
|
324
|
+
window.addEventListener('load', async () => {
|
|
325
|
+
// set the text of the html elements
|
|
326
|
+
recordingInProgressElement.innerText = recordingInProgress;
|
|
327
|
+
allowMicrophoneElement.innerText = allowMicrophone;
|
|
328
|
+
|
|
329
|
+
recordingInProgressElement.style.whiteSpace = 'nowrap';
|
|
330
|
+
recordingInProgressElement.style.fontWeight = 'bold';
|
|
331
|
+
|
|
332
|
+
// fit content
|
|
333
|
+
recordingInProgressElement.style.width = 'fit-content';
|
|
334
|
+
let fontSize = 100;
|
|
335
|
+
recordingInProgressElement.style.fontSize = fontSize + 'px';
|
|
336
|
+
|
|
337
|
+
while (recordingInProgressElement.scrollWidth > window.innerWidth * 0.9 && fontSize > 10) {
|
|
338
|
+
fontSize--;
|
|
339
|
+
recordingInProgressElement.style.fontSize = fontSize + 'px';
|
|
340
|
+
}
|
|
341
|
+
const message = document.getElementById('message');
|
|
342
|
+
message.style.lineHeight = '2.5rem';
|
|
343
|
+
const p = document.createElement('p');
|
|
344
|
+
p.innerText = backToExperimentWindow;
|
|
345
|
+
message.appendChild(p);
|
|
346
|
+
|
|
347
|
+
await window.listener.startCalibration();
|
|
348
|
+
console.log(window.listener);
|
|
349
|
+
});
|
|
350
|
+
break;
|
|
351
|
+
}
|
package/src/main.js
CHANGED
|
@@ -1,23 +1,22 @@
|
|
|
1
|
-
import Listener from './peer-connection/listener';
|
|
2
|
-
import Speaker from './peer-connection/speaker';
|
|
3
|
-
|
|
4
|
-
import VolumeCalibration from './tasks/volume/volume';
|
|
5
|
-
import ImpulseResponseCalibration from './tasks/impulse-response/impulseResponse';
|
|
6
|
-
import CombinationCalibration from './tasks/combination/combination';
|
|
7
|
-
|
|
8
|
-
import {
|
|
9
|
-
UnsupportedDeviceError,
|
|
10
|
-
MissingSpeakerIdError,
|
|
11
|
-
CalibrationTimedOutError,
|
|
12
|
-
} from './peer-connection/peerErrors';
|
|
13
|
-
|
|
14
|
-
export {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
};
|
|
1
|
+
// import Listener from './peer-connection/listener';
|
|
2
|
+
import Speaker from './peer-connection/speaker';
|
|
3
|
+
|
|
4
|
+
import VolumeCalibration from './tasks/volume/volume';
|
|
5
|
+
import ImpulseResponseCalibration from './tasks/impulse-response/impulseResponse';
|
|
6
|
+
import CombinationCalibration from './tasks/combination/combination';
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
UnsupportedDeviceError,
|
|
10
|
+
MissingSpeakerIdError,
|
|
11
|
+
CalibrationTimedOutError,
|
|
12
|
+
} from './peer-connection/peerErrors';
|
|
13
|
+
|
|
14
|
+
export {
|
|
15
|
+
Speaker,
|
|
16
|
+
VolumeCalibration,
|
|
17
|
+
ImpulseResponseCalibration,
|
|
18
|
+
UnsupportedDeviceError,
|
|
19
|
+
MissingSpeakerIdError,
|
|
20
|
+
CalibrationTimedOutError,
|
|
21
|
+
CombinationCalibration,
|
|
22
|
+
};
|