Pages: 1 [2]   Go Down
Author Topic: Lire des signaux MIDI  (Read 2192 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Faraday Member
**
Karma: 33
Posts: 5045
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

INSPIRER.... EXPIRER.... Merci fdufnews  smiley-wink

Pour faire mes tests je vais attendre de recevoir mon petit shield LCD-RVB de chez Adafruit, comme ça je pourrais utiliser l'entrée Série "hard" de l'Arduino pour le MIDI et l'écran du shield en I2C pour me donner quelques ordres d'idée, comme :

- Combien de BYTE je traite au maximum par tour de boucle.
- Est-ce que VRAIMENT ya des Messages Temps-réel qui viennent s'intercaler DANS des Messages plus long (NoteOn par exemple)
- Que se passe-t-il quand j'envoi PLEINS de notes simultanées...

Ça devrait déjà m'informer pas mal sur l'utilisation réelle du truc.

bonjour
déjà tu peux faire du log des événements midi pour regarder ce qui transite
sur PC terminal https://sites.google.com/site/terminalbpp/
permet d'utiliser du port com en valeur custom (31250 pour du midi)
ici un test midi out arduino ---> terminal sur PC
http://cjoint.com/13mi/CEdnRhfsNoQ_midimsg1.jpg



Logged

Offline Offline
Jr. Member
**
Karma: 0
Posts: 52
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
- Combien de BYTE je traite au maximum par tour de boucle.

Un seul avec une machine d'état, plusieurs si tu fais des attentes actives (par ex: 3 pour un NOTE ON)

Quote
- Est-ce que VRAIMENT ya des Messages Temps-réel qui viennent s'intercaler DANS des Messages plus long (NoteOn par exemple)

Je crois que cela dépends de la configuration de l'appareil MIDI qui émet. Mais part du principe que oui

Quote
- Que se passe-t-il quand j'envoi PLEINS de notes simultanées...

L'appareil qui émet va les envoyer à la suite dans la limite du débit MIDI 31250 bauds.
Donc tu les recevra également à la suite. (pas simultanément).

Voici une machine d'état un peu plus aboutie (mais non testée) que mon premier exemple.

Code:
#define NOTE_OFF 0x80
#define NOTE_ON 0x90

void setup() {
}

void note_on(byte note, byte velocity) {
}

void note_off(byte note, byte velocity) {
}

void process(byte b) {
  static byte command; // dernière commande MIDI reçue
  static byte arg_cpt; // compteur d'argument
  static byte arg1; // mémoire pour l'argument 1
  static byte arg2; // mémoire pour l'argument 2 (pas nécessaire, mais pour faire homogène)

  if(b>=0xF8) return; // ignore les messages temps réel
  if(b & 0x80) { // si commande MIDI
    command = b; // mémorise la dernière commande
    arg_cpt = 0; // initialise le compteur d'argument 
  }
  else if(arg_cpt==0) { // si 1er argument
    arg1 = b;
    arg_cpt++;
    // traiter ici les commandes à 1 argument (par ex: aftertouch)
  }
  else if(arg_cpt==1) { // si 2ème argument
    arg2 = b;
    arg_cpt++;
    // on a reçu les 2 arguments pour NOTE_ON ou NOTE_OFF, on délegue à des fonctions pour clarifier le code
    if(command==NOTE_ON) note_on(arg1, arg2);
    else if(command==NOTE_OFF) note_off(arg1, arg2);
  }
  // ignore les argument d'une commande avec plus que 2 arguments (si ça existe, me souviens plus)
}

void loop() {
  int b = Serial.read();
  if(b!=-1) process(b);
  // permet de faire autre chose ici
}

A vue de nez, le temps de la fonction process devrait demander max 20 microsecondes par byte MIDI, donc
largement en dessous des 320.

Prends garde que dans les fonctions note_on ou note_off tu ne prenne pas trop de temps.
Logged

Paris
Offline Offline
Sr. Member
****
Karma: 2
Posts: 366
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Merci Marcha,

C'est vraiment sympa d'avoir pris de ton temps pour écrire ce message et ce code, ça sera mon point de départ  smiley

UniseV
« Last Edit: May 03, 2013, 11:29:04 am by UniseV » Logged

EN: Libraries are my Gurus, they make me believe anything they want !
FR: Les librairies sont mes gourous, elles me font croire ce qu'elles veulent !

Offline Offline
Jr. Member
**
Karma: 0
Posts: 52
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Je t'en prie. ça me rappelle de vieux souvenirs (1990) à cette époque j'avais fait un light show midi basé sur un 8051.

Qu'est-ce que tu es en train de faire toi ?
Logged

Paris
Offline Offline
Sr. Member
****
Karma: 2
Posts: 366
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

J'ajoute des bandes de LED à mon Piano numérique... alors autant qu'elle réagisse en fonction de la musique smiley-wink

J'ai commencé à développer et j'ai un soucis, je n'ai que le premier NoteOn qui est lu correctement, ensuite ma boucle se "décale" et n'arrive plus à déchiffrer correctement les trame MIDI, voici l'état du machin :

Code:
#define ardLed 13

void setup()
{
  pinMode(ardLed,OUTPUT);
  digitalWrite(ardLed,LOW);

  Serial.begin(31250);        // Begin Serial MIDI
}

unsigned long lastMid,lastMsg;
byte state=0;   // Characters for MIDI words
byte cmd,newMsg;   // Characters for MIDI words
int note,velo;   // Characters for MIDI words
byte charTmp;   // Characters for MIDI words

void loop()
{
  if (Serial.available() > 0) { // Only if serial data arrived...
    digitalWrite(ardLed,HIGH);  // Light the Arduino LED for debug.
    lastMid=millis();

    charTmp = Serial.read();    // Read the first char...

    if (charTmp==0xF8 || charTmp==0xFE) { // ...If it is a RealTimeClock Message...      ...just ignore it
    }

    else                 // But if it is NOT a RTC-M...
    {
      switch(state){
      case 2:
        velo=charTmp;     // Save the velocity byte...
        newMsg=1;         //  ... tell the main loop there is a new message
        break;
      case 1:
        note=charTmp;    // Save the note byte
        state=2;
        break;
      case 0:
        cmd=charTmp;     // Save the command byte
        state=1;
        break;
      }
    }
  }

  if (newMsg==1) {    // If there is a new message...
    newMsg=0;
    state=0;
    if (cmd==0x90) {     // ... And if this is a NoteOn...
                       // Use here the note & velo data
    }
    else {     // ... but if not...
                // ... there is a bug.
    }

  }

  // Normal LOOP CODE

  if (millis()>lastMid+5) digitalWrite(ardLed,LOW);   // Switch off the Arduino LED if there is 5ms without MIDI message
}

J'ai viré tout le "code Leds" pour ne laisser que le "code MIDI", si quelque chose vous saute aux yeux.

EDIT : Je précise que mon Piano n'envoi pas de NoteOff à proprement dit, mais simplement des NoteOn avec une velocity à 0.

EDIT 2: Artouste, je n'ai pas réussi à voir mes signaux MIDI dans ton soft, je pense que je ne comprends pas bien comment "raccorder" le tout.
« Last Edit: May 04, 2013, 12:42:49 pm by UniseV » Logged

EN: Libraries are my Gurus, they make me believe anything they want !
FR: Les librairies sont mes gourous, elles me font croire ce qu'elles veulent !

Offline Offline
Jr. Member
**
Karma: 0
Posts: 52
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Salut,

ton code m'a l'air fonctionnel, mais tu dis:

Quote
J'ai viré tout le "code Leds" pour ne laisser que le "code MIDI", si quelque chose vous saute aux yeux.

Si il s'agit d'une bande de led adressable, le temps d'écriture sur la bande pourrait bien être trop long pour gérer
correctement le midi.

En gardant le code que tu as posté ici, peux-tu tester en allumant ardLed quand la vélocité > 0 et en l'éteignant quand la vélocité vaut 0 et essayer
avec ton piano si ardLed suit bien la pression d'une touche ?
Logged

Paris
Offline Offline
Sr. Member
****
Karma: 2
Posts: 366
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Ce sont 2 bandes de LED RVB, elle "consomment" 6 analogWrite par tour de boucle, penses-tu que cela peut perturber le MIDI ?

Voici mon "truc" avec le code LED, qui utilise une ch'tite librairie maison :

Code:
#include "LedRVB.h"
#define ardLed 13
// Serial reading data

LedRVB ledStripL(3,5,6), ledStripR(9,10,11);

void setup()
{
  pinMode(ardLed,OUTPUT);
  Serial.begin(31250);        // Begin Serial MIDI
  ledStripL.setVal(NOIR);
  ledStripR.setVal(NOIR);
  digitalWrite(ardLed,LOW);
}

// Data for MIDI words
byte state=0;  
byte msgTyp=0;
byte charTmp,cmd,newMsg;

// RGB led values & Timeout
int note,velo,lR,lG,lB,rR,rG,rB;  
unsigned long lastMid,lastMsg;

void loop()
{


  if (Serial.available() > 0) { // Only if serial data arrived...
    digitalWrite(ardLed,HIGH);
    lastMid=millis();
    charTmp = Serial.read();    // Read the first char...
    if (charTmp==0xF8 || charTmp==0xFE) { // ...If it is a RealTimeClock Message...
      // ...just ignore it
    }
    else                 // But if it is NOT a RTC-M...
    {
      switch(state){
      case 2:
        velo=charTmp;   // Save the velocity byte
        newMsg=1;
        rB=1;
        break;
      case 1:
        note=charTmp;   // Save the note byte
        state=2;
        rG=1;
        break;
      case 0:
        cmd=charTmp;     // Save the command byte
        state=1;
        rR=1;
        break;
      }
    }
  }

  if (newMsg==1) {
    lastMsg=millis();
    newMsg=0;
    state=0;
    if (cmd==0x90) msgTyp=1;
    else{
      if (cmd==0xB0 && note==0x40) msgTyp=2;
      else msgTyp=0;  
    }
    switch(msgTyp){
    case 2:
      if (velo>0) lB=50;
      else lB=1;
      break;
    case 1:
      if (velo>0) lG=50;
      else lG=1;
      break;
    case 0:
      if (velo>0) lR=50;
      else lR=1;
      break;  
    }
  }
  // Normal LOOP CODE

  if (millis()>lastMid+5) digitalWrite(ardLed,LOW);
  if (millis()>lastMsg+1000) {  // If there is no MIDI word since 1 second, switch off the leds
    rR=0;
    rG=0;
    rB=0;
    lR=0;
    lG=0;
    lB=0;
  }

  ledStripL.setVal(lR,lG,lB);
  ledStripR.setVal(rR,rG,rB);
}


EDIT : J'ajoute que dans tous les tests que je fais, le premier messages MIDI reçu juste après un reboot de l'Arduino est bien pris en compte (NoteOn & SustainOn, y compris avec la bonne vélocité et la bonne note)... surprenant non ?

EDIT 2 : Pour aider à la compréhension du code, les bandes de Led sont en mode "DEBUG" :
  • Celle de gauche est censée s'allumer "VertFort" quand elle reçoit un NoteOn, VertFaible quand elle reçoit un "NoteOff", BleuFort/Faible en fontction du Sustain et Rouge si le message n'est pas reconnu.
  • Celle de droite elle s'allume Rouge quand elle reçoit un cmd, Vert quand elle reçoit une note, et Bleu quand elle reçoit une vélocité, elle doit donc toujours s'allumer "R+V+B"
« Last Edit: May 04, 2013, 07:10:46 pm by UniseV » Logged

EN: Libraries are my Gurus, they make me believe anything they want !
FR: Les librairies sont mes gourous, elles me font croire ce qu'elles veulent !

Offline Offline
Jr. Member
**
Karma: 0
Posts: 52
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
Ce sont 2 bandes de LED RVB, elle "consomment" 6 analogWrite par tour de boucle, penses-tu que cela peut perturber le MIDI ?

Le plus simple c'est de mesurer avec micros() avant et après l'appel à ledStripL.setVal();

Tu as 320 microsecondes à disposition par tour de boucle pour "consommer" ce qui arrive sur ton port série MIDI en entrée. Si tu suis pas
le rythme, le buffer d'entrée série (dont j'ignore la longueur) va simplement déborder et tu perds des données.

