Audio meter (Teensy 3.1 + Audio adaptor board + Led strip)

nicolas_soundforce

Active member
Hi there,

For my second teensy project I went for a Audio Meter for my home studio.
I got a Teensy 3.1 and the audio adapter, soldered 2 jacks and plugged them on 2 audio outputs on my audio interface.
Using my interface software/mixer I route the signal I want to meter to the teensy.
Using 2 x 1/4th of 1m of Adafruit 144 ledstrips, I got left and right channels meter.
Between the signal pins and the led strips I wired a 74HCT245N. It's NOT working without it.
There was a resistor already included at the left side of the strip so I just add to add a 470 Ohms resistor on the right channel led strip.
And a cap between the + and - pins of the power supply to protect the strips.
The whole project (teensy included) is powered via a 5v 2a power supply.

Here is the project in action, the poor video quality does make justice to the speed of the meter :


I took the StereoPeakMeter example as a starting point. I found there the functions peak_L and peak_L.
I am using those but I am not sure how they really work.

For the LED strips I decided to try to the FastLED library as I read that the OctoWS2811 library was not working properly along the audio board. I used pins 0 and 1 for the led strips signal pins.

THE CODE is below.

Paul if you read this, would you mind explaining a bit the working of the function ? I can't find any documentation on this.

Also I saw that maximum value coming of the ADC is about 65000. Is it possible to make a correlation between audio line level signals and the ADC returning values ? I would like to make this meter as accurate as possible.

At the moment I sent sine waves at different audio levels (from -28db to 0db) from the computer to the teensy/audio board and measured the resulting ADC value. I took one calibration value per LED.

I am not sure if I should use the Peak function ?! I just want to take a sample of the incoming audio and lights the right LED according to the level.
Can I just read the line level inputs with an easy function ?

Paul, I love the teensy thank you so much for it.

Gr,
Nicolas.

SoundForce-MTR1.JPGSoundForce-MTR2.JPGSoundForce-MTR3.JPGSoundForce-MTR4.JPG

Code:
// SoundForce - MTR
// 2014
// Nicolas Toussaint - http://sound-force.nl/

#include <Audio.h>
#include <Wire.h>
#include <SD.h>

#include <FastLED.h>
#define NUM_LEDS_PER_STRIP 36 //I used Adafruit 144 led strip and 1/4 meter pieces, so 36 LEDs per piece/chann

CRGB ledsA[NUM_LEDS_PER_STRIP]; //Left channel LED strip
CRGB ledsB[NUM_LEDS_PER_STRIP]; //Right channel LED strip

const int myInput = AUDIO_INPUT_LINEIN;

AudioInputI2S        audioInput;         // audio shield: mic or line-in
AudioPeak            peak_L;
AudioPeak            peak_R;
AudioOutputI2S       audioOutput;        // audio shield: headphones & line-out

AudioConnection c1(audioInput,0,peak_L,0);
AudioConnection c2(audioInput,1,peak_R,0);

AudioControlSGTL5000 audioShield;
    
// Calibration for the 36 leds, measured sending a test signal at different audio levels
  int valueADC[37]= {
    -2395, //0
    -1320, //1
    195, //2
    2335, //3
    5365, //4
    13625, //5
    15665, //6
    17145, //7
    19000, //8
    21015, //9
    22875, //10
    25215, //11
    27755, //12
    30095, //13
    33045, //14
    36245, //15
    37195, //16
    38185, //17
    39185, //18
    40215, //19
    41275, //20
    42355, //21
    43455, //22
    44575, //23
    45745, //24
    46915, //25
    48125, //26
    49365, //27
    50635, //28
    51935, //29
    53265, //30
    54615, //31
    56015, //32
    57465, //33
    58888, //34
    60345, //35
    100000000 //36
} ; 
    
//Integer to store Left and Right channels level values
    int ReadingL=0;
    int ReadingR=0;
            
void setup() {
  AudioMemory(6);
  audioShield.enable();
  audioShield.inputSelect(myInput);
  audioShield.volume(0.75);
  audioShield.unmuteLineout();
  Serial.begin(Serial.baud());
  
  FastLED.addLeds<NEOPIXEL, 0>(ledsA, NUM_LEDS_PER_STRIP); // Left channel LED STRIP
  FastLED.addLeds<NEOPIXEL, 1>(ledsB, NUM_LEDS_PER_STRIP); // Right channel LED STRIP
  LEDS.setBrightness(40); //Dimming the LEDs
}

elapsedMillis timer;
int SamplingPeriod = 45;

void loop() {

//Reading a sampling every sampling period for both channels
    if (timer>SamplingPeriod){
     timer=0;
     ReadingL = peak_L.Dpp() - 5000;
     ReadingR = peak_R.Dpp() - 5000;
     peak_L.begin();
     peak_R.begin();
     }

//LEFT CHANNEL

//Turns off every LEDs when input is under lowest level
    if ((ReadingL<valueADC[0])){
      for (int i=0 ; i<36; i++){
      ledsA[i] = CRGB::Black;  
      }
    }   

//Turns on and off LEDs according to calibration values 
// 4 colors : Green/Yellow/Orange/red
    for (int j =0; j<37; j++){
      if ((ReadingL>valueADC[j]) && (ReadingL<valueADC[j+1])){
       for (int i=0; i<j+1; i++){
         if (i<15){
         ledsA[i] = CRGB::Green;  
         }
         
         if (i>=15 && i<25){
         ledsA[i] = CRGB::Yellow;  
         }
         
         if (i>=25 && i<31){
         ledsA[i] = CRGB::Orange;    
         } 
         
         if (i>=31 && i<36){
         ledsA[i] = CRGB::Red;    
         }
       }
       
         for (int k=j+1; k<36; k++){
         ledsA[k] = CRGB::Black;  
         }
      }
    }
    
    
//RIGHT CHANNEL

//Turns off every LEDs when input is under lowest level
    if ((ReadingR<valueADC[0])){
      for (int i=0 ; i<36; i++){
      ledsB[i] = CRGB::Black;  
      }
    }   

//Turns on and off LEDs according to calibration values 
// 4 colors : Green/Yellow/Orange/red
    for (int j =0; j<37; j++){
      if ((ReadingR>valueADC[j]) && (ReadingR<valueADC[j+1])){
       for (int i=0; i<j+1; i++){
         if (i<15){
         ledsB[i] = CRGB::Green;  
         }
         
         if (i>=15 && i<25){
         ledsB[i] = CRGB::Yellow;  
         }
         
         if (i>=25 && i<31){
         ledsB[i] = CRGB::Orange;    
         } 
         
         if (i>=31 && i<36){
         ledsB[i] = CRGB::Red;    
         }
         
       }
         for (int k=j+1; k<36; k++){
         ledsB[k] = CRGB::Black;  
         }
      }
    }
    

      FastLED.show();

}
 
