Realisation D'un Sequencer Midi 4 Step

Bonjour

Je souhaite réaliser un step séquencer Midi avec un Arduino Uno

4 BOUTONS (on/off)
4 LEDS
1 Potentiomètre ou encoder pour la variation du BPM (avec horloge midi si possible)
1 Rotary Encoder qui me permettrai de changer de Note Midi

Quelque chose de ce genre la;

J’ai regarde beaucoup de projet de ce type (adafruit UNTZtrument ,sugarcube)
Mais ces projets se composent de matrice de led et ne correspondent pas a ce que je voudrai réaliser

Car je souhaiterai que la séquence de lecture se fasse sur une seule ligne (une piste)
sur 4 Boutons
une séquence avec une visualisation avec des leds en fonction du bpm
et lorsque j’appuie sur un Switch la note dans la séquence est jouée et envoyée en midi
la led correspondante a la position de la note reste allumé

J’ai commencé a bricolé ce code assez rudimentaire je vous l’accorde
Pour la séquence de led c’est ok je peux change la vitesse de la séquence avec
la potentiomètre

#include <MIDI.h>  // INlcude MIDI library
// define the pins we use

int LED1 = 8;
int LED2 = 9;
int LED3 = 10;
int LED4 = 11;

int Time = A0;

// general midi notes
char note1 = 60; //
char note2 = 60; //
char note3 = 60; //
char note4 = 60; //

// Variables


int currentSwitchState1 = LOW;
int currentSwitchState2 = LOW;
int currentSwitchState3 = LOW;
int currentSwitchState4 = LOW;
int currentSwitchState5 = LOW;
int currentSwitchState6 = LOW;

const int buttonPin = 2 ; 
const int buttonPin2 = 3;
const int buttonPin3 = 4;
const int buttonPin4 = 5;


int buttonState = 0; 
int buttonState2 = 0;
int buttonState3 = 0;
int buttonState4 = 0;


int lastButtonState = 0;
int lastButtonState2 = 0;
int lastButtonState3 = 0;
int lastButtonState4 = 0;


int toggleState = 0;
int toggleState2 = 0;
int toggleState3 = 0;
int toggleState4 = 0;

int Tempo = 80;

void setup() {

  // set the states of the I/O pins:

  
  pinMode(2, INPUT_PULLUP); // initialize the pushbutton pin as an input:
  pinMode(3, INPUT_PULLUP);
  pinMode(4,INPUT_PULLUP); 
  pinMode(5, INPUT_PULLUP);  
 

  pinMode(LED1, OUTPUT);
  pinMode(LED2, OUTPUT);
  pinMode(LED3, OUTPUT);
  pinMode(LED4, OUTPUT);
  
  // set MIDI baud rate :
  Serial.begin(31250); 

}

void displayled  ()
{
  
  digitalWrite(LED1, HIGH);
  Tempo = analogRead(Time); 
  delay(Tempo+1);
  digitalWrite(LED1, LOW);
  noteOn(0x90, note1, 0x00);

  digitalWrite(LED2, HIGH);
  Tempo = analogRead(Time);
  delay(Tempo+1);
  digitalWrite(LED2, LOW);
  noteOn(0x90, note1, 0x00);

  digitalWrite(LED3, HIGH);
  Tempo = analogRead(Time);
  delay(Tempo+1);
  digitalWrite(LED3, LOW);
 noteOn(0x90, note1, 0x00);
 
  digitalWrite(LED4, HIGH);
  Tempo = analogRead(Time);
  delay(Tempo+1);
  digitalWrite(LED4, LOW);
  noteOn(0x90, note1, 0x00);
  
}

