yoppa.org


芸大 – Interactive Music II 2014

第10回: SuperCollider応用 – ProcessingとSuperColliderの連携2

今回も前回に引き続き、SuperColliderとProcessingの連携について紹介していきます。様々なサンプルを参考にしながら、音:SuperCollider、アニメーション:Processing という組合せで、一体どのような表現が可能となるのか探求していきます。

スライド資料

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

サンプルプログラムのダウンロード

今日とりあげたサンプルプログラムは、以下からダウンロード可能です。

サンプルプログラム、ソースコード

SuperCollider(全サンプル共通)

// Geidai Interactive Music II 141211

// Simple Enveloped Sinwave
SynthDef("test_inst",{
    arg freq=440, length=1.0, amp=0.5;
    var env, out;
    env = Env.perc(0.01, length);
    out = SinOsc.ar([freq,freq*1.001])
    * EnvGen.kr(env, doneAction:2) * amp;
    Out.ar(0, out);
}).store;

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

// Saw wave
SynthDef("mySaw", {
    arg fadeTime = 10, n = 0, rq = 0.3, detune = 0.001, base = 20, ratio = 1.5, harm = 1.5, amp = 0.2, gate=0;
    var lfo, env, out;
    env = EnvGen.kr(Env.new([0,1], [fadeTime], 'sine'));
    lfo = SinOsc.ar(rrand(0.03, 0.05), 0, 100, 600);
    out = Saw.ar([base+detune.rand, base+detune.rand] * (ratio ** n)) * amp
    + Saw.ar([base*harm+detune.rand, base*harm+detune.rand] * (ratio ** n)) * amp;
    out = out * env;
    out = RLPF.ar(out, lfo * (1.5 ** n), rq).clip2 * 0.5;
    out = out * EnvGen.kr(Env.adsr(releaseTime:20), gate, doneAction: 2);
    Out.ar(0, out);
}).store;

// Effect
SynthDef("fx", {
    arg lpf=440, rq=0.5, amp=0.8;
    var in, out;
    in = In.ar(0, 2);
    12.do({ in = AllpassL.ar(in, 0.1, LFNoise2.kr([rrand(0.0, 0.1),rrand(0.0, 0.1)],0.01,0.06), 4.0) });
    out = CompanderD.ar(in) * amp;
    ReplaceOut.ar(0, out);
}).store;

Processingサンプル01: クリックで拡がる円

import supercollider.*;
import oscP5.*;

Synth synth;
int NUM = 100;
float[] radius = new float[NUM];
PVector[] pos = new PVector[NUM];
int counter = 0;

void setup () {
  size(800, 600);
  frameRate(60);
  noFill();
  stroke(31, 127, 255);
  strokeWeight(3);
  for (int i = 0; i < NUM; i++) {
    radius[i] = 0;
    pos[i] = new PVector(width*2, width*2);
  }
}

void draw() {
  background(0);
  for (int i = 0; i < NUM; i++) {
    ellipse(pos[i].x, pos[i].y, radius[i], radius[i]);
    radius[i] += 1;
    if(radius[i] > width*1.5){
      radius[i] = 0;
      pos[i].x = width*2;
      pos[i].y = width*2;
    }
  }
}

void mouseReleased() {
  synth = new Synth("test_inst");
  synth.set("amp", 0.5);
  synth.set("freq", map(mouseY, height, 0, 20, 8000));
  synth.create();
  
  int n = counter % NUM;
  radius[n] = 0;
  pos[n].x = mouseX;
  pos[n].y = mouseY;
  
  counter++;
}

Processingサンプル02: 壁にぶつかると音が鳴りバウンド

import supercollider.*;
import oscP5.*;

int NUM = 400; //配列の数
//位置のベクトルの配列
PVector[] location = new PVector[NUM];
//速度のベクトルの配列
PVector[] velocity = new PVector[NUM];
//塗りの色の配列
color[] col = new color[NUM];
//円の大きさ(直径)の配列
float[] diameter = new float[NUM];

