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

Bonjour,

Je suis en train de développer une petit programme pour "décoder" le signal MIDI qui sort de mon Piano numérique.

Je cherche principalement à décoder des NoteOn/NoteOff, qui sont des mots de 3 bytes (24 bits), du genre 0x90407F.
Le problème c'est que je suis pollué par des "SYSTEME REALTIME MESSAGES", plus exactement des "Timing clock", composé d'un petit mot de 8 bits (0xF8), qui sert normalement à synchroniser les équipements MIDI entre eux.

Comment ignorer cette pollution, qui de plus est bien plus quantitative que mes VRAI messages ?
De plus, apparemment ces petits 0xF8 peuvent même venir s'intercaler A L'INTERIEUR d'un mot plus grand, du style : 0x90F8407F.

Voici le type de code que je comptais utiliser :
Code:
/*Receive Midi
*/

byte commandByte;
byte noteByte;
byte velocityByte;

void setup(){
  Serial.begin(31250);
}

void checkMIDI(){
  do{
    if (Serial.available()){
      commandByte = Serial.read();//read first byte
      noteByte = Serial.read();//read next byte
      velocityByte = Serial.read();//read final byte
    }
  }
  while (Serial.available() > 24);//when three bytes available
}
    

void loop(){
  checkMIDI();
}

J'ai bien une idée, mais ça ne me parait pas être la bonne solution.

UniseV
« Last Edit: April 30, 2013, 07:57:09 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

Salut,

Il te faut faire une machine d'état. ça ressemble à ça:

 
Code:
void loop(){
  static int state = 0;
  static byte note;

  byte b = Serial.read();
  if(b==0xF8) {
    // ignorer ou mesurer le temps pour synchroniser qqch
  }
  else {
    switch(state) {
    case 1:
      if(b==NOTE_ON) state = 2;
      // if(b==NOTE_OFF) state = ...;
      // ...
      break;
    case 2: // Attend la note
      note = b;
      state = 3;
      break;
    case 3: // Attend la vélocité
      byte velocity = b;
      // faire qqch
      state = 1; // retour à l'état initial
    }
  }
}

L'avantage avec le protocol midi (si mes souvenir sont bon) c'est que les commandes ont le msb à 1. Ce qui te permettrait
de simplifier cet exemple et de te resynchroniser sur une commande en cas de perte de donnée.
Logged

France
Offline Offline
Faraday Member
**
Karma: 55
Posts: 5347
Arduino Hacker
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Bonjour,

De plus, apparemment ces petits 0xF8 peuvent même venir s'intercaler A L'INTERIEUR d'un mot plus grand, du style : 0x90F8407F.
Tu est sûr de toi ? Parce que ça me semble bizarre ...
Si les messages midi ne sont pas atomique il est impossible de les interpréter correctement ...
Logged

Des news, des tuto et plein de bonne chose sur http://skyduino.wordpress.com !

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

Les messages temps réel sont intercalables n'importe quand. Cela concerne les codes 0xF8 à 0xFF

Pas mal d'info ici http://www.lehomestudio.org/midi.html
Logged

France
Offline Offline
Faraday Member
**
Karma: 55
Posts: 5347
Arduino Hacker
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Dans ce cas c'est vraiment idiot comme truc ...
Tu ne peut pas différencier une valeur en taille variable d'une commande temps réel si les deux se mélanges ...
Et ignorer tout les 0xF8 me semble un peu trop radicale comme solution ...

Il faudrait trouver un note d'application concernant les sysex temps réel et leurs gestions, je vois pas d'autre solution smiley-small
Logged

Des news, des tuto et plein de bonne chose sur http://skyduino.wordpress.com !

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

Je te laisse ta libre interprétation de l'idiotie de la norme MIDI smiley et t'invite à lire la spec en lien.

Pour faire court. Les paramètres d'une commande NOTE_ON sont sur 7 bits MSB à 0 ce qui permet de les différentier
des commandes.

Logged

Offline Offline
Faraday Member
**
Karma: 33
Posts: 4956
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Dans ce cas c'est vraiment idiot comme truc ...
Tu ne peut pas différencier une valeur en taille variable d'une commande temps réel si les deux se mélanges ...
Et ignorer tout les 0xF8 me semble un peu trop radicale comme solution ...

Il faudrait trouver un note d'application concernant les sysex temps réel et leurs gestions, je vois pas d'autre solution smiley-small
bonjour skywodd
non , j'avais détecté trop tard cette specificité en achetant un EMU 1X1 pour remettre en état un Korg M1 avec midiox
http://www.creative.com/emu/products/product.aspx?pid=19089

le filtrage sur un seul octet sur MSB semble assez trivial


edit : je viens de lire la reponse de marcha  smiley-cool

Logged

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

marcha,

Le problème de ta machine d'état, c'est qu'elle prend 3 tour de LOOP pour lire un mot... si j'ai bien compris.

EDIT: et il faut implémenter aussi un filtrage des RealTimeClock DANS les mots...
« Last Edit: April 30, 2013, 09:05:06 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

Bon voila ce que je compte faire, mais c'est pas très joli :

Code:
void setup()
{
  Serial.begin(31250);        // Begin Serial MIDI
}

