yoppa.org


immediate bitwave

芸大 – Interactive Music II 2014

第4回: SuperCollider入門 4 – 楽器を定義、変調合成(RM, AM, FM)

今回は前半部分でSuperColliderの「楽器」を定義する方法について紹介します。これまでは、楽器を定義することなくUGenを組合せた音の信号を関数 {…} にまとめ、そこに「.play」のメッセージを送ることで直接音を生成していました。しかし、実際にはSuperColliderの内部ではどうなっていたかというと、SuperColliderの音響合成サーバーにテンポラリーに楽器が生成されそれを鳴らしていました。音を止めると楽器は消滅していました。今回はまず明示的に「楽器」を定義する「Synthdef」という関数について紹介します。この方法で楽器が名前付きで定義され、再利用が可能となります。また、外部のプログラムと連携することも可能となります。

後半は、SuperColliderを使用した実践的な音響合成の手法について紹介していきます。まず今回は、ある信号のパラメーターをもう一つの信号で変化(変調)させる「変調合成」という手法について紹介します。変調合成は、信号の何を変調するのかによって、AM(RM)、FMという種類に分けられます。それぞれの変調合成の音色の違いなどに注意しながら、SuperColliderでの実現のやり方を学んでいきましょう。

スライド資料

授業内で使用するスライド資料は、下記から参照してください

サンプルプログラム

今回使用したSuperColliderのプログラムです。

// インタラクティブミュージックII
// 2014.10.23

// 楽器を定義する - SynthDef

// {}.playによる出力
{SinOsc.ar.dup}.play;

// Post Windowの表示
Synth("temp__146" : 1000)

// SyntDefに変換
SynthDef("sine", {Out.ar(0, SinOsc.ar)}).play; //left
SynthDef("sine", {Out.ar(1, SinOsc.ar)}).play; //right
SynthDef("sine", {Out.ar(0, SinOsc.ar.dup)}).play; //l+r

//SynthDefをサーバーに追加
SynthDef.new("test-SinOsc", {
    Out.ar(0, SinOsc.ar(440, 0, 0.2).dup)
}).add;

//Synthを演奏
Synth("test-SinOsc");

//SynthDefの定義(引数あり)
SynthDef.new("test-SinOsc", {
    arg freq = 440, amp = 0.2;
    Out.ar(0, SinOsc.ar(freq, 0, amp).dup)
}).add;

//Synthを演奏
a = Synth("test-SinOsc"); //440Hz
b = Synth("test-SinOsc", [freq:660]); //660Hz
c = Synth("test-SinOsc", [freq:880, amp:0.5]); //880Hz, amp:0.5

a.set("freq", 330); //440Hz -> 330Hz
b.set("freq", 220, "amp", 0.3); //660Hz -> 220Hz, amp: 0.3

//終了
a.free; b.free; c.free;

// -----------------------------------------------------------
// 変調合成(RM, AM, FM)

// RM変調基本
SynthDef.new("test-rm",{
    var car, mod, rm;
    mod = SinOsc.ar(880, 0, 1.0);
    car = SinOsc.ar([440,442], 0, 0.5);
    rm = car * mod;
    Out.ar(0, rm);
}).play;

// RM変調1 - 徐々にModulatorの周波数をアップ
SynthDef.new("test-rm",{
    var car, mod, rm;
    mod = SinOsc.ar(XLine.kr(1, 4000, 30), 0, 1.0);
    car = SinOsc.ar(440, 0, 0.5).dup;
    rm = car * mod;
    Out.ar(0, rm);
}).play;

// RM変調2 - マウスでModulatorの周波数を変更
SynthDef.new("test-rm",{
    var car, mod, rm;
    mod = SinOsc.ar(MouseX.kr(1, 4000, 1), 0, 1.0);
    car = SinOsc.ar(440, 0, 0.5).dup;
    rm = car * mod;
    Out.ar(0, rm);
}).play;

// RM変調3 - 2つのModulator
SynthDef.new("test-rm",{
    var car, mod1, mod2, rm;
    car = SinOsc.ar(440, 0, 0.5);
    mod1 = SinOsc.ar(MouseX.kr(1, 4000, 1), 0, 1.0);
    mod2 = SinOsc.ar(1/[8.0, 7.0]);
    rm = car * (mod1 * mod2);
    Out.ar(0, rm);
}).play;

// AM変調 1
SynthDef.new("test-am",{
    var car, mod, am;
    mod = SinOsc.ar(XLine.kr(1, 4000, 30), 0, 0.5, 0.5);
    car = SinOsc.ar(440, 0, 0.5).dup;
    am = car * mod;
    Out.ar(0, am);
}).play;

// AM変調 2
SynthDef.new("test-am",{
    var car, mod, am;
    mod = SinOsc.ar(MouseX.kr(1, 4000, 1), 0, 0.5, 0.5);
    car = SinOsc.ar(440, 0, 0.5).dup;
    am = car * mod;
    Out.ar(0, am);
}).play;

// AM変調 3
SynthDef.new("test-am",{
    var car, mod1, mod2, am;
    car = SinOsc.ar(440, 0, 0.5);
    mod1 = SinOsc.ar(MouseX.kr(1, 4000, 1), 0, 0.5, 0.5);
    mod2 = SinOsc.ar(1/[8.0, 7.0]);
    am = car * (mod1 * mod2);
    Out.ar(0, am);
}).play;

// AM + LFNoise
SynthDef.new("rand-am",{
    arg freq = 440, amp = 0.5, modFreq = 400;
    var car, mod1, mod2, am;
    car = SinOsc.ar(freq, 0, amp);
    mod1 = SinOsc.ar(LFNoise1.kr(5.reciprocal, modFreq), pi.rand, 0.5, 0.5);
    mod2 = SinOsc.ar(LFNoise1.kr([8,7].reciprocal).abs);
    am = car * (mod1 * mod2);
    Out.ar(0, am);
}).add;

Synth("rand-am", ["freq", 110, "amp", 0.2, "modFreq", 1200]);
Synth("rand-am", ["freq", 220, "amp", 0.2, "modFreq", 200]);
Synth("rand-am", ["freq", 440, "amp", "modFreq", 200]);
Synth("rand-am", ["freq", 880, "amp", 0.2, "modFreq", 100]);
Synth("rand-am", ["freq", 1780, "amp", 0.05, "modFreq", 20]);

//FM基本 1
SynthDef.new("test-fm",{
    arg cfreq = 440, mfreq = 111, index = 200;
    var car, mod;
    mod = SinOsc.ar(mfreq, 0, index);
    car = SinOsc.ar(cfreq + mod, 0, 0.5).dup;
    Out.ar(0, car);
}).play;

//FM基本2 徐々にMod周波数アップ
SynthDef.new("test-fm",{
    arg cfreq = 440, mfreq = 111, index = 200;
    var car, mod;
    mod = SinOsc.ar(XLine.kr(1, 1000, 30), 0, index);
    car = SinOsc.ar(cfreq + mod, 0, 0.5).dup;
    Out.ar(0, car);
}).play;

//FM基本2 徐々にindexアップ
SynthDef.new("test-fm",{
    arg cfreq = 440, mfreq = 111, index = 200;
    var car, mod;
    mod = SinOsc.ar(111, 0, XLine.kr(1, 10000, 30));
    car = SinOsc.ar(cfreq + mod, 0, 0.5).dup;
    Out.ar(0, car);
}).play;

//FMマウスで操作
SynthDef.new("test-fm",{
    var car, mod;
    mod = SinOsc.ar(MouseX.kr(1, 1000, 1), 0,
        MouseY.kr(1, 10000, 1));
    car = SinOsc.ar(440 + mod, 0, 0.5).dup;
    Out.ar(0, car);
}).play;

// FM応用 1
(
SynthDef("fm1", { arg freq = 440, detune = 2, carPartial = 1, modPartial = 1, index = 3, mul = 0.2;
    var mod, car;
    mod = SinOsc.ar(
        [freq, freq+detune] * modPartial, 0,
        freq * index * LFNoise1.kr(10.reciprocal).abs
    );
    car = SinOsc.ar((freq * carPartial) + mod, 0, mul);
    Out.ar(0, car);
}).add;
)

(
Synth("fm1", ["modPartial", 2.4]);
Synth("fm1", ["modPartial", 2.401]);
Synth("fm1", ["freq", 110, "modPartial", 3.1213, "index", 10]);
Synth("fm1", ["freq", 220, "modPartial", 10.99, "index", 20]);
)

// FM応用2 - FM + エフェクト
(
SynthDef("fm2", { arg bus = 0, freq = 440, detune = 2, carPartial = 1, modPartial = 1, index = 3, mul = 0.1;
    var mod, car;
    mod = SinOsc.ar(
        [freq, freq+detune] * modPartial,
        0,
        freq * index * LFNoise1.kr(10.reciprocal).abs
    );
    car = SinOsc.ar((freq * carPartial) + mod, 0, mul);
    Out.ar(bus, car);
}).add;

SynthDef("preDelay", { arg inbus = 2;
    ReplaceOut.ar(
        4,
        DelayN.ar(In.ar(inbus, 1), 0.048, 0.048)
    )
}).add;

SynthDef("combs", {
    ReplaceOut.ar(
        6,
        Mix.arFill(7, { CombL.ar(In.ar(4, 1), 0.1, LFNoise1.kr(Rand(0, 0.1), 0.04, 0.05), 15) })
    )
}).add;

SynthDef("allpass", { arg gain = 0.2;
    var source;
    source = In.ar(6, 1);
    4.do({source = AllpassN.ar(source, 0.050, [Rand(0, 0.05), Rand(0, 0.05)], 1) });
    ReplaceOut.ar(8, source * gain)
}).add;

SynthDef("theMixer", { arg gain = 1;
    ReplaceOut.ar(
        0,
        Mix.ar([In.ar(2, 1), In.ar(8, 2)]) * gain
    )
}).add;
)

(
Synth("fm2", ["bus", 2, "freq", 440, "modPartial", 2.4]);
Synth("fm2", ["bus", 2, "freq", 448, "modPartial", 2.401]);
Synth.tail(s, "preDelay");
Synth.tail(s, "combs");
Synth.tail(s, "allpass");
Synth.tail(s, "theMixer", ["gain", 0.64]);
)