@4players/odin-plugin-web 1.0.0-alpha.9 → 1.0.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 (54) hide show
  1. package/README.md +1 -1
  2. package/cmake/dependencies.cmake +31 -0
  3. package/cmake/options.cmake +53 -0
  4. package/esbuild.config.ts +49 -0
  5. package/lib/plugin/index.d.ts +42 -49
  6. package/lib/plugin/index.js +1 -292
  7. package/lib/worker/shared.js +1 -0
  8. package/lib/worker/unshared.js +1 -0
  9. package/package.json +34 -29
  10. package/lib/plugin/audioController.d.ts +0 -107
  11. package/lib/plugin/audioController.js +0 -488
  12. package/lib/plugin/config.d.ts +0 -10
  13. package/lib/plugin/config.js +0 -10
  14. package/lib/plugin/connectionPool.d.ts +0 -16
  15. package/lib/plugin/connectionPool.js +0 -110
  16. package/lib/plugin/mainConnection.d.ts +0 -39
  17. package/lib/plugin/mainConnection.js +0 -293
  18. package/lib/plugin/roomConnection.d.ts +0 -36
  19. package/lib/plugin/roomConnection.js +0 -303
  20. package/lib/plugin/rpcConnection.d.ts +0 -48
  21. package/lib/plugin/rpcConnection.js +0 -234
  22. package/lib/plugin/token.d.ts +0 -17
  23. package/lib/plugin/token.js +0 -78
  24. package/lib/wasm/index.d.ts +0 -97
  25. package/lib/worker/buffers.d.ts +0 -42
  26. package/lib/worker/buffers.js +0 -79
  27. package/lib/worker/common.d.ts +0 -45
  28. package/lib/worker/common.js +0 -7
  29. package/lib/worker/config.d.ts +0 -8
  30. package/lib/worker/config.js +0 -11
  31. package/lib/worker/index.d.ts +0 -3
  32. package/lib/worker/index.js +0 -1
  33. package/lib/worker/memory.d.ts +0 -9
  34. package/lib/worker/memory.js +0 -29
  35. package/lib/worker/messages.d.ts +0 -14
  36. package/lib/worker/messages.js +0 -15
  37. package/lib/worker/packet.d.ts +0 -11
  38. package/lib/worker/packet.js +0 -59
  39. package/lib/worker/shared/capture.d.ts +0 -28
  40. package/lib/worker/shared/capture.js +0 -63
  41. package/lib/worker/shared/index.d.ts +0 -3
  42. package/lib/worker/shared/index.js +0 -148
  43. package/lib/worker/shared/messages.d.ts +0 -67
  44. package/lib/worker/shared/messages.js +0 -64
  45. package/lib/worker/shared/playback.d.ts +0 -21
  46. package/lib/worker/shared/playback.js +0 -78
  47. package/lib/worker/unshared/capture.d.ts +0 -28
  48. package/lib/worker/unshared/capture.js +0 -66
  49. package/lib/worker/unshared/index.d.ts +0 -3
  50. package/lib/worker/unshared/index.js +0 -171
  51. package/lib/worker/unshared/messages.d.ts +0 -81
  52. package/lib/worker/unshared/messages.js +0 -80
  53. package/lib/worker/unshared/playback.d.ts +0 -23
  54. package/lib/worker/unshared/playback.js +0 -83
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@4players/odin-plugin-web",
3
- "version": "1.0.0-alpha.9",
3
+ "version": "1.0.1",
4
4
  "description": "A backend plugin providing the custom WebAssembly audio processing module for ODIN web clients",
5
5
  "homepage": "https://www.4players.io",
