Weird FFT results for DFPlayer audio signal

Hello,

I'm trying to analyze frequencies of a sound played by a DFPlayer module but I do not get expected results.

The DFPlayer works well and I can hear the sound playing.

I connected SPK1 output directly to A0 (I read it was a non-inverted mono sum with half-supply DC so no negative values) and I tried connecting different pins to the AREF : SPK2, 3.3v, 5v but results were always bad.

I play single frequency sinus wave sound to test my circuit : 5 differents wav files : 200Hz, 400Hz, 500Hz, 600Hz and 800Hz.
Results are wrong for each of them.

Here are some results for 500 Hz: upper line magnitude, bottom line frequencies


Computed magnitudes:
 159 |  174 |   40 |   26 |    9 |   29 |   26 |   76 |   42 |   38 |   11 |   46 |   27 |    9 |   29 |   39 |    8 |   14 |   16 |   44 |   17 |   21 |   40 |   24 |   17 |    6 |   21 |    9 |   11 |   17 |   19 |   25 | 
   0 |   62 |  125 |  187 |  250 |  312 |  375 |  437 |  500 |  562 |  625 |  687 |  750 |  812 |  875 |  937 | 1000 | 1062 | 1125 | 1187 | 1250 | 1312 | 1375 | 1437 | 1500 | 1562 | 1625 | 1687 | 1750 | 1812 | 1875 | 1937 | 
=====
Peak : 38.258132

For 800Hz


Computed magnitudes:
 212 |  300 |  236 |   59 |   34 |   83 |  145 |  197 |  157 |   61 |   14 |   33 |   31 |   14 |   14 |    3 |   11 |   24 |   51 |   50 |   19 |   30 |   30 |   14 |    4 |   20 |   29 |   26 |   20 |   40 |   54 |   48 | 
   0 |   62 |  125 |  187 |  250 |  312 |  375 |  437 |  500 |  562 |  625 |  687 |  750 |  812 |  875 |  937 | 1000 | 1062 | 1125 | 1187 | 1250 | 1312 | 1375 | 1437 | 1500 | 1562 | 1625 | 1687 | 1750 | 1812 | 1875 | 1937 | 
=====
Peak : 68.341506
==============================

And for 200Hz


Computed magnitudes:
 415 |  805 |  164 |   87 |   82 |   34 |  153 |  267 |  248 |   33 |   29 |   37 |   43 |   66 |   26 |   86 |   53 |   67 |   60 |   25 |   24 |   41 |   32 |   20 |   44 |   76 |   81 |   81 |   37 |   38 |   65 |   42 | 
   0 |   62 |  125 |  187 |  250 |  312 |  375 |  437 |  500 |  562 |  625 |  687 |  750 |  812 |  875 |  937 | 1000 | 1062 | 1125 | 1187 | 1250 | 1312 | 1375 | 1437 | 1500 | 1562 | 1625 | 1687 | 1750 | 1812 | 1875 | 1937 | 
=====
Peak : 55.758441
==============================

I tested the FFT with raw data that I generated with the sin function like in the arduinoFFT exemple. I get expected results so I think the FFT code is good and the problem comes from the audio signal but I don't see how, noise ? bad wiring ?

I tried with and without dcRemoval, I tried with and without windowing, it did not improve results.

Somehow modifying the volume has an impact but still even at maximum or minium I haven't convenient results.

Below is my code and a schema :



#include <arduinoFFT.h>
#include <SPI.h>


#define SAMPLES 64  //Must be a power of 2
#define xres 8      // Total number of  columns in the display, must be <= SAMPLES/2
#define yres 8      // Total number of  rows in the display

const uint16_t samples = SAMPLES;
double samplingFrequency = 4000;


