# Actes des Journées d'Informatique Musicale (JIM 2018), Amiens

Louis Bigo, Mathieu Giraud, Richard Groult, Florence Levé

► To cite this version:

Louis Bigo, Mathieu Giraud, Richard Groult, Florence Levé. Actes des Journées d'Informatique Musicale (JIM 2018), Amiens. Journées d'Informatique Musicale (JIM 2018), May 2018, Amiens, France., 2018. hal-01790928

HAL Id: hal-01790928

https://hal.science/hal-01790928v1

Submitted on 20 May 2018

HAL is a multi-disciplinary open access
archive for the deposit and dissemination of sci-
entific research documents, whether they are pub-
lished or not. The documents may come from
teaching and research institutions in France or
abroad, or from public or private research centers.

L'archive ouverte pluridisciplinaire HAL, est
destinée au dépôt et à la diffusion de documents
scientifiques de niveau recherche, publiés ou non,
émanant des établissements d'enseignement et de
recherche français ou étrangers, des laboratoires
publics ou privés.

<figure><img src="image_1.png" /></figure>
JIM 2018

# Journées d'Informatique Musicale

Amiens, 16 – 18 mai 2018

www.algomus.fr/jim2018
Soutiens et sponsors

<figure><img src="image_2.png" /></figure>

<figure><img src="image_3.png" /></figure>

<figure><img src="image_4.png" /></figure>

<figure><img src="image_5.png" /></figure>

<figure><img src="image_6.png" /></figure>

<figure><img src="image_7.png" /></figure>

<figure><img src="image_8.png" /></figure>

<figure><img src="image_9.png" /></figure>

<figure><img src="image_10.png" /></figure>

Région
Hauts-de-France

<figure><img src="image_11.png" /></figure>
Les Journées d'Informatique Musicale 2018, pilotées par l'Association Francophone d'Informatique Musicale (AFIM) ont lieu du 16 au 18 mai 2018 à Amiens, au Logis du Roy, monument du XVIe siècle au cœur du centre historique d'Amiens. Après Bourges (2014), Montréal (2015), Albi (2016) et Paris (2017), les JIM 2018 sont organisées par l'équipe Algomus (CRISTAL, UMR 9189 CNRS, Université de Lille et MIS, Université de Picardie Jules Verne, Amiens, www.algomus.fr).

Le comité de programme a reçu 22 soumissions impliquant au total 31 auteurs. Chaque soumission a été relue et évaluée par au moins 3 membres de la communauté. Au final 13 articles ont été retenus pour une présentation longue et 4 pour une présentation sous forme de poster. Les actes contiennent ainsi des articles longs et des résumés, ainsi qu'une présentation du nouveau groupe de travail de l'AFIM et le résumé des trois présentations invitées.

Le thème choisi pour les JIM 2018, informatique musicale et pédagogie, concerne à la fois l'utilisation de l'outil informatique dans l'apprentissage et la pratique de la musique, mais aussi la pédagogie de l'informatique musicale en tant que discipline. Cinq présentations sont relatives à ce thème, et une table ronde regroupe des enseignants en collège, en lycée et à l'université.

