P5
und P5.sound
- Zeit-Frequenz Darstellungen
via Mikrofon
mit Spectral
Centroid und maximale Amplitude und Tonhöhenerkennung
(in der
eingestrichene Oktave als Beispiel)
Zunächst
werden im <head>
der Seite die beiden Scripte für P5 und P5.sound eingebunden:
<script src="header/p5.js"></script>
<script src="header/p5.sound.js"></script>
Dann wird
im <body> der Seite
ein Container mit einer eindeutigen id mithilfe von DIV-Tags angelegt,
in dem alles angezeigt werden soll. Dies geschieht via
<DIV id="p5container"></DIV>
über
Style-Eigenschaften kann man diesen noch weiter im Aussehen verändern,
z.B.
<DIV id="p5container"
style="width:800;border: 1px solid #333;box-shadow: 8px 8px 5px
#444;padding: 8px 12px;background-color:ffffff"></DIV>
Das dazugehörende
Script ist wie fast alle P5 Scripte aufgebaut:
Erst werden
in der Funktion setup() die
Grundkomponenten festgelegt (die neu hinzugekommenen Scriptteile sind
rot markiert):
function setup(){
var container = createCanvas(800,400); //
Canvas erstellen
container.parent('p5container'); // an
DIV-Container anhängen
mic = new p5.AudioIn(); //
Mikrofoneingang anlegen
mic.start(); // Mikrofon starten
fft = new p5.FFT();
// Fouriertransformation des Signals ermöglichen
fft.setInput(mic); //
Mikrofoneingang mit der FFT-Analye verbinden
aufloesung = sampleRate()/fft.bins/2; //
Abstand zwischen den einzelnen Frequenzen
// der FFT berechnen
}
Außerhalb
der setup()-Funktion und
der darauffolgenden draw()-Funktion
wird ein Array mit Tonhöhennamen erstellt (hier als Anwendungsbeispiel
in der eingestrichenen Oktave), auf das sich das Script bei der Tonhöhenerkennung
bzw. Ausgabe der Tonhöhennamen dann beziehen kann,
also:
var
pitch_array=['c1','c#1','d1','d#1','e1','f1','f#1','g1','g#1','a1','a#1','h1'];
Daraufhin
wird in der Funktion draw() beschrieben,
was im Canvas dargestellt werden soll (die neu hinzugekommenen Scriptteile
sind rot markiert):
function draw() {
background(255,255,255); //zeichne weißen
Hintergrund
//Oszillogramm zeichnen
var waveform = fft.waveform(); // hole
aus dem Klang alle Pegelwerte und schreibe sie
// ins Array waveform
noFill(); //
keine Formen farblich ausfüllen (keine Füllfarbe)
stroke(225, 225, 225); // Strichfarbe auf
ein helles grau setzen
beginShape(); // das Zeichnen einer Form
beginnen
for (var i = 0; i < waveform.length; i++){ //
hole in einer Schleife in der Länge
// des Arrays waveform jeden einzelnen Wert i aus dem Array
var x = map( i, 0, waveform.length, 0, width);
// skaliere die aufsteigend
// gezählten Zahlen i (von 0 bis Ende des waveform Arrays) so, dass
sie von 0 bis zur Weite des
// Canvas dargestellt werden und weise sie der Variablen x zu (Zeit auf
der X-Achse).
var y = map( waveform[i], -1, 1, 0, height);//
skaliere jeden Wert aus dem
// Array waveform, der zwischen -1 und 1 liegt, so, dass er zwischen 0
und der Höhe des Canvas
// dargestellt wird, und weise ihn dem Wert y zu (Amplitude auf der Y-Achse)
vertex(x,y-100); // weise x und y als Koordinaten
eines Punkts zu, der einen Eckpunkt der
// dazustellenden Gesamtform (via beginshape(); s.o.) beschreibt.
}
endShape(); // Schließe die Gesamtform
ab, in ihr ist dann ein Ausschnitt der Wellenform pro
// Frame dargestellt.
//Spektrale Hüllkurve
zeichnen
var spectrum = fft.analyze(); // mache
eine Spektralanalyse in der Länge des aktuellen
// Frames und schreibe sie ins Array spectrum
noFill(); //
keine Formen farblich ausfüllen (keine Füllfarbe)
stroke(188, 48, 47); // Strichfarbe auf
ein dunkles rot setzen
beginShape(); // das Zeichnen einer Form
beginnen
for (i = 0; i < spectrum.length; i++) { //
hole in einer Schleife in der Länge
// des Arrays spectrum jeden einzelnen Wert i aus dem Array (= jede Frequenz)
var x = map(i, 0, spectrum.length, 0, width*4); //
skaliere die aufsteigend
// gezählten Zahlen i (von 0 bis Ende des spectrum Arrays) so, dass
sie von 0 bis zur vierfachen
// Weite des Canvas dargestellt werden (da in den höheren Frequenzanteilen
so gut wie keine
// Amplituden sind) und weise sie der Variablen x zu (Frequenz auf der
X-Achse).
var y = map(spectrum[i], 0, 255, height, 0); //
skaliere jeden Wert
// aus dem Array spectrum, der zwischen 0 und 255 liegt, so, dass er zwischen
der Höhe des Canvas
// und 0 dargestellt wird. Weise ihn dann dem Wert y zu (Amplituden der
einzelnen Frequenzen auf
// der Y-Achse)
vertex(x,y); // weise x und y als Koordinaten
eines Punkts zu, der einen Eckpunkt der
// dazustellenden Gesamtform (via beginshape(); s.o.) beschreibt.
}
endShape(); // Schließe die Gesamtform
ab, in ihr ist dann ein Ausschnitt des Spektrums pro
// Frame dargestellt.
// Finde die stärkste
Frequenz im Spektrum
noFill(); // keine
Formen farblich ausfüllen (keine Füllfarbe)
stroke(188, 48, 47); //
Strichfarbe auf ein dunkles rot setzen
maxfrequenz_index= spectrum.indexOf(Math.max.apply({}, spectrum));
//
// ermittle, an welcher Stelle (= indexnummer)im Array spectrum die Frequenz
mit der höchsten
// Amplitude ist und schreibe die Position ins die Variable maxfrequenz_index.
maxfrequenz = round(maxfrequenz_index*aufloesung);
// Multipliziere die
// gefundene indexzahl maxfrequenz_index
mit der spektralen Auflösung, um die Frequenz der höchsten
// Amplitude im Spektrum zu ermitteln
text("Max. Frequenz: "+maxfrequenz + " Hz",
600, 40); //
schreibe den
// gefundenen Wert in den Canvas
// Finde den
Spectral Centroid
noFill(); //
keine Formen farblich ausfüllen (keine Füllfarbe)
stroke(48, 188, 47); //
Strichfarbe auf ein dunkles grün setzen
spectralCentroid = round(fft.getCentroid()); // ermittle den Spectral
Centroid
text("Spectral Centroid: "+spectralCentroid + "
Hz", 600, 60); //
schreibe
// den gefundenen Wert in den Canvas
rect(spectralCentroid/8, 100, width / spectrum.length, height );
//
// zeichne ein Rechteck mit den Ausgangskoordinaten spectralCentroid/8
(eigentlich das Spekturm /
//
2, da dieses für
die Darstellung aber mal 4 genommen wurde, s.u., muss durch 8 geteilt
werden)
//
und der Höhe
von 100 Pixeln unter der oberen Canvas-Kante, einer Breite, die unabhängig
von der
//
Größe
des spektralen Arrays (spectrum) immer die richtige ist, um insgesamt
den Canvas in der
//
Breite zu füllen,
und der Höhe des Canvas.
// ermittle Frequenz
und Amplitude an der Mausposition
noFill();
//
keine Formen farblich ausfüllen (keine Füllfarbe)
stroke(225, 225,
225);
//
Strichfarbe auf ein helles grau setzen
aktuelle_frequenz
= round(map(mouseX, 0, spectrum.length, 0, width*4)*aufloesung/10);
// ermittle die Frequenz, die unter
der Maus ist
// (aktuelle_frequenz), indem die X-Koordinate der Maus zwischen 0 und
dem maximalen Frequenzband
//
der Spektralanalyse
gemappt wird auf den Wertebereich zwischen 0 und der vierfachen Canvasbreite
//
mal
einem 10tel der spektralen Auflösung
und runde das ganze zu einer ganzen Zahl
aktueller_pegel=round(((map(mouseY,
0, 255, height, 0))+200)/6);
//
// ermittle dem Pegel, der unter der Maus ist (aktueller Pegel), indem
die Y-Koordinate der Maus
// zwischen 0 und 255 gemappt wird auf den Wertebereich zwischen der Höhe
des Canvas (= untere
// Kante) und 0 (=obere Kante).
if (aktuelle_frequenz>=0){
// wenn die aktuelle Frequenz >= 0 ist
text(aktuelle_frequenz
+ " Hz", mouseX+15, mouseY);
// gebe die aktuelle
// Frequenz an der Position der Maus aus.
}
text(aktueller_pegel
+ " %", mouseX+15, mouseY+12);
// gebe die aktuelle
// Amplitude an der Position der Maus aus
rect(mouseX, 100,
width / spectrum.length, height );
// zeichne die X-Achse
// eines Koordinatenkreuzes mit der Maus im Mittelpunkt
rect(0, mouseY,
width, width / spectrum.length );
// zeichne die Y-Achse
// eines Koordinatenkreuzes mit der Maus im Mittelpunkt
//Spektrum zeichnen
noFill(); // keine Formen farblich ausfüllen
(keine Füllfarbe)
stroke(225, 225,
225); // Strichfarbe auf ein helles grau
setzen
for (var i = 0; i< spectrum.length; i++){ //
hole in einer Schleife in der Länge
// des Arrays spectrum jeden einzelnen Wert i aus dem Array (= jede Frequenz)
if (i == maxfrequenz_index){stroke(188,
48, 47);} else {stroke(225, 225, 225);}
//
wenn i dem Index der Frequenz mit der maximalen Amplitude entspricht,
dann wähle
// dunkelrot als Strichfarbe, sonst hellgrau.
var x = map(i, 0, spectrum.length, 0, width*4); //
skaliere die aufsteigend
// gezählten Zahlen i (von 0 bis Ende des spectrum Arrays) so, dass
sie von 0 bis zur vierfachen
// Weite des Canvas dargestellt werden (da in den höheren Frequenzanteilen
so gut wie keine
// Amplituden sind) und weise sie der Variablen x zu (Frequenz auf der
X-Achse).
var h = -height + map(spectrum[i], 0, 255, height, 0); //
skaliere jeden Wert
// aus dem Array spectrum, der zwischen 0 und 255 liegt, so, dass er zwischen
der Höhe des Canvas
// und 0 dargestellt wird, und ziehe ihn von der Höhe des Canvas
ab (damit die Amplitudenwerte für
// die einzelnen Frequenzen nicht auf dem Kopf dargestellt werden). Weise
ihn dann dem Wert y zu
// (Amplituden der einzelnen Frequenzen auf der Y-Achse)
rect(x, height, width / spectrum.length, h ); //
zeichne pro Frequenz ein Rechteck
// mit den Ausgangskoordinaten x und der Höhe des Canvas (= an der
untere Kante), einer Breite, die
// unabhängig von der Größe des spektralen Arrays (spectrum)
immer die richtige ist, um insgesamt
// den Canvas in der Breite zu füllen, und einer Höhe h.
}
//
Tonhöhe finden und ausgeben
noStroke(); //
keine Striche farblich ausfüllen (keine Füllfarbe)
stroke(225, 225, 225);
// Strichfarbe auf ein helles grau setzen
frequenz_array=[]; // erzeuge ein leeres
Array für die Energieanteile einzelner Frequenzen
vol_c1=fft.getEnergy(262); // hole aus
dem Spektrum den Amplitudenwert für die Frequenz
// bei 262 Hz (c1)
frequenz_array.push(vol_c1); // füge
diesen Wert dem frequenz_array hinzu
text("c1: "+vol_c1, 0, 20); //
gebe den Amplitudenwert für c1 aus
vol_cis1=fft.getEnergy(277); //
hole aus dem Spektrum den Amplitudenwert für die
// Frequenz bei 277 Hz (c#1)
frequenz_array.push(vol_cis1);
// füge diesen Wert dem frequenz_array hinzu
text("c#1: "+vol_cis1, 50, 20);
// gebe den Amplitudenwert für c#1
aus
vol_d1=fft.getEnergy(294); //
hole aus dem Spektrum den Amplitudenwert für die
// Frequenz bei 294 Hz (d1)
frequenz_array.push(vol_d1);
// füge diesen Wert dem frequenz_array hinzu
text("d1: "+vol_d1, 100, 20);
// gebe den Amplitudenwert für d1
aus
vol_dis1=fft.getEnergy(311); //
hole aus dem Spektrum den Amplitudenwert für die
// Frequenz bei 311 Hz (d1)
frequenz_array.push(vol_dis1);
// füge diesen Wert dem frequenz_array hinzu
text("d#1: "+vol_dis1, 150, 20);
// gebe den Amplitudenwert für d#1
aus
vol_e1=fft.getEnergy(330); //
hole aus dem Spektrum den Amplitudenwert für die
// Frequenz bei 330 Hz (d#1)
frequenz_array.push(vol_e1);
// füge diesen Wert dem frequenz_array hinzu
text("e1: "+vol_e1, 200, 20);
// gebe den Amplitudenwert für e1
aus
vol_f1=fft.getEnergy(349); //
hole aus dem Spektrum den Amplitudenwert für die
// Frequenz bei 349 Hz (e1)
frequenz_array.push(vol_f1);
// füge diesen Wert dem frequenz_array hinzu
text("f1: "+vol_f1, 250, 20);
// gebe den Amplitudenwert für f1
aus
vol_fis1=fft.getEnergy(370); //
hole aus dem Spektrum den Amplitudenwert für die
// Frequenz bei 370 Hz (f#1)
frequenz_array.push(vol_fis1);
// füge diesen Wert dem frequenz_array hinzu
text("f#1: "+vol_fis1, 300, 20);
// gebe den Amplitudenwert für f#1
aus
vol_g1=fft.getEnergy(392); //
hole aus dem Spektrum den Amplitudenwert für die
// Frequenz bei 392 Hz (g1)
frequenz_array.push(vol_g1);
// füge diesen Wert dem frequenz_array hinzu
text("g1: "+vol_g1, 350, 20);
// gebe den Amplitudenwert für g1
aus
vol_gis1=fft.getEnergy(415); //
hole aus dem Spektrum den Amplitudenwert für die
// Frequenz bei 415 Hz (g#1)
frequenz_array.push(vol_gis1);
// füge diesen Wert dem frequenz_array hinzu
text("g#1: "+vol_gis1, 400, 20);
// gebe den Amplitudenwert für g#1
aus
vol_a1=fft.getEnergy(440); //
hole aus dem Spektrum den Amplitudenwert für die
// Frequenz bei 440 Hz (a1)
frequenz_array.push(vol_a1);
// füge diesen Wert dem frequenz_array hinzu
text("a1: "+vol_a1, 450, 20);
// gebe den Amplitudenwert für a1
aus
vol_ais1=fft.getEnergy(466); //
hole aus dem Spektrum den Amplitudenwert für die
// Frequenz bei 466 Hz (a#1)
frequenz_array.push(vol_ais1);
// füge diesen Wert dem frequenz_array hinzu
text("a#1: "+vol_ais1, 500, 20);
// gebe den Amplitudenwert für a#1
aus
vol_h1=fft.getEnergy(494); //
hole aus dem Spektrum den Amplitudenwert für die
// Frequenz bei 494 Hz (h1)
frequenz_array.push(vol_h1);
// füge diesen Wert dem frequenz_array hinzu
text("h1: "+vol_h1, 550, 20);
// gebe den Amplitudenwert für h1
aus
frequenz_index= frequenz_array.indexOf(Math.max.apply({}, frequenz_array));
// ermittle die Position (index)
der Frequenz mit dem höchsten
// Amplitudenwert im Array frequenz_array und schreibe ihn in die Variable
frequenz_index
var tonhoehe=pitch_array[frequenz_index]; //
hole aus dem oben definierten
// pitch_array über den gerade ermittelten index für die stärkste
Amplitude (frequenz_index) die
// entsprechende Tonhöhenbezeichnung und schreibe sie in die Variable
tonhoehe
noFill(); //
keine Formen farblich ausfüllen (keine Füllfarbe)
stroke(188, 48, 47); //
Strichfarbe auf dunkelrot setzen
text("Tonhöhe: "+tonhoehe, 600, 20); //
Tonhöhenbezeichnung ausgeben
}
Insgesamt
sieht das Script dann folgendermaßen aus:
<script src="header/p5.js"></script>
<script src="header/p5.sound.js"></script>
<script>
function setup()
{
var container = createCanvas(800,400);
container.parent('p5container');
mic = new p5.AudioIn();
mic.start();
fft = new p5.FFT();
fft.setInput(mic);
aufloesung = sampleRate()/fft.bins/2;
}
function draw()
{
background(255,255,255);
//Oszillogramm
zeichnen
var waveform = fft.waveform();
noFill();
stroke(225, 225, 225);
beginShape();
for (var i = 0; i < waveform.length; i++){
var x = map(i, 0, waveform.length, 0, width);
var y = map( waveform[i], -1, 1, 0, height);
vertex(x,y-100);
}
endShape();
//Spektrale Hüllkurve
zeichnen
var spectrum = fft.analyze();
noFill();
stroke(188, 48, 47);
beginShape();
for (i = 0; i < spectrum.length; i++) {
var x = map(i, 0, spectrum.length, 0, width*4);
var y = map(spectrum[i], 0, 255, height, 0);
vertex(x,y);
}
endShape();
// Finde die stärkste
Frequenz im Spektrum
noFill();
stroke(188, 48, 47);
maxfrequenz_index= spectrum.indexOf(Math.max.apply({}, spectrum));
maxfrequenz = round(maxfrequenz_index*aufloesung);
text("Max. Frequenz: "+maxfrequenz + " Hz", 600,
40);
// Finde den Spectral
Centroid
noFill();
stroke(48, 188, 47);
spectralCentroid = round(fft.getCentroid());
text("Spectral Centroid: "+spectralCentroid + " Hz",
600, 60);
rect(spectralCentroid/8, 100, width / spectrum.length, height );
// ermittle Frequenz
und Amplitude an der Mausposition
noFill();
stroke(225, 225, 225);
aktuelle_frequenz = round(map(mouseX, 0, spectrum.length, 0, width*4)*aufloesung/10);
aktueller_pegel=round(((map(mouseY, 0, 255, height, 0))+200)/6);
if (aktuelle_frequenz>=0){text(aktuelle_frequenz + " Hz",
mouseX+15, mouseY);}
text(aktueller_pegel + " %", mouseX+15, mouseY+12);
rect(mouseX, 100, width / spectrum.length, height );
rect(0, mouseY, width, width / spectrum.length );
//Spektrum zeichnen
noFill();
stroke(225, 225, 225);
for (var i = 0; i< spectrum.length; i++){
if (i == maxfrequenz_index){stroke(188, 48, 47);} else {stroke(225,
225, 225);}
var x = map(i, 0, spectrum.length, 0, width*4);
var h = -height + map(spectrum[i], 0, 255, height, 0);
rect(x, height, width / spectrum.length, h );
}
//
Tonhöhe finden und ausgeben
noStroke();
stroke(225, 225, 225);
frequenz_array=[];
vol_c1=fft.getEnergy(262);
frequenz_array.push(vol_c1);
text("c1: "+vol_c1, 0, 20);
vol_cis1=fft.getEnergy(277);
frequenz_array.push(vol_cis1);
text("c#1: "+vol_cis1, 50, 20);
vol_d1=fft.getEnergy(294);
frequenz_array.push(vol_d1);
text("d1: "+vol_d1, 100, 20);
vol_dis1=fft.getEnergy(311);
frequenz_array.push(vol_dis1);
text("d#1: "+vol_dis1, 150, 20);
vol_e1=fft.getEnergy(330);
frequenz_array.push(vol_e1);
text("e1: "+vol_e1, 200, 20);
vol_f1=fft.getEnergy(349);
frequenz_array.push(vol_f1);
text("f1: "+vol_f1, 250, 20);
vol_fis1=fft.getEnergy(370);
frequenz_array.push(vol_fis1);
text("f#1: "+vol_fis1, 300, 20);
vol_g1=fft.getEnergy(392);
frequenz_array.push(vol_g1);
text("g1: "+vol_g1, 350, 20);
vol_gis1=fft.getEnergy(415);
frequenz_array.push(vol_gis1);
text("g#1: "+vol_gis1, 400, 20);
vol_a1=fft.getEnergy(440);
frequenz_array.push(vol_a1);
text("a1: "+vol_a1, 450, 20);
vol_ais1=fft.getEnergy(466);
frequenz_array.push(vol_ais1);
text("a#1: "+vol_ais1, 500, 20);
vol_h1=fft.getEnergy(494);
frequenz_array.push(vol_h1);
text("h1: "+vol_h1, 550, 20);
frequenz_index= frequenz_array.indexOf(Math.max.apply({}, frequenz_array));
var tonhoehe=pitch_array[frequenz_index];
noFill();
stroke(188, 48, 47);
text("Tonhöhe: "+tonhoehe, 600, 20);
}
</script>
<DIV id="p5container"
style="width:800;border: 1px solid #333;box-shadow: 8px 8px 5px
#444;padding: 8px 12px;background-color:ffffff"></DIV>
|