概要
最近、SkyWayを使ったビデオチャットシステムを構築していたのだが、ハウリング対策として高音を低減して欲しいと言われた。JavaScript歴2ヶ月なのに無茶言うな
探すのに少し苦労したので、勉強も兼ねた覚え書きとして残しておこうと思う。
WebAudioAPIの基本概念
WebAudioAPIでは、マイクやスピーカーといった入出力先や、音の加工をするフィルターなどをNodeとして表現する。これらを接続(connect)していくことで、渡された順に処理されていく仕組みとなっている。
今回はSourceとして、getUserMediaで取得したMediaStreamを使用する。
結果
最終的に、以下のようになった
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 autoplay muted></video>
ポイントは、加工前のSourceStreamの音声を再生しないことである。HTML側でmuted属性を付与するのが一番手っ取り早い。
試しにmutedじゃなくてもgetAudioTracksしてstopすればいいのでは?と思ったがダメだった。
stream.getAudioTracks()[0].stop(); // 失敗例
SkyWayと組み合わせる際は、受け取った相手側でstreamにフィルターをかけるのが楽。
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。
audioSource.connect(filter1);
filter1.connect(filter2);
filter2.connect(filter3);
filter3.connect(audioContext.destination);
参考文献
Web Audio API の基礎
BiquadFilterNode
ツッコミどころ等あったらコメントで教えて頂ければ幸いです。