void loop()
{
  
displayled  ();
 //BUTTON 01
   {
  buttonState = digitalRead(buttonPin);
  if(buttonState != lastButtonState && buttonState == 1 && toggleState == 0) 
  {
 
  toggleState = 1;
   noteOn(0x90, note1, 0x45);
  }
  
  else if(buttonState != lastButtonState && buttonState == 1 && toggleState == 1) {
  
  toggleState = 0;
  noteOn(0x90, note1, 0x00);
  }
  lastButtonState = buttonState;
  }

//BUTTON 02
/*
   {
  buttonState2 = digitalRead(buttonPin2);
  if(buttonState2 != lastButtonState2 && buttonState2 == 1 && toggleState2 == 0) {
    MIDI.sendControlChange(52, 127, 1);
  toggleState2 = 1;
  }
  
  else if(buttonState2 != lastButtonState2 && buttonState2 == 1 && toggleState2 == 1) {
    MIDI.sendControlChange(52, 0, 1);
  toggleState2 = 0;
  }
  lastButtonState2 = buttonState2;
  }

//BUTTON 03
   {
  buttonState3 = digitalRead(buttonPin3);

  if(buttonState3 != lastButtonState3 && buttonState3 == 1 && toggleState3 == 0) {
    MIDI.sendControlChange(53, 127, 1);
  toggleState3 = 1;
  }
  else if(buttonState3 != lastButtonState3 && buttonState3 == 1 && toggleState3 == 1) {
    MIDI.sendControlChange(53, 0, 1);
  toggleState3 = 0;
  }
  lastButtonState3 = buttonState3;
  }
//BUTTON 04
   {
  buttonState4 = digitalRead(buttonPin4);
  if(buttonState4 != lastButtonState4 && buttonState4 == 1 && toggleState4 == 0) {
    MIDI.sendControlChange(54, 127, 1);
  toggleState4 = 1;
  }
  else if(buttonState4 != lastButtonState4 && buttonState4 == 1 && toggleState4 == 1) {
    MIDI.sendControlChange(54, 0, 1);
  toggleState4 = 0;
  }
  lastButtonState4 = buttonState4;
  }
*/
}

// Send a MIDI note-on/off message.  
void noteOn(char cmd, char pitch, char velocity) 
{
  Serial.print(cmd);
  Serial.print(pitch);
  Serial.print(velocity);
}

Mais pour les switchs je n’arrive pas a les faire changer d’état dans la séquence
Les notes seules (sans séquence de leds) sont bien envoyé lors d’un pression sur un switch (HairlessMidi et LoopBe1)

Je ne sais pas si c’est possible avec uniquement un code et un arduino car la plupart de projet de séquencer utilise un teensy 3.1 et des multiplexers

J’ai testé pour réaliser mon projet
le multiplexage et le sugarcube
Les tutoriels sont bien expliqués avec les schemas de câblages

J’ai fabriquer 2 matrice une avec des led et une avec des boutons
Pour simuler un PCB Sparkfun 4x4

/

//this code sends data to the 74HC595 without "shiftOut"

//pin connections- the #define tag will replace all instances of "latchPin" in your code with A1 (and so on)
#define latchPin 6
#define clockPin 5
#define dataPin 7

//looping variables
byte i;
byte j;

//storage variable
byte dataToSend;

//storage for led states, 4 bytes
byte ledData[] = {15, 15, 15, 15};

void setup() {
  //set pins as output
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
}

void loop() 
{

  for (i=0;i<4;i++)
  {
   or (j=0;j<4;j++)
  {
        
    //send data from ledData to each row, one at a time
    byte dataToSend = (1 << (i+4)) | (15 & ~ledData[j]);
          // setlatch pin low so the LEDs don't change while sending in bits
    digitalWrite(latchPin, LOW);
   //    // shift out the bits of dataToSend to the 74HC595
//    shiftOut(dataPin, clockPin, LSBFIRST, dataToSend);
// the code below is the equivalent of the two lines above
    for (j=0;j<8;j++){
      digitalWrite(clockPin,LOW);
      digitalWrite(dataPin,((dataToSend>>j)&1));
      digitalWrite(clockPin,HIGH);
    }
   //set latch pin high- this sends data to outputs so the LEDs will light up
    digitalWrite(latchPin, HIGH);
    delay(300);//wait
  }  
  }
}

Avec ce programme la colonnes de led se deplace au rythme du delay
Ensuite deuxième programme présent dans le tutoriel

//basic midi test
//by Amanda Ghassaei 2012
//this firmware will cause the buttons to light up momentarily while they are pressed.

