# [Eurorack 001] ClockMaster

Publié le :

Metronome Electric - Ernest K. Adams - 21 Juillet 1903

Introduction

Si l’architecture, la scultpure ou la danse se donnent l’espace comme objet dans leurs arts, la peinture celui de la couleur, le cinéma celui de l’image; la musique, quant à elle, semble se caracteriser comme art du temps.

Non que ces regions du reel - l’espace, le temps, la couleur, etc. - etablissent de nettes frontières entres ces differents arts, mais chacun d’eux ne se laisse pas moins identifier par ces archetypes fondamentaux desquelles ils puisent et deploient leurs formes specifiques.

Ainsi, la musique apparait avant tout comme un art du temps ! Quoi de plus indispensable alors dans une approche electronique ou machinique de la musique de se munir d’un metronome, ou plus exactement dans le cas de l’electronique et des synthetiseur : d’un signal d’horloge !

Présentation

Plusieurs modules eurorack d’horloges existent déjà et proposent plus ou moins d’options. Pour n’en citer que quelques uns fameux, on troune le MakerNoise Tempy, le Shackmat Clock O’ Pown, l’Intellijel Steppu et le EricaSynth black cv Clock.

Principaux modules d'horloge eurorack sur le marché

La pluspart d’entre eux proposent, si ce n’est la deffinition d’une horloge interne, les réglages suivants qui serons récupérés:

  • Syn IN
  • CV Rate IN
  • Reset
  • TAP
  • MIDI IN/OUT
  • Divider
  • Swing

Le pourquoi du comment :

Afin de se munir d’une horloge maitre propre à syncroniser tout mes modules entre eux mais aussi pouvoir se syncrpniser avec d’autres machines et musiciens, je choisis d’ajouter une entrée/sortie midi ainsi qu’une entré “clock” permetant de se synchroniser avec une horloge exterieure.

Mais également dans le but de pouvoir synchroniser des modules ou machines avec un une horloge différente (de 1 bpq à 48 bpq), ainsi que diviser et surtout mutliplier mon horloge initiale j’ai souhaité ajouter deux potentiometre pour gerer deux sorties de division/multiplication d’horloge.

Compostion du module

L’enjeux restant de conserver un module le plus compact et neamoins egronomique possible au format 1U, l’enssemble sera composer de :

Représentation graphique :

Principaux modules d'horloge eurorack sur le marché

Description :

ENTRÉES :

  • CV IN (Rate)
  • CLOCK IN
  • MIDI IN

SORTIES :

  • CLOCK OUT
  • CLK A
  • CLK B
  • RESET
  • MIDI OUT

COMMANDES :

  • POT 1 [RATE]
  • POT 2 [CLK A]
  • POT 3 [CLK B + SWING]
  • BTN 1 [STOP + RESET]
  • BTN 2 [PLAY + TAP]

AFFICHAGES :

  • 7 SEGMENTS - 4 DIGIT [BPM + OPTION]
  • LED 1 [BTN 1]
  • LED 2 [BTN 2]
  • LED 3 [CLK RATE]
  • LED 4 [CLK A]
  • LED 5 [CLK B]

Fonctions du module

  • TAP | En mode STOP : combinaison de la touche STOP + PLAY/PAUSE 3 fois mini en mode pause

  • SWING | En mode STOP : combinaison de la touche STOP, PLAY/PAUSE et le POT_B : Maintenir les deux boutons et faire varier POT_B.

  • CLOCK SELECT (INT, EXT, MIDI) | En mode STOP : appuy long sur la touche STOP (+ de 3s)

  • RESET | En mode STOP : déclanchement d’un reset si en mode stop + apuuy sur play (non déclanché si en mode pause puis rerise de lecture) | En mode PLAY : déclanchement manuel combinaison STOP + PLAY

Note : Si le bouton STOP se declanche apres le relachement du bouton, le bouton PLAY se déclanche quant à lui dès l’appuy. Cela afin de pouvoir gerer les combinaison de touches lors de la lecture.

Ressources technique :

Composition :

Schéma :

Code source :

/*
//
//      ================================================================
//             ___ _            _                     _            
//            / __\ | ___   ___| | __ /\/\   __ _ ___| |_ ___ _ __ 
//           / /  | |/ _ \ / __| |/ //    \ / _` / __| __/ _ \ '__|
//          / /___| | (_) | (__|   </ /\/\ \ (_| \__ \ ||  __/ |   
//          \____/|_|\___/ \___|_|\_\/    \/\__,_|___/\__\___|_|   
//
//      ================================================================
//
//
//        AUTEUR :              CLÉMENT GASQUE / clement@gasque.fr
//
//
//        TITRE :               ClockMaster
//
//
//        DESCRIPTION :         Un générateur d'Horloge EuroRack complet sur AtMega328p.
//                      
//
//        CARACTERISTIQUES :    - Selection du BPM    | Potentiometre dedié
//                              - Play/Pause          | Bouton dedié.
//                              - TAP                 | Par appuy succesif sur le bouton "Option".
//                              - Diviseur            | Selection par deux potentiometres dediés (2 voies : A & B).
//                              - Reset               | Bouton "Option" + Bouton "Play/Pause" (En pause : reset l'horloge, envoie d'un reset et remise en lecture) / (En lecture : envoie d'un signal reset)
//                              - Sync IN/OUT/MIDI    | Bouton "Option" + Potentiometre A (Selection de l'origine de l'horloge (INT/EXT/MIDI))
//                              - Swing               | Bouton "Option" + Potentiometre B (Selection du taux de Swing)
//
//
//        VERSIONS :            - [1.0] - 26/10/2022 - Version initiale. Fonctionne mais manque le swing et la synchro midi pour le moment.
//                                                   - Basé sur une idée de Hagiwo (https://note.com/solder_state/n/n8907f2f6e8f5) : réécriture du code et ajout de fonctionnalités.
//
//
//        TODO-LIST :           - [ ] - Mettres les bonnes division d'horloge : cf. Carnet 
//                              - [ ] - Mise en place du swing (affichage + btn = ok)
//                              - [ ] - Mise en place du midi OUT -> Necessite soudure
//                              - [ ] - Mise en place du midi IN -> Necessite circuit, composant + soudure
//                              - [ ] - PlayPause en mode ext clock = recharge la moyenne et donc fausse le relancement ... temporiser l'ancienne valeur ... peut etre en general ... attendre la derniere valeur pour actualiser le bpm ... ca foirote de ce coté ... a veriffier du coté des declanchement bouton + options 
//
//
//
*/



/////////////////// CONFIG ////////////////////

// ------------ BIBLIOTHEQUES ------------- //
#include <MsTimer2.h>
#include "LedControl.h"

// ------------ MIDI ------------- //
/* 
// probleme : il fut ue je recup des optovoupler de tpye 6N138 pour l'entrée
// MIDI
// -> https://www.instructables.com/Send-and-Receive-MIDI-with-Arduino/
// -> https://ejlabs.net/tag/arduino/
// 
// je ne peux que gerer la sortie midi en serial pour le moment ... aie
// -> https://docs.arduino.cc/built-in-examples/communication/Midi
*/ 
byte midi_start = 0xfa;
byte midi_stop = 0xfc;
byte midi_clock = 0xf8;
byte midi_continue = 0xfb;
int play_flag = 0;
byte data;

