Pages: [1] 2   Go Down
Author Topic: Calibrating a VU meter using a pot/ dynamic calibration  (Read 1410 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Newbie
*
Karma: 0
Posts: 21
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi Folks,
The following is some code I adopted from a website for my LED VU meter. However, one of the issues is that the Sensitivity is sort of HARD-CODED. Is there a way to incorporate a more dynamic calibration going on from the audio signal depending on the song. Imagine a variable standing for 20 (the calibration factor) and changing depending on the signal strength of the cube.

You can view the "signal" strength coming through from A0 from the serial print screen and values usually are around 200 ish for me when driven by an Ipod.

int led[6] = { 3, 4, 5, 6, 7, 8}; // Assign the pins for the leds
int leftChannel = A0;  // left channel input
int left, i;

void setup()
{
for (i = 0; i < 6; i++)  // Tell the arduino that the leds are digital outputs
  pinMode(led, OUTPUT);
Serial.begin(9600);
}

void loop()
{
left = analogRead(leftChannel);  // read the left channel
Serial.println(left);
Serial.print("\t");
left = left / 20;    // adjusts the sensitivity
Serial.println(left);
Serial.print("\t");


  if (left == 0)  // if the volume is 0 then turn off all leds
   {
   for(i = 0; i < 6; i++)
     {
     digitalWrite(led, LOW);
     }
  }
 
  else
  {
   for (i = 0; i < left; i++) // turn on the leds up to the volume level
    {
     digitalWrite(led, HIGH);
    }
   
    for(i = i; i < 6; i++)  // turn off the leds above the voltage level
     {
      digitalWrite(led, LOW);
     }
  }
}
Logged

0
Offline Offline
Faraday Member
**
Karma: 19
Posts: 3418
20 LEDs are enough
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

This code uses a linear mapping of the analog value to the number of active LEDs. A logarithmic mapping is more common.

What exactly do you want? Usually a VU meter is intended as an indicator of the absolute levels. Thus adopting it to specific songs is somewhat pointless. Unless you want to have it as a kind of mini light show. But then this is pretty hard to do. You would need to know in advance the peak level of the song. Unless the time machine gets invented this is somewhat tricky.

Logged

Check out my experiments http://blog.blinkenlight.net

Offline Offline
Newbie
*
Karma: 0
Posts: 21
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

This code uses a linear mapping of the analog value to the number of active LEDs. A logarithmic mapping is more common.

Can you please direct me to a source or improvise on the code to do a logarithmic mapping? It will be great if you could kindly show me how that is done. Complete newb here; hence, no idea how that can be done.

What exactly do you want? Usually a VU meter is intended as an indicator of the absolute levels. Thus adopting it to specific songs is somewhat pointless. Unless you want to have it as a kind of mini light show. But then this is pretty hard to do. You would need to know in advance the peak level of the song. Unless the time machine gets invented this is somewhat tricky.

Thanks for asking that question. I guess I was not able to make it as clear as I wanted it to.

The problem I am envision is: let's say that the highest value of the signal I get is around 500ish, and I scale the program to react as much. I am using an Ipod to drive the VU meter at FULL volume. However, should I decrease the ipod volume, the signal strength goes down and so does the number of LEDs that light up. All I want is the user to have control over the sensitivity of the lighting. Does that make sense? Or put it the other way: is there a way to ensure that the VU meter behaves independently of the volume of the ipod?
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 21
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Also, one other problem is: I could not drive my VU meter with my laptop!! Any reason?
Logged

Montreal
Offline Offline
Edison Member
*
Karma: 23
Posts: 2486
Per aspera ad astra.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Something wrong with your code:
Code:
for (i = 0; i < left; i++) // turn on the leds up to the volume level
    {
     digitalWrite(led, HIGH);  <<<< Do you mean digitalWrite( i, HIGH)?
    }
    
What you are looking, is "automatic gain control"  loop, or AGC. To make it works, you need a low pass filter in software, look on this web site, should be running average or similar   example somewhere in playground / library section. Arduino IDE has smoothing in analog examples code, should be o'k.
Next, change your bargraph to :
Code:
   int ledLevelKr = map( left_input , 0, PeakHold_smoothedValue, 0, ledCountKr);

  for (int thisLed = 0; thisLed < ledCountKr; thisLed++) {
    if (thisLed < ledLevelKr)
      digitalWrite(ledPinsKr[thisLed], HIGH);
    else
      digitalWrite(ledPinsKr[thisLed], LOW);
  }
 
« Last Edit: March 26, 2012, 10:24:52 pm by Magician » Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 21
Arduino rocks
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

You should average you levels.
Because if you sample sound you have a wave and you never know where you sampled the wave.
If you want a good responsive bargraph use something like this:

Code:
for (int num=0; num < 4; num++) {
sound[num]= analogRead (inputPIN);
if(num==3) {
soundav=(sound[0]+sound[1]+sound[2]+sound[3])/4; // average sound levels

For you pot controle, use a lineaire pot meter a logarithmic will work to but won't give you a fine control.
You have to determine a max and minimum value. I choose between 15 and 25 because of your 20.
Code:
potControl = analogRead(potPIN);   // connect a linear potmeter on potPIN
levelControl = map(potControl, 0, 255,15,25); // you map the values of your pot to a more desired level between 15 and 25
left = left / levelControl;     // youre code

Use Magician his bargraph code, which is much better and more efficient.
Logged

ARDUINO-JAM  BELGIUM 2012

Offline Offline
Newbie
*
Karma: 0
Posts: 21
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Code:
for (i = 0; i < left; i++) // turn on the leds up to the volume level
    {
     digitalWrite(led, HIGH);  <<<< Do you mean digitalWrite( i, HIGH)?>>>>>> Yes, extremely sorry for this. Somehow, the original code has digitalWrite (led[i], HIGH).
    }
    
What you are looking, is "automatic gain control"  loop, or AGC. To make it works, you need a low pass filter in software, look on this web site, should be running average or similar   example somewhere in playground / library section. Arduino IDE has smoothing in analog examples code, should be o'k.
Next, change your bargraph to :
Code:
   int ledLevelKr = map( left_input , 0, PeakHold_smoothedValue, 0, ledCountKr);

  for (int thisLed = 0; thisLed < ledCountKr; thisLed++) {
    if (thisLed < ledLevelKr)
      digitalWrite(ledPinsKr[thisLed], HIGH);
    else
      digitalWrite(ledPinsKr[thisLed], LOW);
  }
 


Thank you so much for putting this in. I was wondering whether you can elaborate / comment on the code. I get the overall picture, but am having a hard time reading what this does:

 int ledLevelKr = map( left_input , 0, PeakHold_smoothedValue, 0, ledCountKr);

where did you get the variables? So I am looking for a AGC? Is the code that you put in part of it, or something attached to it. When you are talking about AGC, are you talking about smoothing as described here: http://arduino.cc/en/Tutorial/Smoothing

Thanks and looking forward to your reply.
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 21
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

You should average you levels.
Because if you sample sound you have a wave and you never know where you sampled the wave.
If you want a good responsive bargraph use something like this:

Code:
for (int num=0; num < 4; num++) {
sound[num]= analogRead (inputPIN);
if(num==3) {
soundav=(sound[0]+sound[1]+sound[2]+sound[3])/4; // average sound levels

So this part averaging every 4 samples it takes. Am I right? Moreover, will it quick enough to response to the music? Extremely sorry for not being able to understand. Providing a little more context will help me.

For you pot controle, use a lineaire pot meter a logarithmic will work to but won't give you a fine control.
You have to determine a max and minimum value. I choose between 15 and 25 because of your 20.
Code:
potControl = analogRead(potPIN);   // connect a linear potmeter on potPIN
levelControl = map(potControl, 0, 255,15,25); // you map the values of your pot to a more desired level between 15 and 25
left = left / levelControl;     // youre code

Use Magician his bargraph code, which is much better and more efficient.

If I do NOT go for a pot control, can you help me devise the code such that the calibration is logarithmic, instead of linear?
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 21
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Also, when you guys are talking about the bargraph, which portion of the code are you referring to: the one where the LEDs are toggled?
Logged

Montreal
Offline Offline
Edison Member
*
Karma: 23
Posts: 2486
Per aspera ad astra.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

In your arduino IDE open: File/Examples/Display->bargraph.
I copy/paste code from there. Play, and try to understand how it works.
There is a line in example sketch:
Code:
  // map the result to a range from 0 to the number of LEDs:
  int ledLevel = map(sensorReading, 0, 1023, 0, ledCount);
  which does the main function of the bargraph, it's scale numbers of lighting leds according to sensorReading value. The only issue with this, that a range from 0 to 1023 is hardcoded, and as it can't adapt itself to different music levels, it's not what you want. This is where AGC steps in.
Upper limit (1023) you should replace with another variable (peakHolder or whatever). Than, arduino should keep a track of the peak values (they call it envelope detector, wikipedia it), and change peaKHolder accordingly. One more things, to make it moves with different speed up and down, like real peak detector does, you would need second variable to store previous value, plus coefficient to regulate a falling speed.

 
Code:
int peakHolder, old_peakHolder;
 float falling_s = 0.95;   //<<<- closer to 1 slower

Than in main loop()

old_peakHolder = peakHolder;  //Save old data  for smoothing;
 
 peakHolder = analogRead(...);  //What is pin number
 
//Smoothing, so it fall down gradually. 
  if ( peakHolder  <  ( old_peakHolder * falling_s ) )
    peakHolder = ( old_peakHolder * falling_s );

  int ledLevel = map(sensorReading, 0, peakHolder, 0, ledCount);  // And here comes BarGraph
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 21
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

you rock magician! thanks a ton. will do it tomorrow. too sleepy now!
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 21
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I have a quick question:

When I am sampling the audio, does it need to come from a MONO audio jack, or can I get it from a stereo? If I am to get it from a stereo do I need 2 audio inputs, aka 2 analog inputs? I don't want to go for that. However, I have a female audio jack that is stereo. Will it matter if I put in a mono male jack in it?

I am really confused.
Logged

Montreal
Offline Offline
Edison Member
*
Karma: 23
Posts: 2486
Per aspera ad astra.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

It's up to you. Two channels stereo are independent, but very close in most audio program. They differ in phase, that isn't important for your project.
So you can take just one left channel, or connect two in parallel. Have you considered to build two channel display?
Btw, you need a DC offset for analog input,.
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 21
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Awesome. Fixed that part. Now I am facing a power issue. I have a few op amps that amplify my signal; however, I am just running of a +12V DC supply! How can I get the -V that is need for my opamp to work? Any ideas apart from going through a virtual ground, which is too complicated for me to understand atm?
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 21
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hey Guys,

So When I am running the following program:

 
int bassPins[] = { 3, 4, 5}; // Assign the pins for the leds
int treblePins[] = {6, 7, 8};
int bassInput = A1;  // left channel input
int trebleInput = A2;
int bass, treble, i;
  
void setup() {
  for (i = 0; i < 3; i++) { // Tell the arduino that the leds are digital outputs
    pinMode(bassPins, OUTPUT);
      pinMode(treblePins, OUTPUT);
    }
  Serial.begin(9600); // Uncomment to enable troubleshooting over serial.
  }
  
void loop() {
  //read in voltages
  bass = analogRead(bassInput);
  treble = analogRead(trebleInput);
  String b = "bass: ";
  String bout = "";
  bout = b + bass;
  String tout = "";
  String t = "treble: ";
  tout = t + treble;
  
  Serial.print(bout);
  Serial.print("\t");
  Serial.println(tout);
  
  bass = bass / 80;    // adjusts the sensitivity
  treble = treble/200;
  
  //bass
  if (bass == 0) { // if the volume is 0 then turn off all leds
    for(i = 0; i < 3; i++) {
      digitalWrite(bassPins, LOW);
      }
  }
  else {
    for (i = 0; i < bass; i++) { // turn on the leds up to the volume level
       digitalWrite(bassPins, HIGH);
      }
      
      for(i = i; i < 3; i++) {  // turn off the leds above the voltage level
        digitalWrite(bassPins, LOW);
       }
    }
    
  //treble
  if (treble == 0) { // if the volume is 0 then turn off all leds
    for(i = 0; i < 3; i++) {
      digitalWrite(treblePins, LOW);
    }
  }
  else {
    for (i = 0; i < treble; i++) {// turn on the leds up to the volume level
       digitalWrite(treblePins, HIGH);
      }
      for(i = i; i < 3; i++)  {// turn off the leds above the voltage level
        digitalWrite(treblePins, LOW);
       }
    }
  }

Even when there is nothing connected to A1 and A2, the serial monitor reads 1021 or 1023. Why? Is there a way to stop it?

Moreover, whenever the song ends or if I stop the song, all the LEDs are HIGH. I could not figure our why this will be the case.
« Last Edit: April 05, 2012, 09:11:37 pm by emceepaul » Logged

Pages: [1] 2   Go Up
Jump to: