piano automatique

Bonjour a tous j'ai découvert l'arduino il y a deux ans .. depuis je réalise quelques montages et j'entame la mécanisation d'un piano acoustique avec des solenoids.

j'ai lu plusieurs sujets divers et la structure de mon projet est bien avancé..

j'utilise la bibliothèque midi pour lire les données recues via le shield midi et les interprétées via 3 Arduino mega.réparties pour les 81 notes de mon piano.

jusque la tout fonctionne presque a part certaines notes qui ne s'éteignent pas.. je pense que cela est du au fameux running status.. qui a été implémenté dans la dernière version de la bibliothèque midi .

malheureusement je n'ai ni trouvé comment utiliser le running status ni l'interprété dans mon code...

je me tourne donc vers vous pour solliciter votre aide.

ci dessous le code que j'utilise actuellement
l'idee serair d'inserer un "case" running status...

#include <MIDI.h>
#include <LiquidCrystal_I2C.h>

static const uint16_t DEBOUNCE_COUNT = 50;

//Déclarations de l'écran
LiquidCrystal_I2C lcd(0x27,16,2);

MIDI_CREATE_DEFAULT_INSTANCE();

byte note;
byte velocity;
byte ctrl;
byte val;

//#define USE_RUNNING_STATUS

void setup()
{
  int inMin = 22; // Lowest input pin
  int inMax = 53; // Highest input pin
  for (int i = inMin; i <= inMax; i++) {
    pinMode(i, OUTPUT);
    
  }
  // initialisation de l'ecran pour debugage lecture midi
  lcd.init();
  lcd.init(); 
  lcd.backlight();
  lcd.setCursor(0,0);
  lcd.print("hello");
  delay(1000);
  lcd.clear();
  

  // We want to receive messages on all channels
  
  MIDI.begin(MIDI_CHANNEL_OMNI);
  MIDI.turnThruOn();
  
}

