[solved] Code optimization

Hello everyone
I’m creating this topic because I’d like to optimise the code of my project.
I’ve created an “audio spectrum visualizer” out of an auto gain electret microphone, and a 16x16 flexible led matrix.
I’m using an arduino mega 2560

The wiring is in the attachement.

I’m using the GFX, NeoPixel and NeoMatrix libraries from Adafruit, and the fft library from Open Music Lab, which you can download here.

The first part which get the sound and sent it to the fft library was taken from an exemple. The part where I display the frequencies level is where I need help. I’d like the leds to update faster, but i don’t really know how to make it.

Here’s the code :

#define LOG_OUT 1 // Utilisation de la fonction LOG
#define FFT_N 32 // Etude sur 32 points

#include <FFT.h> // insert la bibliothèque FFT

#include <Adafruit_GFX.h> // insert la bibliothèque Adafruit GFX
#include <Adafruit_NeoMatrix.h> // insert la bibliothèque Adafruit NeoMatrix pr communiquer avec la matrice led
#include <Adafruit_NeoPixel.h> // insert la bibliothèque Adafruit Neopixel, complémentaire a NeoMatrix
#ifndef PSTR
 #define PSTR // Make Arduino Due happy (je sais pas à quoi ça sert, mais c'est dans l'exemple de la bibliothèque NeoMatrix, donc je le met)
#endif

#define PIN 22 // définie la pin de la matrice

/* Définition de la matrice :
 *  Paramètre n°1 => Largeur de la matrice
 *  Paramètre n°2 => Longueur de la matrice
 *  Paramètre n°3 => Pin de la matrice
 *  Paramètre n°4 => Définition du type de la matrice. A ajouter : 
 *   - Position du premier pixel : RIGHT / LEFT + TOP / BOTTOM
 *   - Agencement des pixels : ROWS / COLUMS
 */

Adafruit_NeoMatrix matrix = Adafruit_NeoMatrix(16, 16, PIN,
  NEO_MATRIX_TOP    + NEO_MATRIX_RIGHT +
  NEO_MATRIX_COLUMNS + NEO_MATRIX_ZIGZAG,
  NEO_GRB           + NEO_KHZ800);

int eq[] = {190, 160, 90, 80, 50, 40, 30, 20, 20, 10, 5, 5, 5, 0, 0, 0};
int level[16];

void setup() {
  TIMSK0 = 0; // turn off timer0 for lower jitter
  ADCSRA = 0xe5; // set the adc to free running mode
  ADMUX = 0x40; // use adc0
  DIDR0 = 0x01; // turn off the digital input for adc0

  matrix.begin();
  matrix.setBrightness(10);
}

void loop() {
  while(1) { // reduces jitter
    cli();  // UDRE interrupt slows this way down on arduino1.0
    for (int i = 0 ; i < 64 ; i += 2) { // save 32 samples
      while(!(ADCSRA & 0x10)); // wait for adc to be ready
      ADCSRA = 0xf5; // restart adc
      byte m = ADCL; // fetch adc data
      byte j = ADCH;
      int k = (j << 8) | m; // form into an int
      k -= 0x0200; // form into a signed int
      k <<= 6; // form into a 16b signed int
      fft_input[i] = k; // put real data into even bins
      fft_input[i+1] = 0; // set odd bins to 0
    }
    fft_window(); // window the data for better frequency response
    fft_reorder(); // reorder the data before doing the fft
    fft_run(); // process the data in the fft
    fft_mag_log(); // take the output of the fft
    sei();
    //From here, the code is from me, that's the part that I'd like to work faster :
    //matrix.writeFastHLine(0, 0, 16, matrix.Color(0, 0, 255));
    byte i = 0;
    while (i < FFT_N/2) { 
      level[i] = fft_log_out[i]; // - eq[i];
      if (level[i] < 0)
        level[i] = 0;
      level[i] = map(level[i], 0, 255, 0, 15);
      affichage(i, level[i]);
      i++;
    }
  }
}

void affichage(int x, int j) {
  int j = 1;
  while (j <= 15) {
    if (j > y) {
      matrix.drawPixel(x, j, 0);
    } else if (j < 2) {
      matrix.drawPixel(x, j, matrix.Color(0, 0, 255));
    } else if (j >= 2 && j <= 10) {
      matrix.drawPixel(x, j, matrix.Color(0, 255, 0));
    } else if (j > 10) {
      matrix.drawPixel(x, j, matrix.Color(255, 0, 0));
    }
    matrix.show();
    j++;
  }
}

Some comments are in french, telle me if you need a translation.
Can someone help me make this work faster ?

Thanks

is your EQ array could be constant ? (is it even used ?? I saw -eq[] but muted...

level[i] = map(level[i], 0, 255, 0, 15);

could be:

level[i] = level[i]/17;//255/15=17...

you got entire number divider.. you could use it...

Can someone help me make this work faster ?

Every time you call matrix.show(), data for all (256?) pixels needs to be sent. If you set 16 pixels, and then call matrix.show(), you will see a significant speed up.

nitrof:
is your EQ array could be constant ? (is it even used ?? I saw -eq but muted…

Set it as a constant. And it was muted cause I wanted to see the microphone noises, but it is not meant to be muted.

nitrof:

level[i] = map(level[i], 0, 255, 0, 15);

could be:

level[i] = level[i]/17;//255/15=17...

you got entire number divider… you could use it…

Made that too.
Things seems to work a bit faster.

New code :

#define LOG_OUT 1 // Utilisation de la fonction LOG
#define FFT_N 32 // Etude sur 32 points

#include <FFT.h> // insert la bibliothèque FFT

#include <Adafruit_GFX.h> // insert la bibliothèque Adafruit GFX
#include <Adafruit_NeoMatrix.h> // insert la bibliothèque Adafruit NeoMatrix pr communiquer avec la matrice led
#include <Adafruit_NeoPixel.h> // insert la bibliothèque Adafruit Neopixel, complémentaire a NeoMatrix
#ifndef PSTR
 #define PSTR // Make Arduino Due happy (je sais pas à quoi ça sert, mais c'est dans l'exemple de la bibliothèque NeoMatrix, donc je le met)
#endif

#define PIN 22 // définie la pin de la matrice

/* Définition de la matrice :
 *  Paramètre n°1 => Largeur de la matrice
 *  Paramètre n°2 => Longueur de la matrice
 *  Paramètre n°3 => Pin de la matrice
 *  Paramètre n°4 => Définition du type de la matrice. A ajouter : 
 *   - Position du premier pixel : RIGHT / LEFT + TOP / BOTTOM
 *   - Agencement des pixels : ROWS / COLUMS
 */

Adafruit_NeoMatrix matrix = Adafruit_NeoMatrix(16, 16, PIN,
  NEO_MATRIX_TOP    + NEO_MATRIX_RIGHT +
  NEO_MATRIX_COLUMNS + NEO_MATRIX_ZIGZAG,
  NEO_GRB           + NEO_KHZ800);

int const eq[] = {190, 160, 90, 80, 50, 40, 30, 20, 20, 10, 5, 5, 5, 0, 0, 0};
int level[16];

void setup() {
  TIMSK0 = 0; // turn off timer0 for lower jitter
  ADCSRA = 0xe5; // set the adc to free running mode
  ADMUX = 0x40; // use adc0
  DIDR0 = 0x01; // turn off the digital input for adc0

  matrix.begin();
  matrix.setBrightness(10);
}


void loop() {
  while(1) { // reduces jitter
    cli();  // UDRE interrupt slows this way down on arduino1.0
    for (int i = 0 ; i < 64 ; i += 2) { // save 32 samples
      while(!(ADCSRA & 0x10)); // wait for adc to be ready
      ADCSRA = 0xf5; // restart adc
      byte m = ADCL; // fetch adc data
      byte j = ADCH;
      int k = (j << 8) | m; // form into an int
      k -= 0x0200; // form into a signed int
      k <<= 6; // form into a 16b signed int
      fft_input[i] = k; // put real data into even bins
      fft_input[i+1] = 0; // set odd bins to 0
    }
    fft_window(); // window the data for better frequency response
    fft_reorder(); // reorder the data before doing the fft
    fft_run(); // process the data in the fft
    fft_mag_log(); // take the output of the fft
    sei();
    matrix.writeFastHLine(0, 15, 16, matrix.Color(0, 0, 255));
    byte i = 0;
    while (i < FFT_N/2) { 
      level[i] = fft_log_out[i] - eq[i];
      if (level[i] < 0)
        level[i] = 0;
      level[i] = level[i] / 17;
      affichage(i, level[i]);
      i++;
    }
  }
}

void affichage(int x, int y) {
  int j = 1;
  while (j <= 15) {
    if (j > y) {
      matrix.drawPixel(x,15 - j, 0);
    } else if (j < 2) {
      matrix.drawPixel(x, 15 - j, matrix.Color(0, 0, 255));
    } else if (j >= 2 && j <= 10) {
      matrix.drawPixel(x, 15 - j, matrix.Color(0, 255, 0));
    } else if (j > 10) {
      matrix.drawPixel(x, 15 - j, matrix.Color(255, 0, 0));
    }
    matrix.show();
    j++;
  }
}

PaulS:
If you set 16 pixels, and then call matrix.show(), you will see a significant speed up.

Well I kinda want my 16 pixels width…

I thought about only updating the pixels that changed, but would that be worth it ?

Well I kinda want my 16 pixels width…

But do you need them to light up one at a time? Or would lighting up all 16 at once work just as well?

int level[16];

Try changing to:

int8_t level[16];

and:

    while (i < FFT_N/2) {
      if [fft_log_out[i]] >= 0) {
        level[i] = ((uint8_t)fft_log_out[i]) / 16; // - eq[i];
      } else {
        level[i] = 0;
      }

This is based on…

  • checking for >= 0 should be approximately the same speed regardless of data type.
  • division of uint8 by 16 should end up being a 4-bit shift. Much faster than the 32bit division that map() will do.

Made some changes, totally changed the way I displayed the pixels. Now can I call matrix.show() only for the pixels I want ?

Code :

#include <gfxfont.h>

#define LOG_OUT 1 // Utilisation de la fonction LOG
#define FFT_N 32 // Etude sur 32 points

#include <FFT.h> // insert la bibliothèque FFT

#include <Adafruit_GFX.h> // insert la bibliothèque Adafruit GFX
#include <Adafruit_SPITFT.h>
#include <Adafruit_SPITFT_Macros.h>
#include <Adafruit_NeoMatrix.h> // insert la bibliothèque Adafruit NeoMatrix pr communiquer avec la matrice led
#include <Adafruit_NeoPixel.h> // insert la bibliothèque Adafruit Neopixel, complémentaire a NeoMatrix
#ifndef PSTR
 #define PSTR // Make Arduino Due happy (je sais pas à quoi ça sert, mais c'est dans l'exemple de la bibliothèque NeoMatrix, donc je le met)
#endif

#define PIN 6 // définie la pin de la matrice

/* Définition de la matrice :
 *  Paramètre n°1 => Largeur de la matrice
 *  Paramètre n°2 => Longueur de la matrice
 *  Paramètre n°3 => Pin de la matrice
 *  Paramètre n°4 => Définition du type de la matrice. A ajouter : 
 *   - Position du premier pixel : RIGHT / LEFT + TOP / BOTTOM
 *   - Agencement des pixels : ROWS / COLUMS
 */

Adafruit_NeoMatrix matrix = Adafruit_NeoMatrix(16, 16, PIN,
  NEO_MATRIX_BOTTOM + NEO_MATRIX_RIGHT +
  NEO_MATRIX_ROWS   + NEO_MATRIX_ZIGZAG,
  NEO_GRB           + NEO_KHZ800);

int const eq[] = {190, 160, 90, 80, 50, 40, 30, 20, 20, 10, 5, 5, 5, 0, 0, 0};
int8_t level[16];

void setup() {
  TIMSK0 = 0; // turn off timer0 for lower jitter
  ADCSRA = 0xe5; // set the adc to free running mode
  ADMUX = 0x40; // use adc0
  DIDR0 = 0x01; // turn off the digital input for adc0

  matrix.begin();
  matrix.setBrightness(20);
}


void loop() {
  while(1) { // reduces jitter
    cli();  // UDRE interrupt slows this way down on arduino1.0
    for (int i = 0 ; i < 64 ; i += 2) { // save 32 samples
      while(!(ADCSRA & 0x10)); // wait for adc to be ready
      ADCSRA = 0xf5; // restart adc
      byte m = ADCL; // fetch adc data
      byte j = ADCH;
      int k = (j << 8) | m; // form into an int
      k -= 0x0200; // form into a signed int
      k <<= 6; // form into a 16b signed int
      fft_input[i] = k; // put real data into even bins
      fft_input[i+1] = 0; // set odd bins to 0
    }
    fft_window(); // window the data for better frequency response
    fft_reorder(); // reorder the data before doing the fft
    fft_run(); // process the data in the fft
    fft_mag_log(); // take the output of the fft
    sei();
    initialisation();
    byte i = 0;
    while (i < FFT_N/2) {
       if (fft_log_out[i] >= 0) {
        level[i] = (((uint8_t)fft_log_out[i]) - eq[i]) / 16;
      } else {
        level[i] = 0;
      }
      displaySound(i, level[i]);
      i++;
    }
  }
}

void initialisation() {
  matrix.writeFillRect(0, 14, 16, 2, matrix.Color(0, 0, 255));
  matrix.writeFillRect(0, 5, 16, 9, matrix.Color(0, 255, 0));
  matrix.writeFillRect(0, 0, 16, 5, matrix.Color(255, 0, 0));
}

void displaySound(int x, int h) {
  matrix.writeFastVLine(x, 0, 15-h, 0);
  matrix.show();
}

Now can I call matrix.show() only for the pixels I want ?

Yes, and no.

What you do is tell one or more pixels to do something different. Then, show() is like Captain Picard saying "Make it so". It actually causes all the pending changes to be committed to the LEDs. So, if you didn't change a pixel color, that pixel will be unaffected by show().

Ok, thanks. Then I guess I’ll just keep my code as it is. It is running well now. Thanks to all of you for helping me.

Got this out by turning the output array to 8bit, dividing it instead of mapping it, and changing the way
I displayed the pixels (line by line instead of pixel by pixel)

Here is the final code :

#define LOG_OUT 1 // Utilisation de la fonction LOG
#define FFT_N 32 // Etude sur 32 points

#include <FFT.h> // insert la bibliothèque FFT

#include <Adafruit_GFX.h> // insert la bibliothèque Adafruit GFX
#include <Adafruit_SPITFT.h>
#include <Adafruit_SPITFT_Macros.h>
#include <Adafruit_NeoMatrix.h> // insert la bibliothèque Adafruit NeoMatrix pr communiquer avec la matrice led
#include <Adafruit_NeoPixel.h> // insert la bibliothèque Adafruit Neopixel, complémentaire a NeoMatrix
#ifndef PSTR
 #define PSTR // Make Arduino Due happy (je sais pas à quoi ça sert, mais c'est dans l'exemple de la bibliothèque NeoMatrix, donc je le met)
#endif

#define PIN 6 // définie la pin de la matrice

/* Définition de la matrice :
 *  Paramètre n°1 => Largeur de la matrice
 *  Paramètre n°2 => Longueur de la matrice
 *  Paramètre n°3 => Pin de la matrice
 *  Paramètre n°4 => Définition du type de la matrice. A ajouter : 
 *   - Position du premier pixel : RIGHT / LEFT + TOP / BOTTOM
 *   - Agencement des pixels : ROWS / COLUMS
 */

Adafruit_NeoMatrix matrix = Adafruit_NeoMatrix(16, 16, PIN,
  NEO_MATRIX_BOTTOM + NEO_MATRIX_RIGHT +
  NEO_MATRIX_ROWS   + NEO_MATRIX_ZIGZAG,
  NEO_GRB           + NEO_KHZ800);

int const eq[] = {190, 160, 90, 80, 50, 40, 30, 20, 20, 10, 5, 5, 5, 0, 0, 0};
int8_t level[16];

void setup() {
  TIMSK0 = 0; // turn off timer0 for lower jitter
  ADCSRA = 0xe5; // set the adc to free running mode
  ADMUX = 0x40; // use adc0
  DIDR0 = 0x01; // turn off the digital input for adc0

  matrix.begin();
  matrix.setBrightness(20);
}


void loop() {
  while(1) { // reduces jitter
    cli();  // UDRE interrupt slows this way down on arduino1.0
    for (int i = 0 ; i < 64 ; i += 2) { // save 32 samples
      while(!(ADCSRA & 0x10)); // wait for adc to be ready
      ADCSRA = 0xf5; // restart adc
      byte m = ADCL; // fetch adc data
      byte j = ADCH;
      int k = (j << 8) | m; // form into an int
      k -= 0x0200; // form into a signed int
      k <<= 6; // form into a 16b signed int
      fft_input[i] = k; // put real data into even bins
      fft_input[i+1] = 0; // set odd bins to 0
    }
    fft_window(); // window the data for better frequency response
    fft_reorder(); // reorder the data before doing the fft
    fft_run(); // process the data in the fft
    fft_mag_log(); // take the output of the fft
    sei();
    initialisation();
    byte i = 0;
    while (i < FFT_N/2) {
       if (fft_log_out[i] >= 0) {
        level[i] = (((uint8_t)fft_log_out[i]) - eq[i]) / 16;
      } else {
        level[i] = 0;
      }
      displaySound(i, level[i]);
      i++;
    }
    matrix.show();
  }
}

void initialisation() {
  matrix.writeFillRect(0, 14, 16, 2, matrix.Color(0, 0, 255));
  matrix.writeFillRect(0, 5, 16, 9, matrix.Color(0, 255, 0));
  matrix.writeFillRect(0, 0, 16, 5, matrix.Color(255, 0, 0));
}

void displaySound(int x, int h) {
  matrix.writeFastVLine(x, 0, 15-h, 0);
}