Schlagwort-Archive: minim

Minim – Audio Analyse


Pegel Spektrum zeichnen

Um ein Pegel Spektrum darzustellen, kann man aus der AudioSource den AudioBuffer mix auslesen. Die Methode toArray() des AudioBuffers mix gibt ein Float Array mit den einzelnen Pegeln im Buffer zurück. Diese Werte kann man dann verwenden, um ein Spektrum zu zeichnen. Ich habe für die Darstellung dann der Einfachheit halber Rechtecke gewählt.

Achtung: Es handelt sich hier um ein Spektrum der Pegel über die Zeit, nicht um ein Frequenzspektrum!

Beispiel: Spektrum zeichnen


// Library importieren
import ddf.minim.*;

// Objekte erstellen
Minim minim;
AudioPlayer input;

int x, y;

// Anzahl der Peaks
int grid=128;

// Abstand zwischen den Peaks
int spacing=1;

// Ausschlagmaximum für Peaks festlegen
float yScale = 2;

void setup() {
size(1024, 400);
smooth();
noStroke();

// Konstruktor des Minim Objekts aufrufen
minim = new Minim(this);

// Livestream vom FM4 laden, Größe des default sample buffer's ist 1024
input = minim.loadFile("http://mp3stream1.apasf.apa.at:8000");

// Wiedergabe starten
input.play();
}

void draw() {

// für etwas Bewegunsunschärfe
fill(50, 10);
rect(0, 0, width, height);

// Auslesen und speichern des Spektrums
float[] buffer = input.mix.toArray();

// Breite der Rechtecke berechnen
for (int i=1; i <= buffer.length; i+=buffer.length/grid) {
float x = map(i, 0, buffer.length, 0, width);
float y = map(buffer[i-1]*yScale, -1, 1, 0, height) ;
fill (102, 145, 250,100);

// Rechteck zeichnen
rect(x+spacing, height, width/grid-2*spacing, -y);
}
}

void stop()
{
// Player in schließen
input.close();
// Minim Object stoppen
minim.stop();

super.stop();
}

Frequenzen darstellen

Der Begriff Spekturm bezieht sich in der Regel auf ein Frequenzspektrum. Dieses kann mit Hilfe der Klasse FFT  und der forward() Methode aus dem AudioBuffer errechnet werden. Dafür werden die Pegel im Buffer einer Fourier-Transformation unterzogen. Als Ergebnis erhält man keine einzelnen Frequenzen, sondern Frequenzbänder.

Umgekehrt kann man auch aus einem Frequenzspektrum ein Zeit-Pegel Spektrum ausgeben. Die Methode dafür heißt inverse();

Der auswertbare Frequenzbereich kann außerdem die halbe Sample-Frequenz nicht übersteigen. Die default Sample-Frequenz bei .mp3 und auf CD ist 44100 Hz, was eine maximale Frequenz von 22050 Hz für die Auwertung ergibt.

Einfaches Frequenzspektrum zeichnen

Um die FFT Klasse nutzen zu können, müssen wir zusätzlich zum letzten Beispiel die minim.analysis – Bibliothek importieren.

import ddf.minim.analysis.*;

Der folgende Code stellt ein Abwandlung des obigen Beispiels dar. Er zeichnet die Frequenzspektren der beiden Kanäle (left und right).

Beispiel: FFT Frequenzspektrum linear

// Library importieren
import ddf.minim.*;
import ddf.minim.analysis.*;

// Objekte erstellen
Minim minim;
AudioPlayer input;
FFT fftR, fftL;

int x, y;

// Anzahl der Peaks
int grid=32;

// Abstand zwischen den Peaks
int spacing=1;

// Ausschlagmaximum für Peaks festlegen
float yScale = 1;

void setup() {
size(1024, 400);
smooth();
noStroke();

// Konstruktor des Minim Objekts aufrufen
minim = new Minim(this);

// Livestream vom FM4 laden, Größe des default sample buffer's ist 1024
input = minim.loadFile("http://mp3stream1.apasf.apa.at:8000");

// Wiedergabe starten
input.play();
input.printControls();

// FFT-Instanz für die Spektrumsanalyse der beiden Kanäle
fftR = new FFT (input.bufferSize (), input.sampleRate ());
fftL = new FFT (input.bufferSize (), input.sampleRate ());
}

