www.pythoneon.org
Moil-2

La partition selon MOIL-2

René Bastian © Mai 2002

http://www.musiques-rb.org

mise à jour 05.03.03

MOIL ?

MOIL est un acronyme signifiant "Music Oriented Input Language". La version 1 de ce langage tenait compte de certaines caractéristiques du temps musical - sa récursivité ou fractalité - mais un sondage d'opinion effectué sur un échantillon représentatif des musiciens, professionnels ou amateurs, de toutes tendances musicales (expérimentales, baroques, hybrides post-romantiques etc.) nous a appris que de cette caractéristique, les musiciens, ils ne s'en inquiètent guère.

Le langage MOIL-2 est orienté vers la musique destinée à être écoutée et ré-écoutée et ne manifeste donc aucun intérêt pour la génération en temps réel ou autres sauvetages du musicien et du concert cérémonieux : la musique, c'est - comme le dit aussi Glenn Gould - quelque chose qu'on écoute.

Mais après un an de pratique et pour avoir passé le cap de l'utilisation du paquetage Numarray, le temps réel est envisageable même pour des modulations complexes, même si le temps réel n'est pas, pour moi, une priorité. Avoir un accès plus rapide à la complexité du « phénomène musique » est une raison valable pour utiliser les outils informatiques idoines. Mais à la condition que les outils informatiques ne soient pas un écran qui cachent la complexité (cette tentation du cache-cache, héritée des logiciels propriétaires, se propage aussi à l'informatique dite libre).

La version 2 de MOIL est donc plus simple que MOIL-1, mais en sauvegardant néanmoins une grande flexibilité dans la notation du temps et des durées. Cette flexibilité est telle qu'elle n'exclut pas, grâce à Python, la récursivité qui caractérisait MOIL-1. Si on se permet d'avoir un regard un peu plus distant : chaque événement sonore a une structure de dictionnaire ; or Python gère très bien les dictionnaires (ceci au point qu'on peut très facilement se faire en Python des dictionnaires bilingues version-thème en entrant les mots en ordre dispersé, mais ceci n'a évidemment rien à voir avec cela) ; il est donc possible d'imaginer des événements musicaux toplogiquement élastiques, entrant dans la constitution ou dans la modulation d'autres événements musicaux et ceci sur la base de la structure de dictionnaire du langage. Je ne prétends pas que c'est simple, mais cela rend compte de la complexité d'un message musical. En fait, c'est peut-être cela, la musique.

MOIL-2 permet d'écrire des partitions et d'en obtenir une traduction en fichier audio. Cette traduction sera effectuée par un script en Python - évidemment ; le langage Python est agréable à manipuler : si on ne comprend pas l'une des voies possibles, eh bien on en choisit une autre. Python est un langage dont les rudiments sont faciles à apprendre, de sorte que chaque utilisateur peut retailler le costume à sa guise.

Incidemment j'ai appris que « moil » est un mot anglais légèrement obsolète dont le sens est « Ah, quelle galère !», mais cela n'est non plus d'aucune importance, évidemment.

Depuis la date de création (17 mars 2002), le langage s'est peu étoffé à peu (en certains points, il s'est rapproché de MOIL-1) de sorte qu'il a faluu réviser la documentation selon les variations saisonnières et même journalières. Aujourd'hui, le 12 mars 2003, je considère qu'il faut enlever quelques pousses inutiles du langage et, après, le laisser en l'état. Tel qu'il est, il peut être utilisé localement dans un programme Python, sous forme de dictionnaire, mais ce serait s'essayer à des acrobaties insensées que de vouloir chercher à faire entrer le « phénomène musique » dans un langage totalement autonome.


 

Les trois formes de la partition

1. La partition peut être écrite en MOIL-2 exclusivement et être interprétée - mais dans ce cas elle n'exploitera pas toutes les possibilités de Python.
2. La partition peut être écrite en Python et avec des parties en MOIL-2 sous forme de dictionnaire ; au prix de quelques arrangements du programme interprétant on accède à toute la puissance de Python.
3. Utiliser Python tout seul avec un paquetage de calcul rapide (voir ci-dessous)
 

Accélération des calculs

Pour effectuer les calculs en Python, trois voies sont possibles :

Table des matières

Cette doc ne comporte pas de renvois dans tous les sens : quand on cherche un concept, par exemple « durée », on a souvent plus de renseignements en utilisant la fonction « Chercher » du surfeur utilisé (Ctrl-F dans konqueror ou netscape).

  1. Généralités
  2. L'objet musical
  3. La notation du temps
  4. La notation des durées
  5. Les hauteurs
  6. Les amplitudes
  7. Les enveloppes
  8. Filtres fixes
  9. Filtres glissants
  10. Convolution
  11. Normalisation
  12. Fonctions d'applications


Généralités

Dispositions générales

La partition sera organisée en chaînes alpha-numériques ou strings (c'est plus court à écrire que « chaîne alpha-numérique » et entre deux mots il faut choisir le moindre). Un string décrit :

Un string décrit les paramètres d'un o.m. :

Les informations « externes » ou documentaires de la partition telles que

seront placées en commentaire. Si on veut les intégrer dans un fichier formaté (.wav ou .aiff), il faudra rédiger le header à la main.

La grammaire

Règle 1 :
La partition est une suite de strings.
Règle 2 :
Un string est organisé comme un dictionnaire : il comporte une ou plusieurs descriptions, chacune précédée de l'entrée ou mot-clé, séparée de la suivante par un « ; » sauf en fin de ligne :

mot-clé : description ; mot-clé : description ; mot-clé : description
      

Exemple

h : 440.0 ; al : 12456 ; w : "bruit" ; rev : "cathedrale" ; d : 2.236
      

Selon le sujet, on saura si la description du sujet tient en une seule ligne ou en plusieurs lignes. Le code qui doit être exécuté par Python pourra prendre plusieurs lignes ; dans ce cas on profitera de la possiblité de Python d'avaler des strings longs (sur une ou plusieurs lignes - on n'est pas Python pour rien). En général, il suffit d'une ligne pour la description d'un événement musical, si plus d'une ligne est nécessaire : - on peut faire référence à un fichier ou une liste ou une variable de stockage, - on peut prolonger la ligne :

Prolonger une ligne

Il se peut qu'on soit à l'étroit dans une ligne pour décrire un événement (grand nombre de paramètres p.ex.). On convient de considérer comme une seule ligne deux ou plusieurs lignes successives dont le premier caractère est "/".

Exemple

h : Db5; d : 2.36 ; al: 1596
/ e : "spirale" ; modf : "tourbillon"
/ w : "cafeNoir"
Ces trois lignes concernent un seul événement. Autrement dit : elles sont considérées comme une seule ligne « logique » ou un seul string.

Lancer l'interprétation

L'interprétation en informatique veut dire qu'un logiciel (programme, script etc.) lit un fichier et le traduit en données : code MIDI, fichier audio (format aiff ou wav), code MusiXTeX, code CMN etc.

Dans notre cas, l'interprétation est lancée par un appel au logiciel python qui va interpréter le script moil2-interpreter.py qui va lire une description de la partition (p.ex. exemple.moil). En principe, la ligne de commande est donc :

Exemple

python moil2-interpreter.py exemple.moil

On peut rendre le programme moil2-interpreter.py autonome en ajoutant une première ligne telle que :

