Format MIDI : composez en C !

GNU/Linux Magazine n° 196 | septembre 2016 | Vincent Magnin
Creative Commons
  • Actuellement 0 sur 5 étoiles
0
Merci d'avoir participé !
Vous avez déjà noté cette page, vous ne pouvez la noter qu'une fois !
Votre note a été changée, merci de votre participation !
Avec le format MIDI, vous pouvez mêler les plaisirs de la composition musicale et de la programmation. Nous allons nous intéresser à ce format binaire et écrire un court programme en C permettant de l'exploiter.

 

Note

Un fichier MIDI est un fichier binaire composé d'événements musicaux codés par des séries d'octets. Nous allons dans ce premier article écrire un petit programme en C pour créer ce type de fichier. Dans un second article, nous utiliserons ce programme pour explorer la musique et ses relations avec les mathématiques ou l'algorithmique.

Nous avions commencé à explorer l'univers MIDI dans le hors-série n°29 « MUSIQUE & SON » de Linux Pratique [1]. MIDI (Musical Instrument Digital Interface), lancé en 1983, définit un protocole de communication entre instruments de musique où les informations sont transmises à une vitesse de 31250 bauds. En 1989, s'y ajoute le format de fichier SMF (standard MIDI file) [2] qui permet de stocker des séquences d'instructions MIDI dans des fichiers très compacts ayant pour extension .mid ou .midi. Il s'agit d'un format binaire dont nous allons explorer les bases en écrivant un programme en C permettant de créer de tels fichiers.

1. Le programme

Vous pouvez récupérer les fichiers midi.c et demo.c sur le dépôt GitHub du magazine. Le premier fichier contient toutes les procédures et fonctions qui nous seront utiles. Le second fichier est une courte démonstration des possibilités du format MIDI qui contient le programme principal et les procédures créant les pistes. Nous n'allons pas implémenter toutes les fonctionnalités du format MIDI mais celles qui sont essentielles pour créer un fichier fonctionnel. Une fois compris les grands principes, vous n'aurez pas de difficultés à ajouter d'autres instructions MIDI en vous basant sur les pages de la documentation en ligne qui vous seront données en références.

Après compilation avec gcc et exécution, la commande hexdump permet de visualiser facilement le début du fichier MIDI généré :

$ gcc demo.c
$ ./a.out

$ hexdump demo.mid -C | head
00000000  4d 54 68 64 00 00 00 06  00 01 00 02 00 80 4d 54  |MThd..........MT|
00000010  72 6b 00 00 00 0b 00 ff  51 03 07 a1 20 00 ff 2f  |rk......Q... ../|
00000020  00 4d 54 72 6b 00 00 06  6f 00 c0 5a 00 90 3c 40  |.MTrk...o..Z..<@|
00000030  40 80 3c 00 00 90 3d 40  40 80 3d 00 00 90 3e 40  |@.<...=@@.=...>@|
00000040  40 80 3e 00 00 90 3f 40  40 80 3f 00 00 90 40 40  |@.>...?@@.?...@@|
00000050  40 80 40 00 00 90 41 40  40 80 41 00 00 90 42 40  |@.@...A@@.A...B@|
00000060  40 80 42 00 00 90 43 40  40 80 43 00 00 90 44 40  |@.B...C@@.C...D@|
00000070  40 80 44 00 00 90 45 40  40 80 45 00 00 90 46 40  |@.D...E@@.E...F@|
00000080  40 80 46 00 00 90 47 40  40 80 47 00 00 90 48 40  |@.F...G@@.G...H@|
00000090  40 80 48 00 00 c0 00 00  90 45 40 81 00 80 45 00  |@.H......E@...E.|

Au fil de cet article, vous pourrez revenir à ce dump et y repérer les valeurs de certains octets.

2. Programme principal

Un fichier MIDI est organisé en chunks (terme que je traduirai par blocs), chacun commençant par un tag de quatre caractères ASCII, suivi d'un entier sur quatre octets codant la taille en octets des données contenues à sa suite dans ce bloc. Il existe deux types de bloc dans un fichier MIDI : le bloc d'en-tête (tag MThd) et les blocs des pistes (tag MTrk). Le programme principal, situé dans le fichier demo.c, va tout d'abord ouvrir en écriture un fichier binaire demo.mid, puis appeler la procédure qui écriera l'en-tête MIDI, et enfin les procédures qui écrieront chaque piste (ici deux), avant de finalement fermer le fichier :