//pin connections
int ledLatchPin = 6;
int ledClockPin = 5;
int ledDataPin = 7;
int buttonLatchPin = 4;
int buttonClockPin = 3;
int buttonDataPin = 2;
//looping variables
byte i;
byte j;
byte k;
//storage for led states, 4 bytes
byte dataToSend;
byte ledData[] = {0, 0, 0, 0};
byte buttonCurrent[] = {0,0,0,0};
byte buttonLast[] = {0,0,0,0};
byte buttonEvent[] = {0,0,0,0};
byte buttonState[] = {0,0,0,0};
//button debounce counter- 16 bytes
byte buttonDebounceCounter[4][4];

//MIDI variables
int velocity = 100;
int noteON = 144;
int MIDIoffset = 60;

void setup() {
  DDRD = 0xFA;//set pins D7-D4 as output, D2 as input
  
  Serial.begin(31250);//set midi baud rate
}

// buttonCheck - checks the state of a given button.
//this buttoncheck function is largely copied from the monome 40h firmware by brian crabtree and joe lake
void buttonCheck(byte row, byte index)
{
  if (((buttonCurrent[row] ^ buttonLast[row]) & (1 << index)) &&   // if the current physical button state is different from the
  ((buttonCurrent[row] ^ buttonState[row]) & (1 << index))) {  // last physical button state AND the current debounced state

    if (buttonCurrent[row] & (1 << index)) {                      // if the current physical button state is depressed
      buttonEvent[row] = 1 << index;              // queue up a new button event immediately
      buttonState[row] |= (1 << index);                         // and set the debounced state to down.
  }
    else{
      buttonDebounceCounter[row][index] = 12;
    }  // otherwise the button was previously depressed and now
    // has been released so we set our debounce counter.
  }
  else if (((buttonCurrent[row] ^ buttonLast[row]) & (1 << index)) == 0 &&  // if the current physical button state is the same as
  (buttonCurrent[row] ^ buttonState[row]) & (1 << index)) {        // the last physical button state but the current physical
    // button state is different from the current debounce 
    // state...
    if (buttonDebounceCounter[row][index] > 0 && --buttonDebounceCounter[row][index] == 0) {  // if the the debounce counter has
      // been decremented to 0 (meaning the
      // the button has been up for 
      // kButtonUpDefaultDebounceCount 
      // iterations///

      buttonEvent[row] = 1 << index;    // queue up a button state change event

      if (buttonCurrent[row] & (1 << index)){          // and toggle the buttons debounce state.
        buttonState[row] |= (1 << index);
      }
      else{
        buttonState[row] &= ~(1 << index);
      }
    }
  }
}

void shift(){

  for (i=0;i<4;i++)
  {
    
    buttonLast[i] = buttonCurrent[i];
    
    byte dataToSend = (1 << (i+4)) | (15 & ~ledData[i]);
      
    // set latch pin low so the LEDs don't change while sending in bits
    digitalWrite(ledLatchPin, LOW);
    // shift out the bits of dataToSend
    shiftOut(ledDataPin, ledClockPin, LSBFIRST, dataToSend);  
    //set latch pin high so the LEDs will receive new data
    digitalWrite(ledLatchPin, HIGH);
      
    //once one row has been set high, receive data from buttons
    //set latch pin high
    digitalWrite(buttonLatchPin, HIGH);
    //shift in data
    buttonCurrent[i] = shiftIn(buttonDataPin, buttonClockPin, LSBFIRST) >> 3;
    //latchpin low
    digitalWrite(buttonLatchPin, LOW);
    
    for (k=0;k<4;k++){
      buttonCheck(i,k);
    }
  }
}
void updateLEDs()
{ //update the leds to reflect hte state of the buttons
  for (j=0;j<4;j++){
    ledData[j] = buttonState[j];
   }
}

void MIDImessage(int command, int MIDInote, int MIDIvelocity) {//send s a MIDI message
  Serial.write(command);//send note on or note off command 
  Serial.write(MIDInote);//send pitch data
  Serial.write(MIDIvelocity);//send velocity data
}

void sendMIDI()
{
  for (byte a=0;a<4;a++){
    for (byte b=0;b<4;b++){
      if (buttonEvent[a]&(1<<b)){
        buttonEvent[a]&=~(1<<b);//zero button event
        if (buttonState[a]&(1<<b)){
          MIDImessage(noteON,(MIDIoffset+a*5+b),100);
        }
        else{
          MIDImessage(noteON,(MIDIoffset+a*5+b),0);
        }
      }
    }
  }
}


