1

I would like to monitor an audio stream and detect when it is "active" and music is playing or when it is "inactive" and is silent. What is a good way to measure when an audio stream changes state and continues to be "active?"

Each audio stream is amplified with this circuit.

Each audio stream has a different baseline voltage when it is not playing and "silent", but that voltage tends to drift slightly and change. This may be an artifact of my amp. I don't have an OScope and my multimeter is rubbish for low voltage so I can't quite pin down where the drift is coming from.

Currently, I'm using the method shown below, but that isn't as effectivewhen the audio stream is loud (e.g. has a compressed dynamic range).

Also, low volume tracks (e.g. classical music) tends have too low of a dynamic range to measure accurately and I get a lot of false "off" states.

#include <elapsedMillis.h>    //measure elapsed time

const int analogInPin = A0;   //Analog input pin - reads from amplifyer circuit
const int statusLight = 10;   //Digital output pin for indicator light
int sensorValueA = 0;         //Sensor reading now
int sensorValueB = 0;         //Previous sensor reading
int sensorDeltaA = 0;          //Change in sensor reading
int sensorDeltaB = 0;          //Record the last delta - useful for prefenting accidental triggering on start
int sensorThreshold = 50;      //Threshold in delta to trigger action
elapsedMillis turnOverTime;   //Timer for measuring the turnover between changes in sensor reading


void loop() {
  //measure the analog input 
  sensorValueA = analogRead(analogInPin);
  sensorDeltaA = abs(sensorValueA - sensorValueB);

 //check if the data is over the threshold and switch on
  if ((sensorDeltaA >= sensorThreshold) and (sensorDeltaB != 0)) {
    turnOverTime = 0;
    powerStateA = true;
  } 

  sensorValueB = sensorValueA;

  //check if no sensor data beyond the threshold has been returned within the delay window, switch off
  if (turnOverTime >= delayTime) {
    turnOverTime = 0;
    powerStateA = false;    
  }

  //if the powerstate changes act acordingly
  if (powerStateA != powerStateB) {
    digitalWrite(statusLight, powerStateA);  
  }
}
2
  • 1
    Sound like you need to AC couple the signal, and add a few MegaOhms resistor to ground on that.
    – Gerben
    Mar 11 '19 at 19:53
  • Where in the circuit would I want to pull to ground? Before or after the amp? And what is an AC couple? I'm pretty new to the audio handling scene and finding out exactly how little I know. Mar 11 '19 at 20:01
1

An audio signal consists of two main components:

  • An AC signal
  • A DC offset

What you are interested in is the AC signal, but not the DC offset. That DC offset is what is drifting in your current model.

So you need to AC couple the signal. That is, couple the signal to your Arduino in such a way that only the AC portion gets through.

The simplest way is with a capacitor.

But the Arduino can't handle negative voltages, and an AC signal has negative voltages. So you need to get rid of them.

The simplest way is to add a DC offset.

But didn't we just get rid of a DC offset that we didn't want? Yep. But that was an unknown DC offset. If we then add in a DC offset that we do know, and is stable, then we can work with it.

Here's a very simple circuit:

schematic

simulate this circuit – Schematic created using CircuitLab

The capacitor C1 removes the existing DC offset. R1 and R2 then form a voltage divider giving 2.5V (half of 5V), which is then added to the pure AC signal.

That means that the signal now varies around 2.5V. Silence is 2.5V, and noise is other values above and below that.

Since 2.5V is half of the 5V the Arduino runs on, silence should read at somewhere near half the ADC value. That's about 512. Sound will be both above and below that value.

So if you subtract 512 from your analog reading you will effectively be subtracting the DC offset you just added, giving you a value that can be either positive or negative.

For simplicity, since you don't care whether it's positive or negative, you can then remove the sign from the number to just give you a magnitude.

int audio = analogRead(A0); // Get a sample
audio = audio - 512; // Remove DC offset
audio = abs(audio); // Remove sign

You can now compare that to a threshold value:

if (audio > 100) {
    // do something
}
5
  • 2
    Aren't you making an assumption that the audio signal does not exceed +/- 2.5V? (That may well be a valid assumption).
    – Nick Gammon
    Mar 12 '19 at 5:39
  • This answer is exceptionally well described. Thank you! My audio source is coming from the RCA output of a Squeezebox and the headphone jack of a Chromecast Audio. I've measured (poorly, with a junk RadioShack multi meter) the voltage to be well under 1v. Will I need to send the audio stream through an amplifier first before using this circuit? Mar 12 '19 at 8:15
  • A standard line level output won't exceed about 1.5v.
    – Majenko
    Mar 12 '19 at 9:10
  • If I have two audio sources and I want to measure them independently would this still work? Wouldn't I end up putting two voltage dividers in parallel effectively making each branch of the divider 5K instead of 10K? Mar 12 '19 at 16:17
  • No, The dividers are separate entities. Each divider is still a pair of 10K resistors (20K in total). The total resistance across the power supply would halve (10K instead of 20K), but each divider knows nothing at all about that.
    – Majenko
    Mar 12 '19 at 16:18

Not the answer you're looking for? Browse other questions tagged or ask your own question.