@picovoice/eagle-web 1.0.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/README.md +20 -3
- package/dist/esm/index.js +14465 -1388
- package/dist/esm/index.min.js +1 -1
- package/dist/iife/index.js +14465 -1388
- package/dist/iife/index.min.js +1 -1
- package/dist/types/eagle.d.ts +90 -45
- package/dist/types/eagle.d.ts.map +1 -1
- package/dist/types/eagle_profiler_worker.d.ts +27 -9
- package/dist/types/eagle_profiler_worker.d.ts.map +1 -1
- package/dist/types/eagle_worker.d.ts +27 -9
- package/dist/types/eagle_worker.d.ts.map +1 -1
- package/dist/types/index.d.ts +2 -2
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/types.d.ts +16 -2
- package/dist/types/types.d.ts.map +1 -1
- package/module.d.ts +5 -0
- package/package.json +6 -5
- package/rollup.config.js +1 -1
- package/src/eagle.ts +768 -451
- package/src/eagle_profiler_worker.ts +120 -30
- package/src/eagle_profiler_worker_handler.ts +9 -3
- package/src/eagle_worker.ts +106 -27
- package/src/eagle_worker_handler.ts +6 -3
- package/src/index.ts +21 -6
- package/src/types.ts +19 -3
- package/tsconfig.json +2 -2
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
|
-
) =>
|
|
60
|
+
) => number;
|
|
58
61
|
type pv_eagle_profiler_export_type = (
|
|
59
62
|
object: number,
|
|
60
63
|
speakerProfile: number
|
|
61
|
-
) =>
|
|
64
|
+
) => number;
|
|
62
65
|
type pv_eagle_profiler_export_size_type = (
|
|
63
66
|
object: number,
|
|
64
67
|
speakerProfileSizeBytes: number
|
|
65
|
-
) =>
|
|
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 = () =>
|
|
82
|
-
type pv_eagle_version_type = () =>
|
|
83
|
-
type
|
|
84
|
-
|
|
85
|
-
|
|
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
|
-
) =>
|
|
90
|
-
type pv_free_error_stack_type = (messageStack: number) =>
|
|
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
|
-
|
|
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
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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
|
-
|
|
131
|
-
|
|
132
|
-
|
|
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
|
|
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
|
|
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
|
|
152
|
-
protected
|
|
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
|
-
|
|
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.
|
|
174
|
-
this.
|
|
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
|
|
200
|
+
return this._sampleRate;
|
|
187
201
|
}
|
|
188
202
|
|
|
189
203
|
/**
|
|
190
204
|
* Version of Eagle.
|
|
191
205
|
*/
|
|
192
206
|
get version(): string {
|
|
193
|
-
return
|
|
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
|
-
|
|
256
|
+
wasmLibBase64: string,
|
|
257
|
+
createModuleFunc: any,
|
|
223
258
|
): Promise<EagleBaseWasmOutput> {
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
const
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
const
|
|
234
|
-
const
|
|
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
|
-
|
|
246
|
-
versionAddress
|
|
271
|
+
module.HEAPU8,
|
|
272
|
+
versionAddress,
|
|
247
273
|
);
|
|
248
274
|
|
|
249
275
|
const sdkEncoded = new TextEncoder().encode(this._sdk);
|
|
250
|
-
const sdkAddress =
|
|
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
|
-
|
|
260
|
-
|
|
261
|
-
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
308
|
-
|
|
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
|
-
|
|
326
|
+
memoryBufferInt32: Int32Array,
|
|
317
327
|
memoryBufferUint8: Uint8Array
|
|
318
328
|
): Promise<string[]> {
|
|
319
|
-
const status =
|
|
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 =
|
|
328
|
-
messageStackAddressAddressAddress,
|
|
329
|
-
true
|
|
330
|
-
);
|
|
334
|
+
const messageStackAddressAddress = memoryBufferInt32[messageStackAddressAddressAddress / Int32Array.BYTES_PER_ELEMENT];
|
|
331
335
|
|
|
332
|
-
const messageStackDepth =
|
|
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 =
|
|
339
|
-
messageStackAddressAddress
|
|
340
|
-
|
|
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
|
-
|
|
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
|
|
361
|
-
private readonly
|
|
362
|
-
private readonly
|
|
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
|
|
368
|
-
private
|
|
369
|
-
private
|
|
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
|
-
|
|
375
|
-
|
|
376
|
-
|
|
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.
|
|
380
|
-
this.
|
|
381
|
-
this.
|
|
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
|
|
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
|
-
|
|
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(
|
|
515
|
+
throw new EagleErrors.EagleInvalidArgumentError(
|
|
516
|
+
"The argument 'pcm' must be provided as an Int16Array"
|
|
517
|
+
);
|
|
476
518
|
}
|
|
477
519
|
|
|
478
|
-
if (pcm.length >
|
|
520
|
+
if (pcm.length > this._maxEnrollSamples) {
|
|
479
521
|
throw new EagleErrors.EagleInvalidArgumentError(
|
|
480
|
-
`'pcm' size must be smaller than ${
|
|
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.
|
|
488
|
-
throw new EagleErrors.EagleInvalidStateError(
|
|
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 =
|
|
492
|
-
Int16Array.BYTES_PER_ELEMENT,
|
|
535
|
+
const pcmAddress = this._module._malloc(
|
|
493
536
|
pcm.length * Int16Array.BYTES_PER_ELEMENT
|
|
494
537
|
);
|
|
495
538
|
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
539
|
+
this._module.HEAP16.set(
|
|
540
|
+
pcm,
|
|
541
|
+
pcmAddress / Int16Array.BYTES_PER_ELEMENT
|
|
499
542
|
);
|
|
500
|
-
|
|
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
|
-
|
|
519
|
-
|
|
547
|
+
this._feedbackAddress,
|
|
548
|
+
this._percentageAddress
|
|
520
549
|
);
|
|
521
|
-
|
|
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.
|
|
531
|
-
this.
|
|
554
|
+
this._module._pv_get_error_stack,
|
|
555
|
+
this._module._pv_free_error_stack,
|
|
532
556
|
this._messageStackAddressAddressAddress,
|
|
533
557
|
this._messageStackDepthAddress,
|
|
534
|
-
|
|
535
|
-
|
|
558
|
+
this._module.HEAP32,
|
|
559
|
+
this._module.HEAPU8
|
|
536
560
|
);
|
|
537
561
|
|
|
538
|
-
throw pvStatusToException(
|
|
562
|
+
throw pvStatusToException(
|
|
563
|
+
status,
|
|
564
|
+
'EagleProfiler enroll failed',
|
|
565
|
+
messageStack
|
|
566
|
+
);
|
|
539
567
|
}
|
|
540
568
|
|
|
541
|
-
const
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
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.
|
|
578
|
-
throw new EagleErrors.EagleInvalidStateError(
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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.
|
|
598
|
-
this.
|
|
617
|
+
this._module._pv_get_error_stack,
|
|
618
|
+
this._module._pv_free_error_stack,
|
|
599
619
|
this._messageStackAddressAddressAddress,
|
|
600
620
|
this._messageStackDepthAddress,
|
|
601
|
-
|
|
602
|
-
|
|
621
|
+
this._module.HEAP32,
|
|
622
|
+
this._module.HEAPU8
|
|
603
623
|
);
|
|
604
624
|
|
|
605
|
-
throw pvStatusToException(
|
|
625
|
+
throw pvStatusToException(
|
|
626
|
+
status,
|
|
627
|
+
'EagleProfiler export failed',
|
|
628
|
+
messageStack
|
|
629
|
+
);
|
|
606
630
|
}
|
|
607
631
|
|
|
608
|
-
const
|
|
609
|
-
|
|
610
|
-
|
|
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
|
-
|
|
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.
|
|
637
|
-
throw new EagleErrors.EagleInvalidStateError(
|
|
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.
|
|
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.
|
|
647
|
-
this.
|
|
668
|
+
this._module._pv_get_error_stack,
|
|
669
|
+
this._module._pv_free_error_stack,
|
|
648
670
|
this._messageStackAddressAddressAddress,
|
|
649
671
|
this._messageStackDepthAddress,
|
|
650
|
-
|
|
651
|
-
|
|
672
|
+
this._module.HEAP32,
|
|
673
|
+
this._module.HEAPU8
|
|
652
674
|
);
|
|
653
675
|
|
|
654
|
-
throw pvStatusToException(
|
|
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.
|
|
672
|
-
|
|
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
|
-
|
|
708
|
+
device: string,
|
|
709
|
+
wasmBase64: string,
|
|
710
|
+
wasmLibBase64: string,
|
|
711
|
+
createModuleFunc: any
|
|
680
712
|
): Promise<EagleProfilerWasmOutput> {
|
|
681
|
-
const baseWasmOutput = await super._initBaseWasm(
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
const
|
|
694
|
-
.
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
const
|
|
701
|
-
|
|
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(
|
|
750
|
+
throw new EagleErrors.EagleOutOfMemoryError(
|
|
751
|
+
'malloc failed: Cannot allocate memory'
|
|
752
|
+
);
|
|
706
753
|
}
|
|
707
754
|
|
|
708
|
-
const accessKeyAddress =
|
|
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(
|
|
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
|
-
|
|
764
|
+
baseWasmOutput.module.HEAPU8[accessKeyAddress + i] =
|
|
765
|
+
accessKey.charCodeAt(i);
|
|
717
766
|
}
|
|
718
|
-
|
|
767
|
+
baseWasmOutput.module.HEAPU8[accessKeyAddress + accessKey.length] = 0;
|
|
719
768
|
|
|
720
769
|
const modelPathEncoded = new TextEncoder().encode(modelPath);
|
|
721
|
-
const modelPathAddress =
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
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.
|
|
744
|
-
baseWasmOutput.
|
|
807
|
+
baseWasmOutput.module._pv_get_error_stack,
|
|
808
|
+
baseWasmOutput.module._pv_free_error_stack,
|
|
745
809
|
baseWasmOutput.messageStackAddressAddressAddress,
|
|
746
810
|
baseWasmOutput.messageStackDepthAddress,
|
|
747
|
-
|
|
748
|
-
|
|
811
|
+
baseWasmOutput.module.HEAP32,
|
|
812
|
+
baseWasmOutput.module.HEAPU8
|
|
749
813
|
);
|
|
750
814
|
|
|
751
|
-
throw pvStatusToException(status,
|
|
815
|
+
throw pvStatusToException(status, 'Initialization failed', messageStack);
|
|
752
816
|
}
|
|
753
817
|
|
|
754
|
-
const objectAddress =
|
|
755
|
-
|
|
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 =
|
|
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(
|
|
828
|
+
throw new EagleErrors.EagleOutOfMemoryError(
|
|
829
|
+
'malloc failed: Cannot allocate memory'
|
|
830
|
+
);
|
|
763
831
|
}
|
|
764
832
|
|
|
765
|
-
status =
|
|
766
|
-
|
|
767
|
-
|
|
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.
|
|
772
|
-
baseWasmOutput.
|
|
840
|
+
baseWasmOutput.module._pv_get_error_stack,
|
|
841
|
+
baseWasmOutput.module._pv_free_error_stack,
|
|
773
842
|
baseWasmOutput.messageStackAddressAddressAddress,
|
|
774
843
|
baseWasmOutput.messageStackDepthAddress,
|
|
775
|
-
|
|
776
|
-
|
|
844
|
+
baseWasmOutput.module.HEAP32,
|
|
845
|
+
baseWasmOutput.module.HEAPU8
|
|
777
846
|
);
|
|
778
847
|
|
|
779
|
-
throw pvStatusToException(
|
|
848
|
+
throw pvStatusToException(
|
|
849
|
+
status,
|
|
850
|
+
'EagleProfiler failed to get min enroll audio length',
|
|
851
|
+
messageStack
|
|
852
|
+
);
|
|
780
853
|
}
|
|
781
854
|
|
|
782
|
-
const minEnrollSamples =
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
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 =
|
|
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(
|
|
865
|
+
throw new EagleErrors.EagleOutOfMemoryError(
|
|
866
|
+
'malloc failed: Cannot allocate memory'
|
|
867
|
+
);
|
|
794
868
|
}
|
|
795
869
|
|
|
796
|
-
status =
|
|
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.
|
|
803
|
-
baseWasmOutput.
|
|
876
|
+
baseWasmOutput.module._pv_get_error_stack,
|
|
877
|
+
baseWasmOutput.module._pv_free_error_stack,
|
|
804
878
|
baseWasmOutput.messageStackAddressAddressAddress,
|
|
805
879
|
baseWasmOutput.messageStackDepthAddress,
|
|
806
|
-
|
|
807
|
-
|
|
880
|
+
baseWasmOutput.module.HEAP32,
|
|
881
|
+
baseWasmOutput.module.HEAPU8
|
|
808
882
|
);
|
|
809
883
|
|
|
810
|
-
throw pvStatusToException(
|
|
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
|
|
814
|
-
|
|
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
|
-
|
|
824
|
-
|
|
825
|
-
|
|
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
|
|
837
|
-
private readonly
|
|
838
|
-
private readonly
|
|
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
|
|
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
|
-
|
|
959
|
+
this._frameLength = handleWasm.frameLength;
|
|
960
|
+
this._numSpeakers = handleWasm.numSpeakers;
|
|
850
961
|
|
|
851
|
-
this.
|
|
852
|
-
this.
|
|
853
|
-
this.
|
|
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
|
|
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(
|
|
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
|
-
|
|
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(
|
|
1090
|
+
throw new EagleErrors.EagleInvalidArgumentError(
|
|
1091
|
+
"The argument 'pcm' must be provided as an Int16Array"
|
|
1092
|
+
);
|
|
946
1093
|
}
|
|
947
1094
|
|
|
948
|
-
if (pcm.length !==
|
|
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 (${
|
|
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.
|
|
958
|
-
throw new EagleErrors.EagleInvalidStateError(
|
|
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 =
|
|
962
|
-
Int16Array.BYTES_PER_ELEMENT,
|
|
1110
|
+
const pcmAddress = this._module._malloc(
|
|
963
1111
|
pcm.length * Int16Array.BYTES_PER_ELEMENT
|
|
964
1112
|
);
|
|
965
1113
|
|
|
966
|
-
|
|
967
|
-
|
|
1114
|
+
this._module.HEAP16.set(
|
|
1115
|
+
pcm,
|
|
1116
|
+
pcmAddress / Int16Array.BYTES_PER_ELEMENT
|
|
1117
|
+
);
|
|
968
1118
|
|
|
969
|
-
const status = await this.
|
|
1119
|
+
const status = await this._pv_eagle_process(
|
|
970
1120
|
this._objectAddress,
|
|
971
1121
|
pcmAddress,
|
|
972
1122
|
this._scoresAddress
|
|
973
1123
|
);
|
|
974
|
-
|
|
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
|
-
|
|
980
|
-
|
|
981
|
-
this.
|
|
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
|
-
|
|
985
|
-
|
|
1132
|
+
this._module.HEAP32,
|
|
1133
|
+
this._module.HEAPU8
|
|
986
1134
|
);
|
|
987
1135
|
|
|
988
|
-
throw pvStatusToException(
|
|
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
|
-
|
|
997
|
-
this._scoresAddress
|
|
998
|
-
|
|
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.
|
|
1024
|
-
throw new EagleErrors.EagleInvalidStateError(
|
|
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.
|
|
1178
|
+
const status = await this._pv_eagle_reset(this._objectAddress);
|
|
1028
1179
|
if (status !== PV_STATUS_SUCCESS) {
|
|
1029
|
-
const
|
|
1030
|
-
|
|
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
|
-
|
|
1038
|
-
|
|
1185
|
+
this._module.HEAP32,
|
|
1186
|
+
this._module.HEAPU8
|
|
1039
1187
|
);
|
|
1040
1188
|
|
|
1041
|
-
throw pvStatusToException(
|
|
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.
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
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(
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
const
|
|
1077
|
-
.
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
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(
|
|
1381
|
+
throw new EagleErrors.EagleOutOfMemoryError(
|
|
1382
|
+
'malloc failed: Cannot allocate memory'
|
|
1383
|
+
);
|
|
1091
1384
|
}
|
|
1092
1385
|
|
|
1093
|
-
const accessKeyAddress =
|
|
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(
|
|
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
|
-
|
|
1395
|
+
baseWasmOutput.module.HEAPU8[accessKeyAddress + i] =
|
|
1396
|
+
accessKey.charCodeAt(i);
|
|
1102
1397
|
}
|
|
1103
|
-
|
|
1398
|
+
baseWasmOutput.module.HEAPU8[accessKeyAddress + accessKey.length] = 0;
|
|
1104
1399
|
|
|
1105
1400
|
const modelPathEncoded = new TextEncoder().encode(modelPath);
|
|
1106
|
-
const modelPathAddress =
|
|
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(
|
|
1405
|
+
throw new EagleErrors.EagleOutOfMemoryError(
|
|
1406
|
+
'malloc failed: Cannot allocate memory'
|
|
1407
|
+
);
|
|
1112
1408
|
}
|
|
1113
|
-
|
|
1114
|
-
|
|
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 =
|
|
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(
|
|
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 =
|
|
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(
|
|
1442
|
+
throw new EagleErrors.EagleOutOfMemoryError(
|
|
1443
|
+
'malloc failed: Cannot allocate memory'
|
|
1444
|
+
);
|
|
1132
1445
|
}
|
|
1133
|
-
|
|
1446
|
+
baseWasmOutput.module.HEAPU8.set(profile.bytes, profileAddress);
|
|
1134
1447
|
profilesAddressList.push(profileAddress);
|
|
1135
1448
|
}
|
|
1136
|
-
|
|
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
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
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
|
|
1155
|
-
baseWasmOutput.
|
|
1156
|
-
baseWasmOutput.
|
|
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
|
-
|
|
1160
|
-
|
|
1472
|
+
baseWasmOutput.module.HEAP32,
|
|
1473
|
+
baseWasmOutput.module.HEAPU8
|
|
1161
1474
|
);
|
|
1162
1475
|
|
|
1163
|
-
throw pvStatusToException(status,
|
|
1476
|
+
throw pvStatusToException(status, 'Eagle init failed', messageStack);
|
|
1164
1477
|
}
|
|
1165
1478
|
|
|
1166
|
-
const objectAddress =
|
|
1167
|
-
|
|
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 =
|
|
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(
|
|
1489
|
+
throw new EagleErrors.EagleOutOfMemoryError(
|
|
1490
|
+
'malloc failed: Cannot allocate memory'
|
|
1491
|
+
);
|
|
1175
1492
|
}
|
|
1176
1493
|
|
|
1177
|
-
const frameLength =
|
|
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
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
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
|
}
|