// ----------- DISPLAY ----------- //
/* 
// 7 Segment - MAX7219
// Récuperation d'un ancien circuit MAX7219 concu pour 8 valeurs.
// Afin là aussi de ne pas se facilité la tache, j'ai intallé mes quatres valeurs en décalage sur le registre !!! d'où la réassignation des broches leds
*/ 
int MaxDataIn = 12;
int MaxClock = 11;
int MaxLoad = 10;
LedControl lc = LedControl(MaxDataIn, MaxClock, MaxLoad, 1);
// réassignation des leds pour le montage du MAX72** (Monté un peu nimporte comment !!!).
int LedDigit_0 = 5;
int LedDigit_1 = 4;
int LedDigit_2 = 7;
int LedDigit_3 = 6;

// ----------- IN/OUT ----------- //
/* 
// ...
*/ 
// BOUTONS
int Btn_A_Pin = 9;
char Btn_Led_A_Pin = A2;
int Btn_B_Pin = 8;
char Btn_Led_B_Pin = A1;
// POTENTIOMETRES
char Pot_rate_Pin = A3;
char Pot_A_Pin = A4;
char Pot_B_Pin = A5;
// IN/OUT JACK
int clk_in = 5;
int out_clk = 7;
int out_A = 4;
int out_B = 3;
int out_reset = 2;
char rate_in = A0;
// IN/OUT MIDI
int midi_in = 13;
int midi_out = 6;

// ----------- VARIABLES ----------- //
/* 
// ...
*/ 
// BOUTONS
byte Btn_A_SW = 1;
byte Btn_B_SW = 0;
byte old_Btn_A_SW = 0;
byte old_Btn_B_SW = 0;
byte btn_start = 0;
byte btn_pause = 0;
byte btn_stop = 0;
unsigned long millis_btn_pause_led = 0;
byte Led_btn_A;
byte Led_btn_B;
// RESET
byte reset_out = 0;
// MAIN BEAT CONFIG
unsigned long ext_count = 400; // for timer count
unsigned long old_ext_pulse = 0; // for average
unsigned long old_int_pulse = 0;
unsigned long ext_period = 0;           // Periode
unsigned long ext_count_result = 0;     // Timer count result
unsigned long old_ext_count_result[10]; // Timer count result
byte ext_pulse = 0;                     // external clock 1 if not, 0 if not
byte int_pulse = 0;                     // Internal clock
byte ext_injudge = 1; // External input existence judgment. 1 if yes, 0 otherwise. Judgment switching when the timer count exceeds the specified value
byte old_ext_injudge = 2;
int rate_val = 512; // rate knob input
int rate = 120;     // used during internal clock operation
// BEAT CONFIG CH1 / CH2
int AD_MD_ch1 = 512; // MD knob input value
int AD_MD_ch2 = 512; // MD knob input value
int AD_MD_ch1_old = 0;
int AD_MD_ch2_old = 0; // ---- voir option des pot
int out_width_ch1 = 10; // Output pulse width.
int out_width_ch2 = 10; // Output pulse width.
int old_AD_MD = 512; // Countermeasures against misreading the RATE value when switching SW
int MD_ch1 = 5; // multiple, divider judgment ch1
int MD_ch2 = 5; // multiple, divider judgment ch2
int M_period_ch1 = 0; // Period of output clock when multiple. 0 if not multiple.
int M_period_ch2 = 0;               // Period of output clock when multiple. 0 if not multiple.
int M_count_ch1 = 1; // When multiple, count every pulse and return to 0 when reaching the set value
int M_count_ch2 = 1; // When multiple, count every pulse and return to 0 when reaching the set value
int D_count_ch1 = 1; // Increase the count when an external pulse comes in
int D_count_ch2 = 1; // Increase the count when an external pulse comes in
int D_full_ch1 = 1;  // High count limit
int D_full_ch2 = 1;  // High count limit
byte CH1out = 0;     // 0 for LOW output, 1 for HIGH output
byte CH2out = 0;     // 0 for LOW output, 1 for HIGH output
byte M_done_ch1 = 0; // For multiple output confirmation. Otherwise, multiple pulses will be generated in one clock.
byte M_done_ch2 = 0; // For multiple output confirmation. Otherwise, multiple pulses will be generated in one clock.
int old_MD_ch1 = 0; // Bug countermeasure for divider clock going wrong when switching
int old_MD_ch2 = 0; // Bug countermeasure for divider clock going wrong when switching
// OPTIONS DIVA / DIVB
byte option_check_A = 0;
byte option_check_B = 0;
byte temp_display_div_A = 0;
byte temp_display_div_B = 0;
unsigned long tempo_ch1 = 0; // superieur à option time pour afficher directe le bpm au demarage
unsigned long tempo_ch2 = 0;
unsigned long tempo_rate_pot = 0;
int option_time = 3000; // temps d'affichage des options (div |||, B, swing, ...)
// BPM TO PPQ
float bpm_rate;
float bps_rate;
unsigned bpm_rate_unsigned = 010;
// CONF CLK
int conf_clk;
unsigned long conf_clk_millis = 0;
// CONF TAP
int tap = 0;
unsigned long tap_millis = 0;
byte tap_lock = 0;
byte tap_on = 0;
unsigned long record_tap[50];
int tap_A;
int tap_B;
int tap_C;
unsigned long tap_diff[50];
unsigned long tap_rate[50];
unsigned long tap_rate_total = 0;
// Conf OPTION

int pot_A_old = 0;
int pot_A;
int clock_option = 0;
int old_clock_option = 0;


byte position_ch1_detect = 0;
byte position_ch2_detect = 0;
byte position_rate_pot_detect = 0;

//SWING
int swing_rate=0;


int rate_pot_old = 0;
int rate_pot;



//conversion bpm
float bpm_f;
float bpm_f_tap;
float bpm_f_ext;
int bpm;
int bpm_tap;
int bpm_tap_old;
int bpm_ext;



unsigned long ext_counter[50];

unsigned long ext_diff[50];
unsigned long ext_rate[50];
unsigned long ext_rate_total = 0;


unsigned long reset_tempo = 0;
      int a = 0;


// ----------- FONCTION AVERAGE ----------- //
// more accurate map : https://forum.arduino.cc/t/how-map-loses-precision-and-how-to-fix-it/371026
long map2(long x, long in_min, long in_max, long out_min, long out_max) {
  return ((x - in_min) * (out_max - out_min) + out_min * (in_max - in_min)) / (in_max - in_min);
}
/* Average & lock pot rate
// Quelques difficulté a obtenir une valeur du potentiometre qui ne varie pas
// ... une moyenne des valeurs et une sorte de verouillage de la valeur (se
// desactive des qu'on depasse +/- 2) ... on perd beaucoup en fluidité mais bon
// https://www.arduino.cc/en/Tutorial/BuiltInExamples/Smoothing
*/
const int numReadings = 100; // Nombres de multiplications
int readings[numReadings];
int readIndex = 0;
int total = 0;
int average = 0;
byte lock_value = 0;
unsigned long millis_average = 0;
int average_old;