void draw() {

// für etwas Bewegunsunschärfe
fill(50, 10);
rect(0, 0, width, height);

// forwar FFT Analyse durchführen
fftR.forward(input.right);
fftL.forward(input.left);

// rechter Kanal
fill(255);
text("right Channel", 10, height/2+20);

// Breite der Rechtecke berechnen
for (int i=1; i <= fftR.specSize(); i+=fftR.specSize()/grid) {
float x = map(i, 0, fftR.specSize(), 0, width);
float y = map(fftR.getBand(i)*yScale, 0, 100, 0, height/2) ;
fill (102, 145, 250, 100);

// Rechteck zeichnen
rect(x+spacing, height, width/grid-2*spacing, -y);
}

// linker Kanal
fill(255);
translate(0, -height/2);
text("left Channel", 10, height/2+20);

// Breite der Rechtecke berechnen
for (int i=1; i <= fftL.specSize(); i+=fftL.specSize()/grid) {
float x = map(i, 0, fftL.specSize(), 0, width);
float y = map(fftL.getBand(i)*yScale, 0, 100, 0, height/2) ;
fill (102, 145, 250, 100);

// Rechteck zeichnen
rect(x+spacing, height, width/grid-2*spacing, -y);
}
}

void stop()
{
// Player in schließen
input.close();
// Minim Object stoppen
minim.stop();

super.stop();
}

Wie man an dem Spektrum unschwer erkennen kann, entspricht die Darstellung nicht unserer Wahrnehmung. Eine logarithmische Darstellung der Frequenzen ist hier angebracht. Mit FFT Klasse kann man eine solche Umwandlung mit der Methode logAverages() vornehmen.

Beispiel: FFT Frequenzspektrum logarithmisch



// Library importieren
import ddf.minim.*;
import ddf.minim.analysis.*;

// Objekte erstellen
Minim minim;
AudioPlayer input;
FFT fftR, fftL;

int x, y;

// Anzahl der Peaks
int grid=20;

// Abstand zwischen den Peaks
int spacing=5;

// Ausschlagmaximum für Peaks festlegen
float yScale = 1;

void setup() {
  size(1024, 400);
  smooth();
  noStroke();

  // Konstruktor des Minim Objekts aufrufen
  minim = new Minim(this);

  // Livestream vom FM4 laden, Größe des default sample buffer's ist 1024
  input = minim.loadFile("http://mp3stream1.apasf.apa.at:8000";,2048);

  // Wiedergabe starten
  input.play();
  input.printControls();

  // FFT-Instanz für die Spektrumsanalyse der beiden Kanäle
  fftR = new FFT (input.bufferSize (), input.sampleRate ());
  fftL = new FFT (input.bufferSize (), input.sampleRate ());
  fftR.logAverages(11, 16);
  fftL.logAverages(11, 16);
}

void draw() {

  // für etwas Bewegunsunschärfe
  fill(50, 10);
  rect(0, 0, width, height);

  // forwar FFT Analyse durchführen
  fftR.forward(input.right);
  fftL.forward(input.left);

  // rechter Kanal
  fill(255);
  text("right Channel", 10, height/2+20);

  // Breite der Rechtecke berechnen
  for (int i=0; i < fftR.avgSize(); i+=fftR.avgSize()/grid) {
    //println(fftR.avgSize());
    float x = map(i, 0, fftR.avgSize(), 0, width);
    //println(fftR.getAvg(i));
    float y = map(fftR.getAvg(i)*yScale, 0, 100, 0, height/5) ;
    fill (102, 145, 250, 100);

    // Rechteck zeichnen
    rect(x+spacing, height, width/grid-2*spacing, -y);
  }

  // linker Kanal
  fill(255);
  translate(0, -height/2);
  text("left Channel", 10, height/2+20);

  // Breite der Rechtecke berechnen
  for (int i=0; i < fftL.avgSize(); i+=fftL.avgSize()/grid) {
    float x = map(i, 0, fftL.avgSize(), 0, width);
    float y = map(fftL.getAvg(i)*yScale, 0, 100, 0, height/5) ;
    fill (102, 145, 250, 100);

    // Rechteck zeichnen
    rect(x+spacing, height, width/grid-2*spacing, -y);
  }
}