27: int main(void) {

28:     FILE *fichier_midi = fopen("demo.mid", "wb") ;

29:     MIDI_ecrire_en_tete(fichier_midi, 1, 2, noire) ;

30:     ecrire_piste1(fichier_midi) ;

31:     ecrire_piste2(fichier_midi) ;

32:     fclose(fichier_midi) ;  

33: }

3. En-tête MIDI

Les octets à écrire dans le fichier sont stockés dans un tableau de variables du type unsigned char. En effet, d'après la norme du langage C [3], une variable de ce type occupe exactement un byte, unité définie comme composée de CHAR_BIT bits. La valeur de la constante CHAR_BIT vaut bien sûr 8 en général, mais la norme prévoit qu'elle puisse être plus grande. À noter que la norme ne définit pas la taille des différents types d'entiers, mais seulement une taille minimale : un short doit occuper au minimum deux octets et un long quatre octets. Dans ce programme, par souci de rigueur nous utiliserons donc des variables de type  unsigned char,  unsigned short ou unsigned long en fonction du nombre d'octets exigés par la norme MIDI. Sur un microprocesseur 32 bits, un long occupera quatre octets et huit octets sur un 64 bits, mais cela n'a pas d'importance car nous n'utiliserons à chaque fois que le nombre d'octets de poids faibles nécessaires.

Le bloc d'en-tête commence par quatre octets formant la chaîne Mthd, soit 0x4d546864 en hexadécimal (ligne 48) :

 42: void MIDI_ecrire_en_tete(FILE *fichier, unsigned char SMF, unsigned short pistes, unsigned short nbdiv) {  

 43:     if ((SMF == 0) && (pistes > 1)) {

 44:         printf("ERREUR : une seule piste possible en SMF 0 ! \n") ;

 45:         exit(EXIT_FAILURE) ;

 46:     }

 47:

 48:     unsigned char octets[14] = {0x4d, 0x54, 0x68, 0x64, 0x00, 0x00, 0x00, 0x06} ;

 49:     octets[8]  = 0 ;

 50:     octets[9]  = SMF ;

 51:     octets[10] = pistes >> 8 ;

 52:     octets[11] = pistes ;  

 53:     octets[12] = nbdiv  >> 8 ;

 54:     octets[13] = nbdiv ;     // Nombre de divisions de la noire

 55:     fwrite(&octets, 14, 1, fichier) ;

 56: }

Les quatre octets suivant la chaîne Mthd codent la longueur en octets de la suite de l'en-tête, valeur qui dans la pratique vaut toujours 6. Les deux octets suivants (8 et 9) codent le type de fichier (paramètre SMF). Il n'existe que trois types de fichiers :

- SMF=0 : il n'y a qu'une seule piste (plage) dans le fichier. D'où le test en début de procédure qui arrête le programme si vous vous êtes trompés.

- SMF=1 : le fichier contient plusieurs pistes qui seront jouées simultanément. On s'en servira généralement pour séparer les différentes voies d'une partition musicale. Le format 1 est le plus utilisé et le plus pratique, c'est donc celui que nous utiliserons dans cet article.

- SMF=2 : le fichier contient plusieurs pistes qui seront jouées séquentiellement. Chaque piste pourra donc correspondre à un morceau de musique différent, comme les plages d'un album. Ce format est très peu utilisé.

Le nombre de pistes est codé sur deux octets et peut donc aller jusque 65535. La valeur de la variable pistes doit être recopiée dans les octets 10 et 11 de l'en-tête. L'octet de poids faible est directement recopié dans la variable octets[11], la variable de type short étant implicitement convertie en une variable de type char. Pour récupérer l'octet de poids fort de notre valeur, nous décalons de 8 bits vers la droite ses bits, avant conversion implicite en char. Pour être plus explicite, on pourrait aussi écrire ces lignes ainsi :

    octets[10] = (char) (pistes >> 8) & 0xFF ;

    octets[11] = (char) pistes & 0xFF ;

