Convert Midi Clock Sync (BPM) to MIDI CC values

Hi.

I'm new to this and I'm struggling to figure this out... I took the code from another thread but it needs reworking.

I have a device that doesn't accept MIDI Clock Sync, but it does have a knob that can be set to change the tempo. I can control this with MIDI CC 0 - 127

Is there a way to change the last few lines of this code to convert the BPM signal to MIDI CC values?

I have the knob set from 60 bpm (1000ms) to 160 bpm (375ms), so MIDI CC# 0 = 60bpm and 127 = 160bpm.

The code:

byte midi_start = 0xfa;
byte midi_stop = 0xfc;
byte midi_clock = 0xf8;
byte midi_continue = 0xfb;
int play_flag = 0;
byte data;

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

void loop() {
  if(Serial.available() > 0) {
    data = Serial.read();
    if(data == midi_start) {
      play_flag = 1;
    }
    else if(data == midi_continue) {
      play_flag = 1;
    }
    else if(data == midi_stop) {
      play_flag = 0;
    }
    else if((data == midi_clock) && (play_flag == 1)) {
      Sync();
    }
  } 
}

void Sync() //this will be where the conversion will be required, right? 

found this thread that seems to have the complete code for your device :slight_smile:

modded the code from there to inculde the MIDI CC requested.

(Compiles, NOT tested!)

#include <MIDI.h>

// MIDI STATUS BYTES
byte midi_start    = 0xfa;
byte midi_stop     = 0xfc;
byte midi_clock    = 0xf8;
byte midi_continue = 0xfb;

// INTERNALS
byte play_flag = 0;
byte incoming_data;

// DATA FOR CALCULATIONS
unsigned int QPM = 4;  // QUARTERS PER MEASURE
unsigned int NOM = 1;  // MINIMUM NUMBERS OF MEASURE OF THE LOOP
unsigned int PPQ = 24; // TICK OR PULSES PER QUARTER
unsigned int BPM = 0;  // BEATS PER MINUTE

// TIMINGS AND COUNTERS
unsigned int Tick_Counter = 0;
unsigned long jamsync_measure_timer;
unsigned long jamsync_loop_timer;
unsigned long jamsync_link_timer ;


// SYSEX ARRAYS
unsigned int jamsync_sync[] {0x00, 0xF0, 0x00, 0x00, 0x10, 0x7F, 0x62, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x04, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF7};
unsigned int jamsync_link[] {0x00, 0xF0, 0x00, 0x00, 0x10, 0x7F, 0x62, 0x01, 0x00, 0x01, 0x01, 0xF7};
unsigned int jamsync_stop[] {0x00, 0xF0, 0x00, 0x00, 0x10, 0x7F, 0x62, 0x01, 0x4A, 0x01, 0x04, 0x46, 0x30, 0x42, 0x02, 0x04, 0x40, 0x03, 0x4E, 0x46, 0x2E, 0x40, 0x04, 0x5C, 0xF7};

MIDI_CREATE_DEFAULT_INSTANCE();

void setup() {
  Serial.begin(31250);
  MIDI.begin(1);
  MIDI.turnThruOff();
  jamsync_link_timer = micros();
}

void loop() {

  // READ THE INCOMING MIDI
  CheckSerial();

  // KEEP LINK ACTIVE
  /*if (micros() - jamsync_link_timer > 400000) {
    sendLink();
    }*/

  // SEND MIDI CC# 0 = 60bpm and 127 = 160bpm.
  if (Tick_Counter == NOM * QPM * PPQ) {
    GetBPM();
    //Sendjamsync_sync();
    if (BPM == 60) {
      MIDI.sendControlChange(0,   0,      1);
      //                    CC   value  channel
    }
    else if (BPM == 160) {
      MIDI.sendControlChange(0,   127,      1);
      //                    CC   value  channel
    }
    Tick_Counter = 0;
  }

}

void CheckSerial() {
  if (Serial.available() > 0) {
    incoming_data = Serial.read();

    if (incoming_data == midi_start) {
      play_flag = 1;
      /*if (BPM > 0) {
        Sendjamsync_sync();
        }*/
      jamsync_measure_timer = micros();
      Tick_Counter = 0;
    }
    else if (incoming_data == midi_continue) {
      play_flag = 1;
    }
    else if (incoming_data == midi_stop) {
      play_flag = 0;
      //sendStop();
    }
    else if ((incoming_data == midi_clock) && (play_flag == 1)) {
      Tick_Counter++;
    }
  }
}