#!/usr/local/bin/python2.2

Si l'interpréteur Python se trouve ailleurs que /usr/local/bin/, il faut rédiger la ligne autrement.


Les commentaires

Un commentaire débute avec un signe # en début de ligne. (Il est encore prévu d'autres formes de commentaires, mais pour l'instant cette forme peut suffire).

Exemple

# Ceci est un commentaire

Délimitation

Une partition est ce qui se trouve entre le début du fichier et une ligne ne comprenant rien d'autre que le mot "END". Dès que l'interpréteur rencontre ce mot "END", il arrête la lecture du fichier-partition. On verra plus loin que c'est un ordre "compile" qui provoque la traduction en fichier audio ; il n'y a ni contradiction ni double emploi : cela donne la possibilité de générer plusieurs fichiers-audio à partir d'un même fichier-partition.

Remarque : si une ligne commence par "ENDYMION", l'interprétation continue.


 

Particularité

Tant qu'un objet musical ne peut être constitué, l'interpréteur lit la partition ...
... mais dès que tous les éléments existent pour faire un o.m., l'interpréteur le fabriquera.
 

Conséquences

La donnée d'un seul paramètre sur une ligne déclenche le calcul d'un o.m. puisque tous les autres paramètres restent valables.
C'est encore plus facile de faire de la musique répétitive ! mais ce n'était pas mon but.
 

Le mode des paramètres

Qu'un paramètre soit un nombre, une expression, une liste ou autre chose, il a deux modes d'existence : En MOIL-2, il n'y a pas de mode symbolique : inutile de refaire un langage informatique, à moins qu'il ne s'agisse de variables générales qui seront stockées dans un module Python (la dénomination des notes, quelques enveloppes fréquentes).

L'ordre des paramètres

Les possibilités sont :

J'ai opté pour l'ordonnancement par mot-clé : il permet de redéfinir cet ordre pour chaque événement. Et il évite de devoir retenir un ordre précis par coeur - ce qui devient encore plus difficile en cas de changements. Néanmoins, la question mérite d'être discutée

Ordre par mot-clé

C'est l'ordre qu'on écrit le plus facilement : il suffit d'avoir sous les yeux un petit carton avec les principales abréviations. Le langage Python peut facilement disséquer une telle partition, en retirer l'information, et l'insérer dans les procédures de génération des ondes acoustiques. Supposons que nous ayons une ligne de partition qui approvisionne des procédures de fabrication d'ondes. Voici une telle ligne avec des mots-clés :

w: sinus; h: Cs6, fa: "grandFiltre"; al:12569; d:1/4; e:"envX"; mf: "moduleur"
    

Si les événements suivants ne changent que la hauteur et la durée, les lignes consécutives seront par exemple :

h:Dn6+0.256, d:0.258
h:Ef6-0.159, d:0.247
h:Fs6+0.12,  d.0.221
    
et, comme le temps ne change pas, ce seront des événements simultanés.

À la lecture de la partition, on verra facilement ce qui change - par rapport à ce qui reste constant. L'ordre des paramètres est absolument libre. On voit l'intérêt de choisir judicieusement les mots-clés.


Les paramètres

Pour MOIL-2, nous convenons :
  1. qu'un paramètre dans une ligne de partition est annoncé par un indicateur ;
  2. qu'il reste valable - s'il a un sens - tant qu'on ne lui a pas attribué d'autre valeur.
Le tableau ci-après donne des exemples comment les principaux paramètres peuvent être notés, les mots-clés les plus pratiques et la forme de ce qu'ils peuvent représenter : des nombres, des fonctions, des listes, des fichiers etc.
PARAMÈTRECODEEXPRESSION
hauteur tonale ht: symbole
nombre MIDIhm:nombre | expression
fréquencehz:nombre | expression
forme d'ondew: fonction | liste | fichier
unité de tempsu: nombre | expression
duréed:nombre | fonction
décalagedd:nombre | fonction
temps absolut:nombre | fonction | None
temps relatif ou écartdt:nombre | fonction
amplitude linéaireal: nombre | expression
amplitude logarithmiquea: nombre | expression
enveloppee:fonction | liste | fichier
filtrefiltre:liste
filtre glissantfiltreglisse:liste
haut-parleursls:symbole | numéro | expression devenant numéro
L'interpréteur reconnaît les modulations de fréquence, d'amplitude et de phase à leur forme
réverbérationrev:liste | fichier



L'objet musical

Idéalement, un objet musical devrait correspondre à une ligne ou au contenu d'une parenthèse comme en Lisp (les âmes vaillantes ont de toute façon intérêt à s'initier à la Common Music de Bill Schottstaedt). En réalité, d'abord à cause de notre champ visuel limité mais aussi à cause de la non-récursivité structurelle de MOIL-2, il faut louvoyer. Une première prothèse consiste à prolonger les lignes (voir ci-dessus) ; mais cela n'est pas suffisant. Il faut donc qu'on accepter d'introduire des stockages intermédiaires : un objet musical est stocké sous un nom, puis repris pour un ou plusieurs traitements avant d'être inséré dans un collecteur qui permet de réaliser les relations chronologiques entre les objets musicaux constituant une pièce de musique. Ceci est évident -- pour un informaticien ...

Si un signal identique est réutilisé, on peut éviter de le recalculer : pour cela on le stocke dans un dictionnaire où on pourra le relire à volonté.

La deuxième prothèse permet de redéfinir l'unité de temps par rapport à la seconde (voir la notation du temps plus loin). Il faut que cette redéfinition soit faite explicitement pour qu'elle s'applique - tandis que dans MOIL-1 elle se faisait automatiquement par l'enchâssement d'objets musicaux dont la durée était réajustée en fonction de la durée externe. Il faut noter que la Common Music (abr. CM) est intégralement récursive ; une telle implémentation serait possible directement en Python (comme CM n'est qu'un développement de Lisp) ; j'ai essayé : l'enchevêtrement des listes est encore plus délicieux que les parenthèses lispiennes.

Ce collecteur - une classe Python - est un objet très pratique permettant, puisqu'il représente le temps, d'étudier échos, réverbérations, filtres ; c'est-à-dire toutes ces transformations qui font la musique. Le son est inintéressant, musicalement compte ce qu'il a été et ce qu'il devient.


Compilation

Pour qu'une partition écrive une fichier audio, il faut indiquer le nom de ce fichier par une commande :

file : nom_du_fichier_audio
      
et il faut aussi indiquer le format qu'on désire obtenir (16 bits, 24 bits ou 64 bits), [je ne dispose que de convertisseurs 16 bits - comme la plupart d'entre nous], d'où la commande :

compile : 16
      

La notation du temps

Points à traiter :

La notation du temps s'effectue en se référant à une unité de durée qui je fixe à 1 seconde ou la noire à 60 MM. Cette unité de tempo peut être changée à volonté. Le mot-clé est u

Définition de l'unité de temps par rapport à la seconde

u : < nombre | variable | expression >

Par défaut, on admet :

u : 1.0

Les notations du temps et des durées sont indépendantes. Il en résulte qu'il n'est pas nécessaire de noter ni les silences ni les accords : un silence a lieu quand il n'y a pas d'événement (ce qui n'empêche pas de noter des événements d'amplitude nulle) ; un accord ou une agrégation d'événements simultanés a lieu quand il n'y a pas eu de décalage de temps.

