Librairie WaveSurfer
Dans le cadre du projet PASSY-DPFV et FRAPéOR nous avons dû mettre en place et utiliser une librairie pour visualiser les enregistrements sous différentes formes :
- sous le forme de melspectrogrammes
- sous la forme de FFT
Pour ce faire nous avons mis en place la librairie Wavesurfer qui nous permet de répondre à ce type de besoin.
Illustrations sur SPGoO voir https://spgoo.org/?page_id=6630

Mise en oeuvre et utilisation de cette librairie :
sources : https://wavesurfer.xyz/
Insertion des différentes inclusions pour permettre l’accès aux différents objets :
/personnalisation/scripts/wavesurfer.min.js
/personnalisation/scripts/plugins/regions.min.js
/personnalisation/scripts/plugins/timeline.min.js
/personnalisation/scripts/plugins/spectrogram.min.js
Pour permettre de réactualiser la graphe en fonction du mode de melspectrogramme (mel, linear, logarithmic …) il a fallu passer par l’appel de la fonction zoom de l’objet pour forcer la réactulisation car l’objet lui même ne dispose pas de méthode update.
Comment s’implémente l’appel à ce type de librairie :
Il faut créer un conteneur html pour accueillir cette représentation
Il faut instancier les objets de cette représentation qui est composée de plusieurs éléments :
Regions, Timeline, Spectrogram et les associer à la représentation graphique.
Représentation Graphique :
<div style="width:550px;border-style:inset;border-width: 3px; background:grey;">
<p style="text-align:center;color:#5cb85c">Locuteur : IA-alloy</p>
<div id="waveform_ref1" style="width:540px; height:360px;" ></div>
<p style="display:flex;justify-content:space-evenly;">
<label><input type="radio" name="MOD_ref7" id="mel" checked />mel</label>
<label><input type="radio" name="MOD_ref7" id="linear" />linear</label>
<label><input type="radio" name="MOD_ref7" id="logarithmic" />logarithmic</label>
<label><input type="radio" name="MOD_ref7" id="bark" />bark</label>
<label><input type="radio" name="MOD_ref7" id="erb" />erb</label>
</p>
</div>
// Regions plugin
const regions_ref1 = WaveSurfer.Regions.create();
const bottomTimeline_1 = WaveSurfer.Timeline.create({
height: 15,
timeInterval: 0.1,
primaryLabelInterval: 0.2,
style: {
fontSize: '12px',
color: '#6A3274',
},
})
Création de l’objet WaveSurfer et du spectrogramme
// Create a WaveSurfer instance
const ws_ref1 = WaveSurfer.create({
height: 80,
minPxPerSec: 1,
barWidth: 2,
barRadius: 2,
barGap: 1,
container: '#waveform_ref1',
waveColor: 'rgb(200, 0, 200)',
progressColor: 'rgb(100, 0, 100)',
mediaControls: true,
interact: false,
url: '/personnalisation/frapeor/tmpreqv8p6t_alloy_ref1.mp3',
plugins: [regions_ref1,bottomTimeline_1],
})
const creation_spectro=function(mode) {
return WaveSurfer.Spectrogram.create({
labels: true,
labelsColor :"yellow",
height: 200,
splitChannels: false,
//scale: 'mel', // or 'linear', 'logarithmic', 'bark', 'erb'
scale: mode,
frequencyMax: 8000,
frequencyMin: 0,
fftSamples: 512,
labelsBackground: 'rgba(25, 25, 25, 0.1)',
windowFunc :"lanczoz",
});
}
var spectro_ref1=null;
// Initialize the Spectrogram plugin
ws_ref1.registerPlugin(
spectro_ref1=creation_spectro("mel")
)
Si on veut modifier en direct le type de spectrogramme (mel, linear ou autre) il faut reconstruire le spectrogramme. Exemple d’utilisation
// ----------------------------------------------------------------
// Evenementiel pour la changement de type de spectrogramme
// ----------------------------------------------------------------
document.querySelectorAll('[name^="mode_ref1_app1"]').forEach(elem=>
elem.addEventListener("click",function(e) {
ws_ref1_app1.plugins.splice(1,1);
spectro_ref1_app1.destroy();
const mode=e.srcElement.id.split("_")[0];
ws_ref1_app1.registerPlugin(
spectro_ref1_app1=creation_spectro(mode)
)
// important ce zoom car il reconstruit et affiche la diagramme
ws_ref1_app1.zoom(1);
})
)
Ce qui nous donne le résultat suivant :

Si on veut ajouter des régions supplémentaires pour les syllabes :
// --------------------------------------------------------------
// Avec en paramètres les syllabes a positionner sur le graphe
// --------------------------------------------------------------
const ajoute_regions=function(WS, Regions, syllabes=null) {
const duration=WS.getDuration(); // Duree total du signal
let debut=0;
const pas=2.0/(syllabes.length+2);
console.log(duration,pas, syllabes);
let i=1;
WS.on('decode', ()=> {
syllabes.forEach(syll => {
const p=document.createElement("span");
p.innerHTML=syll;
p.setAttribute("style","font-size:10px;color:lightgreen;margin:5px;");
Regions.addRegion({
start: debut,
end: debut+pas,
content: p,
color: randomColor(),
drag: true,
resize: true,
})
debut=debut+pas;
i++;
})
})
}
const active_region=function(WS,Regions){
{
let activeRegion = null
Regions.on('region-in', (region) => {
activeRegion = region
})
Regions.on('region-out', (region) => {
if (activeRegion === region) {
if (loop) {
region.play()
} else {
activeRegion = null
}
}
})
Regions.on('region-clicked', (region, e) => {
e.stopPropagation() // prevent triggering a click on the waveform
activeRegion = region
region.play(true)
region.setOptions({ color: randomColor() })
})
// Reset the active region when the user clicks anywhere in the waveform
WS.on('interaction', () => {
activeRegion = null
})
}
}
// a charger quand le doucment est chargé
document.addEventListener("DOMContentLoaded",function(event){
ajoute_regions(ws_ref1, regions_ref1,["un","grand","vent","souffle"]);
active_region(ws_ref1,regions_ref1);
});
Attention une petite précaution à prendre lorsqu’on souhaite créer des spectrogrammes de façon dynamique, voir l’exemple https://spgoo.org/?page_id=6873. Dans cette réalisation, nous avons développé une page qui permet de créer à la volée le spectrogramme et l’effacer. Dans ce cas précis, lors de l’effacement il faut détruire l’objet spectrogram avant de l’enlever de la DOM, sinon le navigateur conserve l’objet ce qui lui pose des problèmes pour la suite. Pour ce faire on peut procéder de la manière suivante :
if (visible) {
const obj=document.getElementById("ZE_"+ind+"_spectrogramme")
const spectro=obj.spectro;
spectro.destroy();
Zanno.remove();
}

Clique que annotation pour générer le visuel ou si il existe le supprimer.

Modèle de classes/objets
