Lire des signaux MIDI

Salut,

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

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 ?

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 :

#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"

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 ?

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 :slight_smile:

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)

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

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

if (newMsg==1) {
   newMsg=0;
   state=1;
   ....

Et si c'est une commande, je le remet à 0 :

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

Voila le premier résultat :

En vous remerciant