IRCAM - Centre PompidouServeur © IRCAM - CENTRE POMPIDOU 1996-2005.
Tous droits réservés pour tous pays. All rights reserved.

FORMES - composition et ordonnancement de processus

Xavier Rodet, Pierre Cointe
Texte français : Esther Starkier

Rapport Ircam 36/85, 1985
Copyright © Ircam - Centre Georges-Pompidou 1985


Trouver une forme qui accommode le gâchis,
telle est actuellement la tâche de l'artiste...

Samuel BECKETT

1. Introduction: Les langages objets

On admet, depuis quelques temps déjà, que l'élaboration et l'utilisation des systèmes complexes ont été gênées par le caractère inadéquat des langages classiques de programmation (Winograd 1979). La Synthèse et Composition Musicale (SCM) par ordinateur en sont un bon exemple. Or, dans les années 1970 une nouvelle génération de langages interactifs est apparue (Cointe 1983) qui a permis d'établir les concepts d'objet-acteur et de transmission de message comme structure de contrôle des calculs. On notera que la programmation orientée objets répond à presque toutes les exigences de la SCM.

Un environnement de programmation orienté objets, appelé FORMES, a été conçu et implémenté à l'IRCAM en vue de résoudre les problèmes de complexité de l'informatique musicale. Il s'agit d'un nouveau langage doté de caractéristiques originales, orienté vers la composition et l'ordonnancement temporel d'objets. Il va de soi que les «processus» FORMES ne correspondent pas forcément aux processus musicaux que perçoit l'auditeur.

FORMES est un environnement interactif destiné initialement à la SCM. Or, par son architecture originale, sa souplesse et sa maniabilité il doit pouvoir trouver des applications dans un grand nombre de domaines comme l'animation graphique ou la synthèse de la parole. Il permet aux compositeurs d'élaborer des partitions et des synthèses de sons. A titre d'exemple, CHREODE, oeuvre composée à l'aide de FORMES par Jean-Baptiste Barrière, s'est vu décerner le Prix du Festival International de Bourges en 1983 (Barrière 1983).

En quoi la SCM est-elle un terrain privilégié pour tester les techniques informatiques les plus avancées? C'est qu'en SCM, bien plus encore qu'en `Intelligence Artificielle', les programmes doivent être très souvent testés, modifiés et réécrits, et ce, rapidement et aisément afin d'assurer une continuité entre intention musicale et résultat final. La complexité habituellement mise en jeu par l'activité compositionnelle dépasse de beaucoup ce que nous pouvons coder aujourd'hui en quelques semaines - voire quelques mois - . De même, dans leurs recherches musicales, les informaticiens et les musiciens ne doivent être freinés par des programmes ésotériques ou «abscons». C'est pourquoi la possibilité d'établir une interaction «amicale» est indispensable. Enfin, le système doit être interactif et suffisamment rapide pour contrôler un synthétiseur en temps réel.

1.1 Objets et Transmission de message

Les concepts d'objets (acteurs) et de transmission de message ont été introduits en informatique par O. Dahl, A. Kay et C. Hewitt et développés pour la première fois sur les langages Simula, Smalltalk et Plasma (Birtwistle et O. Dahl 1973 ; Kay 1969; Hewitt 1976). Ces concepts sont une bonne réponse aux besoins de la SCM.

Avec Simula est apparu le concept d'organisation hiérarchique de classes: une classe décrit toutes les fonctions qui sont activées sur ses instances. Cette particularité répond aux critères 1,3 et 6 ci-dessus. On y trouve également la possibilité d'opérer des simulations par gestion de plusieurs processus.

Plasma (Planner) a introduit le concept d'acteur: chaque entité du langage représente un acteur qui communique avec d'autres acteurs par un mécanisme émetteur de messages utilisant une réception sélective de messages par `pattern matching' (Hewitt and Smith 1975; Hewitt 1976, Pomian 1980; Durieux 1981). Le système dérivé de Plasma et baptisé Act 1 a permis d'améliorer la transmission de messages en autorisant des propriétés d'héritage avec délégation, c'est-à-dire qu'un acteur, plutôt que de refuser un message, peut déléguer ce message à un autre acteur (Lieberman 1981). Grâce à Plasma et à Act 1 on a pu définir des structures de contrôle complexes telles que la recherche ascendante, le `coroutinage' ou l'activation simultanée d'un même acteur (Durieux 1981). Il s'agit là d'une importante contribution à la gestion de structures et de taches musicales complexes ( Lieberman 1982).

