以前FM音源でストリングスを作りました。すこし色々変調をかけたので高い倍音が増えすぎてしまっていましたがローパスフィルターでこれを抑えたいと思います。
楽器の一部としてフィルターを使う
楽器の演奏された音全体に一律にフィルターをかけると面白くないので一つ一つの音に対して個別のフィルターを使うようにします。
楽器の演奏音を一つ返す処理でこれを実装します。具体的には楽器クラスで一つの音を返すメソッドの中に足していきます。メソッドはこのようなインタフェースになっていました。
@Override public SoundReadable play(double note, double duration, double velocity) {
引数はMIDIと共通のノート番号と音の長さの秒数、音の速さです。これを元に演奏音を作っていきます。
Played played = new Played(); double freq = Temperament.getFreq(note);
以下オシレーターを作り演奏音に足していきます。前回と変わらないので割愛します。以下のように最終的にはキャリアーが演奏音クラスに追加されます。
played.addReadable(0, osc3); played.addReadable(0, osc1);
前回は二つのキャリアを演奏音クラスに足したあとこれを返しましたがこの先フィルターによる変調をかけていきます。遮断周波数を最初は1000Hzにしてその後5000Hzまで上げていきます。つまり音は時間が立つほど明るい音になります。この時の時間ですが音の長さの10%に設定しました。割合で時間が変わるようにしたのは早いフレーズの時にもフィルターの遮断周波数の変化が完了するようにするためです。
DoubleMap envCutoff = new DoubleMap(1000); envCutoff.putSecondValue(duration * 0.1, 5000);
クオリティファクターはデフォルトのままでフィルターを作ります。
DoubleMap quality = new DoubleMap(IIRLowPassFilter.DEFAULT_QUALITY); LowPassFilterModule lpf = new LowPassFilterModule(played, envCutoff, quality);
フィルターのクオリティファクターに変調をかけます。これによってトレモロのような効果を出します。音の速さによってこのトレモロの周期を買えるようにしています。具体的には変数velocityが0の場合は3Hz、1つまり最大の場合には4Hzになるようにせっていしました。このように周期が変わると合奏時にも別々の楽器のように聞こえる狙いがあります。
Modulator mod = new Modulator(new SineOscillator(), new DoubleMap(3 + velocity), new DoubleMap(1), 0.2); lpf.setQualityModulator(mod);
このフィルターを最終的には演奏音として返しています。
return lpf;
試し演奏
二つのパートで演奏させました。一つ目はゆっくりのフレーズで二つ目のパートは早めのフレーズにしています。
TimeLine tl = new TimeLine(); Instrumental inst = new FMStrings(); Played pl1 = new Played(); Played pl2 = new Played(); int i = 0; double duration = 1.5; pl1.addReadable(i++ * duration, inst.play(78, duration, 0.9)); pl1.addReadable(i++ * duration, inst.play(76, duration, 0.7)); pl1.addReadable(i++ * duration, inst.play(74, duration, 0.6)); pl1.addReadable(i++ * duration, inst.play(73, duration, 0.5)); pl1.addReadable(i++ * duration, inst.play(71, duration, 0.9)); pl1.addReadable(i++ * duration, inst.play(69, duration, 0.7)); pl1.addReadable(i++ * duration, inst.play(71, duration, 0.6)); pl1.addReadable(i++ * duration, inst.play(73, duration, 0.5)); pl1.addReadable(i++ * duration, inst.play(74, duration, 0.9)); pl1.addReadable(i++ * duration, inst.play(73, duration, 0.7)); pl1.addReadable(i++ * duration, inst.play(71, duration, 0.6)); pl1.addReadable(i++ * duration, inst.play(69, duration, 0.5)); pl1.addReadable(i++ * duration, inst.play(67, duration, 0.9)); pl1.addReadable(i++ * duration, inst.play(66, duration, 0.7)); pl1.addReadable(i++ * duration, inst.play(67, duration, 0.6)); pl1.addReadable(i++ * duration, inst.play(64, duration, 0.5)); i = 16; duration = 0.75; pl2.addReadable(i++ * duration, inst.play(66, duration, 0.9)); pl2.addReadable(i++ * duration, inst.play(62, duration, 0.7)); pl2.addReadable(i++ * duration, inst.play(64, duration, 0.6)); pl2.addReadable(i++ * duration, inst.play(73, duration, 0.5)); pl2.addReadable(i++ * duration, inst.play(74, duration, 0.9)); pl2.addReadable(i++ * duration, inst.play(78, duration, 0.7)); pl2.addReadable(i++ * duration, inst.play(81, duration, 0.6)); pl2.addReadable(i++ * duration, inst.play(69, duration, 0.5)); pl2.addReadable(i++ * duration, inst.play(71, duration, 0.9)); pl2.addReadable(i++ * duration, inst.play(67, duration, 0.7)); pl2.addReadable(i++ * duration, inst.play(69, duration, 0.6)); pl2.addReadable(i++ * duration, inst.play(66, duration, 0.5)); pl2.addReadable(i++ * duration, inst.play(62, duration, 0.9)); pl2.addReadable(i++ * duration, inst.play(74, duration, 0.7)); pl2.addReadable(i++ * duration, inst.play(74, duration, 0.6)); pl2.addReadable(i++ * duration, inst.play(73, duration, 0.5)); DoubleMap cutoff1 = new DoubleMap(2000); DoubleMap cutoff2 = new DoubleMap(2000); tl.addReadable(0, new Panner(new LowPassFilterModule(pl1, cutoff1, new DoubleMap(IIRLowPassFilter.DEFAULT_QUALITY)), new DoubleMap(1))); tl.addReadable(0, new Panner(new LowPassFilterModule(pl2, cutoff2, new DoubleMap(IIRLowPassFilter.DEFAULT_QUALITY)), new DoubleMap(0))); WavFileWriter.create(tl, new File("wav/fmstrings.wav"));
次回はパッヘルベルのカノンをこの音色を使って作りたいと思います。