Minim m = new Minim (this);
AudioPlayer player = m.loadFile ("myTrack.wav");
AudioSample sample = m.loadSample ("mySample.wav");
AudioSnippet snippet = m.loadSnippet ("mySnippet.wav");
Drei Objekte bietet minim für das Laden/Speichern von Audiodateien. Der AudioPlayer bietet im Vergleich mit dem AudioSample und dem AudioSnippet den größten Funktionsumfang. Als Quelle können alle drei mit den Formaten mp3, ogg, wav und aif arbeiten. Wie beim Laden von Bildern und Schriften muss sich die zu ladende Datei im /data Ordner des Sketches befinden. Ebenfalls sollte der Aufruf der minim-Funktionen zum Auslesen der Audiodatei innerhalb des setup()-Blocks statt finden.
player.cue (0); // An den Anfang player.cue (player.length () / 2); // In die Mitte
println (player.getMetaData ().title()); println (player.getMataData ().author ());
player.loop (); // Endlosschleife player.loop (7); // Spielt die Datei 7mal in Folge ab
Das Grundgerüst zum Abspielen von Audiodateien untergliedert sich in zwei Bereiche. Zum Ersten muss minim in den aktuellen Sketch einbezogen werden. Dies geschieht über die Zeile import ddf.minim.*;, welche automatisch durch das Auswählen von “Sketch” → “Import Library…” → “minim” erzeugt wird. Im globalen Bereich, zwischen import und setup(), erfolgt die Definition der Variablen für die minim- und die Player-Instanz (global da sie im setup() sowie im draw() benötigt werden). Zu Beginn des Processing Sketches wird die Datei mit Hilfe von minim aus dem '/data' Ordner geladen und das Abspielen durch play() eingeleitet.
// Einbeziehen des minim-Audio-Toolkits
import ddf.minim.*;
// Instanz der minim Bibliothek
Minim minim;
// Instanz die das geladene Audiodokument repräsentiert
AudioPlayer player;
void setup () {
// Audiotoolkit erstellen
minim = new Minim (this);
// Audiodatei aus dem data-Ordner laden
player = minim.loadFile ("track.aif");
// Audiodatei abspielen
player.play ();
}
void draw () {
// Mögliches Auswerden des aktuellen Spektrums
// bzw. von anderen Audioeigenschaften.
}
Alle folgenden Processing Sketche sollen einen kurzen und flächendeckenden Einstieg in das Arbeiten mit der minim Library aufzeigen. Vom simplen Laden bis zur Auswertung von Frequenzbereichen ist jeder erdenklicher Umgang mit Audioinformationen möglich – jedoch, wie im letzten Beispiel, mit vermehrter Schreibarbeit verbunden. minim ist eine der best-dokumentiertesten Werkzeuge für Processing. Neben einer umfangreichen Beschreibung werden außerdem Beispiele online angeboten.
Zum Einstieg zeigt dieses Beispiel das rudimentären Laden und Abspielen einer Sounddatei. In diesem Fall greifen wir über die minim Instanz auf die Datei track.aif zu (sie muss sich im 'data'-Ordner befinden). Nach dem Laden im setup() startet der Aufruf von play() das Abspielen. Innerhalb des draw()s wird die aktuelle Position im Stück ermittelt und durch einen Kreis im Sketchfenster dargestellt. Die Breite der Zeichenfläche entspricht dabei der Tracklänge.
import ddf.minim.*; Minim minim; AudioPlayer player; void setup () { // Sketcheinstellungen size (320, 170); smooth (); stroke (100); // Audiotoolkit erzeugen minim = new Minim (this); // Audiodatei in das Object 'player' laden player = minim.loadFile ("track.aif"); // Audiodatei abspielen player.play (); } void draw () { background (0); // Relative Abspielposition im Stück ermitteln. // Dieser Wert bewegt sich zwischen 0 und 1. float playPos = player.position (); float playLen = player.length (); float xpos = (playPos / playLen) * width; // Zeichnen der Ellipse als ellipse (xpos, height / 2, 20, 20); }
Die beiden folgenden Funktionen ermöglichen das Steuern des Abspielens der Audiodatei durch Maus- bzw. Tastaturinteraktion. Je nach Position der Maus auf der x-Achse wird bei Klick der 'Spielkopf' verschoben. Ähnlich wie bei der Positionsermittlung berechnet Processing die neue Abspielposition durch relatives mappen der Mausposition zur Breite des Fensters, und überträgt diesen Wert auf die Gesamtlänge der Aufnahme. Da bei der Division von zwei Ganzzahlen (int) das Ergebnis ebenfalls keine Kommastellen enthällt, muss einer der beiden Werte zum (float) umgewandelt werden. Anderenfalls erhält man für die relative Mausposition (zwischen 0 und 1) immer 0.
void mousePressed () { // Relative Position der Maus auf der x-Achse, bezogen // auf die Breite des Fensters (ebenfalls zw. 0 und 1). // Multipliziert mit der Länge der Audiodatei. float pos = ((float) mouseX / width) * player.length (); // Setzt neue Abspielposition. player.cue ((int) pos); } void keyPressed () { // Pausieren und Abspielen auf Tastendruck if (player.isPlaying ()) { player.pause (); }else{ player.play (); } }
Neben der Position im Audiodokument und dessen Steuerung, erlaubt die minim Bibliothek das Auslesen von Eigenschaften. Eine bezeichnendes Kriterium ist die wiedergegebene Lautsträke zu einem bestimmten Zeitpunkt. Während des Abspielens durch den play() Aufruf, kann im draw() das sogenannte 'level' für jedes Einzelbild erfragt werden. Die mix.level()-Funktion beschreibt die Auslastung dieses Merkmals in einem Wertebereich von 0 und 1.
Das Objekt mix repräsentiert das Sterosignal, welches sich aus dem linken (left) und dem rechten (right) Kanal zusammen setzt.
import ddf.minim.*; Minim minim; AudioPlayer player; void setup () { // Sketch einrichten size (320, 240); noStroke (); // Audiodatei laden und abspielen minim = new Minim (this); player = minim.loadFile ("track.aif"); player.play (); } void draw () { // Schwarzes semitransparentes Recht // über die gesamte Bühne zeichnen fill (0,30); rectMode (CORNER); rect (0, 0, width, height); // Größe des Quadrats in Abhänigkeit von // der Lautstärke berechnen float dimension = player.mix.level () * 250; // Rechteck zeichnen fill (255); rectMode (CENTER); rect (width/2, height/2, dimension, dimension); }
Angewendet auf ein Mikrofon-Input visualisiert der folgende Sketch die chronologische Entwicklung der Umgebungsgeräusche. Je lauter, desto größer und separierter wird ein Kreis auf der Zeichenfläche abgebildet. Dabei verschiebt sich die Position auf der x-Achse von links nach rechts, bis es zu einem Umbruch in die nächste Zeile kommt. Mittels der globalen Variablen x und y wird die aktuelle Position gespeichert und durch die if-Bedingung am Ende des draw()-Blocks kontrolliert.
import ddf.minim.*; float x; float y; Minim minim; AudioInput input; void setup () { // Sketch einstellen size (320, 240); smooth(); stroke (255, 25); noFill (); // Startposition festlegen x = 0; y = 20; // Audiotoolkit anlegen minim = new Minim (this); input = minim.getLineIn (Minim.STEREO, 512); background (0); } void draw () { // Kreisgröße Abhängig von Lautstärke float dim = input.mix.level () * width; // Kreis x-Position verschieben x += input.mix.level () * 20; // Kreis zeichnen ellipse (x, y, dim, dim); if (x > width) { x = 0; y += 20; } }
Eine der bekanntesten Darstellungen von Ton und Klang ist das klassische Spekturm. In minim als AudioBuffer bezeichnet, beinhaltet es ein float Array mit einem Wert pro Kanal. Die Anzal der Kanäle wird bei dem Erzeugen des AudioInputs festgelegt. In diesem Fall unterteilen wir das Spektrum zu Beginn im setup-Blocks in 96 Bereiche.
Mit jedem Einzelbild wird eine Kopie des Buffers (toArray()) erzeugt um beim Durchlaufen des Arrays alle Einträge im Spektrum abzubilden. In der dafür benutzen for-Schleife verbindet die Oberseite eines Rechtecks jeweils zwei Teile des Spektrums. Neben der y-Position gibt der Mittelwert beider Kanäle die HSB-Füllfarbe an.
import ddf.minim.*; Minim minim; AudioInput input; float yStart = 100; float yScale = 140; void setup () { // Sketch einstellen size (320, 240); smooth(); noStroke (); colorMode (HSB); // Audiotoolkit anlegen minim = new Minim (this); input = minim.getLineIn (Minim.STEREO, 96); } void draw () { background (0); // Auslesen und speichern des Spektrums float[] buffer = input.mix.toArray (); // Breite der Rechtecke berechnen float step = ceil ((float) width / buffer.length); // Für jeden einzelnen Eintrag im Buffer wird ein // Rechteck gezeichnet. Es entsteht durch das verbinden // zweier Buffer-Einträge. Die Schleife beginnt bei 1, // jeder Eintrag wird mit seinem Vorgänger verbunden. for (int i=1; i < buffer.length; i++) { // Positionen für alle 4 Punkt bestimmen float x1 = (i-1) * step; float x2 = i * step; float y1 = yStart + buffer[i-1] * yScale; float y2 = yStart + buffer[i] * yScale; // Füllfarbe definieren float h = (buffer[i-1] + buffer[i]) / 2; fill (h * 255, 255, 255); // Rechteck zeichnen beginShape (QUADS); vertex (x1, y1); vertex (x2, y2); vertex (x2, height); vertex (x1, height); endShape (); } }
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.
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; }