Frequenz Gruppen ermitteln in Processing

Hinzugefügt am von Jonas Loh
Die minim Bibliothek bietet neben dem Laden/Abspielen von Dokumenten, dem Nutzen von Audiohardware ein Paket von Werkzeugen um Klange zu erzeugen bzw. bestehendes Material auszuwerten. Eine bekannte Mehthode ist die sog. FFT (Fast Fourier Transform), bei der das Frequenzspektrum in eine feste Anzahl von Frequnzbändern unterteilt wird. Durch das Auslesen dieser Bänder erhält man Aufschluss über die Verteilung von Höhen, Mitten und Tiefen in der Audiodatei.

Da es bei hohen Auflösungen in den Bereichen von 1024, 512 oder 256 Bändern (wie in diesem Fall) nicht zu einer Abfrage jedes Frequenzbandes kommen muss, werden diese in den meisten Fällen zu Gruppen zusammengefasst. Im folgenden Beispiel liefert die Funktion getGroup() diese verkürzte Fassung mit einer Auflösung von 16 Bereichen, welche als Parameter angegeben wird. Die Anzahl der Bereich muss dabei immer ein Vielfaches von 2 sein (2, 4, 8, 16, 32, 64, 128, …). Innerhalb der Funktion wird ein Array angelegt und der Mittelwert einer jeden Frequenzgruppe berechnet, final in einem der Arrayfelder abgelegt. Nach dem Aufruf im draw(), kommt es zu einer simplen Abbildung der Audiodaten um die Funktionsweise zu verdeutlichen.

Der Sketch

import ddf.minim.*;
import ddf.minim.analysis.*;
 
Minim minim;
AudioInput input;
FFT fft;

float maxSpec = 0;

void setup () {
  size(320, 240);
  noStroke ();
  
  // Audiotool-Box und Mikrofoneingang erstellen 
  // und einrichten. Die '256' bestimmen dabei die 
  // spätere Auflösung des Spektrums.
  minim = new Minim (this);
  input = minim.getLineIn (Minim.STEREO, 256);
  // FFT-Instanz für die Spektrumsanalyse
  fft = new FFT (input.bufferSize (), 
                 input.sampleRate ());
}
 
void draw () {
  background (0);
  
  float g = 0;    // Grünwert der Füllfarbe
  float h = 0;    // Höhe von Rechteck und Linie
  float specStep; // Breite einer horiz. Linie
  float specScale = (float) width / (fft.specSize () - 1);
  
  // Erzeugen der 'Frequenz-Gruppen' (16 Bereich)
  // mögliche Schritte: 2-4-8-16-32-64-128
  float[] group = getGroup (16);
  
  // Zeichnen des detailierten Frequenzspektrums
  noStroke ();
  for (int i = 0; i < fft.specSize (); i++) {
    g = map (fft.getBand (i), 0, maxSpec, 50, 255);
    h = map (fft.getBand (i), 0, maxSpec, 2, height);
    fill (0, g, 0);
    rect (i * specScale, height - h, specScale, h);
  }
  
  // Zeichnen der Gruppen (Linien)
  stroke (255, 255, 0, 200);
  specStep = width / group.length;
  for (int i=0; i < group.length; i++) {
    h = height - map (group[i], 0, maxSpec, 0, height);
    line (i * specStep, h, (i+1) * specStep, h);
  }
}

/** 
 * Funktion fasst das vorliegende FFT-Spektrum
 * in eine durch den Parameter 'theGroupNum' 
 * festgelegte Anzahl von gleichgroßen Bereichen
 * zusammen – und gibt deren Mittelwert zurück.
 */
float[] getGroup (int theGroupNum) {
  fft.forward (input.mix);
  
  // Leeres Array für die Gruppen erstellen
  float[] group  = new float[theGroupNum];
  // Das FFT-Spekturm hat eine Stelle mehr 
  // als beim Input definiert. (256->257).
  // Diese wird ignoriert.
  int specLimit  = fft.specSize () - 1;
  // Anzahl der Frequenzbänder pro Gruppe
  int groupSize = specLimit / theGroupNum;
  
  // Alle Gruppen mit einem Startwert füllen
  for (int i=0; i < group.length; i++) {
    group[i] = 0;
  }
  
  // Für jedes FFT-Frequenz-Band
  for (int i=0; i < specLimit; i++) {
    // Maximum?
    if (fft.getBand (i) > maxSpec) {
      maxSpec = fft.getBand (i);
    }
    // Jedes Band einer Gruppe zuweisen
    int index = (int) Math.floor (i / groupSize);
    group[index] += fft.getBand (i);
  }
  
  // Der Wert jeder Gruppe durch die Anzahl
  // der enthaltenen Bänder Teilen - >Mittelwert
  for (int i=0; i < group.length; i++) {
    group[i] /= groupSize;
  }
  // Gruppe zurück geben.
  return group;
}