testaugnitorecorder4 1.1.11 → 1.1.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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, isOriginalDeviceReconnected;
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,31 +1595,38 @@ 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.
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
+ isOriginalDeviceReconnected = !!this._originalDeviceId && recoveredDeviceId === this._originalDeviceId && !!this._originalDeviceLabel && recoveredLabel === this._originalDeviceLabel; // Fire onMicReady when:
1619
+ // the recorder is paused, AND
1620
+ // the mic is genuinely usable (!IsMicrophoneMuted), OR the exact
1621
+ // original device (same id + same label) was reconnected even if
1622
+ // it is currently in a muted/ended state.
1623
+ // This means:
1624
+ // - Unplug with no real replacement IsMicrophoneMuted=true,
1625
+ // isOriginalDeviceReconnected=false no callback (correct).
1626
+ // - Unplug, fallback to another muted mic → IsMicrophoneMuted=true,
1627
+ // label differs → isOriginalDeviceReconnected=false → no callback.
1628
+ // - Original USB mic plugged back in while muted → same id+label
1629
+ // → isOriginalDeviceReconnected=true → callback fires (correct).
1623
1630
  if (this.isPaused && (!this.IsMicrophoneMuted || isOriginalDeviceReconnected)) {
1624
1631
  if (this.onMicReady && !this._micReadyFired) {
1625
1632
  this._micReadyFired = true;
@@ -1630,12 +1637,12 @@ var Streamer = /*#__PURE__*/function () {
1630
1637
  }
1631
1638
  }
1632
1639
  this.log("Audio graph recovery completed");
1633
- _context12.next = 34;
1640
+ _context11.next = 34;
1634
1641
  break;
1635
1642
  case 31:
1636
- _context12.prev = 31;
1637
- _context12.t0 = _context12["catch"](3);
1638
- console.error("Recovery failed", _context12.t0);
1643
+ _context11.prev = 31;
1644
+ _context11.t0 = _context11["catch"](3);
1645
+ console.error("Recovery failed", _context11.t0);
1639
1646
 
1640
1647
  // this.onError(
1641
1648
  // JSON.stringify({
@@ -1644,14 +1651,14 @@ var Streamer = /*#__PURE__*/function () {
1644
1651
  // }),
1645
1652
  // );
1646
1653
  case 34:
1647
- _context12.prev = 34;
1654
+ _context11.prev = 34;
1648
1655
  this.isRecovering = false;
1649
- return _context12.finish(34);
1656
+ return _context11.finish(34);
1650
1657
  case 37:
1651
1658
  case "end":
1652
- return _context12.stop();
1659
+ return _context11.stop();
1653
1660
  }
1654
- }, _callee12, this, [[3, 31, 34, 37]]);
1661
+ }, _callee11, this, [[3, 31, 34, 37]]);
1655
1662
  }));
