@picovoice/eagle-web 0.2.0 → 2.0.0

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/src/eagle.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2023 Picovoice Inc.
2
+ Copyright 2023-2025 Picovoice Inc.
3
3
 
4
4
  You may not use this file except in compliance with the license. A copy of the license is located in the "LICENSE"
5
5
  file accompanying this source.
@@ -14,21 +14,23 @@
14
14
  import { Mutex } from 'async-mutex';
15
15
 
16
16
  import {
17
- aligned_alloc_type,
18
- pv_free_type,
19
- buildWasm,
20
17
  arrayBufferToStringAtIndex,
18
+ base64ToUint8Array,
21
19
  isAccessKeyValid,
22
20
  loadModel,
23
- PvError,
24
21
  } from '@picovoice/web-utils';
25
22
 
23
+ import createModuleSimd from "./lib/pv_eagle_simd";
24
+ import createModulePThread from "./lib/pv_eagle_pthread";
25
+
26
26
  import { simd } from 'wasm-feature-detect';
27
27
 
28
28
  import {
29
29
  EagleModel,
30
+ EagleOptions,
30
31
  EagleProfile,
31
32
  EagleProfilerEnrollResult,
33
+ EagleProfilerOptions,
32
34
  PvStatus
33
35
  } from './types';
34
36
 
@@ -41,6 +43,7 @@ import { pvStatusToException } from './eagle_errors';
41
43
  type pv_eagle_profiler_init_type = (
42
44
  accessKey: number,
43
45
  modelPath: number,
46
+ device: number,
44
47
  object: number
45
48
  ) => Promise<number>;
46
49
  type pv_eagle_profiler_delete_type = (object: number) => Promise<void>;
@@ -54,19 +57,20 @@ type pv_eagle_profiler_enroll_type = (
54
57
  type pv_eagle_profiler_enroll_min_audio_length_samples_type = (
55
58
  object: number,
56
59
  numSamples: number
57
- ) => Promise<number>;
60
+ ) => number;
58
61
  type pv_eagle_profiler_export_type = (
59
62
  object: number,
60
63
  speakerProfile: number
61
- ) => Promise<number>;
64
+ ) => number;
62
65
  type pv_eagle_profiler_export_size_type = (
63
66
  object: number,
64
67
  speakerProfileSizeBytes: number
65
- ) => Promise<number>;
68
+ ) => number;
66
69
  type pv_eagle_profiler_reset_type = (object: number) => Promise<number>;
67
70
  type pv_eagle_init_type = (
68
71
  accessKey: number,
69
72
  modelPath: number,
73
+ device: number,
70
74
  numSpeakers: number,
71
75
  speakerProfiles: number,
72
76
  object: number
@@ -78,34 +82,54 @@ type pv_eagle_process_type = (
78
82
  scores: number
79
83
  ) => Promise<number>;
80
84
  type pv_eagle_reset_type = (object: number) => Promise<number>;
81
- type pv_eagle_frame_length_type = () => Promise<number>;
82
- type pv_eagle_version_type = () => Promise<number>;
83
- type pv_sample_rate_type = () => Promise<number>;
84
- type pv_status_to_string_type = (status: number) => Promise<number>;
85
- type pv_set_sdk_type = (sdk: number) => Promise<void>;
85
+ type pv_eagle_frame_length_type = () => number;
86
+ type pv_eagle_version_type = () => number;
87
+ type pv_eagle_list_hardware_devices_type = (
88
+ hardwareDevices: number,
89
+ numHardwareDevices: number
90
+ ) => number;
91
+ type pv_eagle_free_hardware_devices_type = (
92
+ hardwareDevices: number,
93
+ numHardwareDevices: number
94
+ ) => number;
95
+ type pv_sample_rate_type = () => number;
96
+ type pv_set_sdk_type = (sdk: number) => void;
86
97
  type pv_get_error_stack_type = (
87
98
  messageStack: number,
88
99
  messageStackDepth: number
89
- ) => Promise<number>;
90
- type pv_free_error_stack_type = (messageStack: number) => Promise<void>;
100
+ ) => number;
101
+ type pv_free_error_stack_type = (messageStack: number) => void;
102
+
103
+ type EagleModule = EmscriptenModule & {
104
+ _pv_free: (address: number) => void;
105
+
106
+ _pv_eagle_profiler_enroll_min_audio_length_samples: pv_eagle_profiler_enroll_min_audio_length_samples_type
107
+ _pv_eagle_profiler_export: pv_eagle_profiler_export_type
108
+ _pv_eagle_profiler_export_size: pv_eagle_profiler_export_size_type
109
+ _pv_eagle_frame_length: pv_eagle_frame_length_type
110
+ _pv_eagle_version: pv_eagle_version_type
111
+ _pv_eagle_list_hardware_devices: pv_eagle_list_hardware_devices_type;
112
+ _pv_eagle_free_hardware_devices: pv_eagle_free_hardware_devices_type;
113
+ _pv_sample_rate: pv_sample_rate_type
114
+
115
+ _pv_set_sdk: pv_set_sdk_type;
116
+ _pv_get_error_stack: pv_get_error_stack_type;
117
+ _pv_free_error_stack: pv_free_error_stack_type;
118
+
119
+ // em default functions
120
+ addFunction: typeof addFunction;
121
+ ccall: typeof ccall;
122
+ cwrap: typeof cwrap;
123
+ }
91
124
 
92
125
  type EagleBaseWasmOutput = {
93
- memory: WebAssembly.Memory;
94
- alignedAlloc: aligned_alloc_type;
95
- pvFree: pv_free_type;
96
- pvError: PvError;
126
+ module: EagleModule;
97
127
 
98
128
  sampleRate: number;
99
129
  version: string;
100
130
 
101
131
  messageStackAddressAddressAddress: number;
102
132
  messageStackDepthAddress: number;
103
-
104
- pvStatusToString: pv_status_to_string_type;
105
- pvGetErrorStack: pv_get_error_stack_type;
106
- pvFreeErrorStack: pv_free_error_stack_type;
107
-
108
- exports: any;
109
133
  };
110
134
 
