Replacing values with arrays in the Smoothing sketch

Hey guys!

I’m struggling with a probably simple problem. I wanted to replace all the variables with arrays, because it would be too messy and inefficient to write out all the potentiometer variables if I had lets say six of them. I want to send several values as MIDI signals to use the potentiometers as MIDI knobs.

The code with two knobs and without arrays:

const int numReadings = 3;     // Anzahl der Readings
int controlChange = 176;        // MIDI Kanal 1
int readings0[numReadings];     // the readings from the analog input
int readings1[numReadings];
int readIndex0 = 0;   
int readIndex1 = 0; // the index of the current reading
int total0 = 0; 
int total1 = 0;   // the running total
int average0 = 0;   
int average1 = 0;   // the average
int inputPin0 = A0;
int inputPin1 = A1;
int averageMIDI0 = 0;
int averageMIDI1 = 0;
int averageMIDIAlt0 = 0;
int averageMIDIAlt1 = 0;
int controllerNummer0 = 20;
int controllerNummer1 = 21;




void setup() {
  // initialize serial communication with computer:
  Serial.begin(9600);
  // initialize all the readings to 0:
  for (int thisReading = 0; thisReading < numReadings; thisReading++) {
    readings0[thisReading] = 0;
    readings1[thisReading] = 0;
  }
}

void loop() {
  // subtract the last reading:
  total0 = total0 - readings0[readIndex0];
  total1 = total1 - readings1[readIndex1];
  // read from the sensor:
  readings0[readIndex0] = analogRead(inputPin0);
  readings1[readIndex1] = analogRead(inputPin1);
  // add the reading to the total:
  total0 = total0 + readings0[readIndex0];
  total1 = total1 + readings1[readIndex1];
  // advance to the next position in the array:
  readIndex0 = readIndex0 + 1;
  readIndex1 = readIndex1 + 1;


  // if we're at the end of the array...
  if (readIndex0 >= numReadings) {
    // ...wrap around to the beginning:
    readIndex0 = 0;
  }

    // if we're at the end of the array...
  if (readIndex1 >= numReadings) {
    // ...wrap around to the beginning:
    readIndex1 = 0;
  }

  // calculate the average:
  average0 = total0 / numReadings;
  averageMIDI0 = map(average0,0,1023,0,127);
    if (averageMIDI0 != averageMIDIAlt0) {
      Serial.write(controlChange);
      Serial.write(controllerNummer0);
      Serial.write(averageMIDI0);

      }

      averageMIDIAlt0 = averageMIDI0;
  

      average1 = total1 / numReadings;
  averageMIDI1 = map(average1,0,1023,0,127);
    if (averageMIDI1 != averageMIDIAlt1) {
      Serial.write(controlChange);
      Serial.write(controllerNummer1);
      Serial.write(averageMIDI1);

      }

      averageMIDIAlt1 = averageMIDI1;
    delay(5); 
      }

My failed attempt to use arrays:

// const int numReadings = 3;     // Anzahl der Readings
int controlChange = 176;        // MIDI Kanal 1
int readings[3];      // the readings from the analog input
int readIndex[] = {0,0,0};              // the index of the current reading
int total[] = {0,0,0};                  // the running total
int average[] = {0,0,0};                // the average
int inputPin[] = {A0,A1,A2};
int averageMIDI[] = {0,0,0};
int averageMIDIAlt[] = {0,0,0};
int controllerNummer[] = {20,21,22};
int i = 0;




void setup() {
  // initialize serial communication with computer:
  Serial.begin(9600);
  // initialize all the readings to 0:
  }


void loop() {
  for (i = 0; i < 3; i++) {
  // subtract the last reading:
  total[i] = total[i] - readings[readIndex[i]];
  // read from the sensor:
  readings[readIndex[i]] = analogRead(inputPin[i]);
  // add the reading to the total:
  total[i] = total[i] + readings[readIndex[i]];
  // advance to the next position in the array:
  readIndex[i] = readIndex[i] + 1;

  // if we're at the end of the array...
  if (readIndex[i] >= 3) {
    // ...wrap around to the beginning:
    readIndex[i] = 0;
  }

  // calculate the average:
  average[i] = total[i] / 3;
  averageMIDI[i] = map(average[i],0,1023,0,127);
    if (averageMIDI[i] != averageMIDIAlt[i]) {
      Serial.write(controlChange);
      Serial.write(controllerNummer[i]);
      Serial.write(averageMIDI[i]);

      }

      averageMIDIAlt[i] = averageMIDI[i];
    delay(5); 
      } 
}

I was using hairless-midiserial to get incoming midi signals but I only get this message with my code:
Warning: got a status byte when we were expecting 2 more data bytes, sending possibly incomplete MIDI message 0x80.