void loop()
{
  static uint8_t  ticks = 0;        //je ne sais pas a quoi cela sert
  static uint8_t  old_ticks = 0;    //je ne sais pas a quoi cela sert


  // put your main code here, to run repeatedly:

  
    if (  MIDI.read())
    {
      switch (MIDI.getType())
      {
        
        case midi::NoteOn :
          {
          
            note=MIDI.getData1();           
            velocity=MIDI.getData2();                   
            if (note > 20 && note < 53){     //arduino 1 notes 21 à 52
              digitalWrite((note + 1), HIGH);
              lcd.setCursor(0,1);
            lcd.print(note);
            lcd.setCursor(5,1);
            lcd.print(velocity);
            }else{}
            note = 0;
            velocity = 0;
           
              
          }
          break;
          case midi::NoteOff :
          {
           
            note=MIDI.getData1();                                
            velocity = MIDI.getData2();
            if (note > 20 && note < 53){
              digitalWrite((note + 1), LOW);
              lcd.setCursor(0,1);
            lcd.print(note);
            lcd.setCursor(5,1);
            lcd.print(velocity);
            }else{}
            
          
            note = 0;
            velocity = 0;
          }
          break;
/*        case midi::AfterTouchPoly :   // pas encore utilisé
          {
            lcd.clear();
            lcd.setCursor(0,0);
            lcd.print("PolyAT:");
            lcd.setCursor(8,0);
            lcd.print(MIDI.getChannel());
            lcd.setCursor(0,1);
            lcd.print("Note:");
            lcd.setCursor(6,1);
            lcd.print(MIDI.getData1());
            lcd.setCursor(8,1);
            lcd.print("AT:");
            lcd.setCursor(12,1);
            lcd.println(MIDI.getData2());
          }
          break;*/
        case midi::ControlChange :   // sert au canal de pédal douce soustain et note off général
          {
            
            ctrl=MIDI.getData1();           
            val=MIDI.getData2();
            peDals(ctrl, val);
            ctrl=0;
            val=0;
            
          }
          break;
 /*       case midi::ProgramChange :
          {
            Serial.print("PropChange, chan: ");
            Serial.print(MIDI.getChannel());
            Serial.print(" program: ");
            Serial.println(MIDI.getData1());
          }
          break;
        case midi::AfterTouchChannel :
          {
            Serial.print("ChanAT, chan: ");
            Serial.print(MIDI.getChannel());
            Serial.print(" program: ");
            Serial.println(MIDI.getData1());

          }
          break;
        case midi::PitchBend :
          {
            uint16_t val;

            Serial.print("Bend, chan: ");
            Serial.print(MIDI.getChannel());

            // concatenate MSB,LSB
            // LSB is Data1
            val = MIDI.getData2() << 7 | MIDI.getData1();

            Serial.print(" value: 0x");
            Serial.println(val, HEX);


          }
          break;*/
        case midi::SystemExclusive :    // activé pour débugage mais ne sait pas a quoi cela sert
          {
            // Sysex is special.
            // could contain very long data...
            // the data bytes form the length of the message,
            // with data contained in array member
            uint16_t length;
            const uint8_t  * data_p;

            lcd.setCursor(8,0);
            //Serial.print("SysEx, chan: ");
            //Serial.print(MIDI.getChannel());
            length = MIDI.getSysExArrayLength();

            lcd.print("Dx");
            data_p = MIDI.getSysExArray();
            for (uint16_t idx = 0; idx < length; idx++)
            {
              lcd.setCursor(10,0);
              lcd.print(data_p[idx], HEX);
              //Serial.print(" 0x");
            }
            //Serial.println();
          }
          break;
/*        case midi::TimeCodeQuarterFrame :
          {
            // MTC is also special...
            // 1 byte of data carries 3 bits of field info 
            //      and 4 bits of data (sent as MS and LS nybbles)
            // It takes 2 messages to send each TC field,
          
            Serial.print("TC 1/4Frame, type: ");
            Serial.print(MIDI.getData1() >> 4);
            Serial.print("Data nybble: ");
            Serial.println(MIDI.getData1() & 0x0f);
          }
          break;
        case midi::SongPosition :
          {
            // Data is the number of elapsed sixteenth notes into the song, set as 
            // 7 seven-bit values, LSB, then MSB.
          
            Serial.print("SongPosition ");
            Serial.println(MIDI.getData2() << 7 | MIDI.getData1());
          }
          break;
        case midi::SongSelect :
          {
            Serial.print("SongSelect ");
            Serial.println(MIDI.getData1());
          }
          break;
        case midi::TuneRequest :
          {
            Serial.println("Tune Request");
          }
          break;
        case midi::Clock :
          {
            ticks++;

            Serial.print("Clock ");
            Serial.println(ticks);
          }
          break;
        case midi::Start :
          {
            ticks = 0;
            lcd.clear();
            lcd.setCursor(10,0);
            lcd.print("Start");
          }
          break;
        case midi::Continue :
          {
            ticks = old_ticks;
            lcd.clear();
            lcd.setCursor(10,0);
            lcd.print("cont");
          }
          break;
        case midi::Stop :
          {
            old_ticks = ticks;
            lcd.clear();
            lcd.setCursor(10,0);
            lcd.print("Stop");
          }
          break;
      case midi::ActiveSensing :
          {
            Serial.println("ActiveSense");
          }
          break;
        case midi::SystemReset :
          {
            Serial.println("Stopping");
          }
          break;
        case midi::InvalidType :
          {
            Serial.println("Invalid Type");
          }
          break;
        default:
          {
            Serial.println();
          }
          break;
          
      }*/
    }
  }
}


void peDals(byte ctrl, byte val)
{
lcd.clear();
lcd.setCursor(0,0);
lcd.print(ctrl);
lcd.setCursor(4,0);
lcd.print(val);
int force = LOW;
         if (val > 63) 
   {
    force = HIGH;
    } 
  else
    {
    force = LOW;
     }

  if (ctrl == 64) 
         {
    digitalWrite(7, force);
    
  
         } else if (ctrl == 67) 
      {
        digitalWrite(6, force);
      } else if (ctrl == 120 || ctrl >= 123 )
      {
        for (int j = 22; j <= 53; j++) {
    digitalWrite(j, LOW);}//all noteoff
        }else if (ctrl == 122 && val == 0){
          for (int j = 22; j <= 53; j++) {
    digitalWrite(j, LOW);}//all noteoff
          }else {}
}

Bonjour et Bienvenue

C'est déjà un travail bien avançé ....mais pas encore réellement Fini (cf le nom de ce sous-forum)