6
6
  "repository": {
@@ -11,41 +11,46 @@
11
11
  "main": "./lib/plugin/index.js",
12
12
  "types": "./lib/plugin/index.d.ts",
13
13
  "scripts": {
14
- "clean": "rm -rf build lib",
15
- "configure": "npm-run-all -pl configure:*",
14
+ "clean": "rimraf .cache build lib dist",
15
+ "configure": "npm-run-all configure:*",
16
16
  "configure:shared": "emcmake cmake -B build/shared -DCMAKE_BUILD_TYPE=Release -DSHARED_MEMORY=1 -S .",
17
17
  "configure:unshared": "emcmake cmake -B build/unshared -DCMAKE_BUILD_TYPE=Release -S .",
18
- "build": "npm-run-all -s configure build:wasm build:plugin build:worker",
19
- "build:wasm": "npm-run-all -pl build:wasm:* && node install-wasm.js",
20
- "build:wasm:shared": "emmake cmake --build build/shared",
21
- "build:wasm:unshared": "emmake cmake --build build/unshared",
22
- "build:worker": "ts-node src/build.ts && tsc -b ./tsconfig.worker.json",
23
- "build:plugin": "tsc",
24
- "lint": "eslint . --ext .ts",
25
- "test": "npm --prefix test run serve"
18
+ "build": "npm-run-all -s configure build:genversion build:wasm build:bundle build:types",
19
+ "bundle": "npm run build && tsx ./esbuild.config.ts",
20
+ "build:genversion": "genversion -se src/common/version.ts",
21
+ "build:wasm": "npm-run-all -pl build:wasm:*",
22
+ "build:wasm:shared": "emmake cmake --build build/shared -j 8",
23
+ "build:wasm:unshared": "emmake cmake --build build/unshared -j 8",
24
+ "build:bundle": "ts-node src/bundle.ts",
25
+ "build:types": "dts-bundle-generator --fail-on-class --no-banner -o lib/plugin/index.d.ts src/plugin/index.ts",
26
+ "test": "npm-run-all -s test:*",
27
+ "test:install": "npm --prefix test install",
28
+ "test:start": "npm --prefix test start",
29
+ "format": "prettier 'src/**/*.ts' --write",
30
+ "lint": "eslint . --ext .ts"
26
31
  },
27
32
  "dependencies": {
28
- "@4players/odin-common": "~1.4.0",
29
- "@msgpack/msgpack": "~3.0.0-beta2",
30
- "uuid": "~9.0.0",
31
- "zod": "~3.22.0"
33
+ "@4players/odin-common": "~7.3.0",
34
+ "@4players/voice-filter": "~1.2.0",
35
+ "@msgpack/msgpack": "~3.1.0"
32
36
  },
33
37
  "devDependencies": {
34
- "@types/node": "~20.11.0",
35
- "@types/uuid": "~9.0.0",
36
- "@typescript-eslint/eslint-plugin": "~6.20.0",
37
- "@typescript-eslint/parser": "~6.20.0",
38
- "esbuild": "~0.20.0",
39
- "eslint": "~8.56.0",
38
+ "@types/node": "~22.16.0",
39
+ "@typescript-eslint/eslint-plugin": "~8.36.0",
40
+ "@typescript-eslint/parser": "~8.36.0",
41
+ "dts-bundle-generator": "~9.5.0",
42
+ "esbuild": "~0.25.0",
43
+ "eslint": "~8.57.0",
40
44
  "eslint-config-semistandard": "~17.0.0",
41
- "eslint-plugin-import": "~2.29.0",
45
+ "eslint-plugin-import": "~2.32.0",
42
46
  "eslint-plugin-node": "~11.1.0",
43
- "eslint-plugin-promise": "~6.1.0",
44
- "npm-run-all2": "~6.1.0",
47
+ "eslint-plugin-promise": "~6.6.0",
48
+ "genversion": "~3.2.0",
49
+ "npm-run-all2": "~8.0.0",
50
+ "prettier": "~3.6.0",
51
+ "rimraf": "~6.0.0",
45
52
  "ts-node": "~10.9.0",
46
- "typescript": "~5.3.0"
47
- },
48
- "workspaces": [
49
- "test"
50
- ]
53
+ "tsx": "^4.19.4",
54
+ "typescript": "~5.6.0"
55
+ }
51
56
  }