Les journées comportent également une table ronde entreprises réunit des professionnels de la région Hauts-de-France développant des services et des produits liés à la musique et aux nouvelles technologies : Aodyo (contrôleur MIDI *Sylphyo*), Arobas Music (logiciel d'édition de partitions *Guitar Pro*) et Recisio (contenus de karaké, *Jamzone*).

Au programme des JIM figurent enfin des concerts et spectacles (*Solo* de Peter Orins, *Fourier Revient* de Phusis, et un récital de chant lyrique de Bénédicte Hilbert et Anne-Lise Gilet organisé par le CERCLL), la présentation de l'installation *Algo-rhythmme* de Val Kiri, et une visite musicale de la cathédrale d'Amiens.

Merci à l'ensemble des soutiens, à l'AFIM, aux comités de programme et d'organisation, aux auteurs et aux participants !

Louis Bigo

Mathieu Giraud

Richard Groult

Florence Levé
## Comité de programme

Florent Berthaut

Nancy Bertin

Louis Bigo

Bruno Bossis

Muriel Boulan

Jean Bresson

Marc Chemillier

Jean-Marc Chouvel

Julien Debove

Myriam Desainte-Catherine

Dominique Fober

Jean-Louis Giavitto

Mathieu Giraud (co-président)

Richard Groult

Nathalie Hérold

Florent Jacquemard

David Janin

Florence Levé (co-présidente)

Mikhail Malt

Julien Rabin

Marc Rigaudière

Philippe Rigaux

Stephan Schaub

Anna Shvets

Alice Tacaille

Vincent Tiffon

Charlotte Truchet

Stéphanie Weisser

## Comité d'organisation

Florent Berthaut

Louis Bigo (co-président)

Romain Bricout

David Durand

Laurent Feisthauer

Mathieu Giraud

Richard Groult (co-président)

Nicolas Guiomard-Kagan

Emmanuel Leguy

Florence Levé

## Comité de pilotage AFIM

Daniel Arfib

Gérard Assayag

Marc Chemillier

Myriam Desainte-Catherine

Dominique Fober

Mikhail Malt

Yann Orlarey

François Pachet

Laurent Pottier

Julien Rabin

Jean Michel Raczinski

Anne Sedes
Table des matières

<table><tr><td>Soutiens</td><td>iii</td></tr><tr><td>Préface</td><td>v</td></tr><tr><td>Comités</td><td>vii</td></tr><tr><td>Conférenciers invités</td><td>1</td></tr><tr><td>Fourier en musique<br/>Jean-Paul Chehab</td><td>3</td></tr><tr><td>Approches sémiotiques et algorithmiques pour la modélisation de schémas structurels musicaux<br/>Frédéric Bimbot</td><td>5</td></tr><tr><td>Hello World, le making of<br/>François Pachet</td><td>7</td></tr><tr><td>Association Française d'Informatique Musicale</td><td>10</td></tr><tr><td>Présentation du groupe de travail AFIM « Archivage collaboratif et préservation créative »<br/>Alain Bonardi, Serge Lemouton, Laurent Pottier, Jacques Warnier</td><td>11</td></tr></table>
<table>
  <tbody>
    <tr>
      <td>Camomile, enjeux et développements d'un plugiciel audio embarquant Pure Data<br><i>Pierre Guillot</i></td>
      <td>15</td>
    </tr>
    <tr>
      <td>Ordonnancement adaptatif d'un graphe audio avec dégradation de qualité<br><i>Pierre Donat-Bouillud</i></td>
      <td>25</td>
    </tr>
    <tr>
      <td>Synchronisation de données inter-processus dans les applications audio temps réel :<br>qu'est-ce qui débloque?<br><i>Thibaut Carpentier</i></td>
      <td>35</td>
    </tr>
    <tr>
      <td>Une nouvelle implémentation du Spatialisateur dans Max<br><i>Thibaut Carpentier</i></td>
      <td>45</td>
    </tr>
    <tr>
      <td>Étude et appropriation de l'augmentation instrumentale proposée par Jean-Claude<br>Risset dans l'élaboration de ses duos pour un pianiste<br><i>Sébastien Clara</i></td>
      <td>53</td>
    </tr>
    <tr>
      <td>Outils informatiques et analyse musicale : représenter le routing de Jupiter<br><i>Maxence Larrieu</i></td>
      <td>63</td>
    </tr>
    <tr>
      <td>Inférence de segmentation structurelle par compression via des relations multi-échelles<br>dans les séquences d'accords<br><i>Corentin Guichaoua, Frédéric Bimbot</i></td>
      <td>71</td>
    </tr>
    <tr>
      <td>Exploration de dépendances structurelles mélodiques par réseaux de neurones récurrents<br><i>Nathan Libermann, Frédéric Bimbot, Emmanuel Vincent</i></td>
      <td>81</td>
    </tr>
    <tr>
      <td>Évaluation de la correction rythmique des partitions numérisées<br><i>Francesco Foscarin, Florent Jacquemard, Raphaël Fournier-S'Niehotta, Philippe<br>Rigaux</i></td>
      <td>87</td>
    </tr>
    <tr>
      <td>The structural function of musical texture : Towards a computer-assisted analysis of<br>orchestration<br><i>Didier Guigue, Charles de Paiva Santana</i></td>
      <td>97</td>
    </tr>
    <tr>
      <td>Enseigner le patching de manière collective avec le logiciel collaboratif Kiwi<br>(Prix AFIM 2018)<br><i>Philippe Galleron, Eric Maestri, Jean Millot, Alain Bonardi, Eliott Paris</i></td>
      <td>105</td>
    </tr>
    <tr>
      <td>Appropriating Music Computing Practices Through Human-AI Collaboration<br><i>Hugo Scurto, Frédéric Bevilacqua</i></td>
      <td>115</td>
    </tr>
    <tr>
      <td>RéDi-Musix : réseau pour la distribution de la musique mixte<br><i>Alexander Mihalic, Mikhail Malt</i></td>
      <td>121</td>
    </tr>
  </tbody>
</table>
<table><tr><td><strong>Posters</strong></td><td style="text-align: right;"><strong>129</strong></td></tr><tr><td>Music from Aura<br><em>Pierre-Henri Vulliard</em></td><td style="text-align: right;">131</td></tr><tr><td>La théorie de la gravitation tonale<br><em>Emanuele Di Mauro</em></td><td style="text-align: right;">135</td></tr><tr><td>Polyphonic Singing presented as a Multiplayer Classroom Online Game<br><em>Jonathan Bell</em></td><td style="text-align: right;">139</td></tr><tr><td>Learning Geometry and Music through Computer-aided Music Analysis and Composition : A Pedagogical Approach<br><em>Sonia Cannas</em></td><td style="text-align: right;">143</td></tr><tr><td><strong>Index des auteurs</strong></td><td style="text-align: right;"><strong>147</strong></td></tr></table>

À l'issue des JIM, Philippe Galleron et Éric Maestri ont été désignés lauréats du prix *jeune chercheur* AFIM 2018. Le public tout comme le jury AFIM a particulièrement apprécié leur étude en situation pédagogique de Kiwi sur le terrain... qui était de plus parfaitement adaptée au thème de cette année.
Conférences invitées
# FOURIER EN MUSIQUE

Jean-Paul Chehab

LAMFA, Amiens

Université de Picardie Jules Verne

Le travail le plus célèbre de Joseph Fourier (1768-1830) porte sur la théorie
analytique de la chaleur (1807) pour laquelle il a introduit une technique de
résolution extrêmement novatrice, dont les développements et les applications
ont bouleversé tous les domaines scientifiques. Si la musique n’était pas ini-
tialement considérée, l’analyse de Fourier se prête particulièrement bien à la
nature vibratoire du son. Après avoir retracé la biographie du savant et re-
placé son travail fondateur dans son contexte, nous décrirons les phénomènes
sonores, d’abord du point de vue physique à travers la notion de modes propres
des instruments (corde, timbale, guitare) puis de celui du traitement du signal,
s’agissant de leurs représentations fréquentielles (spectrogramme). Cet exposé
grand public s’inscrit dans le cadre de la célébration du 250ᵉ anniversaire de la
naissance de Joseph Fourier.
APPROCHES SÉMIOTIQUES ET ALGORITHMIQUES
POUR LA MODÉLISATION DE SCHÉMAS
STRUCTURELS MUSICAUX

Frédéric Bimbot

IRISA, CNRS

Modéliser la structure musicale à travers différents styles est un enjeu impor-
tant de la recherche en informatique musicale. Cet exposé présentera l'approche
"Système & Contraste" qui permet de décrire avec une grande généralité l'orga-
nisation multi-échelle des éléments au sein des segments structurels musicaux.
HELLO WORLD, LE MAKING OF

François Pachet

Spotify Paris

L'album *Hello world*, sorti en janvier 2018, est le premier à avoir utilisé l'intelligence artificielle de manière systématique pour la composition et l'orchestration de titres de musique mainstream, dans des styles divers. Cet exposé présentera la conception de cet album, en détaillant les enjeux techniques portant sur la génération (partitions et audio) tout comme les enjeux musicaux.
# PRESENTATION DU GROUPE DE TRAVAIL AFIM
## « ARCHIVAGE COLLABORATIF ET PRESERVATION CREATIVE »

Alain Bonardi

Serge Lemouton

Laurent Pottier

Jacques Warnier

CICM-MUSIDANSE

IRCAM

CIEREC Université

CNSMDP

Université Paris 8

lemouton@ircam.fr

Jean-Monnet

JWarnier@cnsmdp.fr

alain.bonardi@gmail.com

laurent.pottier@univ-
st-etienne.fr

## RÉSUMÉ

Le groupe de travail AFIM « Archivage collaboratif et préservation créative » a été lancé mi-décembre 2017. Ce texte en présente les objectifs initiaux, les tout premiers travaux et les perspectives de démarrage. A l'occasion des Journées d'Informatique Musicale 2018, il s'agit essentiellement d'engager le dialogue avec les centres de créations, les RIMs et toutes les personnes intéressées par ces thématiques.

## 1. PRÉSENTATION DES OBJECTIFS INTIAUX

Partant du constat de la volatilité des musiques informatiques et mixtes et des difficultés éprouvées à les rejouer, les réinterpréter ou les restaurer, l'objectif de ce groupe lancé mi-décembre 2017 est de travailler sur les modalités d'écriture des œuvres de ce répertoire permettant de les diffuser, de les enseigner et de les préserver.

A l'IRCAM, depuis une vingtaine d'années, ont été développés des outils (sous la forme d'une base de données accessible en ligne<fnref n="1" />) et des pratiques (principalement celle des réalisateurs en informatique musicale) destinées à préserver le répertoire propre de l'institut. D'autres studios ont également développé de telles pratiques. Nous pensons qu'il est temps de réfléchir à un modèle commun d'archives partagées.

Nous nous situons dans la continuité du groupe sur les nouveaux espaces de la notation musicale [3] puisque l'archive, au même titre que la partition musicale, est le lieu qui permet à la fois de collecter, de conserver et de consulter les intentions musicales du compositeur dans le but de pouvoir les ré-interpréter et les restituer aux auditeurs.

Il faut insister sur la nature en perpétuelle évolution du contenu de ces archives. A chaque remédiation de l'œuvre, pour des raisons techniques (mises à jour de logiciels, obsolescence des matériels) ou esthétiques, des modifications, des mises à jour sont effectuées sur les programmes, sur les patchs, sur les sons. Cela implique que les systèmes destinés à préserver les œuvres doivent à la fois avoir un contenu dynamique pour que les contenus puissent être facilement mis à

jour. Ils doivent également permettre une traçabilité de l'évolution des différentes versions.

Un autre trait caractéristique est la multiplicité des acteurs qui interviennent sur ces archives : compositeurs, interprètes (instrumentistes et réalisateurs en informatique musicale), ingénieurs du son, documentalistes, musicologues, étudiants, éditeurs, producteurs. Sur une même œuvre, voire sur une même version, l'archive doit permettre à ces différents profils de collaborer.

La question des droits légaux et des licences d'utilisation dans ce contexte fortement collaboratif devra également être abordée.

L'objectif sera de faire le point sur les différents acteurs nationaux et internationaux proposant des démarches et des outils visant à la conservation des œuvres numériques, afin d'évaluer les dispositifs les plus appropriés pour traiter du cas des musiques informatiques.

Un des objectifs principaux de notre groupe sera ensuite d'établir un modèle opérationnel et fonctionnel d'archive ouverte participative permettant la diffusion et l'inscription dans la durée des œuvres de musique informatique. Dans le cadre de ce groupe de travail, on envisage de développer (au cours de la seconde année du projet) un site prototype répondant à ce modèle et mis en ligne, permettant aux partenaires du projet de mettre en commun leurs archives.

## 2. TRAVAUX PRÉVUS DU GROUPE EN 2018

Dans un premier temps, le groupe de travail se consacre à un état de l'art des pratiques de documentation des œuvres musicales avec technologie. Pour commencer, nous avons mené une enquête auprès des Centres Nationaux de Création Musicale (CNCM) : GRAME à Lyon, GMEM à Marseille, CIRM à Nice, Césaré à Reims, La Muse en circuit à Paris-Alfortville ainsi qu'auprès du SCRIME à Bordeaux. Les résultats montrent qu'il n'y a pas d'action concertée ni de pratique rigoureuse pour la préservation à long terme ou la documentation de la musique utilisant l'informatique temps réel. Les résidences de compositeurs dans ces centres sont souvent de courte durée (quelques semaines), ce qui ne laisse en général pas de temps pour la documentation. D'une manière générale, les fichiers sont correctement sauvés, les

<figure><img src="image_12.png" /></figure>

<footnote n="1">¹ http://brahms.ircam.fr/sidney/</footnote>
œuvres sont enregistrées et parfois filmées, les parties électroniques sont parfois enregistrées comme fichiers audio séparés. La tendance pour les œuvres temps réel est d'éviter des outils logiciels commerciaux fermés et de se limiter aux objets standard dans le cas de patchs Max. Les sauvegardes concernent trois aspects de l'œuvre : l'architecture des traitements DSP [4], la gestion des événements (déclenchements, presets, suivi de partition) et les interfaces de contrôle gestuel.

Cet état de l'art passe également par la consultation des résultats obtenus lors de projets antérieurs de recherche en documentation des œuvres musicales avec technologie, par exemple MUSTICA (2003-2006) [1] ou ASTRÉE (2009-2011) [2].

Une des étapes importantes de l'année est l'analyse des pratiques actuelles à l'IRCAM autour de la base de documentation des œuvres Sidney [5], qui servira de base à notre réflexion et nos propositions à venir, à partir d'un ensemble de « bonnes pratiques » que nous aurons identifiées.

### 3. REFERENCES

[1] Bachimont, B., et al. (2003). Preserving Interactive Digital Music: A Report on the MUSTICA Research Initiative, Proceedings of the Third International Conference on WEB Delivering of Music (WEB'03), Leeds, England.

[2] Bonardi, A. (2013). Pérenniser pour transmettre, transmettre pour pérenniser - Destins de l'œuvre mixte interactive - Autour de En Echo, pièce de Philippe Manoury, Musique et Technologie - Préserver, archiver, re-produire, Portraits polychromes, hors-série thématique, Paris, Institut National de l'audiovisuel, p.105-126.

[3] Fober, D., Bresson, J., Couprie, P., Geslin, Y. (2015). Les nouveaux espaces de la notation musicale : Groupe de travail AFIM, Actes des Journées d'Informatique Musicale, 2015, Montréal, Canada.

[4] Pottier, L. (2013). La régénération des sons de Turenas de John Chowning, Préserver - Archiver - Re-produire : musique et technologie, jeux vidéo, dir. Evelyne Gayou, Portraits polychromes, hors-série thématique n°21, Paris, INA-GRM, p.145-196.

[5] Lemouton, S. & Goldszmidt, S. (2016). La préservation des œuvres du répertoire IRCAM : Présentation du modèle Sidney et analyse des dispositifs temps réel, Actes des Journées d'Informatique musicale, Albi, GMEA, 2016.
# CAMOMILE, ENJEUX ET DEVELOPPEMENTS D'UN PLUGICIEL AUDIO EMBARQUANT PURE DATA

Pierre Guillot
CICM – EA1572
Université Paris 8
guillotpierre6@gmail.com

## RÉSUMÉ

Cet article présente Camomile un outil plugiciel audio multiplateforme, compatible avec de multiples formats et permettant de charger des patchs Pure Data dans les stations de travail audionumérique. Cette présentation reviendra tout d'abord sur les contextes d'utilisation dans lesquels la nécessité d'un tel outil est apparue. L'objectif sera de mettre en avant les principaux enjeux du projet sur un plan fonctionnel, usuel et technique. Par la suite, un état de l'art, général mais non exhaustif, sera présenté en revenant sur les différentes propositions et les évolutions dans ce domaine. Parcourir ces travaux, des premières approches des logiciels de type patcher embarqués jusqu'aux projets plus récents de plugiciels audio, permettra notamment de clarifier les difficultés, les contraintes et les questions soulevées par cette démarche afin de comprendre les choix opérés lors de la mise en œuvre du projet Camomile. Des solutions seront alors apportées en revenant sur les principaux axes du développement de l'outil. Il s'agira de présenter les choix importants opérés au cours des différentes versions et les répercussions sur l'outil final et son usage. Enfin, un bilan et les perspectives de ce projet seront exposés.

## 1. INTRODUCTION

Camomile<fnref n="1" /> est un outil permettant de créer des plugiciels audionumériques – des modules externes destinés à être utilisés dans les stations de travail audionumériques – à partir de patchs Pure Data [1]<fnref n="2" />. Cet outil est lui-même un plugiciel aux formats VST2, VST3³ et Audio Unit<fnref n="4" /> pour les systèmes d'exploitation Linux, Windows et MacOs embarquant le moteur de Pure Data. Après avoir créé un traitement ou un synthétiseur sonore dans Pure Data, l'utilisateur peut utiliser les plugiciels de Camomile comme base, et leurs associer les patchs élaborés afin de concevoir des plugiciels autonomes (Figure 1).

<figure><img src="image_13.png" /><figcaption>**Figure 1.** Fonctionnement de la création d'un nouveau plugiciel à partir d'un patch Pure Data et d'un plugiciel Camomile.</figcaption></figure>

Cette approche offre donc à la fois les avantages du plugiciel, notamment via la variété des stations de travail audionumérique à laquelle elle donne accès, ainsi que ceux de Pure Data, à savoir son dynamisme et sa modularité. De la part de l'utilisateur, il s'agira de comprendre comment créer des patchs afin de tirer profit de toutes les fonctionnalités offertes par les plugiciels et les stations de travail. Ce sujet est présenté dans la documentation fournie avec l'outil Camomile et sera traité dans une prochaine publication. Car, avant cela, il semble intéressant de revenir sur les raisons de la mise en œuvre de cet outil ainsi que sur les choix opérés lors de celle-ci. En effet, c'est de cela même que résultent les modalités d'utilisation de l'outil. Cet article revient donc dans la section 2, sur le contexte et les enjeux de cette mise en œuvre, afin de présenter les spécifications techniques fonctionnelles qui ont permis de déterminer les choix de développement. Puis, dans la section 3, les travaux à la fois préexistants et parallèles au projet Camomile seront présentés, afin de mettre en avant les questions et les problématiques importantes qu'il est nécessaire de résoudre. Suite à cela, dans la section 4, les axes importants de la mise en œuvre de Camomile seront explorés, en allant des premiers développements en mai 2015 à la dernière version publiée en décembre 2017 (Figure 2). Enfin dans la section 5, ce travail sera mis en

<figure><img src="image_14.png" /></figure>

<footnote n="1">¹Les différentes versions du plugiciel sont disponibles en ligne sur le répertoire Github du projet. Le code source est ouvert, libre et gratuit et est disponible sur ce même répertoire. Depuis la version 0.1.0, les sources sont distribuées sous licence GNU GPLv3. Les sources des versions antérieures sont distribuées sous licence BSD 3 github.com/pierreguillot/camomile (site consulté en janvier 2018).</footnote>

<footnote n="2">²Pure Data est un logiciel de type patcher libre et gratuit, créé par Miller Puckette à l'Université de San Diego en Californie msp.ucsd.edu/software.html (site consulté en janvier 2018).</footnote>

<footnote n="3">³Les formats de plugiciel audionumérique VST (Virtual Studio Technology) 2 et 3 sont développés par la société Steinberg www.steinberg.net (page consultée en janvier 2018).</footnote>

<footnote n="4">⁴Le format de plugiciel audionumérique Audio Unit est développé par Apple developer.apple.com/audio/ (page consulté en janvier 2018).</footnote>
perspective en revenant sur les questions qu'il reste à
explorer mais aussi sur le potentiel offert par un tel outil.

<figure><img src="image_15.png" /><figcaption>**Figure 2.** Le plugiciel *Castafiore* distribué en exemple avec la dernière version de Camomile et permettant de modifier la hauteur du signal sonore en fonction d'un paramètre de transposition contrôlable via l'interface graphique avec soit une boîte nombre, soit une glissière (slider) horizontale.</figcaption></figure>

## 2. CONTEXTES ET ENJEUX

Le plugiciel Camomile a été élaboré afin de répondre
à l'envie et la nécessité d'utiliser des patchs Pure Data au
sein d'une station de travail audionumérique. Bien que
relativement spécifique, ce besoin se manifeste dans des
pratiques, des usages et des contextes variés, qui
répondent à différents enjeux, desquels découlent des
spécifications qui peuvent être complémentaires. Cette
partie revient sur ces questions, afin de comprendre et
mettre en perspective les travaux préexistants présentés
par la suite, ainsi que les choix qui ont été opérés lors de
la mise en œuvre de Camomile.

**2.1. Contextes du projet**

En 2015, au début du projet, l'envie d'utiliser des
patchs dans une station de travail audionumérique
provient du besoin de faciliter le contrôle des traitements
audionumériques mis en œuvre dans un contexte de
temps différé. L'objectif est notamment de tirer profit de
la ligne temporelle généralement présente dans les
stations de travail audionumérique<fnref n="5" />. De nombreux

praticiens de l'informatique musicale ont d'ailleurs fait
part de leurs attentes à ce sujet, ce qui amène notamment
en 2013 l'équipe du CICM, dans le cadre des projets
HOA [4], à concevoir un plugiciel VST offrant alors la
possibilité de spatialiser des sources directionnelles en
ambisonie [5]<fnref n="6" />. Il a alors été envisagé de proposer
d'autres traitements originaux de l'espace et du son – tels
que ceux offerts par les versions pour les logiciels Max<fnref n="7" />
et Pure Data de la bibliothèque HOA<fnref n="8" /> – mais faute de
temps et en raison de la complexité des opérations
nécessaires à leurs mises en œuvre, cet objectif n'a jamais
pu être réalisé. Suite au prolongement des
expérimentations et à l'approfondissement des travaux de
recherches sur la spatialisation du son, notamment via le
logiciel Pure Data [7], la seule solution viable pour
répondre à ce problème semble être un plugiciel flexible
et dynamique permettant de charger des patchs. Cette
approche permet d'assurer une bonne reproduction du
rendu sonore des traitements réalisés sous forme de patch
au sein des stations de travail audionumérique, tout en
limitant le temps de développement. Les logiciels de type
patcher sont idéals pour l'expérimentation mais prendre
en compte et intégrer dans un plugiciel les nombreux et
fréquents changements que cela implique est très
chronophage. Ainsi, l'enjeu du projet est aussi d'offrir un
outil plus adapté à un contexte de développement et
recherche. Enfin, la nécessité d'un tel outil s'est fait
ressentir dans un cadre pédagogique. Le département de
Musique de l'Université Paris 8 propose un cours de 2ème
année de Licence d'Introduction à la Programmation
avec Max et Pure Data<fnref n="9" /> où beaucoup d'étudiants
découvrent pour la première fois les logiciels de type
patcher [9]. Aussi, l'un des défis pédagogiques est de
démontrer le potentiel de ces outils via, entre autres, des
conditions et des contextes concrets d'utilisations. Les
étudiants sont familiers des stations de travail
audionumérique et les utilisent généralement déjà
fréquemment. Ainsi, proposer un plugiciel permettant
d'utiliser un traitement créé sous forme de patch
directement dans un de ces logiciels, offre une réponse à
ce problème<fnref n="10" />. Ce projet tente donc de répondre à des
problématiques provenant d'une approche d'utilisateurs,
de développeurs mais aussi d'enseignants.

## 2.2. Spécifications

Ces observations amènent à définir un certain nombre
de spécifications fonctionnelles et techniques.

<footnote n="5">⁵Dans cet article, il ne s'agit pas d'approfondir la question de la ou des représentations du temps offertes de manière native au sein du logiciel Pure Data, d'autant qu'il est possible de combiner Pure Data à d'autres outils d'écriture temporelle tels que les logiciels Ossia développé au LaBRI [2] ou Iannix [3]. Il s'agit simplement de mettre en avant un besoin et une attente qui est plus liée à des questions d'accessibilité et de facilité de mise en œuvre et d'utilisation.</footnote>

<footnote n="6">⁶Le plugiciel est à présent non fonctionnel mais il est cependant toujours disponible sur le site du projet HOA www.mshparisnord.fr/hoalibrary (site consulté en janvier 2018).</footnote>

<footnote n="7">⁷Le logiciel Max [6], originellement développé à l'IRCAM par Miller Puckette est aujourd'hui développé et distribué par la société Cycling'74 cycling74.com (site consulté en janvier 2018).</footnote>

<footnote n="8">⁸Ces opérations sont par exemple construites autour de la synthèse granulaire ou d'un filtre à réponse impulsionnelle infinie réalisés sous forme de patch dans les logiciels Max et Pure Data. La complexité est alors de recréer en C et C++ l'ensemble des traitements audionumériques des objets utilisés, ainsi que leurs réseaux de connexions mais aussi les interfaces graphiques utilisateurs, même basiques, permettant de contrôler ces traitements.</footnote>

<footnote n="9">⁹Ce cours a été dispensé depuis de nombreuses années par Anne Sèdes, Alain Bonardi, puis par moi-même en 2016 et 2017 et à présent par E. Maestri qui intègre notamment le logiciel Kiwi [8] à l'apprentissage.</footnote>

<footnote n="10">¹⁰Au cours de ces années d'enseignement, savoir s'ils pouvaient utiliser leurs patchs sur des stations de travail audionumérique était en effet une question récurrente des étudiants.</footnote>
### 2.2.1. *Intégration du moteur patcher*

Afin de répondre à l'ensemble de ces usages dans des contextes compositionnels, expérimentaux, pédagogiques ou de développements, l'idéal serait évidemment que l'outil fonctionne sur le plus grand nombre de plateformes logicielles – et donc le plus grand nombre de formats de plugiciel audionumérique – et de systèmes d'exploitation. L'objectif serait de concevoir un outil compatible avec les systèmes Linux, MacOS et Windows<fnref n="11" />, sous forme de plugiciel intégrant les technologie VST, Audio Unit ou encore LV2¹². Sur un aspect plus fonctionnel, il est primordial que le rendu sonore du patch chargé au sein de la station de travail audionumérique soit identique à celui offert par l'application de bureau originel qui a permis de le créer. Cette spécification amène à réutiliser le moteur sonore originel mais aussi implicitement à éviter les logiciels au code source fermé, qui empêchent donc toute réutilisation<fnref n="13" />. De manière générale, le plugiciel final devrait satisfaire le maximum de spécifications définies par les différents formats et les stations de travail audionumérique, et notamment la capacité de gérer plusieurs instances, mais aussi d'être utilisable dans un contexte de *multithreads*<fnref n="14" />. Le premier enjeu principal du développement est donc, au travers du plugiciel, de réaliser le lien entre la station de travail audionumérique et le moteur de type *patcher*, en faisant concorder leurs spécifications et leurs recommandations respectives.

### 2.2.2. *Gestion du moteur patcher et du plugiciel*

Évidemment, afin d'être utilisable, l'outil doit aussi proposer à l'utilisateur un moyen de définir et contrôler l'ensemble des fonctionnalités génériques d'un plugiciel, telles que les paramètres, les préréglages, la latence, etc. L'une des spécifications à ce sujet serait que l'utilisateur souhaitant créer un patch destiné au plugiciel audio n'ait pas à devoir coder en dehors des langages initiaux de type *patcher*, ou du moins n'ait pas à procéder à des opérations qui demandent des notions complexes d'informatique, telles que la compilation d'un code. En effet, si l'outil final est destiné à des étudiants, ce type d'approche peut rapidement décourager son utilisation. Cette spécification liée aux questions d'accessibilité est aussi importante dans un contexte de développement, car elle assure la portabilité d'un même patch sur l'ensemble des plateformes logicielles et des systèmes d'exploitations supportés par le plugiciel. Enfin, les logiciels de type *patcher* étant des applications de programmation

graphique, il serait aussi intéressant de pouvoir offrir la possibilité de créer l'interface graphique utilisateur du plugiciel, via l'interface originale du programme. Le deuxième enjeu principal est de créer un système commun de gestion et de communication entre le logiciel de type *patcher* et le plugiciel. Cela afin de pouvoir définir les propriétés du plugiciel à partir du *patcher* et d'adapter le fonctionnement des patchs en fonction des informations envoyées par le plugiciel.

## 3. ÉTAT DE L'ART

Avant de proposer une réponse à ces enjeux et ces spécifications en exposant les choix réalisés lors de la mise en œuvre du plugiciel Camomile, cette partie revient sur les propositions préexistantes ou développées parallèlement, afin de s'en inspirer et d'éviter les problèmes qu'ils ont pu rencontrer.

### 3.1. Des origines de Max à Max for Live

L'idée d'utiliser des patchs en tant que module d'extension au sein d'un système tiers n'est pas nouvelle. Dès 1988, l'un des objectifs du logiciel Max est notamment de pouvoir créer des synthétiseurs audionumériques sous forme de patch, pouvant être utilisés sous forme de carte de traitement du signal<fnref n="15" />. Cette proposition ne restera longtemps qu'à l'état d'idée. En effet, à ses débuts le logiciel était exclusivement orienté sur le traitement de messages et l'intégration du traitement du signal audionumérique dans Max n'apparaîtra qu'en 1991 [10]. C'est à la suite du développement de l'audionumérique avec l'émergence des stations de travail audionumérique telles que Cubase en 1996 du format VST<fnref n="16" />, que l'idée pourra voir le jour. Notamment en remplaçant les cartes de traitement du signal par des plugiciels audionumériques. En 1998 la société Cycling'74 propose une extension à Max nommée Pluggo [11], permettant de charger des patchs au sein d'une station de travail audionumérique via un plugiciel VST. Cet outil offre un nombre de fonctionnalités très complètes, telles que la création du moteur sonore, la création d'une interface graphique originale et d'un système permettant de configurer et gérer les paramètres, notamment à l'aide d'objets Max dédiés spécifiquement à cet usage. Le plugiciel de Pluggo fonctionne avec une version d'exécution de Max embarquée<fnref n="17" /> qui permet de charger dynamiquement des patchs. Cet outil offre aussi la possibilité de générer des

<footnote n="11">¹¹Il est raisonnable d'affirmer que l'usage de cet outil sur d'autres systèmes d'exploitation ou avec d'autres formats de plugiciel audionumérique se révèle purement anecdotique pour le moment.</footnote>

<footnote n="12">¹²Les informations relatives au format LV2 sont disponibles sur le site lv2plug.in (site consulté en janvier 2018).</footnote>

<footnote n="13">¹³Cette spécification est d'autant plus en accord avec une approche de recherche et recoupe des questions pédagogiques car les applications libres et ouvertes sont aussi souvent gratuites ou du moins plus accessibles.</footnote>

<footnote n="14">¹⁴Système permettant plusieurs tâches d'exécutions en parallèle, ce qui est souvent le cas sur les ordinateurs modernes et les logiciels actuels.</footnote>

<footnote n="15">¹⁵"Max has hooks for patching a digital synthesizer, which may just be (as now) an interpreting program in the Macintosh or may (in the future) be a plug-in signal-processing card.", [9], p. 420.</footnote>

<footnote n="16">¹⁶En 1996, la société Steinberg publie la station de travail audionumérique Cubase 3.02 avec notamment l'intégration de la technologie VST, information d'après artisteaudio.fr/steinberg-cubase/ (page consultée en janvier 2018).</footnote>

<footnote n="17">¹⁷Il est possible exclusivement de jouer le patch. L'édition doit être réalisée au sein du logiciel Max original.</footnote>
nouveaux plugiciels en embarquant directement les patchs dans des copies indépendantes et distinctes du plugiciel original de la distribution. Le développement du projet prend fin en 2006 avec la version 3.6.1 pour Max 4.6.2, ce qui la rend aujourd'hui dépréciée et restreint fortement toute utilisation. La technologie est cependant réintégrée en 2009 au sein de l'extension Max for Live, permettant de charger des patchs au sein du logiciel Ableton Live<fnref n="18" />, dont le fonctionnement y est plutôt similaire<fnref n="19" />. Cet outil puissant, de par ses nombreuses années de développement et d'usage, souffre néanmoins d'une double restriction. Celui-ci n'est plus réservé qu'à une seule station de travail audionumérique, Ableton Live, dont la technologie est fermée tout comme celle du logiciel Max. Aussi, cela freine son appropriation dans un contexte de recherche, où les notions de propriété et d'ouverture sont importantes<fnref n="20" />. De même dans un contexte d'utilisation lié à un cadre pédagogique, où le prix cumulé des technologies devient rapidement un frein à l'appropriation des outils de la part des étudiants universitaires.

## 3.2. De Pure Data au moteur libpd

Le logiciel Pure Data, par sa gratuité, son fonctionnement multiplateforme, son code source ouvert, est accessible à tout un chacun et accepte les ajouts et modifications par des personnes extérieures<fnref n="21" />. Il est ainsi une excellente alternative à l'usage de Max et offre un terrain pour l'émergence d'outils adaptés aux besoins pédagogiques mais aussi de recherche. C'est donc naturellement vers cet outil que les développeurs se tournent lorsqu'il s'agit de s'approprier un moteur audio offrant un modèle de type *patcher*. Pure Data est déjà utilisé par de nombreuses plateformes et de nombreux outils logiciels en tant que moteur audio. Un des premiers projets utilisant cette technologie est PDa (Pure Data Anywhere), réalisé en 2003 par Günter Geiger [13] et visant à faire fonctionner le moteur audio du logiciel sur un PocketPC. Par la suite en 2010, un ensemble de développeurs<fnref n="22" /> proposent libpd, une bibliothèque en C

embarquant le moteur<fnref n="23" /> de Pure Data et offrant des interfaces de programmation en C++, Java, Processing, Objective-C et Python, qui permettent notamment un support pour les plateformes Android and iOS [14]. Cette bibliothèque est utilisée dans de très nombreux projets<fnref n="24" />, dont les applications pour téléphones et tablettes tactiles iOS<fnref n="25" /> avec PdParty de Dan Wilcox [15] ou son homologue pour Android, dont elle est inspirée avec PdDroidParty<fnref n="26" />, développée par Chris McCormick. Dès lors qu'il est possible d'embarquer le moteur de Pure Data au sein d'autre application via libpd, plusieurs projets de plugiciels permettant de charger des patchs Pure Data voient le jour. Par exemple, le plugiciel au format LV2, PdLV2²⁷ qui fonctionne sur les systèmes d'exploitation Linux et dont le développement date d'au moins 2013, ou plus récemment le plugiciel VST, PdPulp<fnref n="28" /> qui fonctionne sur le système d'exploitation MacOS, et dont le développement est contemporain de celui de Camomile. Ces projets sont extrêmement expérimentaux et possèdent sensiblement moins de fonctionnalités qu'un outil tel que Max for Live. Ils sont par conséquent encore difficilement utilisables dans un contexte de production. Il n'est pas donc nécessaire de revenir avec précision sur ces mises en œuvre, mais il est important de relever un problème crucial lié directement à l'architecture du moteur Pure Data. Le logiciel ayant été pensé comme une application autonome, le noyau du code restreint son usage et le résultat concret pour les utilisateurs est que seule une instance d'un plugiciel peut être chargée dans la station de travail audionumérique<fnref n="29" />. Ce problème est primordial à résoudre car l'utilisation multiple d'un même plugiciel dans les chaînes de traitement audio est ancré dans les usages.

## 3.3. Au-delà de libpd

Deux approches ont néanmoins réussi si ce n'est à résoudre le problème, du moins à le contourner. L'une se trouve au sein du plugiciel PdVst~ originellement développé en 2004 par Joseph Jarlo<fnref n="30" /> puis repris depuis 2016 par Jean Yves Gratius<fnref n="31" />. Dans ce plugiciel VST,

<footnote n="18">¹⁸Le transfert de la technologie de Pluggo vers Max for Live a été annoncé en mai 2009 sur le site de Cycling74.com/newsletters/pluggo-technology-moves-to-max-for-live (page consultée en janvier 2018).</footnote>

<footnote n="19">¹⁹Évidemment de nouvelles fonctionnalités sont apparues et/ou ont été modifiées mais le mécanisme général y est relativement similaire.</footnote>

<footnote n="20">²⁰La comparaison entre le développement de Pure Data et de jMax est un bon exemple de ce problème [12].</footnote>

<footnote n="21">²¹"Pd was instantly embraced by a huge, and extremely hip, community of users who have taken it far beyond my wildest dreams of it [...] One meaning of Pd was "Public Domain", [12], p. 200.</footnote>

<footnote n="22">²²Les créateurs de libpd sont à l'époque une équipe rencontrée sur internet et composée de Peter Brinkmann, Peter Kirn, Richard Lawler, Chris McCormick, Martin Roth & Hans-Christopher Steiner.</footnote>

<footnote n="23">²³Le moteur est ici pris dans son sens le large, avec la partie dédiée à l'audio, aux messages, au MIDI et autres normes, telles que l'OSC.</footnote>

<footnote n="24">²⁴Il serait vain de vouloir citer l'ensemble de ces projets, surtout qu'une majeure partie semble rester fermée ou inaccessible. Mais un échantillon d'exemples est disponible sur le site du projet libpd.cc (site consulté en janvier 2018).</footnote>

<footnote n="25">²⁵Système d'exploitation mobile développé par Apple www.apple.com/fr/ios/ios-11 (site consulté en janvier 2018).</footnote>

<footnote n="26">²⁶L'application est disponible sur le site www.droidparty.net (site consulté en janvier 2018).</footnote>

<footnote n="27">²⁷Le plugiciel est originellement développé par l'utilisateur de Github "unknownError" et est publié depuis 2013 sur le répertoire Github github.com/unknownError/pdLV2-stereo (consulté en janvier 2018). Depuis 2016, il est mis à jour et maintenu par Alex Norman et accessible sur son répertoire Github github.com/x37v/pdlv2 (consulté en janvier 2018).</footnote>

<footnote n="28">²⁸Le plugiciel est développé par Karl Pannek et Oliver Greschke depuis juillet 2015 et est disponible sur le site pd-pulp.net (consulté en janvier 2018).</footnote>

<footnote n="29">²⁹Ce diagnostic est aussi fait lors des premiers essais de mise en œuvre de Camomile, cependant les développements et mises à jours parallèles de Pure Data permettront, comme cela sera présenté dans le suite de l'article, de proposer une solution honorable et intermédiaire avant d'arriver à une solution optimale avec les améliorations plus récentes de Pure Data.</footnote>

<footnote n="30">³⁰Le lien crca.ucsd.edu/~jsarlo/pdvst permettant d'accéder au projet initial n'est plus valide en 2018.</footnote>

<footnote n="31">³¹Le plugiciel et les informations relatives au projet sont disponibles via le répertoire Github de Jean Yves Gratius github.com/jyg/pure-data (site consulté en janvier 2018).</footnote>
Pure Data n'est pas réellement embarqué. Pour chaque instance du plugiciel, une instance de l'application de Pure Data est démarrée de telle sorte que chaque application possède un espace mémoire qui lui est propre, évitant dès lors des conflits<fnref n="32" />. Aussi, le plugiciel fonctionne en réalité comme un pont entre ces applications Pure Data associées aux plugiciels et la station de travail audionumérique. Le problème majeur reste que cet outil est restreint au système d'exploitation Windows. Il serait donc nécessaire d'étudier son potentiel fonctionnement sur d'autres systèmes d'exploitation, mais aussi de vérifier si cette approche n'implique pas de latence et si la communication entre les instances de Pure Data et la station de travail audionumérique via ce système est réellement stable, notamment dans un contexte de *multithreads*<fnref n="33" />. Une deuxième approche est proposée par la plateforme Heavy<fnref n="34" /> distribuée par la société Enzien Audio, qui offre la possibilité de générer directement un plugiciel VST à partir d'un patch Pure Data. La plateforme offre un système de compilation en ligne, mais au lieu d'intégrer Pure Data au plugiciel, Heavy interprète le patch avec son propre moteur qui, quant à lui, offre un fonctionnement multi-instances. La force première de cette approche est aussi son principal défaut. En effet, la plateforme n'utilisant pas réellement Pure Data, elle ne propose qu'un sous ensemble de ses fonctionnalités, ce qui restreint les possibilités, et implique immanquablement un retard par rapport aux mises à jour de la distribution de Pure Data vanilla<fnref n="35" />. De plus, l'exacte similitude du rendu sonore entre le patch dans le logiciel Pure Data et le plugiciel généré n'est pas assurée, du fait de la réécriture totale ou partielle du moteur, afin de le rendre opérable dans un système multi-instances.

# 4. MISE EN ŒUVRE DE CAMOMILE

Lors de la définition des spécifications techniques et fonctionnelles de l'outil Camomile, deux enjeux majeurs sont apparus. Cette partie revient donc sur les choix opérés lors de mise en œuvre de l'outil Camomile d'une part, au niveau de l'intégration du moteur *patcher* dans le plugiciel et d'autre part, au niveau de la création d'un système de gestion réciproque entre le moteur *patcher* et le plugiciel. Cela en les confrontant à l'état de l'art présenté dans la partie précédente. À ce jour, deux versions majeures de Camomile ont été réalisées. Le changement de version correspond à une amélioration de la technologie utilisée. Sans pour autant revenir sur

l'ensemble des spécificités de chaque version, les avantages et les conséquences de ces choix seront discutés.

## 4.1. Intégration du moteur dans le plugiciel

Pour les raisons évoquées précédemment, le plugiciel vise à utiliser le moteur de Pure Data qui offre une licence très libre<fnref n="36" /> et un usage gratuit, tout en ayant une large communauté d'utilisateurs très actifs [12]. D'un autre côté, l'interface de programmation applicative JUCE<fnref n="37" /> a été choisie, car elle offre entre autre la possibilité avec un seul code générique, de créer des plugiciels pour les formats VST et Audio Unit, ainsi que pour les systèmes d'exploitations Windows, Linux et MacOS. L'enjeu est alors de réussir à embarquer le moteur de Pure Data au sein du plugiciel. Or, comme cela a été évoqué précédemment, la difficulté première est de rendre le moteur de Pure Data compatible avec un usage multi-instances dans un contexte de *multithreads*.

### 4.1.1. L'approche séquentielle

Les développements de Pure Data pour la version 0.46, avec le début de l'intégration par Miller Puckette de la gestion du multi-instances, ont simplifié la première mise en œuvre de Camomile. Ces modifications du cœur du moteur, bien qu'insuffisantes à une mise en œuvre directe dans le contexte d'un plugiciel audio, ont permis de proposer une première stratégie consistant à forcer le caractère séquentiel des opérations. Pour cela, une interface similaire à libpd a été créée pour répondre spécifiquement à ce problème. La méthode consiste à restreindre l'accès au moteur à une instance à la fois, tout en limitant chaque instance à une seule opération, en utilisant un système de verrous (locks). Afin d'offrir un accès dans un contexte de *multithreads*, le système utilise des listes chaînées pour enregistrer les messages et les instructions qui sont lues et exécutées par la suite de manière séquentielle, avant de traiter le signal audionumérique de chaque instance. L'enjeu suivant est de simplement faire concorder l'interface JUCE avec l'interface enveloppant le moteur Pure Data. En avril 2016, une première version fonctionnelle destinée à un large usage<fnref n="38" /> a pu être publiée. Elle a été testée sur de nombreuses plateformes logicielles et à chaque fois sur les systèmes d'exploitation pour lesquels elles sont disponibles<fnref n="39" />. Malgré l'approche radicale à forcer le

<footnote n="32">³²Cette approche possède aussi l'avantage éventuel comparé aux approches présentées précédemment, de pouvoir utiliser directement l'interface graphique native. Il n'est néanmoins pas forcément souhaitable de vouloir utiliser l'interface graphique originale de Pure Data au sein d'un plugiciel audionumérique. Une interface spécifique et plus adaptée peut potentiellement être plus appropriée. Un autre avantage est la possibilité de charger les bibliothèques d'objets externes.</footnote>

<footnote n="33">³³Faute de moyens techniques et de temps, cela n'a pas pu être réalisé pour le moment.</footnote>

<footnote n="34">³⁴La plateforme Heavy est proposée par depuis 2016 sur enzienaudio.com (site consulté en janvier 2018).</footnote>

<footnote n="35">³⁵Distribution principale de l'application réalisée par Miller Puckette.</footnote>

<footnote n="36">³⁶Pure Data ou le projet libpd sont tous deux sous la même licence définie par "Standard Improved BSD" et distribuée avec les codes sources.</footnote>

<footnote n="37">³⁷JUCE est une interface de programmation applicative orientée vers le traitement du signal audionumérique distribué par la société Roli juce.com (site consulté en janvier 2018).</footnote>

<footnote n="38">³⁸La version 0.0.7 est toujours disponible sur le répertoire Github du projet.</footnote>

<footnote n="39">³⁹Une liste non exhaustive de ces plateformes – comprenant notamment les logiciels Reaper, Cubase, Ableton Live, Tracktion,</footnote>
caractère séquentiel des opérations, le système était jouable et n’offrait pas d’artefacts en dehors d’une hausse de l’utilisation du processeur – résultat de l’attente de déverrouillage d’une instance par une autre.

### 4.1.2. L’approche parallèle

Plus récemment avec la version 0.47 de Pure Data, les derniers problèmes de l’utilisation multi-instances dans un contexte de *multithreads* ont été résolu en utilisant notamment la mémoire locale de *thread*. En gardant la mémoire associée à une instance locale sur chaque *thread*, cela permet d’utiliser en concurrence plusieurs instances sur des *threads* différents<sup>40</sup>. Néanmoins, l’usage d’une telle approche demande de revoir une grande partie de l’interface mise en œuvre précédemment. Aussi, afin que ce travail puisse être utile à une plus large communauté de programmeurs [14], le choix a été fait d’utiliser libpd et d’y intégrer ces améliorations. Les interfaces de programmation élémentaires de libpd pour le multi-instances ont été mises à jours notamment par Miller Puckette et Dan Wilcox. L’enjeu était alors de rendre certains aspects importants – notamment la réception des messages, des événements MIDI ou des notifications de console – compatibles avec le multi-instances. Aussi, la mise à jour pour le multi-instances et le *multithreads* du noyau de Pure Data n’a pas encore réellement confrontée à un usage concret. Cette mise en œuvre était l’occasion de tester l’approche, et de corriger les quelques cas difficiles à prévoir<sup>41</sup>. En décembre 2017, la deuxième version de Camomile a pu être publiée et grâce au relatif succès de la précédente version, de nombreux retours d’utilisation constructifs ont permis de déployer rapidement les outils sur les différents systèmes d’exploitation<sup>42</sup>.

## 4.2. Gestion réciproque entre le moteur et le plugiciel

La gestion du moteur *patcher* par le plugiciel et réciproquement des caractéristiques du plugiciel via le moteur, recouvre plusieurs aspects.

### 4.2.1. La communication et le partage de données

Un premier aspect important de la gestion du moteur par le plugiciel concerne la communication, et de manière générale le partage de données audio, MIDI ou encore de type message. La réception et l'envoi du signal audionumérique du plugiciel vers le moteur *patcher* et réciproquement, peuvent être réalisés respectivement avec les objets *adc~* et *dac~*. De manière analogue, les événements MIDI reçus par le plugiciel peuvent être

récupérés dans un patch via les objets MIDI natifs de Pure Data. Et des événements MIDI peuvent être générés du patch et envoyés vers le plugiciel via les objets MIDI équivalents pour la sortie. Enfin, la réception de messages du plugiciel vers un patch est quant à elle réalisée avec les objets *receive* et les messages sont transmis du patch vers le plugiciel avec les objets *send*. Ces messages sont notamment utilisés pour modifier dans un patch les valeurs des paramètres mais aussi afin d'être notifié de leurs changements par la station de travail audionumérique (Figure 3)<fnref n="43" />.

<figure><img src="image_16.png" /><figcaption>**Figure 3.** Patch Pure Data utilisé dans le plugiciel Bulgroz où le signal en entrée, reçu par l'objet *adc~*, est traité en fonction de la valeur du premier paramètre, reçu par l'objet *receive*, avant d'être renvoyé au plugiciel, via l'objet *dac~*.</figcaption></figure>

L'avantage de cette approche est que, contrairement à Max For Live, aucun objet externe et spécifique à Camomile n'est nécessaire au fonctionnement d'un patch dans le plugiciel. Cela évite une possible obsolescence des patchs et les rend utilisables en dehors du plugiciel. Il est intéressant de remarquer qu'afin d'assurer une bonne distribution des messages entre les instances, la première version nécessitait l'utilisation des indices spécifiques aux patchs dans les symboles de réception et d'envoi<fnref n="44" />. La deuxième version avec sa gestion du multi-instances plus poussée, permet de se libérer de cette contrainte.

### 4.2.2. Le chargement des patchs

Un deuxième aspect notable de la gestion du moteur par le plugiciel, concerne la méthode de chargement des patchs. Deux approches peuvent être envisagées et ont été mises en œuvre respectivement dans la première et la deuxième version du plugiciel. La première version offrait la possibilité de charger des patchs dynamiquement via une boîte de dialogue. Cette approche possède l'avantage d'ouvrir rapidement un

<footnote>Ardour, etc. – et des systèmes d’exploitation – Windows, MacOS et Linux en 32/64bits - est disponible sur le wiki du répertoire Github.</footnote>

<footnote n="40">⁴⁰L'accès à une même instance via plusieurs *threads* peut néanmoins toujours poser problème et l'accès sur un *thread* de plusieurs patchs est obligatoirement séquentielle.</footnote>

<footnote n="41">⁴¹Les dernières modifications devraient être acceptées prochainement sur les branches principales de libpd et Pure Data.</footnote>

<footnote n="42">⁴²Cette version a déjà été testée sur un grand nombre de plateformes logicielles – telles que Reaper, Ableton Live, Tracktion, Bitwig, etc. – et de systèmes d’exploitation – Windows 32/64bits, MacOS 32/64bits et Linux 64bits.</footnote>

<footnote n="43">⁴³Ces approches peuvent être aussi utilisées pour la gestion des préférglages ou encore pour la configuration des canaux audio.</footnote>

<footnote n="44">⁴⁴Ces indices peuvent être intégrés en utilisant les caractères $0$ dans ces symboles.</footnote>
patch dans la station de travail audionumérique. Associée avec la fonction de rechargement du patch courant, c'est une fonctionnalité très utile pour la création des patchs, mais qui pose néanmoins des problèmes. Plusieurs aspects du fonctionnement du plugiciel dépendent du patch chargé, comme les paramètres, les préréglages, les configurations de canaux supportées, etc. Or, changer de patch après le chargement initial du plugiciel par la station de travail audionumérique, implique de modifier ces éléments à la volée, ce qui n'est pas bien supporté par ces logiciels<fnref n="45" />. De plus, la localisation des patchs n'est pas assurée lorsque le projet de la station de travail audionumérique est partagé<fnref n="46" />. Enfin, utiliser l'interface graphique pour le chargement du patch empêche l'utilisation du plugiciel dans les stations de travail qui ne supportent justement pas les interfaces graphiques spécifiques aux plugiciels<fnref n="47" />. Pour répondre à ce problème, la deuxième version de Camomile a choisi d'associer chaque patch – ou un ensemble de patchs – à une nouvelle copie du plugiciel Camomile original. En liant et en intégrant de la sorte les patchs au fichier binaire du plugiciel, le chemin relatif entre les deux est toujours préservé. À la première instanciation du plugiciel dans la station de travail, le ou les patchs associés sont automatiquement chargés et l'ensemble des informations nécessaires au fonctionnement du plugiciel – paramètres, préréglages, configurations des canaux, etc. – peut être récupéré. Par la suite, le patch d'une instance du plugiciel peut être rechargé à la volée<fnref n="48" /> – permettant donc de modifier le patch, que ce soit le moteur sonore ou l'interface graphique – mais les informations fournies à la station de travail audionumérique, et qui doivent nécessairement rester inchangées, ne sont pas modifiées.

### 4.2.3. Définition des propriétés du plugiciel

Enfin, un dernier aspect important concerne la manière de définir ces différentes informations liées au fonctionnement du plugiciel. Là encore, l'approche est différente entre les deux versions de Camomile. La première version se limitait aux seules informations des paramètres. Aussi, il avait semblé judicieux de définir les paramètres à l'aide des interfaces graphiques utilisateurs du patch principal. Par exemple, une glissière (slider) ou une boîte nombre générait automatiquement un paramètre en fonction du label et de la plage de valeur jouable définie dans les propriétés de l'objet graphique<fnref n="49" />. Cette approche possède l'avantage de savoir dans le plugiciel quand la valeur d'un paramètre est modifiée par

l'utilisateur via cette interface, et de simplifier la mise en œuvre du plugiciel et la création du patch. Cependant, un tel choix possède des inconvénients majeurs. L'approche se complexifie dès lors que l'utilisateur souhaite utiliser plusieurs interfaces graphiques pour contrôler un même paramètre (Figure 2). Mais surtout, l'ordre des paramètres<fnref n="50" /> dépend de l'ordre de création des interfaces graphiques. Aussi, dans le processus de création d'un patch pour le plugiciel, définir l'ordre des paramètres peut nécessiter de recréer les interfaces graphiques et leurs connexions. Ce qui peut être laborieux et source d'erreurs. Enfin il restait à définir de nouvelles méthodes pour configurer les autres options du plugiciel, telles que les préréglages, les configurations audio supportées, etc.

<figure><img src="image_17.png" /><figcaption>**Figure 4.** En arrière-plan, le fichier texte permettant de définir les propriétés du plugiciel Bulgroz avec Camomile, dont notamment le paramètre de taille de fenêtre et trois préréglages. Au premier-plan, l'interface graphique par défaut du plugiciel dans le logiciel Reaper permet de modifier la valeur du paramètre et de sélectionner les préréglages.</figcaption></figure>

Afin de remédier à ces problèmes, la dernière version de Camomile utilise un fichier texte additionnel pour définir l'ensemble des informations nécessaires à un plugiciel. La syntaxe de ce fichier est simple. Chaque ligne permet de définir une option, de rajouter un paramètre ou un préréglage<fnref n="51" /> ce qui ouvre la possibilité d'écrire ou de charger le fichier via l'objet text de Pure Data (Figure 4. En arrière-plan, le fichier texte permettant de définir les propriétés du plugiciel Bulgroz avec Camomile, dont notamment le paramètre de taille de fenêtre et trois préréglages. Au premier-plan, l'interface graphique par défaut du plugiciel dans le logiciel Reaper permet de modifier la valeur du paramètre et de sélectionner les préréglages.). Enfin, un système de

<footnote n="45">⁴⁵Cette approche est normalement interdite par la norme VST. Une restriction est définie dans la documentation du VST 3 Plug-In SDK github.com/steinbergmedia/vst3sdk (page consultée en janvier 2018).</footnote>

<footnote n="46">⁴⁶Les patchs n'étant pas directement associés au projet en cours, au fichier binaire du plugiciel ou à la station de travail, son chemin est absolu et lorsqu'un projet est partagé sur un autre ordinateur, le plugiciel ne parvient pas à retrouver le patch et il est nécessaire de redéfinir manuellement tous les chemins.</footnote>

<footnote n="47">⁴⁷Certaines stations de travail audionumériques supportent encore mal les interfaces graphiques des plugiciels. C'est le cas notamment du logiciel libre et gratuit Audacity audacity.fr (page consultée en janvier 2018).</footnote>

<footnote n="48">⁴⁸Cette fonctionnalité peut être très utile notamment lors de la création du plugiciel. La version 1.0.4 du plugiciel offre, pour cela, la possibilité de recharger le patch manuellement – en cliquant sur un bouton dédié – ou automatiquement à chaque nouvelle sauvegarde du patch.</footnote>

<footnote n="49">⁴⁹Cette approche est aussi définie dans la documentation de la version 0.0.7 du plugiciel disponible sur le wiki du répertoire Github.</footnote>

<footnote n="50">⁵⁰L'ordre des paramètres est très important, car les stations de travail audionumérique utilisent leurs indices (ou positions) – et non leurs noms – comme identifiants des paramètres pour organiser les informations qui leurs sont relatives, telles que les automations.</footnote>

<footnote n="51">⁵¹Cette approche est définie dans la documentation du plugiciel et sera développée plus longuement dans une prochaine publication.</footnote>
notifications du plugiciel vers le patch et réciproquement du patch vers le plugiciel, permet d'associer les interfaces graphiques aux paramètres<fnref n="52" />. La version actuelle du plugiciel permet donc un support complet de l'ensemble des fonctionnalités génériques et des spécifications des plugiciels, tout en préservant une cohérence avec le langage et les usages de Pure Data.

## 5. BILAN ET PERSPECTIVES

Camomile offre à présent un outil compatible sur les trois principaux systèmes d'exploitation avec la plupart des stations de travail audionumérique supportant des plugiciels. Les choix opérés dans la dernière version du plugiciel – que ce soit pour le chargement de patchs, la communication entre le moteur et le plugiciel ou encore la création de patchs ainsi que la définition des propriétés du plugiciel – ont permis de supporter pleinement l'ensemble des fonctionnalités usuellement offert par ce type de formats : la gestion des paramètres, des préréglages, des configurations audio, des événements MIDI, des informations de la tête de lecture<fnref n="53" /> ou encore la création d'une interface graphique adaptée et fonctionnelle. Ainsi, des patchs correctement configurés et tirant parti de l'ensemble de ces fonctionnalités, peuvent permettre de générer des plugiciels rivalisant que ce soit sur l'aspect sonore ou l'aspect ergonomique avec des plugiciels codés et compilés de manière classique. De plus, l'outil n'est pas seulement limité à un usage expérimental, mais peut très bien être utilisé dans un contexte de production musicale abouti ou dans un contexte pédagogique<fnref n="54" /> (Figure 5).

<figure><img src="image_18.png" /><figcaption>**Figure 5.** Le plugiciel *AlmondOrgan* au format VST, un synthétiseur douze voix inspiré par l'orgue Hammond, créé avec Camomile et intégrant de multiples fonctionnalités comme la gestion des événements MIDI, des paramètres et des préréglages.</figcaption></figure>

Camomile est, en outre, un outil extrêmement intéressant pour les développeurs car il permet de créer rapidement des prototypes de plugiciels en prenant en compte tous les aspects de la mise en œuvre : moteur audio, interfaces graphiques, messages, etc. Et au-delà des prototypes, les plugiciels créés avec Camomile peuvent être publiés en tant que tels. Le plugiciel jouit déjà d'une relative notoriété dans la communauté d'utilisateurs de Pure Data et de l'audionumérique libre de manière générale. Mais il serait intéressant d'ouvrir la communauté d'utilisateurs via le partage direct de plugiciels créés avec Camomile. Cela pourrait donner envie à des utilisateurs exclusifs des stations de travail audionumérique de s'intéresser aux approches de type *patcher*. Sur le plan du développement, Camomile aura été un excellent terrain d'expérimentations et de mise à l'épreuve du support *multithreads* et multi-instances de Pure Data. Il aura permis de révéler un certain nombre de problèmes qui restaient à corriger. Enfin, certains points sont néanmoins encore à explorer et d'autres problèmes à débloquer. C'est le cas notamment de la gestion des bibliothèques externes d'objets qui nécessitent *a priori* d'être compilées avec le plugiciel. Il existe deux raisons à cela. D'une part, les stations de travail audionumériques semblent pour la plupart bloquer le chargement dynamique de bibliothèques externes. Et d'autre part, il est nécessaire de vérifier si les bibliothèques externes n'interfèrent pas avec les conditions d'activation du support du multi-instances et du *multithreads* de Pure Data. Il serait aussi intéressant de réfléchir à une amélioration ou une extension de la création et de la gestion de l'interface graphique. Il s'agirait de prendre en compte le système de structure de données graphiques [16] ou d'offrir un système dynamique permettant d'intégrer ses propres graphismes aux interfaces, afin de se rapprocher d'un plugiciel plus classique<fnref n="55" />. Enfin, il serait intéressant d'intégrer la génération de plugiciels au format LV2⁵⁶ mais aussi au format Audio Unit v3⁵⁷ qui permettrait de charger les plugiciels sur des tablettes et téléphones iOS.

## 6. REMERCIEMENTS

Je tiens à remercier toute la communauté de développeurs de Pure Data et de libpd, et particulièrement Miller Puckette et Dan Wilcox, pour leurs conseils et leurs explications mais aussi les

<footnote n="52">⁵²Cette approche est aussi définie dans la documentation du plugiciel et sera aussi développée plus longuement dans une prochaine publication.</footnote>

<footnote n="53">⁵³Il est en effet possible d'utiliser toutes les informations fournies par la station de travail audionumérique telles que le chiffrage de la mesure, le tempo de la mesure, la position de la tête de lecture, etc.</footnote>

<footnote n="54">⁵⁴Le plugiciel a notamment été présenté à l'Université Paris 8 aux étudiants en 2016 et 2017, dans le cadre des cours d'Introduction à la programmation avec Max et Pure Data 1 dispensés par moi-même et en 2018, aux étudiants du cours de Composition électroacoustique 2 dispensé par Alain Bonardi et suite auquel ils ont été invité à créer leurs propres plugins.</footnote>

<footnote n="55">⁵⁵Le plugiciel offre déjà à la possibilité de charger des images de fond (Figure 2 & Figure 5) mais il serait intéressant de pouvoir replacer les graphismes natifs de objets de Pure Data, tels que l'interrupteur ( toggle ), la glissière ( slider ) ou encore la boîte nombre ( numbox ), en utilisant des séries d'images données par l'utilisateur.</footnote>

<footnote n="56">⁵⁶Le choix a été fait pour le moment d'attendre que JUCE supporte ce format plutôt que coder un plugiciel indépendant spécifique.</footnote>

<footnote n="57">⁵⁷Grâce au support de ce format par JUCE, il peut être déjà envisagé de publier une version Audio Unit v3 de Camomile mais faute de temps et de moyen techniques il n'a pas encore été possible de tester l'approche.</footnote>
utilisateurs de Camomile pour leurs retours d'utilisation et leurs suggestions. Je tiens aussi à remercier toute l'équipe du CICM pour l'intérêt porté à ce projet, et particulièrement Alain Bonardi et Eliott Paris pour leurs remarques et leurs suggestions.

7. REFERENCES

[1] Puckette M. "Pure Data : Another Integrated Computer Music Environment", Proceedings of the Second Intercollege Computer Music Concerts, p. 37-41, Tachikawa, Japon, 1997.

[2] De la Hogue T., Baltazar P., Catherine M. D., Chao J. et Bossut, C. "Ossia : Open Scenario System For Interactive Applications", actes des Journées d'Informatique Musicale, p.78-84, Bourges, France, mai 2014.

[3] Coduys T., Lefèvre A. et Pape G. "IanniX", actes des Journées d'Informatique Musicale, Montbéliard, France, juin 2003.

[4] Sèdes A., Guillot P. et Paris E. "The HOA library, review and prospect", Proceedings of the ICMC-SMC 2014, p. 855-860, Athènes, Grèce, septembre 2014.

[5] Guillot P., Paris E. et Deneu M. "La bibliothèque de spatialisation HOA pour Max/MSP, Pure Data, VST, FAUST...,", Revue Francophone d'Informatique et Musique (En ligne), n° 3, automne 2013, URL : revues.mshparisnord.org/rfim/index.php?id=245.

[6] Favreau E., Fingerhut M., Koechlin O., Potacsek P., Puckette M. et Rowe R., "Software Developments for the 4X Real-time System", Proceedings of the International Computer Music Conference 1986, p. 369-373, La Haye, Pays-Bas, 1986

[7] Guillot P. "La Représentation Intermédiaire et Abstraite de l'espace Comme Outil de Spatialisation du Son", thèse de doctorat, CICM, Université Paris 8, Saint-Denis, France, 2017.

[8] Paris E., Millot J., Guillot P., Bonardi A. et Sèdes A. "Kiwi : Vers un Environnement de Création Musicale Temps-Réel Collaboratif - Premiers Livrables du Projet Musicoll", Actes des Journées d'Informatique Musicale, Paris, France, mai 2017.

[9] Puckette M. "The Patcher", Proceedings of the International Computer Music Conference 1988, p. 420-429, Cologne, Allemagne, 1988.

[10] Puckette M. "Combining Event and Signal Processing in the MAX Graphical Programming Environment", Computer Music Journal, vol. 15, n° 3, p. 68-77, automne 1991.

[11] Zicarelli D. "Developing Plug-ins in Max/MSP for Pluggo - updated information for Pluggo 3", Documentation du plugiciel Pluggo publiée par Cycling'74, révision 4 du 16 mai 2002.

[12] Puckette M. "Who owns our software ? A First-person Case Study", Proceedings of the International Symposium on Electronic Art, p. 200-202, Helsinki, Finlande, 2004.

[13] Geiger G. "PDa : Real Time Signal Processing and Sound Generation on Handheld Devices," Proceedings of the International Computer Music Conference, Singapour, septembre-octobre 2003.

[14] Brinkmann P., Kirn P., Lawler R., McCormick C., Roth M. et Steiner H.-C. "Embedding Pure Data with libpd", Proceedings of the Pure Data Convention, Weimar, Allemagne, août 2011.

[15] Wilcox D., "PdParty : An iOS Computer Music Platform using libpd", Proceedings of the Pure Data Convention, New-York, États-Unis, novembre 2016.

[16] Puckette M. "Using Pd as a score language", Proceedings of the International Computer Music Conference (ICMC 2002), p. 184-187, Göteborg, Suède, 2002.
# ORDONNANCEMENT ADAPTATIF D'UN GRAPHE AUDIO AVEC DÉGRADATION DE QUALITÉ

Pierre Donat-Bouillud
Sorbonne Université/STMS/Inria

## RÉSUMÉ

Les systèmes interactifs musicaux sont des systèmes particulièrement dynamiques qui combinent du traitement du signal avec du contrôle en temps réel. Ils sont souvent utilisés sur des plateformes où il n'est pas possible d'avoir des garanties temps réel précises. Nous présentons ici un algorithme de dégradation temps réel en ligne pour obtenir un compromis entre la qualité audio et les retards par rapport aux échéances audio, dans un graphe audio dynamique. Nous évaluons expérimentalement les performances de l'algorithme.

## 1. INTRODUCTION

Les systèmes interactifs musicaux (SIM) [15] sont des systèmes programmables de création artistique qui combinent des traitements audio avec du contrôle en temps réel dans un graphe audio. Les graphes audio connectent des nœuds de traitement du signal dans un graphe reconfigurable en ligne et se plient à des contraintes temps réel non critiques, par rapport aux systèmes embarqués dans un avion par exemple, mais plus exigeantes que pour de la vidéo.

Compositeurs et musiciens utilisent généralement ces SIM sur des systèmes d'exploitation grand public comme Windows, macOS, ou Linux, où une estimation fiable du temps d'exécution dans le pire des cas (WCET) est difficile, à cause d'une hiérarchie complexe des caches du processeur, de l'absence d'ordonnanceurs temps-réel, du manque d'isolation temporelle entre tâches, et de la difficulté de prédire quelles tâches seront exécutées à un instant donné.

Par conséquent, nous ne pouvons pas supposer que l'on connaît le WCET des tâches mais nous pouvons réagir aux changements dans l'environnement d'exécution en modifiant le temps d'exécution d'une tâche avec le paradigme de la programmation approximée [17], par dégradation du graphe audio. Il ne s'agit pas de seulement dégrader un seul nœud d'un graphe audio, mais de choisir les nœuds à dégrader, tout en préservant les contraintes temps réel. Dans ce contexte de traitement du signal, les nœuds sont vus comme des boites noires et les dégradations s'effectuent seulement sur les flux de données entre les boites, en rééchantillonnant ces flots de données. On pourra aussi envisager de substituer un nœud par une autre version du nœud moins demandeuse en ressource de calcul.

Si on considère que le temps est une ressource, dans les systèmes temps-réel, le temps est souvent la seule resource à être dégradée [7] (en ratant une échéance). Ici, nous dégradons aussi d'autres ressources et nous cherchons à trouver un compromis explicite entre diverses measures de qualité de la tâche, comme le taux d'échantillonnage du signal.

Dans un premier temps, nous avons étudié comment choisir les nœuds à dégrader au moment de l'exécution, en ligne. L'ordonnanceur doit tenir compte de son propre temps de calcul et nous faisons en sorte que les calculs de l'ordonnanceur eux-même soient les plus légers possibles. Un modèle particulièrement bien adapté à décrire des tâches de traitement du signal et les flots entre les nœuds est le modèle *flot de données* [8]. Nous montreons comment nous pouvons adapter le paradigme de la programmation approximée au modèle *flot de données*.

Nos contributions sont les suivantes :

- intégrer les dégradations au modèle *flot de données*

- détermination des nœuds à dégrader (en ligne)

- application de ces dégradations au cas particulier d'un graphe audio

## 2. CONTEXTE ET MOTIVATIONS

### 2.1. Les systèmes interactifs musicaux

Les SIM servent à jouer des pièces de musique, que l'on décrit par des partitions destinées à l'ordinateur, sous la forme de programme. Pour cela, ils manipulent des flux audio, au moment du concert, en temps réel, à l'aide de divers effets audio. Ils effectuent donc un traitement du signal, ils remplissent périodiquement des tampons audio, les envoient à la carte son, et permettent de contrôler les traitements, avec des contrôles apériodiques (comme des changements de l'interface graphique) ou bien périodiques (par exemple via un oscillateur basse fréquence). Les flux audio et les contrôles sont traités dans un graphe audio de nœuds de calcul audio, qui peut être dynamique, c'est-à-dire que des nœuds peuvent être ajoutés ou retirés pendant l'exécution.

Puredata [13] et Max/MSP [20] sont des exemples de SIM, qui affichent graphiquement le graphe audio mais rendent compliquée sa modification de façon dynamique comme résultat d'un calcul. D'autres SIM, comme ChucK [18] ou SuperCollider [11], sont plus dynamiques. Dans Antescofo [6], des musiciens humains et un ordinateur
peuvent interagir sur scène en concert, à l'aide de stratégies de synchronisation sophistiquées spécifiées par une partition augmentée qui peut aussi décrire des graphes audio dynamiques [5].

## 2.2. Les contraintes temps-réel pour l'audio

La carte son exige que des échantillons audio soient écrits dans son tampon d'entrée périodiquement. Pour le taux d'échantillonnage d'un CD de 44.1 kHz par exemple, et une taille de tampon de 64 échantillons, la période audio est de 1.45 ms. Suivant la latence visée et les ressources de la plateforme considérée, la taille du tampon peut aller de 32 échantillons pour des ordinateurs dédiés au traitement du son, à 2048 échantillons pour certains téléphones tournant sous Android.

Les contraintes temps-réel pour l'audio sont non critiques, mais les exigences sont plus strictes que pour la vidéo. Pour la vidéo, perdre une image parmi les 24 images par seconde n'entraîne pas une baisse visible de la qualité; de nombreux protocoles de streaming [1] se le permettent donc. Au contraire, rater une échéance pour une tâche audio est immédiatement audible.

**Sous-alimentation du tampon** Le pilote audio utilise un tampon circulaire dont la taille est un multiple de la taille du tampon de la carte son. Si la tâche audio rate une échéance, elle ne remplit pas le tampon assez rapidement. Suivant l'implémentation, les tampons précédents sont rejoués (l'effet *mitraillette*) ou du silence est joué, ce qui conduit à de l'audio avec des craquements ou des clics dus aux discontinuités dans le signal, comme on peut le voir sur la Figure 1.