Vois les consignes de fonctionnement du forum (ou poser les questions ? comment intégrer du code dans un messages ? .....) Les consignes sont dans 2 ou 3 messages épinglés en tête de forum.

Demandes au modérateur (lien Report to Moderator en bas à gauche) le déplacement du fil de discussion au bon endroit. (forum principal) La bàs il sera à sa place, aura une bonne visibilité.
Ici il est dans un recoin du forum dédié exculsivement à la présentation de ce qui est Fini, (peu de fréquentation)
On ne vient pas içi pour répondre mais pour découvrir ce qui est au point.

A+

merci et désolé je viens de déplacer le post .. je tacherais de le compléter aussi au fur et a mesure de l'avancement..
mais la je bloque vraiment sur le fameux runnig status ...

peux tu mettre ton code entre les balises appropriées (icône </> ou) il sera copiable dans l'IDE Arduino pour examen et plus lisible en particulier sur smartphone ?

ça donnera un bloc de code comme ça , avec son bouton Select:

bla bla....

jeyf:
merci et désolé je viens de déplacer le post .. je tacherais de le compléter aussi au fur et a mesure de l'avancement..
mais la je bloque vraiment sur le fameux runnig status ...

Bonsoir
Lib MIDI utilisée ?
lien ?
Tu injecte du MIDI vers ton piano avec quoi/quel equipement ?

En parcourant le code je remarque que tu déclares les pin 22 à 53 mais que tu utilises jusqu'à 55.

concernant la library il s'agit de l'a MIDI Library by Forty Seven Effects version 4.3.1
pour l'instant pour tester je branche un clavier midi ou une prise midi bluetooth

ca me permet d'afficher les note off ou on et certains control change sur l'afficheur lcd.
j'ai aussi branché des leds sur les pins 47 à 53 pour simuler les futurs solenoids qui mettrons en mouvement les touches du piano...

avec le clavier midi cela marche bien mais avec la lecture de certains fichiers midi, je constate que certaines notes ( leds ) ne s'allument pas ou ne s'eteignent pas a cause des running status...

c'est a dire que un note on ou note off n'est pas suivi que d'une seule note et velocité mais de plusieurs....

la version 4.3.1 de la bibliotheque midi gere le running status mais je ne sais pas comment l'utiliser.

pour les pins 22 à 53 mais notes de 20 à 55 je n'ai pas fait attention j'ai repris un ancien code et comme je testais le decodage je pensais ajuster le nombre de notes et de pin plus tard ... je viens de corriger le code...

bonjour,

l'idee serair d'inserer un "case" running status...

Hélas, ce n'est pas possible. Running Status n'est pas un type de message MIDI.

Le Running Status est une méthode de compression (pour diminuer le flux MIDI).
Les messages MIDI sont tous composés d'un octet de commande suivi de 1 ou plusieurs octets de données.
L'octet de commande contient :

  • le type de message (Note On, Note Off, Programe Change, Aftertouch...)
  • le numéro de canal MDI (1 à 16)
    Quand un piano MIDI émet en Running Status, il n'envoie pas l'octet de commande si celui-ci est identique au précédent. Si je plaque un accord de 4 notes sur le piano, il envoie normalement 4 messages Note On complets, soit 12 octets. Avec le Running Status, seul le 1er octet de commande est envoyé, puis les données, soient 3+2+2+2=9 octets.

Le fonction MIDI.getType() que tu utilises analyse l'octet de commande pour trouver le type du message, si l'octet de commande manque, ça ne va pas marcher.