Donc on peut noter le temps absolu :

Définition du temps absolu

t : < nombre | variable | expression | None >

Noter que le mot-clé t peut être redéfini par l'utilisateur dans le script Python (il faut évidemment éviter les collisions).

Et on peut noter le temps relatif:

Définition du temps relatif ou écart de temps

dt : < nombre | variable | expression >

Ce temps relatif s'additionne au temps absolu. Il n'est donc pas nécessaire d'avoir une calculette pour situer l'événement suivant (on ne rit pas : les logiciels de musique peuvent être surprenants).

Exemples

t : 3
t : 4*sqrt(2002)
t : temps+9.0/17
dt : 1
dt : noire
dt : noire*1.0/7
dt : (3.0/13 + 7.25/12)

Dans les exemples, sqrt est une fonction du langage Python, temps et noire sont des variables déclarées dans le script de l'interpréteur : en sachant un peu de Python, on peut écrire certaines choses qui ne sont pas impossibles autrement, tout juste un peu plus ardues (conclusion : apprendre un peu de Python est une solution de facilité).

Chaque fois que l'interpréteur rencontre le mot-clé dt, il addtionne son contenu au temps courant ; quand il rencontre le mot-clé t, il affecte cette nouvelle valeur au temps courant. On voit qu'on peut noter une partition en commençant par la fin ou même en boustrophédon (?).

Dans la notation traditionnelle, les durées successives effectuent un balisage du temps ; il en résulte qu'il faut noter explicitement les silences. Le langage MOIL-1 tient compte de cette tradition, tandis qu'en MOIL-2 les silences résultent des intervalles laissés libres entre les événements.

Et quand le temps est du type None ?

En Python les variables ont une valeur ou elles n'en ont pas (ce qui n'est pas la même chose que 0 ou un string de longueur nulle) ; dans pas mal de langages informatiques, le cas où on voudrait qu'une valeur ne soit rien n'est pas traité de manière satisfaisante ; en Python on dispose du type None

L'intérêt de ce type est qu'il peut arriver qu'on veuille stocker une onde non pas dans un fichier définitif, mais dans un fichier intermédiaire qu'on peut relire à volonté, sans être obligé de refaire les calculs.

Mais ce n'est qu'un exemple : attribuer à la variable du temps le type None n'est pas indissolublement lié à un stockage intermédiaire, car ceci empêcherait justement certaines combinaisons - qu'il appartient à l'utilisateur d'imaginer.


La notation des durées

La notation des durées est calquée sur celle du temps : les durées sont évaluées par rapport à l'unité de temps courante (cf. la notation du temps).

Définition de la durée

d : < nombre | variable | expression >

Mais il est possible de corriger localement le temps par l'application d'un décalage qui est surtout important pour noter des arpèges ou des accords dont l'attaque n'est pas absolument simultanée. Le décalage permet de ne pas intervenir sur le temps courant ou en d'autres mots il ne déplace pas le point temporel courant comme le fait dt (le dd ne fait pas double emploi avec le dt).

Je rappelle que les valeurs de Moil-2 sont persistantes. Ceci veut dire que tant qu'une valeur n'est pas changée, elle garde la même valeur. Le calcul du temps ne peut pas respecter ce principe - parce qu'il le respecte. En effet, la notation d'agrégations de sons sur un même point temporel se fait très simplement selon ce principe : le temps ne change pas car on ne note ni écart de temps ni décalage.

Quand des écarts de temps dt sont notés, ces écarts se rajoutent au temps et déplacent le point temporel. Mais dès qu'un écart de temps a été assimilé, sa valeur redevient zéro : dt n'est pas persistant.

Quand l'interpréteur rencontre au moins un décalage dd <> 0.0, le point temporel courant est mis en réserve et reste inchangé pour pouvoir reprendre sa fonction dès que dd=0.0.

Entretemps, le temps courant aura été remplacé par un temps local auquel l'interpréteur aura ajouté le décalage, tant que celui-ci aura été dd <> 0.0.

La distinction entre dt et dd peut sembler inutile ou sophistiquée ; je pense qu'elle correspond à un mode de pensée du temps tel qu'un musicien le pratique très habilement et très intuitivement. L'informatique ne peut bien fonctionner qu'en clarifiant les concepts et en aucun cas la simplicité d'un axe temporel monodimensionnel ne peut correspondre au temps musical.

Définition du décalage

dd : < nombre | variable | expression >

Exemples

d : 3
d : 4*sqrt(17)
d : duree*9.0/17
dd : 0.1
dd : croche/23
dd : noire*0.025
dd : (0.3/13 + 0.25/12)

Dans les exemples, sqrt est une fonction du langage Python, duree, noire et croche sont des variables déclarées dans le script de l'interpréteur.

Définition de la durée en nombre d'ondes

nw : < nombre | variable | expression >

Dans certains cas, il peut sembler utile d'indiquer le nombre d'ondes plutôt que la durée pour disposer d'un train d'ondes cohérent (non implémenté, car pour l'instant nul besoin).


Les hauteurs

Dans l'univers des octets, chaque événement sonore, son sinusoïdal ou bruit, est stocké sous forme d'une suite de nombres. Cette suite est transposable par une technique de balayage - qu'il n'est pas nécessaire de connaître pour le moment (mais plus tard ce ne sera pas inutile). Quand il s'agit d'un son, l'entendement musical lui confère une hauteur bien précise ; quand il s'agit d'un bruit, la perception de hauteur est ou bien moins précise ou même absente. Ce qui caractérise un bruit est qu'il reste le même bruit même si on le transpose ; tandis que s'il change, ce n'est pas tout à fait du bruit.

Donc on ne peut savoir si un événement est du vrai bruit qu'après en avoir entendu une transposition.

Nous avons donc une bonne raison pour dire que, quel que soit l'événement sonore, la hauteur est un de ses constituants. Le devenir de cette hauteur est une autre histoire.

En langage moïlien, nous faisons appel à trois méthodes de noter la hauteur :

Notation symbolique

En musique électronique, cette notation est obsolète : elle ne permet pas d'accéder au continuum sonore sans une débauche de signes. Elle n'est donc utilisable que pour noter des enchaînements dodécaphoniques tempérés - et jusqu'à nouvel ordre, j'en suspens les mises à jour : la notation MIDI est plus efficace. (3.10.02)

Pour que chacune se sente à l'aise, il faut que le moil-interpreter soit adapté au dialecte de chacun (la musique et le chinois, ce sont deux mondes opposés : en musique, chacun écrit différemment ce qu'on entend identiquement ; en chinois on entend différemment un texte identique).

La notation symbolique utilise les lettes de A à G pour noter les 7 degrés diatoniques de la gamme d'Ut majeur et des minuscules pour noter les altérations. En américain, ces minuscules sont : s pour un dièse (sharp), f pour un bémol (flat), n pour un bécarre (natural). On pourra utiliser les variantes européennes. Par exemple d = dièse, b = bémol, n = bécarre/naturel.

La notation des micro-tons est un peu plus complexe : il n'y a pas de convention traditionnelles à respecter. Pour l'instant, le plus simple est d'inclure les rapports arithmétiques ; quand on sait que 5/4 est le rapport des fréquences de la « tierce majeure pure », on sait aussi écrire le « Mi pur » (je ne crois pas que l'idéologie du « pur » ait un sens musical - mais je ne veux empêcher personne de s'y consacrer).