Un tampon de grande taille aide à diminuer ce non-remplissage, mais augmente la latence audio.

**Débordement du tampon** De la même manière, dans certaines implémentations, remplir le tampon audio trop rapidement peut amener à des discontinuités audibles dans le son en sortie si les échantillons de trop ne peuvent pas être stockés pour être utilisés par la suite.

## 2.3. Motivations

Les systèmes d'exploitation grand public comme Windows, macOS ou Linux ne sont pas des systèmes temps-réel et ne donnent aucune garantie forte sur les échéances des traitements audio (voir la Figure 2). Les applications effectuant des traitements audio cohabitent avec de nombreuses autres applications et entrent en compétition pour une part de temps CPU. Par ailleurs, les SIM sont de plus en plus portés vers des cartes embarqués comme le Raspberry Pi et doivent s'adapter aux ressources de calcul limitées de ces plateformes.

Pour relever ces défis, et tenir compte de la complexité croissante des œuvres, les SIMs ont plutôt choisi d'exiger plus de ressources de calcul disponibles et d'augmenter le parallélisme des partitions interactives pour profiter de

<figure><img src="image_19.png" /><figcaption>**Figure 1** : Le traitement audio a raté l'échéance. Ainsi, aucun échantillon audio produit par ce traitement audio pour ce cycle audio ne peut être envoyé au tampon audio de la carte son. Dans cette implémentation, le système envoie du silence, c'est-à-dire des échantillons à zéro. Cela entraîne une discontinuité du signal à l'endroit de la bande rouge, et donc un *clic*.</figcaption></figure>

