Para el envío de mensajes CC, se ha ideado un modelo parecido a los clips de automatizacion de un daw. Cada potenciómetro contiene un valor por el que pasará conforme avance la secuencia, y depende de los mutes que tengas activados, podrás añadir más puntos.

Para evitar que la lectura de saltos por cada valor, se realiza una interpolación entre cada uno de los puntos activados. Puede cambiarse tambien la tensión de la curva cambiando el valor ccSmoothCurve. Ese valor lee un array con valores predefinidos de curva:

const byte smoothCurves[13][100] PROGMEM = {
  // Curva 0: Antiguamente Curva 7 (Logarítmica extrema)
  {0, 161, 173, 180, 185, 189, 193, 196, 198, 201, 203, 205, 206, 208, 210, 211, 213, 2  // Curva 1: Antiguamente Curva 8
  {0, 132, 146, 155, 161, 166, 171, 175, 178, 181, 184, 186, 189, 191, 193, 195, 197, 1  // Curva 2: Antiguamente Curva 9
  {0, 102, 117, 127, 134, 140, 146, 150, 154, 158, 161, 164, 167, 170, 172, 175, 177, 1  // Curva 3: Antiguamente Curva 10
  {0, 81, 96, 106, 114, 121, 127, 131, 136, 140, 144, 147, 150, 154, 156, 159, 162, 16  // Curva 4: Antiguamente Curva 11
  {0, 55, 69, 80, 88, 94, 100, 105, 110, 115, 119, 123, 126, 130, 133, 136, 139, 142, 1  // Curva 5: Antiguamente Curva 12 (Logarítmica suave)
  {0, 26, 36, 44, 51, 57, 63, 68, 72, 77, 81, 85, 89, 92, 96, 99, 103, 106, 109, 112, 1  
  // Curva 6: Antiguamente Curva 0 (Lineal PURA - El Centro)
  {0, 3, 5, 8, 10, 13, 15, 18, 21, 23, 26, 28, 31, 33, 36, 39, 41, 44, 46, 49, 52, 54,  
  // Curva 7: Antiguamente Curva 1 (Exponencial suave)
  {0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 3, 3, 4, 4, 5, 6, 7, 8, 8, 9, 10, 11, 13, 14, 15, 16,  // Curva 8: Antiguamente Curva 2
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 4, 4, 5, 5,  // Curva 9: Antiguamente Curva 3
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1,  // Curva 10: Antiguamente Curva 4
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // Curva 11: Antiguamente Curva 5
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // Curva 12: Antiguamente Curva 6 (Exponencial extrema)
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

Lo que hacemos para calcular la curva es mirar la diferencia entre el paso en que estamos y el paso al que vamos, teniendo en cuenta que tendremos puntos que están activos o inactivos con los mutes.

Para ahorrar procesamiento, no calculamos toda la curva de una vez sino que la troceamos entre cada uno de los pasos que existen.

  if (nClockMsg == 0) { 
    // FASE DE CÁLCULO: Se ejecuta solo en el primer pulso de cada Step
    nMsgCC[id] = 0;
    int totalSteps = seqActual->nTotalSteps;
    
    // Buscar el punto desde el que partimos
		int prevStep = nStep;
    int countBack = 0;
    while (countBack < totalSteps) {
      if (!seqActual->steps[prevStep].ccMutes) { // Byscamos el primer valor no muteado
        break;
      }
      prevStep--;
      if (prevStep < 0) {prevStep = totalSteps - 1;} // Si prevStep alcanza el -1, el paso seguirá contando desde el número máximo de pasos
      countBack++;
    }

    // Mirar hacia adelante y encontrar el primer punto que no esté muteado
    int nextStep = nStep + 1;
    if (nextStep >= totalSteps) nextStep = 0;
    int countForward = 0;
    while (countForward < totalSteps) {
      if (!seqActual->steps[nextStep].ccMutes) {
        break;
      }
      nextStep++;
      if (nextStep >= totalSteps) nextStep = 0; // Si prevStep alcanza al número maximo de pasos, nextStep seguirá contando desde 0
      countForward++;
    }

Luego, calculamos las distancias, tanto entre steps como entre pulsos de reloj midi

    // Distancia total entre los dos puntos a interpolar
    int distanceSteps = 0;
    if (nextStep > prevStep) {distanceSteps = nextStep - prevStep;} 
    else if (nextStep < prevStep) {distanceSteps = (totalSteps - prevStep) + nextStep;} 
    else {distanceSteps = totalSteps;}

    // Punto en el que nos encontramos actualmente (nStep) en cuanto a pasos
    int offsetSteps = 0;
    if (nStep >= prevStep) {offsetSteps = nStep - prevStep;} 
    else {offsetSteps = (totalSteps - prevStep) + nStep;}
    
    // Convertimos esos datos a pulsos de reloj midi
    int subdivisionActual = distanceSteps * subdivision; // Valores totales a calcular entre los dos puntos
    int offset = offsetSteps * subdivision; // Punto en el que nos encontramos actualmente en mensajes midi
    int divisor = (subdivisionActual > 1) ? subdivisionActual : 2;

Por último, y teniendo en cuenta la subdivisión, o sea, la cantidad de mensajes MIDI que hay entre figuras, calculamos la curva:

    for(int i = 0; i < subdivision; i++) {
      int indexCurva = offset + i;
      // vamos calculando cada valor de la curva según el tamaño entre pasos.
      // Aplicamos la fórmula: V = V_inicio + ((Delta * %_Curva) / 255)
      long valorCurva = ccCurveFunction(indexCurva, divisor, seqActual->steps[prevStep].ccSmoothCurve);
      CCinterpolation[i] = valorActual + (((long)ccStepSize * valorCurva) / 255); // 255 es el tamaño maximo de un byte
    }
  }
  
  

int MidiProgramming::ccCurveFunction(int i, int subdivisionActual, int tensionMode){
  // Mapeador para calcular los puntos que dure la curva. 
  // i = punto de la curva; 99 = tamaño de la tabla; subdivision actual = cuan largo es el paso
  int indiceLUT = (i * 99) / (subdivisionActual - 1);
  byte factorCurva = pgm_read_byte(&(smoothCurves[tensionMode][indiceLUT])); //pgm_read_byte es porque la tabla la tenemos en la memoria flash
  return factorCurva;
}

Para el visualizado, se ha optado por un mapa visual que muestre en que nivel se encuentra cada punto y que muestre la interpolación entre pasos

for (int i = 0; i < s->nTotalSteps; i++) {
  int nivel = map(visualBuffer[i], 0, 127, 0, 7);
  switch(nivel) {
  case 0: lcd.write(byte(0)); break;
  case 1: lcd.write(byte(1)); break;
  case 2: lcd.write(byte(2)); break;
  case 3: lcd.write(byte(3)); break;
  case 4: lcd.write(byte(4)); break;
  case 5: lcd.write(byte(5)); break;
  case 6: lcd.write(byte(6)); break;
  case 7: lcd.write(255); break;  
	}
}