Pour le moment ton code passe le plus clair de son temps à faire ledStripL.setVal(); si ce code dépasse 320 microsecondes ça expliquerai le problème.

Peux-tu faire cette mesure stp ? ou donner la référence à la librairie que tu utilises ?
Logged

Paris
Offline Offline
Sr. Member
****
Karma: 2
Posts: 366
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

La librairie je l'ai crée moi-même, voici ce que fait cette fonction :



Je vais effectivement mesurer la durée (micros) de cette fonction dans la main loop, c'est une information intéressante et je peux le faire sans être connecté en MIDI (l'Arduino ne peut pas débugger par moniteur serie & écouter du MIDI en même temps en HardSerial).

Toutefois, je ne pense sincèrement pas que ça puisse venir de la, après une courte recherche sur le net, un analogWrite devrait prendre entre 2µs a 7µs, donc en exagérant au pire, ma fonction devrait durer dans les 60µs, reste à voir les constrain() mais j'y crois pas trop non-plus.

PS : J'attends avec impatiente, l'arrivée de mon shield LCD-I2C pour débugger tout ça en local, par exemple en exportant vers l'écran le nombre de caractères dans le buffer, ou le dernier mot reçu  smiley
Logged

EN: Libraries are my Gurus, they make me believe anything they want !
FR: Les librairies sont mes gourous, elles me font croire ce qu'elles veulent !