<figure><img src="image_20.png" /><figcaption>**Figure 2** : La ressource temporelle accordée au *callback* audio sur un Mac Book Pro avec Mac OS X. Bien qu'elle soit centrée autour de 3.94 ms, il y a des valeurs éloignées et elle peut varier jusqu'à 200 µs.</figcaption></figure>

la généralisation des processeurs multicœurs. Un exemple de système qui s'appuie sur des architectures parallèles est l'ordonnanceur de tâches Supernova [3] pour SuperCOLLIDER. Cependant, ces nouveaux ordonnanceurs ne parallélisent pas les partitions automatiquement, requièrent des instructions explicites, comme *ParGroup* pour SuperNova et *poly* pour Max/MSP, et sont donc difficiles à programmer.

Cependant, une autre façon de faire face à ces défis est d'explorer comment les traitements audio peuvent être dégradés sans que les dégradations soient perçues (ou en quantifiant ces dégradations), au moment où les ressources de calcul demandées par les pièces augmentent (chaînes audio à 96 kHz, comme dans la pièce *Re Orso* de Marco Stroppa<fnref n="1" />), en même temps que les supports – cartes embarquées, systèmes enfouis, plateformes mobiles – sur lesquels peuvent être jouées les pièces se démocratisent.

<footnote n="1">1. http://brahms.ircam.fr/works/work/27678/</footnote>
## 2.4. Travaux en rapport

Certaines approches en ordonnancement ont déjà porté sur l'ordonnancement adaptatif, en temps réel critique ou non-critique, soit en supprimant complètement des tâches, soit en les dégradant, avec le paradigme de programmation approximative ou la criticité mixte. Une autre possibilité pour s'adapter aux changements des ressources de calcul disponibles pendant une exécution est d'effectuer des réservations d'une certaine capacité de calcul.

### 2.4.1. Le paradigme de programmation approximative

Il s'agit d'un paradigme de programmation dans lequel on autorise des erreurs dans les calculs en échange d'une amélioration des performances temporelles. La notion de correction d'un programme est relâchée pour celle d'une correction avec une erreur quantitative. Ce paradigme permet de mettre au point des systèmes dans lesquels on recherche un compromis entre une bonne qualité et les performances ou la consommation en énergie. Venkataramani et al., dans [17], proposent les critères suivants pour décider de la pertinence d'utiliser ce paradigme :

- il n'y a pas une réponse unique, mais un intervalle de réponses est acceptable;

- les utilisateurs se sont habitués à obtenir des résultats acceptables, suffisamment bons mais pas parfaits;

- les données d'entrée sont bruitées et les algorithmes qui les traitent sont conçus de façon à prendre en compte ce bruit;

- des schémas de programmation qui diminuent les approximations sont utilisés.

Une stratégie assez basique [10] consiste à diviser les tâches du système entre une partie obligatoire et une partie optionnelle, qui peut ne pas être exécutée en cas de surcharge du processeur. Cette stratégie rend l'ordonnancement temps réel assez simple à mettre en place, avec peu de calculs supplémentaires induits par l'algorithme d'ordonnancement mais ne prend pas en compte les dépendances entre tâches, en particulier dans l'estimation de la qualité.

Un autre modèle de calcul [19] utilise un graphe pour représenter un programme de type *map-reduce*, avec des nœuds de calcul et des nœuds d'agrégation. Il génère hors ligne des versions approximatives étant donnée une certaine marge d'erreur en paramètre. Les transformations qui permettent de dégrader sont de deux types : des transformations de substitution et des transformations par échantillonnage, où l'entrée est sous-échantillonnée aléatoirement. Cependant, ce modèle est destiné à des applications de traitement par lots pour des données massives et ne prend pas en compte les contraintes temps-réel; il requiert par ailleurs une phase préliminaire de profilage, ce qui empêche de l'appliquer à des graphes reconfigurables et dynamiques.

### 2.4.2. Criticité mixte

Dans les systèmes temps réel dur à criticité mixte [4], les tâches à haute criticité doivent impérativement êtreordonnancées, alors que les tâches moins critiques peuvent être supprimées au cas où cela conduirait à outrepasser des échéances. Dans notre cas cependant, toutes les tâches, liées entre elles dans un graphe, ont la même criticité.

### 2.4.3. La réservation de ressources

Dans cette approche, une partie des capacités de calcul du processeur est réservée [2] à certaines tâches. Cela est justifié lorsque les tâches en présence sont des tâches différentes en compétition pour les ressources : dans le cas du multimédia par exemple, les tâches vidéo et les tâches audio peuvent se voir donner des réservations différentes, comme les tâches audio sont plus temps-réel (plus critiques) que les tâches vidéos. Cependant, la réservation de ressources n'est pas adaptée au cas où les tâches sont identiques (toutes des effets audio) et liées par des relations de dépendance de données.

## 3. MODÈLE DE GRAPHE AUDIO

Dans un graphe audio, des flux audio circulent des entrées aux sorties à des taux différents selon les contraintes des nœuds du graphe. Le modèle *flot de donnée* [8] est particulièrement adapté pour représenter de telles tâches avec des dépendances. Nous nous limiterons ici au modèle *flot de données synchrone*, dans lequel les taux de production et de consommation des données par les tâches sont fixes. Le modèle *flot de données* ne tient pas compte du temps. Nous rajoutons donc aux tâches des informations sur leurs dates de début et leurs durées d'exécution, ce qui constitue le modèle *flot de données temporisé*.

### 3.1. Le modèle *flot de données*

Le modèle *flot de données* [8] est *orienté données*. Un graphe *flots de données* est un graphe dirigé $G = (V, E)$ où les nœuds, dans l'ensemble $V$, représentent des calculs, en l'occurrence, des *effets audio*. Les arcs du graphe, dans $E \subset V \times V$, représentent le chemin des données et connectent les nœuds ensemble via des *ports d'entrée et de sortie*. Si $v_1$ et $v_2$ sont des nœuds, on pourra noter une arête $v_1 \to v_2$. Les données sont des séquences de *jetons* et un nœud s'exécute quand il y a suffisamment de *jetons* dans ses entrées, comme montré sur la Figure 3. Formellement, une fonction $\mu: E \to \mathbb{N} \times \mathbb{N}$ est ajoutée au graphe $G$, qui indique le nombre de jetons en entrée, et en sortie, d'une arête donnée. Dans le cas des traitements audio, le signal est échantillonné sur des intervalles périodiques. Les *jetons* du modèle *flot de données* correspondent à ces échantillons audio.

Le graphe *flot de données* est dit *synchrone* quand le nombre de jetons nécessaires à l'activation d'un nœud et le nombre de jetons produits en sortie, sont connus à l'avance.
<figure><img src="image_21.png" /><figcaption>**Figure 3** : Un graphe simple *flot de données synchrone* avec trois nœuds, **1**, **2** and **3**. Les données circulent de **1** à **3**, de **1** à **2** et de **2** à **3**. **1** produit 1 échantillon par exécution, et **3** requiert 2 échantillons pour s'activer. **1** est un nœud *source*, **2** un *effet*, et **3** une *sortie*.</figcaption></figure>

Les nœuds sans port d'entrée sont appelés *sources* ou *entrées* : ce sont les sources sonores, comme l'entrée micro, un fichier son, un synthétiseur. Les nœuds sans port de sortie sont appelés *sorties* ou *puits*, par exemple, la sortie vers la carte son et des haut-parleurs. Les nœuds qui ne sont ni des *entrées* ni des *sorties* sont appelés *effets*.

Le modèle *flot de données dynamique* [9] est un modèle plus riche que le modèle *synchrone*, où le nombre de jetons en entrée et en sortie n'est pas fixé et peut dépendre du nombre de jetons en entrée. Le graphe peut lui-même changer au cours de son exécution.

## 3.2. Le modèle *flot de donnée* temporisé

Un graphe *flot de données* ne décrit pas les instants où les nœuds du graphe s'activent mais seulement leur ordre partiel. Cependant, les graphes *flots de données* représentent souvent des traitements temps réel, par exemple les traitements audio temps réel. Pour un CD, dont le taux d'échantillonnage des morceaux est de 44.1 kHz, il y a un échantillon toutes les 22,6 µs. Si on donne des dates d'activation aux *sources*, que l'on connaît un temps d'exécution dans le pire des cas ou en moyenne de tous les nœuds, on peut en déduire des temps de déclenchement de chacun des nœuds du graphe. Si par ailleurs les *sorties* ont des échéances, cela permet de vérifier que les échéances sont respectées dans le pire des cas ou en moyenne.

On ajoute donc des dates de déclenchement d'exécution pour chaque *source* v, $s_1^v, \dots, s_n^v$ avec $s_i^v < s_{i+1}^v$ et tous les nœuds du graphe reçoivent un temps d'exécution dans le cas moyen $M_v$, dans le pire cas, $W_v$ et un temps d'exécution actuel $T_v$. On adjoint aussi à chaque *sortie* v des dates d'échéances $d_1^v, \dots, d_n^v$, avec $d_i^v < d_{i+1}^v$, qui correspondent aux moments où la carte son devra avoir reçu un certain nombre d'échantillons. Les traitements audio sont périodiques donc on suppose que pour une *source* v, $s_2^v - s_1^v = s_{i+1}^v - s_i^v$, et pour une *sortie* v, $d_2^v - d_1^v = d_{i+1}^v - d_i^v$. Les taux d'échantillonnage en entrée $f_e^c$ et en sortie $f_e^p$ sur une arête $e = v_1 \to v_2$ et $\mu(e) = (j_1, j_2)$, qui

correspondent au nombre de jetons produits et consommés sur une période sur l'arc, est $f_e^c = \frac{j_1}{T_{e1}}$ and $f_e^p = \frac{j_2}{T_{e2}}$.

Le temps d'exécution dans le pire cas (respectivement en temps moyen) est, pour un chemin $v_1 \to \dots \to v_n$ du graphe, $\sum_{i=1}^n W_{v_i}$ (resp. $\sum_{i=1}^n M_{v_i}$).

## 4. QUALITÉ

La qualité d'un graphe audio est un concept relatif et subjectif : cette version du graphe sonne-t-elle mieux ou moins bien que telle autre ? Une première façon est de calculer la différence entre une version dégradée et une version considérée comme optimale, une mesure de qualité *a posteriori*. Ce n'est pas souhaitable lors d'une exécution temps réel ; cela reviendrait à calculer simultanément deux versions du graphe, alors que le but est d'alléger les calculs. Nous calculons plutôt une mesure de qualité *a priori* qui se base sur des paramètres des calculs en cours.

### 4.1. Quelques propriétés d'une mesure de qualité

La mesure de qualité doit aussi être une mesure *compositionnelle*, au sens où la qualité du graphe doit être calculable comme une fonction de la qualité de ses nœuds et de ses arêtes. Chaque nœud $v$ se voit attribuer une mesure de qualité $q_v \in [0, 1]$, 0 correspondant à la pire qualité, 1 à la meilleure.

La qualité $q_{v \to v'}$ sur un arc $v \to v'$ suit les propriétés suivantes :

**Associativité** $q_{v \to v'} = q_v \otimes q_{v'}$ où $\otimes$ est un opérateur associatif (et pas commutatif en général).

**Décroissance** $q_{v \to v'} \le \min\{q_v, q_{v'}\}$, c'est-à-dire que la qualité n'augmente jamais sur un chemin.

De même, pour un graphe $\mathcal{G}$, la qualité $q_{\mathcal{G}}$ est :

$$q_{\mathcal{G}} = \bigotimes_{c \in \mathcal{C}} q_c$$

où $\mathcal{C}$ est l'ensemble des chemins de $\mathcal{G}$.

Elle n'est jamais supérieure à la qualité de chacune des chaînes du graphe. Par exemple, pour le graphe de la Figure 3 :

$$q_{\mathcal{G}} \le \min\{q_{v_1 \to v_3}, q_{v_1 \to v_2 \to v_3}\}$$

### 4.2. Dégrader la qualité en pratique

On considère ici deux façons de dégrader la qualité du graphe : en insérant des nœuds pour sous-échantillonner des chemins du graphe, ou en modifiant des nœuds du graphe et en les remplaçant par une version de moindre qualité.

**Insertion de nœuds de rééchantillonnage.** Dans un graphe *flot de données*, les nœuds reçoivent des échantillons puis les traitent quand ils en ont reçu suffisamment. Par conséquent, si un nœud reçoit des échantillons moins souvent, il utilisera moins de temps de calcul. Changer le nombre
d'échantillons par unité de temps est une opération habituelle en traitement du signal, et est appelé rééchantillonnage : obtenir moins d'échantillons correspond à sous-échantillonner, tandis qu'en obtenir plus, à sur-échantillonner. Pour rééchantillonner, nous insérons des nœuds dans le graphe qui vont changer les taux de production ou de consommation des échantillons. Ces nœuds insérés sont des nœuds d'effet comme les autres; ils peuvent interposer des valeurs, copier des valeurs entre des tampons audio. Ils sont donc aussi munis d'une mesure de qualité et de caractéristiques temporelles, qui permettent de prendre en compte l'impact des calculs menant à la dégradation elle-même dans la décision de dégrader ou pas.

Si l'on insère un nœud sous-échantillonneur, tous les nœuds qui sont sur un chemin qui commence sur ce nœud vont opérer sur des flux sous-échantillonnés. Si le chemin se termine par une sortie qui dicte un taux d'échantillonnage particulier, il faut aussi insérer un nœud sur-échantillonneur.

**Remplacement d'un nœud.** Le traitement audio opéré par un nœud peut être remplacé par un autre traitement de qualité inférieure et de temps de calcul dans les cas pire et moyen inférieurs, c'est-à-dire, si on note $v'$ et $v''$ deux versions du nœud $v$, si $q_{v'} < q_{v''}$ alors $M_{v'} < M_{v''}$ et $W_{v'} < W_{v''}$. Par exemple, les nœuds de sous-échantillonnage peuvent utiliser un algorithme de plus ou moins bonne qualité : garder un échantillon sur deux ou bien, en plus de cela, utiliser un filtre passe-bas pour se débarrasser des artefacts fréquentiels dus à la première méthode. Un nœud de réverbération peut être implémenté en haute qualité avec une coût de calcul important à l'aide d'une convolution avec une réponse impulsionnelle d'une salle, ou avec des lignes de retards, ce qui entraîne une moins bonne qualité (une réverbération moins réaliste) mais un temps de calcul rapide.

### 4.3. Mesurer la qualité

On choisit comme mesure de qualité le taux d'échantillonnage : plus le taux d'échantillonnage est bas, plus la qualité est basse. Quand l'audio est envoyé trop tard au tampon de sortie, on entend un clic (due une discontinuité du signal, voir Section 2). Au contraire, si le flux audio a été sous-échantillonné, le tampon de sortie comportera des échantillons non-nuls en général. Nous partons donc du principe que sous-échantillonner permet d'obtenir une meilleure qualité que rater une échéance audio.

La qualité $q_v$ d'un nœud inséré d'échantillonnage $v$ est $q_v = \arctan(\alpha f)$ où $f$ est la fréquence d'échantillonnage. Le choix de la fonction arctan<fnref n="2" /> et du coefficient $\alpha$ est permet de prendre en compte que d'un point de vue psychoacoustique [14], au delà d'une certain fréquence, aucune amélioration de qualité n'est perceptible par des humains. En effet, le système auditif de la plupart des êtres

humains ne peut pas percevoir des fréquences au-delà de 20 kHz. Le théorème de Shannon indique donc que le taux d'échantillonnage doit être d'au moins le double, donc à peu près la fréquence des CD de 44.1 kHz. Cependant, le suréchantillonnage permet de mieux prendre en compte les erreurs de calcul et d'approximations lors des traitements audio et c'est pourquoi nous tenons quand même compte des fréquences supérieures à 44.1 kHz. On pourra prendre $\alpha = \frac{\tan(0.9)}{44.1 \cdot 10^3}$. On considère par par ailleurs que les autres nœuds sont de qualité optimale, c'est-à-dire, $q = 1$.

On considère une chaîne $C$ de nœuds $v_1 \to \dots \to v_n$ à dégrader. On note $v_{\text{sous}}$ et $v_{\text{sur}}$ respectivement le nœud de sous-échantillonnage et celui de sur-échantillonnage. Après insertion de ces nœuds, la chaîne devient : $v_{\text{sous}} \to v_1 \to \dots \to v_n \to v_{\text{sur}}$. On peut prendre $\otimes$ le produit sur les réels, ou $\otimes = \min$. Comme une qualité $q$ est toujours dans $[0, 1]$, avec $\times$, la qualité ne peut qu'effectivement diminuer sur une chaîne. Pour $\min$, intuitivement, cela indique que la qualité du tout est celle du maillon le plus faible et que l'on peut dégrader beaucoup plus de nœuds pour respecter les échéances tout en conservant la même qualité.

Les traitements audio nécessitent en général de parcourir au moins une fois chacun des échantillons du tampon audio donc ont au moins une complexité linéaire. Le temps d'exécution de la chaîne est donc au moins divisé par le facteur de sous-échantillonnage $r$ (par exemple si l'on passe de 96 kHz à 48 kHz, $r = 2$). Le temps actuel de calcul (resp. moyen, dans le pire des cas) est majoré par :

$$T_{v_{\text{down}}} + \frac{1}{r} \sum_{i}^{n} T_{v_i} + T_{v_{\text{up}}}$$

### 5. CHOISIR LES NŒUDS À DÉGRADER

Le choix des nœuds à dégrader peut être considéré comme un problème d'optimisation. Ici, on s'intéresse au cas moyen, comme les SIM s'exécutent sur des plateformes grand public sans garanties temps-réel fortes. Le raisonnement serait le même dans le cas le pire.

Soit $\mathcal{G}$ est un graphe audio composés des nœuds $v_1, \dots, v_n$ et $q_{\mathcal{G}}$ sa mesure de qualité associée. L'échéance d'une tâche est notée $d_v$; on peut l'obtenir grâce aux dépendances entre tâches, aux dates d'activation des sources et à leur temps d'exécution en moyenne. On suppose donné $\tau : [0, 1] \to \mathbb{R}^+$ qui décrit le temps d'exécution moyen d'une tâche en fonction de sa qualité.

Le problème d'optimisation sous contraintes peut ainsi s'écrire :

maximiser $q_{\mathcal{G}}$ sous les contraintes :

$$\tau(v_1) \leq d_{v_1}, \dots, \tau(v_n) \leq d_{v_n}$$

En supposant que les qualités ont des valeurs dans un ensemble continue, on peut essayer des algorithmes standards d'optimisation pour résoudre le problème. On sait aussi que $\tau(n) = O(n)$ comme indiqué en section 4.3.

<footnote n="2">². arctan admet la droite $y = 1$ comme asymptote en $+\infty$, ce qui permet de modéliser que l'augmentation de la qualité est de moins en moins grande et ne dépasse pas un palier. D'autres fonctions croissantes avec une asymptote horizontale en $+\infty$ pourraient faire l'affaire.</footnote>
Cependant, le temps de calcul pour la recherche de solutions exactes n'est pas raisonnable s'il doit être fait au moment de l'exécution du graphe audio, mais est beaucoup plus pertinent pour pré-calculer des version dégradées du graphe. Ce calcul hors-ligne de versions dégradées fera l'objet d'un travail ultérieur. Ici, nous nous attachons plutôt à utiliser des heuristiques simples et rapides à calculer lors de l'exécution du graphe audio.

## Exécution du graphe audio

Les tâches sont des tâches dépendantes et les dépendances entre tâches sont données par le graphe audio. On peut trouver un ordonnancement de ces tâches en effectuant un tri topologique du graphe (ce qui signifie que l'on suppose que le graphe audio est acyclique). Avant d'exécuter chaque tâche, on estime le temps d'exécution qui reste avant la fin de l'exécution du graphe, en utilisant le temps d'exécution dans le cas moyen des nœuds. Il faut ensuite vérifier si le temps restant estimé est plus petit que le temps restant avant l'échéance de la procédure audio qui envoie le signal à la carte son. Le temps d'exécution moyen du nœud est ensuite mis à jour.

Deux situations de surcharge du processeur peuvent se produire, une surcharge du processeur *transitoire*, ou bien une surcharge *permanente*. On dit que le processeur est surchargé de manière permanente quand le processeur est surchargé pendant plus de *k* cycles audio. La façon dont est déterminé *k* est hors-sujet dans le cadre de cet article.

**En cas de surcharge transitoire.** Étant donnée une chaîne de nœuds de traitement, il est préférable de dégrader les nœuds à la fin de la chaîne plutôt qu'au début, comme la qualité n'augmente jamais sur une chaîne (voir Section 4.1).

Une autre heuristique est de minimiser le nombre de nœuds de rééchantillonnage ajoutés, tout en maximisant le nombre de nœuds dégradés, pour minimiser le temps supplémentaire de calcul du aux nœuds ajoutés. Cela implique que le nombre de chemins à dégrader doit être minimisé, et c'est pour cela qu'on choisit d'explorer le graphe branche par branche.

A chaque cycle d'exécution du graphe audio, l'algorithme 1 vérifie le temps restant à exécuter avant l'échéance et le compare au temps restant estimé d'exécution. S'il estime qu'il ne reste pas suffisamment de temps, on doit choisir parmi les nœuds restant à exécuter ceux à dégrader, comme décrit dans l'algorithme 2. On part d'un des nœuds *sortie*, on traverse le graphe en arrière jusqu'à ce qu'on a dégradé suffisamment pour que l'estimation du temps restant passe sous le temps restant donné par l'échéance. On peut explorer d'autres branches de même si ce n'est pas suffisant. Finalement, on ajoute les nœuds de sous-échantillonnage et de sur-échantillonnage au début et à la fin de ces branches du graphe.

Cependant, explorer successivement des branches, choisir le nœud à partir duquel dégrader, peut être encore trop coûteux en temps de calcul. Au lieu de l'algorithme 2, on

**Algorithm 1 Exécution du graphe pendant un cycle, avec éventuelle dégradation.**

**Require:** S a schedule, G an audio graph with associated execution times, *d* deadline
**while** schedule is not empty **do**
    node ← pop_first(schedule)
    UPDATE expectedRemainingTime)
    **if** expectedRemainingTime ≥ 0 **then**
        CHOOSENODES(G, deadline, expectedRemainingTime)
    **end if**
    samples ← GETINCOMINGSAMPLES
    **if** node.firstToDegrade **then**
        DOWNSAMPLE(samples)
    **end if**
    out Buffers ← NODE(samples)
    **if** node.lastToDegrade **then**
        UPSAMPLE(out Buffers)
    **end if**
    update performanceCounters
    node.visited ← true