La formulation grammaticale stricte ou presque :

<ton> =><lettre> <altération> <octave> [ .<micro-ton>]<
lettre> = < c | d | e | f | g | a | b >
<altération> = < double-bemol | bemol | becarre | diese | double-diese >
<octave> = <nombre>
<micro-ton> = (*|/) <nombre | variable | expression >

Arrachons-nous aux plaisirs des grammaires formelles pour la pratique : l'implémentation actuelle (c'est aussi un américanisme, mais je trouve que le français le phagocyte très bien et dit la même chose que « réalisation dans le cadre de l'écriture d'un programme en langage informatique »), l'implémentation actuelle donc respecte les habitudes américaines parce qu'on fait ainsi très facilement le pont avec la Common Music Notation (abr. CMN) de Bill Schottstaedt, un excellent logiciel de notation en Common Lisp. Cette CMN européanisée note

Exemples

ht : Cn4 
# c'est le Do de base (4 en notation américaine, 3 en française)
ht : Ef3*5/4 
# une tierce majeur "pure" au-dessus de Mi bémol 3
ht : Fss6/pow(2,1.0/17) 
# un 17e d'octave en-dessous du Fa double-dièse 6
Pour noter des tons qui ne sont pas strictement dans le dodécaphone tempéré, l'écriture devient complexe : je préfère la notation MIDI.

Notation selon MIDI

Définition

hm : < nombre | variable | expression >

Cette notation permet de penser algébriquement les hauteurs. Une fois qu'on a repéré que les C/Do sont des multiples de 12 : 24, 36, 48, 60, 72 ..., qu'on a repéré les D/Ré, E/Mi, F/Fa, G/Sol, A/La, B/Si comme étant chaque fois distants de 2,4,5,7,9 et 11 du C/Do, on saura rapidement par coeur les notes altérées (dièses ou bémols) et, dans la foulée, on saura exprimer l'infinité des hauteurs qui se trouvent en C/Do et Cs/Do# : à chacun son pied.

Exemples

hm : 60
hm : 49.897
hm : 50-0.125
hm : 36+1.0/7

Notation acoustique

La fréquence est un nombre. C'est tout.

Définition

hz : < nombre | variable | expression >

Lors de l'interprétation pythonienne, toutes les indications de hauteur (symboliques ou midiesques) seront traduites en fréquence pour que le calcul final puisse être effectué. Ce qui est simple pour le programme de calcul ne l'est pas pour l'être humain : pour nous il est plus simple de reconnaître les hauteurs quand elles sont exprimées en valeurs logarithmiques, c'est-à-dire symboliques ou au format MIDI.

Notation hybride

La possibilité de multiplier ou de diviser une hauteur musicale anticipe l'un des aspects majeurs de MOIL-2 de pouvoir jongler avec des variables du langage Python : une hauteur, même si elle est exprimée littéralement Cd5, est déjà ce qu'elle va devenir acoustiquement : une fréquence, c'est à dire un nombre auquel on peut appliquer toutes les tortures mathématiques imaginables.

Une expression telle que :

h : cn4+1.05
      
sera « interprétée », mais comme l'utilisateur mélange deux types de notation (la notation symbolique logarithmique et la notation acoustique), elle aura un résultat qui peut ne pas être ce que le compositeur aura souhaité ... ; dans cet exemple, l'interpréteur va calculer la fréqence de cn4 puis additionner 1.05. Il n'y a aucune raison pour empêcher ce genre d'interprétation : la notation MIDI étendue est la plus pratique.


Les amplitudes

(mise à jour du 15.11.02)

La notation des amplitudes - comme pour les hauteurs - débutera par l'indication du niveau initial et sera, si nécessaire, suivi par la forme qui dessinera l'évolution de l'amplitude dans le temps, ce qu'on désigne aussi par « enveloppe ». En expérimentant, on trouvera que amplitude, enveloppe et modulation d'amplitude interréagissent ; il s'agit de modulations externes par opposition aux modulations de fréquence ou de phase qui sont alors des modulations internes.

Deux possibilités d'indiquer l'amplitude :

J'adapte donc la notation logarithmique à mes besoins :

Définition de l'amplitude

a : < nombre | variable | expression >
al : < nombre | variable | expression >

Exemples

a : 0.0
a : 14.269
a : 0.0002
al : 32000
al : 3000*sqrt(17)
al : amplitude*9.0/17

Dans la pratique, l'indication d'un nombre est en général suffisante, mais en remplaçant un nombre par une fonction en t (sous forme de string) on obtient la modulation d'amplitude :

Exemples


al : '32000-100*t'
al : '3000*sqrt(17)+0.5*t**2'
al : '-t*(t-5.0)*8000'


Les enveloppes

Une enveloppe est comme une forme d'onde ; mais une forme d'onde est répétée et d'une durée très courte, tandis qu'une enveloppe a une durée perceptible. Produite mécaniquement, elle peut dépendre d'un grand nombre de facteurs. Mais le propos, ici, n'est pas de simuler un processus vibratoire mais de décrire simplement une enveloppe. Réduisant ainsi le propos, on renvoie à la méta-synthèse cette simulation de processus physiques. Cette méta-synthèse peut aussi se faire en Python, dans un premier temps, cela permet d'imaginer des corrélations entre différents paramètres.

Différentes descriptions sont envisageables :

Pour chaque fragment, il faut donner la durée, la forme du fragment et les paramètres permettant de le calculer :

fragment d'enveloppe : duree, forme, paramètres

Les durées d'un fragment d'enveloppe

La durée peut être adaptée à la durée de l'ensemble de l'objet musical, relative, ou bien elle peut être absolue, c'est-à-dire inchangeable quelle que soit la durée totale. Dans le cas d'une durée relative il suffit d'indiquer un nombre (ou un string qui sera interprété comme une fonction) ; pour le cas d'une durée absolue il faut l'indiquer : le moyen le plus simple en Python est de fournir une liste :

[ 'a', d ]

dans laquelle d représente la durée absolue (un nombre ou, pourquoi pas, un string interprétable).

Résumons l'expression de la durée d'un fragment :

Les formes d'un fragment d'enveloppe

Tant qu'il n'y a qu'un petit nombre de formes (c'est l'état actuel ;-)), la forme peut se déduire du nombre de paramètres, mais comme mon interpréteur n'est pas du tout en un état stable (et comme il n'est pas souhaitable qu'il y en ait en), il faut prévoir des mots-clés pour distinguer les formes possibles.

Voici quelques mots-clés :
Mot-cléSignificationParamètres
ddroitey0, y1
ccubiquey0, pente0, y1, pente1
xarc exponentiely0, y1, courbure

Définition de l'enveloppe

