@iam-protocol/pulse-sdk 0.3.4 → 0.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -54,6 +54,10 @@ interface CaptureOptions {
54
54
  maxDurationMs?: number;
55
55
  /** Called with RMS audio level (0-1) on each buffer during audio capture (~4x per second). */
56
56
  onAudioLevel?: (rms: number) => void;
57
+ /** Pre-acquired MediaStream. If provided, captureAudio skips getUserMedia. */
58
+ stream?: MediaStream;
59
+ /** If true, captureMotion skips requestMotionPermission (already acquired). */
60
+ permissionGranted?: boolean;
57
61
  }
58
62
  /** Stage of a capture session */
59
63
  type CaptureStage = "audio" | "motion" | "touch";
@@ -127,6 +131,7 @@ declare class PulseSession {
127
131
  startMotion(): Promise<void>;
128
132
  stopMotion(): Promise<MotionSample[]>;
129
133
  skipMotion(): void;
134
+ isMotionCapturing(): boolean;
130
135
  startTouch(): Promise<void>;
131
136
  stopTouch(): Promise<TouchSample[]>;
132
137
  skipTouch(): void;
package/dist/index.d.ts CHANGED
@@ -54,6 +54,10 @@ interface CaptureOptions {
54
54
  maxDurationMs?: number;
55
55
  /** Called with RMS audio level (0-1) on each buffer during audio capture (~4x per second). */
56
56
  onAudioLevel?: (rms: number) => void;
57
+ /** Pre-acquired MediaStream. If provided, captureAudio skips getUserMedia. */
58
+ stream?: MediaStream;
59
+ /** If true, captureMotion skips requestMotionPermission (already acquired). */
60
+ permissionGranted?: boolean;
57
61
  }
58
62
  /** Stage of a capture session */
59
63
  type CaptureStage = "audio" | "motion" | "touch";
@@ -127,6 +131,7 @@ declare class PulseSession {
127
131
  startMotion(): Promise<void>;
128
132
  stopMotion(): Promise<MotionSample[]>;
129
133
  skipMotion(): void;
134
+ isMotionCapturing(): boolean;
130
135
  startTouch(): Promise<void>;
131
136
  stopTouch(): Promise<TouchSample[]>;
132
137
  skipTouch(): void;
package/dist/index.js CHANGED
@@ -109,9 +109,10 @@ async function captureAudio(options = {}) {
109
109
  signal,
110
110
  minDurationMs = MIN_CAPTURE_MS,
111
111
  maxDurationMs = MAX_CAPTURE_MS,
112
- onAudioLevel
112
+ onAudioLevel,
113
+ stream: preAcquiredStream
113
114
  } = options;
114
- const stream = await navigator.mediaDevices.getUserMedia({
115
+ const stream = preAcquiredStream ?? await navigator.mediaDevices.getUserMedia({
115
116
  audio: {
116
117
  sampleRate: TARGET_SAMPLE_RATE,
117
118
  channelCount: 1,
@@ -121,6 +122,7 @@ async function captureAudio(options = {}) {
121
122
  }
122
123
  });
123
124
  const ctx = new AudioContext({ sampleRate: TARGET_SAMPLE_RATE });
125
+ await ctx.resume();
124
126
  const capturedSampleRate = ctx.sampleRate;
125
127
  const source = ctx.createMediaStreamSource(stream);
126
128
  const chunks = [];
@@ -197,7 +199,7 @@ async function captureMotion(options = {}) {
197
199
  minDurationMs = MIN_CAPTURE_MS,
198
200
  maxDurationMs = MAX_CAPTURE_MS
199
201
  } = options;
200
- const hasPermission = await requestMotionPermission();
202
+ const hasPermission = options.permissionGranted ?? await requestMotionPermission();
201
203
  if (!hasPermission) return [];
202
204
  const samples = [];
203
205
  const startTime = performance.now();
@@ -1578,12 +1580,25 @@ var PulseSession = class {
1578
1580
  async startAudio(onAudioLevel) {
1579
1581
  if (this.audioStageState !== "idle")
1580
1582
  throw new Error("Audio capture already started");
1583
+ const stream = await navigator.mediaDevices.getUserMedia({
1584
+ audio: {
1585
+ sampleRate: 16e3,
1586
+ channelCount: 1,
1587
+ echoCancellation: false,
1588
+ noiseSuppression: false,
1589
+ autoGainControl: false
1590
+ }
1591
+ });
1581
1592
  this.audioStageState = "capturing";
1582
1593
  this.audioController = new AbortController();
1583
1594
  this.audioPromise = captureAudio({
1584
1595
  signal: this.audioController.signal,
1585
- onAudioLevel
1586
- }).catch(() => null);
1596
+ onAudioLevel,
1597
+ stream
1598
+ }).catch(() => {
1599
+ stream.getTracks().forEach((t) => t.stop());
1600
+ return null;
1601
+ });
1587
1602
  }
1588
1603
  async stopAudio() {
1589
1604
  if (this.audioStageState !== "capturing")
@@ -1599,10 +1614,16 @@ var PulseSession = class {
1599
1614
  async startMotion() {
1600
1615
  if (this.motionStageState !== "idle")
1601
1616
  throw new Error("Motion capture already started");
1617
+ const hasPermission = await requestMotionPermission();
1618
+ if (!hasPermission) {
1619
+ this.motionStageState = "skipped";
1620
+ return;
1621
+ }
1602
1622
  this.motionStageState = "capturing";
1603
1623
  this.motionController = new AbortController();
1604
1624
  this.motionPromise = captureMotion({
1605
- signal: this.motionController.signal
1625
+ signal: this.motionController.signal,
1626
+ permissionGranted: true
1606
1627
  }).catch(() => []);
1607
1628
  }
1608
1629
  async stopMotion() {
@@ -1618,6 +1639,9 @@ var PulseSession = class {
1618
1639
  throw new Error("Motion capture already started");
1619
1640
  this.motionStageState = "skipped";
1620
1641
  }
1642
+ isMotionCapturing() {
1643
+ return this.motionStageState === "capturing";
1644
+ }
1621
1645
  // --- Touch ---
1622
1646
  async startTouch() {
1623
1647
  if (this.touchStageState !== "idle")
@@ -1700,12 +1724,13 @@ var PulseSDK = class {
1700
1724
  }
1701
1725
  try {
1702
1726
  await session.startMotion();
1727
+ } catch {
1728
+ }
1729
+ if (session.isMotionCapturing()) {
1703
1730
  stopPromises.push(
1704
1731
  new Promise((r) => setTimeout(r, DEFAULT_CAPTURE_MS)).then(() => session.stopMotion()).then(() => {
1705
1732
  })
1706
1733
  );
1707
- } catch {
1708
- session.skipMotion();
1709
1734
  }
1710
1735
  if (touchElement) {
1711
1736
  try {