18
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

WebRTCのMediaStreamをWebAudioAPIのFilterで加工する方法

Last updated at Posted at 2018-10-31

概要

最近、SkyWayを使ったビデオチャットシステムを構築していたのだが、ハウリング対策として高音を低減して欲しいと言われた。JavaScript歴2ヶ月なのに無茶言うな

探すのに少し苦労したので、勉強も兼ねた覚え書きとして残しておこうと思う。

WebAudioAPIの基本概念

WebAudioAPIでは、マイクやスピーカーといった入出力先や、音の加工をするフィルターなどをNodeとして表現する。これらを接続(connect)していくことで、渡された順に処理されていく仕組みとなっている。
filter.png
今回はSourceとして、getUserMediaで取得したMediaStreamを使用する。

結果

最終的に、以下のようになった

apply_filter.js
navigator.mediaDevices.getUserMedia({
  audio: true,
  video: true,
}).then(stream => {
  // AudioContextを生成
  const audioContext = new AudioContext();

  // BiquadFilterを生成
  const biquadFilter = audioContext.createBiquadFilter();
  biquadFilter.type = 'highshelf';     // ハイシェルフフィルター
  biquadFilter.frequency.value = 1000; // 周波数閾値
  biquadFilter.gain.value = -50;       // Gain(強さ)

  // getUserMediaで取得したMediaStreamからMediaStreamAudioSourceNodeを生成
  const mediaStreamSource = audioContext.createMediaStreamSource(stream);

  // MediaStreamAudioSourceNodeをBiquadFilterNodeに、BiquadFilterNodeをAudioContext.destinationに接続する
  // AudioContext.destinationはブラウザの出力先を示しており、ここに音を流せばブラウザから出力される
  mediaStreamSource.connect(biquadFilter);
  biquadFilter.connect(audioContext.destination);

  // 最後にHTMLの<video>や<audio>のsrcObjectにstreamを渡す
  const video = document.querySelector('video');
  video.srcObject = stream;

  // HTML側で属性の指定をした場合は不要
  video.addEventListener('loadedmetadata', e => {
    video.muted = true; // streamの加工前音声を無効化
    video.play();
  });
});

HTML側でautoplay属性とmuted属性を指定した場合は、onloadedmetadataのコールバックを指定する必要はない。

video.html
<video autoplay muted></video>

ポイントは、加工前のSourceStreamの音声を再生しないことである。HTML側でmuted属性を付与するのが一番手っ取り早い。
試しにmutedじゃなくてもgetAudioTracksしてstopすればいいのでは?と思ったがダメだった。

failed.js
stream.getAudioTracks()[0].stop(); // 失敗例

SkyWayと組み合わせる際は、受け取った相手側でstreamにフィルターをかけるのが楽。

filter_skyway.js
const peer = new Peer({ key: 'your API key' });
peer.on('call', call => {
  // 事前にgetUserMediaで取得したMediaStreamを渡す
  call.answer(localStream);
  // 相手のMediaStreamを受け取ると発火
  call.on('stream', stream => {
    ApplyFilter(stream); // ここでフィルターをかける
  });
});

おまけ

複数のフィルターをかけたい場合は、かけたい順にconnectしていけばOK。

multi_filter.js
audioSource.connect(filter1);
filter1.connect(filter2);
filter2.connect(filter3);
filter3.connect(audioContext.destination);

参考文献

Web Audio API の基礎
BiquadFilterNode



ツッコミどころ等あったらコメントで教えて頂ければ幸いです。

18
16
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?