@ylink-sdk/mobile-web 0.1.6 → 0.1.9-beta.1

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.
Files changed (55) hide show
  1. package/dist/e30037b8/VERSION +6 -0
  2. package/dist/e30037b8/libipvp-simd.d.ts +474 -0
  3. package/dist/e30037b8/libipvp-simd.js +3267 -0
  4. package/dist/e30037b8/libipvp-simd.prod.d.ts +474 -0
  5. package/dist/e30037b8/libipvp-simd.prod.js +184 -0
  6. package/dist/e30037b8/libipvp-simd.prod.wasm +0 -0
  7. package/dist/e30037b8/libipvp-simd.wasm +0 -0
  8. package/dist/e30037b8/libipvp.d.ts +474 -0
  9. package/dist/e30037b8/libipvp.js +3267 -0
  10. package/dist/e30037b8/libipvp.prod.d.ts +474 -0
  11. package/dist/e30037b8/libipvp.prod.js +184 -0
  12. package/dist/e30037b8/libipvp.prod.wasm +0 -0
  13. package/dist/e30037b8/libipvp.wasm +0 -0
  14. package/dist/e30037b8/librtc.d.ts +438 -0
  15. package/dist/e30037b8/librtc.global.js +5863 -0
  16. package/dist/e30037b8/librtc.global.prod.js +15 -0
  17. package/dist/e30037b8/libsfu-simd.d.ts +767 -0
  18. package/dist/e30037b8/libsfu-simd.js +4822 -0
  19. package/dist/e30037b8/libsfu-simd.prod.d.ts +767 -0
  20. package/dist/e30037b8/libsfu-simd.prod.js +209 -0
  21. package/dist/e30037b8/libsfu-simd.prod.wasm +0 -0
  22. package/dist/e30037b8/libsfu-simd.wasm +0 -0
  23. package/dist/e30037b8/libsfu.d.ts +767 -0
  24. package/dist/e30037b8/libsfu.js +4822 -0
  25. package/dist/e30037b8/libsfu.prod.d.ts +767 -0
  26. package/dist/e30037b8/libsfu.prod.js +209 -0
  27. package/dist/e30037b8/libsfu.prod.wasm +0 -0
  28. package/dist/e30037b8/libsfu.wasm +0 -0
  29. package/dist/e30037b8/libsvc-simd-m1.d.ts +767 -0
  30. package/dist/e30037b8/libsvc-simd-m1.js +4921 -0
  31. package/dist/e30037b8/libsvc-simd-m1.prod.d.ts +767 -0
  32. package/dist/e30037b8/libsvc-simd-m1.prod.js +209 -0
  33. package/dist/e30037b8/libsvc-simd-m1.prod.wasm +0 -0
  34. package/dist/e30037b8/libsvc-simd-m1.wasm +0 -0
  35. package/dist/e30037b8/libsvc-simd.d.ts +767 -0
  36. package/dist/e30037b8/libsvc-simd.js +4857 -0
  37. package/dist/e30037b8/libsvc-simd.prod.d.ts +767 -0
  38. package/dist/e30037b8/libsvc-simd.prod.js +209 -0
  39. package/dist/e30037b8/libsvc-simd.prod.wasm +0 -0
  40. package/dist/e30037b8/libsvc-simd.wasm +0 -0
  41. package/dist/e30037b8/libsvc.d.ts +767 -0
  42. package/dist/e30037b8/libsvc.js +4857 -0
  43. package/dist/e30037b8/libsvc.prod.d.ts +767 -0
  44. package/dist/e30037b8/libsvc.prod.js +209 -0
  45. package/dist/e30037b8/libsvc.prod.wasm +0 -0
  46. package/dist/e30037b8/libsvc.wasm +0 -0
  47. package/dist/e30037b8/native-audio-worker.global.js +1583 -0
  48. package/dist/e30037b8/native-audio-worker.global.prod.js +15 -0
  49. package/dist/e30037b8/native-video-worker.global.js +1960 -0
  50. package/dist/e30037b8/native-video-worker.global.prod.js +15 -0
  51. package/dist/e30037b8/worklet-audio-worker.global.js +900 -0
  52. package/dist/e30037b8/worklet-audio-worker.global.prod.js +15 -0
  53. package/dist/style.css +1 -1
  54. package/dist/ylink-sdk-mobile.umd.js +70 -70
  55. package/package.json +1 -1
