浏览代码

move VADReportingService

dev1
Andrei Gavrilescu 5 年前
父节点
当前提交
42a3cb5f84

+ 1
- 1
modules/detection/ActiveDeviceDetector.js 查看文件

18
  */
18
  */
19
 export default function getActiveAudioDevice() {
19
 export default function getActiveAudioDevice() {
20
 
20
 
21
-    return new Promise((resolve, reject) => {
21
+    return new Promise(resolve => {
22
         RTC.enumerateDevices(devices => {
22
         RTC.enumerateDevices(devices => {
23
             const audioDevices = devices.filter(device => device.kind === 'audioinput');
23
             const audioDevices = devices.filter(device => device.kind === 'audioinput');
24
             const devicePromiseArray = [];
24
             const devicePromiseArray = [];

+ 11
- 0
modules/detection/DetectionEvents.js 查看文件

13
  */
13
  */
14
 export const AUDIO_INPUT_STATE_CHANGE = 'audio_input_state_changed';
14
 export const AUDIO_INPUT_STATE_CHANGE = 'audio_input_state_changed';
15
 
15
 
16
+/**
17
+ * Event generated by VADReportingService when if finishes creating a VAD report for the monitored devices.
18
+ * The generated objects are of type Array<Object>, one score for each monitored device.
19
+ * @event VAD_REPORT_PUBLISHED
20
+ * @type Array<Object> with the following structure:
21
+ * @property {Date} timestamp - Timestamp at which the compute took place.
22
+ * @property {number} avgVAD - Average VAD score over monitored period of time.
23
+ * @property {string} deviceId - Associate local audio device ID.
24
+ */
25
+export const VAD_REPORT_PUBLISHED = 'vad-report-published';
26
+
16
 /**
27
 /**
17
  * Event generated by {@link TrackVADEmitter} when PCM sample VAD score is available.
28
  * Event generated by {@link TrackVADEmitter} when PCM sample VAD score is available.
18
  *
29
  *

+ 247
- 0
modules/detection/VADReportingService.js 查看文件

1
+import EventEmitter from 'events';
2
+import { getLogger } from 'jitsi-meet-logger';
3
+import * as DetectionEvents from './DetectionEvents';
4
+import TrackVADEmitter from './TrackVADEmitter';
5
+
6
+const logger = getLogger(__filename);
7
+
8
+/**
9
+ * Sample rate used by TrackVADEmitter, this value determines how often the ScriptProcessorNode is going to call the
10
+ * process audio function and with what sample size.
11
+ * Basically lower values mean more callbacks with lower processing times bigger values less callbacks with longer
12
+ * processing times. This value is somewhere in the middle, so we strike a balance between flooding with callbacks
13
+ * and processing time. Possible values  256, 512, 1024, 2048, 4096, 8192, 16384. Passing other values will default
14
+ * to closes neighbor.
15
+ */
16
+const SCRIPT_NODE_SAMPLE_RATE = 4096;
17
+
18
+/**
19
+ * Voice activity detection reporting service. The service create TrackVADEmitters for the provided devices and
20
+ * publishes an average of their VAD score over the specified interval via EventEmitter.
21
+ * The service is not reusable if destroyed a new one needs to be created, i.e. when a new device is added to the system
22
+ * a new service needs to be created and the old discarded.
23
+ */
24
+export default class VADReportingService extends EventEmitter {
25
+
26
+    /**
27
+     *
28
+     * @param {number} intervalDelay - Delay at which to publish VAD score for monitored devices.
29
+     *
30
+     * @constructor
31
+     */
32
+    constructor(intervalDelay) {
33
+        super();
34
+
35
+        /**
36
+         * Map containing context for devices currently being monitored by the reporting service.
37
+         */
38
+        this._contextMap = new Map();
39
+
40
+
41
+        /**
42
+         * State flag, check if the instance was destroyed.
43
+         */
44
+        this._destroyed = false;
45
+
46
+        /**
47
+         * Delay at which to publish VAD score for monitored devices.
48
+         */
49
+        this._intervalDelay = intervalDelay;
50
+
51
+        /**
52
+         * Identifier for the interval publishing stats on the set interval.
53
+         */
54
+        this._intervalId = null;
55
+
56
+
57
+        logger.log(`Constructed VADReportingService with publish interval of: ${intervalDelay}`);
58
+    }
59
+
60
+    /**
61
+     * Factory methods that creates the TrackVADEmitters for the associated array of devices and instantiates
62
+     * a VADReportingService.
63
+     *
64
+     * @param {Array<MediaDeviceInfo>} micDeviceList - Device list that is monitored inside the service.
65
+     * @param {number} intervalDelay - Delay at which to publish VAD score for monitored devices.
66
+     * @param {Object} createVADProcessor - Function that creates a Voice activity detection processor. The processor
67
+     * needs to implement the following functions:
68
+     * - <tt>getSampleLength()</tt> - Returns the sample size accepted by getSampleLength.
69
+     * - <tt>getRequiredPCMFrequency()</tt> - Returns the PCM frequency at which the processor operates.
70
+     * - <tt>calculateAudioFrameVAD(pcmSample)</tt> - Process a 32 float pcm sample of getSampleLength size.
71
+     *
72
+     * @returns {Promise<VADReportingService>}
73
+     */
74
+    static create(micDeviceList, intervalDelay, createVADProcessor) {
75
+        const vadReportingService = new VADReportingService(intervalDelay);
76
+        const emitterPromiseArray = [];
77
+
78
+        const audioDeviceList = micDeviceList.filter(device => device.kind === 'audioinput');
79
+
80
+        // Create a TrackVADEmitter for each provided audio input device.
81
+        for (const micDevice of audioDeviceList) {
82
+            logger.log(`Initializing VAD context for mic: ${micDevice.label} -> ${micDevice.deviceId}`);
83
+
84
+            const emitterPromise = createVADProcessor()
85
+            .then(rnnoiseProcessor =>
86
+                TrackVADEmitter.create(micDevice.deviceId, SCRIPT_NODE_SAMPLE_RATE, rnnoiseProcessor))
87
+            .then(emitter => {
88
+                emitter.on(
89
+                    DetectionEvents.VAD_SCORE_PUBLISHED,
90
+                    vadReportingService._devicePublishVADScore.bind(vadReportingService)
91
+                );
92
+                emitter.start();
93
+
94
+                return {
95
+                    vadEmitter: emitter,
96
+                    deviceInfo: micDevice,
97
+                    scoreArray: []
98
+                };
99
+            });
100
+
101
+            emitterPromiseArray.push(emitterPromise);
102
+        }
103
+
104
+        // Once all the TrackVADEmitter promises are resolved get the ones that were successfully initialized and start
105
+        // monitoring them.
106
+        return Promise.allSettled(emitterPromiseArray).then(outcomeArray => {
107
+
108
+            const successfulPromises = outcomeArray.filter(p => p.status === 'fulfilled');
109
+            const rejectedPromises = outcomeArray.filter(p => p.status === 'rejected');
110
+
111
+
112
+            const availableDeviceContexts = successfulPromises.map(p => p.value);
113
+            const rejectReasons = rejectedPromises.map(p => p.value);
114
+
115
+            for (const reason of rejectReasons) {
116
+                logger.error('Failed to acquire audio device with error: ', reason);
117
+            }
118
+
119
+            vadReportingService._setVADContextArray(availableDeviceContexts);
120
+            vadReportingService._startPublish();
121
+
122
+            return vadReportingService;
123
+        });
124
+    }
125
+
126
+    /**
127
+     * Destroy TrackVADEmitters and clear the context map.
128
+     *
129
+     * @returns {void}
130
+     */
131
+    _clearContextMap() {
132
+        for (const vadContext of this._contextMap.values()) {
133
+            vadContext.vadEmitter.destroy();
134
+        }
135
+        this._contextMap.clear();
136
+    }
137
+
138
+    /**
139
+     * Set the watched device contexts.
140
+     *
141
+     * @param {Array<VADDeviceContext>} vadContextArray - List of mics.
142
+     * @returns {void}
143
+     */
144
+    _setVADContextArray(vadContextArray) {
145
+        for (const vadContext of vadContextArray) {
146
+            this._contextMap.set(vadContext.deviceInfo.deviceId, vadContext);
147
+        }
148
+    }
149
+
150
+    /**
151
+     * Start the setInterval reporting process.
152
+     *
153
+     * @returns {void}.
154
+     */
155
+    _startPublish() {
156
+        logger.log('VADReportingService started publishing.');
157
+        this._intervalId = setInterval(() => {
158
+            this._reportVadScore();
159
+        }, this._intervalDelay);
160
+    }
161
+
162
+    /**
163
+     * Function called at set interval with selected compute. The result will be published on the set callback.
164
+     *
165
+     * @returns {void}
166
+     * @fires VAD_REPORT_PUBLISHED
167
+     */
168
+    _reportVadScore() {
169
+        const vadComputeScoreArray = [];
170
+        const computeTimestamp = Date.now();
171
+
172
+        // Go through each device and compute cumulated VAD score.
173
+
174
+        for (const [ deviceId, vadContext ] of this._contextMap) {
175
+            const nrOfVADScores = vadContext.scoreArray.length;
176
+            let vadSum = 0;
177
+
178
+            vadContext.scoreArray.forEach(vadScore => {
179
+                vadSum += vadScore.score;
180
+            });
181
+
182
+            // TODO For now we just calculate the average score for each device, more compute algorithms will be added.
183
+            const avgVAD = vadSum / nrOfVADScores;
184
+
185
+            vadContext.scoreArray = [];
186
+
187
+            vadComputeScoreArray.push({
188
+                timestamp: computeTimestamp,
189
+                score: avgVAD,
190
+                deviceId
191
+            });
192
+        }
193
+
194
+        logger.log('VADReportingService reported.', vadComputeScoreArray);
195
+
196
+        /**
197
+         * Once the computation for all the tracked devices is done, fire an event containing all the necessary
198
+         * information.
199
+         *
200
+         * @event VAD_REPORT_PUBLISHED
201
+         * @type Array<Object> with the following structure:
202
+         * @property {Date} timestamp - Timestamo at which the compute took place.
203
+         * @property {number} avgVAD - Average VAD score over monitored period of time.
204
+         * @property {string} deviceId - Associate local audio device ID.
205
+         */
206
+        this.emit(DetectionEvents.VAD_REPORT_PUBLISHED, vadComputeScoreArray);
207
+    }
208
+
209
+    /**
210
+     * Callback method passed to vad emitters in order to publish their score.
211
+     *
212
+     * @param {Object} vadScore -VAD score emitted by.
213
+     * @param {Date}   vadScore.timestamp - Exact time at which processed PCM sample was generated.
214
+     * @param {number} vadScore.score - VAD score on a scale from 0 to 1 (i.e. 0.7).
215
+     * @param {string} vadScore.deviceId - Device id of the associated track.
216
+     * @returns {void}
217
+     * @listens VAD_SCORE_PUBLISHED
218
+     */
219
+    _devicePublishVADScore(vadScore) {
220
+        const context = this._contextMap.get(vadScore.deviceId);
221
+
222
+        if (context) {
223
+            context.scoreArray.push(vadScore);
224
+        }
225
+    }
226
+
227
+    /**
228
+     * Destroy the VADReportingService, stops the setInterval reporting, destroys the emitters and clears the map.
229
+     * After this call the instance is no longer usable.
230
+     *
231
+     * @returns {void}.
232
+     */
233
+    destroy() {
234
+        if (this._destroyed) {
235
+            return;
236
+        }
237
+
238
+        logger.log('Destroying VADReportingService.');
239
+
240
+        if (this._intervalId) {
241
+            clearInterval(this._intervalId);
242
+            this._intervalId = null;
243
+        }
244
+        this._clearContextMap();
245
+        this._destroyed = true;
246
+    }
247
+}

+ 1
- 1
modules/detection/VADTalkMutedDetection.js 查看文件

1
 import { EventEmitter } from 'events';
1
 import { EventEmitter } from 'events';
2
 import { VAD_SCORE_PUBLISHED, VAD_TALK_WHILE_MUTED } from './DetectionEvents';
2
 import { VAD_SCORE_PUBLISHED, VAD_TALK_WHILE_MUTED } from './DetectionEvents';
3
 import { getLogger } from 'jitsi-meet-logger';
3
 import { getLogger } from 'jitsi-meet-logger';
4
-import TrackVADEmitter from '../detection/TrackVADEmitter';
4
+import TrackVADEmitter from './TrackVADEmitter';
5
 import * as JitsiConferenceEvents from '../../JitsiConferenceEvents';
5
 import * as JitsiConferenceEvents from '../../JitsiConferenceEvents';
6
 
6
 
7
 const logger = getLogger(__filename);
7
 const logger = getLogger(__filename);

正在加载...
取消
保存