1656
1663
  function recoverAudioGraph() {
1657
1664
  return _recoverAudioGraph.apply(this, arguments);
@@ -1782,7 +1789,7 @@ function isDeviceProducingAudio(_x6) {
1782
1789
  * @returns {Promise<string|null>}
1783
1790
  */
1784
1791
  function _isDeviceProducingAudio() {
1785
- _isDeviceProducingAudio = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee13(deviceId) {
1792
+ _isDeviceProducingAudio = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee12(deviceId) {
1786
1793
  var durationMs,
1787
1794
  silenceThreshold,
1788
1795
  stream,
@@ -1792,16 +1799,16 @@ function _isDeviceProducingAudio() {
1792
1799
  bufferLength,
1793
1800
  dataArray,
1794
1801
  _stream,
1795
- _args13 = arguments;
1796
- return _regeneratorRuntime().wrap(function _callee13$(_context13) {
1797
- while (1) switch (_context13.prev = _context13.next) {
1802
+ _args12 = arguments;
1803
+ return _regeneratorRuntime().wrap(function _callee12$(_context12) {
1804
+ while (1) switch (_context12.prev = _context12.next) {
1798
1805
  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;
1806
+ durationMs = _args12.length > 1 && _args12[1] !== undefined ? _args12[1] : 350;
1807
+ silenceThreshold = _args12.length > 2 && _args12[2] !== undefined ? _args12[2] : 0.01;
1801
1808
  stream = null;
1802
1809
  audioCtx = null;
1803
- _context13.prev = 4;
1804
- _context13.next = 7;
1810
+ _context12.prev = 4;
1811
+ _context12.next = 7;
1805
1812
  return navigator.mediaDevices.getUserMedia({
1806
1813
  audio: {
1807
1814
  deviceId: {
@@ -1811,7 +1818,7 @@ function _isDeviceProducingAudio() {
1811
1818
  }
1812
1819
  });
1813
1820
  case 7:
1814
- stream = _context13.sent;
1821
+ stream = _context12.sent;
1815
1822
  audioCtx = new AudioContext();
1816
1823
  source = audioCtx.createMediaStreamSource(stream);
1817
1824
  analyser = audioCtx.createAnalyser();
@@ -1819,7 +1826,7 @@ function _isDeviceProducingAudio() {
1819
1826
  source.connect(analyser);
1820
1827
  bufferLength = analyser.frequencyBinCount;
1821
1828
  dataArray = new Float32Array(bufferLength);
1822
- _context13.next = 17;
1829
+ _context12.next = 17;
1823
1830
  return new Promise(function (resolve) {
1824
1831
  var maxRms = 0;
1825
1832
  var startTime = performance.now();
@@ -1840,25 +1847,25 @@ function _isDeviceProducingAudio() {
1840
1847
  requestAnimationFrame(sample);
1841
1848
  });
1842
1849
  case 17:
1843
- return _context13.abrupt("return", _context13.sent);
1850
+ return _context12.abrupt("return", _context12.sent);
1844
1851
  case 20:
1845
- _context13.prev = 20;
1846
- _context13.t0 = _context13["catch"](4);
1847
- return _context13.abrupt("return", false);
1852
+ _context12.prev = 20;
1853
+ _context12.t0 = _context12["catch"](4);
1854
+ return _context12.abrupt("return", false);
1848
1855
  case 23:
1849
- _context13.prev = 23;
1856
+ _context12.prev = 23;
1850
1857
  (_stream = stream) === null || _stream === void 0 || _stream.getTracks().forEach(function (t) {
1851
1858
  return t.stop();
1852
1859
  });
1853
1860
  if (audioCtx && audioCtx.state !== "closed") {
1854
1861
  audioCtx.close().catch(function () {});
1855
1862
  }
1856
- return _context13.finish(23);
1863
+ return _context12.finish(23);
1857
1864
  case 27:
1858
1865
  case "end":
1859
- return _context13.stop();
1866
+ return _context12.stop();
1860
1867
  }
1861
- }, _callee13, null, [[4, 20, 23, 27]]);
1868
+ }, _callee12, null, [[4, 20, 23, 27]]);
1862
1869
  }));
1863
1870
  return _isDeviceProducingAudio.apply(this, arguments);
1864
1871
  }
@@ -1866,70 +1873,70 @@ function findFirstActiveAudioDevice(_x7) {
1866
1873
  return _findFirstActiveAudioDevice.apply(this, arguments);
1867
1874
  }
1868
1875
  function _findFirstActiveAudioDevice() {
1869
- _findFirstActiveAudioDevice = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee14(excludeDeviceId) {
1876
+ _findFirstActiveAudioDevice = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee13(excludeDeviceId) {
1870
1877
  var devices,
1871
1878
  audioInputs,
1872
1879
  _iterator,
1873
1880
  _step,
1874
1881
  device,
1875
1882
  active;
1876
- return _regeneratorRuntime().wrap(function _callee14$(_context14) {
1877
- while (1) switch (_context14.prev = _context14.next) {
1883
+ return _regeneratorRuntime().wrap(function _callee13$(_context13) {
1884
+ while (1) switch (_context13.prev = _context13.next) {
1878
1885
  case 0:
1879
- _context14.prev = 1;
1880
- _context14.next = 4;
1886
+ _context13.prev = 1;
1887
+ _context13.next = 4;
1881
1888
  return navigator.mediaDevices.enumerateDevices();
1882
1889
  case 4:
1883
- devices = _context14.sent;
1884
- _context14.next = 10;
1890
+ devices = _context13.sent;
1891
+ _context13.next = 10;
1885
1892
  break;
1886
1893
  case 7:
1887
- _context14.prev = 7;
1888
- _context14.t0 = _context14["catch"](1);
1889
- return _context14.abrupt("return", null);
1894
+ _context13.prev = 7;
1895
+ _context13.t0 = _context13["catch"](1);
1896
+ return _context13.abrupt("return", null);
1890
1897
  case 10:
1891
1898
  audioInputs = devices.filter(function (d) {
1892
1899
  return d.kind === "audioinput" && d.deviceId !== excludeDeviceId;
1893
1900
  });
1894
1901
  _iterator = _createForOfIteratorHelper(audioInputs);
1895
- _context14.prev = 12;
1902
+ _context13.prev = 12;
1896
1903
  _iterator.s();
1897
1904
  case 14:
1898
1905
  if ((_step = _iterator.n()).done) {
1899
- _context14.next = 23;
1906
+ _context13.next = 23;
1900
1907
  break;
1901
1908
  }
1902
1909
  device = _step.value;
1903
- _context14.next = 18;
1910
+ _context13.next = 18;
1904
1911
  return isDeviceProducingAudio(device.deviceId);
1905
1912
  case 18:
1906
- active = _context14.sent;
1913
+ active = _context13.sent;
1907
1914
  if (!active) {
1908
- _context14.next = 21;
1915
+ _context13.next = 21;
1909
1916
  break;
1910
1917
  }
1911
- return _context14.abrupt("return", device.deviceId);
1918
+ return _context13.abrupt("return", device.deviceId);
1912
1919
  case 21:
1913
- _context14.next = 14;
1920
+ _context13.next = 14;
1914
1921
  break;
1915
1922
  case 23:
1916
- _context14.next = 28;
1923
+ _context13.next = 28;
1917
1924
  break;
1918
1925
  case 25:
1919
- _context14.prev = 25;
1920
- _context14.t1 = _context14["catch"](12);
1921
- _iterator.e(_context14.t1);
1926
+ _context13.prev = 25;
1927
+ _context13.t1 = _context13["catch"](12);
1928
+ _iterator.e(_context13.t1);
1922
1929
  case 28:
1923
- _context14.prev = 28;
1930
+ _context13.prev = 28;
1924
1931
  _iterator.f();
1925
- return _context14.finish(28);
1932
+ return _context13.finish(28);
1926
1933
  case 31:
1927
- return _context14.abrupt("return", null);
1934
+ return _context13.abrupt("return", null);
1928
1935
  case 32:
1929
1936
  case "end":
1930
- return _context14.stop();
1937
+ return _context13.stop();
1931
1938
  }
1932
- }, _callee14, null, [[1, 7], [12, 25, 28, 31]]);
1939
+ }, _callee13, null, [[1, 7], [12, 25, 28, 31]]);
1933
1940
  }));
1934
1941
  return _findFirstActiveAudioDevice.apply(this, arguments);
1935
1942
  }