void stop()
{
  // Player in schließen
  input.close();
  // Minim Object stoppen
  minim.stop();

  super.stop();
}

Eine weitere Möglichkeit der FFT Klasse ist die Veränderung der Pegel einzelner Frequenzbereiche mit scaleBand() und setBand(). Siehe dazu die JavaDoc.

Beat Detection

Auch hier gibt es wieder 2 Möglichkeiten. Einerseits kann man nur mit den Levels (Amplituden) arbeiten - SOUND_ENERGY. Oder aber man nutzt FREQUENCY_ENERGY und greift die Levels in einzelnen Frequenzbändern ab.

Beispiel: BeatDetect mit SoundEnergy

// Library importieren
import ddf.minim.*;
import ddf.minim.analysis.*;

// Objekte erstellen
Minim minim;
AudioPlayer input;
BeatDetect beat;

float eRadius;

void setup() {
size(512, 512);
smooth();
noStroke();

// Konstruktor des Minim Objekts aufrufen
minim = new Minim(this);

// Livestream vom FM4 laden, Größe des default sample buffer's ist 1024
input = minim.loadFile("http://mp3stream1.apasf.apa.at:8000");

// Wiedergabe starten
input.play();
input.printControls();

// Erstellt die BeatDetect Instanz
beat = new BeatDetect();

ellipseMode(CENTER_RADIUS);
}

void draw() {

// für etwas Bewegunsunschärfe
fill(50, 50);
rect(0, 0, width, height);

// Initiiert die BeatDetection
beat.detect(input.mix);

fill (102, 145, 250, 100);

// Trigger der BeatDetection
if ( beat.isOnset() ) eRadius = 3;

if ( eRadius < 0.1 ) eRadius = 0.1;

// Zeichnet die Kreise
for (float i=1;i<10;i+=0.3) {
fill(102, 145, 250, 10/i*i);
ellipse(width/2, height/2, eRadius*i*i, eRadius*i*i);
}
eRadius *= 0.95;
}

void stop()
{
// Player in schließen
input.close();
// Minim Object stoppen
minim.stop();

super.stop();
}

Beispiel: BeatDetect mit FrequencyEnergy

Hier kann man mit den Methoden isKick(), isSnare() und isHat() Peaks in den Frequenzbändern abrufen, die den jeweiligen Schlagzeugbausteinen entsprechen. Das funktioniert allerdings nicht bei allen Musiktypen gleich gut. Bei Problemen kann man noch auf die Funktion isRange(int low, int high, int threshold) zurückgreifen.

// Library importieren
import ddf.minim.*;
import ddf.minim.analysis.*;

// Objekte erstellen
Minim minim;
AudioPlayer input;
BeatDetect beat;
BeatListener bl;

float radKick, radSnare, radHat;

void setup() {
size(1024, 512);
smooth();
noStroke();

// Konstruktor des Minim Objekts aufrufen
minim = new Minim(this);

// Livestream vom FM4 laden, Größe des default sample buffer's ist 1024
input = minim.loadFile("http://mp3stream1.apasf.apa.at:8000");

// Wiedergabe starten
input.play();
input.printControls();

// Erstellt die BeatDetect Instanz
// Im Frequency Mode müssen BufferSize und SampleRate übergeben werden.
beat = new BeatDetect(input.bufferSize(), input.sampleRate());

//Setzt die Zeit, in der der Algorithmus keine weiteren Beats meldet
beat.setSensitivity(200);
ellipseMode(CENTER_RADIUS);
textAlign(CENTER, CENTER);
}