Offline Offline
Jr. Member
**
Karma: 0
Posts: 52
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Salut,

Peux-tu tester juste ce code qui devrait allumer la led de debug sur un note on et l'etteindre
sur un note off. (ou note on à vélocité 0)

Code:

#define ardLed 13
#define NOTE_OFF 0x80
#define NOTE_ON 0x90

void setup() {
  pinMode(ardLed, OUTPUT);
  Serial.begin(31250);        // Begin Serial MIDI
  digitalWrite(ardLed,LOW);
}

void note_on(byte note, byte velocity) {
  if(velocity>0) digitalWrite(ardLed, HIGH);
  else digitalWrite(ardLed, LOW);
}

void note_off(byte note, byte velocity) {
  digitalWrite(ardLed, LOW);
}

void process(byte b) {
  static byte command; // dernière commande MIDI reçue
  static byte arg_cpt; // compteur d'argument
  static byte arg1; // mémoire pour l'argument 1
  static byte arg2; // mémoire pour l'argument 2 (pas nécessaire, mais pour faire homogène)

  if(b>=0xF8) return; // ignore les messages temps réel
  if(b & 0x80) { // si commande MIDI
    command = b; // mémorise la dernière commande
    arg_cpt = 0; // initialise le compteur d'argument 
  }
  else if(arg_cpt==0) { // si 1er argument
    arg1 = b;
    arg_cpt++;
    // traiter ici les commandes à 1 argument (par ex: aftertouch)
  }
  else if(arg_cpt==1) { // si 2ème argument
    arg2 = b;
    arg_cpt++;
    // on a reçu les 2 arguments pour NOTE_ON ou NOTE_OFF, on délegue à des fonctions pour clarifier le code
    if(command==NOTE_ON) note_on(arg1, arg2);
    else if(command==NOTE_OFF) note_off(arg1, arg2);
  }
  // ignore les argument d'une commande avec plus que 2 arguments (si ça existe, me souviens plus)
}

