본문 바로가기
데모

[processing] Storm (Sound Visualization)

by hansoo.labs 한수댁 2015. 3. 17.

vj 해본다고 만들었던 내용 중 하나만 잘랐습니다. 그래서 내용과 상관없이 소스가 많음..;

Source code:
Storm_SV
메인 루프 소스. : mp3를 플레이하고 소리정보를 전달함
import ddf.minim.spi.*;
import ddf.minim.signals.*;
import ddf.minim.*;
import ddf.minim.analysis.*;
import ddf.minim.ugens.*;
import ddf.minim.effects.*;

/**
 Minim audio Library
 http://code.compartmental.net/tools/minim/ 
 */
Minim minim;
AudioPlayer player;
BeatDetect beat;
BeatListener bl;
SoundInfo si;
Sprite visual;

char mode = '1';
float levelcompose = 200;

void setup()
{
  size(300, 300);
  colorMode(RGB, 255);
  background(0);

  minim = new Minim(this);
  player = minim.loadFile("sound.mp3");
  player.loop();
  beat = new BeatDetect(player.bufferSize(), player.sampleRate());
  beat.setSensitivity(300);
  si = new SoundInfo();
  bl = new BeatListener(beat, player);
  player.play();

  textFont(createFont("Arial", 12));
  visual = new StormSprite(si);
  isConstruct = true;
}

void draw()
{

  if ( beat.isKick() ) si.kick = 100;
  if ( beat.isSnare() ) si.snare = 100;
  if ( beat.isHat() ) si.hat = 100;

  si.level = player.mix.level() * levelcompose;
  si.kick = constrain(si.kick * 0.95, 0, 100);
  si.snare = constrain(si.snare * 0.95, 0, 100);
  si.hat = constrain(si.hat * 0.95, 0, 100);

  noStroke();
  renderMode();

  stroke(255, 100);

  for (int i = 0; i < player.bufferSize () - 1; i++)
  {
    line(i, 50  + player.left.get(i)*50, i+1, 50  + player.left.get(i+1)*50);
    line(i, 250 + player.right.get(i)*50, i+1, 250 + player.right.get(i+1)*50);
  }
}

boolean isConstruct = false;
int blendMode = 3;

void keyPressed() {


  if (key == ',') {
    levelcompose = (max(levelcompose - 1, 1));
    fill(255, 255, 255);
    text(levelcompose, 400, 280);
  }
  if (key == '.') {
    levelcompose = levelcompose + 1;
    fill(255, 255, 255);
    text(levelcompose, 400, 280);
  }

  if (mode != key && int(key) > 47 && int(key) < 50) {

    if (visual != null) visual.reset();
    mode = key;
    isConstruct = false;

    switch(mode) {
    case '0' :
      visual = new TestSprite(si);
      isConstruct = true;
      break;
    case '1' :
      visual = new StormSprite(si);
      isConstruct = true;
      break;
    }
  }

  switch(int(key)) {
  case 33 :
    blendMode = 1;
    println("blendMode fast blur");
    break;
  case 64 :
    blendMode = 2;
    println("blendMode lighten");
    break;
  case 35 :
    blendMode = 3;
    println("blendMode darken ");
    break;
  case 36 :
    blendMode = 4;
    println("blendMode cut");
    break;
  case 37:
    blendMode = 5;
    println("blendMode none");
    break;
  }
}

void renderMode()
{
  if (isConstruct) {
    if (blendMode == 4) {
      visual.render(true);
    } else {
      visual.render(false);
    }
  }

  switch(blendMode) {

  case 1 :
    fastBluring();
    break;
  case 2 :
    lighten();
    break;
  case 3 :
    darken();
    break;
  case 4 :
    //
    break;
  }
}

void stop()
{
  // always close Minim audio classes when you are done with them
  player.close();
  // always stop Minim before exiting
  minim.stop();
  // this closes the sketch
  super.stop();
}