Last edited:
I made the AudioPeak object, it is pretty simplistic really. Sorry about the lack of documentation, sometimes I like to make at least a little, if not well rounded documentation but often I am a lazy and/or distracted so and so...

With default settings, last time I looked at all details, 65535 = 3.3Vpp so Vpp can be calculated as (peak_L.Dpp()/65535)*3.3 - I named the function .Dpp() thinking of "data peak to peak"

Does that help enough? I know more than this may let on but others know more and more again - either case I can explain more but you probably get it from that? No?


Edit: How it works is by taking the largest value (including negative values where, of course, -1 is bigger than -10) seen since last call of Dpp() into one variable and the smallest value into another variable and when called again the difference is returned (as unsigned short integer) indicating the most that the signal changed in between calls.

If you feed it a slow sine (or other shape) you will need to wait enough samples for the signal to make its transition from 'highest positive value' thru to 'lowest negative value' to see the amplitude reflected in the results. Normal musical audio has peaks that occur a lot quicker, for fairly extreme example, than 10Hz - at 10Hz, to see the full swing of the signal, you would have to wait at least 1/10th of a second worth of samples to see the true scale of the amplitude.

For visual effect I think 24 to 60 'frames' per second is more than adequate to meter music with fairly natural looking response to the signal - did my initial response give you enough info to manage the scale you want the LEDs to reflect?
 
Last edited:
I made the AudioPeak object, it is pretty simplistic really. Sorry about the lack of documentation, sometimes I like to make at least a little, if not well rounded documentation but often I am a lazy and/or distracted so and so...

With default settings, last time I looked at all details, 65535 = 3.3Vpp so Vpp can be calculated as (peak_L.Dpp()/65535)*3.3 - I named the function .Dpp() thinking of "data peak to peak"

Does that help enough? I know more than this may let on but others know more and more again - either case I can explain more but you probably get it from that? No?


Edit: How it works is by taking the largest value (including negative values where, of course, -1 is bigger than -10) seen since last call of Dpp() into one variable and the smallest value into another variable and when called again the difference is returned (as unsigned short integer) indicating the most that the signal changed in between calls.

If you feed it a slow sine (or other shape) you will need to wait enough samples for the signal to make its transition from 'highest positive value' thru to 'lowest negative value' to see the amplitude reflected in the results. Normal musical audio has peaks that occur a lot quicker, for fairly extreme example, than 10Hz - at 10Hz, to see the full swing of the signal, you would have to wait at least 1/10th of a second worth of samples to see the true scale of the amplitude.

For visual effect I think 24 to 60 'frames' per second is more than adequate to meter music with fairly natural looking response to the signal - did my initial response give you enough info to manage the scale you want the LEDs to reflect?

Thank you so much for your the details!
I'll read this and the code.
 
How much processing power is being used by the Power Meter? I'm wondering if the Teensy is still able to perform other tasks in an amplifier, for example.
 
that has to depend on the refresh rate, that is how often the LED display is being changed, and on specifics of the FastLED which I don't happen to know.

At a guess, there is probably about 70% or more of available processing time left to other tasks which can be added to the base I see Nicolas using - this assuming that FastLED is fairly lightweight.

Being that I am guessing and assuming stuff the chances I am wrong have to pretty good, or bad, depending on how you look at it.
 
If you mean Teensy loader it is talking about what percentage of the available program memory is being used by your code. There probably are tool-chains for some devices which will guess-estimate something relating to processor idle time like usage but I haven't noticed one and I wouldn't place much confidence in it if I saw one.

See this post by Paul about the Audio library memory and processor usage: http://forum.pjrc.com/threads/24793-Audio-Library?p=41159&viewfull=1#post41159

Those functions only deal with how much the Audio Library is using and won't give an accurate picture of the overall usage by your sketch Nicolas. If the FastLED library has similar functions then you could take its results and add them to the corresponding results from the Audio Library functions and have a very close idea of it but to be accurate you would have to figure out how to rate your additional code usage of the processor.

That isn't as hard as it may seem. There are examples in these forums tho they may be difficult to find - if you insist I will post a fairly rough and ready way you can determine how much available processor time is being chewed by your sketch with at least a modicum of accuracy.

Personally I just write things how I want to calculate them and react to results and if it performs well I just smirk and get on with my life, if it does not perform well I look at the most obvious "burden" like calculations and look at ways of optimising or avoiding them and such like - a lot of the time the way in which it fails to perform well will indicate which particular part of my code sucks the life out of the rest of it, sometimes it can seem very mysterious till I find the part of my code that I will go on to perceive as a mistake on my part.

Any good? Gonna make me post my solution for determining usage? :)
 
Back
Top