int averageAnalog(char analog_pin, int first_map, int last_map) {
  total = total - readings[readIndex];
  readings[readIndex] =
      map2(analogRead(analog_pin), 0, 1023, first_map, last_map);
  total = total + readings[readIndex];
  readIndex = readIndex + 1;
  if (readIndex >= numReadings) {
    readIndex = 0;
  }
  average = total / numReadings;
  
  if (millis() - millis_average < 1500) {
    if (average_old < average || average_old > average) {
      millis_average = millis();
      average_old = average;
    }
    lock_value = 0;
  }
  if (millis() - millis_average > 1500) {
    lock_value = 1;
    if (average_old < average - 1 || average_old > average + 1) {
      millis_average = millis();
      average_old = average;
      lock_value = 0;
      option_check_A = 0; // On reset l'affichage de div |||
      option_check_B = 0; // On reset l'affichage de div B
    }
  }
  return average_old;
}





/////////////////// SETUP ////////////////////
void setup() {
  // MAX72XX
  lc.shutdown(0, false);
  // luminosité
  lc.setIntensity(0, 1);
  lc.clearDisplay(0);

  pinMode(Btn_A_Pin, INPUT_PULLUP);
  pinMode(Btn_Led_A_Pin, OUTPUT);
  pinMode(Btn_B_Pin, INPUT_PULLUP);
  pinMode(Btn_Led_B_Pin, OUTPUT);
  pinMode(Pot_rate_Pin, INPUT);
  pinMode(Pot_A_Pin, INPUT);
  pinMode(Pot_B_Pin, INPUT);
  pinMode(4, OUTPUT);         // CH1out
  pinMode(3, OUTPUT);         // CH2out
  pinMode(7, OUTPUT);         // internal_clock_out
  pinMode(5, INPUT);          // ext_clock_in
  pinMode(out_reset, OUTPUT); // reset out
  pinMode(rate_in, INPUT);    // rate in
  pinMode(midi_in, INPUT);    // midi in
  pinMode(midi_out, OUTPUT);  // midi out

  Serial.begin(9600);

  // Set timer
  MsTimer2::set(1, timer_count); // Timer count every 1ms
  MsTimer2::start(); // When the external input goes High, count until the next
                     // High
}