e  : < description conventionnelle comme indiqué ci-dessus > 
el : < liste de couples [cette forme est - déjà - obsolète] > 
ec : < liste de couples ou triplets  [cette forme est aussi obsolète] > 
ef : < nom-de-fichier [je ne l'utilise pas]>
ev : < variable-Python [utilisé en méta-composition] >
eF : < fonction [intégré dans les formes composites donc obsolète aussi] >

Exemples

el : [[0.1,0.0,0.8],[0.1,1.0],[0.25,1.0,0.80],[0.55,0.8,0.0]]
# une enveloppe « normée »
el : [[1.2,36,74],[8.6,74,47],[31.0,47,45.6],[18.1,45.6,12.56],[200,12.56, 0]]
# enveloppe non  « normée » : les points sont arbitraires
# le script Python procèdera d'abord à une mise en forme normée
ev : enveloppeA
# une enveloppe déclarée sous ce nom dans le source Python
eF : enveloppeB(t,d,x,y)
# une fonction d'un module Python qui fabriquera une enveloppe
#en tenant compte de la valeur actuelle des paramètres
ef : "horizon.env"
# le nom du fichier stockant l'enveloppe

La solution la plus pratique et la plus courante :


La description de l'enveloppe composite ec est la même que la forme d'onde cubi-composite (voir plus loin).

D'autres formes peuvent sans doute être imaginées : il est très utile d'utiliser un langage de base pour ne pas se laisser enfermer. On peut aussi tirer parti de ce qu'une enveloppe peut devenir négative : dans ce cas elle provoque l'inversion de la forme d'onde.


Groupements, séries, hasards

ns : < nombre | variable | expression >

Pour définir un nuage de sons, il faut indiquer le nombre de sons constituant le nuage.

Un ensemble de sons microscopiques peut constituer un autre objet musical. Si le nuage est très dense, cela n'est pas très amusant de décrire chaque son. D'où l'idée de ne décrire que les caractères généraux du nuage et d'organiser un flux sériel de nombres qui détermineront le son individuel en respectant les extrémas indiqués (durée max, durée min, hauteur max, hauteur min, etc.) C'est la méthode que Yannis Xenakis a nommée « stochastique », car pour les décrire il a utilisé l'incapacité de l'entendement humain à retrouver la logique mathématique ou formelle qui peut relier des événements. Il a tiré les conclusions de ce que les constructions sérielles n'étaient pas décodables, mais que l'entendement humain leur attribuait une valeur esthétique : les constructions sérielles sont le résultat d'algorithmes simplissimes, leur logique musicale est impénétrable, mais elles ne manquent pas d'être belles ; elles ont justement la beauté du hasard.

Xenakis pensait remettre les pendules à l'heure en remplaçant les algorithmes sériels par des procédures de description globales d'un groupe de sons. Mais ce qu'il fit, ce n'était qu'une autre forme de technique sérielle : il est dificile de changer la nature du hasard.


Les formes d'ondes

Les formes d'onde ressemblent à des enveloppes : elles peuvent être stockées dans des fichiers normés ou étendus, elles peuvent être stockées dans des listes, elles peuvent être calculés par des fonctions. Où est la différence ? Une forme d'onde oscille, elle se replique, elle est microscopique.

Définition de la forme d'onde

w    : < fonction-procédure en Python >
wf16 : < nom-de-fichier en 16 bits >
wf64 : < nom-de-fichier en 64 bits >
wv   : < variable-Python >
wl   : < liste de valeurs >
wc   : < forme cubi-composite >
      

Exemples

w  : sinus
wl : [(0.0,0.0),(0.10,1.0),(0.25,0.80),(0.75,0.30 ),(1.0,0.0)]
# une forme d'onde « normée »
wl : [(1.2, 36),(8.6, 478),(3.0, 45.6), (187.1,12.56), (200, 0)]
# forme d'onde non  « normée » : les points sont incohérents
# le script Python procèdera d'abord à une mise en forme normée
wv : ondeA
# une forme d'onde déclarée sous ce nom dans le source Python
wF : ondeB(t,d,x,y)
# une fonction d'un module Python qui fabriquera une enveloppe
#en tenant compte de la valeur actuelle des paramètres
wf : "oscillation.onde"
# le nom du fichier stockant l'enveloppe


Les formes simples

Les formes simples que l'interpréteur reconnaît sont :

w : sinus
w : rectangle ; rapport : < nombre | expression | algorithme >
w : triangle  ; rapport : < nombre | expression  | algorithme >
w : sinusG    ; coeffs  : < liste  >
      
C'est peu, mais en les combinant on obtient (déjà) une grande palette, d'autant plus que la micro-composition donne des effets sonores riches en harmoniques et d'une très grande variabilité.

La donnée du rapport permet de varier le rapport cyclique en fonction du temps. Comme on utilise un interpréteur, cela permet un très grande variabilité.


La forme cubi-composite

J'appelle forme cubi-composite (abr. fcc) une forme composée de droites et de courbes, les courbes étant des cubiques. Le recours aux cubiques est justifié parce qu'il permet une formulation de la forme d'onde qui est à la fois précis et imaginable, mais il faut assimiler la notion de tangente (ou de pente). Une fcc est composée d'une suite de courbes et de droites.

Pour décrire un fragment de droite, il suffit d'indiquer le point de départ (xi,yi) et le point d'arrivée (xi+1,yi+1)

Pour décrire un fragment de courbe, il faut indiquer en plus la pente au point de départ et la pente au point d'arrivée. Le point de départ est donc décrit par un triplet (xi,yi,pi) et le point d'arrivée par un autre triplet (xi+1,yi+1,pi+1)

Une forme en dent de scie peut être décrite ou bien par un algorithme ou en tant que forme cubi-composite.

On pourra trouver cette description laborieuse, mais ce n'est que l'esthétique de production industrielle qui exige que l'objet manufacturé soit produit au moindre coût. Dans une esthétique libre, le temps ne fait rien à l'affaire.


Les bruits

Beaucoup de bruits ... sont possibles : uniforme, exponentiel, gamma, lognormal, beta, Loi de Pareto, Loi de Weibull. Il faut indiquer les durées et, selon le type de bruit, un ou deux paramètres supplémentaires.

Mais ce ne sont là que les bruits presque banals que l'on trouve dans les logiciels existants : d'autres effets sont possibles ; pour les trouver, il est nécessaire de programmer, autrement, expérimentalement.

L'interpréteur reconnaît - pour le moment - les bruits suivants :

w : bruit; bruit : brut
w : bruit; bruit : [ pareto, coeff1 ]
w : bruit; bruit : [ exponentiel, coeff1 ]
w : bruit; bruit : [ normal, coeff1, coeff2 ]
w : bruit; bruit : [ gauss, coeff1, coeff2 ]
w : bruit; bruit : [ lognormale, coeff1, coeff2 ]
w : bruit; bruit : [ beta, coeff1, coeff2 ]
w : bruit; bruit : [ vonmises, coeff1, coeff2 ]
w : bruit; bruit : [ weibull, coeff1, coeff2 ]
w : bruit; bruit : [ gamma, coeff1, coeff2 ]
      

Les coefficients sont à choisir selon des règles définies dans la doc.


Les formes polynomiales

Ces formes sont basées sur le fait mathématique qu'un polynome (x+a)*(x+b)*(x+c)* ... prend la valeur zéro quand x=-a, -b, -c .... On obtient donc une sorte de forme d'onde.

w : polynome ; polynome : liste des racines du polynome>

Exemple


w : polynome ; polynome : [-8.2, -5.3, -2.2, 0.0, 2.4, 5.6, 9.0]

      

Oscillations

La forme d'onde osc met en route des processus d'intermodulation où cela n'a pas grand sens d'écrire les hauteurs sous code MIDI ou code tonal. C'est pourquoi les hauteurs sont écrites en Hz. La forme accepte des strings interprétables par Python aussi bien pour le calcul des fréquences que le calcul de la courbure (je sais que le terme de "courbure" n'est pas conforme à l'usage mathématique, mais le terme mathématiquement exact n'est pas conforme à l'usage musical - et entre deux maux il faut choisir le moindre).

w : osc ; osc : [ frequence0, frequence1, courbure ]

Exemple

w:osc; 
/osc :['440.0+40*math.sin(2*math.pi*53.7*t)',
/      '68.7-3.1*math.sin(2*math.pi*13.7*t)',
/      '2.0+math.sin(2*math.pi*4.7*t)']

L'exemple ci-dessus produit une suite d'impulsions, chacune ayant sa propre coloration tonale.

Les impulsions

On peut fabriquer des impulsions positives ou négatives (je ne vois pas bien la différence, mais c'est facile ...) ;

w : impuls; impuls : [ '+', haut, bas ]
w : impuls; impuls : [ '-', haut, bas ]
      
haut indique le nombre de niveaux hauts - et inversement. Les bas n'ont de sens que dans un train d'impulsions (dont la grammaire n'est pas encore bien claire).

w : impuls; impuls : [ 'p', dureePositive ]
w : impuls; impuls : [ 'n', dureeNegative ]
w : impuls; impuls : [ 'p', dureePositive, dureeNegative ]
w : impuls; impuls : [ 'n', dureeNegative, dureePositive ]
      
On décrit ainsi un seul ébranlement du haut-parleur, débutant soit par un débattement positif si l'indicateur est p et négatif si l'indicateur est n. On note les durées des plateaux (et non pas le nombre d'échantillons).

La forme micro-composée

Une forme est micro-composée quand, au lieu d'être continue, elle est fait de sons de durée très brève qui, alignés, donnent l'impression de la continuité d'un son dont la fréquence est déterminée par la distance entre les micro-sons.

La notation de ces micro-sons demande donc qu'on décrive le macro-son (cela se fait comme d'habitude) et le micro-son. L'interpréteur reconnaît la forme d'onde w : 'micro' et les caractéristiques de la micro-forme seront fournies dans une liste précédée du mot-clé micro. Une « ligne » de partition ressemble à ça :

w: micro; t: 12.56; hm: 69.55; al:15634; d:2.34978; e: elem05
micro:[w: sinus; hz: 17.85; al: 9000; d:0.00625; e: elem01]
      

Je ne sais pas encore si le choix de la valeur micro et du mot-clé micro est judicieux ; mais on peut facilement changer ces dénominations - si on trouve mieux.

Les enveloppes elem01 et elem05 se réfèrent à un dictionnaire d'enveloppes qui peut être enrichi progressivement ; ainsi on économise du temps d'écriture de partition.

On peut noter que la récursivité de MOIL-1 revient par la petite porte dans MOIL-2.


Modulation de fréquence

En Python pur :
Pour moduler la fréquence, il faut fournir la fonction modulante sous forme de string à la fonction génératrice de la forme d'onde ; Python se charge du reste. Comme le string peut être fourni dans hm: ou hz:, il semble - pour l'instant - inutile de garder des codes spécifiques, mais on verra bien comment ça va évoluer.

En Python avec le paquetage Numarray :
Les choses sont étonnament simples ; voici deux petites fonctions :


def echelleTemps(duree, tempsInit=0.0, sr=44100):
    T=arrayrange(tempsInit, tempsInit+duree, 1.0/sr)
    return T

def modulation(duree, fForme, fFreq, tempsInit=0.0, sr=44100):
    T=echelleTemps(duree, tempsInit, sr)
    F=fFreq(T)*T
    R=fForme(F)
    return R

La fonction echelleTemps fournit un array des valeurs des points temporels. Si la fréquence d'échantillonnage était de 10 Hz, elle fournirait les valeurs 0.0, 0.1, 0.2, 0.3, etc. Comme elle est de 44.1 kHz ou au lieux 192 kHz, c'est un peu serré en calcul mental.

La fonction modulation fait d'abord établir la suite des points temporels.

Puis elle calcule l'évolution de la fréquence en fonction du temps : F contient la suite des fréquences, une pour chaque point temporel, multipliées par la valeur du point temporel, car en modulation de fréquence on a bien fonction(2*pi*freq*t).

Enfin on applique la suite des fréquences*temps calculées précédemment à la forme d'onde par

    R=fForme(F)
      
et on a le résultat - en quatre lignes de code-source. Les fonctions fFreq et fForme peuvent être des fonctions internes à Python-Numarray ou des fonctions définies par l'utilisateur. Les calculs sont effctués vraiment très rapidement : on peut envisager de les faire faire en temps réel.


Modulation d'amplitude

Idem pour l'amplitude : l'interprète est là pour interpréter. La fonction de modulation est à fournir dans al:.

Convolution

La convolution n'est qu'un cas particulier de toutes les manières dont on eut faire des combinaisons entre deux fichiers de sons (deux fichiers de nombres). Si on consulte les ouvrages traitant du traitement du signal, la chose est généralement expliquée de façon fort embarassée - qui elle-même ne peut s'expliquer qu'en supposant que les auteurs de ces ouvrages souffrent de raideur cervicale. Voici comment et pourquoi.

D'abord il est nécessaire de relire la licence GPL ; si vous acceptez cette licence, vous pouvez lire ce qui suit et en exécuter les algorithmes à vos risques et périls, vous pouvez les changer, les varier et les transmettre à d'autres éventuels lecteurs, gratuitement ou contre rémunération, évidemment à vos risques et périls.

Si vous n'acceptez pas les termes de la licence GPL, vous pouvez lire ce qui suit, mais à vos risques et périls.

Commençons par inventer deux listes en Python :

A=[1,3,2,5,1,0]
B=[2,4,1,0,2,3]
et déduisons en un tableau dans lequel à l'intersection d'une colonne i et d'une ligne j, on trouve le produit A[i] * B[j] :
  | 1  3  2  5  1  0 
---------------------
2 | 2  6  4 10  2  0
4 | 4  12 8 20  4  0 
1 | 1  3  2  5  1  0
0 | 0  0  0  0  0  0
2 | 2  6  4 10  2  0
3 | 3  9  6 15  3  0 
      
Il suffit maintenant d'incliner sans forcer la tête de 45 degrés vers la gauche et de lire le tableau en travers ; on lit successivement :
2
4 6
1 12 4
0 3 8 10
2 0 2 20 2
3 6 0 5 4 0
9 4 0 1 0
6 10 0 0
15 2 0
3 0
0
      

Maintenant, vous faites faire à votre tête un mouvement inverse de rotation de 45 degrés vers la droite. Normalement votre tête devrait se retrouver à la verticale. En cas de déviation, il est vivement conseillé de consulter le mode d'emploi et les conditions de garantie ; en général ces conditions précisent qu'il ne faut procéder à aucun démontage sous peine de perdre le bénéfice de ladite garantie. Souvent il suffit de relire la licence GPL après avoir effectué une convolution entre l'hémisphère gauche du cerveau et la colonne vertébrale. Pour faire une convolution, cliquez ici.

Si vous êtes arrivés ici, il n'y a plus guère de difficultés. On additionne les nombres par ligne et on obtient :

2
10
17 
21 
26
18
14
16
17
3
0

Et voilà, c'est tout et c'est ça la convolution. Voici un petit script en Python qui convolute deux arrays. Il est non optimisé pour être plus facile à comprendre :

import array

def convolveList(A,B):
    jmin=0;
    C=[]
    for imax in range(len(A)+len(B)):
        i=0; j=imax; y=0.0
        while (i<=imax):
            if (0<=i<len(A)) and (0<=j<len(B)):
                y=y+A[i]*B[j]
            i=i+1; j=j-1
        C.append(y)
        jmin=jmin+1
    return C    

if __name__=="__main__":
    A=[1,3,2,5,1,0]
    B=[2,4,1,0,2,3]
    C=convolveList(A,B)
    print C
	       
D'accord ? Cette méthode est bonne pour comprendre de quoi il s'agit. Quelqu'un voulut appliquer cette méthode pour convolutionner 1 seconde de signal avec une seconde d'un autre signal. Quinze jour après ... Non, je n'ai pas le temps maintenant, je vous raconterai tout quand la convolution sera terminée !

Normalisation

Pour être utilisables et ré-utilisables, les formes d'ondes et les enveloppes sont stockées sous une forme que j'appelle « normée ».


Fonctions d'applications

Une fonction d'application est la fonction qui calcule l'événement et l'écrit sur le disque dur ou cd cd en tenant compte des informations données par la partition. On aura le choix entre différentes fonctions d'applications ; l'utilisateur peut écrire les siennes propres - et les mettre à disposition des autres. Une fonction d'application, en Python, est ou bien dans le script-source ou elle se trouve dans un fichier en Python (un module) qu'il faut charger par une instruction import. On indique donc un identificateur pour un module déjà connu ou string pour un nom de fichier qui n'a pas encore été déclaré.

Définition

F : < identificateur | string >


Les traitements

Le MOIL-2 décrit des événements musicaux, produit ces événements et les insère dans un fichier, d'abord en un format intermédiaire de 64 bits par échantillon ; ce format en 64 bits est ensuite ramené à 16 bits ou 24 bits pour pouvoir être écouté.

Une production au format intermédiaire est nécessaire chaque fois qu'on veut appliquer un traitement - filtrage ou réverbération - non seulement à un événement musical unique, mais à toute une séquence. Si on ne dispose pas assez de mémoire RAM, il est conseillé de procéder à un stockage dans un fichier. En effet, la gestion de la mémoire RAM par le système d'exploitation peut amener un délestage de la RAM dans des fichiers selon la technique du swap. Il se peut que le temps mis par cette gestion automatique soit plus long que pour un stockage manuel dans un fichier.

En raison des traitements à effectuer sur des suites d'événements, la partition finale peut n'être qu'une suite d'insertions de fichiers dans le fichier audio final ; ceci pourra être écrit en donnant simplement le temps ou l'écart et le nom du fichier à insérer :

t : < nombre > ; W : < nom-du-fichier >

La spatialisation

On peut répartir une source sonore entre deux haut-parleurs pour simuler un front d'orchestre ou une répartition spatiale quadriphonique ou multiphonique comme le proposent les chaînes de home-cinéma.

On peut aussi concevoir un orchestre de haut-parleurs : dans ce cas, l'événement musical est envoyé à un ou plusieurs haut-parleurs simultanément selon des répartitions variables dans le temps (des enveloppes spécifiquement spatiales). Le but n'est donc pas forcément de produire un disque compact, mais un ou plusieurs flux musicaux indépendamment des normes industrielles. La composition des flux musicaux est le vrai problème ; de combien de pistes on dispose sur un enregistreur multi-pistes n'est pas une question négligeable, mais ce n'est pas un problème musical.

Pratiquement : si un événement est envoyé à un seul haut-parleur, il suffit d'indiquer le nom qu'on lui aura attribué (un identificateur) ; s'il est envoyé à plusieurs haut-parleurs simultanément et à des intensités différentes et variables, il faut aussi indiquer les fonctions de répartition.

Les sources

Pour que l'interpréteur puisse fonctionner, il faut lui indiquer dès la première ligne combien de haut-parleurs il faut approvisionner :


nrsources : < nombre >
      

Ce nombre étant connu, l'interpréteur peut mettre en place autant de collecteurs.

Une autre possibilité est de donner un nom à chaque source un nom qui sera utilisé lors de la répartition des signaux - mais à mon avis le confort n'est pas tant amélioré par ce choix :


nrsources : < liste de strings >
      

La répartition des signaux

Un événement étant calculé, il sera réparti sur une ou plusieurs sources. Dans le cas général, cette répartition est faite selon une enveloppe fonction du temps. Il faut donc autant de fonctions de répartitions que de sources.


ls : < numéro > | < identificateur > '='  < nombre | expression | fonction | fichier | liste | ... >


Ces fonctions de répartition font d'une certaine manière double emploi avec les enveloppes ; je verrai ultérieurement s'il est utile d'expulser les enveloppes.


Les filtres

En technique digitale, en fin de compte, les filtres ne sont qu'une suite de nombres (qui s'en étonne ?) : un filtre est une suite de coefficients qui sont appliqués à la suite de nombres représentant le signal (et il en résulte évidemment une autre suite de nombres ...). Les coefficients sont appliqués les uns aux échantillons entrant dans le filtre, les autres aux échantillons sortants.

Mais ceux qui ont pratiqué les techniques analogiques savent qu'on décrit plus facilement un filtre par l'effet qu'il produit : filtre passe-bas, filtre de bande etc. avec leurs caractéristiques (type de filtre, fréquence de coupure, amortissement etc). Il appartient alors au logiciel à partir des caractéristiques du filtre qu'on souhaite de calculer la suite de coefficients. Plus strictes sont les contraintes, plus laborieux est le calcul à effectuer.

Si ces caractéristiques doivent varier dans le temps (imaginez un filtre analogique plongeant de l'aigu dans le grave), il faut indiquer les caractéristiques sous forme de courbes (comme une enveloppe ou une forme d'onde : la musique électronique est une certaine manière d'utiliser les restes). Python, langage interprété, simplifie la tâche : il peut tout faire, il faut juste le lui demander en y mettant les formes. Et quand il met vraiment beaucoup de temps, c'est vraisemblablement l'algorithme ou la manière d'enchaîner les tâches qui mérite d'être revue.

Résumons : les filtres peuvent être décrits de deux manières, ou bien en donnant l'effet à produire avant calcul des coefficients finaux du filtre ; ou bien en donnant la liste des coefficients ou leur référence (identificateur de liste, nom de fichier).

Comme si ce n'est pas assez compliqué, il y a encore autre chose : notre structure jusqu'à présent est de considérer le flux musical comme une suite d'évéements. Le filtrage peut aussi s'appliquer à une suite d'événements, à un flux dans le flux, donc une sorte de sous-flux (cela donne une idée de la fractalité de l'objet musical).

Pour contourner cet écueil, deux méthodes dans l'immédiat :

Définitions possibles

fl : < liste >
fv : < variable Python >
ff : < nom de fichier >

Le mot-clé fl introduit une liste de nombres ou d'expressions qui sont les coefficients d'un filtre numérique - les expressions sont à évaluer en fonction de variables valables du script Python.

Le mot-clé fv introduit un identificateur qui est la référence d'une liste de coefficients (ou d'expressions) du script Python.

Le mot-clé ff introduit un identificateur qui est le nom d'un fichier stockant une liste.

Les filtres sont d'ailleurs un domaine où il ne faut pas vouloir tout calculer. Après avoir lamentablement erré en solitaire dans le dédale numérique du calcul des filtres en me demandant comment déterminer ou calculer le nombre de coefficients nécessaires, Raymond Séroul me mit sur la bonne voie en me disant « Tu n'as qu'à essayer ».

La manière la plus pratique - et que j'utilise - la voici :

Définition


filtre : < liste >

La liste donne :
  1. le type de filtre : passe-bas, passe-haut, passe-bande, coupe-bande
  2. le nombre de coefficients souhaité
  3. la fréquence de coupure pour les filtres passe-bas ou passe-haut
  4. les deux fréquences de limite de bande pour les filtres passe-bande et coupe-bande

Exemples


dt : 1.5; w : bruit; bruit : 'brut'; al: 20000; d : 1.2; filtre :['pbas',50, 160.0]
dt : 1.5; w : bruit; bruit : 'brut'; al: 20000; d : 1.2; filtre :['pbande',50, 2400.0, 3600.0]

      

Filter avec 50 coefficients peut sembler énorme - quand on voit que les exemples d'école utilisent en général 1, 2 ou 3 coefficients ; selon mon expérience, un effet musicalement pertinent n'est obtenu qu'en laissant l'ordinateur mouliner.

Nous avons donc des filtres fixes, mais ce qui nous intéresse ce sont des filtres glissants - des filtres glissants linéairement, c'est assez facile à faire, mais pour l'instant je ne vois pas comment faire des évolutions circonvoluées ...


Filtres glissants

Le projet

Pour que l'interpréteur se rende compte immédiatement du boulot qui l'attend, j'avais l'intention de lui signaler un filtre glissant par l'injonction que voici :

filtreglisse : [ filtre_initial, filtre_final ]

donc une liste composée de deux descriptions de filtres ; pour que cela fonctionne, je demanderais pour chaque filtre le même nombre de coefficients (bien que j'aie écrit une sorte de parachute, mais je n'ai aucune étude de ce genre d'évolution sous la main : on est là en musique expérimentale). Les autres éléments (type de filtre, fréquences de coupure) peuvent être combinées sans contrainte. L'interpréteur va calculer les coefficients du filtre initial, puis ceux du filtre final et prendre ses dispositions pour pouvoir interpoler convenablement.

La réalisation

Elle a été simplifiée parce que Python est très coopératif : au lieu de donner une suite de descriptions de filtres statiques que l'interpréteur joindra en calculant les transitions de l'un à l'autre, on tire parti de ce que Python sait évaluer des fonctions. La syntaxe d'un filtre glissant est dans ce cas la même qu'un filtre statique, sauf que les données : nombre de coefficients et fréquences de coupures sont données par des expressions où t exprime le temps, par exemple :

'(t*(t-1.1)*(t-2.3)*(t-3.1)*(t-4.7)*(t-5.9)*(t-6.1)+100)*10'
    
une fonction qui dessine une ondulation qu'on peut visualiser avant l'emploi par gnuplot et, après avoir interprétation, écouter et examiner avec snd.


Échos et réverbérations

Les échos sont le stade infantile de la réverbération. Sans nous compliquer la vie, voilà comment on peut manipuler les échos. Plusieurs réflexions sont possibles, chacune pouvant avoir un autre temps de retard et un autre amortissement. La description d'un écho multiple est donc d'abord donné par une liste de couples (temps de retard, amortissement). Mais il se peut qu'on veuille appliquer à ce signal échoé un filtrage ; le couple devient un ménage à trois : (temps de retard, amortissement, filtre). Il faut noter que l'amortissement et le filtre sont des traitements qui sont ou bien fixes ou bien variables dans le temps (ou selon l'environnement : c'est au compositeur de décider ; Python sera son fidèle serviteur). Voilà ce qui est implémenté : Reste à voir comment on écrit une suite d'échos ou une réverb en MOIL-2 :

echo : [[0.125, 0.6],[0.25, 0.3], [0.35,0.15],[0.48, 0.10],[0.60,0.08],[0.75, 0.04] ]
rev :  [50,0.008,0.002, 0.8]


Il reste à régler le problème des « premières réflexions ».

Le dictionnaire

Pour éviter de devoir recalculer certains signaux, on peut les stocker dans un dictionnaire.

L'inclusion dans le dictionnaire se fait par la commande

 
indico : mot-clé

Cette commande ne détruit pas le signal.

La lecture d'un signal stocké dans le dictionnaire se fait ainsi :

 
exdico : mot-clé

Cette commande détruit le signal courant qui est remplacé par celui du dictionnaire.

On peut aussi annuler une clé du dictionnaire :

 
deldico : mot-clé

Cette commande s'utilise s'il faut être économe de mémoire.


Stockage et lecture

Définition


stock : < nom_fichier >
lecture : < nom_fichier >

La commande stock provoque l'enregistrement immédiat de l'objet musical en l'état au format 64 bits dans un fichier nom_fichier.

La commande lecture provoque la lecture du fichier indiqué ; l'objet musical lu sera inséré au point temporel actuel.


Visualisation

Il y a heureusement plusieurs modes de visualisation possibles en tenant compte de ce que les logiciels d'écoute - mxv ou snd - n'ont pas du tout besoin d'un format spécifié : ces logiciels d'écoute (du moins avec Linux) sont capables de faire entendre et faire voir des fichiers bruts.

La documentation pour mxv peut se trouver sur le site de l'»Institut für Theoretische Nachrichtentechnik und Informationsverarbeitung« à Hannover, dont l'adresse est http://www.tnt.uni-hannover.de/soft/audio/.

La doc de snd vient avec le logiciel (c'est quand même la meilleure idée !).

Dans certains cas j'utilise un script Python, plotter.py, qui requiert deux arguments :

  1. le nom du module où se trouve la fonction génératrice de la forme d'onde ;
  2. le nom de la fonction génératrice avec les paramètres nécessaires pour que la fonction puisse agir.
Le script met en forme ces arguments et appelle gnuplot qui affiche les données.

Ce script m'a été utile pour visualiser chaque fois une fraction de secondes des différentes sortes de bruits qu'il est possible d'obtenir avec Python (bruit normal, bruit à la Weibull, bruit à la Pareto, etc.)



Variables et structures

J'avais d'abord prévu d'étendre MOIL-2 à la gestion des noms de variables, mais j'abandonne cette idée qui nous rapprocherait dangereusement d'un langage informatique autonome - ce qui est inutile.

La même remarque vaut pour les structures : celle de Python sont suffisantes pour faire face à toutes les nécessités de la composition. On peut aller très loin dans le domaine de la composition algorithmique - c'est une affaire de goût. En apprenant quelques rudiments de Python, mais vraiment que les choses les plus élémentaires, faire une pièce de musique électronique devient aussi amusant qu'une boîte de construction pour gamins.


www.pythoneon.org

Moil-2
René Bastian
Last modified: Sat Mar 15 21:44:58 CET 2003