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

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


概要

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

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


WebAudioAPIの基本概念

WebAudioAPIでは、USBカメラやスピーカーといった入出力先や、音の加工をするフィルターなどを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




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

コメント

このブログの人気の投稿

投稿時間:2021-06-20 02:06:12 RSSフィード2021-06-20 02:00 分まとめ(3871件)

投稿時間:2021-04-30 23:37:32 RSSフィード2021-04-30 23:00 分まとめ(42件)

投稿時間:2023-02-05 02:09:04 RSSフィード2023-02-05 02:00 分まとめ(9件)