void loop() {
 // ++ Sampling
  for (int
         i = 0;
       i < SAMPLES; i++) {
    while (!(ADCSRA & 0x10))
      ;                     // wait for ADC to complete current conversion ie ADIF bit set
    ADCSRA = 0b11110101;    // clear ADIF bit so that ADC can do next operation (0xf5)
    // int value = ADC - avgDC;  // Read from ADC and subtract DC offset caused value
    int value = ADC;  // Read from ADC and subtract DC offset caused value


    vReal[i] = value;  // Copy to bins after compressing

    vImag[i] = 0;
  }
  FFT.dcRemoval();
  FFT.windowing(FFTWindow::Hamming, FFTDirection::Forward);	
  FFT.compute(FFTDirection::Forward); 
  FFT.complexToMagnitude(); 
  
  Serial.println("Computed magnitudes:");
  PrintVector(vReal, (samples >> 1), SCL_FREQUENCY);
  double x = FFT.majorPeak();
  
  Serial.println("=====");
  Serial.print("Peak : ");
  Serial.println(x, 6);
  Serial.println("==============================");
  Serial.println();
  delay(1000);
}

Thank you for your help

The module uses a class D amplifier, which in turn uses high frequency (ultrasonic) PWM to drive the speaker. To get "clean" audio, use a fourth order or better low pass filter with upper frequency cutoff of a few kHz, and capacitive coupling to remove the DC component.

The DAC outputs will also need a bandpass filter, but the signal should be much cleaner than the speaker output.

1 Like

That diagram looks very wrong. You seem to be connecting the speaker directly to a DC voltage derived from the Vin voltage which is not defined. This will push out and damage the cone of the speaker. It needs to be at least AC coupled on both voltage and ground to keep from damaging things.

That is a none polerised series capacitor of about 1uF on the power and ground of the speaker.

@Grumpy_Mike :
I dont understand why you are saying that I connected the speaker directly to a DC voltage, I just connected it to the two SPK outputs of the DFPlayer and I derived SPK_1 output to the A0 pin.

@jremington :
I changed my circuit : I use the DAC_R output instead of SPK_1 and add a simple low pass, I think its cut frequency is 3kHz. On the serial spotter the signal looks better but I still don't have good results.

You can't expect much from a 1st order low pass filter. This is the response curve:

For informed help, post ALL of the code, explain how you generated the .wav files, and why you think the FFT results are "not good".

If possible, examine the audio output of the module with an oscilloscope and post a pic.

That diagram, I won't call it a schematic in any way, shows you have a DC connection to the speaker.
What is that you don't understand about that?

I can't put it more simply than that. You have a red wire from the SPK_2 DFPlayer direct to the speaker. You have a black wire from SPK1 to the speaker. These are both DC connections. The fact that the SPK_1 is also connected to A0 is totally irrelevant.

@jremington
Here are the results of the FFT for a 500Hz sinus wave sound and a fake sinus wave that I generated mathematicaly : upper line is magnitudes and bottom line is corresponding frequency. As you can see the major peak is not at 500Hz :

Real 500Hz wave sound :

Computed magnitudes:
   5 |   47 |    9 |    2 |    0 |    2 |    0 |    1 |    2 |    1 |    4 |    4 |    2 |    1 |    0 |    1 |    1 |    2 |    1 |    1 |    1 |    1 |    1 |    1 |    0 |    3 |    2 |    1 |    2 |    1 |    1 |    2 | 
   0 |   62 |  125 |  187 |  250 |  312 |  375 |  437 |  500 |  562 |  625 |  687 |  750 |  812 |  875 |  937 | 1000 | 1062 | 1125 | 1187 | 1250 | 1312 | 1375 | 1437 | 1500 | 1562 | 1625 | 1687 | 1750 | 1812 | 1875 | 1937 | 
=====
Peak : 64.890968

Fake 500Hz sinus :

Computed magnitudes:
   1 |    0 |    0 |    1 |    1 |    0 |    1 |   39 |   70 |   24 |    0 |    2 |    1 |    1 |    0 |    0 |    0 |    1 |    0 |    0 |    1 |    0 |    1 |    0 |    4 |    4 |    0 |    1 |    2 |    0 |    0 |    1 | 
   0 |   62 |  125 |  187 |  250 |  312 |  375 |  437 |  500 |  562 |  625 |  687 |  750 |  812 |  875 |  937 | 1000 | 1062 | 1125 | 1187 | 1250 | 1312 | 1375 | 1437 | 1500 | 1562 | 1625 | 1687 | 1750 | 1812 | 1875 | 1937 | 