I would really appreciate any kind of help, thank you in advance! :slight_smile:

i think your problem is you only have one readings array. in your first version of code you have readings0 and readings1.

so you need to define "readings [3][3]"

When you have a bunch of variables and functions that group together to do a thing, it’s a good time to make an object class. Here is an example where I took the existing code and made a “Smooth” class to do the moving average. An array of Smooth objects can keep track of separate averages for each input. Now you can practice by making an object to handle the MIDI side and make an array of them.

const int NumReadings = 3;     // Anzahl der Readings


class Smooth
{
  private:  // Variables and functions that can't be seen from outside the object
    int readings[NumReadings];     // the readings from the analog input
    byte index;
    long total;

  public:
    Smooth() // Constructor.  Called when the object is created.
    {
      for (int thisReading = 0; thisReading < NumReadings; thisReading++)
      {
        readings[thisReading] = 0;
      }
      index = 0;
      total = 0;
    }


    void addSample(int reading)
    {
      total -= readings[index];
      readings[index] = reading;
      total += reading;
      index++;
      if (index >= NumReadings)
        index = 0;
    }


    int getAverage()
    {
      return total / NumReadings;
    }
};


const byte Pins = 2;
Smooth Averages[Pins];
const byte InputPins[Pins] = {A0, A1};


int controlChange = 176;        // MIDI Kanal 1

int averageMIDI0 = 0;
int averageMIDI1 = 0;
int averageMIDIAlt0 = 0;
int averageMIDIAlt1 = 0;
int controllerNummer0 = 20;
int controllerNummer1 = 21;


void setup()
{
  // initialize serial communication with computer:
  Serial.begin(9600);
}


void loop()
{
  for (int i = 0; i < Pins; i++)
  {
    // read from the sensor:
    Averages[i].addSample(analogRead(InputPins[i]));
  }


  // calculate the average:
  averageMIDI0 = map(Averages[0].getAverage(), 0, 1023, 0, 127);
  if (averageMIDI0 != averageMIDIAlt0)
  {
    Serial.write(controlChange);
    Serial.write(controllerNummer0);
    Serial.write(averageMIDI0);
  }
  averageMIDIAlt0 = averageMIDI0;




  averageMIDI1 = map(Averages[1].getAverage(), 0, 1023, 0, 127);
  if (averageMIDI1 != averageMIDIAlt1)
  {
    Serial.write(controlChange);
    Serial.write(controllerNummer1);
    Serial.write(averageMIDI1);
  }
  averageMIDIAlt1 = averageMIDI1;
  
  delay(5);
}

I agree with johnwasser that an object-oriented approach is appropriate here.

You’ll probably find that using averaging alone will not completely eliminate spurious MIDI messages. To prevent the values fluctuating even when the potentiometer isn’t moving, I’d recommend using hysteresis as well. It works great in combination with filtering (filter first, then apply hysteresis).

The simple moving average filter you’re using is not ideal: it uses a lot of memory. For general “smoothing” purposes, an exponential moving average filter is often a better choice. It requires just one reading worth of memory, whereas a simple moving average filter requires NumReadings memory elements. An exponential filter can be implemented really efficiently using fixed-point integer math.
You can find a simple Arduino example here. The mathematical derivation of the implementation is at the top of the page, and there’s a full discussion of the exponential moving average filter two pages back if you’re interested in how it works.

Finally, filtering potentiometer inputs for MIDI controllers is a solved problem, there’s no need to reinvent it yourself (unless it’s for learning purposes, of course!). I’d recommend using the Control Surface library I maintain, it does all filtering for you, under the hood, and allows you to create MIDI controllers using a simple declarative style. Your sketch would look something like this :

#include <Control_Surface.h> // Include the Control Surface library
 
// Instantiate a MIDI interface over the "Serial" port with the given baud rate:
USBSerialMIDI_Interface midi(9600);
 
// Instantiate an array of CCPotentiometer objects that read the position
// of a potentiometer and send MIDI Control Change messages accordingly:
CCPotentiometer potentiometers[] = {
  { A0, 20 },
  // │   └─── Controller number of the first potentiometer
  // └─────── Analog pin connected to the first potentiometer
  { A1, 21 },
  // │   └─── Controller number of the second potentiometer
  // └─────── Analog pin connected to the second potentiometer
  { A2, 22 },
  // etc.
};
 
void setup() {
  Control_Surface.begin(); // Initialize Control Surface
}
 
void loop() {
  Control_Surface.loop(); // Update the Control Surface
}

Pieter

Thank you gcjr your advice just made my code work properly!! :) Also thank you John, I have to take the time to study your code, it looks way more sophisticated and clean! Thank you Pieter too, I will try it with the library too! I am so glad I joined the community, I'm overwhelmed by the helpfulness of you! thanks, thanks!! :) :)