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