Skip to content
This repository was archived by the owner on Oct 25, 2024. It is now read-only.

Commit f7fcf72

Browse files
committed
Enable audio denoise using rnnoise-wasm
1 parent e9e7b60 commit f7fcf72

File tree

7 files changed

+2474
-18
lines changed

7 files changed

+2474
-18
lines changed

scripts/Gruntfile.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ module.exports = function(grunt) {
55
const sdkOutput = 'dist/sdk/owt.js';
66

77
var srcFiles = [
8-
'src/sdk/base/**',
8+
'src/sdk/base/*.js',
99
'src/sdk/p2p/**',
1010
'src/sdk/conference/**'
1111
];
@@ -175,6 +175,8 @@ window.L = L;\n\
175175
copy:{
176176
dist:{
177177
files:[
178+
{expand: true,cwd:'src/sdk/base/',src:['*.wasm'],dest:'dist/samples/p2p/js',flatten:false},
179+
{expand: true,cwd:'src/sdk/base/',src:['*.wasm'],dest:'dist/sdk/',flatten:false},
178180
{expand: true,cwd:'src/samples/p2p/',src:['**'],dest:'dist/samples/p2p/',flatten:false},
179181
{expand: true,cwd:'src/samples/conference/',src:['**'],dest:'dist/samples/conference/',flatten:false},
180182
{expand: true,cwd:'src/samples/conference/',src:['initcert.js'],dest:'dist/samples/conference/',flatten:false,mode:true},

src/samples/p2p/js/peercall.js

+44-17
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ const getTargetId = function() {
4848
return $('#remote-uid').val();
4949
};
5050

51+
function denoiseCheckboxChanged() {
52+
document.getElementById("denoise-message").innerHTML=": Click 'Stop Camera' and 'share camera' if video sharing is already in progress."
53+
}
54+
5155
$(document).ready(function() {
5256
$('#set-remote-uid').click(function() {
5357
p2p.allowedRemoteIds = [getTargetId()];
@@ -89,6 +93,8 @@ $(document).ready(function() {
8993
localStream = undefined;
9094
});
9195

96+
let denoiseCheckbox = document.getElementById('apply-denoise-checkbox');
97+
9298
$('#target-video-publish').click(function() {
9399
$('#target-video-unpublish').prop('disabled', false);
94100
$('#target-video-publish').prop('disabled', true);
@@ -104,23 +110,44 @@ $(document).ready(function() {
104110
const videoConstraintsForCamera = new Owt.Base
105111
.VideoTrackConstraints(Owt.Base.VideoSourceInfo.CAMERA);
106112
let mediaStream;
107-
Owt.Base.MediaStreamFactory.createMediaStream(new Owt.Base
108-
.StreamConstraints(audioConstraintsForMic,
109-
videoConstraintsForCamera)).then((stream) => {
110-
mediaStream = stream;
111-
localStream = new Owt.Base.LocalStream(mediaStream, new Owt
112-
.Base.StreamSourceInfo('mic', 'camera'));
113-
$('#local').children('video').get(0).srcObject = localStream
114-
.mediaStream;
115-
p2p.publish(getTargetId(), localStream).then(
116-
(publication) => {
117-
publicationForCamera = publication;
118-
}, (error) => {
119-
console.log('Failed to share video.');
120-
});
121-
}, (err) => {
122-
console.error('Failed to create MediaStream, ' + err);
123-
});
113+
if(denoiseCheckbox.checked){
114+
Owt.Base.MediaStreamFactory.createMediaStreamDenoised(new Owt.Base
115+
.StreamConstraints(audioConstraintsForMic,
116+
videoConstraintsForCamera)).then((stream) => {
117+
mediaStream = stream;
118+
localStream = new Owt.Base.LocalStream(mediaStream, new Owt
119+
.Base.StreamSourceInfo('mic', 'camera'));
120+
$('#local').children('video').get(0).srcObject = localStream
121+
.mediaStream;
122+
p2p.publish(getTargetId(), localStream).then(
123+
(publication) => {
124+
publicationForCamera = publication;
125+
}, (error) => {
126+
console.log('Failed to share video.');
127+
});
128+
}, (err) => {
129+
console.error('Failed to create MediaStream, ' + err);
130+
});
131+
}
132+
else {
133+
Owt.Base.MediaStreamFactory.createMediaStream(new Owt.Base
134+
.StreamConstraints(audioConstraintsForMic,
135+
videoConstraintsForCamera)).then((stream) => {
136+
mediaStream = stream;
137+
localStream = new Owt.Base.LocalStream(mediaStream, new Owt
138+
.Base.StreamSourceInfo('mic', 'camera'));
139+
$('#local').children('video').get(0).srcObject = localStream
140+
.mediaStream;
141+
p2p.publish(getTargetId(), localStream).then(
142+
(publication) => {
143+
publicationForCamera = publication;
144+
}, (error) => {
145+
console.log('Failed to share video.');
146+
});
147+
}, (err) => {
148+
console.error('Failed to create MediaStream, ' + err);
149+
});
150+
}
124151
}
125152
});
126153

src/samples/p2p/peercall.html

+6
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,12 @@ <h2>P2P Sample</h2>
8585
<input id="remote-uid" type="text" />
8686
<button id="set-remote-uid">Set Remote ID</button>
8787
</p>
88+
<p>
89+
<input id="apply-denoise-checkbox" type="checkbox" onclick="denoiseCheckboxChanged()">
90+
<label for="apply-denoise-checkbox"> Enable noise reduction</label>
91+
<mark><label id="denoise-message"> </label></mark>
92+
</p>
93+
8894
<p>
8995
<button id="target-video-publish">Share Camera</button>
9096
<button id="target-video-unpublish">Stop Camera Sharing</button>

src/sdk/base/denoise.js

+84
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
// Copyright (C) <2020> Intel Corporation
2+
//
3+
// SPDX-License-Identifier: Apache-2.0
4+
5+
/* global window, AudioContext, Float32Array */
6+
7+
'use strict';
8+
// ///////////////////////////////////////////////////////////////////////////////
9+
// Handles the WebAssembly kernel for denoising raw audio files in F32Arrayformat
10+
// ///////////////////////////////////////////////////////////////////////////////
11+
12+
import {Module, wasmMemory} from './rnn_denoise.js';
13+
14+
// cwrap wasm API's used here make wasm calls from JS simpler.
15+
const wasmRnndenoiseRawmem = Module.cwrap('rnnDenoise_rawmem',
16+
'number', ['number', 'number', 'number', 'number']);
17+
18+
const sampleRate = 44100; // Audio Sample rate can be set and controlled here.
19+
const numChannels = 1; // Current channel support limited to 1. Stereo is ToDo.
20+
21+
/**
22+
* @function wasmDenoiseStream
23+
* @desc Apply denoising into raw audio data in F32Array format using
24+
* a WebAssembly port of RNNoise denoising algoritm.
25+
* @return {Float32Array} fProcessdArr with denoised audio data
26+
* @param {Float32Array} f32buffer
27+
*/
28+
export function wasmDenoiseStream(f32buffer) {
29+
// Create and Initialize Wasm memory with input audio data.
30+
const wasmMemPtr = Module._malloc(f32buffer.length * 4 );
31+
const wasmMemArr = new Float32Array(wasmMemory.buffer,
32+
wasmMemPtr, f32buffer.length);
33+
wasmMemArr.set(f32buffer);
34+
35+
// Call Wasm denoising kernel
36+
const wasmRetPtr = wasmRnndenoiseRawmem(wasmMemPtr,
37+
sampleRate, numChannels, f32buffer.length);
38+
39+
// Create JS Array from Wasm memory with results
40+
const fProcessedArr = new Float32Array(wasmMemory.buffer,
41+
wasmRetPtr, f32buffer.length);
42+
43+
return fProcessedArr;
44+
}
45+
46+
// ////////////////////////////////////////////////////////////////////
47+
// Creates a WebAudio Based filter for applying audio denoising.
48+
// ///////////////////////////////////////////////////////////////////
49+
50+
// WebAuddio context
51+
window.AudioContext = window.AudioContext || window.webkitAudioContext;
52+
export const audioContext = new AudioContext(); // new AudioContext({sampleRate: 48000});
53+
54+
55+
// Audio buffer size:
56+
// Accepts powers of 2 between 0 and 16384.
57+
// Too low causes audio glitches due to buffer underruns
58+
// Too high could increase latency.
59+
// Set to 0 and WebAudio API will autopick a value.
60+
const bufferSize = 4096;
61+
62+
export const audioDenoise = (function() {
63+
const numberOfInputChannels = 1;
64+
const numberOfOutputChannels = 1;
65+
let recorder;
66+
67+
if (audioContext.createScriptProcessor) {
68+
recorder = audioContext.createScriptProcessor(bufferSize,
69+
numberOfInputChannels, numberOfOutputChannels);
70+
} else {
71+
recorder = audioContext.createJavaScriptNode(bufferSize,
72+
numberOfInputChannels, numberOfOutputChannels);
73+
}
74+
75+
recorder.onaudioprocess = function(e) {
76+
const input = e.inputBuffer.getChannelData(0);
77+
78+
const wasmOutput = wasmDenoiseStream(input);
79+
80+
e.outputBuffer.copyToChannel(wasmOutput, 0, 0);
81+
};
82+
return recorder;
83+
})();
84+

src/sdk/base/mediastream-factory.js

+39
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
'use strict';
88
import * as utils from './utils.js';
99
import * as MediaFormatModule from './mediaformat.js';
10+
import * as denoise from './denoise.js';
1011

1112
/**
1213
* @class AudioTrackConstraints
@@ -226,4 +227,42 @@ export class MediaStreamFactory {
226227
return navigator.mediaDevices.getUserMedia(mediaConstraints);
227228
}
228229
}
230+
/**
231+
* @function createMediaStreamDenoised
232+
* @static
233+
* @desc Create a MediaStream with given constraints. Applies Rnnoise based
234+
* noise cancellation on all audio streams. If you want to create a
235+
* MediaStream for screen cast, please make sure both audio and video's source
236+
* are "screen-cast".
237+
* @memberof Owt.Base.MediaStreamFactory
238+
* @return {Promise<MediaStream, Error>} Return a promise that is resolved
239+
* when stream is successfully created, or rejected if one of the following
240+
* error happened:
241+
* - One or more parameters cannot be satisfied.
242+
* - Specified device is busy.
243+
* - Cannot obtain necessary permission or operation is canceled by user.
244+
* - Video source is screen cast, while audio source is not.
245+
* - Audio source is screen cast, while video source is disabled.
246+
* @param {Owt.Base.StreamConstraints} constraints
247+
*/
248+
static createMediaStreamDenoised(constraints) {
249+
return this.createMediaStream(constraints).then(
250+
(stream) => {
251+
const audioTracks = stream.getAudioTracks();
252+
const videoTracks = stream.getVideoTracks();
253+
const peer = denoise.audioContext.createMediaStreamDestination();
254+
255+
audioTracks.forEach(function(track) {
256+
const microphone =
257+
denoise.audioContext.createMediaStreamSource(stream);
258+
microphone.connect(denoise.audioDenoise);
259+
denoise.audioDenoise.connect(peer);
260+
});
261+
videoTracks.forEach(function(track) {
262+
peer.stream.addTrack(track);
263+
});
264+
return peer.stream;
265+
}
266+
);
267+
}
229268
}

0 commit comments

Comments
 (0)