Kombination
von Meyda und P5
- MFCCs (-> Mikrophon-Version)
Meyda arbeitet
sehr gut mit P5 und P5.sound zusammen, z.B. um MFCCs aus einem Klangbeispiel
automatisch zu extrahieren und zu visualisieren:
1.
Einbettung der Meyda- und der Plotly-Library
Zunächst
werden im <head>
der Seite die Meyda- und P5-Javascript-Bibliotheken eingebettet:
<script src="header/p5.js"></script>
<script src="header/p5.sound.js"></script>
<script src="header/meyda.min.js"></script>
2.
Script zur Steuerung eines Audioplayers
Dann wird
im <head> der Seite
ein Script zur Steuerung des Audioplayers angelegt:
<script>
function spielAudio()
{ // Funktion zum Abspielen des Audioplayers
var meinplayer = document.getElementById('audio'); //
finde über
// getElementById() den Audioplayer mit der id "audio" und übergebe
ihn an die Variable meinPlayer.
meinplayer.play(); // starte den so gefundenen
Audioplayer
}
function stopAudio()
{ // Funktion zum Stoppen des Audioplayers
var meinplayer = document.getElementById('audio'); //
finde über
// getElementById() den Audioplayer mit der id "audio" und übergebe
ihn an die Variable meinPlayer.
meinplayer.pause(); // pausiere den Player
meinplayer.currentTime = 0; // setze ihn
auf den Anfang des Stücks
}
</script>
3.
Einbettung eines Audioplayers mit id und Steuerungsmöglichkeit
Im <body>
der Seite wird der Player angelegt:
<audio id="audio"
src="klang/fagott_toene.mp3"> </audio>
4.
Einbettung eines Bereichs, in dem die Daten visualisiert werden sollen
Unter dem
Audioplayer wird innerhalb eines <DIV>-Tag
ein Bereich (hier mit der id "p5container") festgelegt, in dem
die Audiodaten mit Hilfe von P5 visualisiert werden sollen:
<DIV id="p5container"
style="width:800;border: 1px solid #333;box-shadow: 8px 8px 5px #444;padding:
8px 12px;background-color:ffffff"></DIV>
5.
Starten des WebAudioApi und Audio-Analyse mit Meyda
Darunter
wird das Script für die Analyse eingebaut.
<script defer> bedeutet, dass das Script nicht seriell im
Seitenaufbau eingebaut ist, sondern parallel zu den anderen Scripten und
Funktionen auf der Seite passiert:
<script defer>
const audioContext = new AudioContext(); //
erzeuge einen Audiokontext (ähnlich wie
// einen Canvas) für das WebAudioApi
const htmlAudioElement = document.getElementById("audio"); //
suche den
// Audioplayer via getElementById() und weise ihn der Variablen htmlAudioElement
zu.
const source = audioContext.createMediaElementSource(htmlAudioElement);
// füge den Audioplayer über
die Variable htmlAudioElement als Mediaquelle dem audioContext hinzu.
source.connect(audioContext.destination); //
verbinde den audioContext mit der
// Soundausgabe des Browsers/Computers.
mfccfaktor = audioContext.sampleRate/512; //
Faktor mit dem die Werte der MFCCs
// multipliziert werden müssen
if (typeof Meyda ===
"undefined") { // falls die Meyda-Library
nicht erkannt wird:
alert("Meyda konnte nicht gefunden werden, wurde die Library nicht
eingebunden?"); // gebe eine Fehlermeldung
aus
} else { // in allen anderen Fällen:
const analyzer = Meyda.createMeydaAnalyzer({ //
erstelle einen Analyzer mit
// folgenden Eigenschaften:
"audioContext": audioContext, // audioContext,
auf den sich der Analyzer bezieht
"source": source, // Quelle auf
die sich der Analyzer bezieht (= der Audioplayer)
"bufferSize": 512, // Buffergröße
"featureExtractors": ["mfcc",], //
Array (Liste) von zu analysierenden Eigenschaften
// (mfcc ist ein Array mit den Zahlenwerten für die 13 Mel-Frequency
Cepstral Coefficients)
"callback": features => { //
Ausgabe der ermittelten Features
mfcc1 = Math.round(features.mfcc[0]/mfccfaktor * 1000)/1000; //
runde den
// ersten Wert aus dem mfcc-Array auf 3 Stellen, multipliziere ihn mit
dem mfccfaktor und übergebe
// ihn an die Variable mfcc1
mfcc2 = Math.round(features.mfcc[1]/mfccfaktor * 1000)/1000; //
runde den
// zweiten Wert aus dem mfcc-Array auf 3 Stellen, multipliziere ihn mit
dem mfccfaktor und übergebe
// ihn an die Variable mfcc2
mfcc3 = Math.round(features.mfcc[2]/mfccfaktor * 1000)/1000; //
runde den
// dritten Wert aus dem mfcc-Array auf 3 Stellen, multipliziere ihn mit
dem mfccfaktor und übergebe
// ihn an die Variable mfcc3
mfcc4 = Math.round(features.mfcc[3]/mfccfaktor * 1000)/1000; //
runde den
// vierten Wert aus dem mfcc-Array auf 3 Stellen, multipliziere ihn mit
dem mfccfaktor und übergebe
// ihn an die Variable mfcc4
mfcc5 = Math.round(features.mfcc[4]/mfccfaktor * 1000)/1000; //
runde den
// fünften Wert aus dem mfcc-Array auf 3 Stellen, multipliziere ihn
mit dem mfccfaktor und übergebe
// ihn an die Variable mfcc5
mfcc6 = Math.round(features.mfcc[5]/mfccfaktor * 1000)/1000; //
runde den
// sechsten Wert aus dem mfcc-Array auf 3 Stellen, multipliziere ihn mit
dem mfccfaktor und
// übergebe ihn an die Variable mfcc6
mfcc7 = Math.round(features.mfcc[6]/mfccfaktor * 1000)/1000; //
runde den
// siebten Wert aus dem mfcc-Array auf 3 Stellen, multipliziere ihn mit
dem mfccfaktor und übergebe
// ihn an die Variable mfcc7
mfcc8 = Math.round(features.mfcc[7]/mfccfaktor * 1000)/1000; //
runde den
// achten Wert aus dem mfcc-Array auf 3 Stellen, multipliziere ihn mit
dem mfccfaktor und übergebe
// ihn an die Variable mfcc8
mfcc9 = Math.round(features.mfcc[8]/mfccfaktor * 1000)/1000; //
runde den
// neunten Wert aus dem mfcc-Array auf 3 Stellen, multipliziere ihn mit
dem mfccfaktor und übergebe
// ihn an die Variable mfcc9
mfcc10 = Math.round(features.chroma[9]/mfccfaktor * 1000)/1000; //
runde
// den zehnten Wert aus dem mfcc-Array auf 3 Stellen, multipliziere ihn
mit dem mfccfaktor und
// übergebe ihn an die Variable mfcc10
mfcc11 = Math.round(features.mfcc[10]/mfccfaktor * 1000)/1000; //
runde
// den elften Wert aus dem mfcc-Array auf 3 Stellen, multipliziere ihn
mit dem mfccfaktor und
// übergebe ihn an die Variable mfcc11
mfcc12 = Math.round(features.mfcc[11]/mfccfaktor * 1000)/1000; //
runde
// den zwölften Wert aus dem mfcc-Array auf 3 Stellen, multipliziere
ihn mit dem mfccfaktor und
// übergebe ihn an die Variable mfcc12
mfcc13 = Math.round(features.mfcc[12]/mfccfaktor * 1000)/1000; //
runde
// den dreizehnten Wert aus dem mfcc-Array auf 3 Stellen, multipliziere
ihn mit dem mfccfaktor und
// übergebe ihn an die Variable mfcc13
}
});
analyzer.start(); // starte den Analyzer
}
</script>
6.
Setup- und Draw-Funktion für P5 erstellen
Auf der Grundlage
der mit Meyda ermittelten Chroma-Werte kann nun mit Hilfe von P5 mit der
Visualisierung begonnen werden. Wie in den meisten P5-Anwendungen benötigt
man hierzu eine setup()-
und eine draw()-Funktion
sowie am Ende eine Funktion, die das Starten und Stoppen des Audioplayers
steuert (togglePlay()):
function setup(){
var container = createCanvas(800,400); //
Canvas erstellen
container.parent('p5container'); // an
DIV-Container anhängen
container.mouseClicked(togglePlay); //
auf Klick auf Canvas mit Funktion togglePlay
// reagieren
}
function draw(){
background(255, 255, 255); // zeichne weißen
Hintergrund
cursor(HAND); // zeige den Cursor als Hand
noStroke(); // keine Strichfarbe
fill(188, 188, 188); // Füllfarbe
auf hellgrau setzen
textSize(12); // Textgröße auf
12 setzen
text("MFCC1: " + mfcc1, 20, 20);
// Text für mfcc1-Wert erstellen
text("MFCC2: " + mfcc2, 20, 40);
// Text für mfcc2-Wert erstellen
text("MFCC3: " + mfcc3, 20, 60);
// Text für mfcc3-Wert erstellen
text("MFCC4: " + mfcc4, 20, 80);
// Text für mfcc4-Wert erstellen
text("MFCC5: " + mfcc5, 20, 100);
// Text für mfcc5-Wert erstellen
text("MFCC6: " + mfcc6, 20, 120);
// Text für mfcc6-Wert erstellen
text("MFCC7: " + mfcc7, 20, 140);
// Text für mfcc7-Wert erstellen
text("MFCC8: " + mfcc8, 20, 160);
// Text für mfcc8-Wert erstellen
text("MFCC9: " + mfcc9, 20, 180);
// Text für mfcc9-Wert erstellen
text("MFCC10: " + mfcc10, 20, 200);
// Text für mfcc10-Wert erstellen
text("MFCC11: " + mfcc11, 20, 220);
// Text für mfcc11-Wert erstellen
text("MFCC12: " + mfcc12, 20, 240);
// Text für mfcc12-Wert erstellen
text("MFCC13: " + mfcc13, 20, 260);
// Text für mfcc13-Wert erstellen
fill(188, 48, 47); // Füllfarbe auf
dunkelrot setzen
c_mfcc1 = map(mfcc1,
-1, 1, 0, height); // skaliere den Wert
zwischen -1 und 1 von
// mfcc1 so, dass er zwischen 0 und der Höhe des Canvas ist und weise
ihn der Variablen c_mfcc1 zu
rect(100, height/2, 30, height/2-c_mfcc1); //
erstelle ein Rechteck, deren Höhe von
// der Variablen c_mfcc1 abhängig ist.
c_mfcc2 = map(mfcc2, -1, 1, 0, height); //
skaliere den Wert zwischen -1 und 1 von
// mfcc2 so, dass er zwischen 0 und der Höhe des Canvas ist und weise
ihn der Variablen c_mfcc2 zu
rect(150, height/2, 30, height/2-c_mfcc2); //
erstelle ein Rechteck, deren Höhe von
// der Variablen c_mfcc2 abhängig ist.
c_mfcc3 = map(mfcc3, -1, 1, 0, height); //
skaliere den Wert zwischen -1 und 1 von
// mfcc3 so, dass er zwischen 0 und der Höhe des Canvas ist und weise
ihn der Variablen c_mfcc3 zu
rect(200, height/2, 30, height/2-c_mfcc3); //
erstelle ein Rechteck, deren Höhe von
// der Variablen c_mfcc3 abhängig ist.
c_mfcc4 = map(mfcc4, -1, 1, 0, height); //
skaliere den Wert zwischen -1 und 1 von
// mfcc4 so, dass er zwischen 0 und der Höhe des Canvas ist und weise
ihn der Variablen c_mfcc4 zu
rect(250, height/2, 30, height/2-c_mfcc4); //
erstelle ein Rechteck, deren Höhe von
// der Variablen c_mfcc4 abhängig ist.
c_mfcc5 = map(mfcc5, -1, 1, 0, height); //
skaliere den Wert zwischen -1 und 1 von
// mfcc5 so, dass er zwischen 0 und der Höhe des Canvas ist und weise
ihn der Variablen c_mfcc5 zu
rect(300, height/2, 30, height/2-c_mfcc5); //
erstelle ein Rechteck, deren Höhe von
// der Variablen c_mfcc5 abhängig ist.
c_mfcc6 = map(mfcc6, -1, 1, 0, height); //
skaliere den Wert zwischen -1 und 1 von
// mfcc6 so, dass er zwischen 0 und der Höhe des Canvas ist und weise
ihn der Variablen c_mfcc6 zu
rect(350, height/2, 30, height/2-c_mfcc6); //
erstelle ein Rechteck, deren Höhe von
// der Variablen c_mfcc6 abhängig ist.
c_mfcc7 = map(mfcc7, -1, 1, 0, height); //
skaliere den Wert zwischen -1 und 1 von
// mfcc7 so, dass er zwischen 0 und der Höhe des Canvas ist und weise
ihn der Variablen c_mfcc7 zu
rect(400, height/2, 30, height/2-c_mfcc7); //
erstelle ein Rechteck, deren Höhe von
// der Variablen c_mfcc7 abhängig ist.
c_mfcc8 = map(mfcc8, -1, 1, 0, height); //
skaliere den Wert zwischen -1 und 1 von
// mfcc8 so, dass er zwischen 0 und der Höhe des Canvas ist und weise
ihn der Variablen c_mfcc8 zu
rect(450, height/2, 30, height/2-c_mfcc8); //
erstelle ein Rechteck, deren Höhe von
// der Variablen c_mfcc8 abhängig ist.
c_mfcc9 = map(mfcc9, -1, 1, 0, height); //
skaliere den Wert zwischen -1 und 1 von
// mfcc9 so, dass er zwischen 0 und der Höhe des Canvas ist und weise
ihn der Variablen c_mfcc9 zu
rect(500, height/2, 30, height/2-c_mfcc9); //
erstelle ein Rechteck, deren Höhe von
// der Variablen c_mfcc9 abhängig ist.
c_mfcc10 = map(mfcc10, -1, 1, 0, height); //
skaliere den Wert zwischen -1 und 1
// von mfcc10 so, dass er zwischen 0 und der Höhe des Canvas ist
und weise ihn der Variablen
// c_mfcc10 zu
rect(550, height/2, 30, height/2-c_mfcc10); //
erstelle ein Rechteck, deren Höhe
// von der Variablen c_mfcc10 abhängig ist.
c_mfcc11 = map(mfcc11, -1, 1, 0, height); //
skaliere den Wert zwischen -1 und 1
// von mfcc11 so, dass er zwischen 0 und der Höhe des Canvas ist
und weise ihn der Variablen
// c_mfcc11 zu
rect(600, height/2, 30, height/2-c_mfcc11); //
erstelle ein Rechteck, deren Höhe
// von der Variablen c_mfcc11 abhängig ist.
c_mfcc12 = map(mfcc12, -1, 1, 0, height); //
skaliere den Wert zwischen -1 und 1
// von mfcc12 so, dass er zwischen 0 und der Höhe des Canvas ist
und weise ihn der Variablen
// c_mfcc12 zu
rect(650, height/2, 30, height/2-c_mfcc12); //
erstelle ein Rechteck, deren Höhe
// von der Variablen c_mfcc12 abhängig ist.
c_mfcc13 = map(mfcc13, -1, 1, 0, height); //
skaliere den Wert zwischen -1 und 1
// von mfcc13 so, dass er zwischen 0 und der Höhe des Canvas ist
und weise ihn der Variablen
// c_mfcc13 zu
rect(700, height/2, 30, height/2-c_mfcc13); //
erstelle ein Rechteck, deren Höhe
// von der Variablen c_mfcc13 abhängig ist.
fill(0, 0, 0); //
Füllfarbe schwarz
text("MFCC1", 100, 210); text("MFCC2", 150, 210);
text("MFCC3", 200, 210); text("MFCC4", 250, 210);
text("MFCC5", 300, 210); text("MFCC6", 350, 210);
text("MFCC7", 400, 210); text("MFCC8", 450, 210);
text("MFCC9", 500, 210); text("MFCC10", 550, 210);
text("MFCC11", 600, 210); text("MFCC12", 650, 210);
text("MFCC13", 700, 210);
// Positioniere die Texte für die
einzelnen MFCCs
noFill(); // keine Füllfarbe
}
function togglePlay()
{ // Steuerung des Audioplayers
meinplayer = document.getElementById('audio'); //
finde über
// getElementById() den Audioplayer mit der id "audio" und übergebe
ihn an die Variable meinplayer.
if (!meinplayer.paused) { // wenn der Audioplayer
nicht pausiert
stopAudio(); // pausiere ihn
} else { // in allen anderen Fällen
spielAudio(); // starte ihn
}
}
</script>
Insgesamt
sieht das Script dann folgendermaßen aus:
<head>
<script src="header/p5.js"></script>
<script src="header/p5.sound.js"></script>
<script src="header/meyda.min.js"></script>
<script>
function spielAudio() {
var meinplayer = document.getElementById('audio');
meinplayer.play();
}
function stopAudio() {
var meinplayer = document.getElementById('audio');
meinplayer.pause();
meinplayer.currentTime = 0;
}
</script>
</head>
<body text="#000000" bgcolor="#FFFFFF">
<audio id="audio"
src="klang/fagott_toene.mp3" > </audio>
<DIV id="p5container" style="width:800;border: 1px
solid #333;box-shadow: 8px 8px 5px #444;padding: 8px 12px;background-color:ffffff"></DIV>
<script defer>
const audioContext = new AudioContext();
const htmlAudioElement = document.getElementById('audio');
const source = audioContext.createMediaElementSource(htmlAudioElement);
source.connect(audioContext.destination);
mfccfaktor = audioContext.sampleRate/512;
if (typeof Meyda === "undefined") {
alert("Meyda konnte nicht gefunden werden, wurde die Library nicht
eingebunden?");
} else {
const analyzer = Meyda.createMeydaAnalyzer({
"audioContext": audioContext,
"source": source,
"bufferSize": 512,
"featureExtractors": ["mfcc"],
"callback": features => {
mfcc1 = Math.round(features.mfcc[0]/mfccfaktor* 1000)/1000;
mfcc2 = Math.round(features.mfcc[1]/mfccfaktor* 1000)/1000;
mfcc3 = Math.round(features.mfcc[2]/mfccfaktor* 1000)/1000;
mfcc4 = Math.round(features.mfcc[3]/mfccfaktor* 1000)/1000;
mfcc5 = Math.round(features.mfcc[4]/mfccfaktor* 1000)/1000;
mfcc6 = Math.round(features.mfcc[5]/mfccfaktor* 1000)/1000;
mfcc7 = Math.round(features.mfcc[6]/mfccfaktor* 1000)/1000;
mfcc8 = Math.round(features.mfcc[7]/mfccfaktor* 1000)/1000;
mfcc9 = Math.round(features.mfcc[8]/mfccfaktor* 1000)/1000;
mfcc10 = Math.round(features.mfcc[9]/mfccfaktor* 1000)/1000;
mfcc11 = Math.round(features.mfcc[10]/mfccfaktor* 1000)/1000;
mfcc12 = Math.round(features.mfcc[11]/mfccfaktor* 1000)/1000;
mfcc13 = Math.round(features.mfcc[12]/mfccfaktor* 1000)/1000;
}
});
analyzer.start();
}
function setup(){
var container = createCanvas(800,400);
container.parent('p5container');
container.mouseClicked(togglePlay);
}
function draw(){
background(255, 255, 255);
cursor(HAND);
noStroke();
fill(188, 188, 188); textSize(12);
text("MFCC1: " + mfcc1, 20, 20);
text("MFCC2: " + mfcc2, 20, 40);
text("MFCC3: " + mfcc3, 20, 60);
text("MFCC4: " + mfcc4, 20, 80);
text("MFCC5: " + mfcc5, 20, 100);
text("MFCC6: " + mfcc6, 20, 120);
text("MFCC7: " + mfcc7, 20, 140);
text("MFCC8: " + mfcc8, 20, 160);
text("MFCC9: " + mfcc9, 20, 180);
text("MFCC10: " + mfcc10, 20, 200);
text("MFCC11: " + mfcc11, 20, 220);
text("MFCC12: " + mfcc12, 20, 240);
text("MFCC13: " + mfcc13, 20, 260);
fill(188, 48, 47);
c_mfcc1 = map(mfcc1, -1, 1, 0, height);
rect(100, height/2, 30, height/2-c_mfcc1);
c_mfcc2 = map(mfcc2, -1, 1, 0, height);
rect(150, height/2, 30, height/2-c_mfcc2);
c_mfcc3 = map(mfcc3, -1, 1, 0, height);
rect(200, height/2, 30, height/2-c_mfcc3);
c_mfcc4 = map(mfcc4, -1, 1, 0, height);
rect(250, height/2, 30, height/2-c_mfcc4);
c_mfcc5 = map(mfcc5, -1, 1, 0, height);
rect(300, height/2, 30, height/2-c_mfcc5);
c_mfcc6 = map(mfcc6, -1, 1, 0, height);
rect(350, height/2, 30, height/2-c_mfcc6);
c_mfcc7 = map(mfcc7, -1, 1, 0, height);
rect(400, height/2, 30, height/2-c_mfcc7);
c_mfcc8 = map(mfcc8, -1, 1, 0, height);
rect(450, height/2, 30, height/2-c_mfcc8);
c_mfcc9 = map(mfcc9, -1, 1, 0, height);
rect(500, height/2, 30, height/2-c_mfcc9);
c_mfcc10 = map(mfcc10, -1, 1, 0, height);
rect(550, height/2, 30, height/2-c_mfcc10);
c_mfcc11 = map(mfcc11, -1, 1, 0, height);
rect(600, height/2, 30, height/2-c_mfcc11);
c_mfcc12 = map(mfcc12, -1, 1, 0, height);
rect(650, height/2, 30, height/2-c_mfcc12);
c_mfcc13 = map(mfcc13, -1, 1, 0, height);
rect(700, height/2, 30, height/2-c_mfcc13);
fill(0, 0, 0);
text("MFCC1", 100, 210); text("MFCC2", 150, 210);
text("MFCC3", 200, 210); text("MFCC4", 250, 210);
text("MFCC5", 300, 210); text("MFCC6", 350, 210);
text("MFCC7", 400, 210); text("MFCC8", 450, 210);
text("MFCC9", 500, 210); text("MFCC10", 550, 210);
text("MFCC11", 600, 210); text("MFCC12", 650, 210)
;text("MFCC13", 700, 210);
noFill();
}
function togglePlay()
{
meinplayer = document.getElementById('audio');
if (!meinplayer.paused) {
stopAudio();
} else {
spielAudio();
}
}
</script>
</body>
|