ならば

音とかで遊んでいたログ

リング変調

二つの波形の掛け算で新しい波形を作り出すことをリング変調(Ring Modulation)という。リング変調という名前は、昔この変調を実現する電子回路の中にリング状の部分があったことに由来するらしい。
単純に波形がどちらもサイン波なら三角関数の積和公式

\sin \alpha \sin \beta = -\frac{1}{2}\{\cos (\alpha + \beta)-\cos (\alpha - \beta)\}

そのままで、入力波形の周波数成分の和と差の周波数を持つ波形が出力される。元々の周波数成分は消えてしまう。入力波形の周波数同士が簡単な整数比なら出力は倍音関係になる。
下のプログラムはサイン波同士のRMの例。一方の周波数を固定して、もう一方を動かす。音高が上昇し続ける音(和の周波数を持つ波形)と一度下がった後で上がっていく音(差の周波数を持つ波形)の二つが聴こえる。

SinOsc c => Gain g => dac;
SinOsc m => g;
220 => m.freq;
3 => g.op;  // .opパラメータが3:入力波形の乗算

for(0 => int i; i < 660; i++) {
    i => c.freq;
    <<<c.freq() + m.freq(), c.freq() - m.freq()>>>;
    10::ms => now;
}


入力がもっと複雑な波形の場合はそれぞれに含まれる周波数成分ごとに和と差がとられるので、入力波形には存在しない周波数成分を多く含んだ波形が出力される。そんなわけで、RMは金属音を作るのによく使われてきた*1。金属音を目指した結果できたプログラム。金属音??

Mandolin m => Gain g => dac;
TriOsc t => g;
3 => g.op;
[60, 62, 64, 65, 67, 69, 71, 72] @=> int scale[];  // 長音階

while(true) {
    Std.rand2f(300, 1000) => t.freq;
    for (int i; i < scale.cap(); i++) {
        scale[i] => Std.mtof => m.freq;
        1 => m.pluck;
        200::ms => now;
    }
}

Mandolinは楽器系ユニットジェネレータで、マンドリンの音をシミュレートする。.pluckは一部の弦楽器のユニットジェネレータに用意されている書き込み専用のfloat型パラメータ。これで弦をはじく*2。値は0.0から1.0までで、はじく強さを設定できる。RMとか金属音とは全然関係ないけど、Mandolinの使用例としてexamples/stk/mand-o-matic.ckに上のプログラムよりはるかに煌びやかで綺麗な音が鳴る作品がある。


それからトレモロのプログラムを書いているときには気付かなかったんだけど、これは実質的にはRMだから.opパラメータを活用するともっと簡潔に書ける。RMで入力の片方をLFOにすると出力される和と差の周波数は非常に近い値になるので、うなりが聞こえる。これがトレモロ効果の正体。

TriOsc s => ADSR amp => Gain g => dac;
SinOsc lfo => g;
amp.set(0::ms, 0::ms, .5, 0::ms);
3 => g.op;
4 => lfo.freq;

Math.exp(1) => float notes;  // 音の列
for (int i; i < 50; i++) {
    Math.floor(notes) + 60 => Std.mtof => s.freq;
    (notes - Math.floor(notes)) * 10 => notes;
    amp.keyOn();
    second => now;
}

*1:一般的に、倍音が多いと明るく煌びやかな音になって少ないと暗くこもった音になる

*2:英単語pluckには弦楽器をかき鳴らすという意味がある