Logiciels > Proteus > Générateur EasyHDL > MIDI

Dernière mise à jour : 11/09/2016

English version

Présentation

Génération d'une trame MIDI (note ou sysex) avec le générateur EasyHDL inclus dans Isis.

Rappels brefs sur les données MIDI

Des infos plus complètes sont disponibles à la page Norme MIDI mais voici tout de même quelques brefs rappels histoire de se remettre rapidement dans le bain. Les données MIDI permettent de transmettre des informations numériques indiquant à un instrument de musique quelle note jouer et avec quelle force (vélocité). Il existe en particulier deux types d'évenement qui normalement vont de pair : les évenements de type NoteOn et NoteOff. Quand on appuie sur une touche d'un clavier MIDI, un évenement de type NoteOn est créé, pour indiquer qu'une note doit être jouée. Quand on relache la touche du clavier, un évenement de type NoteOff est créé, pour indiquer que la note doit être stoppée. Les messages MIDI de type NoteOn ou NoteOff sont tous deux composés de 3 octets :
- une moitié du 1er octet contient l'information NoteOn ou NoteOff
- la seconde moitié du 1er octet contient le numéro de canal sur lequel l'évenement s'applique (16 canaux possibles sur un seul fil MIDI)
- le second octet indique la note à jouer : Do3, Ré4 ou Mi2, par exemple (127 notes possibles)
- le troisième octet indique la force de frappe de la touche
Exemple
La suite d'octets $90,$3C,$64 indique qu'il faut jouer la note Do3, sur le canal MIDI N°1, avec une vélocité (force de frappe) de valeur $64.
La suite d'octets $80,$3C,$00 indique qu'il faut arrêter de jouer la note Do3 précédement déclanchée sur le canal MIDI N°1.

Exemple de code

Le code suivant permet d'envoyer une trame de donnée de longueur trois octets, équivalente à un évenement de type NoteOn, à une vitesse de 31250 bauds, 1 bit de Start, 1 bit de Stop et sans parité, qui correspond au protocole de transmission des données MIDI. La note simulée est le Do3 (C3), jouée sur le canal MIDI N°1, avec une vélocité (force de frappe) de valeur $64.

// MIDI Note generator

// 1 - Define data to be output (here, a NoteOn event)
DATA 0x90,0x3C,0x64

// 2 - Define the baud rate
FLOAT BAUD=31250
FLOAT BITTIME=1.0/BAUD

// 3 - Declare working variables
INT i,j,d

// 4 - Top level
OUT = 1
SLEEP FOR 1000m // wait 1 sec before start sending

// 5 - Read DATA lineLOOP:
i = i + 1
if i > 3 THEN GOSUB THEEND
READ d
GOSUB OUTDATA
GOTO LOOP

// 6 - Stop all
THEEND:
END

// 7 - Output data
OUTDATA:
// Start bit
OUT = 0
SLEEP FOR BITTIME
// Data bits
FOR j=0 TO 7
OUT = d & (1 << j)
SLEEP FOR BITTIME
NEXT j
// Stop bit
OUT = 1
SLEEP FOR BITTIME
RETURN


Pour simuler l'évenement NoteOff correspondant à l'extinction de cette NoteOn Do3 (C3) il suffit de remplacer la valeur $90 du premier octet par la valeur $80.

1 - Define data to be output (here, a NoteOn event)
On définit ici les données à envoyer, grâce au mot-clé DATA, suivi des octets à envoyer. Comme les données à envoyer sont des valeur hexadécimale codées sur 8 bits, on écrit les données à envoyer octet par octet, avec le suffixe 0x. La ligne suivante
DATA 0x90,0x3C,0x64
indique qu'il faut envoyer les trois octets $90, £3C et $64, les uns après les autres et sans délai entre chaque.

2 - Define the baud rate
On définit ici la constante de type FLOAT qui prendra la valeur temporelle séparant chaque bit de la trame à transmettre, et qui dépend bien sûr de la vitesse de transmission de la liaison série à simuler. Ici, la vitesse est de 31250 bauds, ce qui correspond à une "fréquence" de 31250 Hz. La période de temps séparant chaque bit est donc de 1 / 31250 secondes, soit 32 us environ.

3 - Declare working variables
Sont définies ici l'ensemble des variables qui sont utilisées par la suite.

4 - Top level
Indique l'endroit à partir duquel le générateur de signal commence réellement à "émettre" ses données.

5 - Read DATA line
Ici, une boucle lit chaque ligne de données spécifiée au tout début grâce au mot-clé DATA. Ainsi, lors de la première passe de la boucle, le contenu de la première donnée de la première ligne DATA est lu, et est placé dans une variable grâce au mot-clé READ.
La première ligne DATA, qui contient en tout trois valeurs à traiter :
DATA 0x90,0x3C,0x64
est donc lue en trois étapes successives, grâce à la ligne
READ d
qui constate la présence de trois valeurs différentes grâce à la présence des virgules qui les séparent.
La première valeur "0x90" est placée dans la variable d, puis la sous-routine OUTDATA est exécutée pour travailler avec cette valeur précise. Une fois le traitement effectué avec cette valeur, la seconde valeur "0x3C" est placée dans la variable d, puis la sous-routine OUTDATA est de nouveau exécutée pour travailler avec cette nouvelle valeur. Puis c'est le tour de la valeur "0x64", qui subit le même traitement de faveur que les deux valeurs précédentes.

6 - Stop all
Fin du processus global d'envoi des données, on arrête tout. Cette sous-routine est appelée quand les trois octets sont envoyés.

7 - Output data
La routine OUTDATA travaille sur le contenu de la variable d préalablement spécifié par la commande READ précédente, en transformant sa valeur "entière" en son "équivalent de bits".

Simulation d'un Running Status

Prenons l'exemple d'un accord Do3-Mi3-Sol3 qui nécessite en temps normal l'envoi de 3 événements MIDI comportant chacun 3 octets (fixons une vélocité identique pour les trois notes) :

// 1 - Define data to be output (here, 3 NoteOn events)
DATA 0x90,0x3C,0x64,0x90,0x40,0x64,0x90,0x43,0x64


Cette façon de faire fonctionne avec tous les instruments MIDI qui s'attendent à recevoir 3 octets pour chaque note. En parallèle de cette méthode classique, il existe une méthode dont l'objectif est de réduire (compresser) le débit des événements MIDI transmis. Cette méthode miracle s'appelle Running Status et à pour but de supprimer des octets dont on peut se passer. Dans l'exemple qui précède (méthode standard) nous avons vu qu'il fallait 9 octets pour notre accord à trois notes. La méthode avec Running Status permet d'en économiser (supprimer) deux.

// 1 - Define data to be output (again 3 NoteOn events, but with running status)
DATA 0x90,0x3C,0x64,0x40,0x64,0x43,0x64


Le principe de fonctionnement est simple. Le récepteur MIDI (sampler, synthé, VST, etc) reconnait le début d'un message MIDI quand il reçoit un octet "spécial" (NoteOn, NoteOff, CC, PC, SysEx, etc). Cet octet "spécial" peut être vu comme redondant. Dans l'exemple qui précède, le récepteur MIDI sait qu'il a affaire à un événement NoteOn quand il reçoit l'octet $9x (x = numéro de canal MIDI). Tant qu'il ne reçoit pas d'autre octet "spécial", il peut en conclure que toutes les données qui suivent sont de même type (ici des notes).

Et voilà, vous avez désormais de quoi envoyer des données MIDI un peu "tordues" à votre microcontrôleur inséré dans Proteus, pour voir si son soft est compatible avec le running status ;-)