void fastBluring() {

  int pixelTmpA, pixelTmpR, pixelTmpG, pixelTmpB;
  int upNum, downNum, i;
  loadPixels();
  for (int y=0; y<height; y++) {
    for (int x=1; x<width-1; x++) {
      i = x + y*width;
      pixelTmpB = (((pixels[i-1])&0x000000ff)) + (((pixels[i])&0x000000ff) << 1) + (((pixels[i+1])&0x000000ff));
      pixelTmpG = ((((pixels[i-1]&0x0000ff00)>>8)&0xFF)) + ((((pixels[i]&0x0000ff00)>>8)&0xFF) << 1) + ((((pixels[i+1]&0x0000ff00)>>8)&0xFF));
      pixelTmpR = ((((pixels[i-1]&0x00ff0000)>>16)&0xFF)) + ((((pixels[i]&0x00ff0000)>>16)&0xFF) << 1) + ((((pixels[i+1]&0x00ff0000)>>16)&0xFF));
      pixelTmpA = ((((pixels[i-1]&0xff000000)>>24)&0xFF)) + ((((pixels[i]&0xff000000)>>24)&0xFF) << 1) + ((((pixels[i+1]&0xff000000)>>24)&0xFF));
      pixelTmpR >>= 2;
      pixelTmpG >>= 2;
      pixelTmpB >>= 2;
      pixelTmpA >>= 2;
      pixels[i] = (((0xff & pixelTmpA)<<24) | ((0xff & pixelTmpR)<<16) | ((0xff & pixelTmpG)<<8) | (0xff & pixelTmpB));
    }
  }

  for (int x=0; x<width; x++) {
    for (int y=1; y<height-1; y++) {
      i = x + y*width;
      upNum = i - width;
      downNum = i + width;
      pixelTmpB = (((pixels[upNum])&0x000000ff)) + (((pixels[i])&0x000000ff) << 1) + (((pixels[downNum])&0x000000ff));
      pixelTmpG = ((((pixels[upNum]&0x0000ff00)>>8)&0xFF)) + ((((pixels[i]&0x0000ff00)>>8)&0xFF) << 1) + ((((pixels[downNum]&0x0000ff00)>>8)&0xFF));
      pixelTmpR = ((((pixels[upNum]&0x00ff0000)>>16)&0xFF)) + ((((pixels[i]&0x00ff0000)>>16)&0xFF) << 1) + ((((pixels[downNum]&0x00ff0000)>>16)&0xFF));
      pixelTmpA = ((((pixels[upNum]&0xff000000)>>24)&0xFF)) + ((((pixels[i]&0xff000000)>>24)&0xFF) << 1) + ((((pixels[downNum]&0xff000000)>>24)&0xFF));
      pixelTmpR >>= 2;
      pixelTmpG >>= 2;
      pixelTmpB >>= 2;
      pixelTmpA >>= 2;
      pixels[i] = (((0xff & pixelTmpA)<<24) | ((0xff & pixelTmpR)<<16) | ((0xff & pixelTmpG)<<8) | (0xff & pixelTmpB));
    }
  }
  updatePixels();
}

void lighten() {

  int startNum = width*0;
  int endNum = width*(height-0);
  int upNum, downNum, i;
  int pixelTmpR, pixelTmpG, pixelTmpB;
  loadPixels();
  for (i=startNum; i<endNum; i++) {

    pixelTmpB = ((pixels[i]&0x000000ff)) + 1;
    pixelTmpG = ((pixels[i]&0x0000ff00)>>8) + 1;
    pixelTmpR = ((pixels[i]&0x00ff0000)>>16) + 1;

    pixelTmpR = (int)min(pixelTmpR, 255);
    pixelTmpG = (int)min(pixelTmpG, 255);
    pixelTmpB = (int)min(pixelTmpB, 255);

    pixels[i] = (((0xff)<<24) | ((0xff & pixelTmpR)<<16) | ((0xff & pixelTmpG)<<8) | (0xff & pixelTmpB));
  }
  updatePixels();
}

void darken() {

  int startNum = width*0;
  int endNum = width*(height-0);
  int upNum, downNum, i;
  int pixelTmpR, pixelTmpG, pixelTmpB;
  loadPixels();
  for (i=startNum; i<endNum; i++) {

    pixelTmpB = ((pixels[i]&0x000000ff)) - 2;
    pixelTmpG = ((pixels[i]&0x0000ff00)>>8) - 2;
    pixelTmpR = ((pixels[i]&0x00ff0000)>>16) - 2;

    pixelTmpR = (int)max(pixelTmpR, 0);
    pixelTmpG = (int)max(pixelTmpG, 0);
    pixelTmpB = (int)max(pixelTmpB, 0);

    pixels[i] = (((0xff)<<24) | ((0xff & pixelTmpR)<<16) | ((0xff & pixelTmpG)<<8) | (0xff & pixelTmpB));
  }
  updatePixels();
}


BeatListener
소리정보에서 드럼의 foot, hat, snare 에 해당하는 타이밍을 찾아냄(minim 에서 제공하는 것 살짝수정)
class BeatListener implements AudioListener
{
  private BeatDetect beat;
  private AudioSource source;

  BeatListener(BeatDetect beat, AudioSource source)
  {
    this.source = source;
    this.source.addListener(this);
    this.beat = beat;
  }

  void samples(float[] samps)
  {
    beat.detect(source.mix);
  }

  void samples(float[] sampsL, float[] sampsR)
  {
    beat.detect(source.mix);
  }
}