**end while**

peut plus simplement dégrader directement tous les nœuds restants du graphe, en insérant au début de chaque branche restante et à la fin les nœuds rééchantillonneurs.

**En cas de surcharge permanente.** Dans le cas d'une détection d'une surcharge permanente du processeur, une version dégradée calculée précédemment peut être utilisée, sans avoir besoin de la calculer à nouveau. Les versions dégradées calculées hors-ligne pourraient être utilisées ici.

## 6. ÉTUDE EXPÉRIMENTALE

Les algorithmes de dégradation en ligne ont été implémentés dans un prototype en Rust. Le rééchantillonnage est effectué avec `libsampler`[^3]. Cette bibliothèque *opensource* permet de rééchantillonner avec des ratios arbitraires, compris entre un sous-échantillonnage par 256, jusqu'à un sur-échantillonnage par 256. Le taux d'échantillonnage peut être changé en temps réel. La bibliothèque fournit cinq rééchantillonneurs, *best*, *medium*, *fastest quality sinc* rééchantilloneurs (comme dans [16]), *zero order hold* où les valeurs interpolées sont égales à la dernière valeur, et un *rééchantillonneur linéaire*. Ils sont ici rangés en ordre décroissant de qualité.

On considère deux formes extrêmes de graphes audio, l'un où tous les effets sont connectés à un même nœud de *sortie*, sur la Figure 4a, et l'autre avec une seule branche, sur la Figure 4b. Les effets utilisés sont des oscillateurs sinusoïdaux, des modulateurs par une fréquence sinusoïdale, et des mixeurs.

Les expériences ont été effectuées sur un Mac Book Pro avec un processeur Intel Core i7 à 2.6 GHz, muni de 8

<footnote>[^3]: http://www.mega-nerd.com/SRC/</footnote>
**Algorithm 2 Comment choisir les nœuds à dégrader.**

**function** CHOOSENODES(graph, budget, expectedRemainingTime)
    expectedDegradedTime ← expectedRemainingTime
    while budget - expectedDegradedTime ≤ 0 **do**
        currentNode ← LASTNODE(graph)
        LASTNODE(graph).lastToDegrade ← true
        **do**
            UPDATE(expecteDegradedTime)
            parentNodes ← PARENTS(currentNode)
            currentNode ←
                FIRSTNOTVISITED(parentNodes)
            currentNode.visited ← true
            expectedDegradedTime ← expectedDegradedTime - EXPECTEDTIME(currentNode) + DEGRAGEDTIME(currentNode)
            while ¬ currentNode.visited ∧
                      currentNode.firstToDegrade ← true
            **end while**
        **end function**

Gb de Ram. Les graphes ont exécutés pendant 5 secondes chacun.

Sur la Figure 5, on montre comment l’algorithme de dégradation utilisant l’heuristique de dégradation de tous les nœuds restants se comporte quand on passe de 2000 à 3000 modulateurs, pour le graphe de la Figure 4b. La ressource temporelle restante est le temps qui reste avant l’échéance après que tous les traitements audio ont été effectués dans un cycle. Si elle est négative, cela signifie que l’échéance a été ratée et que le nœud de calcul est en retard. Pour un graphe avec 2000 modulateurs, même si l’ordonnanceur détecte qu’il doit parfois dégrader, la resource temporelle est suffisante pour ne pas rater d’échéance en général. Pour le graphe avec 3000 modulateurs, l’algorithme de dégradation empêche bien en pratique de rater des échéances, et donc d’entendre des clics audio.

Nous avons aussi mesuré les frais causés par l’ordonnanceur, c’est-à-dire le temps supplémentaire pris par l’ordonnanceur pour trouver les nœuds à dégrader. Au plus, nous avons trouvé que ce temps supplémentaire s’élevait à 50µs, c’est-à-dire à 1,25% de l’échéance de 4000µs, pour 2000 nœuds. Cependant, la complexité dans le pire des cas de l’algorithme est linéaire en le nombre de nœuds.

## 7. CONCLUSION ET PERSPECTIVES

Nous avons mis au point un algorithme en ligne de dégradation de nœuds dans un graphe flot de données dédié au son, cherchant un compromis entre qualité sonore dépendant du taux d’échantillonnage sur des branches du graphe et respect des échéances induites par la procédure d’entrée/sortie audio. En cas de surcharge permanente du processeur, une version dégradée pré-calculée du graphe pourrait être utilisée à la volée.

Nous souhaitons dans la suite de notre travail explorer

<figure><img src="image_22.png" /><figcaption>(b) Audio graphe chainé avec un oscillateur suivi de *n* modulateurs.</figcaption></figure>

<caption>(a) Graphe audio plat avec *n* oscillateurs.</caption>

<caption>**Figure 4 :** Les graphes utilisés pour les expériences.</caption>

plus en détails le calcul de versions dégradées du graphe hors-ligne. L’algorithme en ligne utilise des heuristiques pour choisir les nœuds à dégrader, au lieu de pré-calculer exactement des versions dégradées du graphe pour différents niveaux de qualité. Les versions dégradées pré-calculées du graphe audio qui peuvent être stockées sont en nombre limité. Les surcharges transitoires du processeur peuvent arriver en pleine exécution du graphe audio, et les versions pré-calculées ne sont pas forcément adaptés car elles peuvent exiger de dégrader certains nœuds qui ont pourtant déjà été exécutées en version normale lors du cycle en cours.

Nous voulons aussi évaluer d'autres heuristiques, sur un plus grand nombre de graphes, par exemple des graphes générés aléatoirement, et tester nos algorithmes de dégradation sur de vraies pièces musicales et dans des SIMs existants; nous avons commencé à étudier la possibilité de les ajouter dans Puredata dans le code source du SIM. Nous pourrions aussi générer directement des versions *optimisés* de patchs Puredata ou Max, ou de programmes Faust [12], en ayant inséré des sous-échantillonneurs dans le graphe audio décrit.

## 8. REMERCIEMENTS

Ce travail a débuté lors d'une visite dans l'équipe du professeur Christoph Kirsch de l'université de Salzbourg en Autriche, que je souhaite remercier pour ses conseils
<figure><img src="image_23.png" /><figcaption>**Figure 5** : Les résultats pour le graphe chainé de la Figure 4b.</figcaption></figure>

avisés. Je remercie aussi mes encadrants de thèse, Florent Jacquemard et Jean-Louis Giavitto, pour leurs conseils.

## 9. REFERENCES

[1] Saamer Akhshabi, Ali C Begen, and Constantine Dovrolis. An experimental evaluation of rate-adaptation algorithms in adaptive streaming over http. In *Proceedings of the second annual ACM conference on Multimedia systems*, pages 157–168. ACM, 2011.

[2] Karl-Erik Årzén, Vanessa Romero Segovia, Stefan Schorr, and Gerhard Fehler. Adaptive resource management made real. In *3rd Workshop on Adaptive and Reconfigurable Embedded Systems*, 2011.

[3] T. Blechmann. Supernova-a multiprocessor aware

real-time audio synthesis engine for supercollider. Master's thesis, Vienna University of Technology, 2011.

[4] Alan Burns and Robert Davis. Mixed criticality systems-a review. *Department of Computer Science, University of York, Tech. Rep*, 2013.

[5] Pierre Donat-Bouillud, Jean-Louis Giavitto, Arshia Cont, Nicolas Schmidt, and Yann Orlarey. Embedding native audio-processing in a score following system with quasi sample accuracy. In *ICMC 2016-42th International Computer Music Conference*, 2016.

[6] José Echeveste, Arshia Cont, Jean-Louis Giavitto, and Florent Jacquemard. Operational semantics of a domain specific language for real time musician-computer interaction. *Discrete Event Dynamic Systems*, 2013.

[7] Christoph M Kirsch. Principles of real-time programming. In *International Workshop on Embedded Software*, pages 61–75. Springer, 2002.

[8] Edward A Lee and David G Messerschmitt. Synchronous data flow. *Proceedings of the IEEE*, 75.

[9] Edward A Lee and Thomas M Parks. Dataflow process networks. *Proceedings of the IEEE*, 83.

[10] Jane WS Liu, Kwei-Jay Lin, Wei Kuan Shih, Albert Chuang-shi Yu, Jen-Yao Chung, and Wei Zhao. *Algorithms for scheduling imprecise computations*. Springer, 1991.

[11] James McCartney. Supercollider : a new real time synthesis language. In *Proceedings of the International Computer Music Conference*, 1996.

[12] Yann Orlarey, Dominique Fober, and Stephane Letz. Syntactical and semantical aspects of faust. *Soft Computing*, 8(9):623–632, 2004.

[13] M. Puckette. Using pd as a score language. In *Proc. Int. Computer Music Conf.*, pages 184–187, September 2002.

[14] Stuart Rosen and Peter Howell. *Signals and systems for speech and hearing*, volume 29. Brill, 2011.

[15] Robert Rowe. *Interactive Music Systems : Machine Listening and Composing*. AAAI Press, 1993.

[16] Julius O Smith and Phil Gossett. A flexible sampling-rate conversion method. In *Acoustics, Speech, and Signal Processing, IEEE International Conference on ICASS'84.*, volume 9, pages 112–115. IEEE, 1984.

[17] Swagath Venkataramani, Srimat T Chakradhar, Kau-shik Roy, and Anand Raghunathan. Computing ap-proximately, and efficiently. In *Proceedings of the 2015 Design, Automation & Test in Europe Conference & Exhibition*, pages 748–751. EDA Consor-tium, 2015.

[18] G. Wang. *The ChucK audio programming language.* A strongly-timed and on-the-fly envi-ron/mentality". PhD thesis, Princeton University, 2009.
[19] Zeyuan Allen Zhu, Sasa Misailovic, Jonathan A Kelner, and Martin Rinard. Randomized accuracy-aware program transformations for efficient approximate computations. In *ACM SIGPLAN Notices*, volume 47, pages 441–454. ACM, 2012.

[20] David Zicarelli. How I learned to love a program that does nothing. *Comput. Music J.*, 26(4):44–51, 2002.
SYNCHRONISATION DE DONNÉES INTER-PROCESSUS
DANS LES APPLICATIONS AUDIO TEMPS RÉEL :
QU'EST-CE QUI DÉBLOQUE ?

Thibaut Carpentier

CNRS – Ircam – Sorbonne Université – Ministère de la Culture
Sciences et Technologies de la Musique et du Son (UMR 9912 STMS)
1, place Igor Stravinsky, 75004 Paris
thibaut.carpentier@ircam.fr

RÉSUMÉ

Cet article expose des considérations pragmatiques pour
le développement d'applications audio temps réel. Il passe
en revue plusieurs concepts fondamentaux qui doivent être
pris en compte lors de l'élaboration de telles applications.
En particulier, les applications audio sont intrinsèquement
multi-thread et asynchrones, et la synchronisation de res-
sources partagées entre plusieurs processus concurrents
doit donc faire l'objet d'une attention minutieuse. Les
concepts exposés ici ne sont pas nouveaux, toutefois leur
mise en pratique, dans un contexte concret de production,
demeure un défi majeur pour tous les développeurs audio.

Enfin, la thèse soutenue dans cette étude est que les mécanismes de synchronisation dits *lock-free* ne sont généralement pas indispensables et doivent être évités autant que faire se peut. Une approche par *mutex* non-bloquant est proposée comme substitut simple et satisfaisant.

1. INTRODUCTION

1.1. Anatomie d'une application audio temps réel

L'expression “application audio temps réel” se réfère,
dans son acception la plus répandue, à des logiciels <fnref n="1" /> qui
génèrent, analysent, ou transforment des signaux audio-
numériques, en un temps suffisamment court pour que la
latence induite soit auditivement imperceptible (de l'ordre
de quelques millisecondes). Ces logiciels sont majori-
tairement mis en œuvre sur des systèmes d'exploitation
“conventionnels” tels que macOS, Linux, ou Windows. Il
est à noter que lesdits systèmes d'exploitation (Operating
Systems, OS) ne sont pas des systèmes temps réel au sens
strict (hard real-time) : le temps maximum entre un sti-
mulus d'entrée et une réponse de sortie ne peut être ga-
ranti de façon déterministe. Lorsque ce temps excède les
contraintes du temps réel, la “qualité du service” se dé-
grade, mais le système reste opérationnel et continue de
fonctionner; on parle dans ce cas de temps réel souple (soft
real-time).

Les logiciels audio se présentent généralement sous
forme d'applications autonomes (standalones) ou de plu-
gins insérables dans des environnements hôtes, tels que des
stations de travail audionumériques (Digital Audio Worksta-
tions, DAW) ou des frameworks multimédia interactifs tels
que Max [48], PureData [49], SuperCollider [37], Unity3D,
etc. Pour des raisons de performances et d'inter-opérabilité
avec leurs environnements hôtes, la vaste majorité des ap-
plications audio temps réel est codée en langage C ou C++.
Ce dernier servira d'exemple pour le présent article ; toute-
fois la plupart des concepts exposés sont transposables à
d'autres langages.

1.2. Un contexte intrinsèquement concurrent

Les OS et les applications hôtes considérés ici s'ap-
puient fortement sur des paradigmes de programmation
concurrente : pour offrir une interaction riche avec l'envi-
ronnement externe, et pour tirer profit des systèmes multi-
cœurs, plusieurs processus (ou threads) sont exécutés en
parallèle. Les applications audio doivent donc s'intégrer
dans de tels contextes intrinsèquement concurrents. Typi-
quement plusieurs threads sont exécutés "simultanément",
afin de gérer le traitement des échantillons audio, les flux
de données entrantes (instruments MIDI, capteurs, vidéo,
etc.), ou des interactions de l'utilisateur (clavier, souris,
etc.). Évidemment, la concurrence ici en jeu est de nature
coopérative, c'est-à-dire que les différents processus ne
sont pas isolés : pour le bon fonctionnement de l'applica-
tion, ils doivent interagir, notamment en s'échangeant des
ressources.

Une conséquence induite par la programmation concurrente est le phénomène dit “d’un indéterminisme d’exécution” : l’ordre d’exécution des instructions n’est plus séquentiel, mais il est soumis à une politique d’ordonnancement (*scheduling*) qui est déterminée par le noyau du système d’exploitation et par les règles de l’environnement hôte. Il en résulte que l’accès aux ressources partagées entre les processus doit être protégé : le développeur doit assurer leur intégrité, typiquement en mettant en œuvre des mécanismes de synchronisation (qui seront discutés plus loin).

<figure><img src="image_24.png" /></figure>

<footnote n="1">1. Les hardwares dédiés (DSP, FPGA, etc.) sortent du cadre de cet
article.</footnote>
### 1.3. Ordonnancement

Nous considérons le comportement de l'environnement temps réel Max comme un exemple canonique. Il s'agit certes d'un cas particulier, mais celui-ci tout en étant simple s'avère représentatif de la majorité des environnements audio. Dans Max, les processeurs audio peuvent être pilotés par des événements (messages, clavier MIDI, clic souris, etc.). Comparativement à la cadence audio (*audio-rate*, typiquement 48000 échantillons par seconde), ces événements surviennent de façon sporadique ; ils sont dits *control-rate*. Les événements peuvent être à haute priorité (i.e. nécessitant une grande précision temporelle, par exemple métronome, messages MIDI) ou à basse priorité (interaction avec souris/clavier, ou plus généralement avec des interfaces graphiques). Les événements à basse priorité sont toujours traités dans le *thread principal* (*main thread*) de l'application ; selon les réglages de Max (paramètre *overdrive*) un second *thread* peut être activé pour gérer les événements à haute priorité (si l'*overdrive* est désactivé, tous les événements transitent dans le *thread principal*). Les événements à haute priorité peuvent donc potentiellement interrompre le *thread principal*, et s'exécuter avant les événements à basse priorité, selon un ordre non déterministe. De la même manière, dans une DAW, un *plugin* pourra recevoir des événements depuis le *thread principal* (interface graphique) et depuis un *thread* séparé pour l'automation<fnref n="2" />.

