@sdata/web-vue 3.22.1 → 3.22.3

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.
@@ -12,7 +12,7 @@ interface UseSpeechOptions {
12
12
  export declare function useSpeech(allowSpeech: MaybeRefOrGetter<SenderAllowSpeech | undefined>, options: UseSpeechOptions): {
13
13
  available: import("vue").ComputedRef<boolean>;
14
14
  recording: import("vue").ComputedRef<boolean>;
15
- requesting: import("vue").ShallowRef<boolean, boolean>;
15
+ requesting: import("vue").ComputedRef<boolean>;
16
16
  statusText: import("vue").ComputedRef<string>;
17
17
  trigger: (breakRecording?: boolean) => Promise<void>;
18
18
  };
@@ -13,6 +13,7 @@ class SdSenderAudioProcessor extends AudioWorkletProcessor {
13
13
  this.bufferSize = Math.max(128, options.processorOptions?.bufferSize || ${DEFAULT_BUFFER_SIZE});
14
14
  this.buffer = new Float32Array(this.bufferSize);
15
15
  this.offset = 0;
16
+ this.stopped = false;
16
17
  this.port.onmessage = (event) => {
17
18
  if (event.data?.type !== 'flush') return;
18
19
  if (this.offset > 0) {
@@ -20,11 +21,13 @@ class SdSenderAudioProcessor extends AudioWorkletProcessor {
20
21
  this.port.postMessage(chunk.buffer, [chunk.buffer]);
21
22
  this.offset = 0;
22
23
  }
24
+ this.stopped = true;
23
25
  this.port.postMessage({ type: 'flushed' });
24
26
  };
25
27
  }
26
28
 
27
29
  process(inputs) {
30
+ if (this.stopped) return false;
28
31
  const channels = inputs[0];
29
32
  if (!channels?.length) return true;
30
33
  const frames = channels[0].length;
@@ -55,6 +58,7 @@ function useSpeech(allowSpeech, options) {
55
58
  const supported = shallowRef(false);
56
59
  const internalRecording = shallowRef(false);
57
60
  const requesting = shallowRef(false);
61
+ const stopping = shallowRef(false);
58
62
  const captureError = shallowRef();
59
63
  const stream = shallowRef();
60
64
  const audioContext = shallowRef();
@@ -65,7 +69,8 @@ function useSpeech(allowSpeech, options) {
65
69
  const chunks = shallowRef(0);
66
70
  const sequence = shallowRef(0);
67
71
  let generatedWorkletUrl;
68
- let resolveWorkletFlush;
72
+ let resolveWorkletStop;
73
+ let workletStopRequested = false;
69
74
  let workletReady = false;
70
75
  let disposed = false;
71
76
  const recording = computed(() => controlled.value && typeof config.value === "object" ? Boolean(config.value.recording) : internalRecording.value);
@@ -122,25 +127,36 @@ function useSpeech(allowSpeech, options) {
122
127
  else reject(errorEvent.error);
123
128
  };
124
129
  });
125
- const flushWorklet = () => new Promise((resolve) => {
130
+ const stopWorklet = () => new Promise((resolve) => {
126
131
  var _workletNode$value;
127
132
  const port = (_workletNode$value = workletNode.value) === null || _workletNode$value === void 0 ? void 0 : _workletNode$value.port;
128
- if (!port) {
133
+ if (!port || workletStopRequested) {
129
134
  resolve();
130
135
  return;
131
136
  }
132
- const timeout = window.setTimeout(resolve, 100);
133
- resolveWorkletFlush = () => {
137
+ workletStopRequested = true;
138
+ let settled = false;
139
+ const finish = () => {
140
+ if (settled) return;
141
+ settled = true;
134
142
  window.clearTimeout(timeout);
135
- resolveWorkletFlush = void 0;
143
+ resolveWorkletStop = void 0;
136
144
  resolve();
137
145
  };
146
+ const timeout = window.setTimeout(finish, 100);
147
+ resolveWorkletStop = finish;
138
148
  port.postMessage({ type: "flush" });
139
149
  });
140
150
  const releaseResources = function() {
141
151
  var _ref = _asyncToGenerator(function* () {
142
- var _workletNode$value2, _sourceNode$value, _stream$value, _audioContext$value;
143
- (_workletNode$value2 = workletNode.value) === null || _workletNode$value2 === void 0 || _workletNode$value2.disconnect();
152
+ var _sourceNode$value, _stream$value, _audioContext$value;
153
+ const currentWorkletNode = workletNode.value;
154
+ yield stopWorklet();
155
+ if (currentWorkletNode) {
156
+ currentWorkletNode.port.onmessage = null;
157
+ currentWorkletNode.port.close();
158
+ currentWorkletNode.disconnect();
159
+ }
144
160
  (_sourceNode$value = sourceNode.value) === null || _sourceNode$value === void 0 || _sourceNode$value.disconnect();
145
161
  (_stream$value = stream.value) === null || _stream$value === void 0 || _stream$value.getTracks().forEach((track) => track.stop());
146
162
  if (((_audioContext$value = audioContext.value) === null || _audioContext$value === void 0 ? void 0 : _audioContext$value.state) === "running") yield audioContext.value.suspend();
@@ -166,25 +182,30 @@ function useSpeech(allowSpeech, options) {
166
182
  }();
167
183
  const stopCapture = function() {
168
184
  var _ref3 = _asyncToGenerator(function* (reason = "manual") {
169
- var _socket$value, _config$value$onRecor, _config$value;
170
- if (!internalRecording.value) return;
171
- yield flushWorklet();
172
- const endedAt = performance.now();
173
- internalRecording.value = false;
174
- if (((_socket$value = socket.value) === null || _socket$value === void 0 ? void 0 : _socket$value.readyState) === WebSocket.OPEN && typeof config.value === "object" && config.value.sendMetadata !== false) socket.value.send(JSON.stringify({
175
- type: "end",
176
- reason
177
- }));
178
- yield releaseResources();
179
- if (typeof config.value === "object") (_config$value$onRecor = (_config$value = config.value).onRecordingChange) === null || _config$value$onRecor === void 0 || _config$value$onRecor.call(_config$value, false);
180
- options.onEnd({
181
- source: "capture",
182
- reason,
183
- startedAt: startedAt.value,
184
- endedAt,
185
- duration: endedAt - startedAt.value,
186
- chunks: chunks.value
187
- });
185
+ if (!internalRecording.value || stopping.value) return;
186
+ stopping.value = true;
187
+ try {
188
+ var _socket$value, _config$value$onRecor, _config$value;
189
+ yield stopWorklet();
190
+ const endedAt = performance.now();
191
+ internalRecording.value = false;
192
+ if (((_socket$value = socket.value) === null || _socket$value === void 0 ? void 0 : _socket$value.readyState) === WebSocket.OPEN && typeof config.value === "object" && config.value.sendMetadata !== false) socket.value.send(JSON.stringify({
193
+ type: "end",
194
+ reason
195
+ }));
196
+ yield releaseResources();
197
+ if (typeof config.value === "object") (_config$value$onRecor = (_config$value = config.value).onRecordingChange) === null || _config$value$onRecor === void 0 || _config$value$onRecor.call(_config$value, false);
198
+ options.onEnd({
199
+ source: "capture",
200
+ reason,
201
+ startedAt: startedAt.value,
202
+ endedAt,
203
+ duration: endedAt - startedAt.value,
204
+ chunks: chunks.value
205
+ });
206
+ } finally {
207
+ stopping.value = false;
208
+ }
188
209
  });
189
210
  return function stopCapture() {
190
211
  return _ref3.apply(this, arguments);
@@ -192,7 +213,7 @@ function useSpeech(allowSpeech, options) {
192
213
  }();
193
214
  const startCapture = function() {
194
215
  var _ref4 = _asyncToGenerator(function* () {
195
- if (requesting.value || internalRecording.value || !available.value) return;
216
+ if (requesting.value || stopping.value || internalRecording.value || !available.value) return;
196
217
  requesting.value = true;
197
218
  captureError.value = void 0;
198
219
  let phase = "permission";
@@ -240,12 +261,13 @@ function useSpeech(allowSpeech, options) {
240
261
  channelCount: 1,
241
262
  processorOptions: _objectSpread2(_objectSpread2({}, speechConfig === null || speechConfig === void 0 ? void 0 : speechConfig.processorOptions), {}, { bufferSize })
242
263
  });
264
+ workletStopRequested = false;
243
265
  workletNode.value = nextWorkletNode;
244
266
  sourceNode.value = nextAudioContext.createMediaStreamSource(stream.value);
245
267
  nextWorkletNode.port.onmessage = (event) => {
246
268
  var _socket$value2;
247
269
  if (!(event.data instanceof ArrayBuffer) && "type" in event.data) {
248
- if (event.data.type === "flushed") resolveWorkletFlush === null || resolveWorkletFlush === void 0 || resolveWorkletFlush();
270
+ if (event.data.type === "flushed") resolveWorkletStop === null || resolveWorkletStop === void 0 || resolveWorkletStop();
249
271
  return;
250
272
  }
251
273
  if (!internalRecording.value) return;
@@ -300,6 +322,7 @@ function useSpeech(allowSpeech, options) {
300
322
  }();
301
323
  const trigger = function() {
302
324
  var _ref5 = _asyncToGenerator(function* (breakRecording = false) {
325
+ if (requesting.value || stopping.value) return;
303
326
  if (breakRecording && !recording.value) return;
304
327
  if (controlled.value && typeof config.value === "object") {
305
328
  var _config$value$onRecor2, _config$value2;
@@ -349,7 +372,7 @@ function useSpeech(allowSpeech, options) {
349
372
  return {
350
373
  available,
351
374
  recording,
352
- requesting,
375
+ requesting: computed(() => requesting.value || stopping.value),
353
376
  statusText,
354
377
  trigger
355
378
  };
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://raw.githubusercontent.com/JetBrains/web-types/master/schema/web-types.json",
3
3
  "framework": "vue",
4
4
  "name": "@sdata/web-vue",
5
- "version": "3.22.0",
5
+ "version": "3.22.2",
6
6
  "contributions": {
7
7
  "html": {
8
8
  "types-syntax": "typescript",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sdata/web-vue",
3
- "version": "3.22.1",
3
+ "version": "3.22.3",
4
4
  "description": "SD Design Vue: A Vue.js 3 UI Library",
5
5
  "keywords": [
6
6
  "sd",