Le développement des systèmes Smalltalk (Kay and Goldberg 1976; Ingalls 1978; Goldberg and Robson 1983) et des machines de bureau comme la Xerox Star (Smith 1983) avec écran mode point et `souris' a suscité la création d'outils logiciels interactifs utilisant des procédés graphiques (Cointe 1982a, 1982b). Avec sa syntaxe et sa structure, Smalltalk répond particulièrement bien aux critères 4-6.

La mise en oeuvre des fenêtres multiples, de l'édition graphique et des systèmes de menus est grandement facilitée par l'emploi d'objets, de classes et du concept d'héritage. Plusieurs langages Lisp de la dernière génération comportent d'ailleurs ce concept d'objets. Le Lisp Machine Flavor (Moon and Weinreb 1980; Cointe 1981, 1982a,1982b; Novak 1982; Rees 1982; Steels 1983) en témoigne bien.

1.2 Programme orientée objets et SCM

La puissance de la programmation orientée objets est désormais bien établie (Lieberman 1982): voyons néanmoins comment elle répond aux exigences de la SCM ? Du point de vue de l'utilisateur, les langages orientés objets offrent les caractéristiques suivantes:
Extensibilité: il est toujours possible d'étendre le champ sémantique en élaborant de nouvelles structures de contrôles. Cette propriété répond aux critères 1-3 décrits au paragraphe suivant.

Interactivité: Le dialogue avec la machine est essentiel pour assurer une bonne maniabilité et aux fins de recherches.

Modularité: Le concept d'héritage permet de définir une situation en termes de sous-situations élémentaires (propriété 6).

Adaptation aux représentations multiples: Grâce au concept d'objet on peut réaliser des implémentations et des représentations multiples d'un concept donné (propriétés 1-3). Ceci est important car notre compréhension d'un phénomène dépend de la manière dont il nous est présenté. Les représentations multiples répondent aux propriétés 4-6.

1.3 Exigences de la SCM

Nous avons développé FORMES après plusieurs années d'expérience et de production SCM - dont la création du système CHANT (Bennett 1981 ; Rodet and Bennet 1980 ; Rodet, Potard and Barrière 1984). En SCM, le musicien tente de traduire une certaine intention musicale; il essaie de la concrétiser à l'aide du programme SCM - en partant de modèles qui mettent en oeuvre ce que nous savons de la production et de la perception des sons - dans un cadre compositionnel donné. FORMES fournit donc ce cadre permettant de manipuler et d'intégrer ces modèles sous forme d'objets. Ils sont caractérisés par leur contribution à la synthèse et par leur comportement (temporel) indépendamment des détails d'implémentation.

Les propriétés souhaitables pour les modèles SCM sont toutes des objectifs de l'environnement FORMES:

  1. Généralité: Hormis application spécifique, un modèle ne doit pas illustrer un son ni une note particulière mais représenter un processus de manière aussi générale que possible (un modèle de crescendo ou d'attaque doit pouvoir s'appliquer à autant de sons différents que possible).
  2. Universalité: Un modèle ne doit pas dépendre d'une quelconque technique d'analyse; il doit se référer aux concepts universaux d'acoustique et de psycho-acoustique.
  3. Compatibilité: Les modèles doivent pouvoir s'appliquer à tout contexte. Quelle que soit leur combinaison, ils doivent coopérer harmonieusement et interagir dans l'univers créé par le compositeur.
  4. Simplicité du texte de programme: Les modèles seront bien moins complexes s' ils respectent certaines conventions et certains présupposés de la communication humaine (ex. valeurs par défaut).
  5. Maniabilité: Pour certaines interactions, des constructions proches du langage naturel facilitent l'apprentissage et l'utilisation du système. Dans les cas contraires, on emploiera un code symbolique simple et clair. L'invocation, la modification et l'intégration des modèles doivent être directes et aisées.
  6. Modularité et construction hiérarchique: Du fait de leur complexité, les modèles doivent être construits à partir de sous-modèles. Ainsi on pourra intégrer divers comportements spécifiques à un nouvel environnement plus général.

2. Lignes directrices de formes

En gardant à l'esprit les exigences de la SCM, on lira ci-dessous les lignes directrices pour le développement du système FORMES.

2.1 Accessibilité

De notre expérience d'enseignement et d'utilisation de FORMES il ressort que les utilisateurs souhaitent avoir accès à chacune des composantes du système pour pouvoir étudier, comprendre et modifier chaque élément de l'architecture globale. A ce souhait répondent deux caractéristiques: d'une part FORMES est un environnement interactif et, d'autre part, chacune de ses entités est un objet.

2.2 Transmission de Message

Chaque objet peut recevoir des messages liés à des fonctions (méthodes). Chaque méthode caractérise le comportement propre à un message donné. L'ensemble des méthodes peut être lu, écrit, augmenté ou réduit par l'utilisateur. Les utilisateurs peuvent observer dynamiquement des modifications dans une famille d'objets tout en ayant la possibilité de conserver ou de modifier les comportements par défaut. Ainsi, on dispose d'un système incrémental auto-documenté dont le dernier état est toujours accessible.

2.3 Processus

Pour représenter la complexité des modèles musicaux, nous avons affaire à des objets dépendants du temps que nous appelons processus construits à partir de sous-objets appelés descendance (fils).

2.4 Implémentation

FORMES a été élaboré en Vlisp, initialement sur un DEC PDP-10 puis sur un DEC VAX-11/780. Il tourne actuellement en Vlisp (Greussay 1982) et Le Lisp (Chailloux 1984). Lisp offre une grande souplesse de développement d'un tel prototype complexe. Mais on pourrait produire d'autres versions de FORMES pour d'autres machines et dans d'autres langages.

3. Principales caractéristiques

3.1 Les processus

Un processus FORMES est une entité nommée qui regroupe des règles (ou corps de procédure), un moniteur (sorte d'ordonnanceur temporel), un environnement (variables locales ou champs) et une descendance (fils). Un processus FORMES a des capacités typiques (comme un script en Plasma) telles que «sommeil», «éveil», «attente» et «synchronisation» à la demande. En règle générale, son comportement est déclenché par les messages qui lui sont transmis.

Le rôle de chaque processus est d'assurer le calcul d'une caractéristique musicale donnée, comme l'aspect du phrasé, le vibrato, l' intensité ou le timbre. Ce calcul se déroule dans un laps de temps précis (appelé span) qui va d'un temps de début (btime) à un temps de fin (etime) qui ne sont pas nécessairement explicites.

Les calculs sont effectués à l'aide de règles (cf. Synthèse par règles [Rodet 1977 ; Sundberg, Askenfelt and Fryden 1983]), dans l'environnement spécifique du processus. L'environnement est constitué de variables locales statiques qui conservent leur valeur sauf modification explicite. Un processus commence l'exécution de ses régies lorsqu'il est activé. Aussi la durée d'un processus correspond à l'intervalle de temps pendant lequel il est actif.

Pour la synthèse, plusieurs processus sont activés. Le rôle du moniteur consiste à maintenir l'activité des processus (exécution des règles) et à assurer l'ordonnancement exact et la collaboration de tous les processus (ex. le calcul de l'intensité générale, puis le calcul de l'intensité d'un motif, puis le calcul de l'enveloppe d'amplitude d'une note). Nous reviendrons plus en détail sur les moniteurs.

Du point du vue de la synthèse, le calcul alimente périodiquement les entrées ou les commandes du synthétiseur avec un nouvel ensemble de valeurs ou de commandes. Ainsi, lancer un processus revient à synthétiser un son et la valeur de la variable `time' dans FORMES (horloge logique] reste proche du temps physique. Cependant, lancer un processus peut provoquer d'autres résultats comme l'affichage sur écran, ou le chargement d'un patch.

Définition d'un processus.

Un processus se définit par l'instanciation d'un générateur original appelé process. On ne doit pas confondre cette définition avec l'activation du processus qui se produit à son temps de début. La définition ou création d'un processus comme instance d'un générateur peut intervenir à tout moment avant le temps de début. A titre d'exemple, I' instanciation du processus foo peut s'accomplir par l'expression:

