3D Spectrogram with Processing and Microphone

3D spectrogram created with Processing.  Realtime visualization of sound using a microphone or line in.  Modified code to allow for realtime recording using line in or microphone. Based on code by John Locke. Link

Video of 3D spectrogram:

Code:

//3D Spectrogram with Microphone Input
//Modified by kylejanzen 2011 - https://kylejanzen.wordpress.com
//Based on script wwritten by John Locke 2011 - http://gracefulspoon.com

//Output .DXF file at any time by pressing "r" on the keyboard

import processing.dxf.*;
import ddf.minim.analysis.*;
import ddf.minim.*;

FFT fftLin;
FFT fftLog;

Waveform audio3D;

Minim minim;
AudioInput microphone;

boolean record;

PFont font;

float camzoom;
float maxX = 0;float maxY = 0;float maxZ = 0;
float minX = 0;float minY = 0;float minZ = 0;

void setup()
{
  size(1250,750,P3D); //screen proportions
  noStroke();
  minim = new Minim(this);
  microphone = minim.getLineIn(Minim.STEREO, 4096); //repeat the song

  background(255);

  fftLog = new FFT(microphone.bufferSize(),microphone.sampleRate());
  fftLog.logAverages(1,2);  //adjust numbers to adjust spacing;
  float w = float (width/fftLog.avgSize());
  float x = w;
  float y = 0;
  float z = 50;
  float radius = 10;
  audio3D = new Waveform(x,y,z,radius);
}
void draw()
{
  background(0);
  directionalLight(126,126,126,sin(radians(frameCount)),cos(radians(frameCount)),1);
  ambientLight(102,102,102);

  if (frameCount>200)
  {
    for(int i = 0; i < fftLog.avgSize(); i++){
      float zoom = 1;
      float jitter = (fftLog.getAvg(i)*2);
      //println(jitter);
      PVector foc = new PVector(audio3D.x+jitter, audio3D.y+jitter, 0);
      PVector cam = new PVector(zoom, zoom, -zoom);
      camera(foc.x+cam.x+50,foc.y+cam.y+50,foc.z+cam.z,foc.x,foc.y,foc.z,0,0,1);
    }
  }
  //play the song
  fftLog.forward(microphone.mix);

  audio3D.update();
  audio3D.textdraw();

  if(record)
  {
    beginRaw(DXF, "output.dxf");
  }
  audio3D.plotTrace();

  if(record)
  {
    endRaw();
    record = false;
    println("It's Done Bitches! Find your DXF!");
  }
}
void stop()
{
  // always close Minim audio classes when you finish with them
  microphone.close();
  // always stop Minim before exiting
  minim.stop();
  super.stop();
}
class Waveform
{
  float x,y,z;
  float radius;

  PVector[] pts = new PVector[fftLog.avgSize()];

  PVector[] trace = new PVector[0];

  Waveform(float incomingX, float incomingY, float incomingZ, float incomingRadius)
  {
    x = incomingX;
    y = incomingY;
    z = incomingZ;
    radius = incomingRadius;
  }
  void update()
  {
    plot();
  }
  void plot()
  {
    for(int i = 0; i < fftLog.avgSize(); i++)
    {
      int w = int(width/fftLog.avgSize());

      x = i*w;
      y = frameCount*5;
      z = height/4-fftLog.getAvg(i)*4; //change multiplier to reduces height default '10'

      stroke(0);
      point(x, y, z);
      pts[i] = new PVector(x, y, z);
      //increase size of array trace by length+1
      trace = (PVector[]) expand(trace, trace.length+1);
      //always get the next to last
      trace[trace.length-1] = new PVector(pts[i].x, pts[i].y, pts[i].z);
    }
  }
  void textdraw()
  {
    for(int i =0; i<fftLog.avgSize(); i++){
      pushMatrix();
      translate(pts[i].x, pts[i].y, pts[i].z);
      rotateY(PI/2);
      rotateZ(PI/2);

      fill(255,200);
      text(round(fftLog.getAvg(i)*100),0,0,0);
      popMatrix();
    }
  }
  void plotTrace()
  {
    stroke(255,80);
    int inc = fftLog.avgSize();

    for(int i=1; i<trace.length-inc; i++)
    {
      if(i%inc != 0)
      {
        beginShape(TRIANGLE_STRIP);

        float value = (trace[i].z*100);
        float m = map(value, -500, 20000, 0, 255);
        fill(m*2, 125, -m*2, 140);
        vertex(trace[i].x, trace[i].y, trace[i].z);
        vertex(trace[i-1].x, trace[i-1].y, trace[i-1].z);
        vertex(trace[i+inc].x, trace[i+inc].y, trace[i+inc].z);
        vertex(trace[i-1+inc].x, trace[i-1+inc].y, trace[i-1+inc].z);
        endShape(CLOSE);
      }
    }
  }
}
void keyPressed()
{
  if (key == 'r') record = true;
}
Advertisements