/////////////////// MAIN LOOP ////////////////////
void loop() {

 



  //--------- DETECTION MOUVEMENT POTENTIOMETRES ----------- //

// avec un genre d'average ... detecter un mouvement du pot
//POT_A
  if (AD_MD_ch1 < AD_MD_ch1_old - 20 || AD_MD_ch1 > AD_MD_ch1_old + 20)
  { 
    AD_MD_ch1_old = AD_MD_ch1;
    position_ch1_detect = 1;
    tempo_ch1 = millis(); // Ajout d'un repeur de compteur pour faire de la tempo
  }
  else
  {
    position_ch1_detect = 0;
  }
//POT_B
  if (AD_MD_ch2 < AD_MD_ch2_old - 20 || AD_MD_ch2 > AD_MD_ch2_old + 20)
  { 
    AD_MD_ch2_old = AD_MD_ch2;
    position_ch2_detect = 1;
    tempo_ch2 = millis();
  }
  else
  {
    position_ch2_detect = 0;
  }
//RATE
rate_pot = analogRead(Pot_rate_Pin);
  if (rate_pot < rate_pot_old - 20 || rate_pot > rate_pot_old + 20)
  {  
    rate_pot_old = rate_pot;
    position_rate_pot_detect = 1;
    tempo_rate_pot = millis(); // Ajout d'un repeur de compteur pour faire de la tempo
  }
  else
  {
    position_rate_pot_detect = 0;
  }




 // ----------- CAPTATION / MESURES ----------- //

// BPM : Switch entre le potentiometre "rate", le TAP et la sync
if (clock_option == 0){ // SI on est sur la position clock interne dans les poptiuon
  if (position_rate_pot_detect == 1){// si detection bpm pot 
      tap_on = 0; // on desactive tap
      }
  //si tap desactivé on lit lme bpm du potat 
  if (tap_on == 0){
    // Bpm rate selon l'entrée CV ou le potentiometre
    if (analogRead(rate_in) > 20) {
      bpm_rate_unsigned = map2(analogRead(rate_in), 0, 1023, 80, 221);
    } else {
      bpm_rate_unsigned = averageAnalog(Pot_rate_Pin, 80, 221);
      //bpm_rate_unsigned = map2(analogRead(Pot_rate_Pin), 0, 1023, 80, 221);
    }
  }
}


//delay(5);

// POT A & POT B
  // Mesure des boutons et potentiometres
  AD_MD_ch1 = analogRead(Pot_A_Pin);
  AD_MD_ch2 = analogRead(Pot_B_Pin);
  // SYNC




// ----------- AFFICHAGE ----------- //
  // Affichage du BPM 
  unsigned digit_0 = (bpm_rate_unsigned / 100U) % 10;
  unsigned digit_1 = (bpm_rate_unsigned / 10U) % 10;
  unsigned digit_2 = (bpm_rate_unsigned / 1U) % 10;
  if (tempo_ch1 + option_time < millis() && tempo_ch2 + option_time < millis()) { // tempo avant de revenir a l'affichage principal
    lc.setChar(0, LedDigit_1, digit_0, false);  // Digit 010
    lc.setChar(0, LedDigit_2, digit_1, false);  // Digit 100
    if (lock_value == 1) {                      // Digit 001 + Si la valeur est bloqué on affiche un point
      lc.setChar(0, LedDigit_3, digit_2, true); 
    } else {
      lc.setChar(0, LedDigit_3, digit_2, false);
    }
  }



// ----------- CONVERSION BPM----------- //
  // Conversion du temps entre deux batement en nombre de battements par minutes
  bpm_f = ext_period;
  bpm_f = bpm_f / 1000;
  bpm = 60 / bpm_f;
  //
  bpm_rate = bpm_rate_unsigned;
  bps_rate = 60 / bpm_rate; // dpm vers DPMS*100
  rate = bps_rate * 1000;   // DPS vers nbr de sec entre chaque batement


// ----------- ??????  ----------- //
  // Redeffinition des variables "a bascule" au debut de la boucle
  old_int_pulse = int_pulse;
  old_ext_injudge = ext_injudge;




  //-----------Reset divider output count------------ //
  if (D_count_ch1 >= D_full_ch1) {
    D_count_ch1 = 0;
  }
  if (D_count_ch2 >= D_full_ch2) {
    D_count_ch2 = 0;
  }

  old_MD_ch1 = MD_ch1; //スイッチ切り替え時にディバイダーのクロックが狂うバグ対策
  old_MD_ch2 = MD_ch2; //スイッチ切り替え時にディバイダーのクロックが狂うバグ対策




  //--------- POT_A & POT_B : RECUPERATION DES VALEURS  ----------- //

  //CH 1
  // Condition de changement de mode de division : lecture du pot si et uniquement si le BtnA est relaché et qu'on detecte un mouvement sur le pot. 
  // Cela permet de modifier le pot A en mode option sans affecter la position de division precedement choisis. Celle-ci est de  nouveau modifiable le boutonA relaché et le pot en mouvement.
  if (AD_MD_ch1 >= 0 && AD_MD_ch1 < 104 && position_ch1_detect == 1 && digitalRead(Btn_A_Pin) == 1) { MD_ch1 = 1; } 
  else if (AD_MD_ch1 >= 114 && AD_MD_ch1 < 177 && position_ch1_detect == 1 && digitalRead(Btn_A_Pin) == 1) { MD_ch1 = 2; }
  else if (AD_MD_ch1 >= 187 && AD_MD_ch1 < 250 && position_ch1_detect == 1 && digitalRead(Btn_A_Pin) == 1) { MD_ch1 = 3; }
  else if (AD_MD_ch1 >= 260 && AD_MD_ch1 < 323 && position_ch1_detect == 1 && digitalRead(Btn_A_Pin) == 1) { MD_ch1 = 4; }
  else if (AD_MD_ch1 >= 333 && AD_MD_ch1 < 396 && position_ch1_detect == 1 && digitalRead(Btn_A_Pin) == 1) { MD_ch1 = 5; }
  else if (AD_MD_ch1 >= 406 && AD_MD_ch1 < 469 && position_ch1_detect == 1 && digitalRead(Btn_A_Pin) == 1) { MD_ch1 = 6; }
  else if (AD_MD_ch1 >= 479 && AD_MD_ch1 < 542 && position_ch1_detect == 1 && digitalRead(Btn_A_Pin) == 1) { MD_ch1 = 7; }
  else if (AD_MD_ch1 >= 552 && AD_MD_ch1 < 615 && position_ch1_detect == 1 && digitalRead(Btn_A_Pin) == 1) { MD_ch1 = 8; }
  else if (AD_MD_ch1 >= 625 && AD_MD_ch1 < 689 && position_ch1_detect == 1 && digitalRead(Btn_A_Pin) == 1) { MD_ch1 = 9; }
  else if (AD_MD_ch1 >= 699 && AD_MD_ch1 < 763 && position_ch1_detect == 1 && digitalRead(Btn_A_Pin) == 1) { MD_ch1 = 10; }
  else if (AD_MD_ch1 >= 773 && AD_MD_ch1 < 836 && position_ch1_detect == 1 && digitalRead(Btn_A_Pin) == 1) { MD_ch1 = 11; }
  else if (AD_MD_ch1 >= 846 && AD_MD_ch1 < 909 && position_ch1_detect == 1 && digitalRead(Btn_A_Pin) == 1) { MD_ch1 = 12; }
  else if (AD_MD_ch1 >= 919 && AD_MD_ch1 < 1024 && position_ch1_detect == 1 && digitalRead(Btn_A_Pin) == 1) { MD_ch1 = 13; }
  
  //CH 2
  if (AD_MD_ch2 >= 0 && AD_MD_ch2 < 104 && position_ch2_detect == 1 && digitalRead(Btn_B_Pin) == 1) { MD_ch2 = 1; }
  else if (AD_MD_ch2 >= 114 && AD_MD_ch2 < 177 && position_ch2_detect == 1 && digitalRead(Btn_B_Pin) == 1) { MD_ch2 = 2; }
  else if (AD_MD_ch2 >= 187 && AD_MD_ch2 < 250 && position_ch2_detect == 1 && digitalRead(Btn_B_Pin) == 1) { MD_ch2 = 3; }
  else if (AD_MD_ch2 >= 260 && AD_MD_ch2 < 323 && position_ch2_detect == 1 && digitalRead(Btn_B_Pin) == 1) { MD_ch2 = 4; }
  else if (AD_MD_ch2 >= 333 && AD_MD_ch2 < 396 && position_ch2_detect == 1 && digitalRead(Btn_B_Pin) == 1) { MD_ch2 = 5; }
  else if (AD_MD_ch2 >= 406 && AD_MD_ch2 < 469 && position_ch2_detect == 1 && digitalRead(Btn_B_Pin) == 1) { MD_ch2 = 6; }
  else if (AD_MD_ch2 >= 479 && AD_MD_ch2 < 542 && position_ch2_detect == 1 && digitalRead(Btn_B_Pin) == 1) { MD_ch2 = 7; }
  else if (AD_MD_ch2 >= 552 && AD_MD_ch2 < 615 && position_ch2_detect == 1 && digitalRead(Btn_B_Pin) == 1) { MD_ch2 = 8; }
  else if (AD_MD_ch2 >= 625 && AD_MD_ch2 < 689 && position_ch2_detect == 1 && digitalRead(Btn_B_Pin) == 1) { MD_ch2 = 9; }
  else if (AD_MD_ch2 >= 699 && AD_MD_ch2 < 763 && position_ch2_detect == 1 && digitalRead(Btn_B_Pin) == 1) { MD_ch2 = 10; }
  else if (AD_MD_ch2 >= 773 && AD_MD_ch2 < 836 && position_ch2_detect == 1 && digitalRead(Btn_B_Pin) == 1) { MD_ch2 = 11; }
  else if (AD_MD_ch2 >= 846 && AD_MD_ch2 < 909 && position_ch2_detect == 1 && digitalRead(Btn_B_Pin) == 1) { MD_ch2 = 12; }
  else if (AD_MD_ch2 >= 919 && AD_MD_ch2 < 1024 && position_ch2_detect == 1 && digitalRead(Btn_B_Pin) == 1) { MD_ch2 = 13; }


  //--------- POT_A & POT_B : DIVISION & AFFICHAGE  ----------- //

  switch (MD_ch1) {
    //------------ *12 / 0.25ppq // ronde pointé ---------------//
    case 1: 
      out_width_ch1 = ext_period / 2 / 48;
      M_period_ch1 = ext_period / 48;
      if (position_ch1_detect == 1 && digitalRead(Btn_A_Pin) == 1) {    // Si on detecte un mouvement et que le bouton d'option est relaché
        lc.setRow(0, LedDigit_1, B0100101);
        lc.setChar(0, LedDigit_2, '4', false);
        lc.setChar(0, LedDigit_3, '8', false);
      }
      break;

    //------------ *8 / 0.5ppq // ronde ---------------//
    case 2: //*24
      out_width_ch1 = ext_period / 2 / 24;
      M_period_ch1 = ext_period / 24;
      if (position_ch1_detect == 1 && digitalRead(Btn_A_Pin) == 1) {
        lc.setRow(0, LedDigit_1, B0100101);
        lc.setChar(0, LedDigit_2, '2', false);
        lc.setChar(0, LedDigit_3, '4', false);
      }
      break;

    //------------ *6 / 0.75ppq // blanche pointé ---------------//
    case 3: //*8
      out_width_ch1 = ext_period / 2 / 8;
      M_period_ch1 = ext_period / 8;
      if (position_ch1_detect == 1 && digitalRead(Btn_A_Pin) == 1) {
        lc.setRow(0, LedDigit_1, B0100101);
        lc.setChar(0, LedDigit_2, ' ', false);
        lc.setChar(0, LedDigit_3, '8', false);
      }
      break;

    //------------ *4 / 1ppq // blanche ---------------//
    case 4: //*4
      out_width_ch1 = ext_period / 2 / 4;
      M_period_ch1 = ext_period / 4;
      if (option_check_A == 1 && digitalRead(Btn_A_Pin) == 1) {
        lc.setRow(0, LedDigit_1, B0100101);
        lc.setChar(0, LedDigit_2, ' ', false);
        lc.setChar(0, LedDigit_3, '4', false);
      }
      break;

    //------------ *3 / 1.5ppq // noir poitné ---------------//
    case 5: //*3
      out_width_ch1 = ext_period / 2 / 3;
      M_period_ch1 = ext_period / 3;
      if (option_check_A == 1 && digitalRead(Btn_A_Pin) == 1) {
        lc.setRow(0, LedDigit_1, B0100101);
        lc.setChar(0, LedDigit_2, ' ', false);
        lc.setChar(0, LedDigit_3, '3', false);
      }
      break;

    //------------ *2 / 2ppq // noir ---------------//
    case 6: //*2
      out_width_ch1 = ext_period / 2 / 2;
      M_period_ch1 = ext_period / 2;
      if (option_check_A == 1 && digitalRead(Btn_A_Pin) == 1) {
        lc.setRow(0, LedDigit_1, B0100101);
        lc.setChar(0, LedDigit_2, ' ', false);
        lc.setChar(0, LedDigit_3, '2', false);
      }
      break;

    //------------ 1 / 4ppq // croche ---------------//
    case 7: // 1
      out_width_ch1 = ext_period / 2;
      M_period_ch1 = 0;
      D_full_ch1 = 1;
      if (position_ch1_detect == 1 && digitalRead(Btn_A_Pin) == 1) {
        lc.setRow(0, LedDigit_1, B1000001);
        lc.setChar(0, LedDigit_2, ' ', false);
        lc.setChar(0, LedDigit_3, '1', false);
      }
      break;

    //------------  ÷ 2 / 8ppq // double croche ---------------//
    case 8:
      out_width_ch1 = ext_period / 2;
      M_period_ch1 = 0;
      D_full_ch1 = 2;
      if (position_ch1_detect == 1 && digitalRead(Btn_A_Pin) == 1) {
        lc.setRow(0, LedDigit_1, B0010011);
        lc.setChar(0, LedDigit_2, ' ', false);
        lc.setChar(0, LedDigit_3, '2', false);
      }
      break;

    //------------  ÷ 3 / 12ppq // double croche pointé ---------------//
    case 9:
      out_width_ch1 = ext_period / 2;
      M_period_ch1 = 0;
      D_full_ch1 = 3;
      if (position_ch1_detect == 1 && digitalRead(Btn_A_Pin) == 1) {
        lc.setRow(0, LedDigit_1, B0010011);
        lc.setChar(0, LedDigit_2, ' ', false);
        lc.setChar(0, LedDigit_3, '3', false);
      }
      break;

    //------------  ÷ 4 / 16ppq // triple croche ---------------//
    case 10:
      out_width_ch1 = ext_period / 2;
      M_period_ch1 = 0;
      D_full_ch1 = 4;
      if (position_ch1_detect == 1 && digitalRead(Btn_A_Pin) == 1) {
        lc.setRow(0, LedDigit_1, B0010011);
        lc.setChar(0, LedDigit_2, ' ', false);
        lc.setChar(0, LedDigit_3, '4', false);
      }
      break;

    //------------  ÷ 6 / 24ppq // triple croche pointé ---------------//
    case 11:
      out_width_ch1 = ext_period / 2;
      M_period_ch1 = 0;
      D_full_ch1 = 8;
      if (position_ch1_detect == 1 && digitalRead(Btn_A_Pin) == 1) {
        lc.setRow(0, LedDigit_1, B0010011);
        lc.setChar(0, LedDigit_2, ' ', false);
        lc.setChar(0, LedDigit_3, '8', false);
      }
      break;

    //------------  ÷ 8 / 32ppq // quadriple croche  ---------------//
    case 12:
      out_width_ch1 = ext_period / 2;
      M_period_ch1 = 0;
      D_full_ch1 = 24;
      if (position_ch1_detect == 1 && digitalRead(Btn_A_Pin) == 1) {
        lc.setRow(0, LedDigit_1, B0010011);
        lc.setChar(0, LedDigit_2, '2', false);
        lc.setChar(0, LedDigit_3, '4', false);
      }
      break;

    //------------  ÷ 12 / 48ppq // quadriple croche pointé  ---------------//
    case 13:
      out_width_ch1 = ext_period / 2;
      M_period_ch1 = 0;
      D_full_ch1 = 48;
      if (position_ch1_detect == 1 && digitalRead(Btn_A_Pin) == 1) {
        lc.setRow(0, LedDigit_1, B0010011);
        lc.setChar(0, LedDigit_2, '4', false);
        lc.setChar(0, LedDigit_3, '8', false);
      }
      break;
    }




//CH2
  switch (MD_ch2) {
    case 1: //*48
      out_width_ch2 = ext_period / 2 / 48;
      M_period_ch2 = ext_period / 48;
      if (position_ch2_detect == 1 && digitalRead(Btn_A_Pin) == 1) {
        lc.setRow(0, LedDigit_1, B0100101);
        lc.setChar(0, LedDigit_2, '4', false);
        lc.setChar(0, LedDigit_3, '8', false);
      }
      break;

    case 2: //*24
      out_width_ch2 = ext_period / 2 / 24;
      M_period_ch2 = ext_period / 24;
      if (position_ch2_detect == 1 && digitalRead(Btn_A_Pin) == 1) {
        lc.setRow(0, LedDigit_1, B0100101);
        lc.setChar(0, LedDigit_2, '2', false);
        lc.setChar(0, LedDigit_3, '4', false);
      }
      break;

    case 3: //*8
      out_width_ch2 = ext_period / 2 / 8;
      M_period_ch2 = ext_period / 8;
      if (position_ch2_detect == 1 && digitalRead(Btn_A_Pin) == 1) {
        lc.setRow(0, LedDigit_1, B0100101);
        lc.setChar(0, LedDigit_2, ' ', false);
        lc.setChar(0, LedDigit_3, '8', false);
      }
      break;

    case 4: //*4
      out_width_ch2 = ext_period / 2 / 4;
      M_period_ch2 = ext_period / 4;
      if (position_ch2_detect == 1 && digitalRead(Btn_A_Pin) == 1) {
        lc.setRow(0, LedDigit_1, B0100101);
        lc.setChar(0, LedDigit_2, ' ', false);
        lc.setChar(0, LedDigit_3, '4', false);
      }
      break;

    case 5: //*3
      out_width_ch2 = ext_period / 2 / 3;
      M_period_ch2 = ext_period / 3;
      if (position_ch2_detect == 1 && digitalRead(Btn_A_Pin) == 1) {
        lc.setRow(0, LedDigit_1, B0100101);
        lc.setChar(0, LedDigit_2, ' ', false);
        lc.setChar(0, LedDigit_3, '3', false);
      }
      break;

    case 6: //*2
      out_width_ch2 = ext_period / 2 / 2;
      M_period_ch2 = ext_period / 2;
      if (position_ch2_detect == 1 && digitalRead(Btn_A_Pin) == 1) {
        lc.setRow(0, LedDigit_1, B0100101);
        lc.setChar(0, LedDigit_2, ' ', false);
        lc.setChar(0, LedDigit_3, '2', false);
      }
      break;

    case 7: // 1
      out_width_ch2 = ext_period / 2;
      M_period_ch2 = 0;
      D_full_ch2 = 1;
      if (position_ch2_detect == 1 && digitalRead(Btn_A_Pin) == 1) {
        lc.setRow(0, LedDigit_1, B1000001);
        lc.setChar(0, LedDigit_2, ' ', false);
        lc.setChar(0, LedDigit_3, '1', false);
      }
      break;

    case 8:
      out_width_ch2 = ext_period / 2;
      M_period_ch2 = 0;
      D_full_ch2 = 2;
      if (position_ch2_detect == 1 && digitalRead(Btn_A_Pin) == 1) {
        lc.setRow(0, LedDigit_1, B0010011);
        lc.setChar(0, LedDigit_2, ' ', false);
        lc.setChar(0, LedDigit_3, '2', false);
      }
      break;

    case 9:
      out_width_ch2 = ext_period / 2;
      M_period_ch2 = 0;
      D_full_ch2 = 3;
      if (position_ch2_detect == 1 && digitalRead(Btn_A_Pin) == 1) {
        lc.setRow(0, LedDigit_1, B0010011);
        lc.setChar(0, LedDigit_2, ' ', false);
        lc.setChar(0, LedDigit_3, '3', false);
      }
      break;

    case 10:
      out_width_ch2 = ext_period / 2;
      M_period_ch2 = 0;
      D_full_ch2 = 4;
      if (position_ch2_detect == 1 && digitalRead(Btn_A_Pin) == 1) {
        lc.setRow(0, LedDigit_1, B0010011);
        lc.setChar(0, LedDigit_2, ' ', false);
        lc.setChar(0, LedDigit_3, '4', false);
      }
      break;

    case 11:
      out_width_ch2 = ext_period / 2;
      M_period_ch2 = 0;
      D_full_ch2 = 8;
      if (position_ch2_detect == 1 && digitalRead(Btn_A_Pin) == 1) {
        lc.setRow(0, LedDigit_1, B0010011);
        lc.setChar(0, LedDigit_2, ' ', false);
        lc.setChar(0, LedDigit_3, '8', false);
      }
      break;

    case 12:
      out_width_ch2 = ext_period / 2;
      M_period_ch2 = 0;
      D_full_ch2 = 24;
      if (position_ch2_detect == 1 && digitalRead(Btn_A_Pin) == 1) {
        lc.setRow(0, LedDigit_1, B0010011);
        lc.setChar(0, LedDigit_2, '2', false);
        lc.setChar(0, LedDigit_3, '4', false);
      }
      break;

    case 13:
      out_width_ch2 = ext_period / 2;
      M_period_ch2 = 0;
      D_full_ch2 = 48;
      if (position_ch2_detect == 1 && digitalRead(Btn_A_Pin) == 1) {
        lc.setRow(0, LedDigit_1, B0010011);
        lc.setChar(0, LedDigit_2, '4', false);
        lc.setChar(0, LedDigit_3, '8', false);
      }
      break;
  }




  //--------- POT_A & POT_B : GENERAL  ----------- //


  // REINIT
  // Lorsque l'on passe d'une option à l'autre ...
  // Switch btn and bug fix for divider clock going wrong when switching
  if (MD_ch1 != old_MD_ch1) {
    D_count_ch1 = 0;
    //D_count_ch2 = 0;
   // option_check_A = 1; // !!!!! a remplacer par le ffnctionnement de dessus avcec une detection,de mouvement
   // option_check_B = 0; //
    tempo_ch1 = millis();
  }

  if (MD_ch2 != old_MD_ch2) { 
    //D_count_ch1 = 0;
    D_count_ch2 = 0;
   // option_check_B = 1; // !!!!!
   // option_check_A = 0; //
    tempo_ch2 = millis();
  }

  // AFFICHAGE REINIT
if(position_rate_pot_detect == 1) { // reinitialisation de l'affichage si detecion du bouton de rate bpm
position_ch1_detect = 0;
position_ch2_detect = 0;
tempo_ch1 = 0;
tempo_ch2 = 0;
}




  //--------- OPTION + POT = SWING / CLK  ----------- //


// CLK -> Bouton option + PotA = 3 position (clock_option) : INT EXT MIDI : 0, 1, 2 
if (conf_clk == 1){ // si on est en mode option

  if (position_ch1_detect == 1) // S'il n'y pas eu de mouvement ... il ne se passe rien ... évite que ca change si on a bougé le pot par ailleur et que l'on arrive dans le mode option
  {
    if (AD_MD_ch1 >= 0 && AD_MD_ch1 <= 250)
    {
      if (old_clock_option > 250)
      {
        clock_option = 0;
        old_clock_option = 150;
      }
    }
    if (AD_MD_ch1 >= 312 && AD_MD_ch1 <= 712)
    {
      if (old_clock_option < 312 || old_clock_option > 712)
      {
        clock_option = 1;
        old_clock_option = 512;
      }
    }
    if (AD_MD_ch1 >= 750 && AD_MD_ch1 < 1024)
    {
      if (old_clock_option < 750)
      {
        clock_option = 2;
        old_clock_option = 850;
      }
    }
  }
}

if (clock_option == 0)
{
  if (conf_clk == 1 && tempo_ch1 + 1000 > millis())
  {
    lc.setRow(0, LedDigit_0, B1000000); //
    lc.setRow(0, LedDigit_1, B0110000); //i
    lc.setRow(0, LedDigit_2, 0x15);     //n
    lc.setRow(0, LedDigit_3, B0001111); //t
  }
  else
  {
    lc.setRow(0, LedDigit_0, B1000000);
  }
  ext_injudge = 0;
}


if (clock_option == 1){

  if (conf_clk == 1 && tempo_ch1 + 1000 > millis())
  {
    lc.setRow(0, LedDigit_0, B0000001); //
    lc.setChar(0, LedDigit_1, 'E',false); //E
    lc.setRow(0, LedDigit_2, B0110111); //X
    lc.setRow(0, LedDigit_3, B0001111); //t
  }
  else
  {
    lc.setRow(0, LedDigit_0, B0000001);
  }


ext_injudge = 1;
}
if (clock_option == 2){
  if (conf_clk == 1 && tempo_ch1 + 1000 > millis())
  {
    lc.setRow(0, LedDigit_0, B0001000); //
    lc.setRow(0, LedDigit_1, B1110110); //M
    lc.setRow(0, LedDigit_2, B0010000); //i
    lc.setRow(0, LedDigit_3, B0111101); //d
  }
  else
  {
    lc.setRow(0, LedDigit_0, B0001000);
  }
  ext_injudge = 0;
}




// SWING
if (conf_clk == 1){ // si on est en mode option

  if (position_ch2_detect == 1) // S'il n'y pas eu de mouvement ... il ne se passe rien ... évite que ca change si on a bougé le pot par ailleur et que l'on arrive dans le mode option
  {
    

    swing_rate = map2(analogRead(Pot_B_Pin),0,1023,0,100);


  int swing_digit_0 = (swing_rate / 100U) % 10;
  int swing_digit_1 = (swing_rate / 10U) % 10;
  int swing_digit_2 = (swing_rate / 1U) % 10;
    
    //lc.setRow(0, LedDigit_0, B1011101); //S
    lc.setChar(0, LedDigit_1, swing_digit_0, false);   
    lc.setChar(0, LedDigit_2, swing_digit_1, false);   
    lc.setChar(0, LedDigit_3, swing_digit_2, false);   


  } else
  {
  }
}


  //------------DETERMINATION ABS/PRES D'UNE ENTREE EXT ----------- //
  // if (ext_count > 4000) { // If there is no count for 4s or more, judge that there is no external input
  //   ext_injudge = 0;
  // } else if (ext_count < 4000 && ext_pulse == 1) {
  //   ext_injudge = 1;
  // }

  // if (old_ext_injudge == 1 &&
  //     ext_injudge == 0) { // When there is no external input
  //   ext_count = 0;
  // }



      // Lecture d'une evantuelle entrée sync





  //---------OPTIONS D'HORLOGE ----------- //
  if (ext_injudge == 1) { // use external clock

 old_ext_pulse = ext_pulse;
  ext_pulse = digitalRead(5);



    if (ext_pulse == 1 && old_ext_pulse == 0) {
      
      if(a>9){a=0;};

      ext_count_result = ext_count;
        old_ext_count_result[a] = ext_count_result;

     // ext_count_result = 0; //init
      for (int i = 0; i < a; i++)
        {
           ext_count_result =  ext_count_result +  old_ext_count_result[i]; // On fait la somme de toutes ces différences
        }



      //old_ext_count_result = ext_count_result; // For averaging two times
                 // ext_count = 0;
      if (a>0){
      ext_period = ext_count_result / (a +1)  ; // External input period. Average of 2 times to reduce variability
      }




      a++;

        // for (int i = 0; i < ext_counter; i++)
        // {
        //   ext_diff[i] = record_ext[i + 1] - record_ext[i]; // On r"écupère toutes les différences entres chaque TAP
        // }

        // for (int i = 0; i < tap; i++)
        // {
        //   ext_rate_total = ext_rate_total + ext_diff[i]; // On fait la somme de toutes ces différences
        // }

        // ext_rate_total = ext_rate_total / ext - 1; // On fait la moyenne





      //Serial.print('tttttt');
      // MsTimer2::start();//When external input goes High, count to next High
    }

  bpm_f_ext = ext_period;
    bpm_f_ext = bpm_f_ext / 1000;
    bpm_ext = 60 / bpm_f_ext;

   // if (reset_tempo + 3000 < millis) {// tempo si reset pour ne pas fausser le bpm
    bpm_rate_unsigned = bpm_ext;
   // }

  }
  
  
  if (ext_injudge == 0) { // use internal clock
    ext_period = rate;
    if (ext_count < 5 || ext_count >= ext_period) {
      int_pulse = 1;
    } else if (ext_count >= 5) {
      int_pulse = 0;
    }
  }





  //--------- INTERUPTEURS ----------- //

  // A - OPTION/TAP
  if (digitalRead(Btn_A_Pin) == 1)
  { // Si le bouton d'option est desactivé uniquement
    if (digitalRead(Btn_B_Pin) == 0)
    {
      Btn_A_SW = 0;
      if (old_Btn_B_SW != Btn_B_SW)
      { // SYSTEME D'INTERUPTEUR A BASCULE
        Btn_B_SW = !Btn_B_SW;
        old_Btn_B_SW = Btn_B_SW;
      }
    }
    else
    {
      old_Btn_B_SW = !Btn_B_SW;
    }
  }

  // B - PLAY/PAUSE/RESET
  if (digitalRead(Btn_A_Pin) == 0)
  { // Si on appuy sur stop
    Led_btn_A = 1;
    if (millis() - conf_clk_millis > 1000)  // MODE OPTION
    {               // si on appuy plus de 1.5s
      conf_clk = 1; // ON ACTIVE les ooption
    }
    if (digitalRead(Btn_B_Pin) == 0)        // MODE RESET
    { // Si on combine avec play on relance le tout avec un reset
      reset_out = 1; // on lance reset
      Btn_B_SW = 1;  // on active le btn 1 pour lancer play
      ext_count = 0; // remise a zero du compteur
    }
  }
  else
  {
    conf_clk_millis = millis();
    conf_clk = 0;
    Led_btn_A = 0;
  }
// SW_B ON = PLAY
  if (Btn_B_SW == 1 && clock_option ==0)
  { 
    Led_btn_B = 1;
    btn_pause = 0;
    btn_start = 1;
  }
// SW_B OFF = PAUSE
  if (Btn_B_SW == 0 && clock_option ==0)
  { 
    btn_pause = 1;
  }
  if(clock_option > 0){ // si on est dans les option midi ou sync alors on desactive play pause (toujours en play) ... le reset fonctionne toujours
    Led_btn_B = 1;
    btn_pause = 0;
    btn_start = 1;
  }

  // PAUSE
  if (btn_pause == 1)
  {
    if (millis() - millis_btn_pause_led > 500)
    { // Systeme de clignotelment pour le mode pause
      Led_btn_B = !Led_btn_B;
      millis_btn_pause_led = millis();
    }
    digitalWrite(7, LOW); // On desactive les signauw sortant (OUT)
    digitalWrite(4, LOW); // On desactive les signauw sortant (A)
    digitalWrite(3, LOW); // On desactive les signauw sortant (B)
  }
  else
  {
    digitalWrite(Btn_Led_B_Pin, LOW);
  }

  // Gestion des leds des btns
  if (Led_btn_A == 1)  {    digitalWrite(Btn_Led_A_Pin, HIGH);  }  else  {    digitalWrite(Btn_Led_A_Pin, LOW);  };
  if (Led_btn_B == 1)  {    digitalWrite(Btn_Led_B_Pin, HIGH);  }  else  {    digitalWrite(Btn_Led_B_Pin, LOW);  };

 //--------- TAP ----------- //

  if (tap_lock == 1)
  {

    if (digitalRead(Btn_A_Pin) == 0)
    {

      if (millis() - tap_millis > 1500)
      {
        tap = 0;

        for (int i = 0; i < 49; i++)
        { // reinit
          record_tap[i] = 0;
          tap_diff[i] = 0;
        }
      }

      tap_millis = millis();

      if (tap == 49)
      {
        tap = 0;
        for (int i = 0; i < 49; i++)
        { // reinit
          record_tap[i] = 0;
          tap_diff[i] = 0;
          tap_rate_total = 0;
        }
      }
      if (tap == 2)
      {
        tap_on = 1; // on affiche a partir de 3 coup
      }

      tap_rate_total = 0;
      record_tap[tap] = millis();

      if (tap > 0) // On commence la mesure au deuxieme coup
      {
        for (int i = 0; i < tap; i++)
        {
          tap_diff[i] = record_tap[i + 1] - record_tap[i]; // On r"écupère toutes les différences entres chaque TAP
        }

        for (int i = 0; i < tap; i++)
        {
          tap_rate_total = tap_rate_total + tap_diff[i]; // On fait la somme de toutes ces différences
        }

        tap_rate_total = tap_rate_total / tap - 1; // On fait la moyenne
      }

      bpm_tap_old = bpm_tap; // on garde en memoire l'ancienne valeur pour ne pas afficher la preiere valeur en reinit (0)

      tap++;
      tap_lock = 0;
    }

    bpm_f_tap = tap_rate_total;
    bpm_f_tap = bpm_f_tap / 1000;
    bpm_tap = 60 / bpm_f_tap;
  }

  if (tap_on == 1 && clock_option == 0) // seulement en mode INT CLK
  { // on ne met a jour le bpm qui s'il y eu ou moins 4 tap"
    if (tap_rate_total == 0)
    { // evite d'afficher 0 lorsqu'on recommence un tap
      bpm_rate_unsigned = bpm_tap_old;
    }
    else
    {
      bpm_rate_unsigned = bpm_tap;
    }
    // bpm_rate_unsigned = tap_diff;
  }

  if (digitalRead(Btn_A_Pin) == 1)
  { // si on release le bouton on rearme la prise de data tap : evite le rebond evident
    tap_lock = 1;
  }

 //--------- RESET ----------- //

  if (reset_out == 1) {

    digitalWrite(out_reset, HIGH);

    if (ext_count >= ext_period / 2) {
      reset_out = 0;
      digitalWrite(out_reset, LOW);
    }
    reset_tempo = millis();
  }




  //--------------INTERNAL CLOCK output (only when there is no external
  // input))-----------

  if (int_pulse == 1) {
    digitalWrite(7, HIGH);
  } else if (ext_count >= ext_period / 2) {
    digitalWrite(7, LOW);
  }

  //-----------------OUT1出力------------------
  if (ext_pulse == 1 && old_ext_pulse == 0) { //外部クロック用
    D_count_ch1++;
    ext_count = 0;
    M_count_ch1 = 0;
    M_done_ch1 = 0;
    if (MD_ch1 <= 6) {
      digitalWrite(4, HIGH);
      CH1out = 1;
    }
  }

  if (int_pulse == 1 && old_int_pulse == 0) { //内部クロック用
    D_count_ch1++;
    ext_count = 0;
    M_count_ch1 = 0;
    M_done_ch1 = 0;
    if (MD_ch1 <= 6) {
      digitalWrite(4, HIGH);
      CH1out = 1;
    }
  }

  if (MD_ch1 < 7) {
    if (ext_count >= M_period_ch1 * M_count_ch1 && CH1out == 0) {
      CH1out = 1;
      digitalWrite(4, HIGH);
    }
    if (ext_count >= M_period_ch1 * M_count_ch1 + out_width_ch1 &&
        CH1out == 1) {
      digitalWrite(4, LOW);
      M_count_ch1++;
      CH1out = 0;
    }
  }

  else if (MD_ch1 == 7) {
    if (D_count_ch1 == 1 && M_done_ch1 == 0) {
      CH1out = 1;
      M_done_ch1 = 1;
      digitalWrite(4, HIGH);
    }
    if (ext_count >= out_width_ch1) {
      digitalWrite(4, LOW);
      CH1out = 0;
    }
  }

  else if (MD_ch1 > 7) {
    if (D_count_ch1 == 1 && M_done_ch1 == 0) {
      CH1out = 1;
      digitalWrite(4, HIGH);
      M_done_ch1 = 1;
    }
    if (ext_count >= out_width_ch1) {
      digitalWrite(4, LOW);
      CH1out = 0;
    }
  }

  //-----------------OUT2出力------------------
  if (ext_pulse == 1 && old_ext_pulse == 0) {
    D_count_ch2++;
    M_count_ch2 = 0;
    M_done_ch2 = 0;
    if (MD_ch2 <= 6) {
      digitalWrite(3, HIGH);
      CH2out = 1;
    }
  }

  if (int_pulse == 1 && old_int_pulse == 0) { //内部クロック用
    D_count_ch2++;
    M_count_ch2 = 0;
    M_done_ch2 = 0;
    if (MD_ch2 <= 6) {
      digitalWrite(3, HIGH);
      CH2out = 1;
    }
  }

  if (MD_ch2 < 7) {
    if (ext_count >= M_period_ch2 * M_count_ch2 && CH2out == 0) {
      CH2out = 1;
      digitalWrite(3, HIGH);
    }
    if (ext_count >= M_period_ch2 * M_count_ch2 + out_width_ch2 &&
        CH2out == 1) {
      digitalWrite(3, LOW);
      M_count_ch2++;
      CH2out = 0;
    }
  }

  else if (MD_ch2 == 7) {
    if (D_count_ch2 == 1 && M_done_ch2 == 0) {
      CH2out = 1;
      M_done_ch2 = 1;
      digitalWrite(3, HIGH);
    }
    if (ext_count >= out_width_ch2) {
      digitalWrite(3, LOW);
      CH2out = 0;
    }
  }

  else if (MD_ch2 > 7) {
    if (D_count_ch2 == 1 && M_done_ch2 == 0) {
      CH2out = 1;
      M_done_ch2 = 1;
      digitalWrite(3, HIGH);
    }
    if (ext_count >= out_width_ch2) {
      digitalWrite(3, LOW);
      CH2out = 0;
    }
  }




/////////////////// DEBUG ////////////////////


  // Serial.print(millis());
  // Serial.print(",");
  // Serial.print(record_tap[0]);
  // Serial.print(",");
  // Serial.print(record_tap[1]);
  // Serial.print(",");
  // Serial.print(record_tap[2]);

  }

/////////////////// TIMER ////////////////////
// timer count. Increment count every 1ms
void timer_count() {
  if (btn_pause == 0 && Btn_A_SW == 0) { // On active le compteur uniquement en mode START
    ext_count++;
  }
}

Assemblage et démonstration :

Bibliographie :

Sources :