Calculating around DC offset

Hi,

I have been trying to find a solution but it seems like everybody is doing something else with the DC offset.

When there is no audio signal I have a constant value of 127. If I connect a jack into the circuit with let say sine wave I get 180 and 74. What I need is to calculate output signal that would read 0 when input is 127 and 54 and 54 when input is 180 adn 74 and so on up to 255 and 0 (incoming signal) to 255 (output).

Can somebody please help me calculate it or just send me on the right track? I have only read about RMS or solutions that are not exactly what I need.

Any help is greatly appreciated.

What I need is to calculate output signal that would read 0 when input is 127 and 54 and 54 when input is 180 adn 74 and so on up to 255 and 0 (incoming signal) to 255 (output).

Typos aside, this makes no sense. If you have one input, you have one output. I don't know why you think one input should provide two outputs.

Audio is a bipolar signal. It has a positive and a negative half. Rather than process a sign bit, we usually use two’s-complement to represent the signal. But, a DAC sometimes gives an unsigned range. All you need to do to convert the unsigned with an offset, to a signed with no offset, is subtract (for 8 bits in this case) 128. It also must then be stored in a signed data type.

PaulS:
Typos aside, this makes no sense. If you have one input, you have one output. I don’t know why you think one input should provide two outputs.

Sorry if Im being unclear. I meant that the audio signal oscilates around zero so if I have sine wave it goes above and below zero of the same value.

aarg:
Audio is a bipolar signal. It has a positive and a negative half. Rather than process a sign bit, we usually use two’s-complement to represent the signal. But, a DAC sometimes gives an unsigned range. All you need to do to convert the unsigned with an offset, to a signed with no offset, is subtract (for 8 bits in this case) 128. It also must then be stored in a signed data type.

Hi, sorry but could you expand on this a little bit? I am using someone elses code that has sampling rate of ~40kHz.

//variable to store incoming audio sample
byte incomingAudio;

//clipping indicator variables
boolean clipping = 0;

void setup(){
    Serial.begin(9600);
  pinMode(13,OUTPUT);//led indicator pin
  
  cli();//disable interrupts
  
  //set up continuous sampling of analog pin 0
  
  //clear ADCSRA and ADCSRB registers
  ADCSRA = 0;
  ADCSRB = 0;
  
  ADMUX |= (1 << REFS0); //set reference voltage
  ADMUX |= (1 << ADLAR); //left align the ADC value- so we can read highest 8 bits from ADCH register only
  
  ADCSRA |= (1 << ADPS2) | (1 << ADPS0); //set ADC clock with 32 prescaler- 16mHz/32=500kHz
  ADCSRA |= (1 << ADATE); //enabble auto trigger
  ADCSRA |= (1 << ADIE); //enable interrupts when measurement complete
  ADCSRA |= (1 << ADEN); //enable ADC
  ADCSRA |= (1 << ADSC); //start ADC measurements
  
  sei();//enable interrupts

  //if you want to add other things to setup(), do it here

}

ISR(ADC_vect) {//when new ADC value ready
  incomingAudio = ADCH;//store 8 bit value from analog pin 0
  if (incomingAudio == 0 || incomingAudio == 255){//if clipping
    digitalWrite(13,HIGH);//set pin 13 high
    clipping = 1;//currently clipping
  }
}

void loop(){

       Serial.print(incomingAudio);
    Serial.println(" amp");
    
  if (clipping){//if currently clipping
    clipping = 0;//
    digitalWrite(13,LOW);//turn off clipping led indicator (pin 13)

  
  }

  delay(100);
}

I am not really sure how to transfer your suggestion into this since the science behing bypassing of the function analogRead() is beyond my comprehension.

Thank you both for you suggestions!

The real problem now, is that you haven't explained your need clearly enough. What are you calculating and why?

Hi sorry about that, english isnt my native language. I would like that 127 value to be 0 when there is no signal and then higher the amplitude the bigger the value from 0 to 255.

So basically VU meter that goes from 0 to 255.

A VU meter doesn't just reproduce audio values. A meter that moves at audio speeds (up to 20Khz) would appear to the eye as a blur. It also doesn't display negative values. Instead, it shows the averaged, absolute value of the signal.

Yes indeed. That is exactly what I need. Though I dont know wthat exactly averaged is in this case but absolute values as quickly as the signal changes. I am sorry I dont really know how to explain it any further without repeating myself.

Perhaps an example: I have music playing or maybe just a droning sound that oscilates in volume. I want to represent this oscilation with LED. So as the sound is geting stronger the LED shines brighter and vice versa. If I was to play complex music it would just react asi quickly as possible.

I hope this makes it a little bit clearer.

EDIT: Also good example is this https://www.youtube.com/watch?v=iQKTrYrdmhM but he just takes the values and switches LEDS on and off.

The samples you're getting can be treated as offset, 2's complement signed values. However, the byte data type is unsigned. To treat these values properly, you have to subtract 128 from them, and cast to a char (which is a signed type).

For example: The input value is 133... 133-128= +5. The input value is 111... 111-128= -17.

To average you have to take the absolute values first: abs(+5) = 5. abs(-17) = 17

Now the average: (17+5)/2 = 11

11 is the VU reading (well, at least to start things off). You have to average all the values in the data stream, not just two. There are different methods for doing it.

Hi, thank you, thats perfect. The only problem I have now is, how can you decide two values to calculate? Woudnt there be hundreds of values generated in the + zone and hundreds in -? in other words woundt your method work only with peaks?

