Why my sketch consume so much memory?

edited November 2013 in Questions about Code

the sketch takes microphone input or song and display spectrum of frequency with time. each frame it generates new object containing PShape. the object rotate itself until it complete the loop and get removed. I use PShape because drawing in immediate mode I get 10 fps but this method I get constant 60 fps. I noticed this method barely use CPU resource but consume so much memory.

  1. the object create and remove at constant rate but why memory consumption is not constant but varying between 2GB-3GB?.

  2. Is using PShape a good method to draw this?

  3. how to optimize the code so that it eat less memory?

sketch01

import ddf.minim.analysis.FFT;
import ddf.minim.*;

ArrayList<Line> lines;
Minim minim;
AudioInput in;
//AudioPlayer in;
FFT         fft;

float THRES = 3;

void setup() {
  size(600, 600, P3D);
  smooth(16);
  frameRate(60);
  lines = new ArrayList<Line>();

  minim = new Minim(this);
  // Microphone input 
  in = minim.getLineIn();

  // Local file
  //in = minim.loadFile("mySong.mp3");
  //in.play();

  //
  fft = new FFT( in.bufferSize(), in.sampleRate() );
  println("buffer size = " + in.bufferSize());
  println("sample rate = " + in.sampleRate());
}

void draw() {
  background(0);

  fft.forward( in.mix );
  Line line = new Line();
  lines.add(line);

  for (int i=lines.size()-1; i>=0; i--) {
    Line l = lines.get(i);
    l.render();
    if (l.dead) lines.remove(i);
  }

  //draw frequency wave
  stroke(255);
  strokeWeight(1);
  noFill();
  pushMatrix();
  translate(width/2, height/2);
  beginShape();
  for (int i=0; i<260; i++) {
    float f = fft.getBand(i)*0.75;
    f = constrain(f,0,60);
    vertex(i-260, -f, i-260, -f);
    vertex(i-260, f, i-260, f);
  }
  endShape();
  popMatrix();

  frame.setTitle(int(frameRate) +  " fps");
}

class Line {

  PShape s;
  boolean dead = false;
  int radius = 260;
  float theta = 0;
  float angVel = 0.15;
  int startAng = 89;

  Line() {
    theta = startAng+1;
    s = createShape(POINTS);
    s.beginShape();
    for (int i=0; i<=radius; i++) {
      colorMode(HSB, 360, 100, 100, 100);
      //float data = abs( in.mix.get(i) );
      float data = fft.getBand(i+10);

      float hue = map(data, 0, THRES, 220, 200);
      float sat = map(data, 0, THRES, 100, 90);
      float br = map(data, 0, THRES, 0, 100);
      color c = color(hue, sat, br, 100);
      s.stroke(c);
      s.strokeWeight(1);
      s.vertex(0, radius - i);  // invert to draw from outside
      colorMode(RGB, 255);
    }
    s.endShape();
  }

  void render() {
    if (!dead) {
      pushMatrix();
      translate(width/2, height/2);
      rotate(radians(theta));
      shape(s);
      popMatrix();
      theta += angVel;
    }
    if (theta > 360+startAng+1) dead = true;
  }
}

Answers

  • edited January 2014 Answer ✓

    1. Because of PShape's memory leak. PShape objects don't get garbage collected, you basically can't delete them at runtime.

    2. Using PShape objects is generally much faster than drawing shapes in immediate mode as they are utilizing VBOs (Vertex Buffer Objects) under the hood. But their memory leak makes them kind of useless in situation where you have to remove a lot of them. Anyhow, ignoring the memory leak, rendering many small PShapes is still not the best way to go, have a look at point 3.

    3. Instead of using PShapes and drawing all frequency lines every frame, try to render only one line per frame to an PGraphics object and then, render the (rotated) PGraphics object on the screen. This should reduce the memory cost to a minimum and is actually much faster than rendering countless PShapes.

    Try the following code:

    import ddf.minim.analysis.FFT;
    import ddf.minim.*;
    
    Minim minim;
    //AudioInput in;
    AudioPlayer in;
    FFT         fft;
    PGraphics canvas;
    
    float THRES = 3;
    float theta = 0;
    int radius = 260;
    float angVel = 0.15;
    
    int halfWidth;
    int halfHeight;
    
    void setup() {
        size(600, 600, P2D);
        frameRate(60);
        smooth(4);
    
        halfWidth = width / 2;
        halfHeight = height / 2;
    
        minim = new Minim(this);
        // Microphone input 
        //in = minim.getLineIn();
    
        // Local file
        in = minim.loadFile("mySong.mp3");
        in.play();
    
        // Create frequency(?) canvas
        canvas = createGraphics(600, 600, P2D);
        canvas.beginDraw();
        canvas.smooth(2);
        canvas.background(0);
        canvas.endDraw();
    
        // Print info to console
        fft = new FFT( in.bufferSize(), in.sampleRate() );
        println("buffer size = " + in.bufferSize());
        println("sample rate = " + in.sampleRate());
    }
    
    void draw() {
      background(0);
    
      fft.forward( in.mix );
    
      // Draw frequency circle
      renderFreqCircle();
    
      // Draw frequency wave
      renderFreqWave();
    
      frame.setTitle(int(frameRate) +  " fps");
    }
    
    void renderFreqCircle() {
    
        // Bind, translate and rotate canvas
        canvas.beginDraw();
        canvas.pushMatrix();
        canvas.translate(halfWidth, halfHeight);
        canvas.rotate(-radians(theta));
    
        // Render a black line to "erase" previously drawn lines
        canvas.stroke(0);
        canvas.strokeWeight(1);
        canvas.line(0, radius, 0, 0);
    
        // Render line shape to canvas
        canvas.beginShape();
        for(int i = 0; i <= radius; i++) {
            colorMode(HSB, 360, 100, 100, 100);
            //float data = abs( in.mix.get(i) );
            float data = fft.getBand(i+10);
            float hue = map(data, 0, THRES, 220, 200);
            float sat = map(data, 0, THRES, 100, 90);
            float br = map(data, 0, THRES, 0, 100);
            color c = color(hue, sat, br, 100);
            canvas.stroke(c);
            canvas.strokeWeight(1);
            canvas.vertex(0, radius - i);  // invert to draw from outside
            colorMode(RGB, 255);
        }
    
        // Unbind canvas
        canvas.endShape();
        canvas.popMatrix();
        canvas.endDraw();
        theta += angVel;
    
        // Render canvas to screen
        pushMatrix();
        translate(halfWidth, halfHeight);
        rotate(HALF_PI + radians(theta));
        image(canvas, -halfWidth, -halfHeight);
        popMatrix();
    
    }
    
    void renderFreqWave() {
        stroke(255);
        strokeWeight(1);
        noFill();
        pushMatrix();
        translate(halfWidth, halfHeight);
        beginShape();
        for(int i = 0; i < radius; i++) {
            float f = fft.getBand(i) * 0.75;
            f = constrain(f, 0, 60);
            vertex(i - radius, -f, i - radius, -f);
            vertex(i - radius, f, i - radius, f);
        }
        endShape();
        popMatrix();
    }
    

    Btw.: Great sketch, looks brilliant!

  • Thank you for taking your time answering all of my thread! now my potato ram can handle the sketch perfectly.

  • Haha :D

    No problem!

Sign In or Register to comment.