SoundInfo
소리정보를 담는 클래스
class SoundInfo
{
  float kick = 0.0;
  float snare = 0.0;
  float hat = 0.0;
  float level = 0.0;

  SoundInfo() {
  }

  SoundInfo(float k, float s, float h, float l) {
    kick = k;
    snare = s;
    hat = h;
    level = l;
  }
}



Sprite
드로잉 부모 클래스
class Sprite {
  SoundInfo si;
  float level; //0~1
  float kick; //0~1
  float snare; //0~1
  float hat; //0~1

  float maxLevel = 80;

  Sprite(SoundInfo sinfo) {
    si = sinfo;
  }

  void render(boolean isCut)
  {    
    if (isCut) background(0);

    // value remaping 0~1
    level = map(si.level, 0, maxLevel, 0, 1);
    kick = map(si.kick, 0, 100, 0, 1);
    snare = map(si.snare, 0, 100, 0, 1);
    hat = map(si.hat, 0, 100, 0, 1);
  }

  void reset() {
  }
}


StormSprite
음악정보에 따라 드로잉 클래스 - 움직임에 대한 소스임
class StormSprite extends Sprite
{
  //draw value
  float r = 255;
  float g = 255;
  float b = 255;
  int value = 0;

  StormSprite(SoundInfo sinfo) {
    super(sinfo);
    refresh();
  }

  void refresh() {
    ellipseMode(CENTER);
    rectMode(CENTER);
  }

  void render(boolean isCut) {
    super.render(isCut);
    storm();
  }

  void none() {
  }


  /////////////////////
  //d. drawing storm
  float G = 1/1.618033989;
  float GA = 360 - 360 * G;
  float rgrowth = 1.005;
  float cur = 10.0;
  float rad, rot, vs, xx, yy;
  int unitNum = 800;
  float unitX[] = new float[800];
  float unitY[] = new float[800];
  float tX[] = new float[800];
  float tY[] = new float[800];
  float unitsize[] = new float[800];
  float tk = 0;

  void storm() {
    rad = level * 2 + (kick + hat + snare) / 2;
    //rot = 0;
    //vs =0;

    tk += (kick - tk) * 0.3;
    tk = max(tk, 0);

    for (int i=0; i 360)? vs : 0;
      xx = cos(rot * PI/180 + vs * PI/180) * rad + width / 2;
      yy = sin(rot * PI/180 + vs * PI/180) * rad + height/ 2;
      tX[i] = xx;
      tY[i] = yy;

      r = min(150, level * 256);
      g = min(255, (level * 200));
      b = min(255, (level * 128 + 100));

      fill(r, g, b, tk * 155 + 100);
      rect(xx, yy, unitsize[i], unitsize[i]);
    }

    //    fill(255);
    //    text("level > " + level, 50, 100);
    //    text("kick  > " + kick, 50, 120);
    //    text("hat   > " + hat, 50, 140);
    //    text("snare > " + snare, 50, 160);
  }
}


Test
음악정보를 눈으로 보기 위한 클래스
class TestSprite extends Sprite
{
  float eRadius;

  TestSprite(SoundInfo sinfo) {
    super(sinfo);
    textFont(createFont("Arial", 12));
    topLevel = 0;
    eRadius = 20;
    ellipseMode(CENTER);
  }

  float topLevel;


  void render(boolean isCut) {

    super.render(isCut);
    fill(255, 255, 255);
    textAlign(CENTER);
    textSize(si.kick);
    text("KICK", width/4, height/2);
    textSize(si.snare);
    text("SNARE", width/2, height/2);
    textSize(si.hat);
    text("HAT", 3*width/4, height/2);

    si.kick = constrain(si.kick * 0.95, 16, 32);
    si.snare = constrain(si.snare * 0.95, 16, 32);
    si.hat = constrain(si.hat * 0.95, 16, 32);

    eRadius = map(si.level, 0, maxLevel, 0, 100);
    fill(0, 250, 250);
    eRadius *= 0.9;
    ellipse(width/2, 200, eRadius, eRadius);

    fill(0);
    textAlign(LEFT);
    textSize(14);
    text("level > " + str(si.level), 50, 100);
    if (topLevel < si.level) topLevel = si.level;
    text("top-level > " + str(topLevel), 50, 140);
  }
}


[조작]
비주얼 모드(기본) : 1키
소리 반응 모드 : 0 키
소리 반응 크게 :  , 키
소리 반응 작게 :  . 키
블러링 모드(기본) : shift키 + 1
화이트 모드 : shift 키 + 2
블랙 모드 : shift 키 + 3
컷 모드 : shift 키 + 4


댓글0