@@ -0,0 +1,1583 @@
1
+ (function () {
2
+ 'use strict';
3
+
4
+ /*! *****************************************************************************
5
+ Copyright (c) Microsoft Corporation.
6
+
7
+ Permission to use, copy, modify, and/or distribute this software for any
8
+ purpose with or without fee is hereby granted.
9
+
10
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
11
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
12
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
13
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
14
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
15
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
16
+ PERFORMANCE OF THIS SOFTWARE.
17
+ ***************************************************************************** */
18
+
19
+ function __awaiter(thisArg, _arguments, P, generator) {
20
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
21
+ return new (P || (P = Promise))(function (resolve, reject) {
22
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
23
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
24
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
25
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
26
+ });
27
+ }
28
+
29
+ function pipeline(middleware) {
30
+ return (context, next) => {
31
+ // last called middleware
32
+ let index = -1;
33
+ const run = (i) => __awaiter(this, void 0, void 0, function* () {
34
+ if (i <= index) {
35
+ console.warn('next() called multiple times');
36
+ return;
37
+ }
38
+ index = i;
39
+ let callback = middleware[i];
40
+ if (i === middleware.length)
41
+ callback = next;
42
+ if (!callback)
43
+ return;
44
+ if (callback.length > 1) {
45
+ return callback(context, dispatch);
46
+ }
47
+ yield callback(context, dispatch);
48
+ return dispatch();
49
+ });
50
+ const dispatch = () => run(index + 1);
51
+ return dispatch();
52
+ };
53
+ }
54
+
55
+ class MojoReceiver {
56
+ constructor(channel, pipe) {
57
+ this.middleware = [];
58
+ this.pipe = pipe || Math.random().toString(36).slice(3);
59
+ this.channel = channel;
60
+ }
61
+ get valid() {
62
+ return this.channel.has(this.pipe);
63
+ }
64
+ implement(key, invoke) {
65
+ const m = (ctx, next) => __awaiter(this, void 0, void 0, function* () {
66
+ const { method, params, status, required, attached = {} } = ctx.data;
67
+ if (key !== method) {
68
+ return next();
69
+ }
70
+ let handled = false;
71
+ const transfer = [];
72
+ const reAttached = {};
73
+ const done = (payload) => {
74
+ if (handled) {
75
+ console.warn(`receiver[${this}] - ${method} reply multiple times`);
76
+ return;
77
+ }
78
+ handled = true;
79
+ this.channel.send(Object.assign(Object.assign(Object.assign({}, ctx.data), {
80
+ // override params as it may contains un-cloned object
81
+ params: undefined,
82
+ // override attached as it may contains un-cloned object
83
+ attached: reAttached }), payload), transfer);
84
+ };
85
+ const reply = {
86
+ transfer: (...args) => {
87
+ if (required) {
88
+ transfer.push(...args);
89
+ }
90
+ return reply;
91
+ },
92
+ resolve: (value) => {
93
+ if (!required) {
94
+ handled = true;
95
+ return;
96
+ }
97
+ done({ resolve: value, status: status + 1 });
98
+ },
99
+ reject: (reason) => {
100
+ if (!required) {
101
+ handled = true;
102
+ return;
103
+ }
104
+ done({ status: status - 1, reject: reason });
105
+ },
106
+ // attached
107
+ set: (key, val) => {
108
+ if (required) {
109
+ reAttached[key] = val;
110
+ }
111
+ return reply;
112
+ },
113
+ get: (key) => attached[key],
114
+ };
115
+ try {
116
+ const timestamp = Date.now();
117
+ yield invoke(reply, params, ctx.ports);
118
+ if (true) {
119
+ const delta = Date.now() - timestamp;
120
+ if (delta > 100) {
121
+ console.log(`receiver[${this}] - ${method}() took ${delta}ms`);
122
+ }
123
+ }
124
+ }
125
+ catch (error) {
126
+ console.warn(error);
127
+ if (!handled) {
128
+ reply.reject(`${error}`);
129
+ }
130
+ }
131
+ ctx.data.touched = true;
132
+ if (!handled) {
133
+ yield next();
134
+ }
135
+ });
136
+ this.middleware.unshift(m);
137
+ const unload = () => {
138
+ const index = this.middleware.findIndex((v) => v === m);
139
+ if (~index) {
140
+ this.middleware.splice(index, 1);
141
+ }
142
+ };
143
+ return unload;
144
+ }
145
+ reject(msg = '') {
146
+ this.channel.reject(this, msg);
147
+ }
148
+ as() {
149
+ return this;
150
+ }
151
+ callback() {
152
+ return (ctx, next) => __awaiter(this, void 0, void 0, function* () {
153
+ const { pipe, status } = ctx.data;
154
+ if (pipe !== this.pipe) {
155
+ return next();
156
+ }
157
+ if (status) {
158
+ return next();
159
+ }
160
+ return pipeline(this.middleware)(ctx, next);
161
+ });
162
+ }
163
+ toString() {
164
+ return `${this.pipe}`;
165
+ }
166
+ }
167
+
168
+ class MojoRemote {
169
+ constructor(channel, pipe) {
170
+ this.sequence = 0;
171
+ this.pendings = new Map();
172
+ this.pipe = pipe || Math.random().toString(36).slice(3);
173
+ this.channel = channel;
174
+ this.accepted = defer();
175
+ }
176
+ get ready() {
177
+ return this.accepted.promise;
178
+ }
179
+ get valid() {
180
+ return this.channel.has(this.pipe);
181
+ }
182
+ make(key) {
183
+ const transfer = [];
184
+ const attached = {};
185
+ const ready = (params) => __awaiter(this, void 0, void 0, function* () {
186
+ try {
187
+ yield this.ready;
188
+ }
189
+ catch (error) {
190
+ console.warn(`remote[${this}] - ${key}(${params}) failed with '${error}'`);
191
+ throw error;
192
+ }
193
+ });
194
+ const send = (params, required) => {
195
+ this.channel.send({
196
+ pipe: this.pipe,
197
+ sequence: ++this.sequence,
198
+ timestamp: Date.now(),
199
+ method: key,
200
+ status: 0,
201
+ params,
202
+ required,
203
+ attached,
204
+ }, transfer);
205
+ };
206
+ const invoke = {
207
+ transfer: (...args) => {
208
+ transfer.push(...args);
209
+ return invoke;
210
+ },
211
+ post: (...params) => __awaiter(this, void 0, void 0, function* () {
212
+ yield ready(params);
213
+ send(params);
214
+ }),
215
+ invoke: (...params) => __awaiter(this, void 0, void 0, function* () {
216
+ yield ready(params);
217
+ send(params, true);
218
+ const d = defer();
219
+ this.pendings.set(this.sequence, d);
220
+ return d.promise;
221
+ }),
222
+ // attached
223
+ set: (key, val) => {
224
+ attached[key] = val;
225
+ return invoke;
226
+ },
227
+ get: (key) => attached[key],
228
+ };
229
+ return invoke;
230
+ }
231
+ break(msg = '') {
232
+ this.channel.break(this, msg);
233
+ this.pendings.forEach((p) => p.reject('broken'));
234
+ this.pendings.clear();
235
+ this.accepted.reject('broken');
236
+ // eslint-disable-next-line prefer-promise-reject-errors
237
+ this.accepted.promise = Promise.reject('broken');
238
+ // handle uncaught promise
239
+ this.accepted.promise.catch(NOOP);
240
+ }
241
+ callback() {
242
+ return (ctx, next) => __awaiter(this, void 0, void 0, function* () {
243
+ const { pipe, sequence, timestamp, method, status, reject, resolve } = ctx.data;
244
+ if (pipe !== this.pipe) {
245
+ return next();
246
+ }
247
+ if (!status) {
248
+ return next();
249
+ }
250
+ const d = this.pendings.get(sequence);
251
+ if (!d) {
252
+ console.log(`remote[${this}] - receive unknown ${method} reply`);
253
+ return;
254
+ }
255
+ if (status < 0) {
256
+ d.reject(reject);
257
+ }
258
+ else {
259
+ d.resolve(resolve);
260
+ }
261
+ this.pendings.delete(sequence);
262
+ {
263
+ const delta = Date.now() - timestamp;
264
+ if (delta > 100) {
265
+ console.log(`remote[${this}] - ${method}() took ${delta}ms`);
266
+ }
267
+ }
268
+ });
269
+ }
270
+ toString() {
271
+ return `${this.pipe}`;
272
+ }
273
+ }
274
+ function defer() {
275
+ const until = {};
276
+ until.promise = new Promise((resolve, reject) => {
277
+ until.resolve = resolve;
278
+ until.reject = reject;
279
+ });
280
+ // handle uncaught promise
281
+ until.promise.catch(NOOP);
282
+ if ( typeof setTimeout !== 'undefined') {
283
+ setTimeout(() => until.reject('timeout'), 10000);
284
+ }
285
+ return until;
286
+ }
287
+ const NOOP = () => {
288
+ // nothing
289
+ };
290
+
291
+ class MojoChannel {
292
+ // fix me
293
+ // incompatible onmessage ev type
294
+ constructor(transport) {
295
+ this.remotes = new Map();
296
+ this.receivers = new Map();
297
+ this.pendingReceivers = new Set();
298
+ const previous = transport.onmessage;
299
+ this.transport = transport;
300
+ this.transport.onmessage = (ev) => {
301
+ const { data, ports = [] } = ev;
302
+ if (isString(data)) {
303
+ if (/^mojo/.test(data)) {
304
+ const [, pipe] = data.split(':');
305
+ const receiver = this.receivers.get(`${pipe}`);
306
+ if (receiver) {
307
+ this.send(`accept:${receiver}`);
308
+ this.oninvitation && this.oninvitation(pipe, receiver);
309
+ receiver.onready && receiver.onready();
310
+ return;
311
+ }
312
+ this.pendingReceivers.add(pipe);
313
+ this.oninvitation && this.oninvitation(pipe);
314
+ if (this.pendingReceivers.has(pipe)) {
315
+ previous && previous.call(this.transport, ev);
316
+ }
317
+ return;
318
+ }
319
+ if (/^accept/.test(data)) {
320
+ const [, pipe] = data.split(':');
321
+ const remote = this.remotes.get(pipe);
322
+ if (!remote) {
323
+ console.log(`unknown accept:\n${data}`);
324
+ return;
325
+ }
326
+ remote.accepted.resolve();
327
+ remote.onaccept && remote.onaccept();
328
+ return;
329
+ }
330
+ if (/^reject/.test(data)) {
331
+ const [, pipe, msg = 'rejected'] = data.split(':');
332
+ const remote = this.remotes.get(pipe);
333
+ if (!remote) {
334
+ console.log(`unknown reject:\n${data}`);
335
+ return;
336
+ }
337
+ this.remotes.delete(pipe);
338
+ remote.accepted.reject(msg);
339
+ remote.onreject && remote.onreject(msg);
340
+ return;
341
+ }
342
+ if (/^break/.test(data)) {
343
+ const [, pipe, msg = 'breaked'] = data.split(':');
344
+ if (this.pendingReceivers.has(pipe)) {
345
+ this.pendingReceivers.delete(pipe);
346
+ return;
347
+ }
348
+ const receiver = this.receivers.get(pipe);
349
+ if (!receiver) {
350
+ console.log(`unknown break:\n${data}`);
351
+ return;
352
+ }
353
+ this.receivers.delete(pipe);
354
+ receiver.onbreak && receiver.onbreak(msg);
355
+ return;
356
+ }
357
+ if (previous) {
358
+ previous.call(this.transport, ev);
359
+ return;
360
+ }
361
+ console.log(`unknown invitation:\n${data}`);
362
+ return;
363
+ }
364
+ const ctx = { data, ports };
365
+ pipeline([
366
+ ...Array.from(this.remotes.values()).map((p) => p.callback()),
367
+ ...Array.from(this.receivers.values()).map((p) => p.callback()),
368
+ ])(ctx, () => {
369
+ if (previous) {
370
+ previous.call(this.transport, ev);
371
+ return;
372
+ }
373
+ if (data.touched) {
374
+
375
+ console.log(`unhandled method: ${data.pipe}::${data.method}\n` +
376
+ `forget to call resolve() or reject() in method implement?`);
377
+ return;
378
+ }
379
+ console.log('unknown message', data);
380
+ });
381
+ };
382
+ }
383
+ fork() {
384
+ return new MojoChannel(this.transport);
385
+ }
386
+ has(pipe) {
387
+ return this.remotes.has(pipe) || this.receivers.has(pipe);
388
+ }
389
+ close() {
390
+ this.remotes.forEach((v) => v.break());
391
+ this.remotes.clear();
392
+ this.receivers.forEach((v) => v.reject());
393
+ this.receivers.clear();
394
+ this.transport.onmessage = null;
395
+ this.transport = null;
396
+ }
397
+ send(msg, transfer) {
398
+ this.transport.postMessage(msg, transfer);
399
+ }
400
+ reject(receiver, msg = '') {
401
+ this.send(`reject:${receiver}:${msg}`);
402
+ this.receivers.delete(`${receiver}`);
403
+ }
404
+ break(remote, msg = '') {
405
+ if (!this.remotes.has(`${remote}`))
406
+ return;
407
+ this.send(`break:${remote}:${msg}`);
408
+ this.remotes.delete(`${remote}`);
409
+ }
410
+ remote(pipe) {
411
+ const remote = new MojoRemote(this, pipe && `${pipe}`);
412
+ this.remotes.set(`${remote}`, remote);
413
+ this.send(`mojo:${remote}`);
414
+ return remote;
415
+ }
416
+ receiver(pipe) {
417
+ if (pipe && this.receivers.has(`${pipe}`)) {
418
+ console.log(`duplicate pipe: ${pipe}`);
419
+ }
420
+ const receiver = new MojoReceiver(this, pipe && `${pipe}`);
421
+ this.receivers.set(`${receiver}`, receiver);
422
+ if (this.pendingReceivers.has(`${receiver}`)) {
423
+ this.send(`accept:${receiver}`);
424
+ this.pendingReceivers.delete(`${receiver}`);
425
+ receiver.onready && receiver.onready();
426
+ }
427
+ return receiver;
428
+ }
429
+ }
430
+ const isString = (val) => typeof val === 'string';
431
+
432
+ class AudioFrameBuffer {
433
+ constructor(builder) {
434
+ const { length, numberOfChannels: channels = 1, sampleRate } = builder;
435
+ let { data } = builder;
436
+ this.length = length;
437
+ this.numberOfChannels = channels;
438
+ this.sampleRate = sampleRate;
439
+ this.duration = length / sampleRate;
440
+ if (!data || !data.length || !data[0].length) {
441
+ // AudioBuffer do not store data as a planar sequence
442
+ data = [...Array(channels)].map(() => new Float32Array(length));
443
+ }
444
+ if (data.length < channels) {
445
+ console.warn(`invalid buffer channels ${channels} vs ${data.length}`);
446
+ }
447
+ if (data[0].length < length) {
448
+ console.warn(`invalid buffer length ${length} vs ${data[0].length}`);
449
+ }
450
+ this.data = data;
451
+ }
452
+ // CopyFrom
453
+ static From(buffer) {
454
+ const { sampleRate, numberOfChannels, length } = buffer;
455
+ const data = [...Array(numberOfChannels).keys()].map((ch) => buffer.getChannelData(ch).slice());
456
+ return new AudioFrameBuffer({ length, numberOfChannels, sampleRate, data });
457
+ }
458
+ static Copy(dst, src) {
459
+ const { numberOfChannels: channels } = dst;
460
+ const { numberOfChannels: available } = src;
461
+ for (let ch = 0; ch < channels; ch++) {
462
+ dst.copyToChannel(src.getChannelData(Math.min(ch, available - 1)), ch);
463
+ }
464
+ }
465
+ static Clip(buffer, begin, end) {
466
+ const { sampleRate, numberOfChannels } = buffer;
467
+ const data = AudioFrameBuffer.Data(buffer, begin, end);
468
+ const { length } = data[0];
469
+ return new AudioFrameBuffer({ length, numberOfChannels, sampleRate, data });
470
+ }
471
+ static Data(buffer, begin, end) {
472
+ return [...Array(buffer.numberOfChannels)].map((_, ch) => buffer.getChannelData(ch).subarray(begin, end));
473
+ }
474
+ static Builder(buffer) {
475
+ const { length, numberOfChannels, sampleRate } = buffer;
476
+ const data = AudioFrameBuffer.Data(buffer);
477
+ return {
478
+ length,
479
+ numberOfChannels,
480
+ sampleRate,
481
+ data,
482
+ };
483
+ }
484
+ copyFromChannel(destination, channel = 0, bufferOffset = 0) {
485
+ if (channel >= this.numberOfChannels) {
486
+ console.warn('invalid channel');
487
+ return;
488
+ }
489
+ const data = this.getChannelData(channel);
490
+ if (bufferOffset >= data.length) {
491
+ console.warn('invalid offset');
492
+ return;
493
+ }
494
+ const source = data.subarray(bufferOffset);
495
+ destination.set(source.subarray(0, Math.min(source.length, destination.length)));
496
+ }
497
+ copyToChannel(source, channel = 0, bufferOffset = 0) {
498
+ if (channel >= this.numberOfChannels) {
499
+ console.warn('invalid channel');
500
+ return;
501
+ }
502
+ const data = this.getChannelData(channel);
503
+ if (bufferOffset >= this.length) {
504
+ console.warn('invalid offset');
505
+ return;
506
+ }
507
+ const length = Math.min(this.length - bufferOffset, source.length);
508
+ const clipped = source.subarray(0, length);
509
+ data.set(clipped, bufferOffset);
510
+ }
511
+ getChannelData(channel) {
512
+ return this.data[channel];
513
+ }
514
+ }
515
+
516
+ // export const enum SpeechType {
517
+ var VADActivity;
518
+ (function (VADActivity) {
519
+ VADActivity[VADActivity["kVadActive"] = 0] = "kVadActive";
520
+ VADActivity[VADActivity["kVadPassive"] = 1] = "kVadPassive";
521
+ VADActivity[VADActivity["kVadUnknown"] = 2] = "kVadUnknown";
522
+ })(VADActivity || (VADActivity = {}));
523
+ // export const enum SpeechType {
524
+ var SpeechType;
525
+ (function (SpeechType) {
526
+ SpeechType[SpeechType["kNormalSpeech"] = 0] = "kNormalSpeech";
527
+ SpeechType[SpeechType["kPLC"] = 1] = "kPLC";
528
+ SpeechType[SpeechType["kCNG"] = 2] = "kCNG";
529
+ SpeechType[SpeechType["kPLCCNG"] = 3] = "kPLCCNG";
530
+ SpeechType[SpeechType["kCodecPLC"] = 4] = "kCodecPLC";
531
+ SpeechType[SpeechType["kUndefined"] = 5] = "kUndefined";
532
+ })(SpeechType || (SpeechType = {}));
533
+ class AudioFrame {
534
+ constructor(builder) {
535
+ this.buffer = builder.buffer;
536
+ this.timestamp = builder.timestamp;
537
+ this.elapsedTimeMs = builder.elapsedTimeMs;
538
+ this.ntpTimeMs = builder.ntpTimeMs;
539
+ this.vad = builder.vad || VADActivity.kVadUnknown;
540
+ this.speech = builder.speech || SpeechType.kUndefined;
541
+ }
542
+ get sampleRate() {
543
+ return this.buffer.sampleRate;
544
+ }
545
+ get samples() {
546
+ return this.buffer.length;
547
+ }
548
+ get channels() {
549
+ return this.buffer.numberOfChannels;
550
+ }
551
+ get duration() {
552
+ return this.buffer.duration;
553
+ }
554
+ }
555
+
556
+ function CopyAudioFrame(output, outputOffset = 0, input, inputOffset = 0, length = input.length) {
557
+ const { numberOfChannels: channels } = output;
558
+ const { numberOfChannels: available } = input;
559
+ for (let channel = 0; channel < channels; channel++) {
560
+ const source = input
561
+ .getChannelData(Math.min(channel, available - 1))
562
+ .subarray(inputOffset, inputOffset + length);
563
+ output.copyToChannel(source, channel, outputOffset);
564
+ }
565
+ }
566
+ function InterleaveT(buffer, factory, map = (val) => val) {
567
+ const channels = buffer.length;
568
+ const length = channels * buffer[0].length;
569
+ const output = factory(length);
570
+ for (let channel = 0; channel < channels; channel++) {
571
+ const data = buffer[channel];
572
+ for (let index = 0; index < data.length; index++) {
573
+ output[index * channels + channel] = map(data[index]);
574
+ }
575
+ }
576
+ return output;
577
+ }
578
+ function PlanarT(buffer, channels, factory, map = (val) => val) {
579
+ const output = [];
580
+ const length = buffer.length / channels;
581
+ for (let channel = 0; channel < channels; channel++) {
582
+ output[channel] = factory(length, channel);
583
+ }
584
+ for (let index = 0; index < buffer.length; index++) {
585
+ const channel = index % channels;
586
+ const data = output[channel];
587
+ data[Math.floor(index / channels)] = map(buffer[index]);
588
+ }
589
+ return output;
590
+ }
591
+ const DeInterleaveT = PlanarT;
592
+ function Float32ToInt16(val) {
593
+ return Math.min(Math.max((val * 0x7fff) | 0, -0x8000), 0x7fff);
594
+ }
595
+ function Int16ToFloat32(val) {
596
+ return val >= 0x8000 ? -(0x10000 - val) / 0x8000 : val / 0x7fff;
597
+ }
598
+
599
+ function makeAudioReshaper(wanted, callback, factory) {
600
+ let pending;
601
+ let offset = 0;
602
+ return (input) => {
603
+ const { samples, channels, sampleRate } = input;
604
+ let current = 0;
605
+ while (current < samples) {
606
+ const needed = wanted - offset;
607
+ const available = samples - current;
608
+ const total = Math.min(available, needed);
609
+ if (!needed) {
610
+ callback(pending);
611
+ pending = null;
612
+ }
613
+ if (!pending) {
614
+ pending = factory
615
+ ? factory(wanted)
616
+ : new AudioFrame({
617
+ buffer: new AudioFrameBuffer({
618
+ length: wanted,
619
+ numberOfChannels: channels,
620
+ sampleRate,
621
+ }),
622
+ });
623
+ offset = 0;
624
+ }
625
+ CopyAudioFrame(pending.buffer, offset, input.buffer, current, total);
626
+ current += total;
627
+ offset += total;
628
+ }
629
+ };
630
+ }
631
+
632
+ class AudioFrameQueue {
633
+ constructor() {
634
+ this.queue = [];
635
+ // total audio samples
636
+ this.total = 0;
637
+ // audio samples offset
638
+ this.offset = 0;
639
+ }
640
+ get length() {
641
+ return this.total;
642
+ }
643
+ clear() {
644
+ this.queue.length = 0;
645
+ this.total = 0;
646
+ this.offset = 0;
647
+ }
648
+ shift() {
649
+ if (!this.total)
650
+ return;
651
+ const frame = this.queue[0];
652
+ const available = frame.samples - this.offset;
653
+ if (this.offset) {
654
+ console.log('maybe unexpected samples');
655
+ }
656
+ return this.pull(available);
657
+ }
658
+ push(frame) {
659
+ const input = (frame) => {
660
+ this.queue.push(frame);
661
+ this.total += frame.samples;
662
+ };
663
+ if (!this.onpush) {
664
+ input(frame);
665
+ return;
666
+ }
667
+ const { samples, callback, factory, reshape = false } = this.onpush;
668
+ let { reshaper } = this.onpush;
669
+ if (reshape) {
670
+ if (!reshaper) {
671
+ reshaper = makeAudioReshaper(samples, input, factory);
672
+ this.onpush.reshaper = reshaper;
673
+ }
674
+ // reshape & input
675
+ reshaper(frame);
676
+ }
677
+ else {
678
+ input(frame);
679
+ }
680
+ while (this.total - this.offset >= samples) {
681
+ callback(reshape ? this.shift() : this.pull(samples, factory && factory(samples)));
682
+ }
683
+ }
684
+ pull(wanted, output) {
685
+ if (wanted <= 0) {
686
+ wanted < 0 && console.warn(`invalid samples: ${wanted}`);
687
+ return;
688
+ }
689
+ if (this.onpull) {
690
+ const { samples, callback, eager } = this.onpull;
691
+ while (this.total < Math.max(samples, wanted) * (eager ? 2 : 1)) {
692
+ const frame = callback(this.total, wanted);
693
+ if (!frame) {
694
+ break;
695
+ }
696
+ this.queue.push(frame);
697
+ this.total += frame.samples;
698
+ }
699
+ }
700
+ if (this.total < wanted) {
701
+ console.warn('audio frame drained');
702
+ return;
703
+ }
704
+ let current = 0;
705
+ let { offset } = this;
706
+ while (current < wanted) {
707
+ const frame = this.queue.shift();
708
+ const available = frame.samples - offset;
709
+ const needed = wanted - current;
710
+ const length = Math.min(available, needed);
711
+ if (!available) {
712
+ continue;
713
+ }
714
+ // clip buffer w/o copy
715
+ if (!output && !current && available >= needed) {
716
+ const buffer = AudioFrameBuffer.Clip(frame.buffer, offset, offset + needed);
717
+ output = new AudioFrame({ buffer });
718
+ if (available > needed) {
719
+ offset += length;
720
+ this.queue.unshift(frame);
721
+ }
722
+ else {
723
+ offset = 0;
724
+ }
725
+ break;
726
+ }
727
+ // copy needed or available
728
+ if (!output) {
729
+ const buffer = new AudioFrameBuffer({
730
+ length: wanted,
731
+ numberOfChannels: frame.channels,
732
+ sampleRate: frame.sampleRate,
733
+ });
734
+ output = new AudioFrame({ buffer });
735
+ }
736
+ if (output.samples < wanted) {
737
+ console.warn('invalid frame buffer length');
738
+ }
739
+ CopyAudioFrame(output.buffer, current, frame.buffer, offset, length);
740
+ current += length;
741
+ if (available > needed) {
742
+ offset += length;
743
+ this.queue.unshift(frame);
744
+ break;
745
+ }
746
+ offset = 0;
747
+ if (this.onpull) {
748
+ // optional chaining
749
+ // this.onpull.drain?.(frame);
750
+ const { drain } = this.onpull;
751
+ drain && drain(frame);
752
+ }
753
+ }
754
+ this.total -= wanted;
755
+ this.offset = offset;
756
+ return output;
757
+ }
758
+ }
759
+
760
+ /// <reference types="emscripten" />
761
+ const DEFAULT_HEAP_TYPE = 'HEAPU8';
762
+ function createMemoryAllocator(native, type) {
763
+ return (length) => {
764
+ const { BYTES_PER_ELEMENT } = native[type || DEFAULT_HEAP_TYPE];
765
+ const bytes = length * BYTES_PER_ELEMENT;
766
+ const pointer = native._malloc(bytes);
767
+ const tuple = () => [pointer, bytes];
768
+ // malloc() might cause wasm memory growth, which will detach 'heap' buffer
769
+ const view = (normalize = true) => {
770
+ const v = native[type || DEFAULT_HEAP_TYPE].subarray(pointer / BYTES_PER_ELEMENT, pointer + length);
771
+ if (normalize && typeof v.constructor !== 'undefined') {
772
+ return new v.constructor(v.buffer, v.byteOffset, length);
773
+ }
774
+ console.log('unnormalized array view has unexpected length');
775
+ return v;
776
+ };
777
+ const free = () => native._free(pointer);
778
+ return {
779
+ BYTES_PER_ELEMENT,
780
+ pointer,
781
+ length,
782
+ bytes,
783
+ tuple,
784
+ view,
785
+ free,
786
+ };
787
+ };
788
+ }
789
+ function createMemoryViewer(native, type) {
790
+ return (begin, length, normalize = true) => {
791
+ const heap = native[type || DEFAULT_HEAP_TYPE];
792
+ const { BYTES_PER_ELEMENT } = heap;
793
+ const view = heap.subarray(begin / BYTES_PER_ELEMENT, begin + length);
794
+ if (normalize && typeof view.constructor !== 'undefined') {
795
+ return new view.constructor(view.buffer, view.byteOffset, length);
796
+ }
797
+ console.log('unnormalized array view has unexpected length');
798
+ return view;
799
+ };
800
+ }
801
+
802
+ function open(name, store) {
803
+ const request = indexedDB.open(name);
804
+ request.onupgradeneeded = () => request.result.createObjectStore(store);
805
+ const promise = make(request);
806
+ return (mode, callback) => promise.then((db) => callback(db.transaction(store, mode).objectStore(store)));
807
+ }
808
+ const LAZY_INSTANCE = (factory) => {
809
+ let instance;
810
+ return (...args) => {
811
+ if (!instance) {
812
+ instance = factory();
813
+ }
814
+ return instance(...args);
815
+ };
816
+ };
817
+ const LAZY_GLOBAL_STORE = LAZY_INSTANCE(() => open('LINE-UI', 'key-value'));
818
+ function get(key, store = LAZY_GLOBAL_STORE) {
819
+ return store('readonly', (store) => make(store.get(key)));
820
+ }
821
+ function clear(store = LAZY_GLOBAL_STORE) {
822
+ return store('readwrite', (store) => {
823
+ store.clear();
824
+ return make(store.transaction);
825
+ });
826
+ }
827
+ function make(request) {
828
+ return new Promise((resolve, reject) => {
829
+ // @ts-ignore - file size hacks
830
+ request.oncomplete = request.onsuccess = () => resolve(request.result);
831
+ // @ts-ignore - file size hacks
832
+ request.onabort = request.onerror = () => reject(request.error);
833
+ });
834
+ }
835
+
836
+ /// <reference types="emscripten" />
837
+ var IPVPEvents;
838
+ (function (IPVPEvents) {
839
+ IPVPEvents[IPVPEvents["kRTPDead"] = 1] = "kRTPDead";
840
+ IPVPEvents[IPVPEvents["kGainCtrl"] = 2] = "kGainCtrl";
841
+ IPVPEvents[IPVPEvents["kCSRCChange"] = 3] = "kCSRCChange";
842
+ IPVPEvents[IPVPEvents["kCCIDChange"] = 4] = "kCCIDChange";
843
+ IPVPEvents[IPVPEvents["kMicFramingDelay"] = 5] = "kMicFramingDelay";
844
+ IPVPEvents[IPVPEvents["kSpkFramengDelay"] = 6] = "kSpkFramengDelay";
845
+ IPVPEvents[IPVPEvents["kRTPViolate"] = 7] = "kRTPViolate";
846
+ IPVPEvents[IPVPEvents["kSignalBreak"] = 8] = "kSignalBreak";
847
+ })(IPVPEvents || (IPVPEvents = {}));
848
+ var IPVPConfigAEC;
849
+ (function (IPVPConfigAEC) {
850
+ IPVPConfigAEC[IPVPConfigAEC["kOff"] = 0] = "kOff";
851
+ IPVPConfigAEC[IPVPConfigAEC["kNormal"] = 1] = "kNormal";
852
+ IPVPConfigAEC[IPVPConfigAEC["kEnhance"] = 2] = "kEnhance";
853
+ })(IPVPConfigAEC || (IPVPConfigAEC = {}));
854
+ var IPVPConfigANS;
855
+ (function (IPVPConfigANS) {
856
+ IPVPConfigANS[IPVPConfigANS["kOff"] = 0] = "kOff";
857
+ IPVPConfigANS[IPVPConfigANS["kLow"] = 1] = "kLow";
858
+ IPVPConfigANS[IPVPConfigANS["kMiddle"] = 2] = "kMiddle";
859
+ IPVPConfigANS[IPVPConfigANS["kHigh"] = 3] = "kHigh";
860
+ })(IPVPConfigANS || (IPVPConfigANS = {}));
861
+ var IPVPConfigAGCMode;
862
+ (function (IPVPConfigAGCMode) {
863
+ IPVPConfigAGCMode[IPVPConfigAGCMode["kAdaptiveDigital"] = 1] = "kAdaptiveDigital";
864
+ IPVPConfigAGCMode[IPVPConfigAGCMode["kAdaptiveAnalog"] = 2] = "kAdaptiveAnalog";
865
+ IPVPConfigAGCMode[IPVPConfigAGCMode["kFixedDigital"] = 4] = "kFixedDigital";
866
+ IPVPConfigAGCMode[IPVPConfigAGCMode["kUnchanged"] = 8] = "kUnchanged";
867
+ IPVPConfigAGCMode[IPVPConfigAGCMode["kCustomTable"] = 16] = "kCustomTable";
868
+ IPVPConfigAGCMode[IPVPConfigAGCMode["kDigitalAgcAdj"] = 32] = "kDigitalAgcAdj";
869
+ IPVPConfigAGCMode[IPVPConfigAGCMode["kVadThresholdAdj"] = 64] = "kVadThresholdAdj";
870
+ })(IPVPConfigAGCMode || (IPVPConfigAGCMode = {}));
871
+ var IPVPDataType;
872
+ (function (IPVPDataType) {
873
+ IPVPDataType[IPVPDataType["kInvalid"] = -1] = "kInvalid";
874
+ IPVPDataType[IPVPDataType["kSpeech"] = 0] = "kSpeech";
875
+ IPVPDataType[IPVPDataType["kPCM"] = 1] = "kPCM";
876
+ IPVPDataType[IPVPDataType["kRTP"] = 2] = "kRTP";
877
+ IPVPDataType[IPVPDataType["kRTCP"] = 3] = "kRTCP";
878
+ IPVPDataType[IPVPDataType["kRFC2833"] = 4] = "kRFC2833";
879
+ IPVPDataType[IPVPDataType["kCNG"] = 5] = "kCNG";
880
+ IPVPDataType[IPVPDataType["kAVS"] = 6] = "kAVS";
881
+ IPVPDataType[IPVPDataType["kRED"] = 7] = "kRED";
882
+ IPVPDataType[IPVPDataType["kVQM"] = 8] = "kVQM";
883
+ IPVPDataType[IPVPDataType["kSDES"] = 9] = "kSDES";
884
+ IPVPDataType[IPVPDataType["kFEC"] = 10] = "kFEC";
885
+ })(IPVPDataType || (IPVPDataType = {}));
886
+ var IPVPTalkFlag;
887
+ (function (IPVPTalkFlag) {
888
+ IPVPTalkFlag[IPVPTalkFlag["kInactive"] = 0] = "kInactive";
889
+ IPVPTalkFlag[IPVPTalkFlag["kRecvOnly"] = 1] = "kRecvOnly";
890
+ IPVPTalkFlag[IPVPTalkFlag["kSendOnly"] = 2] = "kSendOnly";
891
+ IPVPTalkFlag[IPVPTalkFlag["kSendRecv"] = 3] = "kSendRecv";
892
+ IPVPTalkFlag[IPVPTalkFlag["kSendEvnt"] = 4] = "kSendEvnt";
893
+ IPVPTalkFlag[IPVPTalkFlag["kPaging"] = 8] = "kPaging";
894
+ IPVPTalkFlag[IPVPTalkFlag["kMax"] = 15] = "kMax";
895
+ })(IPVPTalkFlag || (IPVPTalkFlag = {}));
896
+ var IPVPSRTPCryptoExtern;
897
+ (function (IPVPSRTPCryptoExtern) {
898
+ IPVPSRTPCryptoExtern[IPVPSRTPCryptoExtern["kUnencryRTP"] = 1] = "kUnencryRTP";
899
+ IPVPSRTPCryptoExtern[IPVPSRTPCryptoExtern["kUnencryRTCP"] = 2] = "kUnencryRTCP";
900
+ IPVPSRTPCryptoExtern[IPVPSRTPCryptoExtern["kUnautoRTP"] = 4] = "kUnautoRTP";
901
+ })(IPVPSRTPCryptoExtern || (IPVPSRTPCryptoExtern = {}));
902
+ var IPVPCodecOpts;
903
+ (function (IPVPCodecOpts) {
904
+ IPVPCodecOpts[IPVPCodecOpts["kAmrWbIO"] = 1] = "kAmrWbIO";
905
+ IPVPCodecOpts[IPVPCodecOpts["kHFOnly"] = 2] = "kHFOnly";
906
+ IPVPCodecOpts[IPVPCodecOpts["kDTXSend"] = 4] = "kDTXSend";
907
+ IPVPCodecOpts[IPVPCodecOpts["kDTXRecv"] = 8] = "kDTXRecv";
908
+ IPVPCodecOpts[IPVPCodecOpts["kStero"] = 16] = "kStero";
909
+ IPVPCodecOpts[IPVPCodecOpts["kInbandFEC"] = 32] = "kInbandFEC";
910
+ IPVPCodecOpts[IPVPCodecOpts["kUseYLRTX"] = 64] = "kUseYLRTX";
911
+ })(IPVPCodecOpts || (IPVPCodecOpts = {}));
912
+ var IPVPFECFormat;
913
+ (function (IPVPFECFormat) {
914
+ IPVPFECFormat[IPVPFECFormat["kURL"] = 0] = "kURL";
915
+ IPVPFECFormat[IPVPFECFormat["kFLEX"] = 1] = "kFLEX";
916
+ })(IPVPFECFormat || (IPVPFECFormat = {}));
917
+ var IPVPGainDir;
918
+ (function (IPVPGainDir) {
919
+ IPVPGainDir[IPVPGainDir["kNone"] = 0] = "kNone";
920
+ IPVPGainDir[IPVPGainDir["kRecv"] = 1] = "kRecv";
921
+ IPVPGainDir[IPVPGainDir["kSend"] = 2] = "kSend";
922
+ })(IPVPGainDir || (IPVPGainDir = {}));
923
+ var IPVPFlowDir;
924
+ (function (IPVPFlowDir) {
925
+ IPVPFlowDir[IPVPFlowDir["kNone"] = 0] = "kNone";
926
+ IPVPFlowDir[IPVPFlowDir["kLocal"] = 1] = "kLocal";
927
+ IPVPFlowDir[IPVPFlowDir["kNet"] = 2] = "kNet";
928
+ IPVPFlowDir[IPVPFlowDir["kMix"] = 4] = "kMix";
929
+ IPVPFlowDir[IPVPFlowDir["kAll"] = 3] = "kAll";
930
+ })(IPVPFlowDir || (IPVPFlowDir = {}));
931
+
932
+ let native;
933
+ let alloc;
934
+ let alloc16;
935
+ let view;
936
+ let view32;
937
+ let loglevel = 1;
938
+ let callid; // aka encoder id
939
+ const decoders = [];
940
+ const decoderGains = [-1, -1];
941
+ function hasDecoder(ccid) {
942
+ return decoders.findIndex((v) => v && v.has(ccid));
943
+ }
944
+ function addDecoder(ccid, track) {
945
+ if (!decoders[track]) {
946
+ decoders[track] = new Set();
947
+ }
948
+ decoders[track].add(ccid);
949
+ }
950
+ function removeDecoder(ccid) {
951
+ let channel = -1;
952
+ decoders.forEach((v, i) => {
953
+ if (!v)
954
+ return;
955
+ if (v.has(ccid)) {
956
+ channel = i;
957
+ }
958
+ v.delete(ccid);
959
+ });
960
+ return channel;
961
+ }
962
+ function moveDecoder(ccid, track) {
963
+ removeDecoder(ccid);
964
+ addDecoder(ccid, track);
965
+ }
966
+ function decoderList(from = 0, to) {
967
+ return decoders.slice(from, to).reduce((p, v) => p.concat(Array.from(v)), []);
968
+ }
969
+ function isMainTrack(track) {
970
+ return track < 11;
971
+ }
972
+ function trackType(track) {
973
+ return isMainTrack(track) ? 0 /* kMain */ : 1 /* kOther */;
974
+ }
975
+ let eventR;
976
+ let memory;
977
+ let inputBuffer;
978
+ let outputBuffer;
979
+ let refBuffer;
980
+ let push;
981
+ let push_ref;
982
+ let pull;
983
+ let drop;
984
+ let vtuner_reply;
985
+ const isWechatWorker = typeof worker !== 'undefined';
986
+ if (isWechatWorker) {
987
+ worker.onMessage((data) => {
988
+ worker.onmessage && worker.onmessage({ data });
989
+ });
990
+ }
991
+ const cleanup = [];
992
+ // eslint-disable-next-line no-restricted-globals
993
+ const channel = new MojoChannel(isWechatWorker ? worker : self);
994
+ const engine = channel.receiver('audio-engine-host');
995
+ engine.implement('init', (reply, [url, transportPR, eventPR, verbose = false]) => __awaiter(void 0, void 0, void 0, function* () {
996
+ let factory;
997
+ let wasmUrl;
998
+ if (isWechatWorker) {
999
+ const trailingSlash = (v) => (!v || v.endsWith('/') ? v : `${v}/`);
1000
+ const filename = (path) => path.substring(path.lastIndexOf('/') + 1);
1001
+ const [scriptUrl, wasmDir = ''] = url.split('?');
1002
+ const scriptName = filename(scriptUrl);
1003
+ factory = require(scriptUrl);
1004
+ wasmUrl = trailingSlash(wasmDir) + scriptName.replace(/\.js$/, '.wasm');
1005
+ }
1006
+ else {
1007
+ importScripts(url);
1008
+ factory = LIBIPVP;
1009
+ wasmUrl = url.replace(/\.js$/, '.wasm');
1010
+ }
1011
+ const wasmBinary = isWechatWorker
1012
+ ? wasmUrl
1013
+ : yield get(wasmUrl).catch(() => null);
1014
+ // should clear cache if init failed
1015
+ native = yield factory({ wasmBinary }).catch((error) => {
1016
+ clear();
1017
+ throw error;
1018
+ });
1019
+ alloc = createMemoryAllocator(native);
1020
+ alloc16 = createMemoryAllocator(native, 'HEAPU16');
1021
+ view = createMemoryViewer(native);
1022
+ view32 = createMemoryViewer(native, 'HEAPU32');
1023
+ console.log(`IPVP: ${native.ipvp_get_version()} - ${native.ipvp_get_build()}`);
1024
+ eventR = channel.remote(eventPR);
1025
+ const transportR = channel.remote(transportPR);
1026
+ cleanup.unshift(() => {
1027
+ transportR.break();
1028
+ eventR.break();
1029
+ eventR = null;
1030
+ });
1031
+ native.ipvp_init((level, msg) => {
1032
+ if (verbose || (level <= loglevel && true)) {
1033
+ console.log(msg);
1034
+ }
1035
+ }, (event, wparam, lparam, data, length) => {
1036
+ if (event === IPVPEvents.kRTPDead) {
1037
+ if (hasDecoder(wparam)) {
1038
+ native.ipvp_decode_stop(wparam);
1039
+ const channel = removeDecoder(wparam);
1040
+ eventR && eventR.make('decodestop').post(wparam, channel);
1041
+ }
1042
+ return;
1043
+ }
1044
+ if (event === IPVPEvents.kGainCtrl) {
1045
+ console.log(`gain control: ${lparam}db`);
1046
+ return;
1047
+ }
1048
+ if (event === IPVPEvents.kCSRCChange) {
1049
+ const speaking = [];
1050
+ if (data && lparam) {
1051
+ // [int32_t, int32_t][]
1052
+ speaking.push(...view32(data, lparam).filter((_, index) => index % 2 === 0));
1053
+ }
1054
+ eventR.make('speaking').post(callid, speaking);
1055
+ maybeUpdateDecodeGain(speaking);
1056
+ return;
1057
+ }
1058
+ if (event === IPVPEvents.kCCIDChange) {
1059
+ const speaking = [];
1060
+ if (data && lparam) {
1061
+ speaking.push(...view32(data, lparam));
1062
+ }
1063
+ eventR.make('focusing').post(callid, speaking);
1064
+ return;
1065
+ }
1066
+ console.log(`ipvp event: ${IPVPEvents[event]}`);
1067
+ }, (callid, data, length) => {
1068
+ const memory = new Uint8Array(view(data, length));
1069
+ transportR.make('send').transfer(memory.buffer).post(callid, memory);
1070
+ }, (data, length) => {
1071
+ vtuner_reply && vtuner_reply(data, length);
1072
+ });
1073
+ // native.ipvp_config_jitter(1, 0, 0, 60);
1074
+ reply.resolve();
1075
+ }));
1076
+ engine.implement('exit', (reply) => {
1077
+ cleanup.forEach((v) => v());
1078
+ cleanup.length = 0;
1079
+ native.ipvp_exit();
1080
+ reply.resolve();
1081
+ });
1082
+ engine.implement('debug', (reply, [level]) => {
1083
+ loglevel = level;
1084
+ reply.resolve();
1085
+ });
1086
+ engine.implement('mos', (reply, [lossrate, rtt, realtime = false]) => {
1087
+ // range[0, 100] to range[0, 10]
1088
+ reply.resolve(native.ipvp_mos(lossrate, rtt, realtime) / 10);
1089
+ });
1090
+ engine.implement('aec', (reply, [level]) => {
1091
+ console.log(`IPVP AEC: ${level}`);
1092
+ native.ipvp_config_aec(level);
1093
+ reply.resolve();
1094
+ });
1095
+ engine.implement('agc', (reply, [enable]) => {
1096
+ console.log(`IPVP AGC: ${enable}`);
1097
+ native.ipvp_config_agc(enable);
1098
+ reply.resolve();
1099
+ });
1100
+ engine.implement('ans', (reply, [ans, tns = ans]) => {
1101
+ console.log(`IPVP ANS: ${ans}, ${tns}`);
1102
+ native.ipvp_config_ans(tns, ans);
1103
+ reply.resolve();
1104
+ });
1105
+ engine.implement('mute', (reply, [enable]) => {
1106
+ console.log(`IPVP mute: ${enable}`);
1107
+ native.ipvp_set_mute(Number(enable));
1108
+ reply.resolve();
1109
+ });
1110
+ engine.implement('bypass', (reply, [enable]) => {
1111
+ if (enable) {
1112
+ native.ipvp_config_aec(IPVPConfigAEC.kOff);
1113
+ native.ipvp_config_ans(IPVPConfigANS.kOff, IPVPConfigANS.kOff);
1114
+ native.ipvp_set_bypass(1);
1115
+ }
1116
+ else {
1117
+ native.ipvp_config_aec(IPVPConfigAEC.kNormal);
1118
+ native.ipvp_config_ans(IPVPConfigANS.kLow, IPVPConfigANS.kLow);
1119
+ native.ipvp_set_bypass(0);
1120
+ }
1121
+ reply.resolve();
1122
+ });
1123
+ engine.implement('dtmf', (reply, [tones]) => {
1124
+ native.ipvp_dtmf_send(callid, tones, IPVPFlowDir.kNet);
1125
+ reply.resolve();
1126
+ });
1127
+ engine.implement('sendstats', (reply) => {
1128
+ reply.resolve(native.ipvp_get_astats_tx(callid));
1129
+ });
1130
+ engine.implement('recvstats', (reply, [ccid]) => {
1131
+ if (ccid !== -1) {
1132
+ reply.resolve(native.ipvp_get_astats_rx(ccid));
1133
+ return;
1134
+ }
1135
+ reply.resolve(decoderList().map((id) => native.ipvp_get_astats_rx(id)));
1136
+ // const counts = decoders.size;
1137
+ // const stats = Array.from(decoders)
1138
+ // .map((id) => native.ipvp_get_astats_rx(id))
1139
+ // .reduce((stats, val, index) => {
1140
+ // if (!index) {
1141
+ // val.mos_conver /= counts;
1142
+ // val.mos_listen /= counts;
1143
+ // val.mos_network /= counts;
1144
+ // return val;
1145
+ // }
1146
+ // stats.mos_conver += val.mos_conver / counts;
1147
+ // stats.mos_listen += val.mos_listen / counts;
1148
+ // stats.mos_network += val.mos_network / counts;
1149
+ // return stats;
1150
+ // }, {} as IPVPAudioStatsRX);
1151
+ // reply.resolve(stats);
1152
+ });
1153
+ engine.implement('bandwidth', (reply) => {
1154
+ reply.resolve(native.ipvp_get_bandwidth());
1155
+ });
1156
+ engine.implement('jitter', (reply) => {
1157
+ reply.resolve(native.ipvp_get_jitter(callid));
1158
+ });
1159
+ engine.implement('sendstatsadv', (reply) => {
1160
+ reply.resolve(native.ipvp_get_astats_tx_advance(callid));
1161
+ });
1162
+ engine.implement('recvstatsadv', (reply, [ccid]) => {
1163
+ if (ccid !== -1) {
1164
+ reply.resolve(native.ipvp_get_astats_rx_advance(ccid));
1165
+ return;
1166
+ }
1167
+ reply.resolve(decoderList().map((id) => native.ipvp_get_astats_rx_advance(id)));
1168
+ });
1169
+ engine.implement('report', (reply) => {
1170
+ reply.resolve(native.ipvp_get_astats_report());
1171
+ });
1172
+ engine.implement('micschedule', (reply) => {
1173
+ reply.resolve(native.ipvp_get_mic_schedule());
1174
+ });
1175
+ engine.implement('spkschedule', (reply) => {
1176
+ reply.resolve(native.ipvp_get_spk_schedule());
1177
+ });
1178
+ engine.implement('process', (reply, [builder]) => {
1179
+ if (!push) {
1180
+ console.log('unexpected - process before setup');
1181
+ reply.reject();
1182
+ return;
1183
+ }
1184
+ push(builder);
1185
+ const output = pull(builder.length);
1186
+ if (!output) {
1187
+ reply.resolve();
1188
+ return;
1189
+ }
1190
+ reply.transfer(...output.data.map((d) => d.buffer)).resolve(output);
1191
+ });
1192
+ engine.implement('processdelay', (reply, [samples]) => {
1193
+ if (!drop) {
1194
+ console.log('unexpected - processdelay before setup');
1195
+ reply.reject();
1196
+ return;
1197
+ }
1198
+ drop(samples);
1199
+ reply.resolve();
1200
+ });
1201
+ engine.implement('push', (reply, [builder]) => {
1202
+ if (!push) {
1203
+ console.log('unexpected - process before setup');
1204
+ reply.reject();
1205
+ return;
1206
+ }
1207
+ push(builder);
1208
+ reply.resolve();
1209
+ });
1210
+ engine.implement('pushref', (reply, [builder]) => {
1211
+ if (!push_ref) {
1212
+ console.log('unexpected - process before setup');
1213
+ reply.reject();
1214
+ return;
1215
+ }
1216
+ push_ref(builder);
1217
+ reply.resolve();
1218
+ });
1219
+ engine.implement('pull', (reply, [samples]) => {
1220
+ const output = pull(samples);
1221
+ if (!output) {
1222
+ reply.resolve();
1223
+ return;
1224
+ }
1225
+ reply.transfer(...output.data.map((d) => d.buffer)).resolve(output);
1226
+ });
1227
+ engine.implement('pulldelay', (reply, [samples]) => {
1228
+ if (!drop) {
1229
+ console.log('unexpected - processdelay before setup');
1230
+ reply.reject();
1231
+ return;
1232
+ }
1233
+ drop(samples);
1234
+ reply.resolve();
1235
+ });
1236
+ engine.implement('receive', (reply, [ccid, data, track = 0]) => {
1237
+ if (hasDecoder(ccid) === -1) {
1238
+ // CHECK RTP EXT - M bit
1239
+ // if (!(data[17] & 0x40)) {
1240
+ // }
1241
+ // CHECK RTP EXT - energy
1242
+ if (!data[18] && !data[19]) {
1243
+ reply.resolve();
1244
+ return;
1245
+ }
1246
+ const result = native.ipvp_decode_start(ccid, ccid);
1247
+ if (result !== 0) {
1248
+ console.log(`audio decoder failed: ${result}`);
1249
+ }
1250
+ if (result > 0) {
1251
+ console.log(`trying to release audio decoder: ${result}`);
1252
+ const track = removeDecoder(result);
1253
+ native.ipvp_decode_stop(result);
1254
+ native.ipvp_decode_start(ccid, ccid);
1255
+ eventR && eventR.make('decodestop').post(result, track);
1256
+ }
1257
+ addDecoder(ccid, track);
1258
+ eventR && eventR.make('decodestart').post(ccid, track);
1259
+ // gain override
1260
+ const gain = decoderGains[trackType(track)];
1261
+ if (gain >= 0) {
1262
+ native.ipvp_decode_gain([[ccid, gain]]);
1263
+ eventR && eventR.make('gainchange').post(gain, track);
1264
+ }
1265
+ }
1266
+ else if (!decoders[track] || !decoders[track].has(ccid)) {
1267
+ moveDecoder(ccid, track);
1268
+ // gain override
1269
+ const gain = decoderGains[trackType(track)];
1270
+ if (gain >= 0) {
1271
+ native.ipvp_decode_gain([[ccid, gain]]);
1272
+ eventR && eventR.make('gainchange').post(gain, track);
1273
+ }
1274
+ else {
1275
+ const gain = isMainTrack(track) && CURRENT_SUPPRESSION ? 20 : 100;
1276
+ native.ipvp_decode_gain([[ccid, gain]]);
1277
+ eventR && eventR.make('gainchange').post(gain, track);
1278
+ }
1279
+ maybeUpdateDecodeGain();
1280
+ }
1281
+ const memory = alloc(data.length);
1282
+ memory.view().set(data);
1283
+ native.ipvp_net_data(ccid, memory.pointer, memory.bytes, IPVPDataType.kRTP);
1284
+ memory.free();
1285
+ reply.resolve();
1286
+ });
1287
+ engine.implement('setup', (reply, [ccid, samplerate, type = 3 /* kSendRecv */], [port]) => {
1288
+ callid = ccid;
1289
+ if (type & 1 /* kSendOnly */) {
1290
+ native.ipvp_encode_start(ccid, ccid);
1291
+ cleanup.unshift(() => native.ipvp_encode_stop(ccid));
1292
+ }
1293
+ if (type & 2 /* kRecvOnly */) {
1294
+ cleanup.unshift(() => decoderList().forEach((id) => native.ipvp_decode_stop(id)));
1295
+ }
1296
+ const channels = 1;
1297
+ const samples = samplerate / 100;
1298
+ memory = alloc16(samples * channels);
1299
+ inputBuffer = new AudioFrameQueue();
1300
+ outputBuffer = new AudioFrameQueue();
1301
+ refBuffer = new AudioFrameQueue();
1302
+ cleanup.unshift(() => memory.free());
1303
+ console.log(`config - channel: ${channels}, samplerate: ${samplerate}`);
1304
+ native.ipvp_dev_config(samplerate, channels);
1305
+ inputBuffer.onpush = {
1306
+ samples,
1307
+ callback: (frame) => {
1308
+ const { buffer } = frame;
1309
+ // always use first channel
1310
+ const data = [...Array(1)].map((_, ch) => buffer.getChannelData(ch));
1311
+ InterleaveT(data, () => memory.view(), Float32ToInt16);
1312
+ native.ipvp_mic_framing(memory.pointer, memory.bytes);
1313
+ },
1314
+ };
1315
+ refBuffer.onpush = {
1316
+ samples,
1317
+ callback: (frame) => {
1318
+ const { buffer } = frame;
1319
+ // always use first channel
1320
+ const data = [...Array(1)].map((_, ch) => buffer.getChannelData(ch));
1321
+ InterleaveT(data, () => memory.view(), Float32ToInt16);
1322
+ native.ipvp_ref_framing(memory.pointer, memory.bytes);
1323
+ },
1324
+ };
1325
+ outputBuffer.onpull = {
1326
+ samples,
1327
+ callback: () => {
1328
+ native.ipvp_spk_framing(memory.pointer, memory.bytes);
1329
+ const data = DeInterleaveT(memory.view(), channels, (samples) => new Float32Array(samples), Int16ToFloat32);
1330
+ const buffer = new AudioFrameBuffer({
1331
+ length: samples,
1332
+ numberOfChannels: channels,
1333
+ sampleRate: samplerate,
1334
+ data,
1335
+ });
1336
+ const frame = new AudioFrame({ buffer });
1337
+ // ref
1338
+ if (is(3 /* kSendRecv */)) {
1339
+ native.ipvp_ref_framing(memory.pointer, memory.bytes);
1340
+ }
1341
+ return frame;
1342
+ },
1343
+ };
1344
+ const is = (val) => !(type ^ val);
1345
+ push = (builder) => {
1346
+ if (is(2 /* kRecvOnly */)) {
1347
+ console.log('push auido buffer but engine is recv only');
1348
+ return;
1349
+ }
1350
+ const buffer = new AudioFrameBuffer(builder);
1351
+ const frame = new AudioFrame({ buffer });
1352
+ inputBuffer.push(frame);
1353
+ };
1354
+ push_ref = (builder) => {
1355
+ if (is(2 /* kRecvOnly */)) {
1356
+ console.log('push ref auido buffer but engine is recv only');
1357
+ return;
1358
+ }
1359
+ const buffer = new AudioFrameBuffer(builder);
1360
+ const frame = new AudioFrame({ buffer });
1361
+ refBuffer.push(frame);
1362
+ };
1363
+ pull = (wanted) => {
1364
+ if (is(1 /* kSendOnly */)) {
1365
+ console.log('pull auido buffer but engine is send only');
1366
+ return;
1367
+ }
1368
+ // true && checkSamples(wanted, samplerate);
1369
+ const frame = outputBuffer.pull(wanted);
1370
+ if (!frame)
1371
+ return;
1372
+ return AudioFrameBuffer.Builder(frame.buffer);
1373
+ };
1374
+ let dropRequests = 0;
1375
+ drop = () => {
1376
+ if (is(1 /* kSendOnly */)) {
1377
+ console.log('drop audio buffer but engine is send only');
1378
+ return;
1379
+ }
1380
+ dropRequests++;
1381
+ };
1382
+ if (port) {
1383
+ const channel = new MojoChannel(port);
1384
+ // processor api
1385
+ const processor = channel.receiver('worklet-audio-processor-host');
1386
+ processor.implement('process', (reply, [builder]) => {
1387
+ push(builder);
1388
+ let wanted = builder.length;
1389
+ if (dropRequests > 0) {
1390
+ dropRequests--;
1391
+ wanted -= samples;
1392
+ }
1393
+ const output = pull(wanted);
1394
+ if (!output) {
1395
+ reply.resolve();
1396
+ return;
1397
+ }
1398
+ reply.transfer(...output.data.map((d) => d.buffer)).resolve(output);
1399
+ });
1400
+ processor.implement('processdelay', (reply, [samples]) => {
1401
+ drop(samples);
1402
+ reply.resolve();
1403
+ });
1404
+ // source|sink api
1405
+ const source = channel.receiver('worklet-audio-source-host');
1406
+ source.implement('onpush', (reply, [builder]) => {
1407
+ push(builder);
1408
+ reply.resolve();
1409
+ });
1410
+ source.implement('onref', (reply, [builder]) => {
1411
+ push_ref(builder);
1412
+ reply.resolve();
1413
+ });
1414
+ const sink = channel.receiver('worklet-audio-sink-host');
1415
+ sink.implement('onpull', (reply, [wanted]) => {
1416
+ const output = pull(wanted);
1417
+ if (!output) {
1418
+ reply.resolve();
1419
+ return;
1420
+ }
1421
+ reply.transfer(...output.data.map((d) => d.buffer)).resolve(output);
1422
+ });
1423
+ sink.implement('ondelay', (reply, [samples]) => {
1424
+ drop(samples);
1425
+ reply.resolve();
1426
+ });
1427
+ cleanup.unshift(() => {
1428
+ channel.close();
1429
+ port.close();
1430
+ });
1431
+ }
1432
+ reply.resolve();
1433
+ });
1434
+ engine.implement('trackgain', (reply, [gain = -1, track = 0]) => {
1435
+ const type = trackType(track);
1436
+ decoderGains[trackType(track)] = gain;
1437
+ if (gain >= 0) {
1438
+ const decoders = type === 0 /* kMain */ ? decoderList(0, 10) : decoderList(11);
1439
+ native.ipvp_decode_gain(decoders.map((ccid) => [ccid, gain]));
1440
+ eventR && eventR.make('gainchange').post(gain, track);
1441
+ if (type === 0 /* kMain */) {
1442
+ debouncedUpdateDecodeGain.cancel();
1443
+ }
1444
+ }
1445
+ reply.resolve();
1446
+ });
1447
+ engine.implement('fec', (reply, [lossrate]) => {
1448
+ native.ipvp_config_fec(callid, lossrate);
1449
+ reply.resolve();
1450
+ });
1451
+ engine.implement('dumpstats', (reply) => {
1452
+ native.ipvp_debug_dump_stat();
1453
+ reply.resolve();
1454
+ });
1455
+ engine.implement('dumpparam', (reply) => {
1456
+ native.ipvp_debug_dump_param();
1457
+ reply.resolve();
1458
+ });
1459
+ engine.implement('dumptalk', (reply) => {
1460
+ native.ipvp_debug_dump_talk();
1461
+ reply.resolve();
1462
+ });
1463
+ {
1464
+ engine.implement('vtuner', (reply, [host = 'localhost:8090', port = Math.random() * 10000 + 1080]) => {
1465
+ /* eslint-disable no-restricted-globals */
1466
+ const { hostname, protocol } = self.location;
1467
+ const scheme = hostname === 'localhost' ? 'ws:' : protocol.replace('http', 'ws');
1468
+ const ws = new WebSocket(`${scheme}//${host}/?port=${port.toFixed(0)}`);
1469
+ ws.binaryType = 'arraybuffer';
1470
+ console.log(`vtuner: ${port.toFixed(0)}`);
1471
+ let close;
1472
+ ws.onopen = () => {
1473
+ ws.onmessage = ({ data }) => {
1474
+ if (typeof data !== 'string') {
1475
+ memory.view().set(new Uint8Array(data));
1476
+ native.ipvp_debug_vtuner_send(memory.pointer, data.byteLength);
1477
+ }
1478
+ else {
1479
+ console.log(`unknown message:\n\n${data}\n`);
1480
+ }
1481
+ };
1482
+ native.ipvp_debug_vtuner_enable(1);
1483
+ const memory = alloc(512 * 1024);
1484
+ vtuner_reply = (data, length) => {
1485
+ ws.send(view(data, length));
1486
+ };
1487
+ close = () => {
1488
+ console.log('closing vtuner...');
1489
+ native.ipvp_debug_vtuner_enable(0);
1490
+ memory.free();
1491
+ ws.close();
1492
+ };
1493
+ cleanup.unshift(() => {
1494
+ close && close();
1495
+ close = null;
1496
+ });
1497
+ };
1498
+ ws.onclose = () => {
1499
+ close && close();
1500
+ close = null;
1501
+ };
1502
+ reply.resolve();
1503
+ });
1504
+ }
1505
+ const DEFAULT_DELAYED_UPDATE_TIME = 5000;
1506
+ let LAST_SPEAKING = [];
1507
+ let CURRENT_SUPPRESSION = false;
1508
+ function maybeUpdateDecodeGain(speaking = LAST_SPEAKING) {
1509
+ LAST_SPEAKING = speaking;
1510
+ let gain = decoderGains[0 /* kMain */];
1511
+ if (gain >= 0)
1512
+ return;
1513
+ const specialDecoders = decoderList(11);
1514
+ const normalDecoders = decoderList(0, 10);
1515
+ const shouldSuppression = specialDecoders.some((v) => speaking.includes(v));
1516
+ // if has channel > 10
1517
+ // all ssrc in channel below 10 set to 20%
1518
+ if (shouldSuppression) {
1519
+ const DEFAULT_SUPPRESSION_GAIN = 20;
1520
+ gain = DEFAULT_SUPPRESSION_GAIN;
1521
+ debouncedUpdateDecodeGain.cancel();
1522
+ updateDecodeGain(normalDecoders, gain);
1523
+ }
1524
+ // if no channel > 10
1525
+ // all ssrc in channel below 10 set to 100%
1526
+ else {
1527
+ const DEFAULT_GAIN = 100;
1528
+ gain = DEFAULT_GAIN;
1529
+ debouncedUpdateDecodeGain(normalDecoders, gain);
1530
+ }
1531
+ }
1532
+ function updateDecodeGain(decoders, gain) {
1533
+ native.ipvp_decode_gain(decoders.map((ccid) => [ccid, gain]));
1534
+ eventR && eventR.make('gainchange').post(gain, 0 /* kMain */);
1535
+ CURRENT_SUPPRESSION = gain !== 100;
1536
+ }
1537
+ const debouncedUpdateDecodeGain = debounce(updateDecodeGain, DEFAULT_DELAYED_UPDATE_TIME);
1538
+ function debounce(fn, delay) {
1539
+ let timer;
1540
+ function delegate(...args) {
1541
+ clearTimeout(timer);
1542
+ // @ts-ignore
1543
+ timer = setTimeout(() => fn.call(this, ...args), delay);
1544
+ }
1545
+ delegate.cancel = () => clearTimeout(timer);
1546
+ return delegate;
1547
+ }
1548
+ setupBusyDetector();
1549
+ // simple cpu overload detection
1550
+ function setupBusyDetector() {
1551
+ let last = Date.now();
1552
+ let detectTimer;
1553
+ const detectInterval = 1000;
1554
+ const factor = 1.1;
1555
+ const detect = () => {
1556
+ const now = Date.now();
1557
+ const delta = now - last;
1558
+ const tester = detectInterval * factor;
1559
+ if (delta > tester) {
1560
+ console.log(`busy:\n` +
1561
+ `delta: ${delta}\n` +
1562
+ `factor: ${factor}\n` +
1563
+ `interval: ${detectInterval}\n` +
1564
+ `delay: ${delta - detectInterval}\n`);
1565
+ }
1566
+ last = now;
1567
+ detectTimer = setTimeout(detect, detectInterval);
1568
+ };
1569
+ detect();
1570
+ return () => clearTimeout(detectTimer);
1571
+ }
1572
+ // const checkSamples = (samples: number, sampleRate: number) => {
1573
+ // const period = sampleRate / 100;
1574
+ // const diff = samples % period;
1575
+ // const upside = samples - diff + period;
1576
+ // const downside = samples - diff;
1577
+ // if (diff) {
1578
+ // true &&
1579
+ // console.log(`invalid samples: ${samples} -> ${upside} or ${downside}`);
1580
+ // }
1581
+ // };
1582
+
1583
+ }());