void sendLink() {
  for (int i = 1; i < 12; i++) {
    Serial.write(jamsync_link[i]);
  }
  jamsync_link_timer = micros();
}

void sendStop() {
  for (int i = 1; i < 25; i++) {
    Serial.write(jamsync_stop[i]);
  }
}

void GetBPM() {
  jamsync_measure_timer = micros() - jamsync_measure_timer;
  jamsync_loop_timer = jamsync_measure_timer;
  BPM = round(QPM * 60000000.0 / jamsync_measure_timer);
  jamsync_measure_timer = micros();
}

void Sendjamsync_sync() {
  unsigned long LoopTime;
  int b163 = 0;
  int w;
  int x;
  int y;
  int z = 0; //xor checksum


  // LoopTime = floor(1000* NOM * QPM * 60.0 / BPM);
  LoopTime = floor(jamsync_loop_timer / 1000.0);
  x = floor(log(LoopTime / 2000.0) / log(4.0));
  b163 = (LoopTime / (2000.0 * pow(4.0, x))) > 2;
  y = 2 * pow(2, b163) * pow(4, x);
  w = floor(LoopTime / y);

  // BPM
  jamsync_sync[8]  = 66 + 8 * ((63 < BPM) && (BPM < 128) || BPM > 191) ;
  jamsync_sync[12] = (4 * BPM > 127 && 4 * BPM < 256) * (4 * BPM - 128) +
                     (2 * BPM > 127 && 2 * BPM < 256) * (2 * BPM - 128) +
                     (BPM > 127 && BPM < 256) * (BPM - 128);
  jamsync_sync[13] = 1 * (BPM > 127) + 66;

  // lenght
  jamsync_sync[16] = 64 + 8 * b163;
  jamsync_sync[21] = 64 + x;
  jamsync_sync[20] = 128 * (0.001 * w - 1);
  jamsync_sync[19] = pow(128.0, 2) * (0.001 * w - 1 - jamsync_sync[20] / 128.0);
  jamsync_sync[18] = pow(128.0, 3) * (0.001 * w - 1 - jamsync_sync[20] / 128.0 - jamsync_sync[19] / pow(128.0, 2));

  // command
  jamsync_sync[22] = 5;

  // checksum
  for (int i = 8; i < 23; i++) {
    z = z ^ int(jamsync_sync[i]); // checksum XOR
  }
  jamsync_sync[23] = z;

  for (int i = 1; i < 25; i++) {
    Serial.write(int(jamsync_sync[i]));
  }
}

hope that helps...

Thank you so much! I have some homework to do now! Really appreciate it

looking a bit more into this I realised that you probably would be using 2 serial ports on your arduino:
1 to read the device sending the MIDI Clock Sync
another one to output the MIDI CC value.

if that is the case the code would be IMHO slightly different as you need to include the second serial port.
Assuming you are using a UNO then you would need to your something like softwareserial to add another serial port.

so something like this maybe:
(Compiles, NOT tested!)

#include <MIDI.h>
#include <SoftwareSerial.h>

// MIDI STATUS BYTES
byte midi_start    = 0xfa;
byte midi_stop     = 0xfc;
byte midi_clock    = 0xf8;
byte midi_continue = 0xfb;

// INTERNALS
byte play_flag = 0;
byte incoming_data;

// DATA FOR CALCULATIONS
unsigned int QPM = 4;  // QUARTERS PER MEASURE
unsigned int NOM = 1;  // MINIMUM NUMBERS OF MEASURE OF THE LOOP
unsigned int PPQ = 24; // TICK OR PULSES PER QUARTER
unsigned int BPM = 0;  // BEATS PER MINUTE

// TIMINGS AND COUNTERS
unsigned int Tick_Counter = 0;
unsigned long jamsync_measure_timer;
unsigned long jamsync_loop_timer;
unsigned long jamsync_link_timer ;


// SYSEX ARRAYS
unsigned int jamsync_sync[] {0x00, 0xF0, 0x00, 0x00, 0x10, 0x7F, 0x62, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x04, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF7};
unsigned int jamsync_link[] {0x00, 0xF0, 0x00, 0x00, 0x10, 0x7F, 0x62, 0x01, 0x00, 0x01, 0x01, 0xF7};
unsigned int jamsync_stop[] {0x00, 0xF0, 0x00, 0x00, 0x10, 0x7F, 0x62, 0x01, 0x4A, 0x01, 0x04, 0x46, 0x30, 0x42, 0x02, 0x04, 0x40, 0x03, 0x4E, 0x46, 0x2E, 0x40, 0x04, 0x5C, 0xF7};

