Projets d'informatique musicale
Licence-Maîtrise d'informatique - Université de Caen
Marc Chemillier

  • Quelques indications sur le format Midi

    Une bibliothèque midi, dans un langage de programmation comme Java (API javasound), permet Il n'est donc pas nécessaire de connaître le détail du format midi. Toutefois, quelques indications peuvent être utiles.

  • Codage des séquences Midi et des fichiers Midifiles

    Les données musicales au format midi comportent des notes caractérisées par:
    1. une date de début (en millisecondes)
    2. un numéro de note (de 0 à 127, le code 60 correspond au Do)
    3. une durée (en millisecondes)
    4. uné vélocité (de 0 à 127)
    5. un numéro de canal (de 0 à 15)

    Une séquence Midi est donc constituée d'une liste d'événements, qui sont des 5uples (note date durée vélocité canal). Par exemple :

    auclairdelalune
    = ((60 0 450 120 0) (60 500 450 120 0) (60 1000 450 120 0) (62 1500 450 120 0) (64 2000 950 120 0) (62 3000 950 120 0))

    Quand on sauve une séquence dans un fichier Midi, on écrit sur le disque des entiers compris entre 0 et 255 (appelés des « octets »). Le fichier Midi commence par une série fixe d'entiers formant l'entête, et se termine par une série fixe de quatre entiers :

    entete = (77 84 104 100 0 0 0 6 0 0 0 1 1 244 77 84 114 107)
    fin = (0 255 47 0)

    Les entiers insérés entre les deux servent à coder la séquence sonore. Pour enregistrer une liste d'événements dans un fichier Midi, le programme doit la traduire en une liste d'octets. Dans les bibliothèques Midi comme Javasound, on dispose de primitive qui font ce travail automatiquement. Pour chaque événement (n t d v c) à la date t et de durée d, on le transforme en 4uple (n t v c) en supprimant sa durée, et on ajoute dans la liste un 4uple (n t+d 0 c) à la date t+d, avec une vélocité égale à 0. Dans la liste ainsi obtenue, chaque 4uple (n t v c) est ensuite traduit en cinq octets o1, o2, o3, o4, o5. Les deux premiers o1 et o2 correspondent à la différence entre la date t du 4uple et celle t' du 4uple précédent (ou t'=0 pour le premier 4uple). En notant d = t - t' cette différence, on calcule q et r le quotient et le reste de la division euclidienne de d par 128, et on pose o1 = 128+q et o2 = r. Les trois autres octets sont respectivement o3 = 144+c, o4 = n, o5 = v. Pour la séquence auclairdelalune ci-dessus, on obtient la liste d'octets suivante :

    (128 0 144 60 120 131 66 144 60 0 128 50 144 60 120 131 66 144 60 0 128 50 144 60 120 131 66 144 60 0 128 50 144 62 120 131 66 144 62 0 128 50 144 64 120 135 54 144 64 0 128 50 144 62 120 135 54 144 62 0 )

    On ajoute ensuite devant la liste d'octets obtenue une liste fixe :

    tempo = (0 255 81 3 7 161 32)

    puis on compte le nombre total d'octets, en incluant ceux de la liste tempo, et ceux de la liste fin. Ce nombre n est lui-même traduit en quatre octets o1, o2, o3, o4 qui sont ajoutés en tête de la liste. En notant q, q', q" les quotients de la division de n par 256, 2562 et 2563, ces octets o4, o3, o2, o1 désignent respectivement les restes de la division de n, q, q' et q" par 256. Finalement la liste d'octets à enregistrer dans un fichier Midi comporte les octets de l'entête, puis les quatre octets de n, puis ceux de tempo, puis ceux qui servent à coder les événements, puis les quatre octets de fin.

  • Petit rappel sur le codage hexadécimal

  • en décimal : somme de puissances de 10

    192 = 1*102 + 9*101 + 2*100

  • en binaire : somme de puissances de 2
    (20=1, 21=2, 22=4, 23=8, 24=16, 25=32, 26=64, 27=128, 28=256,...)

    192 = 128 + 64 = 1*27 + 1*26 donne (en regroupant les chiffres par 8 = 1 octet) 1100 0000

  • en hexadécimal : somme de puissances de 16 = 24
    0 = 0000, 1 = 0001, 2 = 0010, 3 = 0011
    4 = 0100, 5 = 0101, 6 = 0110, 7 = 0111
    8 = 1000, 9 = 1001, (10)A = 1010, (11)B = 1011
    (12)C = 1100, (13)D = 1101, (14)E = 1110, (15)F = 1111

    192 = 12*161 donne C0
    Les hexa sont groupés par 2 = 1 octet

  • Analyse d'un midifile

    Positions des cinq notes par rapport à la barre du temps précédent la plus proche (chaque temps est divisé en 192 "ticks") :

    1. 0 durée entre notes :
    1. 171 171 ticks
    2. 162 162 + (192-171) = 183 ticks
    3. 162 162 + (192-162) = 192 ticks
    4. 173 173 + (192-162) = 203 ticks

    Liste de tous les octets contenus dans le fichier NotesRepetees.mid, représentés au format hexadécimal, avec leur traduction :

    Entête (Header chunk) 4D 54 68 64
    00 00 00 06
    00 00
    00 01
    00 C0
    "MThd"
    longueur = 6 octets
    format 0
    1 piste (track)
    division = 128+64 = 192 ticks/noire
    Données midi (Track chunk) 4D 54 72 6B
    00 00 00 3B

    00 FF 01 07 54 72 61 63 6B 20 31

    00 90 3C 47
    0C 80 3C 47

    81 1F 90 3C 43
    11 80 3C 43

    81 26 90 3C 42
    0E 80 3C 42

    81 32 90 3C 42
    0F 80 3C 42

    81 3C 90 3C 47
    0E 80 3C 47

    00 FF 2F 00

    "MTrk"
    longueur = 32+16+8+2+1 = 59 octets

    texte (méta-événement)  "Track 1"

    deltatime NoteOn do3 intensité
    deltatime NoteOff [do3 intensité]

     
     

     
     

     
     

     
     

    end-of-track (méta-événement)

  • Calcul des durées entre notes

  • Exemple de dump d'un fichier

    
    (define (dump-midifile nom-fich)
      (let ((liste-octets (lecture (open-input-file nom-fich))))
        (map (lambda (x) (display (car x)) (display (cadr x)) (display " ")) liste-octets)
        'ok))
    
    ;;; (dump-midifile "NotesRepetees.mid")
    
    (define (lecture port)
     (let ((octet (read-char port)))
       (if (eof-object? octet) ()
           (cons (reverse (octet->hexa (char->integer octet)))
                 (lecture port)))))
    
    (define (octet->hexa n)
      (list (traduit-hexa (remainder n 16)) (traduit-hexa (quotient n 16))))
    
    (define (traduit-hexa i)
      (case i
        ((10) "A") ((11) "B") ((12) "C") ((13) "D") ((14) "E") ((15) "F") (else i)))
    
    
    4D 54 68 64 00 00 00 06 00 01 00 02 01 E0 4D 54 72 6B 00 00 00 4A 08 FF 58 04 0C 03 0C 0C 8D 64 90 45 32 00 39 32 00 3F 32 83 60 3F 00 00 45 00 00 39 00 81 20 39 28 00 45 28 00 3F 28 82 40 3F 00 00 45 00 00 39 00 81 20 39 2D 00 45 2D 00 3E 2D 83 60 39 00 00 45 00 00 3E 00 83 5C FF 2F 00 4D 54 72 6B 00 00 00 38 8D 6C 90 23 32 83 60 23 00 00 2F 30 81 20 2F 00 00 27 35 82 40 27 00 00 33 2B 81 20 33 00 00 28 37 82 40 28 00 00 34 35 81 20 34 00 00 28 2E 82 40 28 00 81 1C FF 2F 00