Il faut que :

  • tu récupères le 1er octet du message dès que possible, et que du détermines toi-même si c'est bien un octet de commande (sinon ce 1er octet est le 1er octet des données du message).
    Pour cela il suffit de tester le bit de poids fort de ce 1er octet. Les octet de commande ont toujours de valeurs comprises entre (en hexadécimal) 0x80 et 0xFF. Leur bit de poids fort est mis (c'est la norme MIDI).
    Au contraire, tous les octets de données ont un bit de poids fort à zéro ( valeur enre 0x00 et 0x7F).
    Donc si le message commence bien par un octet de commande, tu peux utiliser MIDI.getType() comme tu le fais.
    SINON (et si le mode Running Status est bien activé), l'octet de commande que tu dois utiliser est ... le même que pour le message précédent. Et pour le connaitre, il faut que ...

  • tu mémorises à chaque fois le dernier octet de commande reçu.

Ceci dit, il est possible que d'autres fonctions de la lib soient perturbées par cette compression. Tu aurais intérêt à l'utiliser le moins possible pour interpréter les messages. Récupère les octets bruts du messages et fais-en l'analyse toi-même.
[EDIT] enfin si la lib est bien compatible RS, ça devrait marcher de façon transparente ! Ne faut-il pas demander à la lib d'utiliser le décodage avec RS ?

J'ai programmé tout ça en jour, c'était au XXème siècle et en assembleur MOTOROLA 68000...

effectivement deux solutions

la premiere l'analyse du flux comme tu le propose.. dans ce cas ne serait il pas possible dans le "case" d'un note on d'analyser si c'est un RS et en fonction de demander les differentes notes et velocity? ou de n'en prendre qu'une ? mais comment ? demander si il y a un getData3() ? mais comment tester le getData3 et voir si il y a une erreur ou non ?

la seconde utiliser la fonction RS de la bibliothque... je l'ai activée dans le fichier midi.h en y mettant la valeur True.

j'ai vu qu'il fallait ajouter dans le programme ino

#define USE_RUNNING_STATUS

mais je ne trouve aucune doc indiquant comment l'utiliser.

jeyf:
effectivement deux solutions

la premiere l'analyse du flux comme tu le propose.. dans ce cas ne serait il pas possible dans le "case" d'un note on d'analyser si c'est un RS et en fonction de demander les differentes notes et velocity? ou de n'en prendre qu'une ? mais comment ? demander si il y a un getData3() ? mais comment tester le getData3 et voir si il y a une erreur ou non ?

la seconde utiliser la fonction RS de la bibliothque... je l'ai activée dans le fichier midi.h en y mettant la valeur True.

j'ai vu qu'il fallait ajouter dans le programme ino

#define USE_RUNNING_STATUS

mais je ne trouve aucune doc indiquant comment l'utiliser.

Bonjour
La lib MIDI que tu utilise ne gere le running status qu'en émission MIDI
extrait de midi_Settings.h

/*! Running status enables short messages when sending multiple values of the same type and channel.\n
Warning: does not work with some hardware, enable with caution.
*/

static const bool UseRunningStatus = false;

Ce qui n'est pas ton cas il me semble : tu ne fais que lire/ecouter du MIDI avec tes mega, pas en générer
Question :
A tu déjà fais des logs du MIDI entrant (sortant de ton generateur MIDI) vers tes MEGA , pour verifier que le probleme viendrait bien d'un problème de running status "non /mal interprété" par tes mega ?

bon ok pour le RS de la biblio en effet je ne fais que lire et je n'envoie pas.

je vais donc devoir me pencher sur le decodage en direct.

je n'ai pas fait de log car je n'arrive pas a afficher en direct sur le serial monitor surement car le shield utilise le pin tx rx de ce fameux serial usb..
la j'avoue que je ne maitrise pas j'ai donc fait un affichage sur lcd pour contourner le probleme mais les données défilent trop vite..

si quelqu'un a la solution pour afficher les données sur le serial monitor?

ou un code de base pour lire les octets entrants et les afficher sur le moniteur serie?

j'ai essayé ca

void setup() {
  Serial.begin(38400);
  
}
 
void loop() {
  if (Serial.available() > 0) {
   Serial.println(Serial.read(), BIN);

 }
}

et j'obtiens en appuyant sur une note
100000
11111000
0
et en la relachant
11111111
1111000
0

jeyf:
bon ok pour le RS de la biblio en effet je ne fais que lire et je n'envoie pas.

je vais donc devoir me pencher sur le decodage en direct.

je n'ai pas fait de log car je n'arrive pas a afficher en direct sur le serial monitor surement car le shield utilise le pin tx rx de ce fameux serial usb..
la j'avoue que je ne maitrise pas j'ai donc fait un affichage sur lcd pour contourner le probleme mais les données défilent trop vite..

si quelqu'un a la solution pour afficher les données sur le serial monitor?

ou un code de base pour lire les octets entrants et les afficher sur le moniteur serie?

j'ai essayé ca

void setup() {

Serial.begin(38400);
 
}

void loop() {
 if (Serial.available() > 0) {
  Serial.println(Serial.read(), BIN);

}
}




et j'obtiens en appuyant sur une note 
100000
11111000
0
et en la relachant 
11111111
1111000
0

déja le taux de baud standard MIDI , c'est 31250 , pas 38400 :grin:

Si déjà tu arrive à faire çà

je te conseille d'utiliser un terminal serie qui fait du log
si tu es sous windows , je te conseille terminalbpp

Artouste:
déja le taux de baud standard MIDI , c'est 31250 , pas 38400 :grin:

Si déjà tu arrive à faire çà

je te conseille d'utiliser un terminal serie qui fait du log
si tu es sous windows , je te conseille terminalbpp

j'avais mis 38400 pour le debugage car le moniteur serie du logiciel arduino ne permet pas de regler 31250

je suis sous macos

jeyf:
j'avais mis 38400 pour le debugage car le moniteur serie du logiciel arduino ne permet pas de regler 31250

je suis sous macos

Il y a des utilisateurs de MAC ici , ce n'est pas mon cas :wink:

Attend de voir ce qu'ils ont à te conseiller comme "terminal un peu evolué"

regarde ce que çà donne en restant 34800 avec çà

void setup() {
  Serial.begin(38400);
 
}
 
void loop() {
  if (Serial.available() > 0) {
   Serial.print(Serial.read(), HEX);
   Serial.print(" ");

 }
}

j obtiens en appuyant sur un do 78 0 FF
et en relachant 78 0
puis en reappuyant F8 0 FF
et en relachant 78 0

si j'appuies sur la note précedente le si :
77 0 FF
puis F7 0

pas de Mac , mais CuteCom que j'utilise sous Linux est censé fonctionner sous OSX.
' bauds à volonté' (custom) et logging... "fortement inspiré du Terminal Bray++ selon l'auteur"

entre temps j'ai installé terminalbpp sous windows sur machine virtuelle
j'ai réglé le debit à 31250
et j'obtiens un resultat plus coherent pour le do
quand j'appuie 3C 40 ( en HEX ) 111100 1000000 (en BIN)

quand je relache 3C 0 (en HEX) 111100 0 (en BIN)
pour le si 3B 40 ou 111011 1000000 a l'appui ou 3B 0 ou 111011 0 au relachement

jeyf:
entre temps j'ai installé terminalbpp sous windows sur machine virtuelle
j'ai réglé le debit à 31250
et j'obtiens un resultat plus coherent pour le do
quand j'appuie 3C 40 ( en HEX ) 111100 1000000 (en BIN)

quand je relache 3C 0 (en HEX) 111100 0 (en BIN)
pour le si 3B 40 ou 111011 1000000 a l'appui ou 3B 0 ou 111011 0 au relachement

Non ce n'est pas plus coherent 8)
tu a bien passé le programme arduino à 31250 baud comme pour terminalBpp
en toute logique tu devrais pour le 1er byte reçu etre > à
0x80 en hexa
1000 0000 en binaire

j'ai bien mis a 31250 bauds dans l'arduino et le terminal

alors quand j'allume le clavier midi et que je presse une note
j'ai en effet 90 en Hex et 144 en DEC ce qui correspond a note on
mais cela ne s'affiche qu'au debut ensuite je n'ai que les notes et la velocité..
du style 90 3C 40 3C 0 3C 40 3C 0

jeyf:
j'ai bien mis a 31250 bauds dans l'arduino et le terminal

alors quand j'allume le clavier midi et que je presse une note
j'ai en effet 90 en Hex et 144 en DEC ce qui correspond a note on
mais cela ne s'affiche qu'au debut ensuite je n'ai que les notes et la velocité..
du style 90 3C 40 3C 0 3C 40 3C 0

OK
on avance 8)

il est indiqué il me semble (faut que je retrouve la doc)
edit : retrouvé

qu'en cas de running sensing une velocity à 0 (ZERO) pour une note donnée doit etre traité comme du NOTE OFF de la note.

il se passe quoi avec tes solénoïdes ?
lls restent activés ?
tu gere la durée d'activation des solénoïdes où/comment dans ton programme ?