(process new'foo)
Cette commande transmet un message new à process avec l'argument foo. Il est intéressant de noter que de nouveaux processus peuvent être définis à partir de foo, ou plus précisément dérivés de foo avec quelques modifications. Ainsi,
(foo new 'bar 
     env: '(partial1 440.0)
définit un nouveau processus appelé bar qui est identique à foo sauf en ce qui concerne la valeur de la variable partial 1 dans son environnement.

Figure 1 - La durée d'un processus ENFANT (fils) est comprise dans la durée du processus PARENT (père).

3.2 Structures séquentielles, parallèles et hiérarchiques

De même que tout système complexe, un système musical informatique se doit d'être modulaire. A cette fin, les processus peuvent être organisé en une structure qui traduise certains aspects de la structure musicale souhaitée en combinant des structures séquentielles, parallèles ou hiérarchiques. Un processus peut être construit à partir de sous-parties elles-mêmes dérivées de sous-parties, etc.

Un processus peut donc admettre des processus fils et un processus-père. La relation hiérarchique «Processus-PARENT est le père de Processus-ENFANT» comprend plusieurs aspects, entre autre le temps de durée de Processus-ENFANT qui est inclus dans celui de Processus-PARENT (Fig.1).

Elle comprend également l'ordonnancement des règles de Processus-PARENT et Processus- ENFANT à un instant précis t. Les «pré-règles» de Processus-PARENT (dénotées par l'identificateur each time:) sont exécutées avant les «pré-règles» de Processus-ENFANT et les «post-règles» (définies par l'identificateur each-time*:) de Processus-ENFANT (Fig.2).

Prenons un exemple simple de structure hiérarchique. Supposons que le processus PARENT a trois fils appelés ENFANT1, ENFANT2 et ENFANT3. Cette lignée sera indiquée dans la définition du processus PARENT par l'expression:

children (ENFANT1 ENFANT2 ENFANT3)

Figure 2 - Séquence d'exécution des règles pour un processus-PARENT et ses processus ENFANT.
A partir de là, l'ordonnancement temporel des fils sera défini par le moniteur de PARENT. Voyons maintenant brièvement deux types de moniteurs. Avec le moniteur `sequential node' défini dans le processus PARENT par l'expression:
monitor: sequential-node
les trois fils seront simplement juxtaposés dans le temps, le premier s'activant au btime de PARENT et les autres au moment où celui qui le précède s'achève (Fig. 3).

Avec le moniteur `parallel node' défini dans PARENT par l'expression:

monitor: parallel-node
Les trois fils se déclencheront ensemble au btime de PARENT (Fig. 4).

Mais, avec le moniteur `parallel node' une liste de listes des fils en `sequentiel' est déclenchée. Il nous faut donc ajouter des parenthèses pour écrire:

children: ( (ENFANT1) (ENFANT2) (ENFANT3) )
Le second moniteur démontre comment il peut y avoir absence de relation hiérarchique entre des processus (et parfois leur lignée du point de vue de la durée. C'est ce que nous appelons parallélisme. Cette caractéristique est essentielle à la représentation des nombreux aspects des structures musicales, par exemple lorsque différentes voix «ne sont pas nécessairement synchronisés» mais que les processus des différentes voix communiquent entre eux et interagissent entre eux.

Figure 3 - Processus-fils en mode séquentiel.

3.3 L'arbre de calcul

La structure dans laquelle les processus sont organisés peut être représentée sous forme d'un arbre (généalogique). La racine en est un processus dont l'ambitus temporel couvre celui de tous ses fils. A un instant précis t, seuls sont actifs les processus qui incluent le temps t dans leur ambitus. Ils représentent une «coupe» dans l'arbre que nous appelons arbre de calcul (Fig. 5). A un moment donné, seules sont exécutées les règles des processus actifs dans l'ordre défini par la «coupe». Ainsi, l' arbre de calcul est une liste des règles des processus actifs dans leur ordre respectif. Nous appelons cette structure `arbre' car dans l'implémentation que nous verrons plus tard elle sera véritablement organisée comme un arbre.

3.4 Communication avec les processus

Les utilisateurs peuvent converser avec les processus en transmettant/envoyant des messages. Ils le font lorsqu'ils souhaitent interroger un processus sur sa «nature», son «état», ou «ses capacités» et lorsqu'ils souhaitent lui faire exécuter un ordre comme activer un processus, afficher un graphique sur l'écran. Pour transmettre un message à un processus, l' utilisateur tape: (Processus «message») où «message» représente le nom d'un processus (le sélecteur) éventuellement suivi d'arguments comme dans la notation ci-dessous (BackusNaur Form ou BNF). Dans cet exemple, le côté gauche de la construction est remplacé par une des phrases situées à droite.
message       :: selector argument list
argument list ::= arg
              ::= arg argument list
selector        = help
	        = selectors
                = play
                = understands
                = name
                = kill
                = sleep
                : wake-up
                :
A titre d'exemple, l' expression (processus help) demande au processus d'afficher la documentation qui le concerne. (Processus selectors ) demande au processus d'afficher la liste de tous les messages qu'il connaît.

Figure 4 - Fils en parallèle.

Figure 5 - Arbre de calcul.

3.5 «Portabilité» des règles

Comme nous l'avons souligné dans l'introduction, la généralité, la comptabilité et la modularité sont des caractéristiques essentielles. Dans l'environnement FORMES, une règle se réfère presque toujours au processus dans lequel elle est placée, par exemple pour accéder à son environnement. A cette fin, les règles doivent être indépendantes du nom du processus dans lequel elles sont inclues. On peut donc les copier et les transférer sans modifications d'une bibliothèque à tout autre processus.

Une importante construction sera celle qui se réfère au processus actif qui appelle la règle sans nommer explicitement ce processus. C'est le rôle de la construction fself qui se réfère au processus dont les règles sont en cours d'évaluation. Ainsi, si l'on considère le processus bar défini ci-dessus avec la variable partial1 de la valeur 440 dans son environnement, la construction (fself?'partial1) qui apparaît dans bar donne 440. Le (?) est le sélecteur d'un message transmis au processus fself (bar dans ce contexte) et demande la valeur de la variable partial1 dans son environnement. De même (fself?'partial1'A4) demande au processus bar de modifier la valeur de sa partial1 en A4. Dans la même veine, (fself?'PARENT) donne le père du processus désigné. Pour un certain type de processus appelés processus de transition, que nous verrons plus tard, les expressions

(fself?'left-sibling)
(fself?'right-sibling)
donnent les processus précédant et le processus suivant de fself dans la liste des fils de leur processus-parent. A titre d'exemple, la construction (fself?'left-sibling) qui apparait dans une règle du processus ENFANT2 (tel qu'il a été défini) désignera ENFANT1. Enfin, dans PARENT, l'expression (fself? 'active-children) donne le reste de la liste des fils successifs en commençant par le premier actuellement `actif'.

3.6 Les moniteurs: leur rôle

Un moniteur définit une structure de contrôle temporel. Le moniteur d'un processus c'est l'ordonnanceur de ses fils. Il remplit trois fonctions:

Pour définir un nouveau moniteur l'utilisateur peut soit envoyer un message new au moniteur du métagénérateur soit ne redéfinir que quelques méthodes (fonctions génériques Lisp associées au moniteur). Dans le dernier cas, le message new est également lancé mais à un moniteur existant.

3.7 Autres outils de l'environnement FORMES

L'environnement FORMES fournit plusieurs outils qui aident l'utilisateur à développer des programmes et à élaborer des compositions. Lorsqu'on fait tourner un programme FORMES, la valeur courante de time et l'arbre de calcul sont toujours affichés sur l'écran.

Les messages print et print: font afficher les représentations d'objets internes (Lisp) et externes (définis par l'utilisateur).

Une documentation on-line est fournie par le message help transmis aux objets. En outre, un programme help (appelé Aid, écrit par Patrick Greussay) peut être appelé à tout moment. Un autre programme écrit par Harald Wertz conserve le courrier et les informations concernant FORMES données par les implémenteurs et les utilisateurs.

Une adresse fichier est associée à chaque fonction du système permettant d'avoir accès au fichier qui définit la fonction. Les objets et les fonctions créés ou modifiés par l'utilisateur peuvent également être sauvegardés interactivement dans un fichier. Enfin, les utilisateurs peuvent demander qu'une trace d'éléments de la boucle FORMES soit conservée.

4. La programmation en formes

4.1 Programmation (composition) en FORMES

FORMES permet au compositeur de «jouer» (faire tourner) et d'écouter des processus ou de modifier leur environnement, activité simple quoiqu'enrichissante, notamment pour les débutants dont l'apprentissage se fait littéralement en «jouant» avec des objets préalablement définis (processus et règles de la librairie) sans avoir à construire un patch exempt d'erreurs. C'est également vrai pour les programmeurs chevronnés qui préfèrent parfois modifier un ancien programme au lieu d'en écrire un nouveau. Enfin cette méthode répond à nos exigences à savoir que FORMES doit être un environnement en évolution perpétuelle où l'abstraction et la puissance des objets s'enrichissent de l'expérience passée.

Les utilisateurs peuvent également créer des règles. Créer des processus qui incorporent de nouvelles règles constitue une seconde étape de l'interaction avec FORMES un peu moins simple mais néanmoins accessible à de non-experts en informatique.

; Processes:
          (process new 'CHILD1
            each-time: '((envelope))
            env: '(duration 0.2))
          (CHILD1 new 'CHILD2 env: '(duration 0.3))
          (process new 'PARENT
            each-time: '((envelope))
            children:  '(CHILD1 CHILD2 CHILD1))
; Rules
           (de envelope ()
             (*= amplitude (envelop.fun (tnorm))))
; Initialization:
           (setqq each-quantum (amplitude 1.))
; Load breakpoint functions:
           (bitpad envelop.fun) 
Figure 6 - Code FORMES de création d'enveloppe de notes et de phrases.
Créer de nouveaux moniteurs constitue un mode d'interaction plus rare puisqu'il fait parfois appel à une connaissance poussée du système.

4.2 Exécution du programme FORMES

Les programmes et les commandes FORMES peuvent être préparés dans des fichiers et lus dans le système ou tapés «on line». Un programme FORMES est une structure de processus dont l'exécution implique la répétition à des temps successifs des deux démarches suivantes baptisées boucle FORMES: (1) Mise à jour de la liste des règles qui constituent l'arbre de calcul et (2) exécution (évaluation) des règles de l'arbre de calcul. L'exécution du programme (structure) est déclenchée par l'envoi du message play au processus à la racine de la structure (appelons le root-process (Processus-racine)):
(Root-process play)
Notons que tout processus peut être considéré comme la racine d'une sous-structure (s'il n'y a pas de fils, la sous-structure se réduit au seul processus). L'exécution s'achève lorsque le processus racine trouve une condition donnée par l'utilisateur comme condition de sortie, ex. une durée écoulée.

Le «temps courant» se trouve dans une variable appelée time complétée par le message next-tick. L'expression:

(while (Root-Process end?)
      (tree evaluate)
signifie ceci: le message end? est transmis de manière répétitive au processus-racine. Suivant son moniteur et d'autres conditions, root-process met à jour l'arbre de calcul et indique `faux' s'il n'a pas encore terminé ou `true' dans le cas contraire. La boucle `while' est répétée jusqu'à ce que le processus-racine ait décidé qu'il a terminé.

L'évaluation de l'arbre de calcul provoquée par le message evaluate transmis à l'arbre est telle aussi répétée. La dernière règle qui apparaît dans l'arbre est (clock next-tick) où le message next-tick transmis à clock provoque une incrémentation de time (time = time + quantum). Par défaut, time est incrémenté par la valeur par défaut de quantum (0.01 s.).

On courrait écrire ceci en langage type Algol comme suit:

WHILE (Root-Process-not-ended) 
DO BEGIN
  Update-the-tree;
  Evaluate-the-tree;
END

5. Comment travailler avec FORMES

5.1 Premier exemple

Cet exemple traite d'un problème bien connu: la description de l'enveloppe d'amplitude pour un modèle de note et l'utilisation de ce modèle pour la construction d'une enveloppe d'amplitude destinée à une phrase monodique. Se reporter à la Fig. 6 pour le code. (les commentaires sont précédés d'un point-virgule).

Comme le montre la Fig.6, la règle envelope apparaît sans modification après l'identificateur each-time: dans PARENT et dans les fils (ce qui démontre la portabilité des règles). La règle d'enveloppe est une fonction Lisp définie par l'expression (de envelope() ...). En notation Lisp préfixée, cette fonction ajuste la variation globale amplitude à sa précédente valeur multipliée par celle de la fonction envelop.fun avec l'argument tnorm. Cette abréviation de «time normalized» signifie que sa valeur est proportionnelle à time mais va de O à 1 pendant la durée du processus qui l'utilise. En d'autres termes,

tnorm = (time-btime)/(duration)
ou, plus précisément, tnorm est défini dans FORMES comme suit:
(setq tnorm '(div(sub time (fself? 'btime))
   (fself?'duration)))
La construction (envelop.fun (tnorm) ) se comporte comme un générateur d'enveloppe, avec le temps de l'enveloppe mis à l'échelle de la durée du processus et avec le quantum comme période d'échantillonnage. La définition réelle de l'enveloppe s'obtient par la construction (bitpad envelop.fun) où l'argument envelop.fun est le nom d'un fichier qui comprend la description d'une fonction 'breakpoint' entrée au clavier ou en traçant une courbe sur un `bitpad'. Ex.:
Value Time
0 0
1 1
6 2
4 7
0 10
Les valeurs et les temps sont normalisés dans l'intervalle [0,1].

La manière dont la règle `envelope' est écrite (utilisant *=) exige que l'amplitude soit initialisée à chaque étape de la boucle et ce, avant la règle de PARENT. Cette complication apparente s'explique par le souci d'assurer la portabilité des règles. Une variable globale telle que `amplitude' transmet des informations d'un processus à l'autre pour un faible coût. En outre, les règles peuvent être écrites avec référence explicite aux variables globales comme la fréquence fondamentale, le vibrato ou le tempo. Ainsi les processus peuvent être facilement inclus ou retranchés de la structure globale puisque les variables globales sont communes à presque toutes les structures.

Pour le calcul de l'amplitude à chaque quantum, nous utilisons, entre autres la commande suivante:

(setq each-quantum' (amplitude1.))
par laquelle l'amplitude est fixée à la valeur 1 au début de chaque opération d'évaluation de l'arbre de calcul.

Arrivés à ce point, nous pouvons faire tourner le système. Nous envoyons tout d'abord le message play à ENFANT1:

(ENFANT1 play)
Ainsi la boucle système de FORMES est lancée sur le processus-racine ENFANT1. Ses règles (envelope) après l'identificateur each time:) sont exécutées l'une après l'autre aux temps 0, 0+quantum, 0+(2*quantum), etc. (0, 0.01, 0.02, ...] jusqu'à l'achèvement de ENFANT1 (0.2 sec. qui est sa durée). Ainsi la variable amplitude est successivement placée aux valeurs échantillonnées de l'enveloppe définie plus haut, et peut être envoyée à l'entrée de contrôle d'amplitude d'un synthétiseur.

Le résultat peut être vérifié à l'aide de la commande (amplitude screen) qui affiche les valeurs successives d'amplitude sous forme d'un graphique de l'amplitude en fonction du temps (Fig.7).

(En réalité ce tracé a été obtenu par la commande (amplitude plot) qui le fait apparaître sur une table traçante). Envoyons maintenant le message play à PARENT:

(PARENT play)

Figure 7 - Enveloppe d'amplitude créée par la règle d'enveloppe.
PARENT est donc `actif' du temps O au temps 0.8 (somme des durées des trois enfants). Pendant l'intervalle temporel [O, 0.2] seul ENFANT1 est lui aussi actif et dans l'arbre de calcul la règle de PARENT est suivie par celle de ENFANT1. La règle de PARENT place l'amplitude aux valeurs correspondant à l'image de la Fig.7 mise à l'échelle sur un intervalle (O, 0.8). Mais alors la règle de ENFANT1 multiplie l'amplitude par la même forme mise à l'échelle (O, 0.2). De même, pendant l'intervalle temporel (0.2, 0.5) seuls PARENT et ENFANT2 sont `actifs' et l'amplitude est le produit de l'amplitude correspondante de PARENT par celle de ENFANT2 (0.2 0.5). De même pour ENFANT3 (0.5, 0.8). Finalement, le message (amplitude screen) donne la courbe de la Fig.8.

A l'aide de cet exemple élémentaire, nous avons fait ressortir plusieurs points: la modularité permet d'étendre l'environnement musical en augmentant le domaine des objets existants et de tester des objets élémentaires indépendamment les uns des autres avant de les regrouper dans des objets de niveau plus élevé. La règle que nous avons utilisée se trouve dans une bibliothèque de règles généralement accessible. Elle peut servir pour d'autres objets: les musiciens et les chercheurs peuvent donc l'exploiter dans plusieurs contextes différents.

On remarquera qu'un objet récemment créé peut être testé immédiatement par l'envoi de la commande play: on peut alors écouter le son correspondant. La valeur d'un paramètre de processus (telle que la durée) est contenue dans le processus. L'utilisateur peut la voir dans le code de définition du processus et non pas dans une liste séparée. Elle n'est pas référencée par sa position mais par un identificateur mnémonique: on peut donc facilement la consulter ou la modifier.

La validité d'un programme peut être vérifiée rapidement par affichage graphique. Nous avons souvent observé qu'un graphique de valeurs de paramètres pouvait indiquer automatiquement la présence d'une erreur et sa position dans un programme complexe. Comme tout processus peut être lancé (si cela a un sens] au cours de la création et de l'expérimentation d'une structure, on peut suivre une procédure `de bas en haut' comme nous venons de le faire. C'est-à-dire qu'à partir d'un «processus feuille» (donnant les détails d'un son) satisfaisant, on peut l'inclure dans un processus PARENT pour obtenir une structure PARENT-enfants, etc.


Figure 8 - L'interaction entre le processus PARENT et les processus ENFANT ;odifie l'enveloppe originelle.
On peut également suivre une approche du `haut en bas', c'est-à-dire construire l'élément le plus élevé d'une structure et descendre en elle avec de plus en plus de détails. On peut également partir de n'importe quel niveau intermédiaire, par exemple d'une autre structure trouvée dans la bibliothèque.

Cette flexibilité et cette adaptabilité se prêtent particulièrement bien à la création artistique. En outre, il faut reconnaître que même un exemple aussi simple serait difficile à écrire dans d'autres langages.

Pour aller plus loin avec cet exemple:

Nous pouvons tirer avantage des structures hiérarchiques de FORMES pour taper les commandes suivantes:

(PARENT newcl' UNCLE 'duration 0.3)
  (process new` GRAND-PARENT
    children: PARENT UNCLE) )
L'expression (GRAND-PARENT play) donnerait le tracé d'amplitude de la Fig.9.

Il se peut que vous souhaitiez modifier la courbe de base d'une enveloppe: FORMES vous offre la possibilité de le faire à l'aide d'instructions simples. A titre d'exemple, vous pouvez obtenir une formule beaucoup plus souple en tapant la commande suivante qui redéfinit la fonction envelope:

(de envelope ()
  (*=amplitude
    (*(power (tnorm) gamma)
      (power (- { 1. (tnorm) ) (- {1.gamma)))))
qui signifie que la valeur précédente de l'amplitude est multipliée par:
tnormgamma(1 - tnorm)1 - gamma 

Figure 9 - Enveloppe GRAND-PARENT.
Si vous fixez à gamma la valeur 0.25 et lancez PARENT, le tracé d'amplitude ressemblera à celui de la Fig.10.

Enfin, supposez que nous voulions modifier la valeur du paramètre gamma au cours de la durée de GRAND-PARENT. Pour simplifier cette explication, nous avons choisi une rampe allant de 0.2 à 0.8 pendant la durée de GRAND-PARENT. Nous pouvons alors inclure cette règle dans celles qui sont déjà liées à son identificateur each time:


Figure 10 - Redéfinition de l'enveloppe après ajustement de gamma.
(GRAND-PARENT insert 'each time: 'set-rampq 'gamma 0.2 0.8)
Une fois GRAND-PARENT lancé, l'évolution d'amplitude ressemble à celle de la Fig.11.

Figure 11 - Insertion d'une partie de GRAND-PARENT dans une enveloppe existante.
Nous espérons avoir démontré ainsi comment un programme relativement simple peut donner des résultats complexes et puissants comme des enveloppes d'amplitude contrôlant un synthétiseur. Nous espérons également avoir montré comment on peut facilement modifier des règles et comment la transmission de messages et les procédures de modification des tests peuvent permettre de composer plus rapidement et plus facilement.

5.2 Deuxième exemple

Dans cet exemple (Fig.12) nous décrivons comment FORMES a permis de résoudre un problème musical. L'un des objectifs de cet exemple est d'étudier un problème de synthèse `concret'; l'autre consiste à démontrer que FORMES est capable d'inclure des aspects compositionnels ou de `haut niveau' du processus de synthèse. Nous pensons que nous avons beaucoup à gagner si les `hauts niveaux' et les `niveaux de synthèse pure' sont développés à l'aide du même formalisme et dans le même programme (Rodet, Potard and Barrière 1984).

Avant d'examiner la procédure de synthèse et les aspects compositionnels (même si la distinction entre niveaux `élevé' et `bas' peut prêter à confusion) voyons d'autres constructions FORMES utilisées dans cet exemple.

Autres constructions FORMES:

Afin de produire simultanément deux voix «désynchronisées» nous avons utilisé dans PARENT le moniteur parallel-node décrit précédemment. Ici, les processus-fils sont VOICE1 et VOICE2, chacun représentent une voix indépendante. Ainsi, les deux séquences des fils de VOICE1 et VOICE2 seront exécutées en parallèle.

Nous utilisons également la fonction trelative qui donne un temps relatif au début du processus courant. Sa définition est la suivante:

(de trelative() 
           (sub time (fself? `btime)))
Dans l'exemple précédent, les règles d'un processus apparaissaient après l'identificateur each time:; elles étaient exécutées à chaque quantum pendant l'activation du processus, c'est-à-dire, «chaque fois» que le processus était exécuté. Il est bien entendu souhaitable de tirer profit de règles qui ne sont exécutées qu'une fois, à l'activation du processus identifiée par first-time) ou à la fin du processus (identifiée par last-time).

La construction:

(fself change 'children: '(ENFANT1 ENFANT2 ENFANT3))
modifie la liste des fils du processus présentement désigné par fself qui devient la liste (ENFANT1 ENFANT2 ENFANT3). Dans le code ci-dessous, nous avons:
first-time:((fself change 'children: 
	     (derive 'following 
		     '(short short s-asc Ef2) 
		     (fself ? phrase-number)))
qui signifie qu'à l'initialisation du processus (VOICE1) sa liste de fils est changée en un résultat de l'évaluation de la forme derive que nous verrons ultérieurement.

Un programme FORMES:

Les fils de VOICE2 sont dans une liste appelée bass que nous n'expliquons pas en détail. Pour chaque note du processus de la Fig.12 (Ef1, etc.) nous appliquons une envelope au paramètre coefamp - donnée d'entrée relative au niveau du son du synthétiseur CHANT (ou plus précisément, la pente du spectre; la richesse du son varie également suivant la valeur de coefamp). De plus, pour chaque note, nous utilisons la même enveloppe que dans dans l'exemple précédent.

; Some function definitions appear in Appendix 1
; PROCESSES:
(process new 'PARENT
  monitor: 'parallel_node
  children: '((VOICE1) (VOICE2)))
(process new 'VOICE1
  monitor: 'seq-node
  firts-time: '((fself change 'children:
                  (derive '(following)
                           '(short short s_asc E(4) (fself! 'phrase_number)))
               (transition?))
  each-time: '((rest? in-n-notes))
  children:      `(''')
  env:       '(phrase_number 14))
(process new 'VOICE2
  monitor: 'seq-node
  each-time: '((rest? in_n_notes))
  children: (bass))
(process new 'Ef4
  first-time: '((tempo))
  each-time: '((envelope) (warp) (my-pitch)))
(Ef4 new 'F(4)
(Ef1 new 'Gf1)
.
.
.
; DECLARATIONS:
(synthesizer f0 coefamp freq1 freq2 freq3 freq4 freq5)
(setq each=quantum '(coefamp 1.))
Figure 12 - Exemple de FORMES utilisé pour générer un processus musical de haut niveau.
Ici, la fonction warp (dans la section each time: de Ef4) contrôle les variations de timbre. Elle altère le spectre de l'enveloppe en changeant les fréquences des formants (Rodet, Potard and Barrière 1984). Notez que les fréquences des formants peuvent être utilisées pour déterminer un filtre de formants ou pour commander une autre technique de synthèse. En fait, cette règle est relativement indépendante de la technique de synthèse utilisée.

La fréquence fondamentale de chaque note d'un processus est calculée par la fonction my-pitch suivant le nom du processus.

La liste de note est auto-générée dans VOICE1; elle remplace les ??? qui apparaissent après l'identificateur children:

Autre point important à considérer: l'interaction entre les deux voix. Du fait de tempos variant légèrement (fonction (tempo) dans Ef1, etc.) les deux voix ne sont pas synchrones. Mais à tout moment l'une des deux passe en «mode maître» et anticipe dans la liste de ses fils, c'est-à-dire, qu'elle vérifie s'il y a encore n-n notes à venir grâce à la fonction (rest?) dans la section each time: de VOICE1 et VOICE2. Dans l'affirmative, la «voix maître» modifie la durée des processus à venir dans l'autre voix de manière à assurer une synchronisation des deux voix avec la note qui suit le «rest». En outre, lorsque cette resynchronisation se produit, la «voix-maître» peut décider de renverser les rôles et l'autre voix assume le rôle de «voix-maître». Ce type de communication libre entre deux ou plusieurs voix est habituellement très difficile à réaliser avec les autres langages musicaux.

Pour finir, nous souhaitons élaborer une articulation en legato entre deux notes qui prenne la forme d'un contour continu de hauteur d'une note à la suivante et qui remplace les deux hauteurs fixes de note. Pour cela, nous utilisons un type de processus appelé processus de transition dont la durée (span) couvre la portion temporelle précédant et suivant la transition entre deux processus.

Cette transition a pour fonction d'extraire des informations des deux processus pour générer un schéma d'évolution en les combinant avec une règle de transition. En application musicale, les transitions constituent une classe importante de processus où l'on trouve les attaques de notes successives, des legatos et l'articulation des phrases successives. Les processus d'articulation implémentent le concept d'anticipation dans la mesure où ils peuvent s'informer de l'état à venir du second processus et ainsi se comporter d'une manière non initialement mise en oeuvre dans les processus «non transitionnels». On notera qu'il n'est pas très aisé de traiter de tels effets transitionnels dans les langages musicaux qui divisent l'environnement en patchs et listes de notes.

Dans notre exemple (Fig.12) les processus de transition sont introduits dynamiquement dans la liste des processus fils par la fonction (transition? )dans la section first-time: de VOICE1.

Génération de la liste des fils:

Nous allons maintenant expliquer la fonction d'appel utilisée dans VOICE1 pour générer sa liste de fils. Le but de cet extrait musical est d'utiliser une version très simplifiée et modifiée des règles que l'on trouve dans une étude d'improvisation jazz (Nous remercions André Hodeir et Philippe Gautron qui nous ont communiqué ces règles). Nous les avons formalisées en termes de règles de réécriture. A titre d'exemple, une règle de réécriture s'écrit en notation BNF:

identifieur i::= liste-d'identifieurs-1
             ::= liste-d'identifieurs-2
                          .
                          .
                          .
             ::= liste-d'identifieurs-n
Ce type de règle signifie que identifieur-i est réécrit sous la forme liste-d'identifieurs-1 ou liste-d'identifieurs-2, etc. Mais lorsqu'on se sert de ces règles pour produire une phrase musicale, il est nécessaire de spécifier la solution retenue pour chaque dérivation. En conséquence, à chaque dérivation correspond une condition d'application:
<identifieur> ::=condition-d'application-1 <liste-d'identifieurs-1>
              ::=condition-d'application-2 <liste-d'identifieurs-2>
              ::=condition-d'application-n <liste-d'identifieurs-n>
Soit l'expression expression-i qui une fois évaluée donne la même solution qui serait choisie conformément aux conditions. La règle ci-dessus peut alors s'écrire comme suit:
<identifieur-i>::=expression-i 
Cette expression signifie que lorsque l'identifieur i est trouvé, il est réécrit en la liste des identifieurs issue de l'évaluation de l'expression i (appel de fonction) dont le rôle consiste à choisir parmi plusieurs éventualités et à en donner une explicitement.

Récrire des règles:

Dans cet exemple, sans perdre en généralité, une liste très succincte de règles a été choisie:

<following> ::=derive-following
<rest>::=derive-rest
Dans la règle 1, derive-following est la fonction à appeler pour récrire following sous la forme:
<rest><list-of-notes> [<following>]
où les crochets signifient que following est facultatif. La list-of-notes est choisie parmi plusieurs possibilités conformément aux caractéristiques de la phrase précédente. Les caractéristiques de la phrase précédente forment un groupe de quatre éléments:
[start-beat, end-beat, class pivot],
dans lequel start-beat représente le temps (long ou court) sur lequel la phrase débute, end-beat le temps sur lequel la phrase s'achève, pivot représente la dernière note de la phrase et class correspond au contour de pitch constitué par les trois dernières notes de la phrase - à savoir, une des formes suivantes: descendante et ascendante soit ascendant ou asc, ascendante et ascendante soit super-ascendant ou s-asc, ascendante et descendante soit descendant ou desc et descendante et descendante soit super-descendant ou s-desc. Ces propriétés sont utilisées par la fonction derive-following pour sélectionner la prochaine phrase à venir dans la classe de la phrase précédente conformément à la note pivot.

La fonction rest permet de récrire rest sous la forme long-rest, short-rest ou rien, pour assurer que l'alternance (long, court) soit respectée et que la phrase suivante débute sur un temps de même type que son start-beat.

Le code qui permet de dériver ces règles et le code de ces fonctions se trouvent en Annexe 1. Pour les phrases et les classes ils utilisent la structure de données suivante (on en trouvera aussi un extrait dans l'annexe 1):

<class>::=
     ((pivot-1-<phrase-1-data> ... <phrase-i-data>)
      (pivot-2-<phrase-1-data> ... <phrase-j-data>)
      (pivot-h-<phrase-1-data> ... <phrase-m-data>))
<phrase-x-data>::=
     ((<start-beat> <end-beat><class>)
        (note-1 note-2 ... note-p))
A titre d'exemple supposons que nous choisissions (short, short asc Ef4 )comme «propriétés de la phrase précédente» initiales et que nous souhaitions que la dérivation s'achève après production de quatre phrases. L'évaluation de l'expression:
(derive 'following '(short short asc Ef4) 4)
donne:
(Gf4 G4 Bf4 C5 Ef5 C5 
long-rest Gf5 Bf4 A4 Ef5 
short-rest Gf5 
short-rest F5 Ef5 C5
Par cet exemple nous venons de démontrer que des procédures complexes peuvent être insérées dans FORMES afin de réaliser une tâche compositionnelle. Le principal avantage à tirer de cette possibilité c'est que des procédures de haut niveau peuvent facilement interagir avec les détails des procédures de synthèse puisqu'elles sont implémentées dans le même environnement, voire le même programme.

6. Interconnexion avec les synthétiseurs

Un des objectifs principaux du projet FORMES est de constituer une bibliothèque des «modèles des processus qui interviennent dans la production musicale» (Rodet, Potard et Barrière 1984).

Comme nous le disions plus haut, nous tentons de définir des modèles qui ne soient pas liés à une technique particulière de synthèse.

FORMES sert à élaborer des événements discrets ou continus [i.e., échantillonnés à un taux élevé] ainsi que des paramètres de contrôle qui décrivent des caractéristiques acoustiques, physiques ou musicales. Dans sa structure il n'existe pas de présupposé sur le dispositif qui calcule un signal correspondant (d'aussi près que possible) à ces caractéristiques. C'est pourquoi FORMES est interfacé à plusieurs types de synthétiseurs. Supposons que PARENT soit le processus-racine d'une structure décrite à la Fig.12. La commande (PARENT chant) demande au processus PARENT de formater les sorties programme pour le synthétiseur CHANT. De même, (PARENT4X) permet à ce même programme de commander un patch de synthétiseur 4X [construit à l'IRCAM par G. Di synthétiseur 4x (construit à l'IRCAM par G. Di Giugno) sans modification. Tout autre dispositif peut être contrôlé de la même manière. Ainsi, (PARENT ap) envoie les paramètres du programme à notre processeur vectoriel à arithmétique flottante et (PARENT symbol) donne une représentation symbolique (ex.: une partition) du morceau musical.

Une telle polyvalence garantit un accès plus uniforme aux différents dispositifs. Elle permet de substituer un environnement à l'autre lorsque le premier est défaillant ou inadapté pour des raisons musicales ou techniques. (Par exemple, certains dispositifs de synthèse ont un plus grand nombre de voix, des calculs plus précis ou de meilleures facilités de contrôle que d'autres). Pour finir, et c'est ce qu'on retiendra, la voie est ouverte à une représentation standard ou formalisation de structures et de processus qui sera d'une très grande aide pour tous les utilisateurs d'informatique musicale.

7. L'environnement et le poste de travail Formes

L'environnement que nous souhaitons créer pour FORMES doit pouvoir exécuter en temps réel les programmes FORMES. Pour cela, nous devons disposer d'un ordinateur-hôte rapide doté d'une unité arithmétique à virgule flottante et d'une mémoire principale d'environ 1 Mbyte (rien de plus que ce que l'on trouve sur tous les postes de travail actuels). Un affichage bit-map de haute résolution est indispensable, de même que des facilités comme une «souris», un «joystick» et des potentiomètres.

Cet ordinateur-hôte doit être connecté à un synthétiseur à virgule flottante (plus rarement disponible). Sa structure de contrôle interne doit être aussi libre que possible, c'est-à-dire qu'elle doit pouvoir traiter des sous-routines et des coroutines et assurer un «Tampon» pour tout flot de données en entrée, en sortie ou internes. Le synthétiseur doit également avoir une grande capacité d'entrée/sortie entre les programmes FORMES et les programmes de traitement du signal afin d'assurer des mises à jour rapides à partir de ordinateur hôte.

Le poste de travail doit également comporter toute une série de programmes d'analyse du signal et des données, y compris des techniques sophistiquées de traitement du signal telles que la synthèse et l'analyse par impulsions multiples, des algorithmes d'extraction de données psycho-acoustiques et de bons dispositifs d'affichage graphique (affichage 3-D et en couleurs de représentations symboliques).

Mais la pièce maîtresse que nous souhaitons créer est une bibliothèque (ou base de données) des modèles mis en oeuvre dans des processus et des règles prédéfinis, à laquelle on aura accès par le biais de qualificatifs («plus riche», «plus texturé») ou de termes musicaux ou de termes s'appliquant aux modèles physiques de production de sons (ex.: les modèles de piano et violon créés par G. Weinreich [1983]). Mais ce qui compte surtout c'est que cette bibliothèque doit s'enrichir sans cesse des processus et des règles dûs à la contribution des compositeurs et des informaticiens qui utiliseront ce poste de travail.

Remerciements

Nous remercions Yves Potard pour sa contribution essentielle à la création de FORMES, Bernard Serpette et Jean-Pierre Briot qui ont participé à la mise en oeuvre du système FORMES et Jean-Baptiste Barrière pour ses suggestions et les tests musicaux de l'environnement FORMES.

Nous exprimons aussi notre reconnaissance à Patrick Greussay, Jérôme Chailloux, Jean-François Perrot, Harald Wertz, Eugen Neidl, Christian Queinnec et Daniel Goossens qui ont fourni l'environnement Lisp. Nous remercions enfin Pierre Boulez, Tod Machover, Philippe Manoury, Marco Stroppa et tous ceux qui à l'IRCAM nous ont encouragés par leurs commentaires sur les aspects musicaux du système FORMES.

Références

(Althoff 81) - J.C. Althoff, L.P. Deutsch, D.H. Ingalls, A. Goldberg, T. Kaehler, G. Krasner, F.H. Reenskaug, D. Robson, J. Ross et L. Tesler, «The Smalltalk-80 System», Learning Research Group, BYTE Vol.6 N°8, Mc Graw-Hill Publication (August 1981).

(Barrière 83) - (Barrière 83) - J.E3. Barrière, Chreode, ICMC, Rochester (Octobre 1983).

(Bennet 80) - G. Bennet, Singing Synthesis in Electronic Music, Research Aspects on Singing, Stockholm (September 1980).

(Birtwistle 73) - (Birtwistle 73) - G. Birtwistle, O. Dahl, B. Myhrhaug et K. Nygaard, SIMULA BEGIN, Petrocelli/Charter, New York (1973).

(Boulez 83) - (Boulez 83) - P.Boulez, Quoi? Quand? Comment? La Recherche Musicale, Christian Bourgeois IRCAM éditeurs 1985.

(Causse 84) - R. Causse, J. Kergomard et X. Lurton, «Input Impedance of Brass Instruments», JASA 75 (1) Jan. 84 p.241-254, (1984).

(Chailloux 84) - J. Chailloux, Manuel Le-Lisp (2eme édition), INRIA (Février 1984).

(Cointe 81) - P. Cointe, «Fermetures dans les - interprètes: Application aux Langages LISP, PLASMA et SMALLTALK», LITP 82-11, Université Paris Vl, Paris (Décembre 1981), thèse de 3e cycle.

(Cointe 82) - P. Cointe, «VLISP: un langage objet ?», pp.143-164 dans Actes de l'Ecole de Printemps d'lnformatique Théorique, LITP 83-17, Barèges (Mars 1982).

(Cointe 82a) - P. Cointe, «Une réalisation de SMALLTACK en VLISP», TSI Volume 1 (4) pp.325-340 (Juillet-Août 1982).

(Cointe 83) - P. Cointe, «Evaluation of Object oriented Programming from Simula to Smalltalk», pp.17-24 dans Proceedings of the Eleventh Simula Users'Conference, SIMULA INFORMATION, Paris (7th-9th September 1983).

(Durieux 81) - (Durieux 81) - J.L. Durieux, Sémantique des liaisons nom-valeur: application à l'implémentation des - langages, Université Paul Sabatier, Toulouse (Septembre 81), thèse d'Etat.

(Greussay 82) - P. Greussay, «Le Système VLISP- UNIX», Département Informatique, Université Paris 8 - Vincennes (Février 1982). Draft.

(Hewitt 75) - C.E. Hewitt et B. Smith, A PLASMA Primer, MIT Artificial Intelligence Laboratory (September 1975). Draft.

(Hewitt 76) - C.E. Hewitt, «Viewing Control Structures as Patterns of Message passing», A.l. MEMO 410, MIT (December 1976).

(Ingalls 78) - D.H. Ingalls, «The Smalltalk- 76 Programming System Design and implementation»dans Conference Record of the 5th Annual ACM Symposium on Principles of Programming Languages, Tucson (January 1978).

(Kay 69) - A. Kay, The Reactive Engine, Ph. D. Thesis 1969.

(Kay 76) - A. Kay et A. Goldberg. «SMALLTALK-72 Instruction Manual», SSL 76-6, Xerox Palo Alto Research Center,Palo Alto, CA.(March 1976).

(Lieberman 81) - H. Lieberman, «A Preview of Act 1» AI Memo N°625, MIT (June 1981).

(Lieberman 82) - H. Lieberman, Machine tongues IX: Object- Oriented Programming, MIT (April- 27 1982). Draft.

(Machover 83) - (Machover 83) - T. Machover, La Recherche Musicale, Christian Bourgeois IRCAM éditeurs 1985.

(Moon 80) - D.A. Moon et D. Weinreb, «Flavors: Message Passing in The Lisp Machine», Al Memo N°602, MIT (November 1980).

(Novak 83) - G.S. Jr Novak, «GLISP: A Lisp-based Programming System with Data Abstraction», The Al Magazine (Fall 1983).

(Passien 83) - O. Passien, Codage Multi Impulsionnel du Residuel LPC, Université PARIS-6, Laboratoire de Reconnaissance des Formes (Juillet 1983). Mémoire de DEA.

(Pomian 80) - C. Pomian, Contribution à la définition et à l'implémentation d'un interprète PLASMA, Université Paul-Sabatier, Toulouse (Mars 1980), thèse de 3e cycle.

(Rees 82) - J. Rees et Adams, T: A Dialect of Lisp or, LAMBDA: The Ultimate Software Tool, Conference Record of the 1982 ACM symposium on LISP and Functional Programming, Pittsburgh (1982).

(Rodet 77) - X. Rodet, «Analyse du signal vocal dans sa représentation amplitude-temps. Synthèse de la parole par règles», LIF, Université Paris Vl, Paris (Juin 1977), thèse d'état.

(Rodet 80) - X. Rodet et G. Bennett, Research in Musical Synthesis Using a Model of Vocal Productions, ICMC, Queens College, New York (Novembre 1980).

(Rodet 82) - X. Rodet, P. Cointe, J.B. Barrière et Y. Potard, The CHANT Project: Modelization and Production, ICMC, Venise (Septembre 1982).

(Smith 83) - D.C. Smith, Ch. Irby, R. Kimball, B. Verplank et E. Harslem, «Designing the Star User Interface», pp.297- 313 dans Integrated Interactive Computing Systems, ed. P. Degano & E. Sandewall, North-Holland, Amsterdam, New York, Oxford t1983).

(Steels 83) - L. Steels, «ORBIT: An Applicative View of Object Oriented Programming», pp.193-206 dans Integrated Interactive Computing Systems, ed. P. Degano & E. Sandewall, North-Holland, Amsterdam, New York, Oxford (1983).

(Sundberg 83) - J. Sundberg, Synthesis by Rules, CMJ (1983).

(Weinreich 83) - G. Weinreich, «Violin Sound Synthesis from First Principles», dans 106th Meeting of the ASA, (1983).

(Winograd 79) - T. Winograd, «Beyond Programming Languages», ACM 22 (7) (July 1979).

Annexe 1

(de device (ident S ns)
  (let-named self ( (rules rew-rules) (rule*() ))
  (cond
  ( (null rules) ident )
  ( (setq rule* (apply-rule ident (nextl rules) S ns ))
  ( if (eq rule*no-rest) '*rule* ))
  ( t (self rules nil ) ) ) ) )
(de apply-rule (ident (ident1::= frule) args)
  (when (eq ident ident1) (apply frule args) ) )
(de vcons (elt list) (if elt (cons elt list) list) )
  
(de followinq (S ns)
    (lets ((next-data (next-phrase-data S ns) )
       (rest (derive ' rest S ns) ) )
    append
    (vcons rest, (cadr next-data) )
    (when( ns O) (derive ' following (car next-data) (1-ns) )))))
(de rest (S ns)
    (letvq (?cb.r) S
    (cond
      ((neq cb (caar next-data))'no-rest)
      ((eq cb'short)'long-rest)
      (t'short-rest) )))
(de next-phrase-data ((??class pivot) n)
    (car (find (cval class) pivot) )))
(de find (1 pivot)
    (when 1
      (letvq ((p.phnases).r) 1
         (if (eq p pivot) phrases (find r pivot) ))))
;REWRITING RULES
(setqq 
rew-rules (
   ( following ::= following)
   ( rest ::= rest)
   )
;DATA
(setqq
s-asc
 (
 (Ef4)
 ((short long asc Bf4)
    (Ef4 Gf4 G4 F4 A4 Bf4 r))
    (long long s-asc Bf4)
    (Ef4 Gf4 long-rest G4long-rest Ef long-rest A4 Bf4))
    ((long short asc Ef4)
    (Ef4 Gf4 G4 Gf4 G4 Ef long-rest Ef A4 Bf4 Ef4 short-rest)
 )
 (Gf4
   ((long short asc Gf4)
   (Gf4 Ef4 Gf4 G4 Bf4 C5 Gf4 short-rest))
   ((long short asc Gf4)
   (Ef4 Gf4 Ef G4 Ef Bf4 Ef C5 Ef C5 Gf4 short-rest))
   )
 )
asc
 (
  (Ef4
    ((long short desc C5)
       (Gf4 G4 Bf4 C5 Ef5 C5))
    ((long long des Bf4)
       (Ef4 Af4 Gf4 A4 Ef5))
       .
       .
       .
       .

Annexe 2

(de rest? (in-n-notes) (when (eq master (fself name)) (let ((notes (fself?'active-sons)) (t-sum O)) (let ((a-note (nthcdr in-n-notes notes))) (when (memq a-note'(short-rest'long-rest)) (repeat in-n-notes (+=t-sum ((nextl notes)?'duration))) (ajuste (the other master) in-n-notes (plus time t-sum)) (when (gt (random 06) 3) (setq master (alter master )))))))) (de the-other (voice) (if (eq voice 'VOICE1) 'VOICE2 'VOICE1 )) (de ajuste (voice in-n-notes the-time) (let ((reste ((car (voice?'active-sons))?etime)) (tsum () ) (list-t () ) (list-o () ) (r-s (cdr (voice?'active-sons) ))) (setq tsum reste) (let ( (r-s r-s)) (when (and (lt tsum the-time) r-s) (setq tsum (plus tsum (newl list-t (apply (get (newl list-o (nextl r-s)) 'duration:) ) )))) (self r-s))) (if (setq aux (sub tsum reste) ) (setq fact (div (sub the-time reste) (-tsum reste) ))) (mapc list-o (lambda (o) (put o'duration:[lambda() (mul fact (nextl list- t))])) )))

____________________________
Server © IRCAM-CGP, 1996-2008 - file updated on .

____________________________
Serveur © IRCAM-CGP, 1996-2008 - document mis à jour le .