void draw() {

// für etwas Bewegunsunschärfe
fill(50, 100);
rect(0, 0, width, height);

// Initiiert die BeatDetection
beat.detect(input.mix);

fill (102, 145, 250, 100);

// Trigger der BeatDetection
if ( beat.isKick() ) radKick = 2.5;
if ( beat.isSnare() ) radSnare = 2.5;
if ( beat.isHat() ) radHat = 2.5;

if ( radKick < 0.1 ) radKick = 0.1;
if ( radSnare < 0.1 ) radSnare = 0.1;
if ( radHat < 0.1 ) radHat = 0.1;

// Zeichnet die Kreise
for (float i=1;i<10;i+=0.3) {
fill(102, 145, 250, 10/i*i);
ellipse(width/4, height/2, radKick*i*i, radKick*i*i);
}

// Zeichnet die Kreise
for (float i=1;i<10;i+=0.3) {
fill(255, 0, 50, 10/i*i);
ellipse(width/2, height/2, radSnare*i*i, radSnare*i*i);
}

// Zeichnet die Kreise
for (float i=1;i<10;i+=0.3) {
fill(255, 200, 0, 10/i*i);
ellipse(width*3/4, height/2, radHat*i*i, radHat*i*i);
}

//Zeichnet den Text
fill(255);
text("Kick", width/4, height/2);
text("Snare", width/2, height/2);
text("Hat", width*3/4, height/2);

radKick *= 0.9;
radSnare *= 0.9;
radHat *= 0.9;
}

void stop()
{
// Player in schließen
input.close();
// Minim Object stoppen
minim.stop();

super.stop();
}

Minim Sound abspielen und aufnehmen


Minim ist die Standard Audio-Bibliothek in Processing.

Sie bietet die Möglichkeit Sounds abzuspielen, aufzunehmen, zu verändern, zu synthetisieren und zu analysieren.

Minim

Um mit der Library arbeiten zu können, müssen wir zuerst ein Objekt der Minim-Klasse erstellen. Dieses Objekt kann dann zur Sound Ein- und Ausgabe benutzt werden. Dabei ist zu beachten, dass vor dem Schließen des Programms erst jegliche Ein- und Ausgaben geschlossen, und dann das Minim-Objekt gestoppt werden muss.

// Import der Bibliothek
import ddf.minim.*;

// erstellen eines Objektes (einer Instanz)
Minim minim;

// Aufruf des Konstruktors
minim = new Minim(this);

// Sound vom Eingang der Soundkarte
input = minim.getLineIn();

// Zuerst Eingabe schließen und dann Minim stoppen
input.close();
minim.stop();

Sound abspielen

Um Sound mit Minim abspielen zu können, muss zuerst eine Quelle definiert werden. Dies kann eine Datei (im /data-Ordner des Sketches), oder ein Eingang der Soundkarte sein. Mögliche Dateitypen sind: WAV, AIFF, AU, SND, and MP3.

Interessant ist vielleicht, dass die Dateien zurückgespult werden müssen. D.h. wenn sie an ihr Ende kommen, ist es so, wie bei einem analogen Plattenspieler. Der dreht sich noch, auch wenn keine Musik gespielt wird.

Für die Soundwiedergabe gibt es grundsätzlich 3 Möglichkeiten:

AudioSample

  • Datei wird vor dem Abspielen in den Speicher geladen.
  • Für sehr kurze Sequenzen geeignet.
  • Kann nur getriggert, nicht gelooped, oder  sonst etwas werden.
  • AudioSample sample = loadSample(„mySample.mp3“);

Beispiel: Trigger

Audio Snippet

  • keine Echtzeit Effekte
  • kein Zugriff auf Samples
  • Datei wird vor dem Abspielen in den Speicher geladen.
  • AudioSnippet snippet = loadSnippet(„mySnippet.mp3“);

AudioPlayer

  • zum Apspielen längerer Audio Dateien.
  • Datei wird „on the fly“ decodiert. Das spart Speicher, erhöht aber die Latenzzeit.
  • AudioPlayer player = loadFile(„myFile.mp3“);

Beispiel: Player

Beispiel: Abspielen des FM4 Streams

// Library importieren
import ddf.minim.*;

// Objekte erstellen
Minim minim;
AudioPlayer in;

void setup()
{
size(512, 200);

// Konstruktor des Minim Objekts aufrufen
minim = new Minim(this);

// Livestream vom FM4 laden, Größe des default sample buffer's ist 1024
in = minim.loadFile("http://mp3stream1.apasf.apa.at:8000");

// Wiedergabe starten
in.play();
}

void draw()
{

}

void stop()
{
// Player in schließen
in.close();
// Minim Object stoppen
minim.stop();

super.stop();
}