=====
Peak : 501.838226

As you can see the results for the real sound is wrong.

Here is what gives me the serial plotter when I draw ADC sampling (blue curve) and a fake sinus wave that I mathematicaly generated for 500Hz (orange curve)

@Grumpy_Mike
This is the way I saw that it should be wired on several tutorials

Can you post a link to two or three of those please. There is a lot of rubbish out there on the web.

Please post ALL of the code.

Here is what gives me the serial plotter when I draw ADC sampling (blue curve) and a fake sinus wave that I mathematicaly generated for 500Hz (orange curve)

The bottom curve looks terrible (it is not a sinus curve), and the "ADC" curve is simply nonsense. I'm not surprised that the FFT is giving uninterpretable results.

Try this test on an Uno R3 or similar, as it gives the expected results with the OpenMusicLabs FFT code:

/*
 fft_test_sine
 example sketch for testing the OpenMusicLabs fft library.
 This generates a simple sine wave data set consisting
 of two frequencies f1 and f2, transforms it, calculates 
 and prints the amplitude of the transform.
 */

// do #defines BEFORE #includes
#define LIN_OUT 1 // use the linear output function
#define FFT_N 64 // set to 64 point fft

#include <FFT.h> // include the library

void setup() {
  Serial.begin(9600); // output on the serial port
  int i,k;
  float f1=2.0,f2=5.0;  //the two input frequencies (bin values)

  for (i = 0 ; i < FFT_N ; i++) { // create samples

    // amplitudes are 1000 for f1 and 500 for f2
    k=1000*sin(2*PI*f1*i/FFT_N)+500.*sin(2*PI*f2*i/FFT_N);

    fft_input[2*i] = k; // put real data into even bins
    fft_input[2*i+1] = 0; // set odd bins to 0
  }
  
//  fft_window();  //Try with and without this line, it smears

  fft_reorder(); // reorder the data before doing the fft
  fft_run(); // process the data using the fft
  fft_mag_lin(); // calculate the magnitude of the output

  // print the frequency index and amplitudes

  Serial.println("bin  amplitude");
  for (i=0; i<FFT_N/2; i++) {
    Serial.print(i);
    Serial.print("       ");
    Serial.println(2*fft_lin_out[i]); //*2 for "negative frequency" amplitude
  }
  Serial.println("Done");
}

void loop() {}

@Grumpy_Mike
Here is one on Youtube :

@jremington
These curves are not measured with an oscilloscope but with the serial plotter
The orange one is the sampling of a sinus here is the code I used to generate it (comes from the exemple1 of arduinoFFT library)
Below is the full code (I took off non related parts)

 double cycles = (((samples-1) * signalFrequency) / samplingFrequency);
   double sinus;
   sinus = int8_t((amplitude * (sin((i * (twoPi * cycles)) / samples))) / 2.0); // Build data with positive and negative values

     Serial.print("Sinus:");
     Serial.print(sinus);
     Serial.println();

#include <arduinoFFT.h>
#include <SPI.h>

#define SAMPLES 64  //Must be a power of 2
#define xres 8      // Total number of  columns in the display, must be <= SAMPLES/2
#define yres 8      // Total number of  rows in the display

// ++ FFT 
const uint16_t samples = SAMPLES;
double signalFrequency = 500;
double samplingFrequency = 4000;
uint8_t amplitude = 10;

// -- FFT 
/*
These are the input and output vectors
Input vectors receive computed results from FFT
*/

#define SCL_INDEX 0x00
#define SCL_TIME 0x01
#define SCL_FREQUENCY 0x02



double vReal[SAMPLES];
double vImag[SAMPLES];
double data_avgs[xres];

int avgDC = 655;
int yvalue;
int displaycolumn, displayvalue;
int peaks[xres];

