123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187 |
- import { EventEmitter } from 'events';
-
- import { calculateAverage, filterPositiveValues } from '../util/MathUtil';
-
- import { DETECTOR_STATE_CHANGE, VAD_NOISY_DEVICE } from './DetectionEvents';
-
- /**
- * The average value VAD needs to be under over a period of time to be considered noise.
- * @type {number}
- */
- const VAD_NOISE_AVG_THRESHOLD = 0.2;
-
- /**
- * The average values that audio input need to be over to be considered loud.
- * @type {number}
- */
- const NOISY_AUDIO_LEVEL_THRESHOLD = 0.040;
-
- /**
- * The value that a VAD score needs to be under in order for processing to begin.
- * @type {number}
- */
- const VAD_SCORE_TRIGGER = 0.2;
-
- /**
- * The value that a VAD score needs to be under in order for processing to begin.
- * @type {number}
- */
- const AUDIO_LEVEL_SCORE_TRIGGER = 0.020;
-
- /**
- * Time span over which we calculate an average score used to determine if we trigger the event.
- * @type {number}
- */
- const PROCESS_TIME_FRAME_SPAN_MS = 1500;
-
- /**
- * Detect if provided VAD score and PCM data is considered noise.
- */
- export default class VADNoiseDetection extends EventEmitter {
- /**
- * Creates <tt>VADNoiseDetection</tt>
- *
- * @constructor
- */
- constructor() {
- super();
-
- /**
- * Flag which denotes the current state of the detection service i.e.if there is already a processing operation
- * ongoing.
- */
- this._processing = false;
-
- /**
- * Buffer that keeps the VAD scores for a period of time.
- */
- this._scoreArray = [];
-
- /**
- * Buffer that keeps audio level samples for a period of time.
- */
- this._audioLvlArray = [];
-
- /**
- * Current state of the service, if it's not active no processing will occur.
- */
- this._active = false;
-
- this._calculateNoisyScore = this._calculateNoisyScore.bind(this);
- }
-
- /**
- * Compute cumulative VAD score and PCM audio levels once the PROCESS_TIME_FRAME_SPAN_MS timeout has elapsed.
- * If the score is above the set threshold fire the event.
- * @returns {void}
- * @fires VAD_NOISY_DEVICE
- */
- _calculateNoisyScore() {
- const scoreAvg = calculateAverage(this._scoreArray);
- const audioLevelAvg = calculateAverage(this._audioLvlArray);
-
- if (scoreAvg < VAD_NOISE_AVG_THRESHOLD && audioLevelAvg > NOISY_AUDIO_LEVEL_THRESHOLD) {
- this.emit(VAD_NOISY_DEVICE);
-
- this._setActiveState(false);
- }
-
- // We reset the context in case a new process phase needs to be triggered.
- this.reset();
- }
-
- /**
- * Record the vad score and average volume in the appropriate buffers.
- *
- * @param {number} vadScore
- * @param {number} avgAudioLvl - average audio level of the PCM sample associated with the VAD score.s
- */
- _recordValues(vadScore, avgAudioLvl) {
- this._scoreArray.push(vadScore);
- this._audioLvlArray.push(avgAudioLvl);
- }
-
- /**
- * Set the active state of the detection service and notify any listeners.
- *
- * @param {boolean} active
- * @fires DETECTOR_STATE_CHANGE
- */
- _setActiveState(active) {
- this._active = active;
- this.emit(DETECTOR_STATE_CHANGE, this._active);
- }
-
- /**
- * Change the state according to the muted status of the tracked device.
- *
- * @param {boolean} isMuted - Is the device muted or not.
- */
- changeMuteState(isMuted) {
- // This service only needs to run when the microphone is not muted.
- this._setActiveState(!isMuted);
- this.reset();
- }
-
- /**
- * Check whether or not the service is active or not.
- *
- * @returns {boolean}
- */
- isActive() {
- return this._active;
- }
-
- /**
- * Reset the processing context, clear buffers, cancel the timeout trigger.
- *
- * @returns {void}
- */
- reset() {
- this._processing = false;
- this._scoreArray = [];
- this._audioLvlArray = [];
- clearTimeout(this._processTimeout);
- }
-
- /**
- * Listens for {@link TrackVADEmitter} events and processes them.
- *
- * @param {Object} vadScore -VAD score emitted by {@link TrackVADEmitter}
- * @param {Date} vadScore.timestamp - Exact time at which processed PCM sample was generated.
- * @param {number} vadScore.score - VAD score on a scale from 0 to 1 (i.e. 0.7)
- * @param {Float32Array} vadScore.pcmData - Raw PCM Data associated with the VAD score.
- * @param {string} vadScore.deviceId - Device id of the associated track.
- * @listens VAD_SCORE_PUBLISHED
- */
- processVADScore(vadScore) {
- if (!this._active) {
- return;
- }
-
- // There is a processing phase on going, add score to buffer array.
- if (this._processing) {
- // Filter and calculate sample average so we don't have to process one large array at a time.
- const posAudioLevels = filterPositiveValues(vadScore.pcmData);
-
- this._recordValues(vadScore.score, calculateAverage(posAudioLevels));
-
- return;
- }
-
- // If the VAD score for the sample is low and audio level has a high enough level we can start listening for
- // noise
- if (vadScore.score < VAD_SCORE_TRIGGER) {
- const posAudioLevels = filterPositiveValues(vadScore.pcmData);
- const avgAudioLvl = calculateAverage(posAudioLevels);
-
- if (avgAudioLvl > AUDIO_LEVEL_SCORE_TRIGGER) {
- this._processing = true;
- this._recordValues(vadScore.score, avgAudioLvl);
-
- // Once the preset timeout executes the final score will be calculated.
- this._processTimeout = setTimeout(this._calculateNoisyScore, PROCESS_TIME_FRAME_SPAN_MS);
- }
- }
- }
- }
|