音色を豊かにする方法はいろいろありますがビブラートのような音の変化を作りたいと思います。ビブラートとは音の高さが周期的に高くなったり低くなったりする変化をいいます。これをシンセサイザーではLFOという低周波オシレータによって音の高さを定期的に高くしたり低くしたりすることで実現しています。このオシレーターのことを周波数モジュレーターと呼びます。
改修方法の検討
以前オシレータから音情報を読み込むクラスOscillatorReaderを作りました。
オシレーターから音を読み込む時には周波数を引数で指定していましたがこの周波数に先ほどのモジュレーターで変調をかけます。コードを見た方が理解しやすいかもしれません。
まずフィールドに周波数モジュレーターを追加します。あとで使い勝手の良いようにSoundReadableインタフェースを指定して一番ゆるい型の制限にしています。
SoundReadable[] freqModulators;
コンストラクタを修正します。以前のコンストラクタもそのまま使えるようにthisでより引数の多いコンストラクタを呼び直すようにしています。
public OscillatorReader(Oscillatable oscillatable, DoubleMap freqMap, double seconds) { this(oscillatable, freqMap, new DoubleMap(1), seconds, new SoundReadable[0]); } public OscillatorReader(Oscillatable oscillatable, DoubleMap freqMap, DoubleMap envMap, double seconds){ this(oscillatable, freqMap, envMap, seconds, new SoundReadable[0]); } public OscillatorReader( Oscillatable oscillatable, DoubleMap freqMap, DoubleMap envMap, double seconds, SoundReadable[] freqModulators) { this.oscillatable = oscillatable; this.freqMap = freqMap; this.envMap = envMap; this.seconds = seconds; this.freqModulators = freqModulators; }
次にreadメソッドよ修正します。次にオシレーターに指定する周波数を計算するのにモジュレーターの影響を加えます。まず周波数のマップから通常読み出す周波数を取得します。
double omega_t = freqMap.next();
これにモジュレータから読み出した値を足していきます。
for(SoundReadable freqModulator : freqModulators){ omega_t += freqModulator.read(); }
最後にオシレーターから音情報を読み込みます。音量はエンべロープマップから取得して掛け合わせるのは前回と変わりません。
return oscillatable.read(omega_t, 1) * envMap.next();
ソース
以下修正したソースになります。次回これを使って音を聞いて見ます。
package mocha.sound; public class OscillatorReader implements SoundReadable { public static final int CHANNELS = 1; Oscillatable oscillatable; DoubleMap freqMap; DoubleMap envMap; double seconds; SoundReadable[] freqModulators; public OscillatorReader(Oscillatable oscillatable, DoubleMap freqMap, double seconds) { this(oscillatable, freqMap, new DoubleMap(1), seconds, new SoundReadable[0]); } public OscillatorReader(Oscillatable oscillatable, DoubleMap freqMap, DoubleMap envMap, double seconds) { this(oscillatable, freqMap, envMap, seconds, new SoundReadable[0]); } public OscillatorReader( Oscillatable oscillatable, DoubleMap freqMap, DoubleMap envMap, double seconds, SoundReadable[] freqModulators) { this.oscillatable = oscillatable; this.freqMap = freqMap; this.envMap = envMap; this.seconds = seconds; this.freqModulators = freqModulators; } @Override public long length() { return (long) (SAMPLE_RATE * CHANNELS * seconds); } @Override public int getChannel() { return CHANNELS; } @Override public double read() { double omega_t = freqMap.next(); for (SoundReadable freqModulator : freqModulators) { omega_t += freqModulator.read(); } return oscillatable.read(omega_t, 1) * envMap.next(); } }