# Triangled terrain audio visualizer

**URL:** <https://discourse.processing.org/t/triangled-terrain-audio-visualizer/44384>\
**Category:** Libraries\
**Created:** [May 5, 2024, 3:30am UTC](https://discourse.processing.org/t/triangled-terrain-audio-visualizer/44384 "2024-05-05T03:30:13Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![funnyrectangles](https://avatars.discourse-cdn.com/v4/letter/f/d78d45/32.png) [@funnyrectangles](https://discourse.processing.org/u/funnyrectangles)\
**Post date:** [May 5, 2024, 3:30am UTC](https://discourse.processing.org/t/triangled-terrain-audio-visualizer/44384/1 "2024-05-05T03:30:13Z")

</div>

Inspired by [Coding Challenge: 3D Terrain Generation with Perlin Noise](https://www.youtube.com/watch?v=IKB1hWWedMk) I would like to create an audio visualizer that display several successive audio frames as a terrain. I managed to create a basic working sketch but I can’t go further on achieving a proper display of the waveform. Yellow is the current stored wave frame and the terrain is the history of previously stored wave frames.

 ![image](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/3X/d/9/d9ccd7ad244efd6292a024d11a07c25a108fd21e.png)

I assume the problem here is that the terrain does not have enough resolution for displaying waveforms. Can someone point me some solutions or alternatives visualizations? Any other idea on how to display the “history” of the stored audio frames in 3D?

Here is my code:

```auto
import processing.sound.*;

SoundFile sample;
Waveform waveform;

int scl = 20;
int w = 1200;
int h = 900;
int cols = w / scl;
int rows = h / scl;
float time = 0;
float [][] terrain;
int audioCols, audioRows;

float waveHist[][] = new float[cols][rows];

public void setup() {
  size(600, 600, P3D);
  terrain = new float[cols][rows];

  sample = new SoundFile(this, "beat.aiff");
  sample.loop();

  waveform = new Waveform(this, cols);
  waveform.input(sample);

  for (int y=0; y<rows; y++) {
    for (int x=0; x<cols; x++) {
      waveHist[x][y] = 0;
    }
  }
}

public void draw() {
  waveform.analyze(); //read the audio data

  // fill the current row
  int counter = frameCount % rows; // stores waveforms
  for (int i = 0; i < cols; i++) {
    waveHist[i][counter] = waveform.data[i];
  }

  time -= 0.1; 
  float yoff = time;
  for (int y=0; y<rows; y++) {
    float xoff=0;
    for (int x=0; x<cols; x++) {
      //terrain[x][y] = map(noise(xoff, yoff), 0, 1, -100, 100);
      terrain[x][y] = map(waveHist[x][y],-1,1,-200,200);
      xoff += 0.1;
    }
    yoff += 0.1;
  }

  // Set background color, noFill and stroke style
  background(0);
  stroke(255, 255, 0);
  strokeWeight(2);
  noFill();

  //------------------------------------ 2d waveform
  beginShape();
  for (int i = 0; i < cols; i++) {
    // Draw current data of the waveform
    // Each sample in the data array is between -1 and +1
    vertex(
      map(i, 0, cols, 0, width),
      map(waveform.data[i], -1, 1, 0, height) -200
      );
  }
  endShape();

  stroke(255);
  //------------------------------ terrain
  translate(width/2, height/2);
  rotateX(PI/3);
  //rotateZ(-PI/3);
  translate(-w/2, -h/2);
  for (int y=0; y<rows-1; y++) {
    beginShape(TRIANGLE_STRIP);
    for (int x=0; x<cols; x++) {
      vertex(x*scl, y*scl, terrain[x][y]);
      vertex(x*scl, (y+1)*scl, terrain[x][y+1]);
    }
    endShape();
  }
}

```

The refresh rate is also pretty slow compared with the youtube video tutorial, so I assume I am doing something wrong and messed up…

Thanks a lot!!!

---

<div class="post-metadata">

**Author:** ![glv](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/glv/32/18785_2.png) [@glv](https://discourse.processing.org/u/glv)\
**Post date:** [May 5, 2024, 3:34pm UTC](https://discourse.processing.org/t/triangled-terrain-audio-visualizer/44384/2 "2024-05-05T15:34:19Z")

</div>

Hello @funnyrectangles ,

Consider scrolling the data to give the impression of the terrain moving

A simple example to get you thinking about it:

```auto
ArrayList<Float> data = new ArrayList<Float>();
float x, y;

void setup()
  {
  size(600, 300);
  background(200);
  }
  
void draw()
  {
  //background(255);
  x = frameCount%600;
  if (x == 0)
    background(200);
  
  float a = (x)*(TAU/300);    
  y = 20*sin(a) + 5*sin(7.5*a);
  data.add(y);
  if (data.size() > width)
    data.remove(0);
  
  strokeWeight(2);
  stroke(255, 0, 0);
  point(x, y+50);
  
  noStroke();
  rect(0, 200, width, 100); // Like background for bottom 1/3
  
  stroke(0, 255, 0);
   
  for(int xi = 0; xi < data.size(); xi++)
    {
    point(xi, data.get(xi)+150);  
    }

  stroke(0);    
  // The other direction  
  for(int xi = 0; xi < data.size(); xi++)
    {
    point(width-xi, data.get(xi)+250);  
    }        
  }

```

`:)`

---

<div class="post-metadata">

**Author:** ![mnse](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/mnse/32/16520_2.png) [@mnse](https://discourse.processing.org/u/mnse)\
**Post date:** [May 6, 2024, 12:57pm UTC](https://discourse.processing.org/t/triangled-terrain-audio-visualizer/44384/3 "2024-05-06T12:57:58Z")

</div>

Hi @funnyrectangles,

my first naïve approach as a starting point, based on your code, could look like this …

Hope that helps.

Cheers  
— mnse

```auto
import processing.sound.*;
import java.util.stream.*;

SoundFile sample;
Waveform waveform;

int scl = 5;
int w = 1200;
int h = 900;
int cols = w / scl;
int rows = h / scl;
float scaling = 150.;
ArrayList<ArrayList<Float>> terrain;

public void setup() {
  size(600, 600, P3D);
  sample = new SoundFile(this, "test.mp3");
  sample.loop();

  waveform = new Waveform(this, cols);
  waveform.input(sample);

  terrain=new ArrayList<>();
  for (int y=0; y<rows; y++) {
    terrain.add(Stream.generate(() -> 0.0f).limit(cols).collect(Collectors.toCollection(ArrayList::new)));
  }

  colorMode(HSB, scaling, 1, 1); // coloring based on absolute amplitude
  noStroke();  
}

public void draw() {
  background(0);

  waveform.analyze();
  
  //back to front
  terrain.add(0,IntStream.range(0, waveform.data.length).mapToObj(i -> Float.valueOf(waveform.data[i]*scaling)).collect(Collectors.toCollection(ArrayList::new)));
  terrain.remove(terrain.size()-1);

  //front to back
  //terrain.add(IntStream.range(0, waveform.data.length).mapToObj(i -> Float.valueOf(waveform.data[i])).collect(Collectors.toCollection(ArrayList::new)));
  //terrain.remove(0);

  translate(width/2, height/2);
  rotateX(PI/3);
  translate(-w/2, -h/2);
  // triangle strip surface 
  beginShape(TRIANGLE_STRIP);
  for (int y = 0; y < rows-1; y++) {
    if (y % 2 == 0) { // even rows, right to left
      for (int x = cols - 1; x >= 0; x--) {
        fill(abs(terrain.get(y+1).get(x)), 1, 1); // abs(amplitude) [0-scaling]
        vertex(x * scl, (y + 1) * scl, terrain.get(y+1).get(x));
        fill(abs(terrain.get(y).get(x)), 1, 1);
        vertex(x * scl, y * scl, terrain.get(y).get(x));
      }
    } else { // odd rows left to right 
      for (int x = 0; x < cols; x++) {
        fill(abs(terrain.get(y).get(x)), 1, 1);
        vertex(x * scl, y * scl, terrain.get(y).get(x));
        fill(abs(terrain.get(y+1).get(x)), 1, 1);
        vertex(x * scl, (y + 1) * scl, terrain.get(y+1).get(x));
      }
    }
  }
  endShape();
}

```

![demo](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/3X/6/1/615c09e914782fab0394299d6d47e83c097f53ee.avif)

---

<div class="post-metadata">

**Author:** ![glv](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/glv/32/18785_2.png) [@glv](https://discourse.processing.org/u/glv)\
**Post date:** [May 7, 2024, 1:27am UTC](https://discourse.processing.org/t/triangled-terrain-audio-visualizer/44384/4 "2024-05-07T01:27:21Z")

</div>

> [@funnyrectangles](#):
>
> Can someone point me some solutions or alternatives visualizations?

Consider plotting the frequency domain and use FFT.

Slow melody (Light of the Seven by Ramin Djawadi):

 ![image](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/3X/e/d/edc559b35f84482926f5caa2f84d7304699606ff.png)

Fast beat (beat.aiff):

 ![image](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/3X/5/6/560604f811cdd46b278e8b0feafba327fa126e7a.png)

I used the lower frequencies.

Reference:  
_[Spectrogram - Wikipedia](https://en.wikipedia.org/wiki/Spectrogram)_

`:)`