Les deux derniers octets de l'en-tête MIDI codent le nombre de divisions de la noire. Deux codages existent : si le bit le plus à gauche vaut 1, on utilise le codage temporel SMPTE [4] utilisé dans l'audiovisuel, sinon on définit le nombre de tics correspondant à la noire (soit au maximum 32767 puisqu'il ne reste que 15 bits pour coder la valeur). C'est cette dernière méthode que nous allons utiliser. Attention de ne pas confondre cette grandeur avec le tempo (nombre de noires par minute), qui sera défini plus loin. Il s'agit ici de définir la résolution temporelle dans notre fichier. On choisira de préférence une puissance de deux, par exemple 128, la croche valant une demi-noire, la double croche un quart, etc.

Ligne 55, l'instruction fwrite() écrit dans notre fichier 14 blocs d'un octet (d'où le 1), stockés en mémoire à partir de l'adresse &octets de notre tableau. Vous pouvez vérifier que l'en-tête a bien été correctement écrit en utilisant la commande suivante :

$ file demo.mid

demo.mid: Standard MIDI data (format 1) using 2 tracks at 1/128

4. Structure d'une piste

Après l'en-tête du fichier MIDI vient l'en-tête de la première piste puis ses données, suivies de l'en-tête de la deuxième piste et ses données, etc. L'en-tête d'une piste commence par quatre octets codant la chaîne Mthd, soit 0x4d54726b en hexadécimal. Les quatre octets suivants codent le nombre d'octets composant la suite de la piste. Problème : nous ne connaissons pas forcément ce nombre avant d'avoir écrit les données ! Nous écrivons donc pour l'instant zéro à cet endroit et la fonction  MIDI_ecrire_en_tete_piste() renvoie grâce à la fonction ftell() la position que nous avons atteinte dans le fichier juste après l'écriture de ce zéro :

 99: unsigned long MIDI_ecrire_en_tete_piste(FILE *fichier) {

100:     unsigned char octets[8] = {0x4d, 0x54, 0x72, 0x6b, 0x00, 0x00, 0x00, 0x00} ;

101:     fwrite(&octets, 8, 1, fichier) ;

102:     return ftell(fichier) ;

103: }

L'en-tête est immédiatement suivi des données. Pour indiquer la fin de la piste, il faudra par convention écrire la valeur 0xFF2F00 grâce à cette procédure :

105: void MIDI_fin_de_la_piste(FILE *fichier) {

106:     MIDI_delta_time(fichier, 0) ;

107:     unsigned char octets[3] = {0xFF, 0x2F, 0x00} ;

108:     fwrite(&octets, 3, 1, fichier) ;

109: }

Nous pourrons alors revenir dans l'en-tête de la piste afin d'y écrire le nombre d'octets écrits après l'en-tête de la piste grâce à la procédure ecrire_taille_finale_piste. Lors de l'écriture d'une piste, nous stockons dans une variable marque la valeur renvoyée par la fonction MIDI_ecrire_en_tete_piste(). Nous soustrayons donc à la position actuelle renvoyée par ftell(fichier) la valeur qui est dans marque, puis nous soustrayons 4 pour nous retrouver juste après le tag Mthd. Ligne 114, la fonction fseek() avec pour dernier paramètre SEEK_SET permet de « rembobiner » à l'endroit voulu, où nous écrivons ensuite les quatre octets codant la taille désormais connue (en utilisant des décalages de 24, 16, 8 et   bits suivant le même principe que précédemment vu). Ligne 120, la fonction fseek() avec pour dernier paramètre SEEK_END permet de revenir à la fin actuelle du fichier afin de pouvoir y écrire éventuellement d'autres pistes :

111: void ecrire_taille_finale_piste(FILE *fichier, unsigned long marque) {

112:     unsigned char octets[4] ;

113:     unsigned long taille = ftell(fichier) - marque ;

114:     fseek(fichier, marque-4, SEEK_SET) ;    // On rembobine

115:     octets[0] = taille >> 24 ;

116:     octets[1] = taille >> 16 ;

117:     octets[2] = taille >> 8 ;

118:     octets[3] = taille ;

119:     fwrite(&octets, 4, 1, fichier) ;

120:     fseek(fichier, 0, SEEK_END) ;

121: }

5. Piste des métadonnées

Si vous utilisez le format SMF 1, la première piste doit être réservée aux méta-données (en SMF 0, elles seront écrites au début de l'unique piste). Dans le cadre de cet article, nous restreindrons ces méta-données au strict nécessaire, c'est-à-dire au tempo qui est codé sous forme du nombre de micro-secondes correspondant à la durée d'une noire. Nous prenons ici une valeur de 500000 micro-secondes (ligne 5 du fichier demo.c), donc une demi-seconde, ce qui est la valeur MIDI par défaut et correspond à un tempo de 120 (nombre de noires par minute) :

 1: #include "midi.c"

 2:

 3: void ecrire_piste1(FILE *fichier) {   

 4:     unsigned long marque = MIDI_ecrire_en_tete_piste(fichier) ;

 5:     MIDI_tempo(fichier, 500000) ;   

 6:     MIDI_fin_de_la_piste(fichier) ;

 7:     ecrire_taille_finale_piste(fichier, marque) ;    

 8: }

Toutes les métadonnées commencent par un octet 0xFF. La valeur du tempo est ainsi codée sur trois octets précédés de la valeur 0xFF5103 :

 58: void MIDI_tempo(FILE *fichier, unsigned long duree) {

 59:     MIDI_delta_time(fichier, 0) ;

 60:     unsigned char octets[6] = {0xFF, 0x51, 0x03} ;

 61:     octets[3] = duree >> 16 ;

 62:     octets[4] = duree >> 8 ;

 63:     octets[5] = duree ;

 64:     fwrite(&octets, 6, 1, fichier) ;

 65: }

Une noire durera donc entre une microseconde et 256 au cube microsecondes, soit environ 16,7 secondes. Voilà qui est une plage largement suffisante !

D'autres méta-données peuvent être écrites comme la mesure (4/4 par défaut), l'armure de la clé, le nom de la piste, les paroles, etc.

6. Événements MIDI

Les données d'une piste MIDI sont composées d'une suite d'événements MIDI précédé systématiquement d'un délai (éventuellement nul) appelé delta-time, durée exprimée en tics devant s'écouler depuis le précédent événement MIDI :

 38: void MIDI_delta_time(FILE *fichier, unsigned long duree) {

 39:     ecrire_variable_length_quantity(fichier, duree) ;

 40: }

Il convient pour cela de se référer au nombre de tics composant une noire, valeur qui a été définie dans l'en-tête du fichier.

Afin d'économiser des octets (n'oublions pas que MIDI est une norme des années 80 où les informations circulent entre instruments à environs 31250 bauds), les delta-times sont, selon leur valeur, stockés sur un à quatre octets suivant un format nommé variable length quantity [5].

6.1 Variable Length Quantity

Dans ce codage des entiers, les chiffres binaires sont groupés par sept, le bit le plus significatif de l'octet valant 1 pour indiquer qu'il y a encore des octets à lire et   s'il s'agit du dernier octet. On peut ainsi représenter des entiers arbitrairement grands tout en limitant l'espace mémoire utilisé. Le tableau suivant présente quelques exemples de valeurs codées dans ce format.

Décimal Hexadécimal Binaire VLQ (binaire) VLQ (hexadécimal)
  0x00 00000000 00000000 0x00
127 0x7F 01111111 01111111 0x7F
128 0x80 10000000 10000001 00000000 0x8100
16383 0x3FFF 00111111 11111111 11111111 01111111 0xFF7F
16384 0x4000 01000000 00000000 10000001 10000000 00000000 0x818000
268435455 0x0FFFFFFF 00001111 11111111 11111111 11111111 11111111 11111111 11111111 01111111 0xFFFFFF7F

Dans la norme MIDI, on se limite à quatre octets maximum et la valeur la plus grande est 0x0FFFFFFF puisque le bit le plus significatif de chacun des quatre octets est réservé. Par sécurité, notre procédure vérifie tout d'abord que nous n'avons pas dépassé cette valeur :

 16: void ecrire_variable_length_quantity(FILE *fichier, unsigned long i) {

 17:     bool continuer ;

 18:

 19:     if (i > 0x0FFFFFFF) {

 20:         printf("ERREUR : delai > 0x0FFFFFFF ! \n") ;

 21:         exit(EXIT_FAILURE) ;

 22:     }

 23:     

 24:     unsigned long filo = i & 0x7F ;

 25:     i = i >> 7 ;

 26:     while (i != 0) {

 27:         filo = (filo << 8)  + ((i & 0x7F) | 0x80) ;

 28:         i = i >> 7 ;

 29:     }

 30:     

 31:     do {

 32:         fwrite(&filo, 1, 1, fichier) ;

 33:         continuer = filo & 0x80 ;

 34:         if (continuer) filo = filo >> 8 ;

 35:     } while (continuer) ;    

 36: }

Ensuite, nous récupérons les sept bits les moins significatifs de l'entier i (ligne 24), que nous plaçons dans une variable nommée filo car elle servira de pile de type First In Last Out. Ligne suivante, les bits de i sont décalés vers la droite de sept positions. La première boucle « Tant Que » va continuer de récupérer des groupes de sept bits et de les empiler dans la variable filo, en mettant si nécessaire à 1 le bit le plus significatif de l'octet (opération | 0x80).

La deuxième boucle, lignes 31 à 35, va récupérer chaque octet de filo, en commençant par le moins significatif, et l'écrire dans le fichier MIDI, jusqu'à qu'ils aient tous été écrits : on continue tant que le bit le plus significatif de l'octet qui vient d'être écrit vaut 1.

6.2 Choix d'un instrument

La norme MIDI définit seize canaux (le dixième canal est spécial puisque réservé aux percussions). Chaque canal ne peut jouer qu'un instrument à la fois (mais on peut changer d'instrument au cours du temps). En 1991, la norme General MIDI (GM) a normalisé la numérotation et le nom de 128 instruments répartis en 16 familles (cf. tableau suivant) ainsi que de 47 percussions [6].

  7 Pianos
8 15 Percussions Chromatiques
16 23 Orgues
24 31 Guitares
32 39 Basses
40 47 Cordes
48 55 Ensembles et chœurs
56 63 Cuivres
64 71 Instruments à anche
72 79 Instruments à Vent
80 87 Lead (Synthétiseurs)
88 95 Pad (Synthétiseurs)
96 103 Effets (Synthétiseurs)
104 111 Instruments ethniques
112 119 Percussions
120 127 Effets sonores

La numérotation des canaux et des instruments commence informatiquement à zéro (attention, la liste en référence [6] commence à 1). L'instruction MIDI pour changer l'instrument du canal n est 0xCnXXn est le chiffre hexadécimal correspondant et XX le numéro de l'instrument en hexadécimal (par exemple 00 pour le grand piano acoustique). Pour limiter les problèmes, nous utilisons l'opérateur % (modulo) afin de rester dans les bornes autorisées même si l'utilisateur se trompe (sinon les octets pourraient être interprétés comme d'autres instructions MIDI) :

 67: void MIDI_Program_Change(FILE *fichier, unsigned char canal, unsigned char instrument) {

 68:     unsigned char octets[2] ;

 69:     MIDI_delta_time(fichier, 0) ;

 70:     octets[0] = 0xC0 + canal % 16 ;

 71:     octets[1] = instrument % 128 ;

 72:     fwrite(&octets, 2, 1, fichier) ;

 73: }