Le *scheduler* de Max peut gérer la priorité des événements et les affecter au *thread* haute ou basse priorité (voire à d'autres *threads*). Tous les *threads* restent toutefois tributaires de l'ordonnancement du noyau de l'OS qui régule l'accès des processus au CPU. Enfin, les échantillons audio sont traités dans un *thread* spécifique : le *thread* audio.

### 1.4. Le thread audio

Une application audionumérique délivre un flux d'échantillons audio vers le convertisseur numérique/analogique (*digital to analog converter*, DAC) de l'interface audio (carte son). Les échantillons sont produits à une cadence fixe, imposée par la fréquence d'échantillonnage de l'interface. Sur les OS conventionnels, les échantillons ne sont pas délivrés un par un, mais par bloc (*buffer*) typiquement de 32 à 512 échantillons. Les *buffers* sont convoyés vers le driver audio par l'intermédiaire d'une couche de l'OS, e.g. CoreAudio sous macOS, ALSA sous Linux, ASIO sous Windows. Pour ce faire, l'application audio reçoit périodiquement un appel (*callback*) de la couche audio. Ce *callback* survient toutes les *N* millisecondes (*N*=1000×*buffersize/samplerate*); il fournit le(*s*) *buffer*(*s*) des échantillons d'entrée, et l'application audio doit remplir les *buffers* de sortie. Ce mécanisme de traitement par blocs introduit une certaine latence d'entrée/sortie, proportionnelle à la taille de bloc, et généralement ajustable par l'utilisateur final (dans l'application hôte).

Le *callback* survient dans un *thread* spécifique, appelé *thread* audio et parfois qualifié de "temps réel". Sur les OS

traditionnels, le *thread* audio est en fait un *thread* à "temps borné" (*time constraint policy*), généralement préemptable, et qui doit, pour garantir un fonctionnement correct, exécuter sa tâche avant une échéance donnée (*deadline*). Typiquement, un tel *thread* indique à l'OS sa périodicité minimale *N*, sa durée nominale d'exécution, et sa contrainte maximale (durée minimale d'exécution). Ces données sont comme une promesse faite à l'OS, et sur cette base, le *scheduler* a la responsabilité d'allouer des ressources et d'ordonnancer les différents processus de sorte à servir au mieux les différents *threads* courants.

Si l'échéance du *thread* audio n'est pas satisfaite, des artefacts (*clicks*, *glitches*) sont audibles. Plusieurs phénomènes peuvent en être la cause : a) un défaut du *scheduler* de l'OS (rare), b) le code de traitement audio n'est pas assez rapide pour s'exécuter dans le temps imparti (ne "tient pas" le temps réel), ou c) le code utilisé n'a pas un temps d'exécution déterministe ou borné. Le problème b) est lié à l'efficacité du code et/ou des algorithmes employés ; des techniques d'optimisation des performances peuvent alors être mises en jeu, mais ceci sort du cadre de cet article. Pour éviter le phénomène c), on trouve<fnref n="3" /> un certain nombre de règles de bonne conduite à respecter dans le *thread* audio :

- éviter les opérations (potentiellement) bloquantes telles que l'acquisition de verrous (*locks*), la lecture de données sur disque ou sur une *socket* réseau, etc.;

- proscrire les allocations mémoire (*std::malloc*, *std::free*, opérateurs *new* ou *delete*, etc.) car elles peuvent être bloquantes ou, selon leur implémentation, basée sur des algorithmes à temps non déterministe. La mémoire utilisée dans le *thread* audio doit être pré-allouée à l'avance dans des *threads* non-critiques. Aussi, la réservation de zones mémoire (*memory pool*) est fréquemment recommandée;

- désactiver si besoin le ramasse-miette (*garbage collector*); ceci ne concerne pas C++, mais peut affecter d'autres langages tels que *Java* ou *C#*;

- ne pas appeler de code tiers si son comportement (temps d'exécution) n'est pas connu et prédictible ; ceci concerne également tout appel à des fonctions de l'OS qui, sauf preuve du contraire, doivent être considérées comme bloquantes. De même, tout appel à *Objective-C* ou *Swift* est interdit puisque la résolution des messages durant l'exécution (*dynamic dispatch*) peut déclencher des verrous. Signalons ici qu'il existe des outils d'analyse statique de code permettant de détecter les appels à des fonctions non fiables (voir par exemple [38]);

- dans le choix des algorithmes employés, ne pas tenir compte de leur complexité en moyenne (ni leur complexité amortie), mais de leur complexité dans le cas le plus défavorable (*worst-case complexity*). De façon générale, lisser la charge de travail (sur plusieurs *callbacks*), et éviter tout algorithme pouvant manifester des pics de calcul (CPU spikes);

<footnote n="2">2. Les mécanismes peuvent varier sensiblement d'une DAW à l'autre.</footnote>

<footnote n="3">3. essentiellement sur des sites web dédiés au développement C++, mais on pourra aussi se référer à [19, 12, 47, 51].</footnote>
## 1.5. Problématique

Ainsi, les processeurs audio temps réel doivent s'inscrire dans des contextes *multi-thread*, et échanger de façon asynchrone des données avec le *callback* audio qui est soumis à d'importantes contraintes pour s'exécuter en un temps borné. Comment garantir alors un accès *thread-safe* aux ressources partagées par le *thread* audio ? Cette problématique n'est pas nouvelle : les paradigmes de programmation concurrente et les mécanismes de synchronisation inter-processus sont, d'un point de vue théorique, bien maîtrisés et largement discutés dans la littérature [50, 46, 4]. Toutefois leur mise en œuvre pratique, notamment dans un contexte de développement d'applications audio temps réel, demeure un défi et une source de questionnement pour les développeurs.

# 2. MÉCANISMES DE SYNCHRONISATION DE DONNÉES ENTRE THREADS

## 2.1. Le modèle de mémoire en C++

C et C++ sont spécifiés comme des langages séquentiels à *thread* unique (*single-threaded languages*), la gestion des *threads* étant reléguée dans des bibliothèques de l'OS (par exemple *pthread* sur les systèmes Posix), ou des APIs dédiées (par exemple *OpenMP* <fnref n="4" />). En conséquence, les compileurs n'ont pour l'essentiel pas connaissance des *threads*, et ils sont autorisés à effectuer un certain nombre de transformations du code, en particulier ré-ordonner l'effectation de variables indépendantes tant que ces transformations préservent la consistance du programme séquentiel *single-thread*. Dans des contextes *multithread*, l'accès aux données peut engendrer des problèmes de condition de concurrence (*race condition* ou *data race*). Le modèle de mémoire (*memory model*, voir [10, 1, 11, 8, 57]) du langage décrit la sémantique des variables partagées, i.e. qu'il spécifie la ou les valeurs qu'elles peuvent retourner dans un programme *multithread* <fnref n="5" />. Dans le cas de C++, le *memory model* stipule que deux actions (*threads*) sont en conflit si elles accèdent au même emplacement mémoire (e.g. à la même variable) et que l'une au moins des actions est une écriture. Par exemple lire une variable *x* depuis un *thread A*, alors qu'un *thread B* écrit dans *x* constitue une *data race*. C++ garantit l'exécution séquentielle correcte (*sequential consistency* [33, 1]) des programmes sans *race condition*; en présence de *data race*, la sémantique et le comportement ne sont pas définis (*undefined behaviour*: tout et n'importe quoi peut alors se produire, éventuellement un crash du programme [9]). Il est à noter que le *memory model* de C++ est sensiblement différent, par exemple, de Java ou C# [36, 44, 1]. Par ailleurs, signaux qu'il existe également un *memory model* au niveau *hardware* car les processeurs, à l'instar des compileurs, peuvent ré-ordonner les instructions. Évidemment les modèles *software* et *hardware* se doivent d'être compatibles.

Pour permettre l'accès concurrent à des variables normales conformément au *memory model*, c'est-à-dire en évitant les *data races*, plusieurs primitives de synchronisation sont disponibles.

## 2.2. Verrous de protection

### 2.2.1. Sections critiques

Le mécanisme le plus communément usité pour éviter les *race conditions* est d'employer des verrous de protection (*locks*) qui permettent de garantir un accès mutuelle exclusif à une donnée (ou structure de données). Les primitives de synchronisation sont appelées *mutex* (*mutual exclusion*). Un *thread* peut verrouiller le *mutex* pendant qu'il accède à la donnée partagée; on dira qu'il *possède* le *mutex*. Tant qu'il possède le *mutex*, tous les autres *threads* sont bloqués (i.e. mis en attente) s'ils essayent d'accéder à la ressource. La zone de code protégée par le *mutex* est désignée "section critique". Ce mécanisme garantit que tous les *threads* voient toujours une version consistante de la donnée.

Depuis C++11, la bibliothèque standard (*Standard Template Library*, STL) fournit plusieurs variantes de *mutex* (*std::mutex*, *std::recursive_mutex*, etc.) ainsi que d'utiles wrappers (par exemple *std::lock-guard*) qui facilitent leur mise en œuvre selon un idiome RAII <fnref n="6" />. Avec C++14 et C++17, la STL s'enrichit encore avec les ajouts de *std::shared_mutex*, *std::shared_lock*, *std:: scoped_lock*, etc. L'accès à ces fonctionnalités dans la STL simplifie grandement la tâche des développeurs, les affranchissant des différences d'implémentation entre OS <fnref n="7" />.

### 2.2.2. Inconvénients

L'utilisation de *mutex* s'accompagne toutefois d'un certain nombre de dangers. En particulier, ils peuvent conduire à une situation d'inter-blocage (*deadlock*) lorsque deux processus concurrents s'attendent mutuellement, essayant d'acquérir deux *mutex* dans un ordre différent <fnref n="8" />. Il existe des recommandations et des algorithmes dédiés pour se prémunir de ce phénomène.

Dans un contexte audio temps réel, d'autres inconvénients des *mutex* sont encore à prendre en considération :

— Si une ressource est bloquée par un *mutex*, les autres *threads* ne peuvent savoir pendant combien de temps elle sera bloquée. En outre, l'acquisition d'un *mutex* est tributaire du *scheduling* opéré par le noyau de l'OS. Le temps d'acquisition d'un *mutex* n'est donc pas prédicible de façon déterministe.

<footnote n="4">4. www.openmp.org</footnote>

<footnote n="5">5. Il est surprenant de noter que le *memory model* *multi-thread* n'apparaît qu'à partir de la révision C++11.</footnote>

<footnote n="6">6. Resource acquisition is initialization (RAII): par exemple la classe *std::lock-guard* verrouille le *mutex* dès son constructeur, et le débloque dans son déstructeur donc dès qu'il disparaît du *scope* courant.</footnote>

<footnote n="7">7. Il existe en pratique plusieurs stratégies pour implémenter les *mutex* (e.g. *binary semaphores*, *spinlocks*, *ticket locks*) mais cela sort du cadre de cet article.</footnote>

<footnote n="8">8. Exemple : un *thread* t1 acquiert un *mutex* m1, un *thread* t2 acquiert un *mutex* m2, t1 attend pour acquérir m2, t2 attend pour acquérir m1.</footnote>
— Les mutex peuvent provoquer un scénario d'inversion de priorité (*priority inversion*) : lorsqu'une tâche *H* à haute priorité attend un mutex détenu par une tâche *B* à basse priorité, *B* peut se retrouver préemptée par une tierce tâche *M* de priorité intermédiaire, inversant l'ordonnancement prévu (*M* < *H*) et bloquant l'exécution de *H*.

— Enfin les mutex peuvent générer un problème de performance dit lock convoy lorsque des tâches de même priorité tentent d’acquérir de façon répétée un mutex ; les threads ne sont pas bloqués, mais à chaque tentative avortée d’acquisition du mutex, le thread doit renoncer à son quantum auprès du scheduler, et il provoque une commutation de contexte (*context switch*). Ceci contribue à dégrader les performances du système.

Notons qu'il existe d'autres problèmes à prendre à compte
lors de l'usage de mutex (e.g. statut d'un thread tué tandis
qu'il détient un mutex) mais ceux-ci ne sont pas directe-
ment pertinents pour notre étude.

2.3. Les approches non-bloquantes

Pour pallier les inconvénients des verrous, des tech-
niques de programmation non-bloquantes, généralement
dites lock-free, ont été proposées. La notion de lock-free
fait référence à l'accès thread-safe (i.e. exempt de race
conditions) à des données partagées, sans le recours à des
primitives telles que les mutex. On recense trois niveaux
de stratégies non-bloquantes [25] :

— Programmation *wait-free* [31] : il s'agit de la garantie "la plus forte". Un système est dit sans attente (*wait-free*) si tous les processus sont garantis de toujours progresser ; aucun *thread* n'est entravé par un autre. Il est donc assuré que toute opération pourra se réaliser un nombre fini d'instructions. En pratique, l'implémentation d'algorithmes *wait-free* est très difficile et assez rare. Ils sont toutefois employés dans des contextes temps réel strict (*hard real-time*) [21].

— Programmation *lock-free* : les algorithmes *lock-free* garantissent qu'au moins l'un des *threads* pourra toujours progresser. En particulier, si l'un des *threads* est préempté tandis qu'il travaille sur une ressource *lock-free*, il ne bloque pas les autres *threads* qui peuvent accéder à cette ressource et donc converger vers un résultat en un nombre fini d'instructions. Des trois approches non-bloquantes, le paradigme *lock-free* est incontestablement le plus répandu. Notons que tous les algorithmes *wait-free* sont nécessairement *lock-free*.

— Programmation *obstruction-free* [30] : il s’agit de la garantie “la plus faible”. Dans ce paradigme, une opération concurrente peut être annulée et ré-essayée ultérieurement, au profit des autres threads en compétition. Un algorithme est donc *obstruction-free* si, à un instant donné, un thread “isolé” (tous les autres threads en concurrence étant suspendus) peut accomplir ses opérations en un nombre fini d’instructions. Cette approche permet d’éviter les situations d’inter-blocage et d’inversion de priorité, mais peut causer des phénomènes de famine (*livelock*) lorsque deux opérations

mutuellement en compétition s'annulent l'une l'autre.
Notons enfin que tous les algorithmes *lock-free* sont
nécessairement *obstruction-free*.

2.4. Implémentation pratique des structures lock-free

La conception d'algorithmes lock-free se révèle très
difficile, même d'un point de vue formel. En pratique, on
développe plutôt des structures de données lock-free (voir
paragraphe 2.4.2). Celles-ci sont fréquemment usitées ou
recommandées dans le domaine des applications audio
temps réel [23, 24, 35, 19, 54, 53, 6, 7, 18, 55]. L'implé-
mentation de telles structures lock-free s'appuie sur des
variables dites de synchronisation (par opposition aux va-
riables normales) et qui permettent l'exécution d'opération
atomiques.

2.4.1. Opérations atomiques

On dit d'une opération qu'elle est atomique si elle est
indivisible, i.e. qu'elle ne peut être interrompue. Soit l'opé-
ration s'exécute en entier, soit elle ne s'exécute pas du tout,
et il ne peut y avoir d'état intermédiaire.

Depuis C++11, des types atomiques std::atomic sont disponibles dans la STL. Toutes les opérations qu'ils permettent sont atomiques, et ce sont les seules opérations strictement atomiques offertes par le langage 9. std::atomic offre un nombre restreint de méthodes telles que load, store, fetch_add et compareexchange. Cette dernière primitive est fondamentale et elle est à la base de toutes les implémentations de structures lock-free. Comme son nom l'indique, elle réalise un compare-and-swap (aussi appelé compare-and-exchange) : cette méthode compare la valeur courante x avec une valeur attendue y ; en cas d'égalité, remplace la valeur courante par une nouvelle valeur désirée z (opération read-modify-write) ; sinon, la valeur courante x est chargée (load) dans y. L'atOMICITÉ de cet échange est rendue possible par des propriétés du processeur (hardware feature) et non de l'OS. Notons que ceci introduit nécessairement une synchronisation (barrière) au niveau CPU ; le traitement de variables atomiques est donc significativement plus lent que celui de variables normales 10 .

<footnote n="9">9. Les fonctionnalités de std::atomic sont peu ou prou équivalentes à la notion de volatile en langage Java. Notons en revanche que le mot-clé volatile en C++ n'a aucun rapport ! En C++, volatile ne garantit pas l'atomicité, ne permet pas la synchronisation entre threads et n'empêche pas le ré-ordonnancement des instructions (ni au niveau du compilateur, ni au niveau hardware).</footnote>

<footnote n="10">10. Par souci d'exhaustivité, indiquons que les opérations sur std::atomic peuvent être paramétrées plus finement en spécifiant le mode d'ordonnancement mémoire (memory ordering). En effet, le memory model C++ offre ici plusieurs alternatives (relaxed memory ordering, acquire/release model, consume/release model) qui permettent de contrôler au bas-niveau les barrières mémoires (memory fences) employées par le hardware [5]. On parle de low-level atomics. Ce contrôle de bas-niveau peut, en théorie, accroître les performances. Toutefois, ces modes de fonctionnement impliquent un assouplissement du memory model, et en particulier la consistance séquentielle du programme (voir paragraphe 2.1) n'est plus garantie. Sans consistance séquentielle, le code devient extrêmement difficile à écrire et pire encore à déboguer ; c'est pourquoi il est généralement déconseillé de s'y risquer, et nous ne nous étendrons pas</footnote>
`std::atomic<T>` est une classe template dont le paramètre `T` peut prendre un type scalaire (`bool`, `char`, `int`, etc.) à l'exception des flottants (`float` ou `double`). Les scalaires englobant les pointeurs, `T` peut également être une adresse : `std::atomic<T*>`. Ici, il est important de noter que `T` ne peut être un object (`class`) ou un composé (`struct`, `union`, `array`); ceux-ci ne peuvent qu'être passés par leur adresse (i.e. par pointeur).

Enfin, signalons que `std::atomic` n'implique pas nécessairement une implémentation *lock-free*. Sur la plupart des processeurs "courants", `std::atomic<T>` est *lock-free* au moins pour les types `T` de 8 bits ; pour des types plus longs, il est possible que le processeur ne dispose pas du jeu d'instruction nécessaire et, de fait, `std::atomic<T>` sera implémenté de façon bloquante (au niveau du système)<fnref n="11" />.

### 2.4.2. De nombreuses structures existantes

Les variables atomiques rendent possible le développement de structures de données *lock-free*, notamment des queues (FIFO), des piles (LIFO), ou des listes chaînées (*singly* ou *doubly linked lists*), etc. Les exemples d'implémentationns ne manquent pas dans la littérature [56, 42, 23, 24, 50, 57, 25, 43, 2, 3, 30, 26]. Chacune de ces structures de données a des caractéristiques spécifiques, et le choix d'un *container* approprié dépend de l'application visée. Dans le cas de structures *lock-free*, ce choix est rendu plus difficile par l'existence de nombreuses variantes, fonction du contexte *multi-thread* : il faut en effet distinguer les *threads* qui écrivent dans le *container* (*producers*) de ceux qui y lisent (*consumers*). On trouve ainsi des déclinaisons *single producer-single consumer* (SPSC : cas le plus simple où un *thread* écrit et un autre lit), *multiple producers-multiple consumers* (MPMC : plusieurs *threads* concurrents peuvent écrire tandis que plusieurs *threads* peuvent lire), et les combinaisons SPMC et MPSC. On comprend aisément qu'une structure SPSC requiert moins d'efforts de synchronisation qu'une MPMC ; on peut donc supposer que cette dernière sera moins performante.

Les containers *lock-free* sont le plus souvent implémentés sous forme de *buffers* circulaires de taille fixe, de sorte à pré-allouer l'espace de stockage (statiquement ou durant l'initialisation) et à éviter les allocations dynamiques durant l'exécution.

## 2.5. Avantages et inconvénients des structures non-bloquantes

La principale motivation et l'avantage putatif de la programmation *lock-free* est la scalabilité, c'est-à-dire la capacité d'exploiter au mieux le parallélisme en augmentant le nombre d'opérations sur une structure de données tout en minimisant le temps d'attente de chacun des *threads*,

et en évitant les problèmes de deadlocks et d'inversion de priorité.

Cependant cela s'accompagne d'un certain nombre de difficultés, voire d'inconvénients par rapport aux techniques bloquantes :

- La conception et le développement de structures *lock-free* est considérablement plus difficile que la programmation à base de *mutex*. Il est communément admis que cela doit être strictement réservé aux développeurs les plus aguerris <fnref n="12" />. Bjarne Stroustrup et Herb Sutter ont récemment publié un ensemble de recommandations pour l'usage pertinent et efficace du langage C++ (Core Guidelines <fnref n="13" />) ; la première règle (CP.100) de la section *lock-free* stipule : "Don't use lock-free programming unless you absolutely have to. Reason : It's error-prone and requires expert level knowledge of language features, machine architecture, and data structures."

- En corollaire, les programmes *lock-free* sont très difficiles à maintenir et à débogguer, d'autant qu'il est compliqué sinon impossible de prouver formellement leur exactitude [34]. Par ailleurs, il est délicat d'évaluer les performances de la programmation non-bloquante et de démontrer ses bénéfices.

- Un autre inconvénient crucial des structures *lock-free* concerne la gestion de la mémoire et de la durée de vie des objets : comme expliqué au paragraphe 2.4.1, les containers *lock-free* de données "complexes" (composées) nécessitent l'usage de `std::atomic<T*>`. L'utilisation d'un pointeur nu `T*` contrevient à toutes les bonnes règles de codage en C++ moderne, et délègue à l'appelant la gestion mémoire (allocation/libération) des éléments du container. Ceci augmente considérablement la complexité du code et le risque d'erreurs <fnref n="14" />. Pour "pallier" cela, les structures *lock-free* doivent généralement être accompagnées d'un dispositif ad-hoc de gestion de la mémoire, ce qui constitue en soi un défi considérable. Ont été proposés à cet effet : des *pools* d'objets [32], des ramasse-miettes avec compteur de références [22, 29, 27], des *hazard pointers* [40, 3], des approches d'accès optimistes [17], etc.

- Elles peuvent être soumises au problème dit d'échange d'états ABA [20, 21].

- Elles ne constituent pas un remède au problème de contention.

- Certaines implémentations font l'objet de brevets [52, 45].

<footnote>sur ce sujet.</footnote>

<footnote n="11">11. La méthode `std::atomic<T>::is_lock_free()` permet de tester si l'implémentation est *lock-free* pour le type `T` et l'architecture courante. (Bizarrement, cette méthode est une fonction membre et non une fonction statique de classe.)</footnote>

<footnote n="12">12. Herb Sutter, éminent spécialiste du langage et président du comité de standardisation ISO C++, a donné de nombreux keynotes et tenu une colonne sur www.drdobbs.com au sujet de la programmation concurrente. Il explique en substance que la programmation *lock-free* est réellement maîtrisée par environ "fifty people in the world (and that's a generous idea)", car "writing lock-free code can confound anyone – even expert programmers" (Lock-Free Code : A False Sense of Security – publié en septembre 2008).</footnote>

<footnote n="13">13. https://github.com/isocpp/CppCoreGuidelines</footnote>

<footnote n="14">14. Tempérons un peu en signalant que des propositions concernant `std::atomic_shared_ptr` font l'objet de discussions pour C++20. Ceci pourrait grandement simplifier l'usage de structure de données atomiques.</footnote>
### 3. UNE APPROCHE SOUS-ESTIMÉE :
LES MUTEX NON-BLOQUANTS

Revenons à la problématique des applications audio temps réel. Nous l’avons dit dans la section 1, il est généralement inévitable de se confronter à la concurrence *multi-thread*, car le *callback* audio a besoin de communiquer avec un *thread* graphique, des *threads* haute priorité (inter-faces MIDI, sockets réseau, etc.) ou basse priorité (accès de fichiers sur disque, etc.). Dans ce contexte concurrent, une queue d’événements est requise. Deux cas d’usage typiques se présentent, et ils couvrent une vaste majorité des applications audio :

- Queue MPSC : différents *threads* (GUI et/ou haute priorité) sont employés pour piloter un moteur audio vers qui ils envoient des messages. Le *callback* audio est dans ce cas le seul et unique *consumer*, et plusieurs *threads* sont *producers* (généralement en nombre assez restreint – deux ou guère plus).

- Queue SPMC : le *thread* audio analyse les signaux et renvoie des valeurs par exemple vers une interface graphique (e.g. spectroscope, mesure de niveaux RMS et vu-mètres, etc.). Dans ce cas le *callback* audio est l’unique *producer*, et les autres *threads* sont *consumers*.

Dans la plupart des usages courants, il est en outre légitime de considérer que le timing des événements n’est pas critique. Typiquement, une réactivité de l’ordre de quelques millisecondes (supérieur à la taille de bloc audio) est satisfaisante (e.g. pour changer les paramètres d’un processeur audio ou rafraîchir la visualisation d’une analyse).

Sous ces hypothèses, nous proposons d’utiliser une technique de synchronisation inter-processus basée sur des *mutex* non-bloquants. Cette technique n’est pas nouvelle, mais elle semble largement sous-estimée et inexploitée. Elle repose simplement sur la méthode `try_lock()` de `std::mutex` qui, comme son nom l’indique, tente d’accéder le *mutex*. En cas d’échec (si le *mutex* est déjà verrouillé par ailleurs), la méthode retourne immédiatement, sans bloquer. En cas de succès, elle obtient la lock et les autres *threads* ne pourront entrer dans la section critique. La méthode est susceptible de retourner des faux négatifs.

Par exemple, dans le scénario de queue MPSC évoqué, l’écriture des événements par les *threads producers* est protégée par `std::mutex::lock()`, tandis que la lecture de la queue dans le *thread* audio se contente d’un `try_lock`. Si le `try_lock` échoue, l’opération sera re-tentée ultérieurement, par exemple au prochain bloc audio<fnref n="15" />. Le scénario de queue SPMC peut être traité de façon analogue.

Il est en théorie possible que le `try_lock` échoue systématiquement et que les paramètres dans le *thread* audio ne soient donc jamais mis à jour. Nous spéculons que ce phénomène n’a, en pratique, que peu de risque de se produire : le nombre de *threads* concurrents est très restreint,

les événements ne surviennent que sporadiquement, et les sections critiques sont très “courtes” (il s’agit de lire ou écrire un élément – e.g. message MIDI ou OSC – dans la queue, ce qui ne requiert que quelques instructions). Pour les mêmes raisons, on peut espérer que les *threads producers* pâtiront peu des blocages de *mutex*, de sorte que le programme demeure réactif.

La technique de *mutex* non-bloquants ici proposée permet donc une synchronisation *thread-safe* qui tire profit de la grande simplicité des *mutex* (l’effort de codage est minimal), sans risquer d’inversion de priorité (le *callback* audio n’étant jamais bloqué). Elle garantit la progression *obstruction-free* du *thread* audio [41].

### 3.1. Évaluation des performances

La notion de “progression garantie” est indépendante des performances du sytème. Il convient donc d’évaluer l’efficacité de la solution *try_lock*. Les mérites des structures non-bloquantes s’évaluent essentiellement selon deux critères [39] : leur capacité de traitement (*throughput*), c’est-à-dire la quantité de données pouvant être produites– consommées par la structure par unité de temps, et leur scalabilité, autrement dit leur capacité à “fournir plus de travail” lorsque plus de *threads* sont mobilisés. La scalabilité constitue un facteur crucial pour les applications massivement parallèles, mais, comme évoqué précédemment, ce critère est assez peu pertinent pour les programmes audio où le nombre de *threads* concurrents est très faible. Nous nous focaliserons donc sur le *throughput*.

### 3.2. Protocole

Nous réalisons un test comparatif entre la technique de *mutex* non-bloquants et une approche *lock-free*. Nous considérons le cas d’une queue de capacité fixe (de taille arbitraire $2^{15} = 32768$ éléments). Afin de s’affranchir des problèmes de gestion mémoire (voir paragraphe 2.5), nous testons une queue d’éléments entiers, c’est-à-dire que la queue *lock-free* s’appuie sur `std::atomic<int>`. Pour l’implémentation *lock-free*, nous utilisons une bibliothèque développée par Erik Rigtorp <fnref n="16" />. Celle-ci est simple d’usage (fichiers d’entête seulement), écrite en utilisant des idiomes C++ modernes, et optimisée (utilisation d’atomes de bas-niveau, alignement de cache, etc.). Elle propose une queue SPSC et une queue MPMC (il est plus rare de trouver des implémentations MSPC ou SPMC). Les tests sont conduits dans l’environnement Max (7.3.5 en 64 bit), sous macOS (10.13.4), processeur Intel Core i7 (4 Mo de cache L3). Nous évaluons le *throughput* des différentes implémentations en mesurant le nombre d’événements qui peuvent être écrits (*push*) et lus (*pop*) dans la queue pendant une unité de temps. Les événements sont simplement des messages Max convoyant un nombre entier tiré aléatoirement. Le *thread* audio est le seul et unique

<footnote n="15">15. Il est également possible d’obtenir une granularité plus fine en re-découpant le bloc audio en sous-blocs de taille plus petite.</footnote>

<footnote n="16">16. https://github.com/rigtorp/SPSCQueue
https://github.com/rigtorp/MPMCQueue</footnote>
consumer de ces messages. À chaque entrée dans le call-back audio, l'approche par mutex opère un try_lock; s'il est concluant, tous les événements de la queue sont dépilés un par un. Sinon l'exécution continue et un nouvel essai sera réalisé au bloc suivant. Les tests sont menés pour différentes tailles de bloc audio. La répétabilité n'étant pas garantie, les résultats sont moyennés sur trois réalisations de dix secondes chacune.

Dans le test #1, un seul *thread producer* (à haute priorité) est considéré, les messages étant générés par un métronome (objet metro). Le test #2 est identique, sinon que la contention globale du programme est accrue, en déclenchant un nombre considérable d'événements concurrents (stress test) dans les *threads* haute priorité, basse priorité, et d'autres *threads* de priorité normale. Le test #3 est similaire à #1, mais deux *threads producers* sont mobilisés, en utilisant à la fois metro et qmetro. Le test #4 est similaire à #3, mais la contention est accrue selon un protocole similaire à #2. Le test #4 représente la situation la plus réaliste d'un processeur audio temps réel en situation de performance. L'implémentation SPSC n'est pas valide lorsque plusieurs *threads* sont *producers*, aussi n'est-elle évaluée que dans les tests #1 et #2.

### 3.3. Analyse des résultats

Les résultats sont présentés dans la figure 1. Les données absolues n'ayant guère d'utilité ici, nous nous limiterons à des analyses en relatif.

Test #1 : les performances des queues *lock-free* SPSC et MPMC sont sensiblement identiques. Elles se dégradent notablement pour des tailles de bloc de 1024 et 2048. Nous émettons l'hypothèse que ce phénomène pourrait être lié à des problèmes de cache CPU ou d'erreur de page (*cache miss* ou *page fault*). Les performances de l'implémentation *try_lock* ne dépendent (quasiment) pas de la taille de *buffer*. Pour des *buffers* de taille ≤ 512, les queues *lock-free* surpassent nettement le *mutex*, et la variante SPSC est marginalement meilleure que MPMC (comme conjecturé paragraphe 2.4.2).

Test #2 : En présence de contention, les performances des trois implémentations se dégradent nettement. Toutefois la méthode mutex (avec une diminution de throughput d'environ 20%) semble plus robuste que les approches *lock-free* (perte d'environ 35%). Le comportement "anormal" des structures *lock-free* pour un bloc de 2048 (et 1024) est similaire au test #1 et demeure inexplicable.

Test #3 : En présence de *threads producers* concurrents, les performances générales diminuent encore. L'avantage de la queue MPMC sur le *try_lock* devient plus marginal.

Test #4 : Le throughput des implémentations testées est globalement moindre que dans #3. Le gain de MPMC par rapport au mutex non-bloquant est approximativement de 10%.

En définitive, les implémentations *lock-free* sont

globalement plus performantes que l'approche par mutex *try_lock*, toutefois elles sont plus sensibles à la contention. Dans le cas le plus réaliste (test #4), le gain de performances de MPMC est minime, d'autant que ces benchmarks ont été réalisés sur des queues d'entier donc ne étant pas compte du possible surcoût de gestion dynamique de la mémoire. Les mutex font partie des primitives de base de tous les OS courants; il est donc permis de croire qu'ils sont extrêmement optimisés pour tous les usages "courants", justifiant ainsi leur bon rendement ici observé.

Ces remarques sont proches des conclusions dressées dans [13], dans lequel l'auteur évalue les mérites respectifs des queues *lock-free* (implémentation de la bibliothèque Boost) et à base de lock : "[...] both the lock-based and lock-free queue perform just as well. This is contrary to [19], which suggests that audio programs should use lock-free data structures. These patterns, however, were based on the concurrency primitives that were available to programmers in 2005. The assumption now is that the newer C++ concurrency primitives perform much better than their older counterparts <fnref n="17" />, meaning that for some applications, the implementation overheads of a lock-free queue may not warrant its use."

### 4. CONCLUSION

Le développement d'applications audio temps réel nécessite la synchronisation de données partagées entre différents processus et le *thread* audio. En raison des contraintes spécifiques auxquelles est soumis ce dernier, on recommande fréquemment la mise en œuvre de mécanismes *lock-free*, afin d'éviter les phénomènes d'inter-blocage ou d'inversion de priorité. La programmation de structures *lock-free*, basée sur des variables atomiques, est particulièrement ardue et propice aux erreurs car elle requiert une connaissance experte du langage, du *memory model* et de l'architecture *hardware*. Elle complexifie en outre la gestion dynamique des ressources mémoire.

Dans cet article, nous avançons que les approches *lock-free*, si elles sont parfois les plus performantes, ne sont généralement pas indispensables dans un contexte d'audio temps réel, et elles devraient être évitées au profit de l'hygiène du code. Comme substitut simple et pragmatique, nous proposons un paradigme *thread-safe* non-bloquant s'appuyant sur le *try_lock* des mutex. Cette méthode garantit une progression *obstruction-free* du *thread* audio. Nous montrons que les performances obtenues sont proches des structures *lock-free*, et viables pour une exploitation en production <fnref n="18" />.

<footnote n="17">17. N. Pipenbrinck. Online discussion on lock-free data structures. http:// stackoverflow.com/ questions/27738660</footnote>

<footnote n="18">18. La technique *try_lock* est effectivement mise en œuvre dans plusieurs applications temps réel développées par l'auteur [16, 15, 28, 14].</footnote>
<figure><img src="image_25.png" /></figure>

<figure><img src="image_26.png" /></figure>

<figure><img src="image_27.png" /></figure>

<figure><img src="image_28.png" /><figcaption>**Figure 1.** En haut à gauche : test #1. En haut à droite : test #2. En bas à gauche : test #3. En bas à droite : test #4.</figcaption></figure>

## 5. REFERENCES

[1] Adve, S.V., Boehm, H.J., “Memory Models: A Case for Rethinking Parallel Languages and Hardware”, *Communications of the ACM*, volume 53(8), pages 90–101, Aug 2010.

[2] Alexandrescu, A., “Lock-Free Data Structures”, *C/C++ Users Journal*, Oct 2004.

[3] Alexandrescu, A., Michael, M.M., “Lock-Free Data Structures with Hazard Pointers”, *C/C++ Users Journal*, Dec 2004.

[4] Andrews, G.R., *Foundations of Multithreaded, Parallel, and Distributed Programming*, Pearson, 1999.

[5] Batty, M., Owens, S., Sarkar, S., Sewell, P., Weber, T., “Mathematizing C++ Concurrency”, *Proceedings of the 38th annual Symposium on Principles of programming languages (ACM SIGPLAN-SIGACT)*, pages 55–66, Austin, TX, USA, Jan 2011.

[6] Bencina, R., “Interfacing real-time audio and file I/O”, *Proc. of the Australasian Computer Music Conference (ACMC)*, pages 21–28, Melbourne, July 2014.

[7] Blechmann, T., “Supernova – A scalable parallel audio synthesis server for SuperCollider”, *Proc. International Computer Music Conference (ICMC)*, Huddersfield, UK, August 2011.

[8] Boehm, H.J., “Threads Cannot Be Implemented As a Library”, *Proc of the Conference on Programming Language Design and Implementation (ACM SIGPLAN)*, volume 40, pages 261–268, Chicago, IL, USA, June 2005.

[9] Boehm, H.J., “Position Paper: Nondeterminism is unavoidable, but data races are pure evil”, *Proc of the ACM workshop on Relaxing synchronization for multicore and manycore scalability*, pages 9–14, Tucson, AZ, USA, Oct 2012.

[10] Boehm, H.J., Adve, S.V., “Foundations of the C++ Concurrency Memory Model”, *Proc. of the Conference on Programming Language Design and Implementation*
mentation (ACM SIGPLAN), volume 43, pages 68–78, Tucson, AZ, USA, June 2008.

[11] Boehm, H.J., Adve, S.V., “You Don’t Know Jack about Shared Variables or Memory Models”, *ACM Queue*, volume 9(12), Dec 2011.

[12] Boulanger, R., Lazzarini, V., *The Audio Programming Book*, MIT Press, 2011.

[13] Cameron, E., *Parallelizing the ALSA modular audio synthesizer*, Master's thesis, Concordia University, Montreal, Canada, 2015.

[14] Carpentier, T., “ToscA : An OSC Communication Plugin for Object-Oriented Spatialization Authoring”, *Proc. of the 41st International Computer Music Conference*, pages 368 – 371, Denton, TX, USA, Sept. 2015.

[15] Carpentier, T., “Panoramix : 3D mixing and post-production workstation”, *Proc. 42nd International Computer Music Conference (ICMC)*, pages 122 – 127, Utrecht, Netherlands, Sept 2016.

[16] Carpentier, T., Noisternig, M., Warusfel, O., “Twenty Years of Ircam Spat : Looking Back, Looking Forward”, *Proc. of the 41st International Computer Music Conference*, pages 270 – 277, Denton, TX, USA, Sept. 2015.

[17] Cohen, N., Petrank, E., “Efficient Memory Management for Lock-Free Data Structures with Optimistic Access”, *Proc. of the 27th ACM Symposium on Parallelism in Algorithms and Architectures (SPAA)*, pages 254 – 263, Portland, OR, USA, June 2015.

[18] Colmenares, J.A., Saxton, I., Battenberg, E., Avizienis, R., Peters, N., Asanovic, K., Kubiatowicz, J.D., Wessel, D., “Real-time Musical Applications on an Experimental Operating System for Multi-Core Processors”, *Proc. International Computer Music Conference (ICMC)*, Huddersfield, UK, August 2011.

[19] Dannenberg, R.B., Bencina, R., “Design Patterns for Real-Time Computer Music Systems”, *Workshop on Real Time Systems Concepts for Computer Music (ICMC)*, Sept 2005.

[20] Dechev, D., Pirkelbauer, P., Stroustrup, B., “Understanding and Effectively Preventing the ABA Problem in Descriptor-based Lock-free Designs”, *Proc. 13th IEEE International Symposium on Object/Component/Service-Oriented Real-Time Distributed Computing (ISORC)*, pages 185–192, Seville, Spain, May 2010.

[21] Dechev, D., Stroustrup, B., “Scalable Nonblocking Concurrent Objects for Mission Critical Code”, *Proc. ACM Conference on Object-Oriented Programming, Systems, Languages & Applications (OOPSLA)*, Orlando, FL, USA, Oct 2009.

[22] Detlefs, D.L., Martin, P.A., Moir, M., Jr., G.L.S., “Lock-free reference counting”, *Distributed Computing*, volume 15(4), pages 255 – 271, 2002.

[23] Fober, D., Orlarey, Y., Letz, S., “Optimised Lock-Free FIFO Queue”, Technical report, GRAME - Computer Music Research Lab, Lyon, France, Jan 2001.

[24] Fober, D., Orlarey, Y., Letz, S., “Lock-Free Techniques for Concurrent Access to Shared Objects”, *Actes des Journées d'Informatique musicale (JIM)*, pages 143 – 150, Marseille, France, 2002.

[25] Fraser, K., “Practical lock-freedom”, Technical report, University of Cambridge Computer Laboratory, Feb 2004.

[26] Fraser, K., Harris, T., “Concurrent Programming Without Locks”, *ACM Transactions on Computer Systems (TOCS)*, volume 25(2), May 2007.

[27] Gao, H., Groote, J., Hesselink, W., “Lock-free parallel and concurrent garbage collection by mark & sweep”, *Science of Computer Programming*, volume 64, pages 341 – 374, 2007.

[28] Geier, M., Carpentier, T., Noisternig, M., Warusfel, O., “Software tools for object-based audio production using the Audio Definition Model”, *Proc. of the 4th International Conference on Spatial Audio (ICSA)*, Graz, Austria, Sept 2017.

[29] Gidenstam, A., Papatriantafilou, M., Sundell, H., Tsigas, P., “Practical and Efficient Lock-Free Garbage Collection Based on Reference Counting”, Technical Report no. 2005-04, Chalmers University of Technology and Göteborg University, Göteborg, Sweden, 2005.

[30] Herlihy, M., Luchangco, V., Moir, M., “Obstruction-free synchronization : double-ended queues as an example”, *Proc. of the 23rd International Conference on Distributed Computing Systems*, pages 522 – 529, May 2003.

[31] Herlihy, M.P., “Impossibility and universality results for wait-free synchronization”, *Proc. of the seventh annual ACM Symposium on Principles of distributed computing (POD)*, pages 276 – 290, Toronto, Canada, August 1988.

[32] Herlihy, M.P., “A methodology for implementing highly concurrent data objects”, *ACM Transactions on Programming Languages and Systems (TOPLAS)*, volume 15(5), pages 745 – 770, Nov 1993.

[33] Lamport, L., “How to make a multi-processor computer that correctly executes multiprocess programs”, *IEEE Transactions on Computers*, volume C-28(9), pages 690 – 691, Sept 1979.

[34] Lamport, L., “The temporal logic of actions”, *ACM Transactions on Programming Languages and Systems (TOPLAS)*, volume 16(3), pages 872 – 923, May 1994.

[35] Letz, S., Fober, D., Orlarey, Y., “Jack audio server for multi-processor machines”, *Proc. of the International Computer Music Conference (ICMC)*, pages 1 – 4, Barcelona, Spain, Sept 2005.
[36] Manson, J., Pugh, W., Adve, S.V., "The Java Memory Model", *Proc of the 32nd Symposium on Principles of programming languages (ACM SIGPLAN-SIGACT)*, volume 40, pages 378–391, Long Beach, CA, USA, Jan 2005.

[37] McCartney, J., "Rethinking the Computer Music Language: SuperCollider", *Computer Music Journal*, volume 26(4), pages 61–68, Winter 2002.

[38] McCurry, M., "Static (LLVM) Object Analysis Tool: Stoat", *Proc. of the Linux Audio Conference (LAC)*, Saint-Etienne, France, May 2017.

[39] Meneghin, M., Pasetto, D., Franke, H., Petrini, F., Xenidis, J., "Performance evaluation of inter-thread communication mechanisms on multicore/multithreaded architectures", *Proc. of the 21st international symposium on High-Performance Parallel and Distributed Computing (HPDC)*, 2012.

[40] Michael, M., "Hazard pointers: Safe memory reclamation for lock-free objects", *IEEE Transactions on Parallel and Distributed Systems*, volume 15(6), pages 491–504, 2004.

[41] Michael, M.M., "The Balancing Act of Choosing Nonblocking Features", *Communications of the ACM*, volume 56(9), pages 46–53, Sept 2013.

[42] Michael, M.M., Scott, M.L., "Simple, Fast, and Practical Non-Blocking and Blocking Concurrent Queue Algorithms", *Proc. of the fifteenth annual ACM symposium on Principles of distributed computing (PODC)*, pages 267–275, 1996.

[43] Michael, M.M., Scott, M.L., "Nonblocking Algorithms and Preemption-Safe Locking on Multiprogrammed Shared Memory Multiprocessors", *Journal of Parallel and Distributed Computing*, volume 51(1), pages 1–26, May 1998.

[44] Ostrovsky, I., "The C# Memory Model in Theory and Practice", Dec 2012.

[45] Otenko, O., "System and method for efficient concurrent queue implementation – US Patent 8,607,249 B2", Technical report, Oracle International Corporation, Dec 2013.

[46] Pacheco, P., *An Introduction to Parallel Programming*, Morgan Kaufmann, 2011.

[47] Pirkle, W., *Designing Audio Effect Plug-Ins in C++: With Digital Audio Signal Processing Theory*, Focal Press, 2012.

[48] Puckette, M., "The Patcher", *Proc. International Computer Music Conference (ICMC)*, pages 420–429, San Francisco, CA, USA, 1988.

[49] Puckette, M., "Pure Data", *Proc. of the International Computer Music Conference (ICMC)*, pages 224–227, Thessaloniki, Greece, 1997.

[50] Raynal, M., *Concurrent Programming: Algorithms, Principles, and Foundations*, Springer, 2013.

[51] Robinson, M., *Getting Started with JUCE*, Packt Publishing, 2013.

[52] Rushworth, T.B., Telfer, A.R., "Multi-reader, multi-writer lock-free ring buffer", Technical report, Inetco Systems Limited, 2012.

[53] Schmeder, A., Freed, A., Wessel, D., "Best Practices for Open Sound Control", *Proc. of the Linux Audio Conference (LAC)*, Utrecht, Netherlands, May 2010.

[54] Schnell, N., Roebel, A., Schwarz, D., Peeters, G., Borghesi, R., "MuBu and friends-assembling tools for content based real-time interactive audio processing in Max/MSP", *Proc. International Computer Music Conference (ICMC)*, pages 423–426, Montreal, Canada, Aug 2009.

[55] Shelton, R.J., *A Lock-Free Environment for Computer Music: Concurrent Components for Computer Supported Cooperative Work*, Ph.D. thesis, University of Melbourne, 2011.

[56] Valois, J.D., "Lock-Free Linked Lists Using Compare-and Swap", *Proc. of the Fourteenth Annual ACM Symposium on Principles of Distributed Computing*, pages 214–222, 1995.

[57] Williams, A., *C++ Concurrency in Action Practical Multithreading*, Manning Publications, 2010.
# UNE NOUVELLE IMPLÉMENTATION DU SPATialisATEUR DANS MAX

Thibaut Carpentier

CNRS - Ircam - Sorbonne Université - Ministère de la Culture
Sciences et Technologies de la Musique et du Son (UMR 9912 STMS)
1, place Igor Stravinsky, 75004 Paris
thibaut.carpentier@ircam.fr

## RÉSUMÉ

Le Spatialisateur (spat~) est une bibliothèque d'outils dédiés à la spatialisation sonore, la réverbération artificielle, et la diffusion multicanale. Cet article présente une révision majeure de l'environnement (version 5) et son intégration dans Max.

## 1. INTRODUCTION

Le Spatialisateur [20, 22], communément appelé spat~, est un outil temps réel dédié au traitement de spatialisation sonore, à la réverbération artificielle, et à la diffusion multicanale. Il est développé à l'Ircam depuis le début des années 1990, et s'incarne principalement dans l'environnement Max [27]. Il se présente sous la forme d'une bibliothèque de processeurs articulés autour d'un moteur de réverbération à réseau de retards rebouclés [21] et de modules de panoramique. Ces processeurs sont pilotés par le biais d'une interface de haut-niveau qui permet de moduler la qualité acoustique de l'effet de salle synthétisé selon plusieurs attributs perceptivement pertinents [23, 24].

Visant des domaines d'application variés (concerts, mixage, post-production, réalité virtuelle, installations interactives, etc.), le logiciel est développé selon des méthodologies agiles [26] et il agrège en continu les principaux résultats de recherche de l'équipe Espaces Acoustiques et Cognitifs (anciennement Acoustique des Salles). L'outil a donc connu au fil des ans diverses évolutions, et la dernière révision majeure, spat~ 4, fut diffusée en 2009 et présentée dans [13, 4].

Cet article présente spat~ 5, une nouvelle mouture de l'environnement. Comme tout logiciel exploité au long cours, son cycle de développement s'oriente selon différentes directions : l'amélioration des fonctionnalités existantes (incluant maintenance corrective, adaptative et évolutив), l'ajout de nouvelles fonctionnalités, et le réusinage (refactoring) de l'infrastructure logicielle (afin d'assainir l'architecture et assurer sa pérennité). Nous présentons dans les sections 2 et 3 une profonde refonte architecturale, et des nouvelles fonctionnalités sont décrites dans la section 4.

Le modèle interne du spat~ se fonde historiquement sur une représentation orientée objet de l'effet de salle, constitué de quatre sections temporelles filtrées

et pannées indépendamment : son direct, réflexions précoces, réflexions tardives et queue de réverbération (voir par exemple la section 3 dans [4]). Cette architecture est perpétuée dans spat~ 5; en revanche, l'une des principales évolutions introduites dans cette nouvelle version concerne l'interfaçage de l'outil avec son (ou ses) environnement(s) hôte(s).

## 2. INTERFAÇAGE OSC

### 2.1. Motivations

Sorti en 2008, Max 5 introduisit pour la première fois la notion “d’attributs”. Par analogie avec les paradigmes de programmation orientée objet, on peut dire que chaque objet (external) dans Max est une instance de classe, et les attributs sont des variables membres de cette classe. Grâce aux fonctionnalités offertes dans l’API, il est possible d’exposer publiquement (i.e. à l’utilisateur du patch Max) les attributs et les manipuler via des accesseurs (getter) et museurs (setter). spat~ 4 utilise intensivement ce concept, puisque les paramètres utilisateur des modules du spat~ sont exposés majoritairement via leurs attributs. Ce choix de conception présente des avantages et, rétrospectivement, un certain nombre d’inconvénients. Les avantages sont clairs : le mécanisme d’attributs est très bien intégré dans Max et il est aisé de manipuler (attrui, getattr, inspector) ou sauvegarder (pattr, pattrstorage) les attributs d’objets; leur syntaxe est explicite (par opposition aux “arguments”), et leur mise en œuvre (pour le DEVELOPEUR) via l’API est aisée. Dans le contexte spécifique du spat~, il peut cependant s’avérer que les attributs fussent inadéquats pour plusieurs raisons :

- Les objets spat~, multicaunaux par essence, présentent généralement un grand nombre d'attributs (souvent proportionnel au nombre de canaux). Dans les menus contextuels de Max, ceci peut conduire à une navigation mal commode et lente. En outre, les attributs dans Max sont stockés sous forme de listes (array) ce qui s'avère assez inapproprié aux paramètres du spat~; une structure hiérarchique serait plus pertinente pour refléter l'arborescence des données <fnref n="1" />. En outre, stocker

<footnote n="1">1. Il est en théorie possible de créer des attributs d'attributs, et donc d'élaborer une forme d'arborescence; toutefois cela ne résout pas vraiment la question et ne va pas dans le sens de la simplicité.</footnote>
les attributs sous forme de listes peut nuire aux performances : par exemple *pattr* (mécanisme de sauvegarde des attributs) aura besoin de copier l'ensemble de la liste dès lors qu'un des éléments constitutif est modifié.

— Les objets `spat~` sont souvent polymorphiques : le nombre et la nature des paramètres exposés peuvent évoluer au cours du temps. Un exemple est l'objet `spat.pan~` qui présente des propriétés différentes selon qu'il opère en stéréo, binaural, Ambisonics, etc. Les attributs de l'API Max sont mal adaptés pour refléter ce polymorphisme <fnref n="2" />.

— Les attributs sont spécifiques à l'API de Max ; or la bibliothèque logicielle `spat~` est bindée vers de nombreux autres environnements (Matlab <fnref n="3" />, Spat Revolution <fnref n="4" />, plugins Ircam Tools <fnref n="5" />, Open Music [17], Pure Data <fnref n="6" />, etc.). Chaque nouveau binding requiert une couche de code de liaison (*glue code*) pour s'interfacer avec l'environnement hôte. Côté développeur, il est important de minimiser l'effort induit par ce *glue code* ; côté utilisateur, il est pertinent d'avoir un interfaçage similaire (e.g. même syntaxe) dans les différents hôtes.

Ces considérations nous ont amené à “changer notre fusil d’épaule” et adopter dans `spat~` 5 un interfaçage selon le protocole et la syntaxe Open Sound Control (OSC [31]). Les attributs sont désormais abandonnés.

Le choix de l'OSC s'est imposé de façon somme toute évidente : ce protocole est largement répandu et maîtrisé dans la communauté audio, il encourage et facilite la communication (e.g. via UDP/IP) inter-applications ou avec des interfaces de contrôle compatibles ; son implémentation est très simple (de nombreuses bibliothèques sont par ailleurs disponibles dans plusieurs langages). L'espace d'adressage hiérarchique se prête bien à la plupart des applications d'informatique musicale et notamment du `spat~`. Outre les messages conventionnels, le protocole permet également d'encapsuler plusieurs messages au sein d'un *bundle*, simplifiant la transmission synchrone de grandes quantités d'événements. Enfin, il offre un puissant mécanisme de distribution (*dispatching*) de messages grâce à sa sémantique de pattern matching.

## 2.2. Intégration dans Max

### 2.2.1. Syntaxe

Les *externals* utilisent donc une syntaxe OSC. À l'interface avec Max, les messages sont convertis de/vers le format natif de Max, les *atoms*. Cette conversion est triviale puisque le typage des arguments OSC est proche de celui des *atoms* (int, float, symbole, etc.). Les bundles OSC sont quant à eux transmis sous forme de *FullPacket* vé-

hiculant uniquement un pointeur vers la mémoire du *bundle* (à l'instar des *dict* de Max). Ceci peut donc permettre de transmettre d'importantes quantités de données de façon efficace (le scheduler de Max n'est sollicité qu'une fois par bundle, et non pour chaque message).

La syntaxe adoptée s'inspire du style REST [29] (Representational State Transfer), et s'avère très proche de la syntaxe de `spat~` 4 (en y ajoutant le séparateur `/`). Par exemple pour spécifier la position cartésienne d'une source dans `spat~` 5, on pourra utiliser le message :

/source/1/xy [float][float]

Les *externals* supportent les sémantiques habituelles de *pattern matching*, favorisant le *grouping* d'éléments :

/source/*-/mute [boolean]

/source/[2-5]/mute [boolean]

/source/{3, 6, 7}/mute [boolean]

Le routage et la distribution de *patterns* d'adresses OSC dans Max peut nécessiter la manipulation d'expressions régulières (*regexp*) ce qui est généralement mal com mode et peu performant ; aussi avons-nous développé une bibliothèque d'outils utilitaires (environ 25 *externals*) pour simplifier les opérations usuelles (voir Figure 1).

D'un point de vue du développement logiciel, les adresses OSC les plus couramment utilisées dans `spat~` sont stockées (lors de la compilation) dans une table de hash. Ceci évite toute manipulation (coûteuse) de *strings* lors de l'exécution et garantit ainsi une résolution dynamique aussi performante que les tables de symboles traditionnelles de Max.

<figure><img src="image_29.png" /><figcaption>**Figure 1.** Exemple de manipulations OSC usuelles. Les *patchcords* représentées en bleu véhiculent des bundles OSC (*FullPacket*).</figcaption></figure>

<footnote n="2">2. Certes, il existe des attributs d'objet – par opposition aux attributs de classe – qui peuvent être ajoutés/supprimés dynamiquement, mais cela contrevient également à la simplicité de mise en œuvre.</footnote>

<footnote n="3">3. Non diffusé publiquement au moment de la rédaction de cet article.</footnote>

<footnote n="4">4. www.spatrevolution.com</footnote>

<footnote n="5">5. www.ircamtools.com</footnote>

<footnote n="6">6. Non diffusé publiquement au moment de la rédaction de cet article.</footnote>
### 2.2.2. Inter-opérabilité et compatibilité

Un avantage potentiel de l'interfaçage OSC est que les utilisateurs peuvent également bénéficier des bibliothèques et outils OSC existants, notamment :

- le package *odot* [16] qui offre un langage de manipulation d'expressions (parseur et interpréteur) opérant sur des bundles OSC. Typiquement, *odot* pourra être utilisé pour la génération et la transformation algorithmiques de données de spatialisation (trajectoires).

- ToscA [6, 5], un plugin insérable dans les stations de travail et qui permet la transmission via OSC de données d'automation. Bien que générique, ce plugin a, dès sa conception, été pensé pour un usage couplé avec spat~.

En revanche, il convient de noter que la syntaxe OSC ne préserve pas la compatibilité descendante avec spat~4. Cet aspect n'est évidemment pas négligeable puisque plusieurs centaines d'œuvres s'appuient sur spat~4. Il n'existe à l'heure actuelle pas de mécanisme de "portage automatique" de spat~4 vers spat~5; toutefois nous estimons que, dans la majorité des cas, la transition pourra se faire de façon indolore (il s'agit essentiellement d'adaptation syntaxique mineure). Des exemples de portage de patchers canoniques sont également fournis dans la distribution.

Notons enfin que spat~4 et spat~5 peuvent cohabiter sans conflit (notamment pour permettre le portage progressif des pièces) puisque les *externals* spat~5 s'inscrivent dans un espace d'adressage (*namespace*) dédié (préfixe spat5.*).

## 3. AUTRES REFONTES DE L'ENVIRONNEMENT

La refonte de l'environnement engagée dans spat~5 ne se restreint pas uniquement à la syntaxe des objets, mais a une incidence à plusieurs niveaux : ergonomie générale, processeurs audio, et interfaces graphiques de contrôle.

### 3.1. Ergonomie

Comme signalé dans la section 2, les *externals* de spat~5 n'utilisent plus d'attributs. En conséquence, ils ne bénéficient plus de certaines fonctionnalités de Max telles que l'inspecteur ou les mécanismes de documentation automatique (*hints*). Pour pallier cela, des mécanismes "de substitution" ont été introduits : chaque objet dispose d'une fenêtre de *status* et de *help* (voir Figure 2). La fenêtre de *status* affiche l'état courant de tous les paramètres de l'objet ; à l'instar de l'inspecteur Max, il est possible de filtrer les requêtes, et sélectionner et copier des messages depuis cette fenêtre vers le patcher. La fenêtre *help* affiche une documentation textuelle de tous les messages OSC supportés. Des pages de référence sont également générées automatiquement et sont accessibles via le navigateur standard de documentation de Max.

<figure><img src="image_30.png" /><figcaption>**Figure 2.** Fenêtres de statut (à gauche) et de documentation (à droite) de l'objet spat5 viewer.</figcaption></figure>

Si l'on compare avec l'inspecteur d'attributs dans spat~4 (Figure 3), on peut comprendre certains bénéfices de la nouvelle architecture : la fenêtre de *status* propose une vision hiérarchique des paramètres qui est plus claire que les listes (*array*) de valeurs dans l'inspecteur ; en outre ceci permet une granularité beaucoup plus fine : par exemple l'attribut *sources editable* [boolean] de spat~4 (qui autorise ou non l'édition des sources) agit globalement sur l'ensemble des sources, tandis que spat~5 offre un contrôle indépendant pour chaque élément :

/source/i/editable [boolean]

L'actualisation de paramètres indépendants plutôt que de (longues) listes de données peut également améliorer les performances générales de l'outil.

<figure><img src="image_31.png" /><figcaption>**Figure 3.** Inspecteur du spat viewer dans spat~4.</figcaption></figure>

### 3.2. Ordonnancement et thread-safety

Par rapport aux attributs, l'encapsulation des événements dans des messages OSC simplifie considérablement le développement d'une queue *thread-safe* pour la synchronisation de données partagées entre les différents processus impliqués dans Max (*thread audio*, *thread message* de l'application, *thread* d'événements à haute priorité, etc.), épuisant ainsi l'hygiène du code et limitant significativement le risque de bogues : les événements entrants (messages ou bundles) sont empilés dans une queue FIFO puis traités au moment et dans le *thread* opportuns. L'accès à cette queue est garanti *thread-safe* et non-bloquant 7. A contra-

<footnote n="7">7. Ce mécanisme est décrit en détail dans [11].</footnote>
rio, seuls quelques objets dans `spat~4` étaient garantis `thread-safe`.

Pour les objets DSP, le comportement par défaut est que la queue est dépilée dans le *thread* audio, au début du *callback* (voir Figure 4). Ce comportement correspond peu ou prou au mécanisme dit *scheduler in audio interrupt* dans Max. Il contribue également à une meilleure sécurité (*thread-safety*) du code. Notons que tous les objets `spat~5` fonctionnent selon ce mécanisme, indépendamment des réglages *overdrive* ou *interrupt* de l'environnement hôte.

<figure><img src="image_32.png" /><figcaption>**Figure 4.** Ordonnancement des événements selon un mécanisme *scheduler in audio interrupt*.</figcaption></figure>

### 3.3. Interfaces de contrôle

Le package `spat~` contient une vingtaine d'externals d'interfaces graphiques de contrôle (voir par exemple Figure 5). Ces interfaces ont été "rafraîchies" pour une meilleure lisibilité et ergonomie, et de nombreuses options de personnalisation y ont été ajoutées. Ont également été ajoutés un grand nombre de raccourcis clavier pour un accès immédiat aux fonctionnalités usuelles; ces raccourcis sont par ailleurs configurables par l'utilisateur (Figure 6).

<figure><img src="image_33.png" /><figcaption>**Figure 5.** Interface du `spat5 oper` (opérateur perceptif de contrôle haut-niveau du `spat~`). Facteurs perceptifs de contrôle de l'effet de salle (gauche); filtrages (centre); représentation schématique 2D de la scène sonore (droite).</figcaption></figure>

<figure><img src="image_34.png" /><figcaption>**Figure 6.** Fenêtre de personnalisation des raccourcis clavier pour `spat5 oper`.</figcaption></figure>

Les externals portant ces interfaces graphiques bénéficient par ailleurs du mécanisme de queue *thread-safe* présenté au paragraphe précédent. Ceci a permis de les implémenter sans recourir à *deferlow* (renvoi dans le *thread* à basse priorité), offrant ainsi une réactivité bien supérieure à `spat~4` (dans lequel la plupart des GUIs utilisaient *deferlow*, ce qui conduit, en cas de charge importante, à un empilement des messages et un ralentissement global de l'interface et du scheduler).

L'état global (i.e. l'ensemble des paramètres) d'un *external* GUI est représenté sous forme d'un bundle OSC (voir par exemple Figure 2). Ce bundle peut facilement être exporté sous forme de fichier texte (donc éditable), puis ré-importé. Ceci peut donc être utilisé comme mécanisme simple de sauvegarde/chargement de presets. Le bundle peut également être stocké directement dans le patcher (on dira qu'il est *embedded*) ou via les snapshots de Max (en activant le "Parameter Enable Mode"); dans ces cas, le bundle OSC est préalablement converti sous forme de *blob* binaire (non éditable) sauvé avec le patcher.

### 3.4. Aspects de génie logiciel

Le code C++ des bibliothèques sous-jacentes a été considérablement modernisé, en utilisant les nouveaux idiomes de programmation offerts par les standards C++11 à C++17⁸: expressions constantes à la compilation (constexpr), déduction de typage automatique (auto), lambda fonctions, range-based for loop, etc. Ces nouvelles fonctionnalités permettent d'assainir l'hygiène du code, de diminuer les risques de bogues, et incidemment de réduire la taille du code source; par exemple, le *glue code* requis pour l'interfaçage avec l'API de Max (voir section 2) a pu

<footnote n="8">⁸. ISO International Standard ISO/IEC 14882:2017(E) – Programming Language C++</footnote>
être réduit de 80%.

Les algorithmes de traitement du signal, déjà vectori-
sés et optimisés grâce au *framework Accelerate* <fnref n="9" />,
ont encore été améliorés par l'utilisation des primitives
hautes performances Intel® Integrated Performance Pri-
mitives (IPP) <fnref n="10" />.

4. NOUVELLES FONCTIONNALITÉS

Cette section recense, sans souci d'exhaustivité, plu-
sieurs nouveautés marquantes du package spat~ 5.

4.1. Higher Order Ambisonics

Concernant les processeurs audio pour l'ana-
lyse/synthèse de scènes sonores spatialisées, de nom-
breuses améliorations et nouvelles fonctionnalités ont
été introduites dans la bibliothèque spat~ 5. Il serait
fastidieux d'en tenir une liste complète. Toutefois les re-
cherches des dernières années ont mis un accent particulier
sur la technique Higher Order Ambisonics (HOA [14]), et
nous énumérons ici quelques nouveautés afférentes :

- Les différents schémas de normalisation des composantes HOA en vigueur (FuMa, MaxN, SN3D, N3D, etc.) sont une source fréquente de confusion pour les utilisateurs, et ils nuisent à l'inter-opérabilité des outils. Un travail de formalisation [10] et de documentation a été mené afin de clarifier leur impact dans une chaîne de production Ambisonics, et de limiter les risques d'erreur.

- Plusieurs stratégies de décodage HOA sont offertes dans spat~ (voir [4]). Dans spat~ 5, nous y avons ajouté les décodeurs dits “all-rad” (All-Round Ambisonic Panning and Decoding [32]) et “constant-spread” (Constant Angular Spread Ambisonic Decoding [15]) qui s’appuient sur un décodage HOA régulier, sur une sphère virtuelle de type *t-design*, projeté via VBAP ou MDIP sur le dispositif de haut-parleurs physiques.

— Les différents décodeurs HOA disponibles (*sampling decoder*, *mode-matching* [14], *energy-preserving* [33], *all-rad* [32], *constant-spread* [15]) peuvent conduire à des champs sonores de puissances significativement différentes (jusqu’à plusieurs dizaines de dB d’écart selon les configurations). Ceci empêche toute étude/écoute comparative des différents décodages, aussi avons nous développé une technique de compensation énergétique qui permet d’aligner les décodeurs entre eux (ou sur une référence arbitraire). La méthode s’appuie sur une estimation de la puissance rendue en condition de champ diffus (voir par exemple la section 4.4 dans [33]).

— spat5.hoa.blur~ est un nouvel outil permettant de manipuler la “résolution spatiale” d’un champ en-codé HOA. Il permet de modifier continûment l’ordre

du flux HOA (simulant de la sorte des ordres frac-
tionnaires), tout en maintenant la puissance globale
constante [9]. Il peut être utilisé pour adapter l'ordre
de contenus existants, ou comme effet créatif (typique-
ment en faisant varier dynamiquement le facteur de
"flou").

— spat5.hoa.focus~ est un autre effet opérant dans le domaine HOA. Inspiré de [25], il permet de synthéti-ser des diagrammes de directivité virtuels qui “filtrent” un flux HOA. L’orientation et la sélectivité des dia-grammes virtuels peuvent être éditées via une interface graphique dédiée (Figure 7). L’outil trouve un inté-rêt aussi bien dans le domaine de la post-production (“zoom” dans une scène sonore enregistrée) que de la création.

<figure><img src="image_35.png" /><figcaption>Figure 7. Module spat5.hoa.focus pour la synthèse de directivités virtuelles dans le domaine HOA.</figcaption></figure>

4.2. Audio Definition Model

Ces dernières années ont vu un regain d'intérêt pour les
modèles orientés objet pour la production et la diffusion
de contenus multicaux. Plusieurs formats d'échange ont
ainsi été proposés ; en particulier Audio Definition Mo-
del (ADM [19]) est un standard ouvert publié par ITU et
EBU <fnref n="11" /> pour la description de médias orientés objet et en-
capsulés dans un conteneur Broadcast Wave Format (BWF).
ADM spécifie un ensemble de métadonnées (notamment
positions et gains d'objets sonores évoluant dans le temps
et l'espace) codées selon une représentation XML. spat~
est l'une des premières bibliothèques proposant une so-
lution complète pour la création et le rendu de fichiers
BWF-ADM : spat5.adm.record~ permet l'écriture
de fichier BWF embarquant des métadonnées de spatialisa-
tion, et spat5.adm rendered Assure le rendu temps
réel de médias ADM sur différents dispositifs de restitution
(casque ou haut-parleurs); d'autres externals permettent

<footnote n="9">9. http://developer.apple.com</footnote>

<footnote n="10">10. http://software.intel.com</footnote>

<footnote n="11">11. International Telecommunication Union et European Broadcasting Union</footnote>
également de gérer l'interactivité des objets. Ces outils sont décrits plus en détail dans [18]. Notons toutefois que seul un sous-ensemble des spécifications ADM est actuellement supporté (couvrant cependant la plupart des cas d'usage classiques), et qu'une intégration plus étroite du format au sein de l'architecture du spat~ reste à faire (e.g. import/export de fichiers ADM directement depuis les processeurs tels que spat5.spat~). Ceci fait l'objet de travaux en cours.

### 4.3. Panoramix

panoramix est une station de travail pour la spatialisation et la réverbération en contexte de mixage et de post-production 3D. L'outil a été présenté dans plusieurs publications [7, 12, 8]. Il s'appuie de façon sous-jacente sur la bibliothèque C++ spat~, et son développement rapide n'a été possible que grâce à la profonde refonte logicielle exposée dans les sections 2 et 3. panoramix peut être vu comme une déclinaison spécialisée des outils spat5 oper et spat5.spat~ (qui sont plus génériques), dotée d'un frontend ad-hoc pour les situations de mixage multicanal. Bien que distribué séparément sous forme d'exécutable standalone, panoramix est également inclus dans le package spat~ 5, sous forme d'externals pouvant être utilisés pour des applications moins conventionnelles. Le lecteur est invité à consulter [7, 12, 8] pour plus de détails sur la conception et l'exploitation de panoramix.

### 4.4. Quaternions

Avec la démocratisation des équipements de réalité virtuelle, spat~ est de plus en plus employé pour assurer le rendu audio d'applications multimédia immersives, typiquement présentées selon une modalité binaurale. Ces environnements requièrent des manipulations géométriques 3D plus ou moins complexes. En particulier, la gestion de l'orientation des entités (auditeur ou objets sonores/visuels) dans l'espace est une source fréquente de confusion pour les utilisateurs, en raison des nombreuses conventions en vigueur (et incompatibles entre elles). Pour pallier cette difficulté, nous avons développé une bibliothèque d'externals permettant de manipuler quaternions, angles d'Euler et matrices de rotation 3D. Ils assurent notamment les conversions entre ces différentes représentations. Les processeurs audio de spat~ (par exemple spat5.binaural~) peuvent être pilotés indifféremment par des quaternions ou des angles d'Euler, simplifiant l'inter-opérabilité avec les différents SDK de réalité virtuelle.

### 4.5. Time Code

spat~ est fréquemment utilisé dans des contextes audiovisuels; lors du couplage avec des environnements video/graphiques, il est nécessaire de mettre en place des mécanismes de synchronisation entre les flux audio et vi-

déo. Une des techniques les plus populaires consiste alors à utiliser un *Linear Timecode* (LTC [28]) encodant des *frames* SMPTE et transmis sous forme de signal audio longitudinal. Ce standard n'est malheureusement pas supporté nativement par Max, aussi avons nous développé des outils permettant la réception (spat5.ltc.decode~), et la génération (spat5.ltc.encode~) de tels *timecodes*. En outre, l'objet spat5.ltc.trigger~ peut être utilisé comme gestionnaire d'événements (*cue manager*) déclenchant des actions en fonction du timecode courant; la granularité temporelle est certes assez faible (typiquement 30 fps ≈ 33 millisecondes), mais suffisante pour la plupart des applications en spatialisation sonore.

### 4.6. Intégration dans Open Music

La spatialisation sonore n'est pas seulement déployée lors de l'exécution des œuvres, mais elle doit également être intégrée au processus compositionnel. Aussi est-il pertinent de proposer des bindings de spat~ dans des environnements de composition assistée par ordinateur. Comme discuté au paragraphe 2, une des motivations pour l'adoption d'une interface OSC est de s'abstraire des spécificités de Max, et d'accélérer l'intégration de spat~ dans différentes applications hôtes, en offrant une syntaxe unifiée. Dans le cadre du projet EFFICACe [3], nous avons tiré profit de cette nouvelle architecture OSC pour insérer plusieurs modules de la bibliothèque spat~ dans les environnements Open Music et o7. Les premiers fruits de cette intégration ont été présentés dans [17, 1, 2], et ils ouvrent la porte à de nouveaux champs d'expérimentation faisant l'objet de travaux et de résidences artistiques en cours.

## 5. CONCLUSION

Cet article présente spat~ 5, une nouvelle mouture de l'environnement de spatialisation et réverbération spat~ implémentée dans Max. La bibliothèque contient près de 200 externals couvrant toutes les activités liées à la diffusion multicanale. Par rapport aux versions précédentes, cette implémentation propose un nouvel interfaçage avec l'application hôte, basé sur le protocole OSC. Hormis les aspects syntaxiques, cette nouvelle structure OSC s'accompagne d'une refonte plus profonde de la bibliothèque, visant à améliorer son ergonomie, sa stabilité, ses performances et son inter-opérabilité. La distribution spat~ 5 inclut par ailleurs nombre de nouveaux objets (contrôle, DSP, et GUI) qui élargissent le spectre des traitements réalisables. Signalons encore que plusieurs "sous-ensembles" du package (e.g. les modules OSC, LTC, quaternions, etc.) sont indépendants du modèle du spatialisateur à proprement parler, et peuvent être d'un intérêt plus général pour la communauté d'informatique musicale.

Impulsé tant par les innovations technologiques que par les productions artistiques, spat~ demeure un outil en constante évolution; les principaux travaux en cours et les
perspectives à court terme concernant :

— l'intégration de formats orientés objet, tel que discuté au paragraphe 4.2;

— la compatibilité avec les protocoles d'interrogation du namespace OSC; de nombreuses propositions ont été faites à cet égard (OSC Query[30], Minuit <fnref n="12" />, lib-mapper <fnref n="13" />, OSNIP <fnref n="14" />, OSCQueryProposal <fnref n="15" />), et il n'existe pour l'heure pas de consensus dans la communauté. L'intégration de l'un ou l'autre de ces protocoles dans spat~5 est en cours d'évaluation;

— l'extension des formalismes d'analyse/synthèse de scènes sonores spatiales;

— et l'exploitation dans des environnements de composition assistée par ordinateur.

## 6. REFERENCES

[1] Agger, S., Bresson, J., Carpentier, T., "Landschaften – Visualization, Control and Processing of Sounds in 3D Spaces", *Proc. of the International Computer Music Conference (ICMC)*, Shanghai, China, Oct 2017.

[2] Bresson, J., Bouche, D., Carpentier, T., Schwarz, D., Garcia, J., "Next-generation Computer-aided Composition Environment: A New Implementation of Open-Music", *Proc. of the International Computer Music Conference (ICMC)*, Shanghai, China, Oct 2017.

[3] Bresson, J., Bouche, D., Garcia, J., Carpentier, T., Jacquemard, F., MacCallum, J., Schwarz, D., "Projet EF-FICACe: Développements et perspectives en composition assistée par ordinateur", *Journées d'Informatique Musicale (JIM)*, Montreal, Canada, May 2015.

[4] Carpentier, T., "Récents développements du Spatialisateur", *Journées d'Informatique Musicale (JIM)*, Montreal, Canada, May 2015.

[5] Carpentier, T., "ToscA: Un plugin de communication OSC pour le mixage spatialisé orienté objet", *Journées d'Informatique Musicale (JIM)*, Montreal, Canada, May 2015.

[6] Carpentier, T., "ToscA: An OSC Communication Plugin for Object-Oriented Spatialization Authoring", *Proc. of the 41st International Computer Music Conference (ICMC)*, pages 368–371, Denton, TX, USA, Sept. 2015.

[7] Carpentier, T., "Panoramix: 3D mixing and post-production workstation", *Proc. 42nd International Computer Music Conference (ICMC)*, pages 122–127, Utrecht, Netherlands, Sept 2016.

[8] Carpentier, T., "A versatile workstation for the diffusion, mixing, and post-production of spatial audio", *Proc. of the Linux Audio Conference (LAC)*, Saint-Etienne, France, May 2017.

[9] Carpentier, T., "Ambisonic spatial blur", *Proc of the 142nd Convention of the Audio Engineering Society (AES)*, Berlin, Germany, May 2017.

[10] Carpentier, T., "Normalization schemes in Ambisonic: does it matter?", *Proc of the 142nd Convention of the Audio Engineering Society (AES)*, Berlin, Germany, May 2017.

[11] Carpentier, T., "Synchronisation de données interprocessus dans les applications audio temps réel: qu'est-ce qui débloque?", *Journées d'Informatique Musicale*, Amiens, France, May 2018.

[12] Carpentier, T., Cornuau, C., "panoramix: station de mixage et post-production 3D", *Journées d'Informatique Musicale*, pages 162–169, Albi, France, April 2016.

[13] Carpentier, T., Noisternig, M., Warusfel, O., "Twenty Years of Ircam Spat: Looking Back, Looking Forward", *Proc. of the 41st International Computer Music Conference (ICMC)*, pages 270–277, Denton, TX, USA, Sept. 2015.

[14] Daniel, J., "Représentation de champs acoustiques, application à la transmission et à la reproduction de scènes sonores complexes dans un contexte multimédia", Ph.D. thesis, Université de Paris VI, 2001.

[15] Epain, N., Jin, C., Zotter, F., "Ambisonic Decoding With Constant Angular Spread", *Acta Acustica united with Acustica*, volume 100, pages 928–936, 2014.

[16] Freed, A., MacCallum, J., Schmeder, A., "Dynamic, instance-based, object-oriented programming in Max/MSP using Open Sound Control message delegation", *Proc. of the 37th International Computer Music Conference (ICMC)*, pages 491–498, Huddersfield, Aug. 2011.

[17] Garcia, J., Carpentier, T., Bresson, J., "Interactive-compositional authoring of sound spatialization", *Journal of New Music Research – Special Issue on Interactive Composition*, volume 46(1), pages 74–86, 2017.

[18] Geier, M., Carpentier, T., Noisternig, M., Warusfel, O., "Software tools for object-based audio production using the Audio Definition Model", *Proc. of the 4th International Conference on Spatial Audio (ICSA)*, Graz, Austria, Sept 2017.

[19] ITU, "ITU-R BS.2076 (ADM Audio Definition Model)", Technical report, www.itu.int/rec/R-REC-BS.2076, 2015.

[20] Jot, J.M., "Real-time spatial processing of sounds for music, multimedia and interactive human-computer interfaces", *ACM Multimedia Systems Journal (Special issue on Audio and Multimedia)*, volume 7(1), pages 55–69, 1999.

[21] Jot, J.M., Chaigne, A., "Digital Delay Networks for Designing Artificial Reverberators", *Proc. of the 90th Convention of the Audio Engineering Society (AES)*, Paris, France, Feb 1991.

<footnote n="12">12. https://github.com/Minuit</footnote>

<footnote n="13">13. http://libmapper.github.io</footnote>

<footnote n="14">14. https://github.com/jamoma/osnip/wiki</footnote>

<footnote n="15">15. https://github.com/mrRay/OSCQueryProposal</footnote>
[22] Jot, J.M., Warusfel, O., “A Real-Time Spatial Sound Processor for Music and Virtual Reality Applications”, *Proc. of the International Computer Music Conference (ICMC)*, pages 294 – 295, Banff, Canada, 1995.

[23] Jullien, J.P., “Structured model for the representation and the control of room acoustic quality”, *Proc. of the 15th International Congress on Acoustics (ICA)*, pages 517 – 520, Trondheim, Norway, June 1995.

[24] Kahle, E., Jullien, J.P., “Subjective Listening Tests in Concert Halls: Methodology and Results”, *Proc. of the 15th International Congress on Acoustics (ICA)*, pages 521 – 524, Trondheim, Norway, June 1995.

[25] Kronlachner, M., Zotter, F., “Spatial transformations for the enhancement of Ambisonic recordings”, 2nd *International Conference on Spatial Audio (ICSA)*, Erlangen, Germany, February 2014.

[26] Larman, C., *Agile and Iterative Development: A Manager's Guide*, Addison Wesley, 2003.

[27] Puckette, M., “The Patcher”, *Proc. of the International Computer Music Conference (ICMC)*, pages 420 – 429, San Francisco, CA, USA, 1988.

[28] Ratcliff, J., *Timecode: A user's guide, 3rd Edition*, Focal Press, 1999.

[29] Schmeder, A., Freed, A., Wessel, D., “Best Practices for Open Sound Control”, *Proc. of the Linux Audio Conference (LAC)*, Utrecht, Netherlands, May 2010.

[30] Schmeder, A.W., Wright, M., “A Query System for Open Sound Control (Draft Proposal)”, Technical report, Center for New Music and Audio Technology (CNMAT), UC Berkeley, 2004.

[31] Wright, M., “Open Sound Control: an enabling technology for musical networking”, *Organised Sound*, volume 10(3), pages 193 – 200, Dec 2005.

[32] Zotter, F., Frank, M., “All-Round Ambisonic Panning and Decoding”, *Journal of the Audio Engineering Society*, volume 60(10), pages 807 – 820, 2012.

[33] Zotter, F., Pomberger, H., Noisternig, M., “Energy-Preserving Ambisonic Decoding”, *Acta Acustica united with Acustica*, volume 98, pages 37 – 47, 2012.