4 responses to “3D Spectrogram with Processing and Microphone”

  1. Jack Colley says :

    Hey mate, love the code cheers.

    Getting this error

    RawDXF can only be used with beginRaw(), because it only supports lines and triangles.

    Exception in thread “Animation Thread” java.lang.RuntimeException: RawDXF can only be used with beginRaw(), because it only supports lines and triangles
    at processing.dxf.RawDXF.beginShape(RawDXF.java:328)
    at processing.opengl.PGraphicsOpenGL.rawPoints(PGraphicsOpenGL.java:2613)
    at processing.opengl.PGraphicsOpenGL.flush(PGraphicsOpenGL.java:2286)
    at processing.opengl.PGraphicsOpenGL.endShape(PGraphicsOpenGL.java:1995)
    at processing.core.PApplet.endShape(PApplet.java:11115)
    at Spectrograph$Waveform.plotTrace(Spectrograph.java:179)
    at Spectrograph.draw(Spectrograph.java:92)
    at processing.core.PApplet.handleDraw(PApplet.java:2266)
    at processing.opengl.PGL$PGLListener.display(PGL.java:3240)
    at jogamp.opengl.GLDrawableHelper.displayImpl(GLDrawableHelper.java:573)
    at jogamp.opengl.GLDrawableHelper.display(GLDrawableHelper.java:558)
    at jogamp.opengl.GLAutoDrawableBase$2.run(GLAutoDrawableBase.java:286)
    at jogamp.opengl.GLDrawableHelper.invokeGLImpl(GLDrawableHelper.java:1021)
    at jogamp.opengl.GLDrawableHelper.invokeGL(GLDrawableHelper.java:896)
    at com.jogamp.newt.opengl.GLWindow.display(GLWindow.java:545)
    at processing.opengl.PGL.requestDraw(PGL.java:1197)
    at processing.opengl.PGraphicsOpenGL.requestDraw(PGraphicsOpenGL.java:1550)
    at processing.core.PApplet.run(PApplet.java:2140)
    at java.lang.Thread.run(Thread.java:680)

    The highlighted text is

    beginShape(TRIANGLE_STRIP);

    float value = (trace[i].z*100);
    float m = map(value, -500, 20000, 0, 255);
    fill(m*2, 125, -m*2, 140);
    vertex(trace[i].x, trace[i].y, trace[i].z);
    vertex(trace[i-1].x, trace[i-1].y, trace[i-1].z);
    vertex(trace[i+inc].x, trace[i+inc].y, trace[i+inc].z);
    vertex(trace[i-1+inc].x, trace[i-1+inc].y, trace[i-1+inc].z);
    THIS PART ——– ” endShape(CLOSE); ”
    }
    }
    }
    }

    Any ideas? I am a noob and putting together a big visualisation sonofication project.

    Cheers mate

    • kylejanzen says :

      Hi,

      I looked into the situation. Seems to be a code consistency with the updated version of processing 2.0b (Beta). I downloaded Processing 1.5.1 (Stable) and copied the code from above. saved the file. and everything worked. once you want to save the .dxf file hit ‘r’ key and then it will save to the patches folders as ‘output.dxf’

      I am on a Mac and running Processing 1.5.1. so everything should work…

      Cheers,
      Kyle

  2. luke says :

    Really nice work, thanks a lot… but i can’t open output.dxf with any software, which one are you using ?

    • kylejanzen says :

      I use a program called Rhino 5 osx… or autpcad. Rhino osx is beta and dowloadable for free as a ‘testing’ but seems to work just fine… thouhg you have to renew it every month… but its free if you are on a mac.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: