Playing Audio While Running FFT

steven32bits

Active member
Hello World!

I am trying to design a device that will measure the acoustics of a room. The concept behind it involves playing test tones such as pink noise and a logarithmic frequency sweep while simultaneously measuring the FFT of the tone played through a speaker. I have the teensy 3.2 audio tutorial breadboard kit. I have tried to accomplish this task in a couple of different ways.

First I tried playing the test tones through files saved on the SD while running the FFT. I tried merging two example codes "FFT" with "do_more_while_playing" and I couldn't get them to work properly. Obviously I had to modify the codes because the two example codes referenced different control for the AudioControlSGTL5000. So I stuck with one and replaced the instances from the other code. I got it to compile however when I uploaded it neither the tone played nor did the serial monitor print the FFT.

Next I tried Pete's (el_supremo's) tonesweep fix and I was able to merge this code semi successfully with the FFT example code. I can now get the frequency sweep to play and the microphone to read from the FFT from the line in input. My problem is I can't get them to do it at the same time. Currently with the code attached in this post they do the operations sequentially. The changes i made to merge the two example codes are mentioned in comments throughout the code.

I'm sure I just need to put them in a loop of some kind but I can't figure out the best way to do this. Any suggestions? :confused:

Code:
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>

const int myInput = AUDIO_INPUT_LINEIN;
//const int myInput = AUDIO_INPUT_MIC;

// Create the Audio components.  These should be created in the
// order data flows, inputs/sources -> processing -> outputs
//
AudioInputI2S          audioInput;         // audio shield: mic or line-in
AudioSynthWaveformSine sinewave;
AudioAnalyzeFFT1024    myFFT;

//commented this line to only have one output
//AudioOutputI2S         audioOutput;        // audio shield: headphones & line-out

// Connect either the live input or synthesized sine wave
AudioConnection patchCord1(audioInput, 0, myFFT, 0);
//AudioConnection patchCord1(sinewave, 0, myFFT, 0);


  /*
   * ///////////////////////////////////////////////////////////////////////////////
   *                 These next few lines are from Pete's tone sweep code
   */

// GUItool: begin automatically generated code
AudioSynthToneSweep      tonesweep1;     //xy=255,382
AudioOutputI2S           i2s1;           //xy=763,360
AudioConnection          patchCord5(tonesweep1, 0, i2s1, 0);
AudioConnection          patchCord6(tonesweep1, 0, i2s1, 1);
AudioControlSGTL5000     sgtl5000_1;     //xy=242,174
// GUItool: end automatically generated code

/*
 *                    Pete's Tone sweep lines end here for now
 * /////////////////////////////////////////////////////////////////////////////////
 * 
 */
  


//AudioControlSGTL5000 audioShield;

void setup() {
  // Audio connections require memory to work.  For more
  // detailed information, see the MemoryAndCpuUsage example

  
  //Changed this line to add the memory from Pete's tonesweep code so 
  //there is only one instance of this line
  AudioMemory(16);


  /*
   * ///////////////////////////////////////////////////////////////////////////////
   *                 These next few lines are from Pete's tone sweep code
   */
  while(!Serial);
  sgtl5000_1.enable();
  sgtl5000_1.volume(0.5);  Serial.begin(115200);

  //AudioMemory(4);
  AudioProcessorUsageMaxReset();
  AudioMemoryUsageMaxReset();
/*
 *                    Pete's Tone sweep lines end here for now
 * /////////////////////////////////////////////////////////////////////////////////
 * 
 */

//I had to comment these lines out to only have one instance
//also changed the line from audioShield.inputSelect(myInput) 
//to sgtl5000_1.inputSelect(myInput)

  // Enable the audio shield and set the output volume.
 // audioShield.enable();
  sgtl5000_1.inputSelect(myInput);
 // audioShield.volume(0.5);
  

  // Configure the window algorithm to use
  myFFT.windowFunction(AudioWindowHanning1024);
  //myFFT.windowFunction(NULL);

  // Create a synthetic sine wave, for testing
  // To use this, edit the connections above
  sinewave.amplitude(0.8);
  sinewave.frequency(1034.007);
}

void loop() {
  float n;
  int i;

  if (myFFT.available()) {
    // each time new FFT data is available
    // print it all to the Arduino Serial Monitor
    Serial.print("FFT: ");
    for (i=0; i<40; i++) {
      n = myFFT.read(i);
      if (n >= 0.01) {
        Serial.print(n);
        Serial.print(" ");
      } else {
        Serial.print("  -  "); // don't print "0.00"
      }
    }
    Serial.println();


  }


}
 
AudioSynthToneSweep might use too much CPU time?

I'd recommend use the noise or regular sine wave object. They're very efficient.
 
I am going to cover a spectrum of 10Hz to 22050Hz and a time interval of 30 seconds for the sweep. I think Paul is right about CPU usage. Would this method support the data I am trying to collect? If not would it be easier to run parallel teensies with shields to share data and run separate functions? I'm about to test this example code. Reading through the code it is seems to read the SD card and measure FFT, hopefully at the same time

*fingers crossed*

I used this example :: 1_Audio_Tutorial\Part_3_02_Fourier_Transform

Is that one of the above?
 
Before you go much further, I highly recommend carefully reading part 3-2 of the audio tutorial.

http://www.pjrc.com/store/audio_tutorial_kit.html

From what you've said so far, I'm guessing you're trying to build some sort of frequency response measurement system, where you play a single tone and analyze the frequencies received. Maybe?

If so, the good news is this is one of the very few applications where you can use FFT without a window. Maybe. If the response is non-linear, you could end up with frequencies that aren't harmonics of the test signal. But let's ignore that... Of course, you must use test frequencies that have an exact integer number of cycles per FFT. That concept is explained in the tutorial material.

You should also pay close attention to the timing, especially the overlapped output. If you ignore this, you could end up changing the test tone too soon, giving you an analysis consisting partially of the prior tone, partially of the new tone, and partially of a bunch of spectral leakage representing the discontinuity during the change between the two tones.

If using a 1024 point FFT, you'll probably at best be able to achieve a new measurement every 1536 samples. That's 1024 for the full FFT, and probably 512 of lost time where you changed frequencies. But if you're testing a large room, or even a moderately sized space, you might need to wait more than 512 samples before the prior tone fades from the reverberant field and the mic is only picking up a stable signal representing the response to the new tone.

But let's assume virtually no room echo, so you can achieve a new result every 1536 samples. At 44.1 kHz, that's just over 28 measurements per second. In 30 seconds, you'd manage to make about 860 measurements. Maybe that's good, or maybe you're imagining testing at every single FFT bin? (which would be kind of silly in the top 10-20 kHz octave)

You also mentioned "10Hz to 22050Hz", which prompts me to again suggest reading part 3-2 of the tutorial carefully. Pay close attention to the FFT bin frequencies.

Much of this might seem like bad news. But it'll be a lot less painful to learn the fundamental limitations of the algorithms early on, before you've gone down the path of building lots of stuff.
 
Where can I find these docs on the following example codes:

FFT
Part_3_02_Fourier_Transform
do_more_while_playing
tonesweep (Paul's fix if possible)
 
Where can I find these docs on the following example codes:

On this page:

http://www.pjrc.com/store/audio_tutorial_kit.html

Scroll down a bit and click the PDF. I recommend reading all 31 pages (and actually doing the tutorial), but if you're pressed for time, turn to page 24.

There's also a 48 minute video walkthrough of the whole tutorial. Like a video game walkthrough, it can show you everything quickly, but the only way to really gain the experience is by actually doing it. If you're pressed for time, skip forward to 34:56 in the video if you want to watch only the FFT portion.
 
If you're building a play-tone-and-capture-response type project (still guesswork), tonesweep is probably the last thing you need. It ramps the frequency continuously. How would you make any sense of the FFT data if the waveform changed frequency during the FFT's capture time? You almost certainly would want to change the tone right after a FFT data set, and keep it constant while you allow time for the signal and room acoustics to stabilize, and then capture again. That's the sort of control you do from the Arduino code by calling the library's functions at the times you want things done.
 
Yes Paul, most of this critical information (at least within pages 24-31) isn't new information as some of it is described in the audio tool. If I am doing my math correctly with FFT 1024 it is creating 512 bins (43Hz) apart measuring up to 22kHz but it only displays the first 30 bins (approx 1.5kHz). I am fine with these limitations and the window options (for now) hoping to find something in the future that will provide more accuracy for professional audio design. I will admit I love what you have here and can't wait for 3.6!

After analyzing the room acoustics the ultimate goal for me is to create multiple band-pass and stop band filter across the spectrum to create a correction filter for audio to play through. Like I asked earlier do you think it would be worth my time using two teensy + audio shields to run different operations?

Thank you for your help,

Steven
 
Last edited:
I got it to work. I just had to create my own loop in order to have them both function. I like being able to use the tonesweep because it allows for user interaction to change variables as opposed to playing a tonesweep file from the SD. The accuracy of the acoustic measurement is no where near a sophisticated audio tool like Smaart or Room EQ Wizard but it is however extremely inexpensive and portable. I can take my new tool to a gig and play sample tones. I will have my audio tools beside me to compare information.

Still curious if running two teensy's is possible. To have one slave to another and share a clock source?


Thank you,

Steven
 
Last edited:
Is it possible to display all 512 sampled bins? I would just need to display it one time. Or is this part of the limitations you are speaking of?
 
Thank you Frank! This is exactly what I needed. I have successfully merged the example code ''spectrumAnalyzerBasic'' with Pete's fix to the ''tonesweep'' example code.

On a separate note, I just purchased a Teensy 3.6 and I am curious if there will be an audio shield to come in the near future?


Thanks again Paul,

Steven
 
Hi Steve,
you can use the existing Audio Shield - it is compatible. It is a bit too short, ok, but electrically, it works.
 
No problem - I don't know what happens when the new library for the 3.6 gets released - but at least, until then, you can use SD.h, and it will use the slot on the Audio-shield.

Currently, there is no official supported lib that uses the Slot on the 3.6 -but i think that's only a matter of days/a few weeks.

Edit: maybe, both can be used ? they use different pins!
 
Last edited:
Back
Top