Methoden des AudioPlayer:

  • cue(int millis): legt die Abspielposition fest (in ms vom Start).
  • getMetaData(): gibt die ID3 Tags als String zurück.
    • album(), author(), comment(), composer(), copyright(), date(), disc(), encoded(), fileName(), genre(), length(), orchestra(), publisher(), title(), track().
  • isLooping(): gibt true zurück, wenn das Stück noch mehr als einmal gespielt werden muss.
  • isPlaying(): bei Wiedergabe true
  • length(): gibt die länge des Stücks in Millisekunden als int zurück.
  • loop(): schaltet looping ein.
  • loop(int n): schaltet looping ein. Spielt das Stück n mal ab.
  • pause(): pausiert die Wiedergabe
  • play(): startet die Wiedergabe
  • play(int millis): startet die Wiedergabe bei millis Millisekunden.
  • position(): gibt die aktuelle Position im Stück zurück
  • rewind(): spult an den Beginn.
  • setLoopPoints(int start, int end): setzt loopPoints ()
  • skipp(int millis): Spult millis Millisekunden

Außerdem bietet der AudioPlayer noch einige vererbte Methoden.

  • printControls(): gibt die Einstellungsmöglichkeiten des Eingangsgerätes auf der Konsole aus.
    Die zurückgegebenen Einstellungen sind hier beschrieben.
  • in.setVolume(int volume): legt die Lautstärke fest. Der Bereich ist von der jeweiligen Soundkarte abhängig (bei mir 0-65536).
  • in.getVolume(): gibt die aktuelle Lautstärkeneinstellung zurück.
  • mix.level(), left.level(), right.level(): gibt den Pegel des jeweiligen Kanals als float zurück

Aufgabe: Verändere das obige Programm so, dass man die Lautstärke verändern kann und der Pegel auf einfache Art und weise visualisiert wird.

Sound aufnehmen

Mit der AudioRecorder Klasse kann man den Inet-Radio-Sound auch aufnehmen. Dabei wird der Dateiname des aufgenommenen Stücks aus den Metadaten des Streams generiert. Bei FM4 ist das leider nur „FM4“. Bei anderen Sendern kann man vielleicht aber auch den Titel und den Interpreten auslesen.

// Library importieren
import ddf.minim.*;

// Objekte erstellen
Minim minim;
AudioPlayer in;
AudioRecorder recorder;

int x, y;

void setup() {
size(512, 200);
y=height/2;

// Konstruktor des Minim Objekts aufrufen
minim = new Minim(this);

// Livestream vom FM4 laden, Größe des default sample buffer's ist 1024
in = minim.loadFile("http://mp3stream1.apasf.apa.at:8000");

// Wiedergabe starten
in.play();

// Recorder erstellen
// Dateiname für Aufnahme festlegen
recorder = minim.createRecorder(in, in.getMetaData().title()+"_"+timestamp()+"_##.wav", true);

noFill();
background(0);
stroke(255);
}

void draw() {
// wenn gerade aufgenommen wird, wird rot gezeichnet, sonst weiß!
if (recorder.isRecording()) stroke(255, 0, 0);
else stroke(255);

// kleine Anmation
fill(0, 5);
rect(0, 0, width, height);
noFill();
// Kreisgröße Abhängig von Lautstärke
float dim = in.mix.level () * width;
// Kreis x-Position verschieben
x += in.mix.level() * 20;
// Kreis zeichnen
ellipse (x, y, dim, dim);
if (x > width) {
x = 0;
}
}

void keyReleased()
{
// mit "r" wird die Aufnahme gestartet
if ( key == 'r' ) {
if (recorder.isRecording()) recorder.endRecord();
else {
// Dateiname für Aufnahme festlegen
recorder = minim.createRecorder(in, in.getMetaData().title()+"_"+timestamp()+"_##.wav", true);
recorder.beginRecord();
}
}

// erst mit "s" wird die Aufnahme gespeichert
if ( key == 's' ) {
recorder.save();
println("Done saving.");
}
}

void stop()
{
// Player in schließen
in.close();
// Minim Object stoppen
minim.stop();

super.stop();
}

// timestamp
String timestamp() {
Calendar now = Calendar.getInstance();
return String.format("%1$ty%1$tm%1$td_%1$tH%1$tM%1$tS", now);
}