Kombination von P5 und Plotly



Zunächst werden im <head> der Seite sowohl das Script für Plotly als auch die beiden Scripte für P5 und P5.sound eingebunden:

<script src="header/plotly-latest.min.js"></script>
<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>

In diesen Container wird der Anzeige-Container für Plotly eingebettet:

<DIV id="p5container" >
<div id="AnzeigeDiv" >
</div>
</DIV>

über Style-Eigenschaften kann man diesen noch weiter im Aussehen verändern, z.B.

<DIV id="p5container" >
<div id="AnzeigeDiv" style="width:800;height:400;border: 1px solid #333;box-shadow: 8px 8px 5px #444;padding: 8px 12px;background-color:ffffff;">
</div>
</DIV>

Das dazugehörende Script ist eine Mischung aus P5- und Plotly-Script: zunächst werden zwei Arrays definiert:

var zeit_array = []; // Array für die Samplepositionen (Zeit)
var peaks_array =[]; // Array für die Samplewerte (Amplituden)

Dann wird in der Funktion preload() das Audiobeispiel vorausgeladen und mit sound_fertiggeladen bestimmt, was passieren soll, sobald das Audio komplett geladen ist.

function preload() {
sound = loadSound('klang/fagott_toene.mp3', sound_fertiggeladen);
// lade einen Klang, übergebe ihn an die Variable "sound", und sobald er fertig geladen ist, führe
// die Funktion sound_fertiggeladen() aus.

}

Dann wird in der Funktion setup() der Canvas erstellt und an das DIV "p5container" angehängt:

function setup() {
var container = createCanvas(1,1); // Canvas erstellen, muss nur 1 Pixel groß sein
container.parent('p5container'); // an DIV-Container anhängen
}

Danacg wird in der Funktion sound_fertiggeladen() beschrieben, was passieren soll, sobald der Klang fertig geladen ist:

function sound_fertiggeladen(){
samplepunkte = round(sound.sampleRate()/2 * sound.duration()); // ermittle
// aus der Hälfte der Sample-Rate (wegen Nyquist-Theorem) und der Dauer des Klangs die Anzahl der
// Samplepunkte innerhalb der Datei

peaks_array = sound.getPeaks(samplepunkte); // hole via getPeaks() für alle
//
Samplepunkte die Amplitudenwerte und schreibe sie in das Array peaks
for (i = 0; i < peaks_array.length; i++){ // für jeden Punkt im Array peaks:
zeit_array.push(i); // gibt einen Wert i ins Array Zeit (=Reihenfolge der Samplepunkte)
}

Aus den nun gefüllten Arrays für Zeit und Peaks, kann das Trace für Plotly erstellt werden (s. Anleitung für Plotly-Scripte):

var trace1 = {
x: zeit_array,
y: peaks_array,
mode: 'lines',
type: 'scatter' ,
name: 'Amplitude',
marker: {
color: 'rgba(188, 48, 47, 0.8)',
line: {
color: 'rgba(188, 48, 47, 1.0)',
width: 1,
},
},
text: zeit_array,
};

Diese Trace wird dem Data-Array zugewiesen

var data = [ trace1,];

Dann wird das Layout bestimmt:

var layout = {
showlegend: true,
xaxis: {
title: 'Samples',
},
yaxis: {
title: 'Amplitude',
},
legend: {
font: { family: 'Verdana, sans-serif', size: 10, color: 'black', }
},
title: 'Oszillogramm',
};

Und die Darstellung der Wellenform ausgeplottet:

Plotly.plot('AnzeigeDiv', data, layout, {displayModeBar: false});

Für die Audio-Synchronisierung holt man sich via document.getElementById(); die Ausgabe des Plotly-Plots auf der Seite (AnzeigeDiv) und beschreibt, was bei einem MouseOver (plotly_hover) passieren soll und was bei einem MouseOut (plotyl_unhover):

myPlot = document.getElementById('AnzeigeDiv'); // hole die Ausgabe von Plotly
// auf der html-Seite

myPlot.on('plotly_hover', function(data){ // bei einem MouseOver suche im Array
// data ...

var ausgabe = data.points.map(function(d){ // ... die folgenden Eigenschaften des Punkts d (= Datenpunkt, über dem die Maus gerade ist):
indexTrace = d.curveNumber; // die Nummer der Kurve, zu der der Punkt gehört
indexNummer = d.pointNumber; // die Position des Punkts auf der gefundenen Kurve
audiofaktor = zeit_array.length / sound.duration(); // ermittle mit Hilfe der
// Dauer des Klangbeispiels und der Länge des zeit-Arrays einen Teilungsfaktor, durch den die
// Position des Punkts auf der gefundenen Kurve auf die zeitliche Position im Audiobeispiel
// übertragen werden kann.

sound.play(); // spiele das Audiobeispiel ab und ...
sound.jump(indexNummer/audiofaktor); //... springe zur entsprechenden Stelle im
// Audiobeispiel

}); // Ende der funktion(d)
}) // Ende der function(data)
.on('plotly_unhover', function(data){ // bei einem MouseOut ...
sound.pause(); // ... pausiere das Audiobeispiel
});

} // die letzte Klammer schließt die Funktion sound_fertiggeladen()

 

Insgesamt sieht das Script dann folgendermaßen aus:

<script src="header/plotly-latest.min.js"></script>
<script src="header/p5.js"></script>
<script src="header/p5.sound.js"></script>

var zeit_array = [];
var peaks_array =[];

function preload() {
sound = loadSound('klang/fagott_toene.mp3', sound_fertiggeladen);
}

function setup() {
var container = createCanvas(1,1);
container.parent('p5container');
}

function sound_fertiggeladen(){
samplepunkte = round(sound.sampleRate()/2 * sound.duration());
peaks_array = sound.getPeaks(samplepunkte);
for (i = 0; i < peaks_array.length; i++){
zeit_array.push(i);
}
//Ab hier eine Trace für Plotly erstellen
var trace1 = {
x: zeit_array,
y: peaks_array,
mode: 'lines',
type: 'scatter' ,
name: 'Amplitude',
marker: {
color: 'rgba(188, 48, 47, 0.8)',
line: {
color: 'rgba(188, 48, 47, 1.0)',
width: 1,
},
},
text: zeit_array,
};

var data = [ trace1,];

var layout = {
showlegend: true,
xaxis: {
title: 'Samples',
},
yaxis: {
title: 'Amplitude',
},
legend: {
font: { family: 'Verdana, sans-serif', size: 10, color: 'black', }
},
title: 'Oszillogramm',
};

Plotly.plot('AnzeigeDiv', data, layout, {displayModeBar: false});

myPlot = document.getElementById('AnzeigeDiv');

myPlot.on('plotly_hover', function(data){
var ausgabe = data.points.map(function(d){
indexTrace = d.curveNumber;
indexNummer = d.pointNumber;
audiofaktor = zeit_array.length / sound.duration();
sound.play();
sound.jump(indexNummer/audiofaktor);
});
})
.on('plotly_unhover', function(data){
sound.pause();
});
}

</script>

<DIV id="p5container" >
<div id="AnzeigeDiv" style="width:800;height:400;border: 1px solid #333;box-shadow: 8px 8px 5px #444;padding: 8px 12px;background-color:ffffff;">
</div>
</DIV>