testaugnitorecorder4 1.1.11 → 1.1.13

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.
@@ -913,6 +913,7 @@ var Streamer = /*#__PURE__*/function () {
913
913
  this.deviceChangeHandler = null;
914
914
  this.onMicReady = onMicReady;
915
915
  this._originalDeviceId = null;
916
+ this._originalDeviceLabel = null;
916
917
  if (this.shouldPreIntialiseRecorder) {
917
918
  this.InitialiseMediaStream(reconnectAudioDuration, socketUrl);
918
919
  }
@@ -1131,13 +1132,13 @@ var Streamer = /*#__PURE__*/function () {
1131
1132
  }, {
1132
1133
  key: "createMediaStreamSourceNode",
1133
1134
  value: function () {
1134
- var _createMediaStreamSourceNode = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee6() {
1135
+ var _createMediaStreamSourceNode = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee5() {
1135
1136
  var _this2 = this;
1136
- var _track$getSettings$de2, _track$getSettings2, audioConstraints, track, acquiredDeviceId, errMessage;
1137
- return _regeneratorRuntime().wrap(function _callee6$(_context6) {
1138
- while (1) switch (_context6.prev = _context6.next) {
1137
+ var _track$getSettings$de2, _track$getSettings2, _track$label, audioConstraints, track, acquiredDeviceId, acquiredLabel, errMessage;
1138
+ return _regeneratorRuntime().wrap(function _callee5$(_context5) {
1139
+ while (1) switch (_context5.prev = _context5.next) {
1139
1140
  case 0:
1140
- _context6.prev = 0;
1141
+ _context5.prev = 0;
1141
1142
  audioConstraints = {
1142
1143
  channelCount: CHANNEL_COUNT,
1143
1144
  noiseSuppression: this.noiseSuppression !== undefined ? this.noiseSuppression : NOISE_SUPPRESS_ENABLED
@@ -1162,21 +1163,29 @@ var Streamer = /*#__PURE__*/function () {
1162
1163
  exact: this._preferredDeviceId
1163
1164
  };
1164
1165
  }
1165
- _context6.next = 8;
1166
+ _context5.next = 8;
1166
1167
  return navigator.mediaDevices.getUserMedia({
1167
1168
  audio: audioConstraints
1168
1169
  });
1169
1170
  case 8:
1170
- this.audioStream = _context6.sent;
1171
+ this.audioStream = _context5.sent;
1171
1172
  if (!this.audioContext || this.audioContext.state === "closed") {
1172
1173
  this.audioContext = new AudioContext();
1173
1174
  }
1174
1175
  this.source = this.audioContext.createMediaStreamSource(this.audioStream);
1175
1176
  track = this.audioStream.getAudioTracks()[0]; // Record the device that was first used for recording so we can
1176
1177
  // identify when that exact mic is reconnected after being unplugged.
1177
- acquiredDeviceId = (_track$getSettings$de2 = (_track$getSettings2 = track.getSettings) === null || _track$getSettings2 === void 0 || (_track$getSettings2 = _track$getSettings2.call(track)) === null || _track$getSettings2 === void 0 ? void 0 : _track$getSettings2.deviceId) !== null && _track$getSettings$de2 !== void 0 ? _track$getSettings$de2 : track.label;
1178
+ // We store BOTH the deviceId and the label because on Windows the
1179
+ // deviceId is often a generic alias ("default" / "communications")
1180
+ // that does not change when a USB mic is unplugged and a fallback
1181
+ // device takes over. The label is the physical device name and IS
1182
+ // unique per device, so the combination of both fields lets us
1183
+ // reliably distinguish a true reconnect from a same-alias fallback.
1184
+ acquiredDeviceId = (_track$getSettings$de2 = (_track$getSettings2 = track.getSettings) === null || _track$getSettings2 === void 0 || (_track$getSettings2 = _track$getSettings2.call(track)) === null || _track$getSettings2 === void 0 ? void 0 : _track$getSettings2.deviceId) !== null && _track$getSettings$de2 !== void 0 ? _track$getSettings$de2 : "";
1185
+ acquiredLabel = (_track$label = track.label) !== null && _track$label !== void 0 ? _track$label : "";
1178
1186
  if (!this._originalDeviceId) {
1179
1187
  this._originalDeviceId = acquiredDeviceId;
1188
+ this._originalDeviceLabel = acquiredLabel;
1180
1189
  }
1181
1190
 
1182
1191
  // Clear any previously registered handler before attaching a new one
@@ -1184,52 +1193,43 @@ var Streamer = /*#__PURE__*/function () {
1184
1193
  if (this.trackEndedHandler) {
1185
1194
  this.trackEndedHandler = null;
1186
1195
  }
1187
- this.trackEndedHandler = /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee5() {
1188
- return _regeneratorRuntime().wrap(function _callee5$(_context5) {
1189
- while (1) switch (_context5.prev = _context5.next) {
1190
- case 0:
1191
- // this.log("Microphone disconnected");
1196
+ this.trackEndedHandler = function () {
1197
+ // this.log("Microphone disconnected");
1192
1198
 
1193
- _this2.micNeedsRecovery = true;
1194
- _this2.onError(JSON.stringify({
1195
- Type: "ERROR",
1196
- Data: "Audio track ended. Possibly the mic was unplugged."
1197
- }));
1198
- if (!(_this2.isStreaming && !_this2.isPaused)) {
1199
- _context5.next = 5;
1200
- break;
1201
- }
1202
- _context5.next = 5;
1203
- return _this2.recoverAudioGraph();
1204
- case 5:
1205
- case "end":
1206
- return _context5.stop();
1207
- }
1208
- }, _callee5);
1209
- }));
1199
+ _this2.micNeedsRecovery = true;
1200
+ _this2.onError(JSON.stringify({
1201
+ Type: "ERROR",
1202
+ Data: "Audio track ended. Possibly the mic was unplugged."
1203
+ }));
1204
+
1205
+ // Do NOT call recoverAudioGraph() here. The devicechange event fires
1206
+ // immediately after track-ended on all major browsers/OS and will
1207
+ // handle recovery in deviceChangeHandler. Calling it here as well
1208
+ // causes a double-recovery race (two sequential error/ready callbacks).
1209
+ };
1210
1210
  track.addEventListener("ended", this.trackEndedHandler);
1211
- _context6.next = 24;
1211
+ _context5.next = 25;
1212
1212
  break;
1213
- case 19:
1214
- _context6.prev = 19;
1215
- _context6.t0 = _context6["catch"](0);
1213
+ case 20:
1214
+ _context5.prev = 20;
1215
+ _context5.t0 = _context5["catch"](0);
1216
1216
  errMessage = "";
1217
- if (_context6.t0.name == "NotAllowedError") {
1217
+ if (_context5.t0.name == "NotAllowedError") {
1218
1218
  errMessage = "Mic permission denied";
1219
- } else if (_context6.t0.name === "NotFoundError") {
1219
+ } else if (_context5.t0.name === "NotFoundError") {
1220
1220
  errMessage = "No suitable media device found";
1221
- } else if (_context6.t0.name === "NotReadableError") {
1221
+ } else if (_context5.t0.name === "NotReadableError") {
1222
1222
  errMessage = "Microphone is being used by another application";
1223
1223
  }
1224
1224
  this.onError(JSON.stringify({
1225
1225
  Type: "ERROR",
1226
1226
  Data: errMessage
1227
1227
  }));
1228
- case 24:
1228
+ case 25:
1229
1229
  case "end":
1230
- return _context6.stop();
1230
+ return _context5.stop();
1231
1231
  }
1232
- }, _callee6, this, [[0, 19]]);
1232
+ }, _callee5, this, [[0, 20]]);
1233
1233
  }));
1234
1234
  function createMediaStreamSourceNode() {
1235
1235
  return _createMediaStreamSourceNode.apply(this, arguments);
@@ -1239,35 +1239,35 @@ var Streamer = /*#__PURE__*/function () {
1239
1239
  }, {
1240
1240
  key: "loadAudio",
1241
1241
  value: function () {
1242
- var _loadAudio = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee7() {
1242
+ var _loadAudio = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee6() {
1243
1243
  var response, arrayBuffer, audioBuffer;
1244
- return _regeneratorRuntime().wrap(function _callee7$(_context7) {
1245
- while (1) switch (_context7.prev = _context7.next) {
1244
+ return _regeneratorRuntime().wrap(function _callee6$(_context6) {
1245
+ while (1) switch (_context6.prev = _context6.next) {
1246
1246
  case 0:
1247
- _context7.prev = 0;
1248
- _context7.next = 3;
1247
+ _context6.prev = 0;
1248
+ _context6.next = 3;
1249
1249
  return fetch("./radiology_speed_test.wav");
1250
1250
  case 3:
1251
- response = _context7.sent;
1252
- _context7.next = 6;
1251
+ response = _context6.sent;
1252
+ _context6.next = 6;
1253
1253
  return response.arrayBuffer();
1254
1254
  case 6:
1255
- arrayBuffer = _context7.sent;
1256
- _context7.next = 9;
1255
+ arrayBuffer = _context6.sent;
1256
+ _context6.next = 9;
1257
1257
  return this.audioContext.decodeAudioData(arrayBuffer);
1258
1258
  case 9:
1259
- audioBuffer = _context7.sent;
1260
- return _context7.abrupt("return", audioBuffer);
1259
+ audioBuffer = _context6.sent;
1260
+ return _context6.abrupt("return", audioBuffer);
1261
1261
  case 13:
1262
- _context7.prev = 13;
1263
- _context7.t0 = _context7["catch"](0);
1264
- console.error("Unable to fetch the audio file. Error: ".concat(_context7.t0.message));
1265
- return _context7.abrupt("return", null);
1262
+ _context6.prev = 13;
1263
+ _context6.t0 = _context6["catch"](0);
1264
+ console.error("Unable to fetch the audio file. Error: ".concat(_context6.t0.message));
1265
+ return _context6.abrupt("return", null);
1266
1266
  case 17:
1267
1267
  case "end":
1268
- return _context7.stop();
1268
+ return _context6.stop();
1269
1269
  }
1270
- }, _callee7, this, [[0, 13]]);
1270
+ }, _callee6, this, [[0, 13]]);
1271
1271
  }));
1272
1272
  function loadAudio() {
1273
1273
  return _loadAudio.apply(this, arguments);
@@ -1277,14 +1277,14 @@ var Streamer = /*#__PURE__*/function () {
1277
1277
  }, {
1278
1278
  key: "createProcessorNode",
1279
1279
  value: function () {
1280
- var _createProcessorNode = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee8() {
1280
+ var _createProcessorNode = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee7() {
1281
1281
  var _this3 = this;
1282
1282
  var _this$shouldReadInten, sampleRateParam, bufferSizeIntervalParam, pausedBufferIntervalParam, shouldReadIntensityParam;
1283
- return _regeneratorRuntime().wrap(function _callee8$(_context8) {
1284
- while (1) switch (_context8.prev = _context8.next) {
1283
+ return _regeneratorRuntime().wrap(function _callee7$(_context7) {
1284
+ while (1) switch (_context7.prev = _context7.next) {
1285
1285
  case 0:
1286
- _context8.prev = 0;
1287
- _context8.next = 3;
1286
+ _context7.prev = 0;
1287
+ _context7.next = 3;
1288
1288
  return this.audioContext.audioWorklet.addModule("data:application/javascript,".concat(encodeURIComponent(worklet)));
1289
1289
  case 3:
1290
1290
  this.processorNode = new AudioWorkletNode(this.audioContext, WORKLET_PROCESSOR);
@@ -1332,17 +1332,17 @@ var Streamer = /*#__PURE__*/function () {
1332
1332
  }
1333
1333
  }
1334
1334
  };
1335
- _context8.next = 18;
1335
+ _context7.next = 18;
1336
1336
  break;
1337
1337
  case 15:
1338
- _context8.prev = 15;
1339
- _context8.t0 = _context8["catch"](0);
1340
- console.error("Error: Unable to create worklet node: ", _context8.t0);
1338
+ _context7.prev = 15;
1339
+ _context7.t0 = _context7["catch"](0);
1340
+ console.error("Error: Unable to create worklet node: ", _context7.t0);
1341
1341
  case 18:
1342
1342
  case "end":
1343
- return _context8.stop();
1343
+ return _context7.stop();
1344
1344
  }
1345
- }, _callee8, this, [[0, 15]]);
1345
+ }, _callee7, this, [[0, 15]]);
1346
1346
  }));
1347
1347
  function createProcessorNode() {
1348
1348
  return _createProcessorNode.apply(this, arguments);
@@ -1352,25 +1352,25 @@ var Streamer = /*#__PURE__*/function () {
1352
1352
  }, {
1353
1353
  key: "PauseStream",
1354
1354
  value: function () {
1355
- var _PauseStream = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee9() {
1356
- return _regeneratorRuntime().wrap(function _callee9$(_context9) {
1357
- while (1) switch (_context9.prev = _context9.next) {
1355
+ var _PauseStream = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee8() {
1356
+ return _regeneratorRuntime().wrap(function _callee8$(_context8) {
1357
+ while (1) switch (_context8.prev = _context8.next) {
1358
1358
  case 0:
1359
1359
  if (!(this.audioContext.state == "running")) {
1360
- _context9.next = 9;
1360
+ _context8.next = 9;
1361
1361
  break;
1362
1362
  }
1363
1363
  if (this.shouldPreIntialiseRecorder) {
1364
- _context9.next = 8;
1364
+ _context8.next = 8;
1365
1365
  break;
1366
1366
  }
1367
- _context9.next = 4;
1367
+ _context8.next = 4;
1368
1368
  return this.audioContext.suspend();
1369
1369
  case 4:
1370
1370
  this.log("Stream paused...");
1371
1371
  // message sent to worklet-thread
1372
1372
  this.processorNode.port.postMessage(PAUSE_MSG);
1373
- _context9.next = 9;
1373
+ _context8.next = 9;
1374
1374
  break;
1375
1375
  case 8:
1376
1376
  this.processorNode.port.postMessage(PAUSE_SOCKET_MSG);
@@ -1380,9 +1380,9 @@ var Streamer = /*#__PURE__*/function () {
1380
1380
  this.isStreaming = false;
1381
1381
  case 12:
1382
1382
  case "end":
1383
- return _context9.stop();
1383
+ return _context8.stop();
1384
1384
  }
1385
- }, _callee9, this);
1385
+ }, _callee8, this);
1386
1386
  }));
1387
1387
  function PauseStream() {
1388
1388
  return _PauseStream.apply(this, arguments);
@@ -1392,52 +1392,52 @@ var Streamer = /*#__PURE__*/function () {
1392
1392
  }, {
1393
1393
  key: "ResumeStream",
1394
1394
  value: function () {
1395
- var _ResumeStream = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee10() {
1395
+ var _ResumeStream = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee9() {
1396
1396
  var _this$audioStream3, _this$audioStream3$ge;
1397
1397
  var track, trackEnded;
1398
- return _regeneratorRuntime().wrap(function _callee10$(_context10) {
1399
- while (1) switch (_context10.prev = _context10.next) {
1398
+ return _regeneratorRuntime().wrap(function _callee9$(_context9) {
1399
+ while (1) switch (_context9.prev = _context9.next) {
1400
1400
  case 0:
1401
1401
  track = (_this$audioStream3 = this.audioStream) === null || _this$audioStream3 === void 0 || (_this$audioStream3$ge = _this$audioStream3.getAudioTracks) === null || _this$audioStream3$ge === void 0 ? void 0 : _this$audioStream3$ge.call(_this$audioStream3)[0];
1402
1402
  trackEnded = !track || track.readyState === "ended";
1403
1403
  if (!(trackEnded || this.micNeedsRecovery)) {
1404
- _context10.next = 5;
1404
+ _context9.next = 5;
1405
1405
  break;
1406
1406
  }
1407
- _context10.next = 5;
1407
+ _context9.next = 5;
1408
1408
  return this.recoverAudioGraph();
1409
1409
  case 5:
1410
1410
  if (!this.IsMicrophoneMuted) {
1411
- _context10.next = 11;
1411
+ _context9.next = 11;
1412
1412
  break;
1413
1413
  }
1414
1414
  if (!(this.audioContext && this.audioContext.state === "running")) {
1415
- _context10.next = 9;
1415
+ _context9.next = 9;
1416
1416
  break;
1417
1417
  }
1418
- _context10.next = 9;
1418
+ _context9.next = 9;
1419
1419
  return this.audioContext.suspend();
1420
1420
  case 9:
1421
1421
  this.onError(JSON.stringify({
1422
1422
  Type: "ERROR",
1423
1423
  Data: "Microphone is muted."
1424
1424
  }));
1425
- return _context10.abrupt("return");
1425
+ return _context9.abrupt("return");
1426
1426
  case 11:
1427
1427
  if (!this.shouldPreIntialiseRecorder) {
1428
- _context10.next = 16;
1428
+ _context9.next = 16;
1429
1429
  break;
1430
1430
  }
1431
1431
  this.processorNode.port.postMessage(RESUME_SOCKET_MSG);
1432
1432
  this.executor.Send(RESUME_SOCKET_MSG);
1433
- _context10.next = 20;
1433
+ _context9.next = 20;
1434
1434
  break;
1435
1435
  case 16:
1436
1436
  if (!(this.audioContext.state === "suspended")) {
1437
- _context10.next = 19;
1437
+ _context9.next = 19;
1438
1438
  break;
1439
1439
  }
1440
- _context10.next = 19;
1440
+ _context9.next = 19;
1441
1441
  return this.audioContext.resume();
1442
1442
  case 19:
1443
1443
  this.log("Stream resumed...");
@@ -1447,9 +1447,9 @@ var Streamer = /*#__PURE__*/function () {
1447
1447
  this.isStreaming = true;
1448
1448
  case 23:
1449
1449
  case "end":
1450
- return _context10.stop();
1450
+ return _context9.stop();
1451
1451
  }
1452
- }, _callee10, this);
1452
+ }, _callee9, this);
1453
1453
  }));
1454
1454
  function ResumeStream() {
1455
1455
  return _ResumeStream.apply(this, arguments);
@@ -1479,17 +1479,17 @@ var Streamer = /*#__PURE__*/function () {
1479
1479
  }, {
1480
1480
  key: "StopStream",
1481
1481
  value: function () {
1482
- var _StopStream = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee11(shouldSendEOS) {
1482
+ var _StopStream = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee10(shouldSendEOS) {
1483
1483
  var _this$audioContext, _this$audioStream5, _this$audioStream5$ge, _this$audioStream6, _this$audioStream6$ge, _this$processorNode;
1484
1484
  var _this$audioContext2, track;
1485
- return _regeneratorRuntime().wrap(function _callee11$(_context11) {
1486
- while (1) switch (_context11.prev = _context11.next) {
1485
+ return _regeneratorRuntime().wrap(function _callee10$(_context10) {
1486
+ while (1) switch (_context10.prev = _context10.next) {
1487
1487
  case 0:
1488
1488
  if (!(((_this$audioContext = this.audioContext) === null || _this$audioContext === void 0 ? void 0 : _this$audioContext.state) !== "suspended")) {
1489
- _context11.next = 4;
1489
+ _context10.next = 4;
1490
1490
  break;
1491
1491
  }
1492
- _context11.next = 3;
1492
+ _context10.next = 3;
1493
1493
  return (_this$audioContext2 = this.audioContext) === null || _this$audioContext2 === void 0 ? void 0 : _this$audioContext2.suspend();
1494
1494
  case 3:
1495
1495
  this.onStateChanged(false);
@@ -1519,9 +1519,9 @@ var Streamer = /*#__PURE__*/function () {
1519
1519
  (_this$processorNode = this.processorNode) === null || _this$processorNode === void 0 || (_this$processorNode = _this$processorNode.port) === null || _this$processorNode === void 0 || _this$processorNode.postMessage(STOP_MSG);
1520
1520
  case 14:
1521
1521
  case "end":
1522
- return _context11.stop();
1522
+ return _context10.stop();
1523
1523
  }
1524
- }, _callee11, this);
1524
+ }, _callee10, this);
1525
1525
  }));
1526
1526
  function StopStream(_x5) {
1527
1527
  return _StopStream.apply(this, arguments);
@@ -1531,20 +1531,20 @@ var Streamer = /*#__PURE__*/function () {
1531
1531
  }, {
1532
1532
  key: "recoverAudioGraph",
1533
1533
  value: function () {
1534
- var _recoverAudioGraph = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee12() {
1534
+ var _recoverAudioGraph = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee11() {
1535
1535
  var _this4 = this;
1536
- var _this$audioStream7, _this$audioStream8, _this$audioStream8$ge, _recoveredTrack$getSe, _recoveredTrack$getSe2, _this$source, _this$processorNode2, recoveredTrack, recoveredDeviceId, isOriginalDeviceReconnected;
1537
- return _regeneratorRuntime().wrap(function _callee12$(_context12) {
1538
- while (1) switch (_context12.prev = _context12.next) {
1536
+ var _this$audioStream7, _this$audioStream8, _this$audioStream8$ge, _recoveredTrack$getSe, _recoveredTrack$getSe2, _recoveredTrack$label, _this$source, _this$processorNode2, recoveredTrack, recoveredDeviceId, recoveredLabel;
1537
+ return _regeneratorRuntime().wrap(function _callee11$(_context11) {
1538
+ while (1) switch (_context11.prev = _context11.next) {
1539
1539
  case 0:
1540
1540
  if (!this.isRecovering) {
1541
- _context12.next = 2;
1541
+ _context11.next = 2;
1542
1542
  break;
1543
1543
  }
1544
- return _context12.abrupt("return");
1544
+ return _context11.abrupt("return");
1545
1545
  case 2:
1546
1546
  this.isRecovering = true;
1547
- _context12.prev = 3;
1547
+ _context11.prev = 3;
1548
1548
  // this.log("Recovering audio graph...");
1549
1549
 
1550
1550
  // stop old track
@@ -1565,11 +1565,11 @@ var Streamer = /*#__PURE__*/function () {
1565
1565
  } catch (_unused5) {}
1566
1566
 
1567
1567
  // recreate media stream ONLY
1568
- _context12.next = 9;
1568
+ _context11.next = 9;
1569
1569
  return this.createMediaStreamSourceNode();
1570
1570
  case 9:
1571
1571
  if (this.source) {
1572
- _context12.next = 11;
1572
+ _context11.next = 11;
1573
1573
  break;
1574
1574
  }
1575
1575
  throw new Error("Failed to recreate source");
@@ -1579,13 +1579,13 @@ var Streamer = /*#__PURE__*/function () {
1579
1579
  this.audioContext = new AudioContext();
1580
1580
  }
1581
1581
  if (!(this.audioContext.state === "suspended")) {
1582
- _context12.next = 15;
1582
+ _context11.next = 15;
1583
1583
  break;
1584
1584
  }
1585
- _context12.next = 15;
1585
+ _context11.next = 15;
1586
1586
  return this.audioContext.resume();
1587
1587
  case 15:
1588
- _context12.next = 17;
1588
+ _context11.next = 17;
1589
1589
  return this.createProcessorNode();
1590
1590
  case 17:
1591
1591
  // reconnect graph
@@ -1595,32 +1595,41 @@ var Streamer = /*#__PURE__*/function () {
1595
1595
  // AudioContext immediately after rebuilding the graph so the mic does
1596
1596
  // not start capturing audio without an explicit start/resume call.
1597
1597
  if (!(!this.isStreaming || this.isPaused)) {
1598
- _context12.next = 22;
1598
+ _context11.next = 22;
1599
1599
  break;
1600
1600
  }
1601
1601
  if (!(this.audioContext.state === "running")) {
1602
- _context12.next = 22;
1602
+ _context11.next = 22;
1603
1603
  break;
1604
1604
  }
1605
- _context12.next = 22;
1605
+ _context11.next = 22;
1606
1606
  return this.audioContext.suspend();
1607
1607
  case 22:
1608
1608
  this.micNeedsRecovery = false;
1609
1609
 
1610
1610
  // Determine whether the recovered stream is the original recording device.
1611
+ // On Windows, deviceId alone is unreliable (it can be a generic alias like
1612
+ // "default" that stays the same even after a USB mic is unplugged and a
1613
+ // fallback device takes over). We therefore require BOTH deviceId AND label
1614
+ // to match before declaring the original device reconnected.
1611
1615
  recoveredTrack = (_this$audioStream8 = this.audioStream) === null || _this$audioStream8 === void 0 || (_this$audioStream8$ge = _this$audioStream8.getAudioTracks) === null || _this$audioStream8$ge === void 0 ? void 0 : _this$audioStream8$ge.call(_this$audioStream8)[0];
1612
- recoveredDeviceId = (_recoveredTrack$getSe = recoveredTrack === null || recoveredTrack === void 0 || (_recoveredTrack$getSe2 = recoveredTrack.getSettings) === null || _recoveredTrack$getSe2 === void 0 || (_recoveredTrack$getSe2 = _recoveredTrack$getSe2.call(recoveredTrack)) === null || _recoveredTrack$getSe2 === void 0 ? void 0 : _recoveredTrack$getSe2.deviceId) !== null && _recoveredTrack$getSe !== void 0 ? _recoveredTrack$getSe : recoveredTrack === null || recoveredTrack === void 0 ? void 0 : recoveredTrack.label;
1613
- isOriginalDeviceReconnected = !!this._originalDeviceId && recoveredDeviceId === this._originalDeviceId;
1614
- console.log("Recovered device ID: ".concat(recoveredDeviceId, ", Original device ID: ").concat(this._originalDeviceId, ", Is original device reconnected: ").concat(isOriginalDeviceReconnected));
1615
- // Notify the client that a replacement mic is ready — but only when the
1616
- // recorder is in the paused state. In stopped state the client manages
1617
- // the flow themselves; in actively-streaming state recovery is seamless
1618
- // and no notification is needed.
1619
- // Fire if the mic is unmuted, OR if the original recording device was
1620
- // reconnected (even while muted — the client should know it is back).
1621
- // Do NOT fire if a different fallback device is muted, preserving the
1622
- // original intent of the !IsMicrophoneMuted guard.
1623
- if (this.isPaused && (!this.IsMicrophoneMuted || isOriginalDeviceReconnected)) {
1616
+ recoveredDeviceId = (_recoveredTrack$getSe = recoveredTrack === null || recoveredTrack === void 0 || (_recoveredTrack$getSe2 = recoveredTrack.getSettings) === null || _recoveredTrack$getSe2 === void 0 || (_recoveredTrack$getSe2 = _recoveredTrack$getSe2.call(recoveredTrack)) === null || _recoveredTrack$getSe2 === void 0 ? void 0 : _recoveredTrack$getSe2.deviceId) !== null && _recoveredTrack$getSe !== void 0 ? _recoveredTrack$getSe : "";
1617
+ recoveredLabel = (_recoveredTrack$label = recoveredTrack === null || recoveredTrack === void 0 ? void 0 : recoveredTrack.label) !== null && _recoveredTrack$label !== void 0 ? _recoveredTrack$label : "";
1618
+ !!this._originalDeviceId && recoveredDeviceId === this._originalDeviceId && !!this._originalDeviceLabel && recoveredLabel === this._originalDeviceLabel;
1619
+ console.log("Recovered device ID: \"".concat(recoveredDeviceId, "\", label: \"").concat(recoveredLabel, "\""));
1620
+ // Fire onMicReady when:
1621
+ // the recorder is paused, AND
1622
+ // the mic is genuinely usable (!IsMicrophoneMuted), OR the exact
1623
+ // original device (same id + same label) was reconnected even if
1624
+ // it is currently in a muted/ended state.
1625
+ // This means:
1626
+ // - Unplug with no real replacement → IsMicrophoneMuted=true,
1627
+ // isOriginalDeviceReconnected=false no callback (correct).
1628
+ // - Unplug, fallback to another muted mic → IsMicrophoneMuted=true,
1629
+ // label differs → isOriginalDeviceReconnected=false → no callback.
1630
+ // - Original USB mic plugged back in while muted → same id+label
1631
+ // → isOriginalDeviceReconnected=true → callback fires (correct).
1632
+ if (this.isPaused && !this.IsMicrophoneMuted) {
1624
1633
  if (this.onMicReady && !this._micReadyFired) {
1625
1634
  this._micReadyFired = true;
1626
1635
  setTimeout(function () {
@@ -1630,12 +1639,12 @@ var Streamer = /*#__PURE__*/function () {
1630
1639
  }
1631
1640
  }
1632
1641
  this.log("Audio graph recovery completed");
1633
- _context12.next = 34;
1642
+ _context11.next = 35;
1634
1643
  break;
1635
- case 31:
1636
- _context12.prev = 31;
1637
- _context12.t0 = _context12["catch"](3);
1638
- console.error("Recovery failed", _context12.t0);
1644
+ case 32:
1645
+ _context11.prev = 32;
1646
+ _context11.t0 = _context11["catch"](3);
1647
+ console.error("Recovery failed", _context11.t0);
1639
1648
 
1640
1649
  // this.onError(
1641
1650
  // JSON.stringify({
@@ -1643,15 +1652,15 @@ var Streamer = /*#__PURE__*/function () {
1643
1652
  // Data: "Failed to recover microphone",
1644
1653
  // }),
1645
1654
  // );
1646
- case 34:
1647
- _context12.prev = 34;
1655
+ case 35:
1656
+ _context11.prev = 35;
1648
1657
  this.isRecovering = false;
1649
- return _context12.finish(34);
1650
- case 37:
1658
+ return _context11.finish(35);
1659
+ case 38:
1651
1660
  case "end":
1652
- return _context12.stop();
1661
+ return _context11.stop();
1653
1662
  }
1654
- }, _callee12, this, [[3, 31, 34, 37]]);
1663
+ }, _callee11, this, [[3, 32, 35, 38]]);
1655
1664
  }));
1656
1665
  function recoverAudioGraph() {
1657
1666
  return _recoverAudioGraph.apply(this, arguments);
@@ -1782,7 +1791,7 @@ function isDeviceProducingAudio(_x6) {
1782
1791
  * @returns {Promise<string|null>}
1783
1792
  */
1784
1793
  function _isDeviceProducingAudio() {
1785
- _isDeviceProducingAudio = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee13(deviceId) {
1794
+ _isDeviceProducingAudio = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee12(deviceId) {
1786
1795
  var durationMs,
1787
1796
  silenceThreshold,
1788
1797
  stream,
@@ -1792,16 +1801,16 @@ function _isDeviceProducingAudio() {
1792
1801
  bufferLength,
1793
1802
  dataArray,
1794
1803
  _stream,
1795
- _args13 = arguments;
1796
- return _regeneratorRuntime().wrap(function _callee13$(_context13) {
1797
- while (1) switch (_context13.prev = _context13.next) {
1804
+ _args12 = arguments;
1805
+ return _regeneratorRuntime().wrap(function _callee12$(_context12) {
1806
+ while (1) switch (_context12.prev = _context12.next) {
1798
1807
  case 0:
1799
- durationMs = _args13.length > 1 && _args13[1] !== undefined ? _args13[1] : 350;
1800
- silenceThreshold = _args13.length > 2 && _args13[2] !== undefined ? _args13[2] : 0.01;
1808
+ durationMs = _args12.length > 1 && _args12[1] !== undefined ? _args12[1] : 350;
1809
+ silenceThreshold = _args12.length > 2 && _args12[2] !== undefined ? _args12[2] : 0.01;
1801
1810
  stream = null;
1802
1811
  audioCtx = null;
1803
- _context13.prev = 4;
1804
- _context13.next = 7;
1812
+ _context12.prev = 4;
1813
+ _context12.next = 7;
1805
1814
  return navigator.mediaDevices.getUserMedia({
1806
1815
  audio: {
1807
1816
  deviceId: {
@@ -1811,7 +1820,7 @@ function _isDeviceProducingAudio() {
1811
1820
  }
1812
1821
  });
1813
1822
  case 7:
1814
- stream = _context13.sent;
1823
+ stream = _context12.sent;
1815
1824
  audioCtx = new AudioContext();
1816
1825
  source = audioCtx.createMediaStreamSource(stream);
1817
1826
  analyser = audioCtx.createAnalyser();
@@ -1819,7 +1828,7 @@ function _isDeviceProducingAudio() {
1819
1828
  source.connect(analyser);
1820
1829
  bufferLength = analyser.frequencyBinCount;
1821
1830
  dataArray = new Float32Array(bufferLength);
1822
- _context13.next = 17;
1831
+ _context12.next = 17;
1823
1832
  return new Promise(function (resolve) {
1824
1833
  var maxRms = 0;
1825
1834
  var startTime = performance.now();
@@ -1840,25 +1849,25 @@ function _isDeviceProducingAudio() {
1840
1849
  requestAnimationFrame(sample);
1841
1850
  });
1842
1851
  case 17:
1843
- return _context13.abrupt("return", _context13.sent);
1852
+ return _context12.abrupt("return", _context12.sent);
1844
1853
  case 20:
1845
- _context13.prev = 20;
1846
- _context13.t0 = _context13["catch"](4);
1847
- return _context13.abrupt("return", false);
1854
+ _context12.prev = 20;
1855
+ _context12.t0 = _context12["catch"](4);
1856
+ return _context12.abrupt("return", false);
1848
1857
  case 23:
1849
- _context13.prev = 23;
1858
+ _context12.prev = 23;
1850
1859
  (_stream = stream) === null || _stream === void 0 || _stream.getTracks().forEach(function (t) {
1851
1860
  return t.stop();
1852
1861
  });
1853
1862
  if (audioCtx && audioCtx.state !== "closed") {
1854
1863
  audioCtx.close().catch(function () {});
1855
1864
  }
1856
- return _context13.finish(23);
1865
+ return _context12.finish(23);
1857
1866
  case 27:
1858
1867
  case "end":
1859
- return _context13.stop();
1868
+ return _context12.stop();
1860
1869
  }
1861
- }, _callee13, null, [[4, 20, 23, 27]]);
1870
+ }, _callee12, null, [[4, 20, 23, 27]]);
1862
1871
  }));
1863
1872
  return _isDeviceProducingAudio.apply(this, arguments);
1864
1873
  }
@@ -1866,70 +1875,70 @@ function findFirstActiveAudioDevice(_x7) {
1866
1875
  return _findFirstActiveAudioDevice.apply(this, arguments);
1867
1876
  }
1868
1877
  function _findFirstActiveAudioDevice() {
1869
- _findFirstActiveAudioDevice = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee14(excludeDeviceId) {
1878
+ _findFirstActiveAudioDevice = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee13(excludeDeviceId) {
1870
1879
  var devices,
1871
1880
  audioInputs,
1872
1881
  _iterator,
1873
1882
  _step,
1874
1883
  device,
1875
1884
  active;
1876
- return _regeneratorRuntime().wrap(function _callee14$(_context14) {
1877
- while (1) switch (_context14.prev = _context14.next) {
1885
+ return _regeneratorRuntime().wrap(function _callee13$(_context13) {
1886
+ while (1) switch (_context13.prev = _context13.next) {
1878
1887
  case 0:
1879
- _context14.prev = 1;
1880
- _context14.next = 4;
1888
+ _context13.prev = 1;
1889
+ _context13.next = 4;
1881
1890
  return navigator.mediaDevices.enumerateDevices();
1882
1891
  case 4:
1883
- devices = _context14.sent;
1884
- _context14.next = 10;
1892
+ devices = _context13.sent;
1893
+ _context13.next = 10;
1885
1894
  break;
1886
1895
  case 7:
1887
- _context14.prev = 7;
1888
- _context14.t0 = _context14["catch"](1);
1889
- return _context14.abrupt("return", null);
1896
+ _context13.prev = 7;
1897
+ _context13.t0 = _context13["catch"](1);
1898
+ return _context13.abrupt("return", null);
1890
1899
  case 10:
1891
1900
  audioInputs = devices.filter(function (d) {
1892
1901
  return d.kind === "audioinput" && d.deviceId !== excludeDeviceId;
1893
1902
  });
1894
1903
  _iterator = _createForOfIteratorHelper(audioInputs);
1895
- _context14.prev = 12;
1904
+ _context13.prev = 12;
1896
1905
  _iterator.s();
1897
1906
  case 14:
1898
1907
  if ((_step = _iterator.n()).done) {
1899
- _context14.next = 23;
1908
+ _context13.next = 23;
1900
1909
  break;
1901
1910
  }
1902
1911
  device = _step.value;
1903
- _context14.next = 18;
1912
+ _context13.next = 18;
1904
1913
  return isDeviceProducingAudio(device.deviceId);
1905
1914
  case 18:
1906
- active = _context14.sent;
1915
+ active = _context13.sent;
1907
1916
  if (!active) {
1908
- _context14.next = 21;
1917
+ _context13.next = 21;
1909
1918
  break;
1910
1919
  }
1911
- return _context14.abrupt("return", device.deviceId);
1920
+ return _context13.abrupt("return", device.deviceId);
1912
1921
  case 21:
1913
- _context14.next = 14;
1922
+ _context13.next = 14;
1914
1923
  break;
1915
1924
  case 23:
1916
- _context14.next = 28;
1925
+ _context13.next = 28;
1917
1926
  break;
1918
1927
  case 25:
1919
- _context14.prev = 25;
1920
- _context14.t1 = _context14["catch"](12);
1921
- _iterator.e(_context14.t1);
1928
+ _context13.prev = 25;
1929
+ _context13.t1 = _context13["catch"](12);
1930
+ _iterator.e(_context13.t1);
1922
1931
  case 28:
1923
- _context14.prev = 28;
1932
+ _context13.prev = 28;
1924
1933
  _iterator.f();
1925
- return _context14.finish(28);
1934
+ return _context13.finish(28);
1926
1935
  case 31:
1927
- return _context14.abrupt("return", null);
1936
+ return _context13.abrupt("return", null);
1928
1937
  case 32:
1929
1938
  case "end":
1930
- return _context14.stop();
1939
+ return _context13.stop();
1931
1940
  }
1932
- }, _callee14, null, [[1, 7], [12, 25, 28, 31]]);
1941
+ }, _callee13, null, [[1, 7], [12, 25, 28, 31]]);
1933
1942
  }));
1934
1943
  return _findFirstActiveAudioDevice.apply(this, arguments);
1935
1944
  }