@usero/sdk 1.1.8 → 1.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.
|
@@ -727,7 +727,9 @@ function micChipState(store) {
|
|
|
727
727
|
if (store.micAcquiring) return "connecting";
|
|
728
728
|
return "none";
|
|
729
729
|
}
|
|
730
|
-
|
|
730
|
+
if (store.muted) return "muted";
|
|
731
|
+
if (store.micSilent) return "silent";
|
|
732
|
+
return "recording";
|
|
731
733
|
}
|
|
732
734
|
function renderIndicatorState(store) {
|
|
733
735
|
const root = store.indicatorRoot;
|
|
@@ -740,7 +742,8 @@ function renderIndicatorState(store) {
|
|
|
740
742
|
if (!(dot instanceof HTMLElement) || !mic || !(micIcon instanceof HTMLElement) || !(micLabel instanceof HTMLElement) || !btn) return;
|
|
741
743
|
dot.setAttribute("data-state", store.indicatorState);
|
|
742
744
|
const chipState = micChipState(store);
|
|
743
|
-
|
|
745
|
+
const micStateAttr = chipState === "inactive" || chipState === "silent" ? "none" : chipState;
|
|
746
|
+
mic.setAttribute("data-mic-state", micStateAttr);
|
|
744
747
|
mic.removeAttribute("data-mic-fail");
|
|
745
748
|
if (chipState === "none") mic.setAttribute("data-mic-fail", store.micFailReason ?? "blocked");
|
|
746
749
|
switch (store.indicatorState) {
|
|
@@ -784,6 +787,13 @@ function renderIndicatorState(store) {
|
|
|
784
787
|
mic.setAttribute("aria-pressed", "false");
|
|
785
788
|
mic.setAttribute("tabindex", "-1");
|
|
786
789
|
break;
|
|
790
|
+
case "silent":
|
|
791
|
+
micIcon.innerHTML = MIC_MUTED_ICON_SVG;
|
|
792
|
+
micLabel.textContent = "We can't hear you, tap to recheck";
|
|
793
|
+
mic.setAttribute("aria-label", "We can't hear your microphone. Check your input device, then tap to recheck. Recording continues.");
|
|
794
|
+
mic.setAttribute("aria-pressed", "false");
|
|
795
|
+
mic.removeAttribute("tabindex");
|
|
796
|
+
break;
|
|
787
797
|
case "none": {
|
|
788
798
|
micIcon.innerHTML = MIC_MUTED_ICON_SVG;
|
|
789
799
|
const failLabel = store.micFailReason === "not-found" ? "No mic found, tap to retry" : "Mic blocked, tap to retry";
|
|
@@ -1371,9 +1381,92 @@ function enqueueChunk(store, ctx, blob) {
|
|
|
1371
1381
|
store.pendingUploads -= 1;
|
|
1372
1382
|
});
|
|
1373
1383
|
}
|
|
1384
|
+
var SILENCE_RMS_DB_THRESHOLD = -60;
|
|
1385
|
+
var SILENCE_FLOOR_DB = -100;
|
|
1386
|
+
var SILENCE_SUSTAINED_MS = 1800;
|
|
1387
|
+
var SILENCE_POLL_MS = 250;
|
|
1388
|
+
function rmsDbFromSamples(samples) {
|
|
1389
|
+
const n = samples.length;
|
|
1390
|
+
if (n === 0) return SILENCE_FLOOR_DB;
|
|
1391
|
+
let sumSquares = 0;
|
|
1392
|
+
for (let i = 0; i < n; i += 1) {
|
|
1393
|
+
const s = samples[i] ?? 0;
|
|
1394
|
+
sumSquares += s * s;
|
|
1395
|
+
}
|
|
1396
|
+
const rms = Math.sqrt(sumSquares / n);
|
|
1397
|
+
if (rms <= 0) return SILENCE_FLOOR_DB;
|
|
1398
|
+
const db = 20 * Math.log10(rms);
|
|
1399
|
+
return db < SILENCE_FLOOR_DB ? SILENCE_FLOOR_DB : db;
|
|
1400
|
+
}
|
|
1401
|
+
function isStreamSilent(input) {
|
|
1402
|
+
const rmsDb = typeof input === "number" ? input : rmsDbFromSamples(input);
|
|
1403
|
+
return rmsDb <= SILENCE_RMS_DB_THRESHOLD;
|
|
1404
|
+
}
|
|
1405
|
+
function startSilenceMonitor(stream, onChange, logger) {
|
|
1406
|
+
const Ctor = typeof window !== "undefined" ? window.AudioContext ?? window.webkitAudioContext : void 0;
|
|
1407
|
+
if (!Ctor) return null;
|
|
1408
|
+
let audioCtx;
|
|
1409
|
+
let source;
|
|
1410
|
+
let analyser;
|
|
1411
|
+
try {
|
|
1412
|
+
audioCtx = new Ctor();
|
|
1413
|
+
source = audioCtx.createMediaStreamSource(stream);
|
|
1414
|
+
analyser = audioCtx.createAnalyser();
|
|
1415
|
+
analyser.fftSize = 2048;
|
|
1416
|
+
source.connect(analyser);
|
|
1417
|
+
} catch (err) {
|
|
1418
|
+
logger.warn("silence monitor: failed to attach analyser", err);
|
|
1419
|
+
return null;
|
|
1420
|
+
}
|
|
1421
|
+
const buffer = new Float32Array(analyser.fftSize);
|
|
1422
|
+
let reportedSilent = false;
|
|
1423
|
+
let runStartedAt = Date.now();
|
|
1424
|
+
let lastRaw = false;
|
|
1425
|
+
let intervalId = null;
|
|
1426
|
+
const tick = () => {
|
|
1427
|
+
try {
|
|
1428
|
+
analyser.getFloatTimeDomainData(buffer);
|
|
1429
|
+
} catch {
|
|
1430
|
+
return;
|
|
1431
|
+
}
|
|
1432
|
+
const rawSilent = isStreamSilent(buffer);
|
|
1433
|
+
const now = Date.now();
|
|
1434
|
+
if (rawSilent !== lastRaw) {
|
|
1435
|
+
lastRaw = rawSilent;
|
|
1436
|
+
runStartedAt = now;
|
|
1437
|
+
}
|
|
1438
|
+
if (rawSilent !== reportedSilent && now - runStartedAt >= SILENCE_SUSTAINED_MS) {
|
|
1439
|
+
reportedSilent = rawSilent;
|
|
1440
|
+
onChange(reportedSilent);
|
|
1441
|
+
}
|
|
1442
|
+
};
|
|
1443
|
+
intervalId = setInterval(tick, SILENCE_POLL_MS);
|
|
1444
|
+
return {
|
|
1445
|
+
stop() {
|
|
1446
|
+
if (intervalId !== null) {
|
|
1447
|
+
clearInterval(intervalId);
|
|
1448
|
+
intervalId = null;
|
|
1449
|
+
}
|
|
1450
|
+
try {
|
|
1451
|
+
source.disconnect();
|
|
1452
|
+
analyser.disconnect();
|
|
1453
|
+
} catch {
|
|
1454
|
+
}
|
|
1455
|
+
try {
|
|
1456
|
+
void audioCtx.close();
|
|
1457
|
+
} catch {
|
|
1458
|
+
}
|
|
1459
|
+
}
|
|
1460
|
+
};
|
|
1461
|
+
}
|
|
1374
1462
|
async function startRecording(store, ctx) {
|
|
1375
1463
|
store.micAcquiring = true;
|
|
1376
1464
|
store.micFailReason = null;
|
|
1465
|
+
if (store.silenceMonitor) {
|
|
1466
|
+
store.silenceMonitor.stop();
|
|
1467
|
+
store.silenceMonitor = null;
|
|
1468
|
+
}
|
|
1469
|
+
store.micSilent = false;
|
|
1377
1470
|
renderIndicatorState(store);
|
|
1378
1471
|
if (!isMediaRecorderSupported()) {
|
|
1379
1472
|
ctx.logger.warn("MediaRecorder not supported, continuing without audio");
|
|
@@ -1428,6 +1521,17 @@ async function startRecording(store, ctx) {
|
|
|
1428
1521
|
store.indicatorState = "recording";
|
|
1429
1522
|
}
|
|
1430
1523
|
renderIndicatorState(store);
|
|
1524
|
+
const monitor = startSilenceMonitor(
|
|
1525
|
+
stream,
|
|
1526
|
+
(silent) => {
|
|
1527
|
+
const effectiveSilent = silent && !store.muted;
|
|
1528
|
+
if (store.micSilent === effectiveSilent) return;
|
|
1529
|
+
store.micSilent = effectiveSilent;
|
|
1530
|
+
renderIndicatorState(store);
|
|
1531
|
+
},
|
|
1532
|
+
ctx.logger
|
|
1533
|
+
);
|
|
1534
|
+
store.silenceMonitor = monitor;
|
|
1431
1535
|
}
|
|
1432
1536
|
function toggleMute(store) {
|
|
1433
1537
|
if (!store.stream || !store.hasMicPermission) return false;
|
|
@@ -1458,6 +1562,11 @@ function flushMuteIfActive(store) {
|
|
|
1458
1562
|
store.mutedSinceMs = null;
|
|
1459
1563
|
}
|
|
1460
1564
|
function stopRecording(store) {
|
|
1565
|
+
if (store.silenceMonitor) {
|
|
1566
|
+
store.silenceMonitor.stop();
|
|
1567
|
+
store.silenceMonitor = null;
|
|
1568
|
+
}
|
|
1569
|
+
store.micSilent = false;
|
|
1461
1570
|
const recorder = store.recorder;
|
|
1462
1571
|
if (recorder && recorder.state !== "inactive") {
|
|
1463
1572
|
try {
|
|
@@ -1589,6 +1698,8 @@ function userTest(options = {}) {
|
|
|
1589
1698
|
muted: false,
|
|
1590
1699
|
mutedSinceMs: null,
|
|
1591
1700
|
mutedSegments: [],
|
|
1701
|
+
micSilent: false,
|
|
1702
|
+
silenceMonitor: null,
|
|
1592
1703
|
muteToastShown: false,
|
|
1593
1704
|
muteToastTimers: [],
|
|
1594
1705
|
notes: [],
|
|
@@ -1616,9 +1727,16 @@ function userTest(options = {}) {
|
|
|
1616
1727
|
}
|
|
1617
1728
|
return;
|
|
1618
1729
|
}
|
|
1730
|
+
if (store.micSilent && !store.micAcquiring && store.indicatorState !== "finishing" && store.indicatorState !== "done" && store.indicatorState !== "error") {
|
|
1731
|
+
void startRecording(store, ctx);
|
|
1732
|
+
return;
|
|
1733
|
+
}
|
|
1619
1734
|
const ok = toggleMute(store);
|
|
1620
1735
|
if (!ok) return;
|
|
1621
|
-
if (store.muted)
|
|
1736
|
+
if (store.muted) {
|
|
1737
|
+
store.micSilent = false;
|
|
1738
|
+
showMuteToast(store);
|
|
1739
|
+
}
|
|
1622
1740
|
renderIndicatorState(store);
|
|
1623
1741
|
};
|
|
1624
1742
|
const closeNote = () => closeNotePopover(store);
|
|
@@ -1754,9 +1872,20 @@ function userTest(options = {}) {
|
|
|
1754
1872
|
}
|
|
1755
1873
|
};
|
|
1756
1874
|
}
|
|
1757
|
-
var __test__ = {
|
|
1875
|
+
var __test__ = {
|
|
1876
|
+
getTestSlug,
|
|
1877
|
+
pickMimeType,
|
|
1878
|
+
isMediaRecorderSupported,
|
|
1879
|
+
micChipState,
|
|
1880
|
+
isStreamSilent,
|
|
1881
|
+
rmsDbFromSamples,
|
|
1882
|
+
SILENCE_RMS_DB_THRESHOLD,
|
|
1883
|
+
SILENCE_FLOOR_DB
|
|
1884
|
+
};
|
|
1758
1885
|
|
|
1759
1886
|
exports.__test__ = __test__;
|
|
1887
|
+
exports.isStreamSilent = isStreamSilent;
|
|
1888
|
+
exports.rmsDbFromSamples = rmsDbFromSamples;
|
|
1760
1889
|
exports.userTest = userTest;
|
|
1761
1890
|
//# sourceMappingURL=user-test.cjs.map
|
|
1762
1891
|
//# sourceMappingURL=user-test.cjs.map
|