@@ -1,107 +0,0 @@
1
- import { Backend, Strand } from '@4players/odin-common';
2
- import { DeviceParameters } from '.';
3
- import { RoomConnection } from './roomConnection';
4
- import { CaptureSink, PlaybackId, PlaybackWorker } from '../worker/common';
5
- export type Volume = Backend.Volume;
6
- export type Activity = Backend.Activity;
7
- export type CreateAudioPlaybackParameters = Backend.CreateAudioPlaybackParameters;
8
- export type CreateAudioCaptureParameters = Backend.CreateAudioCaptureParameters & {
9
- readonly device?: Device;
10
- };
11
- export type AudioContextFactory = (sampleRate: number | undefined) => Promise<AudioContext>;
12
- export interface GlobalMediaId extends PlaybackId {
13
- }
14
- declare global {
15
- interface AudioSinkOptions {
16
- type: 'none';
17
- }
18
- interface AudioContext {
19
- setSinkId?: (sinkId: string | undefined | AudioSinkOptions) => Promise<undefined>;
20
- }
21
- }
22
- export declare class AudioContextCollection {
23
- readonly audioContextFactory: AudioContextFactory;
24
- readonly entries: Array<{
25
- sampleRate: undefined | number;
26
- audioContext: AudioContext;
27
- references: number;
28
- }>;
29
- constructor(audioContextFactory: AudioContextFactory);
30
- acquire(sampleRate: number | undefined): Promise<AudioContext>;
31
- release(audioContext: BaseAudioContext): Promise<void>;
32
- close(): void;
33
- }
34
- export declare class AudioController {
35
- readonly audioContexts: AudioContextCollection;
36
- readonly sink: CaptureSink;
37
- playbackWorker: PlaybackWorker | undefined;
38
- globalVolume: number;
39
- playbacks: Map<string, {
40
- id: GlobalMediaId | undefined;
41
- volume: Volume;
42
- playback: AudioPlayback;
43
- }>;
44
- setPlaybackStrand: Strand;
45
- create(audioContexts: AudioContextCollection, sink: CaptureSink): Promise<AudioController>;
46
- constructor(audioContexts: AudioContextCollection, sink: CaptureSink);
47
- receivePacket(connectionId: number, packet: Uint8Array): void;
48
- createAudioPlayback(parameters: CreateAudioPlaybackParameters): Promise<AudioPlayback>;
49
- createAudioCapture(parameters: CreateAudioCaptureParameters): Promise<AudioCapture>;
50
- enumerateDevices(): Promise<Device[]>;
51
- /**
52
- * Requests audio and video browser permissions. Optionally, requests either audio or video permissions.
53
- * @private
54
- * @throws
55
- */
56
- private _requestMediaPermissions;
57
- playbackSetDevice(device?: Device): Promise<void>;
58
- playbackSetGlobalVolume(value: Backend.Volume): void;
59
- playbackStart(uid: string, id: GlobalMediaId): void;
60
- playbackStop(uid: string, id: GlobalMediaId): void;
61
- playbackGetActivity(uid: string): Activity;
62
- playbackSetVolume(uid: string, volume: Volume): void;
63
- playbackGetVolume(uid: string): Volume;
64
- closePlayback(uid: string): void;
65
- close(): void;
66
- }
67
- export declare class Device implements Backend.Device {
68
- readonly device: MediaDeviceInfo;
69
- readonly isDefault: boolean;
70
- constructor(device: MediaDeviceInfo, isDefault?: boolean);
71
- get type(): "AudioPlayback" | "AudioCapture";
72
- get name(): string;
73
- }
74
- export declare class AudioPlayback implements Backend.AudioPlayback {
75
- readonly controller: AudioController;
76
- readonly uid: Backend.UID;
77
- get volume(): Backend.Volume;
78
- get activity(): Backend.Activity;
79
- constructor(controller: AudioController, uid: Backend.UID);
80
- start(id: GlobalMediaId): void;
81
- stop(id: GlobalMediaId): void;
82
- setVolume(value: Backend.Volume): void;
83
- close(): void;
84
- }
85
- export declare class AudioCapture implements Backend.AudioCapture {
86
- readonly controller: AudioController;
87
- private _Capture;
88
- private readonly _Medias;
89
- private _Vad;
90
- private _Apm;
91
- private _Volume;
92
- private _deviceParameters?;
93
- get vad(): Backend.VadConfig;
94
- get apm(): Backend.ApmConfig;
95
- get volume(): Backend.Volume;
96
- get activity(): import("../worker/common").Activity;
97
- get speechProbability(): number;
98
- constructor(controller: AudioController, parameters: CreateAudioCaptureParameters);
99
- start(room: RoomConnection, media: GlobalMediaId, uid: Backend.UID): void;
100
- stop(room: RoomConnection, media: GlobalMediaId): void;
101
- setVad(value: Backend.VadConfig): void;
102
- setApm(config: Backend.ApmConfig): Promise<void>;
103
- setDevice(parameters: DeviceParameters): Promise<import("@4players/odin-common").Success<void> | undefined>;
104
- setVolume(value: Backend.Volume): void;
105
- close(): void;
106
- _UpdateMedias(): Promise<void>;
107
- }
@@ -1,488 +0,0 @@
1
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
- return new (P || (P = Promise))(function (resolve, reject) {
4
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
- step((generator = generator.apply(thisArg, _arguments || [])).next());
8
- });
9
- };
10
- import { Strand, assert, failure, sleep, success, } from '@4players/odin-common';
11
- import { Silence, } from '../worker/common';
12
- import { createCaptureWorker, createPlaybackWorker } from '../worker';
13
- const defaultVad = {
14
- voiceActivity: {
15
- attackThreshold: 0.9,
16
- releaseThreshold: 0.8,
17
- },
18
- volumeGate: {
19
- attackThreshold: -30,
20
- releaseThreshold: -40,
21
- },
22
- };
23
- const defaultApm = {
24
- echoCanceller: false,
25
- highPassFilter: false,
26
- preAmplifier: false,
27
- captureLevelAdjustment: false,
28
- noiseSuppression: 'None',
29
- transientSuppressor: false,
30
- gainController: false,
31
- };
32
- export class AudioContextCollection {
33
- constructor(audioContextFactory) {
34
- this.audioContextFactory = audioContextFactory;
35
- this.entries = [];
36
- }
37
- acquire(sampleRate) {
38
- return __awaiter(this, void 0, void 0, function* () {
39
- for (const entry of this.entries) {
40
- if (entry.sampleRate === sampleRate) {
41
- entry.references += 1;
42
- return entry.audioContext;
43
- }
44
- else if (entry.sampleRate === undefined) {
45
- const audioContext = entry.audioContext;
46
- if (audioContext.sampleRate === sampleRate) {
47
- entry.references += 1;
48
- return entry.audioContext;
49
- }
50
- }
51
- }
52
- const entry = {
53
- sampleRate,
54
- audioContext: yield this.audioContextFactory(sampleRate),
55
- references: 1,
56
- };
57
- this.entries.push(entry);
58
- return entry.audioContext;
59
- });
60
- }
61
- release(audioContext) {
62
- return __awaiter(this, void 0, void 0, function* () {
63
- const index = this.entries.findIndex((entry) => entry.audioContext === audioContext);
64
- if (index < 0)
65
- return;
66
- this.entries[index].references -= 1;
67
- if (this.entries[index].references <= 0) {
68
- const context = this.entries[index].audioContext;
69
- this.entries.splice(index, 1);
70
- yield context.close();
71
- }
72
- });
73
- }
74
- close() {
75
- for (const entry of this.entries) {
76
- entry.audioContext.close();
77
- }
78
- }
79
- }
80
- let hasAudioPermissions = false;
81
- export class AudioController {
82
- create(audioContexts, sink) {
83
- return __awaiter(this, void 0, void 0, function* () {
84
- return new AudioController(audioContexts, sink);
85
- });
86
- }
87
- constructor(audioContexts, sink) {
88
- this.audioContexts = audioContexts;
89
- this.sink = sink;
90
- this.globalVolume = 1;
91
- this.playbacks = new Map();
92
- this.setPlaybackStrand = new Strand();
93
- }
94
- receivePacket(connectionId, packet) {
95
- var _a;
96
- (_a = this.playbackWorker) === null || _a === void 0 ? void 0 : _a.receivePacket(connectionId, packet);
97
- }
98
- createAudioPlayback(parameters) {
99
- return __awaiter(this, void 0, void 0, function* () {
100
- let entry = this.playbacks.get(parameters.uid);
101
- let volume = parameters.volume;
102
- if (entry === undefined) {
103
- volume = volume !== null && volume !== void 0 ? volume : 1;
104
- const playback = new AudioPlayback(this, parameters.uid);
105
- entry = { id: undefined, volume, playback };
106
- this.playbacks.set(parameters.uid, entry);
107
- }
108
- if (volume !== undefined) {
109
- entry.playback.setVolume(volume);
110
- }
111
- return entry.playback;
112
- });
113
- }
114
- createAudioCapture(parameters) {
115
- return __awaiter(this, void 0, void 0, function* () {
116
- const capture = new AudioCapture(this, parameters);
117
- yield capture.setDevice(parameters);
118
- return capture;
119
- });
120
- }
121
- enumerateDevices() {
122
- return __awaiter(this, void 0, void 0, function* () {
123
- hasAudioPermissions = yield this._requestMediaPermissions();
124
- const mediaDeviceInfos = yield navigator.mediaDevices.enumerateDevices();
125
- const devices = [];
126
- let outputDeviceIndex = 0;
127
- let inputDeviceIndex = 0;
128
- for (const mediaDeviceInfo of mediaDeviceInfos) {
129
- if (mediaDeviceInfo.kind === 'audioinput') {
130
- if (inputDeviceIndex === 0) {
131
- devices.push(new Device(mediaDeviceInfo, true));
132
- }
133
- else {
134
- devices.push(new Device(mediaDeviceInfo, false));
135
- }
136
- inputDeviceIndex++;
137
- }
138
- if (mediaDeviceInfo.kind === 'audiooutput') {
139
- if (outputDeviceIndex === 0) {
140
- devices.push(new Device(mediaDeviceInfo, true));
141
- }
142
- else {
143
- devices.push(new Device(mediaDeviceInfo, false));
144
- }
145
- outputDeviceIndex++;
146
- }
147
- }
148
- return devices;
149
- });
150
- }
151
- /**
152
- * Requests audio and video browser permissions. Optionally, requests either audio or video permissions.
153
- * @private
154
- * @throws
155
- */
156
- _requestMediaPermissions() {
157
- return __awaiter(this, void 0, void 0, function* () {
158
- if (hasAudioPermissions)
159
- return true;
160
- const constraints = {};
161
- constraints.audio = {
162
- sampleRate: undefined,
163
- deviceId: '',
164
- autoGainControl: false,
165
- noiseSuppression: false,
166
- advanced: undefined,
167
- aspectRatio: undefined,
168
- echoCancellation: false,
169
- channelCount: 0,
170
- facingMode: undefined,
171
- frameRate: 0,
172
- };
173
- console.log('Plugin _requestMediaPermissions: ', constraints);
174
- try {
175
- const ms = yield navigator.mediaDevices.getUserMedia(constraints);
176
- ms === null || ms === void 0 ? void 0 : ms.getTracks().forEach((track) => {
177
- track.stop();
178
- });
179
- }
180
- catch (e) {
181
- console.error(`Requesting permissions for ${'audio'} failed.`);
182
- console.error(e);
183
- return false;
184
- }
185
- return true;
186
- });
187
- }
188
- playbackSetDevice(device) {
189
- return this.setPlaybackStrand.enqueue(() => __awaiter(this, void 0, void 0, function* () {
190
- var _a;
191
- let deviceId = (_a = device === null || device === void 0 ? void 0 : device.device.deviceId) !== null && _a !== void 0 ? _a : '';
192
- if (deviceId === 'default') {
193
- deviceId = '';
194
- }
195
- let audioContext;
196
- if (this.playbackWorker === undefined) {
197
- audioContext = yield this.audioContexts.acquire(48000);
198
- console.log('playbackSetDevice: this.audioContexts.acquire(48000)');
199
- this.playbackWorker = yield createPlaybackWorker(audioContext);
200
- this.playbackWorker.setGlobalVolume(this.globalVolume);
201
- }
202
- else {
203
- console.log('this.playbackWorker.audioContext');
204
- audioContext = this.playbackWorker.audioContext;
205
- }
206
- console.log('playbackSetDevice audioContext: ', audioContext);
207
- if ((audioContext === null || audioContext === void 0 ? void 0 : audioContext.setSinkId) !== undefined &&
208
- device &&
209
- (audioContext === null || audioContext === void 0 ? void 0 : audioContext.state) === 'running') {
210
- console.log('Browser audioContext.setSinkId');
211
- yield audioContext.setSinkId(deviceId);
212
- }
213
- }));
214
- }
215
- playbackSetGlobalVolume(value) {
216
- var _a;
217
- this.globalVolume = value === 'muted' ? 0 : value;
218
- (_a = this.playbackWorker) === null || _a === void 0 ? void 0 : _a.setGlobalVolume(this.globalVolume);
219
- }
220
- playbackStart(uid, id) {
221
- var _a;
222
- const entry = this.playbacks.get(uid);
223
- if (entry === undefined)
224
- return;
225
- if (entry.id !== undefined) {
226
- this.playbackStop(uid, entry.id);
227
- }
228
- entry.id = id;
229
- (_a = this.playbackWorker) === null || _a === void 0 ? void 0 : _a.addMedias(id.connectionId, [id.mediaId]);
230
- this.playbackSetVolume(uid, entry.volume);
231
- }
232
- playbackStop(uid, id) {
233
- var _a;
234
- const entry = this.playbacks.get(uid);
235
- if (entry === undefined)
236
- return;
237
- if (entry.id !== id)
238
- console.error('playback mediaId out of sync!');
239
- entry.id = undefined;
240
- (_a = this.playbackWorker) === null || _a === void 0 ? void 0 : _a.removeMedias(id.connectionId, [id.mediaId]);
241
- }
242
- playbackGetActivity(uid) {
243
- var _a, _b;
244
- const entry = this.playbacks.get(uid);
245
- if ((entry === null || entry === void 0 ? void 0 : entry.id) === undefined)
246
- return Silence;
247
- return (_b = (_a = this.playbackWorker) === null || _a === void 0 ? void 0 : _a.getActivity(entry.id)) !== null && _b !== void 0 ? _b : Silence;
248
- }
249
- playbackSetVolume(uid, volume) {
250
- var _a;
251
- const entry = this.playbacks.get(uid);
252
- if (entry === undefined)
253
- return;
254
- entry.volume = volume === 'muted' ? 0 : volume;
255
- if (entry.id !== undefined) {
256
- (_a = this.playbackWorker) === null || _a === void 0 ? void 0 : _a.setVolume(entry.id, entry.volume);
257
- }
258
- }
259
- playbackGetVolume(uid) {
260
- var _a;
261
- const entry = this.playbacks.get(uid);
262
- return (_a = entry === null || entry === void 0 ? void 0 : entry.volume) !== null && _a !== void 0 ? _a : 1;
263
- }
264
- closePlayback(uid) {
265
- const entry = this.playbacks.get(uid);
266
- if (entry === undefined)
267
- return;
268
- if (entry.id !== undefined)
269
- this.playbackStop(uid, entry.id);
270
- this.playbacks.delete(uid);
271
- }
272
- close() {
273
- var _a;
274
- (_a = this.playbackWorker) === null || _a === void 0 ? void 0 : _a.close();
275
- this.audioContexts.close();
276
- }
277
- }
278
- export class Device {
279
- constructor(device, isDefault = false) {
280
- this.device = device;
281
- this.isDefault = isDefault;
282
- }
283
- get type() {
284
- switch (this.device.kind) {
285
- case 'audioinput':
286
- return 'AudioCapture';
287
- case 'audiooutput':
288
- return 'AudioPlayback';
289
- default:
290
- assert(false, 'not supported device');
291
- }
292
- }
293
- get name() {
294
- return this.device.label;
295
- }
296
- }
297
- export class AudioPlayback {
298
- get volume() {
299
- return this.controller.playbackGetVolume(this.uid);
300
- }
301
- get activity() {
302
- return this.controller.playbackGetActivity(this.uid);
303
- }
304
- constructor(controller, uid) {
305
- this.controller = controller;
306
- this.uid = uid;
307
- }
308
- start(id) {
309
- this.controller.playbackStart(this.uid, id);
310
- }
311
- stop(id) {
312
- this.controller.playbackStop(this.uid, id);
313
- }
314
- setVolume(value) {
315
- this.controller.playbackSetVolume(this.uid, value);
316
- }
317
- close() {
318
- this.controller.closePlayback(this.uid);
319
- }
320
- }
321
- export class AudioCapture {
322
- get vad() {
323
- return this._Vad;
324
- }
325
- get apm() {
326
- return this._Apm;
327
- }
328
- get volume() {
329
- return this._Volume;
330
- }
331
- get activity() {
332
- var _a, _b;
333
- return (_b = (_a = this._Capture) === null || _a === void 0 ? void 0 : _a.worker.activity) !== null && _b !== void 0 ? _b : Silence;
334
- }
335
- get speechProbability() {
336
- var _a, _b;
337
- return (_b = (_a = this._Capture) === null || _a === void 0 ? void 0 : _a.worker.speechProbability) !== null && _b !== void 0 ? _b : 0;
338
- }
339
- constructor(controller, parameters) {
340
- this.controller = controller;
341
- this._Medias = new Map();
342
- this._Vad = defaultVad;
343
- this._Apm = defaultApm;
344
- this._Volume = 1;
345
- if (parameters.apm) {
346
- this.setApm(parameters.apm);
347
- }
348
- if (parameters.vad) {
349
- this.setVad(parameters.vad);
350
- }
351
- if (parameters.volume) {
352
- this.setVolume(parameters.volume);
353
- }
354
- }
355
- start(room, media, uid) {
356
- var _a;
357
- let entry = this._Medias.get(media.connectionId);
358
- if (entry === undefined) {
359
- this._Medias.set(media.connectionId, (entry = new Set()));
360
- }
361
- if (entry.has(media.mediaId))
362
- return;
363
- entry.add(media.mediaId);
364
- if (((_a = room.mainConnection) === null || _a === void 0 ? void 0 : _a.id) === media.connectionId) {
365
- room
366
- .typedRequest('StartMedia', {
367
- media_id: media.mediaId,
368
- properties: { kind: 'audio', uid },
369
- })
370
- .catch((reason) => console.error(`StartMedia got rejected; ${reason}`));
371
- }
372
- this._UpdateMedias();
373
- }
374
- stop(room, media) {
375
- var _a;
376
- const entry = this._Medias.get(media.connectionId);
377
- if (!(entry === null || entry === void 0 ? void 0 : entry.delete(media.mediaId)))
378
- return;
379
- if (((_a = room.mainConnection) === null || _a === void 0 ? void 0 : _a.id) === media.connectionId) {
380
- room
381
- .typedRequest('StopMedia', { media_id: media.mediaId })
382
- .catch((reason) => console.error(`StopMedia got rejected; ${reason}`));
383
- }
384
- this._UpdateMedias();
385
- }
386
- setVad(value) {
387
- this._Vad = value;
388
- if (this._Capture) {
389
- this._Capture.worker.setVad(value);
390
- }
391
- }
392
- setApm(config) {
393
- return __awaiter(this, void 0, void 0, function* () {
394
- this._Apm = config;
395
- if (this._deviceParameters) {
396
- yield this.setDevice(this._deviceParameters);
397
- }
398
- });
399
- }
400
- setDevice(parameters) {
401
- var _a;
402
- return __awaiter(this, void 0, void 0, function* () {
403
- const device = yield openCaptureDevice(this.controller, parameters, this._Apm);
404
- if (device.type !== 'Success') {
405
- console.error(`unable to open capture device; ${device.reason}`);
406
- return;
407
- }
408
- this._deviceParameters = parameters;
409
- if (this._Capture !== undefined) {
410
- this._Capture.source.disconnect(this._Capture.worker.worklet);
411
- }
412
- if (this._Capture === undefined ||
413
- this._Capture.worker.worklet.context !== device.value.context) {
414
- (_a = this._Capture) === null || _a === void 0 ? void 0 : _a.worker.close();
415
- this._Capture = {
416
- worker: yield createCaptureWorker(device.value.context, this.controller.sink),
417
- source: device.value,
418
- };
419
- yield this._UpdateMedias();
420
- this.setVolume(this.volume);
421
- this.setVad(this.vad);
422
- }
423
- else {
424
- this._Capture.source = device.value;
425
- }
426
- this._Capture.source.connect(this._Capture.worker.worklet);
427
- return success();
428
- });
429
- }
430
- setVolume(value) {
431
- this._Volume = value;
432
- if (this._Capture) {
433
- this._Capture.worker.setVolume(value === 'muted' ? 0 : value);
434
- }
435
- }
436
- close() {
437
- if (this._Capture !== undefined) {
438
- this._Capture.worker.close();
439
- this._Capture.source.disconnect(this._Capture.worker.worklet);
440
- this.controller.audioContexts.release(this._Capture.source.context);
441
- }
442
- this._Capture = undefined;
443
- }
444
- _UpdateMedias() {
445
- return __awaiter(this, void 0, void 0, function* () {
446
- if (this._Capture !== undefined) {
447
- const connections = Array.from(this._Medias).map(([key, value]) => {
448
- return { id: key, mediaIds: Array.from(value) };
449
- });
450
- this._Capture.worker.setMedias(connections);
451
- }
452
- });
453
- }
454
- }
455
- function openCaptureDevice(controller, parameters, apm) {
456
- return __awaiter(this, void 0, void 0, function* () {
457
- const deviceId = parameters.device
458
- ? { exact: parameters.device.device.deviceId }
459
- : undefined;
460
- let stream;
461
- try {
462
- const result = yield Promise.race([
463
- navigator.mediaDevices.getUserMedia({
464
- audio: {
465
- echoCancellation: apm.echoCanceller,
466
- noiseSuppression: apm.noiseSuppression !== 'None',
467
- autoGainControl: apm.gainController,
468
- // @ts-ignore chrome fix
469
- echoCancellationType: 'system',
470
- // sampleRate: 48000,
471
- deviceId,
472
- },
473
- }),
474
- sleep(30000, 'timeout'),
475
- ]);
476
- if (result === 'timeout')
477
- return failure('getUserMedia; timeout');
478
- hasAudioPermissions = true;
479
- stream = result;
480
- }
481
- catch (reason) {
482
- return failure(`getUserMedia; ${reason}`);
483
- }
484
- const sampleRate = stream.getAudioTracks()[0].getSettings().sampleRate;
485
- const context = yield controller.audioContexts.acquire(sampleRate);
486
- return success(context.createMediaStreamSource(stream));
487
- });
488
- }
@@ -1,10 +0,0 @@
1
- export declare const HealthCheckTimeout = 1000;
2
- export declare const ClockJumpCheckInterval = 1000;
3
- export declare const ClockJumpThreshold = 10000;
4
- export declare const MainConnectionPingInterval = 10000;
5
- export declare const WebRTCIceTimeout = 3000;
6
- export declare const WebRTCTimeout = 10000;
7
- export declare const ConnectTimeout = 5000;
8
- export declare const MinTokenLifetime = 60000;
9
- export declare const MinTokenRefreshInterval = 5000;
10
- export declare const ConnectInterval: number[];