Un spectrogramme en 3D avec R

1. D’une coupe spectrale  à un spectrogramme

1.1 Avec Praat

Dans le cadre de mon cours de phonétique expérimentale, il m’a toujours semblé complexe, sans recourir à une quelconque illustration, de faire comprendre aux étudiants le passage d’une coupe spectrale statique à un spectrogramme dynamique. Les deux illustrations suivantes produites avec le logiciel Praat représentent une occurrence de la voyelle française /ɔ/.

 

Même en utilisant des représentations, saisir le passage de l’une à l’autre de ces deux représentations tout en saisissant la distinction entre harmoniques et formants me semble peu évident pour des phonéticiens débutants. En 2016, mon ancien étudiant de maitrise, Xavier Saint-Gelais avait eu l’excellente idée, de concevoir une illustration faite maison visant à schématiser ce passage en simulant une représentation 3D, je me permets de la reproduire.

1.2 avec R

Les fonctionnalités récentes de traitement du signal sonore et de représentations 3D offertes par différents packages de l’écosystème du R Project for Statistical Computing permettent désormais d’aller plus loin.

Dans les lignes qui suivent, outre le package ggplot2, seront plus spécifiquement utilisés les packages tuneR et seewave qui comprennent un vaste ensemble de fonctions permettant de traiter, d’analyser et représenter les signaux sonores (Sueur, 2018). Concernant la 3D, le package rayshader publié par 2019 par Tyler Morgan-Wall ouvre  des perspectives inédites tant en ce qui a trait à la cartographie 3D (non traité ici) et à l’adaptation 3D des graphiques produits par l’entremise de ggplot2.

En premier lieu, les packages au sein desquels se trouvent les fonctions utilisées sont déclarés.

library(tuneR)
library(seewave)
library(ggplot2)
library(rayshader)

Le fichier sonore est ouvert par l’intermédiaire de la fonction readWave() et imputé à un objet nommé son.

son <- readWave("o.wav")
son
## Wave Object
##  Number of Samples:      21409
##  Duration (seconds):     0.49
##  Samplingrate (Hertz):   44100
##  Channels (Mono/Stereo): Mono
##  PCM (integer format):   TRUE
##  Bit (8/16/24/32/64):    16

La fonction spectro() permet de calculer un spectrogramme pour ce fichier sonore. Le résultat de ce calcul est imputé à un objet nommé spectrogramme. L’argument plot=FALSE évite que le spectrogramme soit affiché à l’aide des paramètres par défaut. Seuls les résultats de ce traitement soit stocké dans l’objet spectrogramme. Concernant la configuration des arguments de cette fonction, le théorème d’incertitude d’Heisenberg et la question du compromis entre résolution fréquentielle et résolution temporelle, je renvoie le lecteur au chapitre 11 de Sueur (2018, 309 et suivantes).

spectrogramme <- spectro(son, f=son@samp.rate, plot = FALSE, ovlp=88, dBref=2*10e-5, wn="hanning", wl=1024)

À partir de cet objet, une base de données au format data.frame comprenant, pour chaque trame temporelle, la valeur d’amplitude (ici, en dB SPL) de chacune des fréquences analysées est créée. Cette base de données est ensuite sous-échantillonnée pour ne conserver que les fréquences inférieures à 5000 Hz. En fonction de la durée du fichier sonore utilisé, la taille de cette base de données, au format long, peut être imposante (dans cet exemple, elle comprend déjà 24300 lignes).

frequence <- rep(spectrogramme$freq*1000, times=ncol(spectrogramme$amp))
temps <- rep(spectrogramme$time, each=nrow(spectrogramme$amp))
amplitude <- as.numeric(spectrogramme$amp)
 
df <- data.frame(temps=as.numeric(temps), frequence=as.numeric(frequence), amplitude=as.numeric(amplitude))
 
frequence.max <- 5000
df <- df[which(df$frequence < frequence.max),]

Joey Stanley (Brigham Young University), dont je recommande le site, propose, lui aussi, une méthode de création de spectrogrammes en 3D en recourant au package rayshader. Cependant, pour extraire les données permettant de construire un spectrogramme en 2D, il utilise la fonction spectrogram() du package PhonTools, ce qui tend, de son propre aveu, à complexifier l’extraction des données de temps, de fréquence et d’amplitude nécessaires à la construction de cette représentation du signal.

I did some digging and I just could not find a way to extract the data that gets eventually plotted in spectrogram. I mean, it has to have gone through some Fourier analysis or something first to be able to extract frequencies. So, I figure if the spectrogram function could do it, the key was in the function itself.

La création de cette base de données permet d’afficher le spectrogramme en 2D en utilisant le package ggplot2. Ce diagramme est stocké dans l’objet spectrogramme.gg. Notez que les données présentes dans la base de données sont visuellement rendues sous la forme d’un raster permettant d’afficher une surface et non plus d’un ensemble distinct de points de mesure.

spectrogramme.gg <- ggplot(df)+
geom_raster(aes(temps, frequence, fill = amplitude), interpolate = TRUE)+
scale_y_continuous(breaks = seq(from=0, to=frequence.max, by=500))+
labs(x="Temps (s)", y="Fréquence (Hz)", title="")+
coord_cartesian(expand=FALSE)
 
spectrogramme.gg

 

2. En 3D

Le spectrogramme en 2D peut alors être adapté en 3D au moyen de la fonction plot_gg().

plot_gg(spectrogramme.gg, 
width=6, height=4, scale=300, 
windowsize=c(1000, 800),
fov=70, zoom=0.5, 
theta=330, phi=40,
multicore=TRUE)

Une fois le calcul terminé (comptez ici de quelques secondes à plusieurs minutes selon la taille du fichier sonore), une fenêtre d’aperçu permet de choisir un angle et un degré de zoom appropriés. Il est alors possible de sauvegarder un ou plusieurs instantanés en utilisant la fonction render_snapshot(). Les fichiers .png générés sont stockés dans le répertoire de travail.

2.1 En images

render_snapshot("image0x")

 

 

2.2 En mouvements

Il est également possible de sauvegarder une animation au format .mp4 en utilisant la commande render_movie(). Concernant les mouvements de caméra utilisés, j’ai choisi d’utiliser une des deux options proposées par défaut . Il y a cependant possibilité de générer des mouvements de caméra personnalisés (voir par exemple Lego World Map – Rayshader Walkthrough).

render_movie(filename = "spectrogramme.gg", type="oscillate", frames=390, phi=30, theta=-45)

Le site de l’UQAC n’acceptant pas pas les vidéos, l’animation a été convertie au format .gif animé.

Pour allez plus loin


3D ggplots with rayshader – RStudio