// Set up a new SoftwareSerial object
SoftwareSerial mySerial (9, 8); //rxPin, txPin

MIDI_CREATE_INSTANCE(SoftwareSerial, mySerial, MIDI);

void setup() {
  Serial.begin(31250);
  MIDI.begin(1);
  MIDI.turnThruOff();
  jamsync_link_timer = micros();
}

void loop() {

  // READ THE INCOMING MIDI
  CheckSerial();

  // KEEP LINK ACTIVE
  /*if (micros() - jamsync_link_timer > 400000) {
    sendLink();
    }*/

  // SEND MIDI CC# 0 = 60bpm and 127 = 160bpm.
  if (Tick_Counter == NOM * QPM * PPQ) {
    GetBPM();
    //Sendjamsync_sync();
    if (BPM == 60) {
      MIDI.sendControlChange(0,   0,      1);
      //                    CC   value  channel
    }
    else if (BPM == 160) {
      MIDI.sendControlChange(0,   127,      1);
      //                    CC   value  channel
    }
    Tick_Counter = 0;
  }

}

void CheckSerial() {
  if (Serial.available() > 0) {
    incoming_data = Serial.read();

    if (incoming_data == midi_start) {
      play_flag = 1;
      /*if (BPM > 0) {
        Sendjamsync_sync();
        }*/
      jamsync_measure_timer = micros();
      Tick_Counter = 0;
    }
    else if (incoming_data == midi_continue) {
      play_flag = 1;
    }
    else if (incoming_data == midi_stop) {
      play_flag = 0;
      //sendStop();
    }
    else if ((incoming_data == midi_clock) && (play_flag == 1)) {
      Tick_Counter++;
    }
  }
}

void sendLink() {
  for (int i = 1; i < 12; i++) {
    Serial.write(jamsync_link[i]);
  }
  jamsync_link_timer = micros();
}

void sendStop() {
  for (int i = 1; i < 25; i++) {
    Serial.write(jamsync_stop[i]);
  }
}

void GetBPM() {
  jamsync_measure_timer = micros() - jamsync_measure_timer;
  jamsync_loop_timer = jamsync_measure_timer;
  BPM = round(QPM * 60000000.0 / jamsync_measure_timer);
  jamsync_measure_timer = micros();
}

void Sendjamsync_sync() {
  unsigned long LoopTime;
  int b163 = 0;
  int w;
  int x;
  int y;
  int z = 0; //xor checksum


  // LoopTime = floor(1000* NOM * QPM * 60.0 / BPM);
  LoopTime = floor(jamsync_loop_timer / 1000.0);
  x = floor(log(LoopTime / 2000.0) / log(4.0));
  b163 = (LoopTime / (2000.0 * pow(4.0, x))) > 2;
  y = 2 * pow(2, b163) * pow(4, x);
  w = floor(LoopTime / y);

  // BPM
  jamsync_sync[8]  = 66 + 8 * ((63 < BPM) && (BPM < 128) || BPM > 191) ;
  jamsync_sync[12] = (4 * BPM > 127 && 4 * BPM < 256) * (4 * BPM - 128) +
                     (2 * BPM > 127 && 2 * BPM < 256) * (2 * BPM - 128) +
                     (BPM > 127 && BPM < 256) * (BPM - 128);
  jamsync_sync[13] = 1 * (BPM > 127) + 66;

  // lenght
  jamsync_sync[16] = 64 + 8 * b163;
  jamsync_sync[21] = 64 + x;
  jamsync_sync[20] = 128 * (0.001 * w - 1);
  jamsync_sync[19] = pow(128.0, 2) * (0.001 * w - 1 - jamsync_sync[20] / 128.0);
  jamsync_sync[18] = pow(128.0, 3) * (0.001 * w - 1 - jamsync_sync[20] / 128.0 - jamsync_sync[19] / pow(128.0, 2));

  // command
  jamsync_sync[22] = 5;

  // checksum
  for (int i = 8; i < 23; i++) {
    z = z ^ int(jamsync_sync[i]); // checksum XOR
  }
  jamsync_sync[23] = z;

  for (int i = 1; i < 25; i++) {
    Serial.write(int(jamsync_sync[i]));
  }
}

hope that helps...

Thank you. Yes, I will be using a Nano. I will test this to see if it works soon.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.