void loop() 
{
  shift();
  updateLEDs();
  sendMIDI();
}

A chaque pression d’un bouton de la matrice la led correspondante au bouton s’allume et un note midi est envoyé
Avec le logiciel Hairless Midi on visualise bien les notes midi

Mais ensuite lorsque je charge le programme complet du sugarcube comme indiqué dans le tutoriel
bien plus rien ne fonctionne il ne se passe rien
Même avec une alimentions externe 9v 1A

J’essaye de décrypter le programme mais mes connaissances en programmation sont assez basique
Je mis remet progressivement

La fonction qui m’interresse et la fonction stepsequencer
Mais son programme appelle beaucoup de sous programme
D’apres ce que j’ai compris la fonction delay et remplacer par un timer ce qui permet de faire plusieurs action en meme temps.

//  /*

//  */
  #include "SugarCube.h"
  StepSequencer::StepSequencer()
  {
    _tempoTimer = 0;
    _maxTempo = this->maxTempoFromPotVal(_sugarcube->getPot2Val());
    _xOffset = xOffsetFromPotVal(_sugarcube->getPot1Val());
    _playhead = absolutePosition(15);//start at -1
    
    _velocity = 100;
    //change these to change available notes
    _notes[3] = 72;
    _notes[2] = 67;
    _notes[1] = 63;
    _notes[0] = 60;   
    
    this->clearAllStorage();
  }
  
  void StepSequencer::routine100kHz()
  {
    if (_tempoTimer++>_maxTempo){
      _tempoTimer = 0;
      this->incrementPlayhead();
    }
  }
  
  void StepSequencer::buttonPressed(byte xPos, byte yPos)
  {
    byte colState = _seqStates[this->absolutePosition(xPos)];
    if (colState & 1<<(3-yPos)){//if already on
      colState &= ~(1<<(3-yPos));//turn off
      _sugarcube->turnOffLED(xPos, yPos);
    } else {
      colState |= (1<<(3-yPos));//turn on
      _sugarcube->turnOnLED(xPos, yPos);
    }
    _seqStates[this->absolutePosition(xPos)]  = colState;
  }
  
  byte StepSequencer::absolutePosition(byte pos)
  {
    return (pos+_xOffset)&15;
  }
  
  byte StepSequencer::relativePosition()
  {
    return (_playhead+16-_xOffset)&15;
  }
  
  void StepSequencer::pot1HasChanged(int val)
  {
    _xOffset = xOffsetFromPotVal(val);
    for (byte i=0;i<4;i++){
      _sugarcube->setLEDCol(i, _seqStates[this->absolutePosition(i)]);
    }
    if (this->relativePosition()<4) _sugarcube->setLEDCol(this->relativePosition(), 15);//turn on column
  }
  
  void StepSequencer::pot2HasChanged(int val)
  {
    _maxTempo = this->maxTempoFromPotVal(val);
  }
  
  byte StepSequencer::maxTempoFromPotVal(int val)//10 bit val
  {
    return ((val>>6) + 1)*5;
  }
  
  void StepSequencer::incrementPlayhead()
  {
    if (this->relativePosition()<4)_sugarcube->setLEDCol(this->relativePosition(), _seqStates[_playhead]);//set col to original state
    this->playNotesForStates(_seqStates[_playhead], false);
    _playhead++;
    if (_playhead>15) _playhead = 0;
    if (this->relativePosition()<4) _sugarcube->setLEDCol(this->relativePosition(), 15);//turn on column
    this->playNotesForStates(_seqStates[_playhead], true);
  }
  void StepSequencer::playNotesForStates(byte column, boolean noteOn)
  {
    for (byte i=0;i<4;i++){
      if (column & 1<<i){
        if (noteOn){
          _sugarcube->noteOn(_notes[i], _velocity);
        } else {
          _sugarcube->noteOff(_notes[i]);
        }
      }
    }
  }
  
  void StepSequencer::wasShaken()
  {
    this->clearAllStorage();
    for (byte i=0;i<4;i++){
      _sugarcube->noteOff(_notes[i]);//turn off any notes that might be stuck on
    }
     _sugarcube->clearLEDs();
     if (this->relativePosition()<4) _sugarcube->setLEDCol(this->relativePosition(), 15);//turn on column
  }
  
  void StepSequencer::clearAllStorage()
  {
    for (byte i=0;i<16;i++){
        _seqStates[i] = 0;
    }
  }