ArduinoFFT<double> FFT = ArduinoFFT<double>(vReal, vImag, samples, samplingFrequency);  // FFT object


// -- Audio Spectrum variables


void setup() {

  // ++ ADC setup

  ADCSRA = 0b11100101;  // set ADC to free running mode and set pre-scalar to 32 (0xe5)
  ADMUX = 0b00000000;   // use pin A0 and external voltage reference
  //ADMUX = 0b10000000;   // use pin A0 and internal voltage reference
  delay(50);            // wait to get reference voltage stabilized
  // -- ADC setup
}




void loop() {
  

  // ++ ADC

  // ++ Sampling
  for (int
         i = 0;
       i < SAMPLES; i++) {
    while (!(ADCSRA & 0x10))
      ;                     // wait for ADC to complete current conversion ie ADIF bit set
    ADCSRA = 0b11110101;    // clear ADIF bit so that ADC can do next operation (0xf5)
    // int value = ADC - avgDC;  // Read from ADC and subtract DC offset caused value
    int value = ADC-450;  // Read from ADC and subtract DC offset caused value

  // Raw sinus
// For serial plotter
 double cycles = (((samples-1) * signalFrequency) / samplingFrequency);
   double sinus;
   sinus = int8_t((amplitude * (sin((i * (twoPi * cycles)) / samples))) / 2.0); // Build data with positive and negative values
    
     Serial.print("Sinus:");
     Serial.print(sinus);
     Serial.println();

     Serial.print("ADC:");
     Serial.print(value);
     Serial.println();
    

    vReal[i] = value;  // Copy to bins after compressing

    vImag[i] = 0;
  }

    // Build raw data 
    /*
  double cycles = (((samples-1) * signalFrequency) / samplingFrequency); //Number of signal cycles that the sampling will read
  for (uint16_t i = 0; i < samples; i++)
  {
    vReal[i] = int8_t((amplitude * (sin((i * (twoPi * cycles)) / samples))) / 2.0); // Build data with positive and negative values
  }
  */

  // -- Sampling

  // ++ FFT
  //FFT.Windowing(vReal, SAMPLES, FFT_WIN_TYP_HAMMING, FFT_FORWARD);

  // Print the results of the sampling according to time 
//  Serial.println("Data:");
//   PrintVector(vReal, samples, SCL_TIME);
  FFT.dcRemoval();
  FFT.windowing(FFTWindow::Hamming, FFTDirection::Forward);	
  // Serial.println("Weighed data:");
  // PrintVector(vReal, samples, SCL_TIME);
  FFT.compute(FFTDirection::Forward); 
  // Serial.println("Computed Real values:");
  // PrintVector(vReal, samples, SCL_INDEX);
  // Serial.println("Computed Imaginary values:");
  // PrintVector(vImag, samples, SCL_INDEX);
  FFT.complexToMagnitude(); 
  
  
  Serial.println("Computed magnitudes:");
  PrintVector(vReal, (samples >> 1), SCL_FREQUENCY);
  double x = FFT.majorPeak();
  
  Serial.println("=====");
  Serial.print("Peak : ");
  Serial.println(x, 6);
  Serial.println("==============================");
  Serial.println();
  delay(1000);  
  // -- FFT
}

Only part of the code, and insufficient to determine what you are doing incorrectly.

Good luck with your project.

I only took off the code for the music player commands and the display on a led matrix...

I was hoping for a link rather than a picture. It looks nothing like your picture anyway. But that doesn’t keep DC out of the speaker anyway.

As I said there is a lot of rubbish on the web.

I have been doing electronics since I was 14, I am 74 now so I do know what I am talking about.

I solved my problem. I will post the changes I made later.
@Grumpy_Mike
You can easily find the link with the video title on the screenshot.
If I connect 1uF capacitors in series with the speakers power and ground it does not produce any sound

it looks like you got 50 Hz AC from your wall outlet.

The DAC outputs are bipolar, so you need to bias them so they don't go negative. Also no filter is needed on the DAC outputs.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.