6.3 Débuter et relâcher une note

Les événements MIDI consistant à débuter ou à relâcher une note (Note On et Note Off) commencent respectivement par les octets 0x9n et 0x8nn est le numéro du canal en hexadécimal.

Le deuxième octet code la note à jouer : le système MIDI définit 128 notes numérotées de   (do -2) à 127 (sol 8), sur plus de dix octaves. Comme indiqué figure 1, le la 3 (celui de la troisième octave, à 440 Hz) correspond au numéro 69 (0x45), le la 3 dièse a pour numéro 70, le si 3 le numéro 71, le do 4 le numéro 71, etc. Vous trouverez sur Wikipediaune figure vous permettant de trouver le numéro de chaque note [7], mais attention, la numérotation des octaves est différente dans la notation anglo-saxonne : le la3 est noté C4.

Fig. 1. Numérotation des notes MIDI.

Le troisième octet correspond à la vélocité avec laquelle la touche est enfoncée et correspond concrètement au volume sonore. La valeur minimale est 1 et la valeur maximale est 127 (0x7F). La valeur   peut être utilisée pour arrêter la note (relâchement de la touche) à la place d'un message Note Off.

Nous utiliserons la même procédure pour débuter ou relâcher une note :

 84: void MIDI_Note(unsigned char etat, FILE *fichier, unsigned char canal, unsigned char Note_MIDI, unsigned char velocite) {

 85:     unsigned char octets[3] ;

 86:     octets[0] = etat + canal % 16 ;

 87:     octets[1] = Note_MIDI % 128 ;

 88:     octets[2] = velocite % 128 ;

 89:     fwrite(&octets, 3, 1, fichier) ;

 90: }

Pour cela, le premier paramètre etat devra prendre pour valeur ON ou OFF, constantes définies au début du code source :

  3: #include <stdio.h>

  4: #include <stdlib.h>

  5: #include <stdbool.h>

  6:

  7: #define noire 128

  8: #define ON  0x90

  9: #define OFF 0x80

 10: #define C3  60

 11: #define percu 9

 12: #define reverb 0x5B

 13: #define chorus 0x5D

 14: #define phaser 0x5F

Dans une partition, il est courant qu'un même instrument joue plusieurs notes simultanément mais qui ne se terminent pas forcément en même temps et en particulier avant que d'autres notes commencent. Cela complique beaucoup le calcul des delta-times entre événements. Dans le cadre de cet article, nous allons simplifier les choses en créant une procédure permettant à la fois de débuter et de relâcher une même note (paramètre durée). Aucune autre note ne pourra alors commencer pendant la durée de cette note :

 92: void Note_unique_avec_duree(FILE *fichier, unsigned char canal, unsigned char Note_MIDI, unsigned char velocite, unsigned long duree) {

 93:     MIDI_delta_time(fichier, 0) ;

 94:     MIDI_Note(ON,  fichier, canal, Note_MIDI, velocite) ;

 95:     MIDI_delta_time(fichier, duree) ;

 96:     MIDI_Note(OFF, fichier, canal, Note_MIDI, 0) ;

 97: }