J’ai réalisé le projet SUGARCUBE

On a ici une seule Matrice De Led Button 4x4 Sparkfun

J’ai toujours le même problème lorsque je charge le programme final dans l’Arduino il ne se passe rien par la suite

Or je peux effectuer des programmes test et “m’amuser” avec la matrice de led
Programmes test qui comprennent eux une boucle principale
Les buttons et les led fonctionnent bien donc le schéma de câblage est bien respecté
J’ai checké également les éventuels faux contact du au soudures

Voici le programme principal qui appelle toutes les libraries
on constate qu’il n’y a pas de boucle principale car nous faisons face à de la programmation orienté objet d’après ce que j’ai pu comprendre

J’ai pris contact avec une personne qui a réaliser ce projet il m’a indiqué qu’il avait bien utilisé le programme qu’a mis a disposition la programmatrice,mais bon le projet date

 #include "SugarCube.h"
  
  SugarCube sugarcube;

  void setup()
  {
//    default pin connections are given below:
//    Analog
//    0 - Gyroscope Y (Y4.5)
//    1 - Potentiometer 1
//    2 - Gyroscope X (X4.5)
//    3 - Accelerometer Y (YAcc)
//    4 - Accelerometer X (XAcc)
//    5 - Potentiometer 2
//    Digital
//    0 - serial in - this much remain unconnected
//    1 - serial out - this is hooked up to the MIDI output
//    2 - 74HC165 data pin (Q7)
//    3 - 74HC165 clock pin (CP)
//    4 - 74HC165 latch pin (PL)
//    5 - 74HC595 clock pin (SH_CP)
//    6 - 74HC595 latch pin (ST_CP)
//    7 - 74HC595 data pin (DS)
//    
//    set custom pin connections using the following commands, this must happen before sugarcube.init()
//    leave digital pins 0 and 1 (RX/TX Serial pins) empty
sugarcube.setLedLatchPin(6);
 sugarcube.setLedClockPin(5);
 sugarcube.setLedDataPin(7);
  sugarcube.setButtonLatchPin(4);
  sugarcube.setButtonClockPin(3);
 sugarcube.setButtonDataPin(2);
//  sugarcube.setXAccPin(A4);
//    sugarcube.setYAccPin(A3);
 sugarcube.setPot1Pin(A1);
  sugarcube.setPot2Pin(A5);
//    sugarcube.setXGyroPin(A2);
//    sugarcube.setYGyroPin(A0);
    
    byte patchNum = sugarcube.init();
    switch(patchNum) {
      case 0:
        {
          StepSequencer stepSequencer;
          sugarcube.setDelegate(&stepSequencer);
          for (;;) {}
        }
      case 1:
        {
          Flin flin;
          sugarcube.setDelegate(&flin);
          for (;;) {}
        }
      case 2:
        {
          Boiing boiing;
          sugarcube.setDelegate(&boiing);
          for (;;) {}
        }
      case 3:
        {
          Arp arp;
          sugarcube.setDelegate(&arp);
          for (;;) {}
        }
      case 4:
        {
          SimpleMIDIKeyboard simpleMIDIKeyboard;
          sugarcube.setDelegate(&simpleMIDIKeyboard);
          for (;;) {}
        }
      case 5:
        {
          PixelTilt pixelTilt;
          sugarcube.setDelegate(&pixelTilt);
          for (;;) {}
        }
      case 6:
      case 7:
      case 8:
      case 9:
      case 10:
      case 11:
      case 12:
      case 13:
      case 14:
      case 15:
        {
        SerialComm serialComm;
        sugarcube.setDelegate(&serialComm);
        for (;;) {}
        }
      break;
    }
  }
  
  void loop()
  {

  }
  
  //---------------------------------------------------------------------
  //--------------------INTERRUPT ROUTINES-------------------------------
  //---------------------------------------------------------------------
  
  ISR(TIMER1_COMPA_vect)
  {//time 1 interrupt, at freq of 1kHz
      sugarcube.timer1Routine();
  }

  ISR(TIMER2_COMPA_vect) 
  {//timer 2 interupt, every 128us
      sugarcube.timer2Routine();
  }