Serveur © IRCAM - CENTRE POMPIDOU 1996-2005. Tous droits réservés pour tous pays. All rights reserved. |
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
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.
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.
Les propriétés souhaitables pour les modèles SCM sont toutes des objectifs de l'environnement FORMES:
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.
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é
Un processus peut donc admettre des processus
fils et un processus-père.
La relation hiérarchique «
Elle comprend également l'ordonnancement des
règles de
Prenons un exemple simple de structure hiérarchique. Supposons
que le processus
Avec le moniteur
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:
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
Pour définir un nouveau moniteur l'utilisateur peut soit envoyer
un message
Les messages
Une documentation on-line est fournie par le message
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.
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.
Le «temps courant» se trouve dans une variable appelée
L'évaluation de l'arbre de calcul provoquée par le message
On courrait écrire ceci en langage type Algol comme suit:
Comme le montre la Fig.6, la règle
La manière dont la règle
Pour le calcul de l'amplitude à chaque quantum, nous utilisons, entre
autres la commande suivante:
Arrivés à ce point, nous pouvons faire tourner le système.
Nous envoyons tout d'abord le message
Le résultat peut être vérifié à l'aide de la
commande (
(En réalité ce tracé a été obtenu par la
commande (
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
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.
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:
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
Enfin, supposez que nous voulions modifier la valeur du paramètre
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
Nous utilisons également la fonction
La construction:
Un programme FORMES:
Les fils de VOICE2 sont dans une liste appelée
La fréquence fondamentale de chaque note d'un processus est
calculée par la fonction
La liste de note est auto-générée dans VOICE1; elle remplace les ???
qui apparaissent après l'identificateur
Autre point important à considérer: l'interaction entre
les deux voix. Du fait de tempos variant légèrement (fonction
(
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 (
Génération de la liste des fils:
Nous allons maintenant expliquer la fonction d'appel utilisée
dans
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:
La fonction
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):
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
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.
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.
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.
(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).
____________________________ ____________________________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
Cette commande transmet un message
(process new'foo)
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,
définit un nouveau processus appelé
(foo new 'bar
env: '(partial1 440.0)
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.
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).
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).
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)
A partir de là, l'ordonnancement temporel des fils sera défini
par le moniteur de
Figure 2 - Séquence d'exécution des règles pour un processus-PARENT et ses processus ENFANT.
PARENT
. Voyons maintenant brièvement deux types de
moniteurs. Avec le moniteur `sequential node' défini dans le processus
PARENT
par l'expression:
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).
monitor: sequential-node
`parallel node'
défini dans
PARENT
par l'expression:
Les trois fils se déclencheront ensemble au btime de PARENT (Fig. 4).
monitor: parallel-node
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.
children: ( (ENFANT1) (ENFANT2) (ENFANT3) )
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.
A titre d'exemple, l' expression (
message :: selector argument list
argument list ::= arg
::= arg argument list
selector = help
= selectors
= play
= understands
= name
= kill
= sleep
: wake-up
:
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.
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
donnent les processus précédant et le processus suivant de
(fself?'left-sibling)
(fself?'right-sibling)
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:
init, end?
et
duration.
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.
print
et print:
font afficher les représentations
d'objets internes (Lisp) et externes (définis par l'utilisateur).
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.
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.
Créer de nouveaux moniteurs constitue un mode d'interaction plus rare
puisqu'il fait parfois appel à une connaissance poussée du
système.
; 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.
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)):
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.
(Root-process play)
time
complétée par le message next-tick
.
L'expression:
signifie ceci: le message
(while (Root-Process end?)
(tree evaluate)
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é.
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.).
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).
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,
ou, plus précisément,
tnorm = (time-btime)/(duration)
tnorm
est défini dans FORMES comme
suit:
La construction (
(setq tnorm '(div(sub time (fself? 'btime))
(fself?'duration)))
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.:
Les valeurs et les temps sont normalisés dans l'intervalle [0,1].
Value
Time
0
0
1
1
6
2
4
7
0
10
`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.
par laquelle l'amplitude est fixée à la valeur 1 au début
de chaque opération d'évaluation de l'arbre de calcul.
(setq each-quantum' (amplitude1.))
play
à ENFANT1:
Ainsi la boucle système de FORMES est lancée sur le processus-racine
(ENFANT1 play)
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.
amplitude screen
) qui affiche les valeurs successives
d'amplitude sous forme d'un graphique de l'amplitude en fonction du temps
(Fig.7).
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.
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.
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.
Figure 8 - L'interaction entre le processus PARENT et les processus ENFANT ;odifie l'enveloppe originelle.
L'expression (
(PARENT newcl' UNCLE 'duration 0.3)
(process new` GRAND-PARENT
children: PARENT UNCLE) )
GRAND-PARENT play
) donnerait le tracé d'amplitude de la Fig.9.
envelope:
qui signifie que la valeur précédente de l'amplitude est
multipliée par:
(de envelope ()
(*=amplitude
(*(power (tnorm) gamma)
(power (- { 1. (tnorm) ) (- {1.gamma)))))
tnormgamma(1 - tnorm)1 - gamma
Si vous fixez à
Figure 9 - Enveloppe GRAND-PARENT.
gamma
la valeur 0.25 et lancez PARENT
, le
tracé d'amplitude ressemblera à celui de la Fig.10.
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.
Une fois
(GRAND-PARENT insert 'each time: 'set-rampq 'gamma 0.2 0.8)
GRAND-PARENT
lancé, l'évolution d'amplitude ressemble à celle de la Fig.11.
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.
Figure 11 - Insertion d'une partie de GRAND-PARENT dans une enveloppe existante.
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).
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.
trelative
qui donne un temps
relatif au début du processus courant. Sa définition est la suivante:
Dans l'exemple précédent, les règles d'un processus
apparaissaient après l'identificateur
(de trelative()
(sub time (fself? `btime)))
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
).
modifie la liste des fils du processus présentement
désigné par
(fself change 'children: '(ENFANT1 ENFANT2 ENFANT3))
fself
qui devient la liste (ENFANT1 ENFANT2
ENFANT3)
. Dans le code ci-dessous, nous avons:
qui signifie qu'à l'initialisation du processus (
first-time:((fself change 'children:
(derive 'following
'(short short s-asc Ef2)
(fself ? phrase-number)))
VOICE1
) sa liste de
fils est changée en un résultat de l'évaluation de la
forme derive que nous verrons ultérieurement.
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.
Ici, la fonction
; 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.
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.
my-pitch
suivant le nom du processus.
children:
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.
transition?
)dans la section first-time:
de VOICE1
.
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:
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 i::= liste-d'identifieurs-1
::= liste-d'identifieurs-2
.
.
.
::= liste-d'identifieurs-n
Soit l'expression
<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>
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:
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.
<identifieur-i>::=expression-i
Dans la règle 1,
<following> ::=derive-following
<rest>::=derive-rest
derive-following
est la fonction à
appeler pour récrire following
sous la forme:
où les crochets signifient que
<rest><list-of-notes> [<following>]
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:
dans lequel
[
start-beat
, end-beat
, class pivot
],
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.
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.
A titre d'exemple supposons que nous choisissions (
<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))
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:
donne:
(derive 'following '(short short asc Ef4) 4)
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.
(Gf4 G4 Bf4 C5 Ef5 C5
long-rest Gf5 Bf4 A4 Ef5
short-rest Gf5
short-rest F5 Ef5 C5
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).
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.
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.
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.
Références
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 .