このプログラムは、音声信号を解析し、周波数領域での音声のパワーをグラフィカルに表現することができます。具体的には、左右のオーディオ入力を取り込み、FFT(高速フーリエ変換)アルゴリズムを使用して各バンドの周波数成分を計算し、グラフィカルに表示しています。グラフは、周波数帯域ごとのバーで構成されており、各バーの高さは周波数成分の強さに対応しています。音声のパワーが高い領域は、バーが高くなるため、グラフの形状は音声のパワースペクトルを反映しています。

import processing.sound.*;
color dCyan = #005678;
color lCyan = #0099aa;

AudioIn audioInL, audioInR;
FFT fftL, fftR;
int samples = 2048;
int numBars = 16;
float[][] barHeights = new float[2][numBars];
float sampleRate = 48000.0;

void setup() {
  size(720, 300, P2D);
  noStroke();
  background(dCyan);
  frameRate(90);
  audioInL = new AudioIn(this, 0);
  audioInR = new AudioIn(this, 1);
  audioInL.start();
  audioInR.start();

  fftL = new FFT(this, samples);
  fftR = new FFT(this, samples);
  fftL.input(audioInL);
  fftR.input(audioInR);
}

void draw() {
  background(dCyan);
  fftL.analyze();
  fftR.analyze();
  updateBarHeights(fftL, 0);
  updateBarHeights(fftR, 1);
  for (int i = 0; i < numBars; i++) {
    drawBar(i, barHeights[0][i], width / 4, true);
    drawBar(i, barHeights[1][i], 3 * width / 4, false);
  }
}


float[] generateOctaveBands(int numBands) {
  float minFreq = 31.25;
  float maxFreq = sampleRate / 2.0; // ナイキスト周波数を最大周波数に設定
  float[] bands = new float[numBands + 1];
  float ratio = pow(maxFreq / minFreq, 1.0 / numBands);
  for (int i = 0; i <= numBands; i++) {
    bands[i] = minFreq * pow(ratio, i);
  }
  return bands;
}
void updateBarHeights(FFT fft, int channel) {
  float[] spectrum = fft.analyze();
  int spectrumSize = spectrum.length;
  float freqPerBin = sampleRate / 2.0 / spectrumSize;
  float[] octaveBands = generateOctaveBands(numBars);
  int numOctaveBands = octaveBands.length - 1;

  for (int i = 0; i < numOctaveBands; i++) {
    float lowFreq = octaveBands[i];
    float highFreq = octaveBands[i + 1];
    int lowIndex = round(lowFreq / freqPerBin);
    int highIndex = min(round(highFreq / freqPerBin), spectrumSize - 1);
    int numBins = highIndex - lowIndex;

    float sum = 0;
    for (int j = lowIndex; j < highIndex; j++) {
      sum += spectrum[j];
    }
    float average = sum / numBins;
    float db = max(20 * log(average) / log(10), -100);
    barHeights[channel][i] = map(db, -100, 0, 0, height);
  }
}

void drawBar(int index, float barHeight, float centerX, boolean flip) {
  float barWidth = (width / 2) / float(numBars);
  float x;
  if (flip) {
    x = map(index, 0, numBars, centerX + (width / 4) - barWidth, centerX - (width / 4));
  } else {
    x = map(index, 0, numBars, centerX - (width / 4), centerX + (width / 4) - barWidth);
  }
  float y = -barHeight;
  fill(lCyan);
  noStroke();
  rect(x, height/2 -y/2, barWidth, y);
}