Si vous voulez jouer plusieurs notes simultanément, par exemple pour jouer un accord, vous devrez donc gérer vous-même les états ON et OFF de chaque note avec la procédure MIDI_Note().

Note

MIDI définit un mode Running status qui permet de ne pas répéter le premier octet d'une instruction destinée à un canal, par exemple 0x90 quand plusieurs notes se succèdent. Nous ne l'utilisons pas dans cet article afin de ne pas compliquer la programmation inutilement.

6.4 Contrôler les paramètres MIDI

L'instruction MIDI Control Change, dont le premier octet est 0xBn, permet de régler sur le canal n un paramètre MIDI (type) à une valeur donnée (comprise en   et 127) :

 75: void MIDI_Control_Change(FILE *fichier, unsigned char canal, unsigned char type, unsigned char valeur) {

 76:     unsigned char octets[3] ;

 77:     MIDI_delta_time(fichier, 0) ;

 78:     octets[0] = 0xB0 + canal % 16 ;

 79:     octets[1] = type % 128 ;

 80:     octets[2] = valeur % 128 ;

 81:     fwrite(&octets, 3, 1, fichier) ;

 82: }

On pourra par exemple agir sur le niveau de réverbération (0x5B), de chorus (0x5D), de l'effet phaser (0x5F), du panoramique (0x0A), des paramètres du son (Attack, Release...), contrôler le niveau des molettes et pédales que l'on trouve sur tout synthétiseur, etc. On pourra également changer de banque sonore (0x00), à condition que la fonte sonore utilisée dispose de plusieurs banques, ce qui permet d'outrepasser la limite de 127 instruments. Vous trouverez la liste des Control Changes sur le site officiel [8].

7. Exemple de piste musicale

Dans notre programme d'exemple demo.c, nous nous contenterons de créer une seconde piste (la première étant celle des méta-données). Une première boucle joue avec l'instrument 90 (Pad 3 polysynth) une gamme chromatique (3e octave) : do, do#, ré, ré#, mi, fa, fa#, sol, sol#, la, la#, si, do. Puis une seconde boucle passe en revue les 128 sons de la norme General MIDI en jouant un la 3 (neuf notes au-dessus du do3 dont le numéro est stocké dans la constante C3) :

10: void ecrire_piste2(FILE *fichier) {

11:     unsigned long marque = MIDI_ecrire_en_tete_piste(fichier) ;

12:     

13:     MIDI_Program_Change(fichier, 0, 90) ;

14:     for(int i=C3 ; i<=C3+12 ; i=i+1){

15:         Note_unique_avec_duree(fichier, 0, i, 64, noire/2) ;        

16:     }

17:     

18:     for(int i=0 ; i<=127 ; i=i+1){

19:         MIDI_Program_Change(fichier, 0, i) ;

20:         Note_unique_avec_duree(fichier, 0, C3 + 9, 64, noire) ;        

21:     }

22:     

23:     MIDI_fin_de_la_piste(fichier) ;

24:     ecrire_taille_finale_piste(fichier, marque) ;    

25: }

