Kombination
von Meyda und P5
- Chroma (-> Mikrophon-Version)
Meyda arbeitet
sehr gut mit P5 und P5.sound zusammen, z.B. um die Tonklassen (Chroma)
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.
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": ["chroma",], //
Array (Liste) von zu analysierenden Eigenschaften
// (chroma ist ein Array mit den Zahlenwerten für die 12 Tonklassen)
"callback": features => { //
Ausgabe der ermittelten Features
chroma_c = Math.round(features.chroma[0]* 1000)/1000; //
runde den ersten Wert
// aus dem chroma-Array auf 3 Stellen und übergebe ihn an die Variable
chroma_c
chroma_cis = Math.round(features.chroma[1]* 1000)/1000; //
runde den zweiten
// Wert aus dem chroma-Array auf 3 Stellen und übergebe ihn an die
Variable chroma_cis
chroma_d = Math.round(features.chroma[2]* 1000)/1000; //
runde den dritten
// Wert aus dem chroma-Array auf 3 Stellen und übergebe ihn an die
Variable chroma_d
chroma_dis = Math.round(features.chroma[3]* 1000)/1000; //
runde den vierten
// Wert aus dem chroma-Array auf 3 Stellen und übergebe ihn an die
Variable chroma_dis
chroma_e = Math.round(features.chroma[4]* 1000)/1000; //
runde den fünften
// Wert aus dem chroma-Array auf 3 Stellen und übergebe ihn an die
Variable chroma_e
chroma_f = Math.round(features.chroma[5]* 1000)/1000; //
runde den sechsten
// Wert aus dem chroma-Array auf 3 Stellen und übergebe ihn an die
Variable chroma_f
chroma_fis = Math.round(features.chroma[6]* 1000)/1000; //
runde den siebten
// Wert aus dem chroma-Array auf 3 Stellen und übergebe ihn an die
Variable chroma_fis
chroma_g = Math.round(features.chroma[7]* 1000)/1000; //
runde den achten
// Wert aus dem chroma-Array auf 3 Stellen und übergebe ihn an die
Variable chroma_g
chroma_gis = Math.round(features.chroma[8]* 1000)/1000; //
runde den neunten
// Wert aus dem chroma-Array auf 3 Stellen und übergebe ihn an die
Variable chroma_gis
chroma_a = Math.round(features.chroma[9]* 1000)/1000; //
runde den zehnten
// Wert aus dem chroma-Array auf 3 Stellen und übergebe ihn an die
Variable chroma_a
chroma_ais = Math.round(features.chroma[10]* 1000)/1000; //
runde den elften
// Wert aus dem chroma-Array auf 3 Stellen und übergebe ihn an die
Variable chroma_ais
chroma_h = Math.round(features.chroma[11]* 1000)/1000; //
runde den zwölften
// Wert aus dem chroma-Array auf 3 Stellen und übergebe ihn an die
Variable chroma_h
}
});
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, 48, 47); // Füllfarbe auf
dunkelrot setzen
textSize(12); // Textgröße auf
12 setzen
text("C: " + chroma_c, 20, 20); //
Text für die Ausgabe der Tonklasse c erstellen
text("Cis: " + chroma_cis, 20, 40); //
Text für die Ausgabe der Tonklasse cis
// erstellen
text("D: " + chroma_d, 20, 60); //
Text für die Ausgabe der Tonklasse d erstellen
text("Dis: " + chroma_dis, 20, 80); //
Text für die Ausgabe der Tonklasse dis
//
erstellen
text("E: " + chroma_e, 20, 100); //
Text für die Ausgabe der Tonklasse e erstellen
text("F: " + chroma_f, 20, 120); //
Text für die Ausgabe der Tonklasse f erstellen
text("Fis: " + chroma_fis, 20, 140); //
Text für die Ausgabe der Tonklasse fis
//
erstellen
text("G: " + chroma_g, 20, 160); //
Text für die Ausgabe der Tonklasse g erstellen
text("Gis: " + chroma_gis, 20, 180); //
Text für die Ausgabe der Tonklasse gis
//
erstellen
text("A: " + chroma_a, 20, 200); //
Text für die Ausgabe der Tonklasse a erstellen
text("Ais: " + chroma_ais, 20, 220); //
Text für die Ausgabe der Tonklasse ais
//
erstellen
text("H: " + chroma_h, 20, 240); //
Text für die Ausgabe der Tonklasse h erstellen
if(chroma_c==1){ textSize(24);
fill(188, 48, 47); text("C", 20, 300);} else {fill(188, 188,
188); } // wenn der Wert für chroma_c
= 1 ist, dann färbe den
// Balken und die Schrift dunkelrot, sonst lasse sie grau
c_r = map(chroma_c, 0, 1, 0, height); //
skaliere den Wert zwischen 0 und 1 von
// chroma_c so, dass er zwischen 0 und der Höhe des Canvas ist und
weise ihn der Variablen c_r zu
rect(100, height-c_r+50, 30, height); //
erstelle ein Rechteck, deren Höhe von der
// Variablen c_r abhängig ist.
if(chroma_cis==1){ textSize(24); fill(188, 48, 47); text("Cis",
20, 300);} else {fill(188, 188, 188); } //
wenn der Wert für chroma_cis = 1 ist, dann
// färbe den Balken und die Schrift dunkelrot, sonst lasse sie grau
cis_r = map(chroma_cis, 0, 1, 0, height); //
skaliere den Wert zwischen 0 und 1 von
// chroma_cis so, dass er zwischen 0 und der Höhe des Canvas ist
und weise ihn der Variablen cis_r
// zu
rect(150, height-cis_r+50, 30, height); //
erstelle ein Rechteck, deren Höhe von der
// Variablen cis_r abhängig ist.
if(chroma_d==1){ textSize(24); fill(188, 48, 47); text("D",
20, 300);} else {fill(188, 188, 188); } //
wenn der Wert für chroma_d = 1 ist, dann färbe den
// Balken und die Schrift dunkelrot, sonst lasse sie grau
d_r = map(chroma_d, 0, 1, 0, height); //
skaliere den Wert zwischen 0 und 1 von
// chroma_d so, dass er zwischen 0 und der Höhe des Canvas ist und
weise ihn der Variablen d_r zu
rect(200, height-d_r+50, 30, height); //
erstelle ein Rechteck, deren Höhe von der
// Variablen d_r abhängig ist.
if(chroma_dis==1){ textSize(24); fill(188, 48, 47); text("Dis",
20, 300);} else {fill(188, 188, 188); } //
wenn der Wert für chroma_dis = 1 ist, dann
// färbe den Balken und die Schrift dunkelrot, sonst lasse sie grau
dis_r = map(chroma_dis, 0, 1, 0, height);
// skaliere den Wert zwischen 0 und 1 von
// chroma_dis so, dass er zwischen 0 und der Höhe des Canvas ist
und weise ihn der Variablen dis_r
// zu
rect(250, height-dis_r+50, 30, height); //
erstelle ein Rechteck, deren Höhe von der
// Variablen dis_r abhängig ist.
if(chroma_e==1){ textSize(24); fill(188, 48, 47); text("E",
20, 300);} else {fill(188, 188, 188); } //
wenn der Wert für chroma_e = 1 ist, dann färbe den
// Balken und die Schrift dunkelrot, sonst lasse sie grau
e_r = map(chroma_e, 0, 1, 0, height); //
skaliere den Wert zwischen 0 und 1 von
// chroma_e so, dass er zwischen 0 und der Höhe des Canvas ist und
weise ihn der Variablen e_r zu
rect(300, height-e_r+50, 30, height); //
erstelle ein Rechteck, deren Höhe von der
// Variablen e_r abhängig ist.
if(chroma_f==1){ textSize(24); fill(188, 48, 47); text("F",
20, 300);} else {fill(188, 188, 188); } //
wenn der Wert für chroma_f = 1 ist, dann färbe den
// Balken und die Schrift dunkelrot, sonst lasse sie grau
f_r = map(chroma_f, 0, 1, 0, height); //
skaliere den Wert zwischen 0 und 1 von
// chroma_f so, dass er zwischen 0 und der Höhe des Canvas ist und
weise ihn der Variablen f_r zu
rect(350, height-f_r+50, 30, height); //
erstelle ein Rechteck, deren Höhe von der
// Variablen f_r abhängig ist.
if(chroma_fis==1){ textSize(24); fill(188, 48, 47); text("Fis",
20, 300);} else {fill(188, 188, 188); } //
wenn der Wert für chroma_fis = 1 ist, dann
// färbe den Balken und die Schrift dunkelrot, sonst lasse sie grau
fis_r = map(chroma_fis, 0, 1, 0, height);
// skaliere den Wert zwischen 0 und 1 von
// chroma_fis so, dass er zwischen 0 und der Höhe des Canvas ist
und weise ihn der Variablen fis_r
// zu
rect(400, height-fis_r+50, 30, height);
//
erstelle ein Rechteck, deren Höhe von der
// Variablen fis_r abhängig ist.
if(chroma_g==1){ textSize(24); fill(188, 48, 47); text("G",
20, 300);} else {fill(188, 188, 188); } //
wenn der Wert für chroma_g = 1 ist, dann färbe den
// Balken und die Schrift dunkelrot, sonst lasse sie grau
g_r = map(chroma_g, 0, 1, 0, height); //
skaliere den Wert zwischen 0 und 1 von
// chroma_g so, dass er zwischen 0 und der Höhe des Canvas ist und
weise ihn der Variablen g_r zu
rect(450, height-g_r+50, 30, height); //
erstelle ein Rechteck, deren Höhe von der
// Variablen g_r abhängig ist.
if(chroma_gis==1){ textSize(24); fill(188, 48, 47); text("Gis",
20, 300);} else {fill(188, 188, 188); } //
wenn der Wert für chroma_gis = 1 ist, dann
// färbe den Balken und die Schrift dunkelrot, sonst lasse sie grau
gis_r = map(chroma_gis, 0, 1, 0, height); //
skaliere den Wert zwischen 0 und 1 von
// chroma_gis so, dass er zwischen 0 und der Höhe des Canvas ist
und weise ihn der Variablen gis_r
// zu
rect(500, height-gis_r+50, 30, height); //
erstelle ein Rechteck, deren Höhe von der
// Variablen gis_r abhängig ist.
if(chroma_a==1){ textSize(24); fill(188, 48, 47); text("A",
20, 300);} else {fill(188, 188, 188); } //
wenn der Wert für chroma_a = 1 ist, dann färbe den
// Balken und die Schrift dunkelrot, sonst lasse sie grau
a_r = map(chroma_a, 0, 1, 0, height); //
skaliere den Wert zwischen 0 und 1 von
// chroma_a so, dass er zwischen 0 und der Höhe des Canvas ist und
weise ihn der Variablen a_r zu
rect(550, height-a_r+50, 30, height); //
erstelle ein Rechteck, deren Höhe von der
// Variablen a_r abhängig ist.
if(chroma_ais==1){ textSize(24); fill(188, 48, 47); text("Ais",
20, 300);} else {fill(188, 188, 188); } //
wenn der Wert für chroma_ais = 1 ist, dann
// färbe den Balken und die Schrift dunkelrot, sonst lasse sie grau
ais_r = map(chroma_ais, 0, 1, 0, height); //
skaliere den Wert zwischen 0 und 1 von
// chroma_ais so, dass er zwischen 0 und der Höhe des Canvas ist
und weise ihn der Variablen ais_r
// zu
rect(600, height-ais_r+50, 30, height); //
erstelle ein Rechteck, deren Höhe von der
// Variablen ais_r abhängig ist.
if(chroma_h==1){ textSize(24); fill(188, 48, 47); text("H",
20, 300);} else {fill(188, 188, 188); } //
wenn der Wert für chroma_h = 1 ist, dann färbe den
// Balken und die Schrift dunkelrot, sonst lasse sie grau
h_r = map(chroma_h, 0, 1, 0, height); //
skaliere den Wert zwischen 0 und 1 von
// chroma_h so, dass er zwischen 0 und der Höhe des Canvas ist und
weise ihn der Variablen h_r zu
rect(650, height-h_r+50, 30, height); //
erstelle ein Rechteck, deren Höhe von der
// Variablen h_r abhängig ist.
textSize(24); //
Setze die Textgröße auf 24
fill(0, 0, 0); // Füllfarbe schwarz
text("C", 100, 395);text("Cis", 150, 395);text("D",
200, 395);text("Dis", 250, 395);text("E", 300, 395);text("F",
350, 395); text("Fis", 400, 395);text("G", 450, 395);text("Gis",
500, 395);text("A", 550, 395);text("Ais", 600, 395);text("H",
650, 395);
// Positioniere die Texte für die
einzelnen Tonklassennamen
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 = window.AudioContext || window.webkitAudioContext;
const audioContext = new AudioContext();
const htmlAudioElement = document.getElementById('audio');
const source = audioContext.createMediaElementSource(htmlAudioElement);
source.connect(audioContext.destination);
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": ["chroma",],
"callback": features => {
chroma_c = Math.round(features.chroma[0]* 1000)/1000;
chroma_cis = Math.round(features.chroma[1]* 1000)/1000;
chroma_d = Math.round(features.chroma[2]* 1000)/1000;
chroma_dis = Math.round(features.chroma[3]* 1000)/1000;
chroma_e = Math.round(features.chroma[4]* 1000)/1000;
chroma_f = Math.round(features.chroma[5]* 1000)/1000;
chroma_fis = Math.round(features.chroma[6]* 1000)/1000;
chroma_g = Math.round(features.chroma[7]* 1000)/1000;
chroma_gis = Math.round(features.chroma[8]* 1000)/1000;
chroma_a = Math.round(features.chroma[9]* 1000)/1000;
chroma_ais = Math.round(features.chroma[10]* 1000)/1000;
chroma_h = Math.round(features.chroma[11]* 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, 48, 47); textSize(12);
text("C: " + chroma_c, 20, 20);
text("Cis: " + chroma_cis, 20, 40);
text("D: " + chroma_d, 20, 60);
text("Dis: " + chroma_dis, 20, 80);
text("E: " + chroma_e, 20, 100);
text("F: " + chroma_f, 20, 120);
text("Fis: " + chroma_fis, 20, 140);
text("G: " + chroma_g, 20, 160);
text("Gis: " + chroma_gis, 20, 180);
text("A: " + chroma_a, 20, 200);
text("Ais: " + chroma_ais, 20, 220);
text("H: " + chroma_h, 20, 240);
if(chroma_c==1){ textSize(24); fill(188, 48, 47); text("C",
20, 300);} else {fill(188, 188, 188); }
c_r = map(chroma_c, 0, 1, 0, height);
rect(100, height-c_r+50, 30, height);
if(chroma_cis==1){ textSize(24); fill(188, 48, 47); text("Cis",
20, 300);} else {fill(188, 188, 188); }
cis_r = map(chroma_cis, 0, 1, 0, height);
rect(150, height-cis_r+50, 30, height);
if(chroma_d==1){ textSize(24); fill(188, 48, 47); text("D",
20, 300);} else {fill(188, 188, 188); }
d_r = map(chroma_d, 0, 1, 0, height);
rect(200, height-d_r+50, 30, height);
if(chroma_dis==1){ textSize(24); fill(188, 48, 47); text("Dis",
20, 300);} else {fill(188, 188, 188); }
dis_r = map(chroma_dis, 0, 1, 0, height);
rect(250, height-dis_r+50, 30, height);
if(chroma_e==1){ textSize(24); fill(188, 48, 47); text("E",
20, 300);} else {fill(188, 188, 188); }
e_r = map(chroma_e, 0, 1, 0, height);
rect(300, height-e_r+50, 30, height);
if(chroma_f==1){ textSize(24); fill(188, 48, 47); text("F",
20, 300);} else {fill(188, 188, 188); }
f_r = map(chroma_f, 0, 1, 0, height);
rect(350, height-f_r+50, 30, height);
if(chroma_fis==1){ textSize(24); fill(188, 48, 47); text("Fis",
20, 300);} else {fill(188, 188, 188); }
fis_r = map(chroma_fis, 0, 1, 0, height);
rect(400, height-fis_r+50, 30, height);
if(chroma_g==1){ textSize(24); fill(188, 48, 47); text("G",
20, 300);} else {fill(188, 188, 188); }
g_r = map(chroma_g, 0, 1, 0, height);
rect(450, height-g_r+50, 30, height);
if(chroma_gis==1){ textSize(24); fill(188, 48, 47); text("Gis",
20, 300);} else {fill(188, 188, 188); }
gis_r = map(chroma_gis, 0, 1, 0, height);
rect(500, height-gis_r+50, 30, height);
if(chroma_a==1){ textSize(24); fill(188, 48, 47); text("A",
20, 300);} else {fill(188, 188, 188); }
a_r = map(chroma_a, 0, 1, 0, height);
rect(550, height-a_r+50, 30, height);
if(chroma_ais==1){ textSize(24); fill(188, 48, 47); text("Ais",
20, 300);} else {fill(188, 188, 188); }
ais_r = map(chroma_ais, 0, 1, 0, height);
rect(600, height-ais_r+50, 30, height);
if(chroma_h==1){ textSize(24); fill(188, 48, 47); text("H",
20, 300);} else {fill(188, 188, 188); }
h_r = map(chroma_h, 0, 1, 0, height);
rect(650, height-h_r+50, 30, height);
textSize(24); fill(0,
0, 0);
text("C", 100, 395);text("Cis", 150, 395);text("D",
200, 395);text("Dis", 250, 395);text("E", 300, 395);text("F",
350, 395); text("Fis", 400, 395);text("G", 450,
395);text("Gis", 500, 395);text("A", 550, 395);text("Ais",
600, 395);text("H", 650, 395);
noFill();
}
function togglePlay()
{
meinplayer = document.getElementById('audio');
if (!meinplayer.paused) {
stopAudio();
} else {
spielAudio();
}
}
</script>
</body>
|