前回作ったのはフィルターのロジック部分ですが今回はこれをコントロールするクラスを作ります。遮断周波数やクオリティファクターを変えると音色も変わるのでこれを自由に変更できるようにしたいというのがここでの狙いです。
IIRローパスフィルタの作成(1)
IIRローパスフィルタの作成(2)
実装内容
クラス名はLowPassFilterModuleとしました。以下のような要件が必要と考えています。
- SoundReadableを実装した音源クラスを保持する
- 自身もSoundReadableを実装する
- 遮断周波数をエンベロープとして保持する
- クオリティファクターをエンベロープとして保持する
エンベロープは以前作ったDoubleMapクラスを使います。このクラスを使うと設定値を時間の変化と共に変えていくことができます。
実装
では実装をしていきます。
public class LowPassFilterModule implements SoundReadable{ IIRLowPassFilter filter; SoundReadable source; DoubleMap cutoff; DoubleMap quality;
コンストラクタ
エンベロープの最初の値を元にIIRローパスフィルターを作成します。今のところは音源はモノラルのみとします。
public LowPassFilterModule(SoundReadable source, DoubleMap cutoff, DoubleMap quality){ this.filter = new IIRLowPassFilter(cutoff.getInitial(), quality.getInitial()); this.source = source; if(source.getChannel() != 1){ throw new IllegalArgumentException("monoral only"); } this.cutoff = cutoff; this.quality = quality; }
SoundReadableに対する実装
SoundReadableインタフェースで要求される実装をします。
@Override public long length() { return source.length(); } @Override public int getChannel() { return source.getChannel(); }
遮断周波数とクオリティファクターの値をエンベロープから取得しフィルターを更新します。フィルター側では現在の値と更新する値が同じ場合には計算処理はしないように作られています。そのあとで音源から読み出したものをフィルタリングして戻り値として返します。
@Override public double read() { double nextCutoff = cutoff .next(); double nextQuality = quality.next(); filter.set(nextCutoff, nextQuality); return filter.process(source.read()); }
検査
実装内容が正しく実装されているか検査します。以下のような音を作ることにします。
- 440Hzの矩形波を5秒間3回鳴らす
- 最初のものはフィルターなし
- 2番目は遮断周波数が5秒間で500Hzから10000Hzまで変化、クオリティファクターはデフォルト値のまま変化なし
- 3番目は遮断変数は2000Hzのまま変化なし、クオリティファクターは5秒間で0.2から3まで変化する
実装します。
まず5秒間の矩形波を作るメソッドを作っておきます。これを3回呼び出します。
public static OscillatorReader getOscillatorReader() { DoubleMap freq = new DoubleMap(440); DoubleMap env = new DoubleMap(1); env.putSecondValue(4.5, 1); env.putSecondValue(5, 0); return new OscillatorReader(new SquareOscillator(), freq, env, 5); }
次にメインの処理です。まずタイムラインを作ります。
TimeLine tl = new TimeLine();
最初は矩形波そのままを書き出します。
tl.addReadable(0, getOscillatorReader());
つぎに遮断周波数を初期値500、5秒後の値が10000になるようにしてフィルタリングします。
DoubleMap cutoff1 = new DoubleMap(500); cutoff1.putSecondValue(5, 10000); DoubleMap quality1 = new DoubleMap(IIRLowPassFilter.DEFAULT_QUALITY); LowPassFilterModule lpf1 = new LowPassFilterModule(getOscillatorReader(), cutoff1, quality1); tl.addReadable(5, lpf1);
最後にクオリティファクターを0.2から3に変化させるように設定してフィルタリングします。
DoubleMap cutoff2 = new DoubleMap(2000); DoubleMap quality2 = new DoubleMap(0.2); quality2.putSecondValue(5, 3); LowPassFilterModule lpf2 = new LowPassFilterModule(getOscillatorReader(), cutoff2, quality2); tl.addReadable(10, lpf2);
ファイルに書き出します。
WavFileWriter.create(tl, new File("wav/filter.wav"));
以下のようになりました。
ソースの細かい点はGithubにあげていますので確認してください。