[ Jpn | Eng ]

Main Menu



Recent

最近の記事

Search

サイト内検索:

Archive

Powered by
MTOS 5.2.2


g200kg > Web Audio API 解説 > 10.フィルターの使い方

Web Audio API 解説

2016/11/14

10.フィルターの使い方



Biquad Filterとは

Web Audio API では汎用的なフィルタとして「BiquadFilter」、日本語では「双2次フィルター」という形式のものが用意されています。これはオーディオ系の信号処理でも頻繁に使用されるものです。この BiquadFilter には Robert Bristow-Johnson と言う人が書いた有名な設計手法の解説があって、通称「RBJ EQ Cookbook」などと呼ばれているのですが、Web Audio API の仕様でもフィルターの詳細についてはその「RBJ EQ Cookbook」を参考にしているという記述があります。

以前私もこの RBJ EQ Cookbook を凄く適当に翻訳してみた事がありますので、フィルターの中身に興味があれば参照してください。

VSTiの作り方-フィルタの料理法

フィルターの種類としては次の8種類が使用できます。

  • ローパスフィルター : 特定の周波数より下だけを通す
  • ハイパスフィルター : 特定の周波数より上だけを通す
  • バンドパスフィルター : 特定の周波数範囲だけを通す
  • ローシェルフフィルター : 特定の周波数より下を増幅または減衰させる
  • ハイシェルフフィルター : 特定の周波数より上を増幅または減衰させる
  • ピーキングフィルター : 特定の周波数を増幅する(減衰も可)
  • ノッチフィルター : 特定の周波数を減衰させる
  • オールパスフィルター : すべての周波数を通し位相特性だけを回転させる

これらは全て基本的には「RBJ EQ Cookbook」で解説されているものですが、ローパスフィルター、ハイパスフィルターについては 「RBJ EQ Cookbook」そのままというわけではありません。楽器用途として多少のチューニングが施されているようで、Q を上げてゆくとレゾナンスがきつめに掛かるようになっています 。


フィルターのパラメータ

フィルターノードのパラメータは次のとおりです。

パラメータ単位デフォルト値値の範囲内容
type文字列-"lowpass""lowpass"
"highpass"
"bandpass"
"lowshelf"
"highshelf"
"peaking"
"notch"
"allpass"
フィルターのタイプ
"lowpass":ローパス
"highpass":ハイパス
"bandpass":バンドパス
"lowshelf":ローシェルフ
"highshelf":ハイシェルフ
"peaking":ピーキング
"notch":ノッチ
"allpass":オールパス
frequencyAudioParamHz3500~ナイキスト周波数カットオフ周波数。範囲のナイキスト周波数はサンプルレートの1/2。このパラメータは a-rate です。
detuneAudioParamセント0-∞ ~ +∞デチューン。カットオフ周波数をずらします。単位の「セント」は半音の 1/100 で 1200 で 1 オクターブになります。このパラメータは a-rate です。
QAudioParamなし1-∞ ~ +∞フィルターのQ。このパラメータは a-rate です。
gainAudioParamdB0-∞ ~ +∞デシベルで表されるフィルターのゲインです。ローシェルフ/ハイシェルフ/ピーキングの場合のみ使用されます。このパラメータは a-rate です。
getFrequencyResponse
(freq,mag,phase)
関数---フィルター特性カーブを取得します。

フィルターを使用したサンプル

フィルターによってどんな効果が得られるのかはもう、耳と目で確かめるのが速いと思いますので、実際に音とスペクトラムを表示するサンプルを紹介します。

少し長くなっていますが、コードの後ろ半分はAnalyserノードとグラフ表示です。ScriptProcessor で作った乱数ノイズを元にしてフィルターを通した後、Analyserノードでスペクトラムを計測しています。構成は下の図のようになります。

フィルターのタイプ、周波数、Qなどを変化させるとスペクトラムはどうなって、どう聴こえるのかがわかるかと思います (※ Gain はローシェルフ、ハイシェルフ、ピーキングにだけ影響します)。


<!DOCTYPE html>
<html>
<head></head>
<body>
<h1>BiquadFilter Test</h1>
<div><div style="float:left"><table>
<tr><td>Type : </td><td><select id="type" onchange="Setup()"><option>LPF</option><option>HPF</option><option>BPF</optioin>
<option>LowShelf</option><option>HighShelf</option><option>Peaking</option><option>Notch</option><option>AllPass</option>
</select></td></tr>
<tr><td>Freq : </td><td><input type="range" min="100" max="20000" id="freq" size="10" onchange="Setup()" value="5000"/></td><td><div id="freqlabel">5000</td></tr>
<tr><td>Q : </td><td><input type="range" min="0" max="50" step="0.1" id="q" size="10" onchange="Setup()" value="5"/></td><td><div id="qlabel">5</td></tr>
<tr><td>Gain : </td><td><input type="range" min="-50" max="50" id="gain" size="10" onchange="Setup()" value="0"/></td><td><div id="gainlabel">0</td></tr>
</table>
<p><button onclick="Play()">Play</button></p>
</div>
<img src="images/filter.png" style="float:left"/>
</div>
<br/>
<p><canvas id="cvs" width=512 height=256></canvas></p>

<script test="text/javascript">

window.AudioContext = window.webkitAudioContext||window.AudioContext;
var audioctx = new AudioContext();

var bufsize = 1024;
var play = 0;
var data = new Float32Array(bufsize);

var scrproc = audioctx.createScriptProcessor(bufsize);
var filter = audioctx.createBiquadFilter();
var osc = null;
scrproc.onaudioprocess = Process;
scrproc.connect(filter);

filter.connect(audioctx.destination);
filter.type = 0;
filter.frequency.value = 5000;
filter.Q.value = 5;

function Setup() {
	filter.type = ["lowpass","highpass","bandpass","lowshelf","highshelf","peaking","notch","allpass"][document.getElementById("type").selectedIndex];
	filter.type = document.getElementById("type").selectedIndex;
	filter.frequency.value = document.getElementById("freqlabel").innerHTML = parseFloat(document.getElementById("freq").value);
	filter.Q.value = document.getElementById("qlabel").innerHTML = parseFloat(document.getElementById("q").value);
	filter.gain.value = document.getElementById("gainlabel").innerHTML = parseFloat(document.getElementById("gain").value);
}

function Play() {
	if(play) {
		play = 0;
	}
	else {
		if(osc == null){
			osc = audioctx.createOscillator();
			osc.start();
		}
		play = 1;
	}
}

function Process(ev) {
	var buf0 = ev.outputBuffer.getChannelData(0);
	var buf1 = ev.outputBuffer.getChannelData(1);
	for(var i = 0; i < bufsize; ++i) {
		buf0[i] = buf1[i] = (Math.random() - 0.5) * play;
	}
}
Setup();


/////////////////////////
var analyzer = audioctx.createAnalyser();
filter.connect(analyzer);
setInterval(DrawGraph,100);

function DrawGraph() {
	Analyze();
	var cv = document.getElementById("cvs");
	var ctx = cv.getContext("2d");
	ctx.fillStyle = "#000000";
	ctx.fillRect(0, 0, 512, 256);
	ctx.fillStyle = "#009900";
	for(var i = 0; i < 512; ++i) {
		var f = audioctx.sampleRate * i / 1024;
		y = 128 + (data[i] + 48.16) * 2.56;
		ctx.fillRect(i, 256 - y, 1, y);
	}
	ctx.fillStyle = "#ff8844";
	for(var d = -50; d < 50; d += 10) {
		var y = 128 - (d * 256 / 100) | 0;
		ctx.fillRect(20, y, 512, 1);
		ctx.fillText(d + "dB", 5, y);
	}
	ctx.fillRect(20, 128, 512, 1);
	for(var f = 2000; f < audioctx.sampleRate / 2; f += 2000) {
		var x = (f * 1024 / audioctx.sampleRate) | 0;
		ctx.fillRect(x, 0, 1, 245);
		ctx.fillText(f + "Hz", x - 10, 255);
	}
}
function Analyze() {
	analyzer.smoothingTimeConstant = 0.7;
	analyzer.fftSize = 1024;
	analyzer.getFloatFrequencyData(data);
}

</script>
</body>
</html>

テストページ:BiquadFilterテスト




g200kg