Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

NoiseSuppressionEffect.ts 4.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. import { getBaseUrl } from '../../base/util/helpers';
  2. import logger from './logger';
  3. /**
  4. * Class Implementing the effect interface expected by a JitsiLocalTrack.
  5. * Effect applies rnnoise denoising on a audio JitsiLocalTrack.
  6. */
  7. export class NoiseSuppressionEffect {
  8. /**
  9. * Web audio context.
  10. */
  11. private _audioContext: AudioContext;
  12. /**
  13. * Source that will be attached to the track affected by the effect.
  14. */
  15. private _audioSource: MediaStreamAudioSourceNode;
  16. /**
  17. * Destination that will contain denoised audio from the audio worklet.
  18. */
  19. private _audioDestination: MediaStreamAudioDestinationNode;
  20. /**
  21. * `AudioWorkletProcessor` associated node.
  22. */
  23. private _noiseSuppressorNode: AudioWorkletNode;
  24. /**
  25. * Audio track extracted from the original MediaStream to which the effect is applied.
  26. */
  27. private _originalMediaTrack: MediaStreamTrack;
  28. /**
  29. * Noise suppressed audio track extracted from the media destination node.
  30. */
  31. private _outputMediaTrack: MediaStreamTrack;
  32. /**
  33. * Effect interface called by source JitsiLocalTrack.
  34. * Applies effect that uses a {@code NoiseSuppressor} service initialized with {@code RnnoiseProcessor}
  35. * for denoising.
  36. *
  37. * @param {MediaStream} audioStream - Audio stream which will be mixed with _mixAudio.
  38. * @returns {MediaStream} - MediaStream containing both audio tracks mixed together.
  39. */
  40. startEffect(audioStream: MediaStream): MediaStream {
  41. this._originalMediaTrack = audioStream.getAudioTracks()[0];
  42. this._audioContext = new AudioContext();
  43. this._audioSource = this._audioContext.createMediaStreamSource(audioStream);
  44. this._audioDestination = this._audioContext.createMediaStreamDestination();
  45. this._outputMediaTrack = this._audioDestination.stream.getAudioTracks()[0];
  46. const baseUrl = `${getBaseUrl()}libs/`;
  47. const workletUrl = `${baseUrl}noise-suppressor-worklet.min.js`;
  48. // Connect the audio processing graph MediaStream -> AudioWorkletNode -> MediaStreamAudioDestinationNode
  49. this._audioContext.audioWorklet.addModule(workletUrl)
  50. .then(() => {
  51. // After the resolution of module loading, an AudioWorkletNode can be constructed.
  52. this._noiseSuppressorNode = new AudioWorkletNode(this._audioContext, 'NoiseSuppressorWorklet');
  53. this._audioSource.connect(this._noiseSuppressorNode).connect(this._audioDestination);
  54. })
  55. .catch(error => {
  56. logger.error('Error while adding audio worklet module: ', error);
  57. });
  58. // Sync the effect track muted state with the original track state.
  59. this._outputMediaTrack.enabled = this._originalMediaTrack.enabled;
  60. // We enable the audio on the original track because mute/unmute action will only affect the audio destination
  61. // output track from this point on.
  62. this._originalMediaTrack.enabled = true;
  63. return this._audioDestination.stream;
  64. }
  65. /**
  66. * Checks if the JitsiLocalTrack supports this effect.
  67. *
  68. * @param {JitsiLocalTrack} sourceLocalTrack - Track to which the effect will be applied.
  69. * @returns {boolean} - Returns true if this effect can run on the specified track, false otherwise.
  70. */
  71. isEnabled(sourceLocalTrack: any): boolean {
  72. // JitsiLocalTracks needs to be an audio track.
  73. return sourceLocalTrack.isAudioTrack();
  74. }
  75. /**
  76. * Clean up resources acquired by noise suppressor and rnnoise processor.
  77. *
  78. * @returns {void}
  79. */
  80. stopEffect(): void {
  81. // Sync original track muted state with effect state before removing the effect.
  82. this._originalMediaTrack.enabled = this._outputMediaTrack.enabled;
  83. // Technically after this process the Audio Worklet along with it's resources should be garbage collected,
  84. // however on chrome there seems to be a problem as described here:
  85. // https://bugs.chromium.org/p/chromium/issues/detail?id=1298955
  86. this._noiseSuppressorNode?.port?.close();
  87. this._audioDestination?.disconnect();
  88. this._noiseSuppressorNode?.disconnect();
  89. this._audioSource?.disconnect();
  90. this._audioContext?.close();
  91. }
  92. }