void loop() {
  int b = Serial.read();
  if(b!=-1) process(b);
}
Logged

Paris
Offline Offline
Sr. Member
****
Karma: 2
Posts: 366
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

J'ai trouvé !!

En fait mon piano n'envoi pas forcément la commande NoteOn ou SustainOn si plusieurs messages de ce même type se suivent, exemple :
NoteOn de la note 23 avec vélocité 50, suivi d'un NoteOff de la même note :

0x902350
0x2300

Du coup, au lieu de remettre mon state à 0 je le met à 1 (prêt à lire une nouvelle note) :
Code:
if (newMsg==1) {
   newMsg=0;
   state=1;
   ....

Et si c'est une commande, je le remet à 0 :
Code:
if (charTmp==0x90 || charTmp==0xB0) state=0;    //...if it is a command...

 Merci à tous !

PS : Pour y arriver j'ai monté un SoftSerial pour écouter le MIDI de mon piano, et l'Arduino renvoyait les valeurs vers le moniteur Arduino (par le "hard"serial).
« Last Edit: May 07, 2013, 02:37:45 am by UniseV » Logged

EN: Libraries are my Gurus, they make me believe anything they want !
FR: Les librairies sont mes gourous, elles me font croire ce qu'elles veulent !

Paris
Offline Offline
Sr. Member
****
Karma: 2
Posts: 366
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Voila le premier résultat :



En vous remerciant
Logged

EN: Libraries are my Gurus, they make me believe anything they want !
FR: Les librairies sont mes gourous, elles me font croire ce qu'elles veulent !

Pages: 1 [2]   Go Up
Jump to: