{"id":744,"date":"2020-06-04T19:45:26","date_gmt":"2020-06-04T23:45:26","guid":{"rendered":"http:\/\/portfolio-pre.uqac.ca\/vincentarnaud\/?p=744"},"modified":"2025-09-11T11:05:03","modified_gmt":"2025-09-11T15:05:03","slug":"un-spectrogramme-en-3d-avec-r","status":"publish","type":"post","link":"https:\/\/portfolio.uqac.ca\/vincentarnaud\/un-spectrogramme-en-3d-avec-r\/","title":{"rendered":"Un spectrogramme en 3D avec R"},"content":{"rendered":"<h1>1. D&#8217;une coupe spectrale\u00a0 \u00e0 un spectrogramme<\/h1>\n<h2>1.1 Avec Praat<\/h2>\n<p>Dans le cadre de mon cours de phon\u00e9tique exp\u00e9rimentale, il m&#8217;a toujours sembl\u00e9 complexe, sans recourir \u00e0 une quelconque illustration, de faire comprendre aux \u00e9tudiants le passage d&#8217;une coupe spectrale statique \u00e0 un spectrogramme dynamique. Les deux illustrations suivantes produites avec le logiciel <a href=\"https:\/\/www.praat.org\" target=\"_blank\" rel=\"noopener\">Praat <\/a>repr\u00e9sentent une occurrence de la voyelle fran\u00e7aise \/\u0254\/.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignright size-full wp-image-746\" src=\"https:\/\/portfolio.uqac.ca\/vincentarnaud\/files\/2020\/05\/xsg_coupe.jpg\" alt=\"\" width=\"663\" height=\"432\" srcset=\"https:\/\/portfolio.uqac.ca\/vincentarnaud\/files\/2020\/05\/xsg_coupe.jpg 663w, https:\/\/portfolio.uqac.ca\/vincentarnaud\/files\/2020\/05\/xsg_coupe-150x98.jpg 150w, https:\/\/portfolio.uqac.ca\/vincentarnaud\/files\/2020\/05\/xsg_coupe-300x195.jpg 300w\" sizes=\"(max-width: 663px) 100vw, 663px\" \/> <img loading=\"lazy\" decoding=\"async\" class=\"alignright size-full wp-image-747\" src=\"https:\/\/portfolio.uqac.ca\/vincentarnaud\/files\/2020\/05\/xsg_spectro.jpg\" alt=\"\" width=\"663\" height=\"432\" srcset=\"https:\/\/portfolio.uqac.ca\/vincentarnaud\/files\/2020\/05\/xsg_spectro.jpg 663w, https:\/\/portfolio.uqac.ca\/vincentarnaud\/files\/2020\/05\/xsg_spectro-150x98.jpg 150w, https:\/\/portfolio.uqac.ca\/vincentarnaud\/files\/2020\/05\/xsg_spectro-300x195.jpg 300w\" sizes=\"(max-width: 663px) 100vw, 663px\" \/><\/p>\n<p>&nbsp;<\/p>\n<p>M\u00eame en utilisant des repr\u00e9sentations, saisir le passage de l&#8217;une \u00e0 l&#8217;autre de ces deux repr\u00e9sentations tout en saisissant la distinction entre <em>harmoniques<\/em> et <em>formants<\/em> me semble peu \u00e9vident pour des phon\u00e9ticiens d\u00e9butants. En 2016, mon ancien \u00e9tudiant de maitrise, <a href=\"https:\/\/constellation.uqac.ca\/5087\/\" target=\"_blank\" rel=\"noopener\">Xavier Saint-Gelais<\/a> avait eu l\u2019excellente id\u00e9e, de concevoir une illustration <em>faite maison<\/em> visant \u00e0 sch\u00e9matiser ce passage en simulant une repr\u00e9sentation 3D, je me permets de la reproduire.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignright size-full wp-image-748\" src=\"https:\/\/portfolio.uqac.ca\/vincentarnaud\/files\/2020\/05\/xsg_image.jpg\" alt=\"\" width=\"738\" height=\"568\" srcset=\"https:\/\/portfolio.uqac.ca\/vincentarnaud\/files\/2020\/05\/xsg_image.jpg 738w, https:\/\/portfolio.uqac.ca\/vincentarnaud\/files\/2020\/05\/xsg_image-150x115.jpg 150w, https:\/\/portfolio.uqac.ca\/vincentarnaud\/files\/2020\/05\/xsg_image-300x231.jpg 300w\" sizes=\"(max-width: 738px) 100vw, 738px\" \/><\/p>\n<h2>1.2 avec R<\/h2>\n<p>Les fonctionnalit\u00e9s r\u00e9centes de traitement du signal sonore et de repr\u00e9sentations 3D offertes par <em>diff\u00e9rents packages<\/em> de l&#8217;\u00e9cosyst\u00e8me du <a href=\"https:\/\/www.r-project.org\/\" target=\"_blank\" rel=\"noopener\">R Project for Statistical Computing<\/a> permettent d\u00e9sormais d&#8217;aller plus loin.<\/p>\n<p>Dans les lignes qui suivent, outre le <em>package<\/em> <a href=\"https:\/\/ggplot2.tidyverse.org\/\" target=\"_blank\" rel=\"noopener\"><code>ggplot2<\/code><\/a>, seront plus sp\u00e9cifiquement utilis\u00e9s les <em>packages<\/em> <code>tuneR<\/code> et <a href=\"https:\/\/rug.mnhn.fr\/seewave\/\" target=\"_blank\" rel=\"noopener\"><code>seewave<\/code> <\/a>qui comprennent un vaste ensemble de fonctions permettant de traiter, d&#8217;analyser et repr\u00e9senter les signaux sonores (Sueur, 2018). Concernant la 3D, le <em>package<\/em> <a href=\"https:\/\/www.rayshader.com\/\" target=\"_blank\" rel=\"noopener\"><code>rayshader<\/code><\/a> publi\u00e9 par 2019 par <a href=\"https:\/\/www.tylermw.com\/\" target=\"_blank\" rel=\"noopener\">Tyler Morgan-Wall<\/a> ouvre\u00a0 des perspectives in\u00e9dites tant en ce qui a trait \u00e0 la cartographie 3D (non trait\u00e9 ici) et \u00e0 l&#8217;adaptation 3D des graphiques produits par l&#8217;entremise de <code>ggplot2<\/code>.<\/p>\n<p>En premier lieu, les <em>packages<\/em> au sein desquels se trouvent les fonctions utilis\u00e9es sont d\u00e9clar\u00e9s.<\/p>\n<pre><code>library(tuneR)\r\nlibrary(seewave)\r\nlibrary(ggplot2)\r\nlibrary(rayshader)\r\n<\/code><\/pre>\n<p>Le fichier sonore est ouvert par l&#8217;interm\u00e9diaire de la fonction <code>readWave()<\/code> et imput\u00e9 \u00e0 un objet nomm\u00e9 <code>son<\/code>.<\/p>\n<pre><code>son &lt;- readWave(\"o.wav\")\r\nson\r\n<\/code><\/pre>\n<pre><code>## Wave Object\r\n##  Number of Samples:      21409\r\n##  Duration (seconds):     0.49\r\n##  Samplingrate (Hertz):   44100\r\n##  Channels (Mono\/Stereo): Mono\r\n##  PCM (integer format):   TRUE\r\n##  Bit (8\/16\/24\/32\/64):    16<\/code><\/pre>\n<p>La fonction <code>spectro()<\/code> permet de calculer un spectrogramme pour ce fichier sonore. Le r\u00e9sultat de ce calcul est imput\u00e9 \u00e0 un objet nomm\u00e9 <code>spectrogramme<\/code>. L&#8217;argument <code>plot=FALSE<\/code> \u00e9vite que le spectrogramme soit affich\u00e9 \u00e0 l&#8217;aide des param\u00e8tres par d\u00e9faut. Seuls les r\u00e9sultats de ce traitement soit stock\u00e9 dans l&#8217;objet <code>spectrogramme<\/code>. Concernant la configuration des arguments de cette fonction, le th\u00e9or\u00e8me d&#8217;incertitude d&#8217;Heisenberg et la question du compromis entre r\u00e9solution fr\u00e9quentielle et r\u00e9solution temporelle, je renvoie le lecteur au chapitre 11 de Sueur (2018, 309 et suivantes).<\/p>\n<pre class=\"r\"><code class=\"hljs\">spectrogramme &lt;- spectro(son, f=son@samp.rate, plot = FALSE, ovlp=88, dBref=2*10e-5, wn=\"hanning\", wl=1024)<\/code><\/pre>\n<p>\u00c0 partir de cet objet, une base de donn\u00e9es au format <code>data.frame<\/code> comprenant, pour chaque trame temporelle, la valeur d&#8217;amplitude (ici, en dB SPL) de chacune des fr\u00e9quences analys\u00e9es est cr\u00e9\u00e9e. Cette base de donn\u00e9es est ensuite sous-\u00e9chantillonn\u00e9e pour ne conserver que les fr\u00e9quences inf\u00e9rieures \u00e0 5000 Hz. En fonction de la dur\u00e9e du fichier sonore utilis\u00e9, la taille de cette base de donn\u00e9es, au format long, peut \u00eatre imposante (dans cet exemple, elle comprend d\u00e9j\u00e0 24300 lignes).<\/p>\n<pre><code>frequence &lt;- rep(spectrogramme$freq*1000, times=ncol(spectrogramme$amp))\r\ntemps &lt;- rep(spectrogramme$time, each=nrow(spectrogramme$amp))\r\namplitude &lt;- as.numeric(spectrogramme$amp)\r\n \r\ndf &lt;- data.frame(temps=as.numeric(temps), frequence=as.numeric(frequence), amplitude=as.numeric(amplitude))\r\n \r\nfrequence.max &lt;- 5000\r\ndf &lt;- df[which(df$frequence &lt; frequence.max),]<\/code><\/pre>\n<p><a href=\"https:\/\/joeystanley.com\/blog\/3d-vowel-plots-with-rayshader\" target=\"_blank\" rel=\"noopener\">Joey Stanley<\/a> (Brigham Young University), dont je recommande le <a href=\"https:\/\/joeystanley.com\" target=\"_blank\" rel=\"noopener\">site<\/a>, propose, lui aussi, une m\u00e9thode de cr\u00e9ation de spectrogrammes en 3D en recourant au package <code>rayshader<\/code>. Cependant, pour extraire les donn\u00e9es permettant de construire un spectrogramme en 2D, il utilise la fonction <code>spectrogram()<\/code> du package <code>PhonTools<\/code>, ce qui tend, de son propre aveu, \u00e0 complexifier l&#8217;extraction des donn\u00e9es de temps, de fr\u00e9quence et d&#8217;amplitude n\u00e9cessaires \u00e0 la construction de cette repr\u00e9sentation du signal.<\/p>\n<blockquote><p>I did some digging and I just could not find a way to extract the data that gets eventually plotted in <code>spectrogram<\/code>. 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 <code>spectrogram<\/code> function could do it, the key was in the function itself.<\/p><\/blockquote>\n<p>La cr\u00e9ation de cette base de donn\u00e9es permet d&#8217;afficher le spectrogramme en 2D\u00a0en utilisant le <em>package<\/em> <code>ggplot2<\/code>. Ce diagramme est stock\u00e9 dans l&#8217;objet <code>spectrogramme.gg<\/code>. Notez que les donn\u00e9es pr\u00e9sentes dans la base de donn\u00e9es sont visuellement rendues sous la forme d&#8217;un <em>raster<\/em> permettant d&#8217;afficher une surface et non plus d&#8217;un ensemble distinct de points de mesure.<\/p>\n<pre><code>spectrogramme.gg &lt;- ggplot(df)+\r\ngeom_raster(aes(temps, frequence, fill = amplitude), interpolate = TRUE)+\r\nscale_y_continuous(breaks = seq(from=0, to=frequence.max, by=500))+\r\nlabs(x=\"Temps (s)\", y=\"Fr\u00e9quence (Hz)\", title=\"\")+\r\ncoord_cartesian(expand=FALSE)\r\n \r\nspectrogramme.gg<\/code><\/pre>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignright size-full wp-image-775\" src=\"https:\/\/portfolio.uqac.ca\/vincentarnaud\/files\/2020\/06\/spec_2D.png\" alt=\"\" width=\"1344\" height=\"960\" srcset=\"https:\/\/portfolio.uqac.ca\/vincentarnaud\/files\/2020\/06\/spec_2D.png 1344w, https:\/\/portfolio.uqac.ca\/vincentarnaud\/files\/2020\/06\/spec_2D-150x107.png 150w, https:\/\/portfolio.uqac.ca\/vincentarnaud\/files\/2020\/06\/spec_2D-300x214.png 300w, https:\/\/portfolio.uqac.ca\/vincentarnaud\/files\/2020\/06\/spec_2D-768x549.png 768w, https:\/\/portfolio.uqac.ca\/vincentarnaud\/files\/2020\/06\/spec_2D-1024x731.png 1024w\" sizes=\"(max-width: 1344px) 100vw, 1344px\" \/><\/p>\n<p>&nbsp;<\/p>\n<h1>2. En 3D<\/h1>\n<p>Le spectrogramme en 2D peut alors \u00eatre adapt\u00e9 en 3D au moyen de la fonction <code>plot_gg()<\/code>.<\/p>\n<pre><code>plot_gg(spectrogramme.gg, \r\nwidth=6, height=4, scale=300, \r\nwindowsize=c(1000, 800),\r\nfov=70, zoom=0.5, \r\ntheta=330, phi=40,\r\nmulticore=TRUE)<\/code><\/pre>\n<p>Une fois le calcul termin\u00e9 (comptez ici de quelques secondes \u00e0 plusieurs minutes selon la taille du fichier sonore), une fen\u00eatre d&#8217;aper\u00e7u permet de choisir un angle et un degr\u00e9 de zoom appropri\u00e9s. Il est alors possible de sauvegarder un ou plusieurs instantan\u00e9s en utilisant la fonction <code>render_snapshot()<\/code>. Les fichiers <code>.png<\/code> g\u00e9n\u00e9r\u00e9s sont stock\u00e9s dans le r\u00e9pertoire de travail.<\/p>\n<h2>2.1 En images<\/h2>\n<pre><code>render_snapshot(\"image0x\")<\/code><\/pre>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignright wp-image-776 size-full\" src=\"https:\/\/portfolio.uqac.ca\/vincentarnaud\/files\/2020\/06\/spec_3Da-e1591020304164.png\" width=\"1000\" height=\"705\" srcset=\"https:\/\/portfolio.uqac.ca\/vincentarnaud\/files\/2020\/06\/spec_3Da-e1591020304164.png 1000w, https:\/\/portfolio.uqac.ca\/vincentarnaud\/files\/2020\/06\/spec_3Da-e1591020304164-150x106.png 150w, https:\/\/portfolio.uqac.ca\/vincentarnaud\/files\/2020\/06\/spec_3Da-e1591020304164-300x212.png 300w, https:\/\/portfolio.uqac.ca\/vincentarnaud\/files\/2020\/06\/spec_3Da-e1591020304164-768x541.png 768w\" sizes=\"(max-width: 1000px) 100vw, 1000px\" \/> <img loading=\"lazy\" decoding=\"async\" class=\"alignright wp-image-777 size-full\" src=\"https:\/\/portfolio.uqac.ca\/vincentarnaud\/files\/2020\/06\/spec_3Db-e1591020331712.png\" width=\"1000\" height=\"595\" srcset=\"https:\/\/portfolio.uqac.ca\/vincentarnaud\/files\/2020\/06\/spec_3Db-e1591020331712.png 1000w, https:\/\/portfolio.uqac.ca\/vincentarnaud\/files\/2020\/06\/spec_3Db-e1591020331712-150x89.png 150w, https:\/\/portfolio.uqac.ca\/vincentarnaud\/files\/2020\/06\/spec_3Db-e1591020331712-300x179.png 300w, https:\/\/portfolio.uqac.ca\/vincentarnaud\/files\/2020\/06\/spec_3Db-e1591020331712-768x457.png 768w\" sizes=\"(max-width: 1000px) 100vw, 1000px\" \/> <img loading=\"lazy\" decoding=\"async\" class=\"alignright wp-image-774 size-full\" src=\"https:\/\/portfolio.uqac.ca\/vincentarnaud\/files\/2020\/06\/spec_3Dc-e1591020359622.png\" width=\"992\" height=\"655\" srcset=\"https:\/\/portfolio.uqac.ca\/vincentarnaud\/files\/2020\/06\/spec_3Dc-e1591020359622.png 992w, https:\/\/portfolio.uqac.ca\/vincentarnaud\/files\/2020\/06\/spec_3Dc-e1591020359622-150x99.png 150w, https:\/\/portfolio.uqac.ca\/vincentarnaud\/files\/2020\/06\/spec_3Dc-e1591020359622-300x198.png 300w, https:\/\/portfolio.uqac.ca\/vincentarnaud\/files\/2020\/06\/spec_3Dc-e1591020359622-768x507.png 768w\" sizes=\"(max-width: 992px) 100vw, 992px\" \/><\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<h2>2.2 En mouvements<\/h2>\n<p>Il est \u00e9galement possible de sauvegarder une animation au format <code>.mp4<\/code> en utilisant la commande <code>render_movie()<\/code>. Concernant les mouvements de cam\u00e9ra utilis\u00e9s, j&#8217;ai choisi d&#8217;utiliser une des deux options propos\u00e9es par d\u00e9faut . Il y a cependant possibilit\u00e9 de g\u00e9n\u00e9rer des mouvements de cam\u00e9ra personnalis\u00e9s (voir par exemple<a href=\"https:\/\/arthurwelle.github.io\/RayshaderWalkthrough\/index.html#32_let%C2%B4s_move_the_camera\" target=\"_blank\" rel=\"noopener\"> Lego World Map &#8211; Rayshader Walkthrough<\/a>).<\/p>\n<pre><code>render_movie(filename = \"spectrogramme.gg\", type=\"oscillate\", frames=390, phi=30, theta=-45)<\/code><\/pre>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignright size-full wp-image-779\" src=\"https:\/\/portfolio.uqac.ca\/vincentarnaud\/files\/2020\/05\/spectrogramme.gif\" alt=\"\" width=\"580\" height=\"464\" \/><\/p>\n<p>Le site de l&#8217;UQAC n\u2019acceptant pas pas les vid\u00e9os, l&#8217;animation a \u00e9t\u00e9 convertie au format <code>.gif<\/code> anim\u00e9.<\/p>\n<h1>Pour allez plus loin<\/h1>\n<ul>\n<li>Sueur, J. (2018). <i>Sound Analysis and Synthesis with R<\/i>. Springer.<\/li>\n<li><a href=\"https:\/\/www.tylermw.com\/3d-ggplots-with-rayshader\/\" target=\"_blank\" rel=\"noopener\">Introducing 3D ggplots with rayshader<\/a>, Tyler Morgan-Wall<\/li>\n<li><a href=\"https:\/\/github.com\/tylermorganwall\/MusaMasterclass\" target=\"_blank\" rel=\"noopener\">Penn MUSA Masterclass: 3D Mapping and Visualization with R and Rayshader<\/a>, Tyler Morgan-Wall<\/li>\n<li><a href=\"https:\/\/www.rayshader.com\/reference\/plot_gg.html\" target=\"_blank\" rel=\"noopener\">Transform ggplot2 objects into 3D<\/a>, Tyler Morgan-Wall<\/li>\n<li><a href=\"https:\/\/joeystanley.com\/blog\/3d-vowel-plots-with-rayshader\" target=\"_blank\" rel=\"noopener\">3D Vowel Plots with Rayshader<\/a>, Joey Stanley<\/li>\n<\/ul>\n<p><a href=\"https:\/\/rstudio.com\/resources\/rstudioconf-2020\/3d-ggplots-with-rayshader\/?wvideo=a18qo2kjlo\"><img loading=\"lazy\" decoding=\"async\" style=\"width: 400px;height: 225px\" src=\"https:\/\/embedwistia-a.akamaihd.net\/deliveries\/cf41b59697e407eb8f2218372b97e51d.jpg?image_play_button_size=2x&amp;image_crop_resized=960x540&amp;image_play_button=1&amp;image_play_button_color=4287c7e0\" width=\"400\" height=\"225\" \/><br \/>\n3D ggplots with rayshader &#8211; RStudio<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>1. D&#8217;une coupe spectrale\u00a0 \u00e0 un spectrogramme 1.1 Avec Praat Dans le cadre de mon cours de phon\u00e9tique exp\u00e9rimentale, il m&#8217;a toujours sembl\u00e9 complexe, sans recourir \u00e0 une quelconque illustration, de faire comprendre aux \u00e9tudiants le passage d&#8217;une coupe spectrale statique \u00e0 un spectrogramme dynamique. Les deux illustrations suivantes produites avec le logiciel Praat repr\u00e9sentent &hellip; <a href=\"https:\/\/portfolio.uqac.ca\/vincentarnaud\/un-spectrogramme-en-3d-avec-r\/\" class=\"more-link\">Continuer la lecture de <span class=\"screen-reader-text\">Un spectrogramme en 3D avec R<\/span>  <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":129,"featured_media":777,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"_links":{"self":[{"href":"https:\/\/portfolio.uqac.ca\/vincentarnaud\/wp-json\/wp\/v2\/posts\/744"}],"collection":[{"href":"https:\/\/portfolio.uqac.ca\/vincentarnaud\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/portfolio.uqac.ca\/vincentarnaud\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/portfolio.uqac.ca\/vincentarnaud\/wp-json\/wp\/v2\/users\/129"}],"replies":[{"embeddable":true,"href":"https:\/\/portfolio.uqac.ca\/vincentarnaud\/wp-json\/wp\/v2\/comments?post=744"}],"version-history":[{"count":31,"href":"https:\/\/portfolio.uqac.ca\/vincentarnaud\/wp-json\/wp\/v2\/posts\/744\/revisions"}],"predecessor-version":[{"id":1069,"href":"https:\/\/portfolio.uqac.ca\/vincentarnaud\/wp-json\/wp\/v2\/posts\/744\/revisions\/1069"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/portfolio.uqac.ca\/vincentarnaud\/wp-json\/wp\/v2\/media\/777"}],"wp:attachment":[{"href":"https:\/\/portfolio.uqac.ca\/vincentarnaud\/wp-json\/wp\/v2\/media?parent=744"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/portfolio.uqac.ca\/vincentarnaud\/wp-json\/wp\/v2\/categories?post=744"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/portfolio.uqac.ca\/vincentarnaud\/wp-json\/wp\/v2\/tags?post=744"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}