That being said, would just this suffice:

amp = (incomingAudio - 127)
amp2 = abs(amp)

?

EDIT: I tried that and it doesnt work.

JaneDawwon: Hi, thank you, thats perfect. The only problem I have now is, how can you decide two values to calculate? Woudnt there be hundreds of values generated in the + zone and hundreds in -? in other words woundt your method work only with peaks?

That being said, would just this suffice:

amp = (incomingAudio - 127) amp2 = abs(amp)

?

As I said, "you have to average all the values in the data stream". I'm refining an answer to the math question.

Do you literally mean a VU meter? That is a type of meter with very specific attack/decay characteristics based on the dynamics of the signal. https://en.wikipedia.org/wiki/VU_meter

KeithRB:
Do you literally mean a VU meter? That is a type of meter with very specific attack/decay characteristics based on the dynamics of the signal.
VU meter - Wikipedia

Hello, basically yes. I just want to record and use immediate signal level for other purposes as precisely as I can but now anything will do. I just want to be atleast close.

aarg:
As I said, “you have to average all the values in the data stream”. I’m refining an answer to the math question.

Thank you for your response though after googling both averaging and values in the data stream its still a complete mystery what you mean. I understand the averaging but all I could find is how to average certain number of input data.

Here is a demo:

// VU demo
// simulate an input sine wave and take the average of the absolute values
// 2016-01-12 aarg in Arduino forum

const int numSamples = 64;
const int DCoffset = 128;
const int peakAmplitude = 100;
int accumulator;

void setup()
{
  Serial.begin(9600);
  delay(100);
  Serial.println("VU meter:");

  accumulator = 0; // clear for averaging

  // simulated input sine wave with DC offset
  for (int i = 0; i < numSamples; i++)
  {
    byte sample = sin(i / 256.0 * PI * 8) * peakAmplitude + DCoffset;
    Serial.print("sample:");
    Serial.print(sample);
    Serial.print(" value:");
    Serial.print((int)(sample - DCoffset));
    Serial.println();

    accumulator += abs(sample - DCoffset);
  }

  int average = accumulator / numSamples;
  Serial.println("**********");
  Serial.print("VU reading:");
  Serial.println(average);
}


void loop()
{
}

Hi thank you for finding this for me! So basically just smoothing with number of samples clearly defined ahead. I thought that " all the values in the data stream" means something changing based on the frequency or something like that.

Well I am trying it implement now so I will let you know how that went.

So now I tried with the help of the tutorial on smooting but only value I get now is “1”.

//Audio in with 38.5kHz sampling rate, interrupts, and clipping indicator
//by Amanda Ghassaei
//http://www.instructables.com/id/Arduino-Audio-Input/
//Sept 2012

/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
*/

//variable to store incoming audio sample
byte incomingAudio;
int amp;
int led = 9;
boolean clipping = 0;
[i]
const int numReadings = 10;
int readings[numReadings];      // the readings from the analog input
int readIndex = 0;              // the index of the current reading
int total = 0;                  // the running total
int average = 0;                // the average
[/i]
void setup(){
    Serial.begin(9600);
    [i]  for (int thisReading = 0; thisReading < numReadings; thisReading++) {
    readings[thisReading] = 0;[/i]
      }
  pinMode(13,OUTPUT);//led indicator pin
    pinMode(led,OUTPUT);//led indicator pin
  cli();//disable interrupts
  
  //set up continuous sampling of analog pin 0
  
  //clear ADCSRA and ADCSRB registers
  ADCSRA = 0;
  ADCSRB = 0;
  
  ADMUX |= (1 << REFS0); //set reference voltage
  ADMUX |= (1 << ADLAR); //left align the ADC value- so we can read highest 8 bits from ADCH register only
  
  ADCSRA |= (1 << ADPS2) | (1 << ADPS0); //set ADC clock with 32 prescaler- 16mHz/32=500kHz
  ADCSRA |= (1 << ADATE); //enabble auto trigger
  ADCSRA |= (1 << ADIE); //enable interrupts when measurement complete
  ADCSRA |= (1 << ADEN); //enable ADC
  ADCSRA |= (1 << ADSC); //start ADC measurements
  
  sei();//enable interrupts

  //if you want to add other things to setup(), do it here

}

ISR(ADC_vect) {//when new ADC value ready
 [B] incomingAudio[/B] = ADCH;//store 8 bit value from analog pin 0
  if (incomingAudio == 0 || incomingAudio == 255){//if clipping
    digitalWrite(13,HIGH);//set pin 13 high
    clipping = 1;//currently clipping
  }
}

void loop(){
[B]amp = abs(incomingAudio - 128);[/B]
[i]
  // subtract the last reading:
  total = total - readings[readIndex];
  // read from the sensor:
  readings[readIndex] = [B]amp[/B];
  // add the reading to the total:
  total = total + readings[readIndex];
  // advance to the next position in the array:
  readIndex = readIndex + 1;

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

  // calculate the average:
  average = total / numReadings;
  // send it to the computer as ASCII digits
  Serial.println(average);
[/I]
 [B] analogWrite(led, average);[/B]
  


    
  if (clipping){//if currently clipping
    clipping = 0;//
    digitalWrite(13,LOW);//turn off clipping led indicator (pin 13)

  
  }

  delay(100);
}

any idea what could be wrong?

EDIT: It actually does work! Just a bad connection. I am going to play around it a little bit and see if everything is as I hoped for but for now: THANK YOU VERY MUCH!