La première boucle joue des croches (durée valant noire/2), la seconde des noires. Dans les deux cas, le volume (vélocité) est fixé à 64, la moitié de la valeur maximale permise. Le canal MIDI utilisé est le canal  .

Conclusion

Pour jouer le fichier demo.mid, vous pouvez installer le logiciel TiMidity++ (paquet timidity dans le cas d'une distribution Ubuntu) [9]. Tapez ensuite :

$ timidity demo.mid

Remarquez que le fichier ne fait que 1688 octets ! Le rendu sonore dépend principalement de la qualité de la fonte sonore (soudfont) utilisée. TiMidity++ utilise par défaut une fonte nommée freepats [10], constituée de sons échantillonnés (samples) de divers instruments, mais qui n'est pas complète (le fichier /etc/timidity/freepats.cfg contient la liste des instruments présents). Il est préférable d'utiliser une des fontes sonores libres les plus réputées, la Fluid R3 General MIDI soundfont disponible dans le paquet fluid-soundfont-gm (142 Mio) :

$ timidity demo.mid -x "soundfont /usr/share/sounds/sf2/FluidR3_GM.sf2"

Si vous avez suffisamment de mémoire vive, vous pouvez également télécharger la fonte Crisis General Midi 3.01 [11] qui fait 1,6 Gio une fois décompactée !

TiMidity++ dispose de nombreuses options. Vous pouvez par exemple exporter votre musique en .wav facilement en ajoutant les options -o demo.wav -Ow, en .ogg avec l'option -Ov, ou au format FLAC (Free Lossless Audio Codec) avec -OF. Vous pouvez même tracer un spectrogramme en temps réel avec l'option -g .05 (définition temporelle).

Enfin, pour visualiser la partition de votre morceau, vous pouvez utiliser le séquenceur Rosegarden [12, 13]. Vous pouvez également récupérez vos pistes dans LMMS [14] où vous pourrez les manipuler et les faire jouer par tout un tas de plugins synthétiseurs.

Vous disposez désormais des bases minimales nécessaires pour créer vos propres fichiers MIDI, from scratch. Dans un second article, nous explorerons plus avant les possibilités offertes par ce programme en mêlant musique, algorithmes et mathématiques.

Références

[1] Magnin V., « Avec MIDI, lancez-vous dans la musique assistée par ordinateur », Linux Pratique Hors-Série n°29, p. 36-47.

[2] Standard MIDI Files : https://www.midi.org/articles/about-midi-part-4-midi-files

[3] Brouillon de la norme C 2011 (ISO/IEC 9899:201x) : http://www.open-std.org/JTC1/SC22/WG14/www/docs/n1570.pdf

[4] Code temporel SMPTE : https://fr.wikipedia.org/wiki/Timecode_%28temporel%29

[5] CodageVariable Length Quantity : https://en.wikipedia.org/wiki/Variable-length_quantity

[6] Liste des instruments General MIDI : https://www.midi.org/specifications/item/gm-level-1-sound-set

[7] Numérotation MIDI des notes : https://upload.wikimedia.org/wikipedia/commons/3/36/NoteNamesFrequenciesAndMidiNumbers_V3.svg

[8] Control Change Messages : https://www.midi.org/specifications/item/table-3-control-change-messages-data-bytes-2

[9] Lecteur MIDI Timidity++ : http://timidity.sourceforge.net/

[10] Fonte sonore freepats : http://freepats.zenvoid.org/

[11] Fonte CGM3.01 : http://www.bismutnetwork.com/04CrisisGeneralMidi/Soundfont3.0.php

[12] Séquenceur Rosegarden : http://www.rosegardenmusic.com/

[13] MAGNIN V., « Composez librement avec le séquenceur Rosegarden », Linux Pratique Hors-Série n°29, p. 48-57.

[14] MAGNIN V.,« Lancez-vous dans la "dance music" avec Linux MultiMedia Studio ! », Linux Pratique n°82, p. 56-63.

Tags : Audio/Vidéo, C, MIDI