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.
192 = 1*102 + 9*101 + 2*100
192 = 128 + 64 = 1*27 + 1*26 donne (en regroupant les chiffres par 8 = 1 octet) 1100 0000
192 = 12*161 donne C0
Les hexa sont groupés par 2 = 1 octet
| 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
81 1F 90 3C 43
81 26 90 3C 42
81 32 90 3C 42
81 3C 90 3C 47 00 FF 2F 00
|
"MTrk" longueur = 32+16+8+2+1 = 59 octets
texte (méta-événement) "Track 1"
end-of-track (méta-événement)
|
0C = (0)000 1100 -> 8+4 = 12
81 1F = (1)000 0001 (0)001 1111 -> 128+16+8+4+2+1 = 159
total = 171 ticks
11 = (0)001 0001 -> 16+1 = 17
81 26 = (1)000 0001 (0)010 0110 -> 128+32+4+2 = 166
total = 183 ticks
0E = (0)000 1110 -> 8+4+2 = 14
81 32 = (1)000 0001 (0)011 0010 -> 128+32+16+2 = 178
total = 192 ticks
0F = (0)000 1111 -> 8+4+2+1 = 15
81 3C = (1)000 0001 (0)011 1100 -> 128+32+16+8+4 = 188
total = 203 ticks
0E = (0)000 1110
(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