void setup() {
  size(640, 480); //640x480pixelの画面を生成
  frameRate(60); //フレームレート
  noStroke();
  for (int i = 0; i < NUM; i++) { //配列の数だけ繰り返し
    //位置のベクトルの初期設定
    location[i] = new PVector(random(width), random(height));
    //速度のベクトルの初期設定
    velocity[i] = new PVector(random(-4, 4), random(-4, 4));
    //色の初期設定
    col[i] = color(random(255), random(255), random(255), 127);
    //大きさの初期設定
    diameter[i] = random(3, 40);
  }
}
void draw() {
  background(15); //背景を描画
  //配列の数だけ繰り返し
  for (int i = 0; i < NUM; i++) {
    fill(col[i]); //色を指定
    //指定した位置に円を描画
    ellipse(location[i].x, location[i].y, diameter[i], diameter[i]);
    //位置のベクトルに速度のベクトルを加算、次の位置になる
    location[i].add(velocity[i]);
    //もし画面の左端、または右端に到達したら
    if ((location[i].x > width) || (location[i].x < 0)) {
      velocity[i].x *= -1; //X方向のスピドを反転
      playSynth(location[i].x, location[i].y);
    }
    //もし画面の下端、または上端に到達したら
    if ((location[i].y > height) || (location[i].y < 0)) {
      velocity[i].y *= -1; //Y方向のスピードを反転
      playSynth(location[i].x, location[i].y);
    }
  }
}

void playSynth(float x, float y) {
  //新規に楽器を定義(まだ生成はされず)
  Synth synth = new Synth("test_inst");
  //引数を設定
  synth.set("amp", 0.1);
  synth.set("freq", map(x, height, 0, 20, 8000));
  //楽器を生成
  synth.create();
}

Processingサンプル03: 拡がる四角形とFM

import supercollider.*;
import oscP5.*;

int NUM = 100;
int count = 0;

PVector pos[] = new PVector[NUM];
float size[] = new float[NUM];
Synth fm[] = new Synth[NUM];

void setup() {
  size(640, 480);
  frameRate(60);
  for (int i = 0; i < NUM; i++) {
    pos[i] = new PVector(width*2, height*2);
    size[i] = 0;
  }
  rectMode(CENTER);
}

void draw() {
  background(0);
  stroke(255, 127);
  noFill();
  strokeWeight(3);
  for (int i = 0; i < NUM; i++) {
    rect(pos[i].x, pos[i].y, size[i], size[i]);
    if (size[i] > 0) {
      size[i] += 1;
      fm[i].set("index", size[i]);
    }
    if (size[i] > width/4) {
      size[i] = 1;
    }
  }
}

void mouseReleased() {
  int n = count % NUM;
  pos[n].x = mouseX;
  pos[n].y = mouseY;
  size[n] = 1;
  count++;

  fm[n] = new Synth("fm1");
  fm[n].set("amp", 0.2);
  fm[n].set("freq", map(mouseY, height, 0, 40, 800));
  fm[n].set("pan", map(mouseX, 0, width, -1.0, 1.0));
  fm[n].set("modPartial", map(mouseY, 0, height, 1.0, 20.0));
  fm[n].set("index", 0.0);
  fm[n].create();
}

Processingサンプル04: 移動する帯と持続音

import supercollider.*;
import oscP5.*;

int BAR_NUM = 100;
int count = 0;

float[] x = new float[BAR_NUM];
float[] xSpeed = new float[BAR_NUM];
float[] bWidth = new float[BAR_NUM];
color[] bColor = new color[BAR_NUM];

void setup() {
  size(640, 480);
  frameRate(30);
  colorMode(HSB, 360, 100, 100, 100);
  noStroke();

  for (int i=0; i width || x[i] < -bWidth[i]) {
      xSpeed[i] *= -1;
    }
  }
}

void mouseReleased() {
  int n = int(random(1, 12));
  x[count] = mouseX;
  bWidth[count] = n * 40;
  count++;

  Synth synth = new Synth("mySaw");
  synth.set("n", n);
  synth.set("gate", 1);
  synth.create();
}