芸大 – 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]); )