This guide will provide instructions and code samples to help you get started with integrating Krisp into your SIP.js application.

Although this guide assumes that you are building on top of SIP.js SimpleUser implementation, it will still be helpful if you’re integrating in a SIP.js full api implementation, as in fact SimpleUser is based on the full api as well.

Integration

To integrate Krisp with SIP.js we should override the MediaStreamFactory used to provide audio stream in SIP.js. The easiest way to do that is to create a custom MediaStreamFactory which will initialize Krisp, get the cleaned audio stream and return it instead of the default microphone stream.

The code for our custom MediaStreamFactory will look something like this:

function krispMediaStreamFactory(): MediaStreamFactory {
  return async (constraints: MediaStreamConstraints): Promise<MediaStream> => {
    // if no audio and no video, return a media stream without tracks
    if (!constraints.audio && !constraints.video) {
      return Promise.resolve(new MediaStream());
    }
   
    if (navigator.mediaDevices === undefined) {
      return Promise.reject(new Error("Media devices not available in insecure contexts."));
    }

    let filteredStream: MediaStream = await prepareKrisp();
    let videoStream: MediaStream = new MediaStream();
    if(constraints.video){
      videoStream = await navigator.mediaDevices.getUserMedia.call(navigator.mediaDevices, {video: true})
    }
    return new MediaStream([...videoStream.getTracks(), ...filteredStream.getTracks()]);
  };
}

Here the prepareKrisp() function is creating a new AudioContext, initializing KrispSDK, taking a new microphone stream, initializing media stream source and destination, creating our audio filter, and connecting everything together. At the end it returns our cleaned stream from destination.

The example code for preparing Krisp will look like this (for more details refer to our documentation).

let krispSDK, // to store our sdk
    audioContext, // to store new AudioCntext
    source, // to store MediaStreamSource
    microphoneStream, // to store microphone stream
    destination, // to store MediaStreamDestination
    filterNode; // to store our filter
async function prepareKrisp() {
  audioContext = new AudioContext();

  krispSDK = new KrispSDK({
      params: {
          logProcessStats: false,
          useSharedArrayBuffer: false,
          debugLogs: false,
          models: {
              model8: '/dist/models/model_8.kw',
              model16: '/dist/models/model_16.kw',
              model32: '/dist/models/model_32.kw',
          },
      },
      callbacks: {
        errorCallback: () => {},
      },
  });

  krispSDK.init();

  const audioSettings = {
      audio: {
        echoCancellation: true,
        noiseSuppression: false,
        autoGainControl: false,
      },
  };

  microphoneStream = await navigator.mediaDevices.getUserMedia(audioSettings);

  if (!microphoneStream) {
      throw new Error('no available microphone');
  }

  source = audioContext.createMediaStreamSource(microphoneStream);
  destination = audioContext.createMediaStreamDestination();

  filterNode = await krispSDK.createNoiseFilter(
      audioContext,
      function onReady() {
          console.log("Krisp filter node is ready");
          toggleKrisp.disabled = false;
      }
  );

  source.connect(filterNode).connect(destination);

  return destination.stream;
}

"/**
 * Example of handling Krisp sdk toggle
 */
toggleKrisp.addEventListener('click', () => {
  if (filterNode.isEnabled()) {
      filterNode.disable();
      toggleKrisp.innerText = 'Toggle Krisp ✘';
  } else {
      filterNode.enable();
      toggleKrisp.innerText = 'Toggle Krisp ✓';
  }
});"

:grey-exclamation:Please note, that by default Krisp filter is not enabled.

When we have our custom MediaStreamFactory, the only thing remaining is to use it!

To do that we are going to import defaultSessionDescriptionHandlerFactory from SIP.js as we are going to pass our custom MediaStreamFactory as an argument to defaultSessionDescriptionHandlerFactory, and then pass the result in the userAgentOptions of SimpleUserOptions as a sessionDescriptionHandlerFactory value. This may sound complicated but here is all you have to do:

import {
  MediaStreamFactory,
  defaultSessionDescriptionHandlerFactory,
} from "sip.js/lib/platform/web"
const options: SimpleUserOptions = {
    aor,
    media: {
      constraints: {
        audio: true,
        video: true,
      },
      local: {
        video: videoLocalElement,
      },
      remote: {
        video: videoRemoteElement,
      },
    },
    userAgentOptions: {
      sessionDescriptionHandlerFactory: defaultSessionDescriptionHandlerFactory(krispMediaStreamFactory()),
    },
  };

All done, now SIP.js will use our MediaStreamFactory and you will get crystal-clear audio powered by Krisp JS SDK.

For the next steps of integration (toggling filter, disposing SDK and more) check out our documentation.