void loop()
{
  static unsigned char charTmp,cmd,note,velo,newMsg;   // Characters for MIDI words

  if (Serial.available() > 0) { // Only if serial data arrived...
    charTmp = Serial.read();    // Read the first char...
    if (charTmp==0xF8) { // ...If it is a RealTimeClock Message...
      // ...just ignore it
    }
    else                 // But if it is NOT a RTC-M...
    {
      if (charTmp==0x90 || charTmp==0xB0) // It can be a NoteOn or SustainOn...
      {
        cmd=charTmp;     // Save the command message
        charTmp = Serial.read(); // Next char should be the NOTE...

        if (charTmp==0xF8) { // ...But if there is a RTC-M instead...                       
          note = Serial.read();   // ...ignore it and take the next one...
        }
        else note = charTmp; // ... else, take the char that was firstly read.

        charTmp = Serial.read(); // Next char should be the VELOCITY...

        if (charTmp==0xF8) { // ...But if there is a RTC-M instead...                       
          velo = Serial.read();   // ...ignore it and take the next one...
        }
        else velo = charTmp; // ... else, take the char that was firstly read.

        newMsg=1;   // To inform LOOP about new message avaliable
      }
      else {
        // Unrecognized MIDI CMD byte
      }
    }
  }

  if (newMsg==1) {
    // The new message is treated here
    newMsg=0;
  }
  // Normal LOOP CODE
}
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 !

France
Offline Offline
Faraday Member
**
Karma: 55
Posts: 5347
Arduino Hacker
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Pour faire court. Les paramètres d'une commande NOTE_ON sont sur 7 bits MSB à 0 ce qui permet de les différentier
des commandes.
La syntaxe des commandes midi n'est pas un truc que je garde en tête smiley-mr-green
Mais il n'y a pas de commande midi qui prend des arguments pouvant être >128 ?

J'avais bricolé fut un temps un parseur de fichier midi et je gérai des valeurs >128 par moment ...
C'est peut être bien tout sur 7 bits effectivement ... faut dire que j'avais codé mon truc en gérant que très sommairement les sysex classique smiley-sweat

Edit: j'ai le cerveau lent parfois ... en midi (version fichier) il y a le "delta temps" (qui correspondant au délai entre deux events midi) avant la commande.
C'est pour ça que je parlait de valeur "en taille variable", c'est une spécificité de la version fichier ... smiley-sweat
J'ai mélangé sysex classique, fichier midi et sysex RT ... ... bon bon bon, pause café ! smiley-mr-green
« Last Edit: April 30, 2013, 10:13:20 am by skywodd » Logged

Des news, des tuto et plein de bonne chose sur http://skyduino.wordpress.com !

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

Attention, Serial.read() n'est pas bloquant, il va te retourner -1 si il n'y a pas de donnée "available"

Au début de ton programme tu teste bien Serial.available() > 0, il est fort probable que Serial.available() te
retourne 1 lors de l'arrivée d'un octet car ton programme ne faisant rien d'autre il va réagir à l'arrivée du
premier octet MIDI reçu. Quelques microsecondes plus tard tu fais charTmp = Serial.read();
qui devrait te retourner -1 car il faut environ 320 microsecondes entre 2 bytes MIDI à 31250 bauds

Tu pourrais donc créer une fonction d'attente active d'un octet MIDI, mais je pense que la voie d'une machine
d'état comme dans mon premier exemple te permet plus facilement de faire autre chose avec ton cpu dans loop()

EDIT: mon premier exemple n'est pas fonctionnel (c'était illustratif). Si tu as besoin je peux y apporter quelques corrections pour en faire une base utilisable.



« Last Edit: May 01, 2013, 03:19:26 am by marcha » Logged

France
Offline Offline
Faraday Member
**
Karma: 55
Posts: 5347
Arduino Hacker
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Bien vu marcha, j'avais pas fait gaffe à ca.

Un petit :
Code:
while(Serial.available() < 1);
devrait déjà empêcher pas mal de probléme de lecture.
Logged

Des news, des tuto et plein de bonne chose sur http://skyduino.wordpress.com !

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

Je reformule pour vérifier que j'arrive bien à vous suivre :

Le problème de mon CODE :
Si je n'ai reçu que le premier octet d'un NOTEON par exemple, je risque de ne pas encore avoir reçu le(s) octet(s) suivant(s), au moment ou j'essaye de les lire.

Le problème de la de l'exemple de machine d’états de marcha :
Tu ne lis qu'un octet par tour de boucle LOOP... et c'est la que skywdd propose de boucler sur la machine d’état tant qu'on a des octets à traiter dans le buffer.

Ca me va pas mal, il faut juste être certain que même avec une liaison série qui communique en "continu", il reste assez de temps pour jouer le reste de la LOOP...

En tout cas j'en ai assez pour sortir un nouveau CODE  smiley-wink

EDIT: Hey, ça veut dire aussi qu'il faut que je prévois le cas ou je reçois PLUSIEURS NoteOn dans le même tour de boucle... car le reste de mon LOOP ne sera pas toujours vide  smiley-roll-sweat
« Last Edit: May 02, 2013, 10:14:11 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 !

France
Offline Offline
Faraday Member
**
Karma: 36
Posts: 3415
There is an Arduino for that
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset


Ca me va pas mal, il faut juste être certain que même avec une liaison série qui communique en "continu", il reste assez de temps pour jouer le reste de la LOOP...

En tout cas j'en ai assez pour sortir un nouveau CODE  smiley-wink

EDIT: Hey, ça veut dire aussi qu'il faut que je prévois le cas ou je reçois PLUSIEURS NoteOn dans le même tour de boucle... car le reste de mon LOOP ne sera pas toujours vide  smiley-roll-sweat

Garde la tête froide, tu t'emballes.
Le MIDI ce n'est pas très rapide je serais surpris, vu la boucle actuelle, que tu reçoives plusieurs caractères en un tour de boucle.
Logged

Paris
Offline Offline
Sr. Member
****
Karma: 2
Posts: 366
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.
« Last Edit: May 03, 2013, 05:56:30 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 !

Pages: [1] 2   Go Up
Jump to: