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 ;-)