111
135
  type EagleProfilerWasmOutput = EagleBaseWasmOutput & {
@@ -113,11 +137,13 @@ type EagleProfilerWasmOutput = EagleBaseWasmOutput & {
113
137
  profileSize: number;
114
138
 
115
139
  objectAddress: number;
140
+ feedbackAddress: number;
141
+ percentageAddress: number;
142
+ profileAddress: number;
116
143
 
117
- pvEagleProfilerDelete: pv_eagle_profiler_delete_type;
118
- pvEagleProfilerEnroll: pv_eagle_profiler_enroll_type;
119
- pvEagleProfilerExport: pv_eagle_profiler_export_type;
120
- pvEagleProfilerReset: pv_eagle_profiler_reset_type;
144
+ pv_eagle_profiler_enroll: pv_eagle_profiler_enroll_type;
145
+ pv_eagle_profiler_reset: pv_eagle_profiler_reset_type;
146
+ pv_eagle_profiler_delete: pv_eagle_profiler_delete_type;
121
147
  };
122
148
 
123
149
  type EagleWasmOutput = EagleBaseWasmOutput & {
@@ -127,51 +153,39 @@ type EagleWasmOutput = EagleBaseWasmOutput & {
127
153
  objectAddress: number;
128
154
  scoresAddress: number;
129
155
 
130
- pvEagleDelete: pv_eagle_delete_type;
131
- pvEagleProcess: pv_eagle_process_type;
132
- pvEagleReset: pv_eagle_profiler_reset_type;
156
+ pv_eagle_process: pv_eagle_process_type;
157
+ pv_eagle_reset: pv_eagle_reset_type;
158
+ pv_eagle_delete: pv_eagle_delete_type;
133
159
  };
134
160
 
135
161
  const PV_STATUS_SUCCESS = 10000;
136
162
  const MAX_PCM_LENGTH_SEC = 60 * 15;
137
163
 
138
164
  class EagleBase {
139
- protected readonly _pvStatusToString: pv_status_to_string_type;
140
- protected _wasmMemory: WebAssembly.Memory | undefined;
141
- protected readonly _alignedAlloc: CallableFunction;
142
- protected readonly _pvFree: pv_free_type;
143
- protected readonly _functionMutex: Mutex;
165
+ protected _module?: EagleModule;
144
166
 
145
- protected readonly _pvGetErrorStack: pv_get_error_stack_type;
146
- protected readonly _pvFreeErrorStack: pv_free_error_stack_type;
167
+ protected readonly _functionMutex: Mutex;
147
168
 
148
169
  protected readonly _messageStackAddressAddressAddress: number;
149
170
  protected readonly _messageStackDepthAddress: number;
150
171
 
151
- protected static _sampleRate: number;
152
- protected static _version: string;
172
+ protected readonly _sampleRate: number;
173
+ protected readonly _version: string;
153
174
 
154
- protected static _wasm: string;
155
175
  protected static _wasmSimd: string;
176
+ protected static _wasmSimdLib: string;
177
+ protected static _wasmPThread: string;
178
+ protected static _wasmPThreadLib: string;
179
+
156
180
  protected static _sdk: string = 'web';
157
181
 
158
182
  protected static _eagleMutex = new Mutex();
159
183
 
160
- protected readonly _pvError: PvError;
161
-
162
184
  protected constructor(handleWasm: EagleBaseWasmOutput) {
163
- EagleBase._sampleRate = handleWasm.sampleRate;
164
- EagleBase._version = handleWasm.version;
165
-
166
- this._pvStatusToString = handleWasm.pvStatusToString;
167
-
168
- this._wasmMemory = handleWasm.memory;
169
- this._alignedAlloc = handleWasm.alignedAlloc;
170
- this._pvFree = handleWasm.pvFree;
171
- this._pvError = handleWasm.pvError;
185
+ this._module = handleWasm.module;
172
186
 
173
- this._pvGetErrorStack = handleWasm.pvGetErrorStack;
174
- this._pvFreeErrorStack = handleWasm.pvFreeErrorStack;
187
+ this._sampleRate = handleWasm.sampleRate;
188
+ this._version = handleWasm.version;
175
189
 
176
190
  this._messageStackAddressAddressAddress = handleWasm.messageStackAddressAddressAddress;
177
191
  this._messageStackDepthAddress = handleWasm.messageStackDepthAddress;
@@ -183,24 +197,14 @@ class EagleBase {
183
197
  * Audio sample rate required by Eagle.
184
198
  */
185
199
  get sampleRate(): number {
186
- return EagleBase._sampleRate;
200
+ return this._sampleRate;
187
201
  }
188
202
 
189
203
  /**
190
204
  * Version of Eagle.
191
205
  */
192
206
  get version(): string {
193
- return EagleBase._version;
194
- }
195
-
196
- /**
197
- * Set base64 wasm file.
198
- * @param wasm Base64'd wasm file to use to initialize wasm.
199
- */
200
- public static setWasm(wasm: string): void {
201
- if (this._wasm === undefined) {
202
- this._wasm = wasm;
203
- }
207
+ return this._version;
204
208
  }
205
209
 
206
210
  /**
@@ -213,67 +217,79 @@ class EagleBase {
213
217
  }
214
218
  }
215
219
 
220
+ /**
221
+ * Set base64 SIMD wasm file in text format.
222
+ * @param wasmSimdLib Base64'd wasm file in text format.
223
+ */
224
+ public static setWasmSimdLib(wasmSimdLib: string): void {
225
+ if (this._wasmSimdLib === undefined) {
226
+ this._wasmSimdLib = wasmSimdLib;
227
+ }
228
+ }
229
+
230
+ /**
231
+ * Set base64 wasm file with SIMD and pthread feature.
232
+ * @param wasmPThread Base64'd wasm file to use to initialize wasm.
233
+ */
234
+ public static setWasmPThread(wasmPThread: string): void {
235
+ if (this._wasmPThread === undefined) {
236
+ this._wasmPThread = wasmPThread;
237
+ }
238
+ }
239
+
240
+ /**
241
+ * Set base64 SIMD and thread wasm file in text format.
242
+ * @param wasmPThreadLib Base64'd wasm file in text format.
243
+ */
244
+ public static setWasmPThreadLib(wasmPThreadLib: string): void {
245
+ if (this._wasmPThreadLib === undefined) {
246
+ this._wasmPThreadLib = wasmPThreadLib;
247
+ }
248
+ }
249
+
216
250
  public static setSdk(sdk: string): void {
217
251
  EagleBase._sdk = sdk;
218
252
  }
219
253
 
220
254
  protected static async _initBaseWasm(
221
255
  wasmBase64: string,
222
- wasmMemorySize: number
256
+ wasmLibBase64: string,
257
+ createModuleFunc: any,
223
258
  ): Promise<EagleBaseWasmOutput> {
224
- // A WebAssembly page has a constant size of 64KiB. -> 1MiB ~= 16 pages
225
- const memory = new WebAssembly.Memory({ initial: wasmMemorySize });
226
- const memoryBufferUint8 = new Uint8Array(memory.buffer);
227
- const pvError = new PvError();
228
- const exports = await buildWasm(memory, wasmBase64, pvError);
229
-
230
- const aligned_alloc = exports.aligned_alloc as aligned_alloc_type;
231
- const pv_free = exports.pv_free as pv_free_type;
232
- const pv_eagle_version = exports.pv_eagle_version as pv_eagle_version_type;
233
- const pv_sample_rate = exports.pv_sample_rate as pv_sample_rate_type;
234
- const pv_status_to_string =
235
- exports.pv_status_to_string as pv_status_to_string_type;
236
- const pv_set_sdk = exports.pv_set_sdk as pv_set_sdk_type;
237
- const pv_get_error_stack =
238
- exports.pv_get_error_stack as pv_get_error_stack_type;
239
- const pv_free_error_stack =
240
- exports.pv_free_error_stack as pv_free_error_stack_type;
241
-
242
- const sampleRate = await pv_sample_rate();
243
- const versionAddress = await pv_eagle_version();
259
+ const blob = new Blob(
260
+ [base64ToUint8Array(wasmLibBase64)],
261
+ { type: 'application/javascript' }
262
+ );
263
+ const module: EagleModule = await createModuleFunc({
264
+ mainScriptUrlOrBlob: blob,
265
+ wasmBinary: base64ToUint8Array(wasmBase64),
266
+ });
267
+
268
+ const sampleRate = module._pv_sample_rate();
269
+ const versionAddress = module._pv_eagle_version();
244
270
  const version = arrayBufferToStringAtIndex(
245
- memoryBufferUint8,
246
- versionAddress
271
+ module.HEAPU8,
272
+ versionAddress,
247
273
  );
248
274
 
249
275
  const sdkEncoded = new TextEncoder().encode(this._sdk);
250
- const sdkAddress = await aligned_alloc(
251
- Uint8Array.BYTES_PER_ELEMENT,
252
- (sdkEncoded.length + 1) * Uint8Array.BYTES_PER_ELEMENT
253
- );
276
+ const sdkAddress = module._malloc((sdkEncoded.length + 1) * Uint8Array.BYTES_PER_ELEMENT);
254
277
  if (!sdkAddress) {
255
- throw new EagleErrors.EagleOutOfMemoryError(
256
- 'malloc failed: Cannot allocate memory'
257
- );
278
+ throw new EagleErrors.EagleOutOfMemoryError('malloc failed: Cannot allocate memory');
258
279
  }
259
- memoryBufferUint8.set(sdkEncoded, sdkAddress);
260
- memoryBufferUint8[sdkAddress + sdkEncoded.length] = 0;
261
- await pv_set_sdk(sdkAddress);
280
+ module.HEAPU8.set(sdkEncoded, sdkAddress);
281
+ module.HEAPU8[sdkAddress + sdkEncoded.length] = 0;
282
+ module._pv_set_sdk(sdkAddress);
283
+ module._pv_free(sdkAddress);
262
284
 
263
- const messageStackDepthAddress = await aligned_alloc(
264
- Int32Array.BYTES_PER_ELEMENT,
265
- Int32Array.BYTES_PER_ELEMENT
266
- );
285
+ const messageStackDepthAddress = module._malloc(Int32Array.BYTES_PER_ELEMENT);
267
286
  if (!messageStackDepthAddress) {
268
287
  throw new EagleErrors.EagleOutOfMemoryError(
269
288
  'malloc failed: Cannot allocate memory'
270
289
  );
271
290
  }
272
291
 
273
- const messageStackAddressAddressAddress = await aligned_alloc(
274
- Int32Array.BYTES_PER_ELEMENT,
275
- Int32Array.BYTES_PER_ELEMENT
276
- );
292
+ const messageStackAddressAddressAddress = module._malloc(Int32Array.BYTES_PER_ELEMENT);
277
293
  if (!messageStackAddressAddressAddress) {
278
294
  throw new EagleErrors.EagleOutOfMemoryError(
279
295
  'malloc failed: Cannot allocate memory'
@@ -281,22 +297,13 @@ class EagleBase {
281
297
  }
282
298
 
283
299
  return {
284
- memory: memory,
285
- alignedAlloc: aligned_alloc,
286
- pvFree: pv_free,
287
- pvError: pvError,
300
+ module: module,
288
301
 
289
302
  sampleRate: sampleRate,
290
303
  version: version,
291
304
 
292
305
  messageStackAddressAddressAddress: messageStackAddressAddressAddress,
293
306
  messageStackDepthAddress: messageStackDepthAddress,
294
-
295
- pvStatusToString: pv_status_to_string,
296
- pvGetErrorStack: pv_get_error_stack,
297
- pvFreeErrorStack: pv_free_error_stack,
298
-
299
- exports: exports,
300
307
  };
301
308
  }
302
309
 
@@ -304,8 +311,11 @@ class EagleBase {
304
311
  * Releases resources acquired by Eagle
305
312
  */
306
313
  public async release(): Promise<void> {
307
- await this._pvFree(this._messageStackAddressAddressAddress);
308
- await this._pvFree(this._messageStackDepthAddress);
314
+ if (!this._module) {
315
+ return;
316
+ }
317
+ this._module._pv_free(this._messageStackAddressAddressAddress);
318
+ this._module._pv_free(this._messageStackDepthAddress);
309
319
  }
310
320
 
311
321
  protected static async getMessageStack(
@@ -313,43 +323,40 @@ class EagleBase {
313
323
  pv_free_error_stack: pv_free_error_stack_type,
314
324
  messageStackAddressAddressAddress: number,
315
325
  messageStackDepthAddress: number,
316
- memoryBufferView: DataView,
326
+ memoryBufferInt32: Int32Array,
317
327
  memoryBufferUint8: Uint8Array
318
328
  ): Promise<string[]> {
319
- const status = await pv_get_error_stack(
320
- messageStackAddressAddressAddress,
321
- messageStackDepthAddress
322
- );
329
+ const status = pv_get_error_stack(messageStackAddressAddressAddress, messageStackDepthAddress);
323
330
  if (status !== PvStatus.SUCCESS) {
324
331
  throw pvStatusToException(status, 'Unable to get Eagle error state');
325
332
  }
326
333
 
327
- const messageStackAddressAddress = memoryBufferView.getInt32(
328
- messageStackAddressAddressAddress,
329
- true
330
- );
334
+ const messageStackAddressAddress = memoryBufferInt32[messageStackAddressAddressAddress / Int32Array.BYTES_PER_ELEMENT];
331
335
 
332
- const messageStackDepth = memoryBufferView.getInt32(
333
- messageStackDepthAddress,
334
- true
335
- );
336
+ const messageStackDepth = memoryBufferInt32[messageStackDepthAddress / Int32Array.BYTES_PER_ELEMENT];
336
337
  const messageStack: string[] = [];
337
338
  for (let i = 0; i < messageStackDepth; i++) {
338
- const messageStackAddress = memoryBufferView.getInt32(
339
- messageStackAddressAddress + i * Int32Array.BYTES_PER_ELEMENT,
340
- true
341
- );
342
- const message = arrayBufferToStringAtIndex(
343
- memoryBufferUint8,
344
- messageStackAddress
345
- );
339
+ const messageStackAddress = memoryBufferInt32[
340
+ (messageStackAddressAddress / Int32Array.BYTES_PER_ELEMENT) + i
341
+ ];
342
+ const message = arrayBufferToStringAtIndex(memoryBufferUint8, messageStackAddress);
346
343
  messageStack.push(message);
347
344
  }
348
345
 
349
- await pv_free_error_stack(messageStackAddressAddress);
346
+ pv_free_error_stack(messageStackAddressAddress);
350
347
 
351
348
  return messageStack;
352
349
  }
350
+
351
+ protected static wrapAsyncFunction(module: EagleModule, functionName: string, numArgs: number): (...args: any[]) => any {
352
+ // @ts-ignore
353
+ return module.cwrap(
354
+ functionName,
355
+ "number",
356
+ Array(numArgs).fill("number"),
357
+ { async: true }
358
+ );
359
+ }
353
360
  }
354
361
 
355
362
  /**
@@ -357,38 +364,39 @@ class EagleBase {
357
364
  * It enrolls a speaker given a set of utterances and then constructs a profile for the enrolled speaker.
358
365
  */
359
366
  export class EagleProfiler extends EagleBase {
360
- private readonly _pvEagleProfilerDelete: pv_eagle_profiler_delete_type;
361
- private readonly _pvEagleProfilerEnroll: pv_eagle_profiler_enroll_type;
362
- private readonly _pvEagleProfilerExport: pv_eagle_profiler_export_type;
363
- private readonly _pvEagleProfilerReset: pv_eagle_profiler_reset_type;
367
+ private readonly _pv_eagle_profiler_enroll: pv_eagle_profiler_enroll_type;
368
+ private readonly _pv_eagle_profiler_reset: pv_eagle_profiler_reset_type;
369
+ private readonly _pv_eagle_profiler_delete: pv_eagle_profiler_delete_type;
364
370
 
365
371
  private readonly _objectAddress: number;
372
+ private readonly _feedbackAddress: number;
373
+ private readonly _percentageAddress: number;
366
374
 
367
- private static _maxEnrollSamples: number;
368
- private static _minEnrollSamples: number;
369
- private static _profileSize: number;
375
+ private readonly _maxEnrollSamples: number;
376
+ private readonly _minEnrollSamples: number;
377
+ private readonly _profileSize: number;
370
378
 
371
379
  private constructor(handleWasm: EagleProfilerWasmOutput) {
372
380
  super(handleWasm);
373
381
 
374
- EagleProfiler._minEnrollSamples = handleWasm.minEnrollSamples;
375
- EagleProfiler._profileSize = handleWasm.profileSize;
376
- EagleProfiler._maxEnrollSamples =
377
- MAX_PCM_LENGTH_SEC * EagleProfiler._sampleRate;
382
+ this._minEnrollSamples = handleWasm.minEnrollSamples;
383
+ this._profileSize = handleWasm.profileSize;
384
+ this._maxEnrollSamples = MAX_PCM_LENGTH_SEC * this._sampleRate;
378
385
 
379
- this._pvEagleProfilerDelete = handleWasm.pvEagleProfilerDelete;
380
- this._pvEagleProfilerEnroll = handleWasm.pvEagleProfilerEnroll;
381
- this._pvEagleProfilerExport = handleWasm.pvEagleProfilerExport;
382
- this._pvEagleProfilerReset = handleWasm.pvEagleProfilerReset;
386
+ this._pv_eagle_profiler_enroll = handleWasm.pv_eagle_profiler_enroll;
387
+ this._pv_eagle_profiler_reset = handleWasm.pv_eagle_profiler_reset;
388
+ this._pv_eagle_profiler_delete = handleWasm.pv_eagle_profiler_delete;
383
389
 
384
390
  this._objectAddress = handleWasm.objectAddress;
391
+ this._feedbackAddress = handleWasm.feedbackAddress;
392
+ this._percentageAddress = handleWasm.percentageAddress;
385
393
  }
386
394
 
387
395
  /**
388
396
  * The minimum length of the input pcm required by `.enroll()`.
389
397
  */
390
398
  get minEnrollSamples(): number {
391
- return EagleProfiler._minEnrollSamples;
399
+ return this._minEnrollSamples;
392
400
  }
393
401
 
394
402
  /**
@@ -402,37 +410,69 @@ export class EagleProfiler extends EagleBase {
402
410
  * Set to a different name to use multiple models across `eagle` instances.
403
411
  * @param model.forceWrite Flag to overwrite the model in storage even if it exists.
404
412
  * @param model.version Version of the model file. Increment to update the model file in storage.
413
+ * @param options Optional configuration arguments.
414
+ * @param options.device String representation of the device (e.g., CPU or GPU) to use. If set to `best`, the most
415
+ * suitable device is selected automatically. If set to `gpu`, the engine uses the first available GPU device. To select a specific
416
+ * GPU device, set this argument to `gpu:${GPU_INDEX}`, where `${GPU_INDEX}` is the index of the target GPU. If set to
417
+ * `cpu`, the engine will run on the CPU with the default number of threads. To specify the number of threads, set this
418
+ * argument to `cpu:${NUM_THREADS}`, where `${NUM_THREADS}` is the desired number of threads.
405
419
  *
406
420
  * @return An instance of the Eagle Profiler.
407
421
  */
408
422
  public static async create(
409
423
  accessKey: string,
410
- model: EagleModel
424
+ model: EagleModel,
425
+ options: EagleProfilerOptions = {}
411
426
  ): Promise<EagleProfiler> {
412
427
  const customWritePath = model.customWritePath
413
428
  ? model.customWritePath
414
429
  : 'eagle_model';
415
430
  const modelPath = await loadModel({ ...model, customWritePath });
416
431
 
417
- return EagleProfiler._init(accessKey, modelPath);
432
+ return EagleProfiler._init(accessKey, modelPath, options);
418
433
  }
419
434
 
420
435
  public static async _init(
421
436
  accessKey: string,
422
- modelPath: string
437
+ modelPath: string,
438
+ options: EagleProfilerOptions = {}
423
439
  ): Promise<EagleProfiler> {
424
440
  if (!isAccessKeyValid(accessKey)) {
425
441
  throw new EagleErrors.EagleInvalidArgumentError('Invalid AccessKey');
426
442
  }
427
443
 
444
+ let { device = "best" } = options;
445
+
446
+ const isSimd = await simd();
447
+ if (!isSimd) {
448
+ throw new EagleErrors.EagleRuntimeError('Browser not supported.');
449
+ }
450
+
451
+ const isWorkerScope =
452
+ typeof WorkerGlobalScope !== 'undefined' &&
453
+ self instanceof WorkerGlobalScope;
454
+ if (
455
+ !isWorkerScope &&
456
+ (device === 'best' || (device.startsWith('cpu') && device !== 'cpu:1'))
457
+ ) {
458
+ // eslint-disable-next-line no-console
459
+ console.warn('Multi-threading is not supported on main thread.');
460
+ device = 'cpu:1';
461
+ }
462
+
463
+ const sabDefined = typeof SharedArrayBuffer !== 'undefined'
464
+ && (device !== "cpu:1");
465
+
428
466
  return new Promise<EagleProfiler>((resolve, reject) => {
429
467
  EagleProfiler._eagleMutex
430
468
  .runExclusive(async () => {
431
- const isSimd = await simd();
432
469
  const wasmOutput = await EagleProfiler._initProfilerWasm(
433
- accessKey,
434
- modelPath,
435
- isSimd ? this._wasmSimd : this._wasm
470
+ accessKey.trim(),
471
+ modelPath.trim(),
472
+ device,
473
+ sabDefined ? this._wasmPThread : this._wasmSimd,
474
+ sabDefined ? this._wasmPThreadLib : this._wasmSimdLib,
475
+ sabDefined ? createModulePThread : createModuleSimd
436
476
  );
437
477
  return new EagleProfiler(wasmOutput);
438
478
  })
@@ -472,86 +512,68 @@ export class EagleProfiler extends EagleBase {
472
512
  */
473
513
  public async enroll(pcm: Int16Array): Promise<EagleProfilerEnrollResult> {
474
514
  if (!(pcm instanceof Int16Array)) {
475
- throw new EagleErrors.EagleInvalidArgumentError("The argument 'pcm' must be provided as an Int16Array");
515
+ throw new EagleErrors.EagleInvalidArgumentError(
516
+ "The argument 'pcm' must be provided as an Int16Array"
517
+ );
476
518
  }
477
519
 
478
- if (pcm.length > EagleProfiler._maxEnrollSamples) {
520
+ if (pcm.length > this._maxEnrollSamples) {
479
521
  throw new EagleErrors.EagleInvalidArgumentError(
480
- `'pcm' size must be smaller than ${EagleProfiler._maxEnrollSamples}`
522
+ `'pcm' size must be smaller than ${this._maxEnrollSamples}`
481
523
  );
482
524
  }
483
525
 
484
526
  return new Promise<EagleProfilerEnrollResult>((resolve, reject) => {
485
527
  this._functionMutex
486
528
  .runExclusive(async () => {
487
- if (this._wasmMemory === undefined) {
488
- throw new EagleErrors.EagleInvalidStateError('Attempted to call `.enroll()` after release');
529
+ if (this._module === undefined) {
530
+ throw new EagleErrors.EagleInvalidStateError(
531
+ 'Attempted to call `.enroll()` after release'
532
+ );
489
533
  }
490
534
 
491
- const pcmAddress = await this._alignedAlloc(
492
- Int16Array.BYTES_PER_ELEMENT,
535
+ const pcmAddress = this._module._malloc(
493
536
  pcm.length * Int16Array.BYTES_PER_ELEMENT
494
537
  );
495
538
 
496
- const feedbackAddress = await this._alignedAlloc(
497
- Int32Array.BYTES_PER_ELEMENT,
498
- Int32Array.BYTES_PER_ELEMENT
539
+ this._module.HEAP16.set(
540
+ pcm,
541
+ pcmAddress / Int16Array.BYTES_PER_ELEMENT
499
542
  );
500
- if (feedbackAddress === 0) {
501
- throw new EagleErrors.EagleOutOfMemoryError('malloc failed: Cannot allocate memory');
502
- }
503
- const percentageAddress = await this._alignedAlloc(
504
- Int32Array.BYTES_PER_ELEMENT,
505
- Int32Array.BYTES_PER_ELEMENT
506
- );
507
- if (percentageAddress === 0) {
508
- throw new EagleErrors.EagleOutOfMemoryError('malloc failed: Cannot allocate memory');
509
- }
510
-
511
- const memoryBufferInt16 = new Int16Array(this._wasmMemory.buffer);
512
- memoryBufferInt16.set(pcm, pcmAddress / Int16Array.BYTES_PER_ELEMENT);
513
-
514
- const status = await this._pvEagleProfilerEnroll(
543
+ const status = await this._pv_eagle_profiler_enroll(
515
544
  this._objectAddress,
516
545
  pcmAddress,
517
546
  pcm.length,
518
- feedbackAddress,
519
- percentageAddress
547
+ this._feedbackAddress,
548
+ this._percentageAddress
520
549
  );
521
- await this._pvFree(pcmAddress);
522
- if (status !== PV_STATUS_SUCCESS) {
523
- await this._pvFree(feedbackAddress);
524
- await this._pvFree(percentageAddress);
525
-
526
- const memoryBufferView = new DataView(this._wasmMemory.buffer);
527
- const memoryBufferUint8 = new Uint8Array(this._wasmMemory.buffer);
550
+ this._module._pv_free(pcmAddress);
528
551
 
552
+ if (status !== PV_STATUS_SUCCESS) {
529
553
  const messageStack = await EagleProfiler.getMessageStack(
530
- this._pvGetErrorStack,
531
- this._pvFreeErrorStack,
554
+ this._module._pv_get_error_stack,
555
+ this._module._pv_free_error_stack,
532
556
  this._messageStackAddressAddressAddress,
533
557
  this._messageStackDepthAddress,
534
- memoryBufferView,
535
- memoryBufferUint8
558
+ this._module.HEAP32,
559
+ this._module.HEAPU8
536
560
  );
537
561
 
538
- throw pvStatusToException(status, "EagleProfiler enroll failed", messageStack);
562
+ throw pvStatusToException(
563
+ status,
564
+ 'EagleProfiler enroll failed',
565
+ messageStack
566
+ );
539
567
  }
540
568
 
541
- const memoryBufferView = new DataView(this._wasmMemory.buffer);
542
-
543
- const feedback = memoryBufferView.getInt32(
544
- feedbackAddress,
545
- true
546
- );
547
-
548
- const percentage = memoryBufferView.getFloat32(
549
- percentageAddress,
550
- true
551
- );
552
-
553
- await this._pvFree(feedbackAddress);
554
- await this._pvFree(percentageAddress);
569
+ const feedback =
570
+ this._module.HEAP32[
571
+ this._feedbackAddress / Int32Array.BYTES_PER_ELEMENT
572
+ ];
573
+ const percentage =
574
+ this._module.HEAPF32[
575
+ this._percentageAddress / Float32Array.BYTES_PER_ELEMENT
576
+ ];
555
577
 
556
578
  return { feedback, percentage };
557
579
  })
@@ -574,45 +596,44 @@ export class EagleProfiler extends EagleBase {
574
596
  return new Promise<EagleProfile>((resolve, reject) => {
575
597
  this._functionMutex
576
598
  .runExclusive(async () => {
577
- if (this._wasmMemory === undefined) {
578
- throw new EagleErrors.EagleInvalidStateError('Attempted to call `.export()` after release');
599
+ if (this._module === undefined) {
600
+ throw new EagleErrors.EagleInvalidStateError(
601
+ 'Attempted to call `.export()` after release'
602
+ );
579
603
  }
580
604
 
581
- const profileAddress = await this._alignedAlloc(
582
- Uint8Array.BYTES_PER_ELEMENT,
583
- Uint8Array.BYTES_PER_ELEMENT * EagleProfiler._profileSize
605
+ const profileAddress = this._module._malloc(
606
+ Uint8Array.BYTES_PER_ELEMENT * this._profileSize
584
607
  );
585
608
 
586
- const status = await this._pvEagleProfilerExport(
609
+ const status = this._module._pv_eagle_profiler_export(
587
610
  this._objectAddress,
588
611
  profileAddress
589
612
  );
590
613
  if (status !== PV_STATUS_SUCCESS) {
591
- await this._pvFree(profileAddress);
592
-
593
- const memoryBufferView = new DataView(this._wasmMemory.buffer);
594
- const memoryBufferUint8 = new Uint8Array(this._wasmMemory.buffer);
614
+ this._module._pv_free(profileAddress);
595
615
 
596
616
  const messageStack = await EagleProfiler.getMessageStack(
597
- this._pvGetErrorStack,
598
- this._pvFreeErrorStack,
617
+ this._module._pv_get_error_stack,
618
+ this._module._pv_free_error_stack,
599
619
  this._messageStackAddressAddressAddress,
600
620
  this._messageStackDepthAddress,
601
- memoryBufferView,
602
- memoryBufferUint8
621
+ this._module.HEAP32,
622
+ this._module.HEAPU8
603
623
  );
604
624
 
605
- throw pvStatusToException(status, "EagleProfiler export failed", messageStack);
625
+ throw pvStatusToException(
626
+ status,
627
+ 'EagleProfiler export failed',
628
+ messageStack
629
+ );
606
630
  }
607
631
 
608
- const memoryBufferUint8 = new Uint8Array(this._wasmMemory.buffer);
609
-
610
- const profile = memoryBufferUint8.slice(
611
- profileAddress / Uint8Array.BYTES_PER_ELEMENT,
612
- profileAddress / Uint8Array.BYTES_PER_ELEMENT +
613
- EagleProfiler._profileSize
632
+ const profile = this._module.HEAPU8.slice(
633
+ profileAddress,
634
+ profileAddress + Uint8Array.BYTES_PER_ELEMENT * this._profileSize
614
635
  );
615
- await this._pvFree(profileAddress);
636
+ this._module._pv_free(profileAddress);
616
637
 
617
638
  return { bytes: profile };
618
639
  })
@@ -633,25 +654,30 @@ export class EagleProfiler extends EagleBase {
633
654
  return new Promise<void>((resolve, reject) => {
634
655
  this._functionMutex
635
656
  .runExclusive(async () => {
636
- if (this._wasmMemory === undefined) {
637
- throw new EagleErrors.EagleInvalidStateError('Attempted to call `.reset()` after release');
657
+ if (this._module === undefined) {
658
+ throw new EagleErrors.EagleInvalidStateError(
659
+ 'Attempted to call `.reset()` after release'
660
+ );
638
661
  }
639
662
 
640
- const status = await this._pvEagleProfilerReset(this._objectAddress);
663
+ const status = await this._pv_eagle_profiler_reset(
664
+ this._objectAddress
665
+ );
641
666
  if (status !== PV_STATUS_SUCCESS) {
642
- const memoryBufferView = new DataView(this._wasmMemory.buffer);
643
- const memoryBufferUint8 = new Uint8Array(this._wasmMemory.buffer);
644
-
645
667
  const messageStack = await EagleProfiler.getMessageStack(
646
- this._pvGetErrorStack,
647
- this._pvFreeErrorStack,
668
+ this._module._pv_get_error_stack,
669
+ this._module._pv_free_error_stack,
648
670
  this._messageStackAddressAddressAddress,
649
671
  this._messageStackDepthAddress,
650
- memoryBufferView,
651
- memoryBufferUint8
672
+ this._module.HEAP32,
673
+ this._module.HEAPU8
652
674
  );
653
675
 
654
- throw pvStatusToException(status, "EagleProfiler reset failed", messageStack);
676
+ throw pvStatusToException(
677
+ status,
678
+ 'EagleProfiler reset failed',
679
+ messageStack
680
+ );
655
681
  }
656
682
  })
657
683
  .then(() => {
@@ -667,151 +693,233 @@ export class EagleProfiler extends EagleBase {
667
693
  * Releases resources acquired by Eagle Profiler
668
694
  */
669
695
  public async release(): Promise<void> {
696
+ if (!this._module) {
697
+ return;
698
+ }
699
+
670
700
  await super.release();
671
- await this._pvEagleProfilerDelete(this._objectAddress);
672
- delete this._wasmMemory;
673
- this._wasmMemory = undefined;
701
+ await this._pv_eagle_profiler_delete(this._objectAddress);
702
+ this._module = undefined;
674
703
  }
675
704
 
676
705
  private static async _initProfilerWasm(
677
706
  accessKey: string,
678
707
  modelPath: string,
679
- wasmBase64: string
708
+ device: string,
709
+ wasmBase64: string,
710
+ wasmLibBase64: string,
711
+ createModuleFunc: any
680
712
  ): Promise<EagleProfilerWasmOutput> {
681
- const baseWasmOutput = await super._initBaseWasm(wasmBase64, 3500);
682
- const memoryBufferUint8 = new Uint8Array(baseWasmOutput.memory.buffer);
683
-
684
- const pv_eagle_profiler_init = baseWasmOutput.exports
685
- .pv_eagle_profiler_init as pv_eagle_profiler_init_type;
686
- const pv_eagle_profiler_delete = baseWasmOutput.exports
687
- .pv_eagle_profiler_delete as pv_eagle_profiler_delete_type;
688
- const pv_eagle_profiler_enroll = baseWasmOutput.exports
689
- .pv_eagle_profiler_enroll as pv_eagle_profiler_enroll_type;
690
- const pv_eagle_profiler_enroll_min_audio_length_samples = baseWasmOutput
691
- .exports
692
- .pv_eagle_profiler_enroll_min_audio_length_samples as pv_eagle_profiler_enroll_min_audio_length_samples_type;
693
- const pv_eagle_profiler_export = baseWasmOutput.exports
694
- .pv_eagle_profiler_export as pv_eagle_profiler_export_type;
695
- const pv_eagle_profiler_export_size = baseWasmOutput.exports
696
- .pv_eagle_profiler_export_size as pv_eagle_profiler_export_size_type;
697
- const pv_eagle_profiler_reset = baseWasmOutput.exports
698
- .pv_eagle_profiler_reset as pv_eagle_profiler_reset_type;
699
-
700
- const objectAddressAddress = await baseWasmOutput.alignedAlloc(
701
- Int32Array.BYTES_PER_ELEMENT,
713
+ const baseWasmOutput = await super._initBaseWasm(
714
+ wasmBase64,
715
+ wasmLibBase64,
716
+ createModuleFunc
717
+ );
718
+
719
+ const pv_eagle_profiler_init: pv_eagle_profiler_init_type =
720
+ this.wrapAsyncFunction(
721
+ baseWasmOutput.module,
722
+ 'pv_eagle_profiler_init',
723
+ 4
724
+ );
725
+ const pv_eagle_profiler_enroll: pv_eagle_profiler_enroll_type =
726
+ this.wrapAsyncFunction(
727
+ baseWasmOutput.module,
728
+ 'pv_eagle_profiler_enroll',
729
+ 5
730
+ );
731
+
732
+ const pv_eagle_profiler_reset: pv_eagle_profiler_reset_type =
733
+ this.wrapAsyncFunction(
734
+ baseWasmOutput.module,
735
+ 'pv_eagle_profiler_reset',
736
+ 1
737
+ );
738
+
739
+ const pv_eagle_profiler_delete: pv_eagle_profiler_delete_type =
740
+ this.wrapAsyncFunction(
741
+ baseWasmOutput.module,
742
+ 'pv_eagle_profiler_delete',
743
+ 1
744
+ );
745
+
746
+ const objectAddressAddress = baseWasmOutput.module._malloc(
702
747
  Int32Array.BYTES_PER_ELEMENT
703
748
  );
704
749
  if (objectAddressAddress === 0) {
705
- throw new EagleErrors.EagleOutOfMemoryError('malloc failed: Cannot allocate memory');
750
+ throw new EagleErrors.EagleOutOfMemoryError(
751
+ 'malloc failed: Cannot allocate memory'
752
+ );
706
753
  }
707
754
 
708
- const accessKeyAddress = await baseWasmOutput.alignedAlloc(
709
- Uint8Array.BYTES_PER_ELEMENT,
755
+ const accessKeyAddress = baseWasmOutput.module._malloc(
710
756
  (accessKey.length + 1) * Uint8Array.BYTES_PER_ELEMENT
711
757
  );
712
758
  if (accessKeyAddress === 0) {
713
- throw new EagleErrors.EagleOutOfMemoryError('malloc failed: Cannot allocate memory');
759
+ throw new EagleErrors.EagleOutOfMemoryError(
760
+ 'malloc failed: Cannot allocate memory'
761
+ );
714
762
  }
715
763
  for (let i = 0; i < accessKey.length; i++) {
716
- memoryBufferUint8[accessKeyAddress + i] = accessKey.charCodeAt(i);
764
+ baseWasmOutput.module.HEAPU8[accessKeyAddress + i] =
765
+ accessKey.charCodeAt(i);
717
766
  }
718
- memoryBufferUint8[accessKeyAddress + accessKey.length] = 0;
767
+ baseWasmOutput.module.HEAPU8[accessKeyAddress + accessKey.length] = 0;
719
768
 
720
769
  const modelPathEncoded = new TextEncoder().encode(modelPath);
721
- const modelPathAddress = await baseWasmOutput.alignedAlloc(
722
- Uint8Array.BYTES_PER_ELEMENT,
770
+ const modelPathAddress = baseWasmOutput.module._malloc(
723
771
  (modelPathEncoded.length + 1) * Uint8Array.BYTES_PER_ELEMENT
724
772
  );
725
773
  if (modelPathAddress === 0) {
726
- throw new EagleErrors.EagleOutOfMemoryError('malloc failed: Cannot allocate memory');
774
+ throw new EagleErrors.EagleOutOfMemoryError(
775
+ 'malloc failed: Cannot allocate memory'
776
+ );
777
+ }
778
+ baseWasmOutput.module.HEAPU8.set(modelPathEncoded, modelPathAddress);
779
+ baseWasmOutput.module.HEAPU8[
780
+ modelPathAddress + modelPathEncoded.length
781
+ ] = 0;
782
+
783
+ const deviceAddress = baseWasmOutput.module._malloc(
784
+ (device.length + 1) * Uint8Array.BYTES_PER_ELEMENT
785
+ );
786
+ if (deviceAddress === 0) {
787
+ throw new EagleErrors.EagleOutOfMemoryError(
788
+ 'malloc failed: Cannot allocate memory'
789
+ );
790
+ }
791
+ for (let i = 0; i < device.length; i++) {
792
+ baseWasmOutput.module.HEAPU8[deviceAddress + i] = device.charCodeAt(i);
727
793
  }
728
- memoryBufferUint8.set(modelPathEncoded, modelPathAddress);
729
- memoryBufferUint8[modelPathAddress + modelPathEncoded.length] = 0;
794
+ baseWasmOutput.module.HEAPU8[deviceAddress + device.length] = 0;
730
795
 
731
796
  let status = await pv_eagle_profiler_init(
732
797
  accessKeyAddress,
733
798
  modelPathAddress,
799
+ deviceAddress,
734
800
  objectAddressAddress
735
801
  );
736
- await baseWasmOutput.pvFree(accessKeyAddress);
737
- await baseWasmOutput.pvFree(modelPathAddress);
738
-
739
- const memoryBufferView = new DataView(baseWasmOutput.memory.buffer);
740
-
741
- if (status !== PV_STATUS_SUCCESS) {
802
+ baseWasmOutput.module._pv_free(accessKeyAddress);
803
+ baseWasmOutput.module._pv_free(modelPathAddress);
804
+ baseWasmOutput.module._pv_free(deviceAddress);
805
+ if (status !== PvStatus.SUCCESS) {
742
806
  const messageStack = await EagleProfiler.getMessageStack(
743
- baseWasmOutput.pvGetErrorStack,
744
- baseWasmOutput.pvFreeErrorStack,
807
+ baseWasmOutput.module._pv_get_error_stack,
808
+ baseWasmOutput.module._pv_free_error_stack,
745
809
  baseWasmOutput.messageStackAddressAddressAddress,
746
810
  baseWasmOutput.messageStackDepthAddress,
747
- memoryBufferView,
748
- memoryBufferUint8
811
+ baseWasmOutput.module.HEAP32,
812
+ baseWasmOutput.module.HEAPU8
749
813
  );
750
814
 
751
- throw pvStatusToException(status, "EagleProfiler init failed", messageStack, baseWasmOutput.pvError);
815
+ throw pvStatusToException(status, 'Initialization failed', messageStack);
752
816
  }
753
817
 
754
- const objectAddress = memoryBufferView.getInt32(objectAddressAddress, true);
755
- await baseWasmOutput.pvFree(objectAddressAddress);
818
+ const objectAddress =
819
+ baseWasmOutput.module.HEAP32[
820
+ objectAddressAddress / Int32Array.BYTES_PER_ELEMENT
821
+ ];
822
+ baseWasmOutput.module._pv_free(objectAddressAddress);
756
823
 
757
- const minEnrollSamplesAddress = await baseWasmOutput.alignedAlloc(
758
- Int32Array.BYTES_PER_ELEMENT,
824
+ const minEnrollSamplesAddress = baseWasmOutput.module._malloc(
759
825
  Int32Array.BYTES_PER_ELEMENT
760
826
  );
761
827
  if (minEnrollSamplesAddress === 0) {
762
- throw new EagleErrors.EagleOutOfMemoryError('malloc failed: Cannot allocate memory');
828
+ throw new EagleErrors.EagleOutOfMemoryError(
829
+ 'malloc failed: Cannot allocate memory'
830
+ );
763
831
  }
764
832
 
765
- status = await pv_eagle_profiler_enroll_min_audio_length_samples(
766
- objectAddress,
767
- minEnrollSamplesAddress
768
- );
833
+ status =
834
+ baseWasmOutput.module._pv_eagle_profiler_enroll_min_audio_length_samples(
835
+ objectAddress,
836
+ minEnrollSamplesAddress
837
+ );
769
838
  if (status !== PV_STATUS_SUCCESS) {
770
839
  const messageStack = await EagleProfiler.getMessageStack(
771
- baseWasmOutput.pvGetErrorStack,
772
- baseWasmOutput.pvFreeErrorStack,
840
+ baseWasmOutput.module._pv_get_error_stack,
841
+ baseWasmOutput.module._pv_free_error_stack,
773
842
  baseWasmOutput.messageStackAddressAddressAddress,
774
843
  baseWasmOutput.messageStackDepthAddress,
775
- memoryBufferView,
776
- memoryBufferUint8
844
+ baseWasmOutput.module.HEAP32,
845
+ baseWasmOutput.module.HEAPU8
777
846
  );
778
847
 
779
- throw pvStatusToException(status, "EagleProfiler failed to get min enroll audio length", messageStack, baseWasmOutput.pvError);
848
+ throw pvStatusToException(
849
+ status,
850
+ 'EagleProfiler failed to get min enroll audio length',
851
+ messageStack
852
+ );
780
853
  }
781
854
 
782
- const minEnrollSamples = memoryBufferView.getInt32(
783
- minEnrollSamplesAddress,
784
- true
785
- );
786
- await baseWasmOutput.pvFree(minEnrollSamplesAddress);
855
+ const minEnrollSamples =
856
+ baseWasmOutput.module.HEAP32[
857
+ minEnrollSamplesAddress / Int32Array.BYTES_PER_ELEMENT
858
+ ];
859
+ baseWasmOutput.module._pv_free(minEnrollSamplesAddress);
787
860
 
788
- const profileSizeAddress = await baseWasmOutput.alignedAlloc(
789
- Int32Array.BYTES_PER_ELEMENT,
861
+ const profileSizeAddress = baseWasmOutput.module._malloc(
790
862
  Int32Array.BYTES_PER_ELEMENT
791
863
  );
792
864
  if (profileSizeAddress === 0) {
793
- throw new EagleErrors.EagleOutOfMemoryError('malloc failed: Cannot allocate memory');
865
+ throw new EagleErrors.EagleOutOfMemoryError(
866
+ 'malloc failed: Cannot allocate memory'
867
+ );
794
868
  }
795
869
 
796
- status = await pv_eagle_profiler_export_size(
870
+ status = baseWasmOutput.module._pv_eagle_profiler_export_size(
797
871
  objectAddress,
798
872
  profileSizeAddress
799
873
  );
800
874
  if (status !== PV_STATUS_SUCCESS) {
801
875
  const messageStack = await EagleProfiler.getMessageStack(
802
- baseWasmOutput.pvGetErrorStack,
803
- baseWasmOutput.pvFreeErrorStack,
876
+ baseWasmOutput.module._pv_get_error_stack,
877
+ baseWasmOutput.module._pv_free_error_stack,
804
878
  baseWasmOutput.messageStackAddressAddressAddress,
805
879
  baseWasmOutput.messageStackDepthAddress,
806
- memoryBufferView,
807
- memoryBufferUint8
880
+ baseWasmOutput.module.HEAP32,
881
+ baseWasmOutput.module.HEAPU8
808
882
  );
809
883
 
810
- throw pvStatusToException(status, "EagleProfiler failed to get export size", messageStack, baseWasmOutput.pvError);
884
+ throw pvStatusToException(
885
+ status,
886
+ 'EagleProfiler failed to get export size',
887
+ messageStack
888
+ );
889
+ }
890
+
891
+ const profileSize =
892
+ baseWasmOutput.module.HEAP32[
893
+ profileSizeAddress / Int32Array.BYTES_PER_ELEMENT
894
+ ];
895
+ baseWasmOutput.module._pv_free(profileSizeAddress);
896
+
897
+ const feedbackAddress = baseWasmOutput.module._malloc(
898
+ Int32Array.BYTES_PER_ELEMENT
899
+ );
900
+ if (feedbackAddress === 0) {
901
+ throw new EagleErrors.EagleOutOfMemoryError(
902
+ 'malloc failed: Cannot allocate memory'
903
+ );
811
904
  }
812
905
 
813
- const profileSize = memoryBufferView.getInt32(profileSizeAddress, true);
814
- await baseWasmOutput.pvFree(profileSizeAddress);
906
+ const percentageAddress = baseWasmOutput.module._malloc(
907
+ Int32Array.BYTES_PER_ELEMENT
908
+ );
909
+ if (percentageAddress === 0) {
910
+ throw new EagleErrors.EagleOutOfMemoryError(
911
+ 'malloc failed: Cannot allocate memory'
912
+ );
913
+ }
914
+
915
+ const profileAddress = baseWasmOutput.module._malloc(
916
+ Uint8Array.BYTES_PER_ELEMENT * profileSize
917
+ );
918
+ if (profileAddress === 0) {
919
+ throw new EagleErrors.EagleOutOfMemoryError(
920
+ 'malloc failed: Cannot allocate memory'
921
+ );
922
+ }
815
923
 
816
924
  return {
817
925
  ...baseWasmOutput,
@@ -819,11 +927,13 @@ export class EagleProfiler extends EagleBase {
819
927
  profileSize: profileSize,
820
928
 
821
929
  objectAddress: objectAddress,
930
+ feedbackAddress: feedbackAddress,
931
+ percentageAddress: percentageAddress,
932
+ profileAddress: profileAddress,
822
933
 
823
- pvEagleProfilerDelete: pv_eagle_profiler_delete,
824
- pvEagleProfilerEnroll: pv_eagle_profiler_enroll,
825
- pvEagleProfilerExport: pv_eagle_profiler_export,
826
- pvEagleProfilerReset: pv_eagle_profiler_reset,
934
+ pv_eagle_profiler_enroll: pv_eagle_profiler_enroll,
935
+ pv_eagle_profiler_reset: pv_eagle_profiler_reset,
936
+ pv_eagle_profiler_delete: pv_eagle_profiler_delete,
827
937
  };
828
938
  }
829
939
  }
@@ -833,35 +943,35 @@ export class EagleProfiler extends EagleBase {
833
943
  * It processes incoming audio in consecutive frames and emits a similarity score for each enrolled speaker.
834
944
  */
835
945
  export class Eagle extends EagleBase {
836
- private readonly _pvEagleDelete: pv_eagle_delete_type;
837
- private readonly _pvEagleProcess: pv_eagle_process_type;
838
- private readonly _pvEagleReset: pv_eagle_profiler_reset_type;
946
+ private readonly _pv_eagle_process: pv_eagle_process_type;
947
+ private readonly _pv_eagle_reset: pv_eagle_reset_type;
948
+ private readonly _pv_eagle_delete: pv_eagle_delete_type;
839
949
 
840
950
  private readonly _objectAddress: number;
841
951
  private readonly _scoresAddress: number;
842
- private readonly _numSpeakers: number;
843
952
 
844
- private static _frameLength: number;
953
+ private readonly _frameLength: number;
954
+ private readonly _numSpeakers: number;
845
955
 
846
956
  private constructor(handleWasm: EagleWasmOutput) {
847
957
  super(handleWasm);
848
958
 
849
- Eagle._frameLength = handleWasm.frameLength;
959
+ this._frameLength = handleWasm.frameLength;
960
+ this._numSpeakers = handleWasm.numSpeakers;
850
961
 
851
- this._pvEagleDelete = handleWasm.pvEagleDelete;
852
- this._pvEagleProcess = handleWasm.pvEagleProcess;
853
- this._pvEagleReset = handleWasm.pvEagleReset;
962
+ this._pv_eagle_process = handleWasm.pv_eagle_process;
963
+ this._pv_eagle_reset = handleWasm.pv_eagle_reset;
964
+ this._pv_eagle_delete = handleWasm.pv_eagle_delete;
854
965
 
855
966
  this._objectAddress = handleWasm.objectAddress;
856
967
  this._scoresAddress = handleWasm.scoresAddress;
857
- this._numSpeakers = handleWasm.numSpeakers;
858
968
  }
859
969
 
860
970
  /**
861
971
  * Number of audio samples per frame expected by Eagle (i.e. length of the array passed into `.process()`)
862
972
  */
863
973
  get frameLength(): number {
864
- return Eagle._frameLength;
974
+ return this._frameLength;
865
975
  }
866
976
 
867
977
  /**
@@ -876,13 +986,20 @@ export class Eagle extends EagleBase {
876
986
  * @param model.forceWrite Flag to overwrite the model in storage even if it exists.
877
987
  * @param model.version Version of the model file. Increment to update the model file in storage.
878
988
  * @param speakerProfiles One or more Eagle speaker profiles. These can be constructed using `EagleProfiler`.
989
+ * @param options Optional configuration arguments.
990
+ * @param options.device String representation of the device (e.g., CPU or GPU) to use. If set to `best`, the most
991
+ * suitable device is selected automatically. If set to `gpu`, the engine uses the first available GPU device. To select a specific
992
+ * GPU device, set this argument to `gpu:${GPU_INDEX}`, where `${GPU_INDEX}` is the index of the target GPU. If set to
993
+ * `cpu`, the engine will run on the CPU with the default number of threads. To specify the number of threads, set this
994
+ * argument to `cpu:${NUM_THREADS}`, where `${NUM_THREADS}` is the desired number of threads.
879
995
  *
880
996
  * @return An instance of the Eagle engine.
881
997
  */
882
998
  public static async create(
883
999
  accessKey: string,
884
1000
  model: EagleModel,
885
- speakerProfiles: EagleProfile[] | EagleProfile
1001
+ speakerProfiles: EagleProfile[] | EagleProfile,
1002
+ options: EagleOptions = {}
886
1003
  ): Promise<Eagle> {
887
1004
  const customWritePath = model.customWritePath
888
1005
  ? model.customWritePath
@@ -892,32 +1009,60 @@ export class Eagle extends EagleBase {
892
1009
  return Eagle._init(
893
1010
  accessKey,
894
1011
  modelPath,
895
- !Array.isArray(speakerProfiles) ? [speakerProfiles] : speakerProfiles
1012
+ !Array.isArray(speakerProfiles) ? [speakerProfiles] : speakerProfiles,
1013
+ options
896
1014
  );
897
1015
  }
898
1016
 
899
1017
  public static async _init(
900
1018
  accessKey: string,
901
1019
  modelPath: string,
902
- speakerProfiles: EagleProfile[]
1020
+ speakerProfiles: EagleProfile[],
1021
+ options: EagleOptions = {}
903
1022
  ): Promise<Eagle> {
904
1023
  if (!isAccessKeyValid(accessKey)) {
905
1024
  throw new EagleErrors.EagleInvalidArgumentError('Invalid AccessKey');
906
1025
  }
907
1026
 
908
1027
  if (!speakerProfiles || speakerProfiles.length === 0) {
909
- throw new EagleErrors.EagleInvalidArgumentError('No speaker profiles provided');
1028
+ throw new EagleErrors.EagleInvalidArgumentError(
1029
+ 'No speaker profiles provided'
1030
+ );
910
1031
  }
911
1032
 
1033
+ let { device = "best" } = options;
1034
+
1035
+ const isSimd = await simd();
1036
+ if (!isSimd) {
1037
+ throw new EagleErrors.EagleRuntimeError('Browser not supported.');
1038
+ }
1039
+
1040
+ const isWorkerScope =
1041
+ typeof WorkerGlobalScope !== 'undefined' &&
1042
+ self instanceof WorkerGlobalScope;
1043
+ if (
1044
+ !isWorkerScope &&
1045
+ (device === 'best' || (device.startsWith('cpu') && device !== 'cpu:1'))
1046
+ ) {
1047
+ // eslint-disable-next-line no-console
1048
+ console.warn('Multi-threading is not supported on main thread.');
1049
+ device = 'cpu:1';
1050
+ }
1051
+
1052
+ const sabDefined = typeof SharedArrayBuffer !== 'undefined'
1053
+ && (device !== "cpu:1");
1054
+
912
1055
  return new Promise<Eagle>((resolve, reject) => {
913
1056
  Eagle._eagleMutex
914
1057
  .runExclusive(async () => {
915
- const isSimd = await simd();
916
1058
  const wasmOutput = await Eagle._initWasm(
917
- accessKey,
918
- modelPath,
1059
+ accessKey.trim(),
1060
+ modelPath.trim(),
1061
+ device,
919
1062
  speakerProfiles,
920
- isSimd ? this._wasmSimd : this._wasm
1063
+ sabDefined ? this._wasmPThread : this._wasmSimd,
1064
+ sabDefined ? this._wasmPThreadLib : this._wasmSimdLib,
1065
+ sabDefined ? createModulePThread : createModuleSimd
921
1066
  );
922
1067
  return new Eagle(wasmOutput);
923
1068
  })
@@ -942,61 +1087,65 @@ export class Eagle extends EagleBase {
942
1087
  */
943
1088
  public async process(pcm: Int16Array): Promise<number[]> {
944
1089
  if (!(pcm instanceof Int16Array)) {
945
- throw new EagleErrors.EagleInvalidArgumentError("The argument 'pcm' must be provided as an Int16Array");
1090
+ throw new EagleErrors.EagleInvalidArgumentError(
1091
+ "The argument 'pcm' must be provided as an Int16Array"
1092
+ );
946
1093
  }
947
1094
 
948
- if (pcm.length !== Eagle._frameLength) {
1095
+ if (pcm.length !== this._frameLength) {
949
1096
  throw new EagleErrors.EagleInvalidArgumentError(
950
- `Length of input frame (${pcm.length}) does not match required frame length (${Eagle._frameLength})`
1097
+ `Length of input frame (${pcm.length}) does not match required frame length (${this._frameLength})`
951
1098
  );
952
1099
  }
953
1100
 
954
1101
  return new Promise<number[]>((resolve, reject) => {
955
1102
  this._functionMutex
956
1103
  .runExclusive(async () => {
957
- if (this._wasmMemory === undefined) {
958
- throw new EagleErrors.EagleInvalidStateError('Attempted to call `.process` after release');
1104
+ if (this._module === undefined) {
1105
+ throw new EagleErrors.EagleInvalidStateError(
1106
+ 'Attempted to call `.process` after release'
1107
+ );
959
1108
  }
960
1109
 
961
- const pcmAddress = await this._alignedAlloc(
962
- Int16Array.BYTES_PER_ELEMENT,
1110
+ const pcmAddress = this._module._malloc(
963
1111
  pcm.length * Int16Array.BYTES_PER_ELEMENT
964
1112
  );
965
1113
 
966
- const memoryBufferInt16 = new Int16Array(this._wasmMemory.buffer);
967
- memoryBufferInt16.set(pcm, pcmAddress / Int16Array.BYTES_PER_ELEMENT);
1114
+ this._module.HEAP16.set(
1115
+ pcm,
1116
+ pcmAddress / Int16Array.BYTES_PER_ELEMENT
1117
+ );
968
1118
 
969
- const status = await this._pvEagleProcess(
1119
+ const status = await this._pv_eagle_process(
970
1120
  this._objectAddress,
971
1121
  pcmAddress,
972
1122
  this._scoresAddress
973
1123
  );
974
- await this._pvFree(pcmAddress);
975
- if (status !== PV_STATUS_SUCCESS) {
976
- const memoryBufferView = new DataView(this._wasmMemory.buffer);
977
- const memoryBufferUint8 = new Uint8Array(this._wasmMemory.buffer);
1124
+ this._module._pv_free(pcmAddress);
978
1125
 
979
- const messageStack = await EagleProfiler.getMessageStack(
980
- this._pvGetErrorStack,
981
- this._pvFreeErrorStack,
1126
+ if (status !== PV_STATUS_SUCCESS) {
1127
+ const messageStack = await Eagle.getMessageStack(
1128
+ this._module._pv_get_error_stack,
1129
+ this._module._pv_free_error_stack,
982
1130
  this._messageStackAddressAddressAddress,
983
1131
  this._messageStackDepthAddress,
984
- memoryBufferView,
985
- memoryBufferUint8
1132
+ this._module.HEAP32,
1133
+ this._module.HEAPU8
986
1134
  );
987
1135
 
988
- throw pvStatusToException(status, "Eagle process failed", messageStack);
1136
+ throw pvStatusToException(
1137
+ status,
1138
+ 'Eagle process failed',
1139
+ messageStack
1140
+ );
989
1141
  }
990
1142
 
991
- const memoryBufferView = new DataView(this._wasmMemory.buffer);
992
-
993
1143
  const scores: number[] = [];
994
1144
  for (let i = 0; i < this._numSpeakers; i++) {
995
1145
  scores.push(
996
- memoryBufferView.getFloat32(
997
- this._scoresAddress + i * Float32Array.BYTES_PER_ELEMENT,
998
- true
999
- )
1146
+ this._module.HEAPF32[
1147
+ this._scoresAddress / Float32Array.BYTES_PER_ELEMENT + i
1148
+ ]
1000
1149
  );
1001
1150
  }
1002
1151
 
@@ -1020,25 +1169,28 @@ export class Eagle extends EagleBase {
1020
1169
  return new Promise<void>((resolve, reject) => {
1021
1170
  this._functionMutex
1022
1171
  .runExclusive(async () => {
1023
- if (this._wasmMemory === undefined) {
1024
- throw new EagleErrors.EagleInvalidStateError('Attempted to call `.reset` after release');
1172
+ if (this._module === undefined) {
1173
+ throw new EagleErrors.EagleInvalidStateError(
1174
+ 'Attempted to call `.reset` after release'
1175
+ );
1025
1176
  }
1026
1177
 
1027
- const status = await this._pvEagleReset(this._objectAddress);
1178
+ const status = await this._pv_eagle_reset(this._objectAddress);
1028
1179
  if (status !== PV_STATUS_SUCCESS) {
1029
- const memoryBufferView = new DataView(this._wasmMemory.buffer);
1030
- const memoryBufferUint8 = new Uint8Array(this._wasmMemory.buffer);
1031
-
1032
- const messageStack = await EagleProfiler.getMessageStack(
1033
- this._pvGetErrorStack,
1034
- this._pvFreeErrorStack,
1180
+ const messageStack = await Eagle.getMessageStack(
1181
+ this._module._pv_get_error_stack,
1182
+ this._module._pv_free_error_stack,
1035
1183
  this._messageStackAddressAddressAddress,
1036
1184
  this._messageStackDepthAddress,
1037
- memoryBufferView,
1038
- memoryBufferUint8
1185
+ this._module.HEAP32,
1186
+ this._module.HEAPU8
1039
1187
  );
1040
1188
 
1041
- throw pvStatusToException(status, "Eagle reset failed", messageStack);
1189
+ throw pvStatusToException(
1190
+ status,
1191
+ 'Eagle reset failed',
1192
+ messageStack
1193
+ );
1042
1194
  }
1043
1195
  })
1044
1196
  .then(() => {
@@ -1054,127 +1206,292 @@ export class Eagle extends EagleBase {
1054
1206
  * Releases resources acquired by Eagle
1055
1207
  */
1056
1208
  public async release(): Promise<void> {
1209
+ if (!this._module) {
1210
+ return;
1211
+ }
1212
+
1057
1213
  await super.release();
1058
- await this._pvFree(this._scoresAddress);
1059
- await this._pvEagleDelete(this._objectAddress);
1060
- delete this._wasmMemory;
1061
- this._wasmMemory = undefined;
1214
+ await this._pv_eagle_delete(this._objectAddress);
1215
+ this._module = undefined;
1216
+ }
1217
+
1218
+ /**
1219
+ * Lists all available devices that Eagle can use for inference.
1220
+ * Each entry in the list can be the used as the `device` argument for the `.create` method.
1221
+ *
1222
+ * @returns List of all available devices that Eagle can use for inference.
1223
+ */
1224
+ public static async listAvailableDevices(): Promise<string[]> {
1225
+ return new Promise<string[]>((resolve, reject) => {
1226
+ Eagle._eagleMutex
1227
+ .runExclusive(async () => {
1228
+ const isSimd = await simd();
1229
+ if (!isSimd) {
1230
+ throw new EagleErrors.EagleRuntimeError('Unsupported Browser');
1231
+ }
1232
+
1233
+ const blob = new Blob([base64ToUint8Array(this._wasmSimdLib)], {
1234
+ type: 'application/javascript',
1235
+ });
1236
+ const module: EagleModule = await createModuleSimd({
1237
+ mainScriptUrlOrBlob: blob,
1238
+ wasmBinary: base64ToUint8Array(this._wasmSimd),
1239
+ });
1240
+
1241
+ const hardwareDevicesAddressAddress = module._malloc(
1242
+ Int32Array.BYTES_PER_ELEMENT
1243
+ );
1244
+ if (hardwareDevicesAddressAddress === 0) {
1245
+ throw new EagleErrors.EagleOutOfMemoryError(
1246
+ 'malloc failed: Cannot allocate memory for hardwareDevices'
1247
+ );
1248
+ }
1249
+
1250
+ const numHardwareDevicesAddress = module._malloc(
1251
+ Int32Array.BYTES_PER_ELEMENT
1252
+ );
1253
+ if (numHardwareDevicesAddress === 0) {
1254
+ throw new EagleErrors.EagleOutOfMemoryError(
1255
+ 'malloc failed: Cannot allocate memory for numHardwareDevices'
1256
+ );
1257
+ }
1258
+
1259
+ const status: PvStatus = module._pv_eagle_list_hardware_devices(
1260
+ hardwareDevicesAddressAddress,
1261
+ numHardwareDevicesAddress
1262
+ );
1263
+
1264
+ const messageStackDepthAddress = module._malloc(
1265
+ Int32Array.BYTES_PER_ELEMENT
1266
+ );
1267
+ if (!messageStackDepthAddress) {
1268
+ throw new EagleErrors.EagleOutOfMemoryError(
1269
+ 'malloc failed: Cannot allocate memory for messageStackDepth'
1270
+ );
1271
+ }
1272
+
1273
+ const messageStackAddressAddressAddress = module._malloc(
1274
+ Int32Array.BYTES_PER_ELEMENT
1275
+ );
1276
+ if (!messageStackAddressAddressAddress) {
1277
+ throw new EagleErrors.EagleOutOfMemoryError(
1278
+ 'malloc failed: Cannot allocate memory messageStack'
1279
+ );
1280
+ }
1281
+
1282
+ if (status !== PvStatus.SUCCESS) {
1283
+ const messageStack = await Eagle.getMessageStack(
1284
+ module._pv_get_error_stack,
1285
+ module._pv_free_error_stack,
1286
+ messageStackAddressAddressAddress,
1287
+ messageStackDepthAddress,
1288
+ module.HEAP32,
1289
+ module.HEAPU8
1290
+ );
1291
+ module._pv_free(messageStackAddressAddressAddress);
1292
+ module._pv_free(messageStackDepthAddress);
1293
+
1294
+ throw pvStatusToException(
1295
+ status,
1296
+ 'List devices failed',
1297
+ messageStack
1298
+ );
1299
+ }
1300
+ module._pv_free(messageStackAddressAddressAddress);
1301
+ module._pv_free(messageStackDepthAddress);
1302
+
1303
+ const numHardwareDevices: number =
1304
+ module.HEAP32[
1305
+ numHardwareDevicesAddress / Int32Array.BYTES_PER_ELEMENT
1306
+ ];
1307
+ module._pv_free(numHardwareDevicesAddress);
1308
+
1309
+ const hardwareDevicesAddress =
1310
+ module.HEAP32[
1311
+ hardwareDevicesAddressAddress / Int32Array.BYTES_PER_ELEMENT
1312
+ ];
1313
+
1314
+ const hardwareDevices: string[] = [];
1315
+ for (let i = 0; i < numHardwareDevices; i++) {
1316
+ const deviceAddress =
1317
+ module.HEAP32[
1318
+ hardwareDevicesAddress / Int32Array.BYTES_PER_ELEMENT + i
1319
+ ];
1320
+ hardwareDevices.push(
1321
+ arrayBufferToStringAtIndex(module.HEAPU8, deviceAddress)
1322
+ );
1323
+ }
1324
+ module._pv_eagle_free_hardware_devices(
1325
+ hardwareDevicesAddress,
1326
+ numHardwareDevices
1327
+ );
1328
+ module._pv_free(hardwareDevicesAddressAddress);
1329
+
1330
+ return hardwareDevices;
1331
+ })
1332
+ .then((result: string[]) => {
1333
+ resolve(result);
1334
+ })
1335
+ .catch((error: any) => {
1336
+ reject(error);
1337
+ });
1338
+ });
1062
1339
  }
1063
1340
 
1064
1341
  private static async _initWasm(
1065
1342
  accessKey: string,
1066
1343
  modelPath: string,
1344
+ device: string,
1067
1345
  speakerProfiles: EagleProfile[],
1068
- wasmBase64: string
1346
+ wasmBase64: string,
1347
+ wasmLibBase64: string,
1348
+ createModuleFunc: any
1069
1349
  ): Promise<EagleWasmOutput> {
1070
- const baseWasmOutput = await super._initBaseWasm(wasmBase64, 3150);
1071
- const memoryBufferUint8 = new Uint8Array(baseWasmOutput.memory.buffer);
1072
- const memoryBufferInt32 = new Int32Array(baseWasmOutput.memory.buffer);
1073
-
1074
- const pv_eagle_init = baseWasmOutput.exports
1075
- .pv_eagle_init as pv_eagle_init_type;
1076
- const pv_eagle_delete = baseWasmOutput.exports
1077
- .pv_eagle_delete as pv_eagle_delete_type;
1078
- const pv_eagle_process = baseWasmOutput.exports
1079
- .pv_eagle_process as pv_eagle_process_type;
1080
- const pv_eagle_frame_length = baseWasmOutput.exports
1081
- .pv_eagle_frame_length as pv_eagle_frame_length_type;
1082
- const pv_eagle_reset = baseWasmOutput.exports
1083
- .pv_eagle_reset as pv_eagle_reset_type;
1084
-
1085
- const objectAddressAddress = await baseWasmOutput.alignedAlloc(
1086
- Int32Array.BYTES_PER_ELEMENT,
1350
+ const baseWasmOutput = await super._initBaseWasm(
1351
+ wasmBase64,
1352
+ wasmLibBase64,
1353
+ createModuleFunc
1354
+ );
1355
+
1356
+ const pv_eagle_init: pv_eagle_init_type = this.wrapAsyncFunction(
1357
+ baseWasmOutput.module,
1358
+ 'pv_eagle_init',
1359
+ 6
1360
+ );
1361
+ const pv_eagle_process: pv_eagle_process_type = this.wrapAsyncFunction(
1362
+ baseWasmOutput.module,
1363
+ 'pv_eagle_process',
1364
+ 3
1365
+ );
1366
+ const pv_eagle_reset: pv_eagle_reset_type = this.wrapAsyncFunction(
1367
+ baseWasmOutput.module,
1368
+ 'pv_eagle_reset',
1369
+ 1
1370
+ );
1371
+ const pv_eagle_delete: pv_eagle_delete_type = this.wrapAsyncFunction(
1372
+ baseWasmOutput.module,
1373
+ 'pv_eagle_delete',
1374
+ 1
1375
+ );
1376
+
1377
+ const objectAddressAddress = baseWasmOutput.module._malloc(
1087
1378
  Int32Array.BYTES_PER_ELEMENT
1088
1379
  );
1089
1380
  if (objectAddressAddress === 0) {
1090
- throw new EagleErrors.EagleOutOfMemoryError('malloc failed: Cannot allocate memory');
1381
+ throw new EagleErrors.EagleOutOfMemoryError(
1382
+ 'malloc failed: Cannot allocate memory'
1383
+ );
1091
1384
  }
1092
1385
 
1093
- const accessKeyAddress = await baseWasmOutput.alignedAlloc(
1094
- Uint8Array.BYTES_PER_ELEMENT,
1386
+ const accessKeyAddress = baseWasmOutput.module._malloc(
1095
1387
  (accessKey.length + 1) * Uint8Array.BYTES_PER_ELEMENT
1096
1388
  );
1097
1389
  if (accessKeyAddress === 0) {
1098
- throw new EagleErrors.EagleOutOfMemoryError('malloc failed: Cannot allocate memory');
1390
+ throw new EagleErrors.EagleOutOfMemoryError(
1391
+ 'malloc failed: Cannot allocate memory'
1392
+ );
1099
1393
  }
1100
1394
  for (let i = 0; i < accessKey.length; i++) {
1101
- memoryBufferUint8[accessKeyAddress + i] = accessKey.charCodeAt(i);
1395
+ baseWasmOutput.module.HEAPU8[accessKeyAddress + i] =
1396
+ accessKey.charCodeAt(i);
1102
1397
  }
1103
- memoryBufferUint8[accessKeyAddress + accessKey.length] = 0;
1398
+ baseWasmOutput.module.HEAPU8[accessKeyAddress + accessKey.length] = 0;
1104
1399
 
1105
1400
  const modelPathEncoded = new TextEncoder().encode(modelPath);
1106
- const modelPathAddress = await baseWasmOutput.alignedAlloc(
1107
- Uint8Array.BYTES_PER_ELEMENT,
1401
+ const modelPathAddress = baseWasmOutput.module._malloc(
1108
1402
  (modelPathEncoded.length + 1) * Uint8Array.BYTES_PER_ELEMENT
1109
1403
  );
1110
1404
  if (modelPathAddress === 0) {
1111
- throw new EagleErrors.EagleOutOfMemoryError('malloc failed: Cannot allocate memory');
1405
+ throw new EagleErrors.EagleOutOfMemoryError(
1406
+ 'malloc failed: Cannot allocate memory'
1407
+ );
1112
1408
  }
1113
- memoryBufferUint8.set(modelPathEncoded, modelPathAddress);
1114
- memoryBufferUint8[modelPathAddress + modelPathEncoded.length] = 0;
1409
+ baseWasmOutput.module.HEAPU8.set(modelPathEncoded, modelPathAddress);
1410
+ baseWasmOutput.module.HEAPU8[
1411
+ modelPathAddress + modelPathEncoded.length
1412
+ ] = 0;
1413
+
1414
+ const deviceAddress = baseWasmOutput.module._malloc(
1415
+ (device.length + 1) * Uint8Array.BYTES_PER_ELEMENT
1416
+ );
1417
+ if (deviceAddress === 0) {
1418
+ throw new EagleErrors.EagleOutOfMemoryError(
1419
+ 'malloc failed: Cannot allocate memory'
1420
+ );
1421
+ }
1422
+ for (let i = 0; i < device.length; i++) {
1423
+ baseWasmOutput.module.HEAPU8[deviceAddress + i] = device.charCodeAt(i);
1424
+ }
1425
+ baseWasmOutput.module.HEAPU8[deviceAddress + device.length] = 0;
1115
1426
 
1116
1427
  const numSpeakers = speakerProfiles.length;
1117
- const profilesAddressAddress = await baseWasmOutput.alignedAlloc(
1118
- Int32Array.BYTES_PER_ELEMENT,
1428
+ const profilesAddressAddress = baseWasmOutput.module._malloc(
1119
1429
  numSpeakers * Int32Array.BYTES_PER_ELEMENT
1120
1430
  );
1121
1431
  if (profilesAddressAddress === 0) {
1122
- throw new EagleErrors.EagleOutOfMemoryError('malloc failed: Cannot allocate memory');
1432
+ throw new EagleErrors.EagleOutOfMemoryError(
1433
+ 'malloc failed: Cannot allocate memory'
1434
+ );
1123
1435
  }
1124
1436
  const profilesAddressList: number[] = [];
1125
1437
  for (const profile of speakerProfiles) {
1126
- const profileAddress = await baseWasmOutput.alignedAlloc(
1127
- Uint8Array.BYTES_PER_ELEMENT,
1438
+ const profileAddress = baseWasmOutput.module._malloc(
1128
1439
  profile.bytes.length * Uint8Array.BYTES_PER_ELEMENT
1129
1440
  );
1130
1441
  if (profileAddress === 0) {
1131
- throw new EagleErrors.EagleOutOfMemoryError('malloc failed: Cannot allocate memory');
1442
+ throw new EagleErrors.EagleOutOfMemoryError(
1443
+ 'malloc failed: Cannot allocate memory'
1444
+ );
1132
1445
  }
1133
- memoryBufferUint8.set(profile.bytes, profileAddress);
1446
+ baseWasmOutput.module.HEAPU8.set(profile.bytes, profileAddress);
1134
1447
  profilesAddressList.push(profileAddress);
1135
1448
  }
1136
- memoryBufferInt32.set(
1449
+ baseWasmOutput.module.HEAP32.set(
1137
1450
  new Int32Array(profilesAddressList),
1138
1451
  profilesAddressAddress / Int32Array.BYTES_PER_ELEMENT
1139
1452
  );
1140
1453
  const status = await pv_eagle_init(
1141
1454
  accessKeyAddress,
1142
1455
  modelPathAddress,
1456
+ deviceAddress,
1143
1457
  numSpeakers,
1144
1458
  profilesAddressAddress,
1145
1459
  objectAddressAddress
1146
1460
  );
1147
- await baseWasmOutput.pvFree(accessKeyAddress);
1148
- await baseWasmOutput.pvFree(modelPathAddress);
1149
- await baseWasmOutput.pvFree(profilesAddressAddress);
1150
-
1151
- const memoryBufferView = new DataView(baseWasmOutput.memory.buffer);
1461
+ baseWasmOutput.module._pv_free(accessKeyAddress);
1462
+ baseWasmOutput.module._pv_free(modelPathAddress);
1463
+ baseWasmOutput.module._pv_free(deviceAddress);
1464
+ baseWasmOutput.module._pv_free(profilesAddressAddress);
1152
1465
 
1153
1466
  if (status !== PV_STATUS_SUCCESS) {
1154
- const messageStack = await EagleProfiler.getMessageStack(
1155
- baseWasmOutput.pvGetErrorStack,
1156
- baseWasmOutput.pvFreeErrorStack,
1467
+ const messageStack = await Eagle.getMessageStack(
1468
+ baseWasmOutput.module._pv_get_error_stack,
1469
+ baseWasmOutput.module._pv_free_error_stack,
1157
1470
  baseWasmOutput.messageStackAddressAddressAddress,
1158
1471
  baseWasmOutput.messageStackDepthAddress,
1159
- memoryBufferView,
1160
- memoryBufferUint8
1472
+ baseWasmOutput.module.HEAP32,
1473
+ baseWasmOutput.module.HEAPU8
1161
1474
  );
1162
1475
 
1163
- throw pvStatusToException(status, "Eagle init failed", messageStack, baseWasmOutput.pvError);
1476
+ throw pvStatusToException(status, 'Eagle init failed', messageStack);
1164
1477
  }
1165
1478
 
1166
- const objectAddress = memoryBufferView.getInt32(objectAddressAddress, true);
1167
- await baseWasmOutput.pvFree(objectAddressAddress);
1479
+ const objectAddress =
1480
+ baseWasmOutput.module.HEAP32[
1481
+ objectAddressAddress / Int32Array.BYTES_PER_ELEMENT
1482
+ ];
1483
+ baseWasmOutput.module._pv_free(objectAddressAddress);
1168
1484
 
1169
- const scoresAddress = await baseWasmOutput.alignedAlloc(
1170
- Float32Array.BYTES_PER_ELEMENT,
1171
- numSpeakers * Float32Array.BYTES_PER_ELEMENT
1485
+ const scoresAddress = baseWasmOutput.module._malloc(
1486
+ Float32Array.BYTES_PER_ELEMENT * numSpeakers
1172
1487
  );
1173
1488
  if (scoresAddress === 0) {
1174
- throw new EagleErrors.EagleOutOfMemoryError('malloc failed: Cannot allocate memory');
1489
+ throw new EagleErrors.EagleOutOfMemoryError(
1490
+ 'malloc failed: Cannot allocate memory'
1491
+ );
1175
1492
  }
1176
1493
 
1177
- const frameLength = await pv_eagle_frame_length();
1494
+ const frameLength = baseWasmOutput.module._pv_eagle_frame_length();
1178
1495
 
1179
1496
  return {
1180
1497
  ...baseWasmOutput,
@@ -1183,9 +1500,9 @@ export class Eagle extends EagleBase {
1183
1500
  objectAddress: objectAddress,
1184
1501
  scoresAddress: scoresAddress,
1185
1502
 
1186
- pvEagleDelete: pv_eagle_delete,
1187
- pvEagleProcess: pv_eagle_process,
1188
- pvEagleReset: pv_eagle_reset,
1503
+ pv_eagle_process: pv_eagle_process,
1504
+ pv_eagle_reset: pv_eagle_reset,
1505
+ pv_eagle_delete: pv_eagle_delete,
1189
1506
  };
1190
1507
  }
1191
1508
  }