Included averaging in analyze_fft1024_F32 (real input)

pull/6/merge
boblark 3 years ago
parent 822701e4a4
commit 319fa42626
  1. 82
      analyze_fft1024_F32.cpp
  2. 79
      analyze_fft1024_F32.h
  3. 68
      examples/TestFFT1024/TestFFT1024.ino

@ -31,15 +31,15 @@
#include <Arduino.h> #include <Arduino.h>
#include "analyze_fft1024_F32.h" #include "analyze_fft1024_F32.h"
// #include "utility/dspinst.h"
// Move audio data from an audio_block_f32_t to the FFT instance buffer. // Move audio data from an audio_block_f32_t to the FFT instance buffer.
// This is for 128 numbers per block
static void copy_to_fft_buffer(void *destination, const void *source) static void copy_to_fft_buffer(void *destination, const void *source)
{ {
const float *src = (const float *)source; const float *src = (const float *)source;
float *dst = (float *)destination; float *dst = (float *)destination;
for (int i=0; i < AUDIO_BLOCK_SAMPLES; i++) { for (int i=0; i < 128; i++) {
*dst++ = *src++; // real sample *dst++ = *src++; // real sample
*dst++ = 0.0f; // 0 for Imag *dst++ = 0.0f; // 0 for Imag
} }
@ -51,17 +51,18 @@ static void apply_window_to_fft_buffer(void *buffer, const void *window)
const float *win = (float *)window; const float *win = (float *)window;
for (int i=0; i < 1024; i++) for (int i=0; i < 1024; i++)
buf[2*i] *= *win++; buf[2*i] *= *win++;
} }
void AudioAnalyzeFFT1024_F32::update(void) void AudioAnalyzeFFT1024_F32::update(void) {
{
audio_block_f32_t *block; audio_block_f32_t *block;
block = receiveReadOnly_f32(); uint32_t tt;
block = AudioStream_F32::receiveReadOnly_f32();
if (!block) return; if (!block) return;
// What all does 7EM cover?? // tt=micros();
#if defined(__ARM_ARCH_7EM__)
switch (state) { switch (state) {
case 0: case 0:
blocklist[0] = block; blocklist[0] = block;
@ -93,44 +94,69 @@ void AudioAnalyzeFFT1024_F32::update(void)
break; break;
case 7: case 7:
blocklist[7] = block; blocklist[7] = block;
// We have 4 previous blocks pointed to by blocklist[]:
copy_to_fft_buffer(fft_buffer+0x000, blocklist[0]->data); copy_to_fft_buffer(fft_buffer+0x000, blocklist[0]->data);
copy_to_fft_buffer(fft_buffer+0x100, blocklist[1]->data); copy_to_fft_buffer(fft_buffer+0x100, blocklist[1]->data);
copy_to_fft_buffer(fft_buffer+0x200, blocklist[2]->data); copy_to_fft_buffer(fft_buffer+0x200, blocklist[2]->data);
copy_to_fft_buffer(fft_buffer+0x300, blocklist[3]->data); copy_to_fft_buffer(fft_buffer+0x300, blocklist[3]->data);
// and 4 new blocks, just gathered:
copy_to_fft_buffer(fft_buffer+0x400, blocklist[4]->data); copy_to_fft_buffer(fft_buffer+0x400, blocklist[4]->data);
copy_to_fft_buffer(fft_buffer+0x500, blocklist[5]->data); copy_to_fft_buffer(fft_buffer+0x500, blocklist[5]->data);
copy_to_fft_buffer(fft_buffer+0x600, blocklist[6]->data); copy_to_fft_buffer(fft_buffer+0x600, blocklist[6]->data);
copy_to_fft_buffer(fft_buffer+0x700, blocklist[7]->data); copy_to_fft_buffer(fft_buffer+0x700, blocklist[7]->data);
if (pWin) if (pWin)
apply_window_to_fft_buffer(fft_buffer, window); apply_window_to_fft_buffer(fft_buffer, window);
#if defined(__IMXRT1062__)
// Teensyduino core for T4.x supports arm_cfft_f32
// arm_cfft_f32 (const arm_cfft_instance_f32 *S, float32_t *p1, uint8_t ifftFlag, uint8_t bitReverseFlag)
arm_cfft_f32 (&Sfft, fft_buffer, 0, 1);
#else
// For T3.x go back to old (deprecated) style
arm_cfft_radix4_f32(&fft_inst, fft_buffer); arm_cfft_radix4_f32(&fft_inst, fft_buffer);
#endif
count++; // Next do non-coherent averaging
for (int i=0; i < 512; i++) { for (int i=0; i < 512; i++) {
float magsq = fft_buffer[2*i]*fft_buffer[2*i] + fft_buffer[2*i+1]*fft_buffer[2*i+1]; float magsq = fft_buffer[2*i]*fft_buffer[2*i] + fft_buffer[2*i+1]*fft_buffer[2*i+1];
if(outputType==FFT_RMS) if(count==1) // Starting new average
output[i] = sqrtf(magsq); sumsq[i] = magsq;
else if(outputType==FFT_POWER) else if (count<=nAverage) // Adding on to average
output[i] = magsq; sumsq[i] += magsq;
else if(outputType==FFT_DBFS) }
output[i] = 10.0f*log10f(magsq)-54.1854f; // Scaled to FS sine wave if (count >= nAverage) { // Average is finished
else count = 0;
output[i] = 0.0f; float inAf = 1.0f/(float)nAverage;
} for(int i=0; i<512; i++) {
outputflag = true; if(outputType==FFT_RMS)
release(blocklist[0]); output[i] = sqrtf(inAf*sumsq[i]);
release(blocklist[1]); else if(outputType==FFT_POWER)
release(blocklist[2]); output[i] = inAf*sumsq[i];
release(blocklist[3]); else if(outputType==FFT_DBFS) {
if(sumsq[i]>0.0f)
output[i] = 10.0f*log10f(inAf*sumsq[i])-54.1854f; // Scaled to FS sine wave
else
output[i] = -193.0f; // lsb for 23 bit mantissa
}
else
output[i] = 0.0f;
} // End, set output[i] over all 512
outputflag = true;
} // End of average is finished
AudioStream_F32::release(blocklist[0]);
AudioStream_F32::release(blocklist[1]);
AudioStream_F32::release(blocklist[2]);
AudioStream_F32::release(blocklist[3]);
blocklist[0] = blocklist[4]; blocklist[0] = blocklist[4];
blocklist[1] = blocklist[5]; blocklist[1] = blocklist[5];
blocklist[2] = blocklist[6]; blocklist[2] = blocklist[6];
blocklist[3] = blocklist[7]; blocklist[3] = blocklist[7];
state = 4; state = 4;
break; break;
} } // End switch(state)
#else // Serial.print("uSec: "); Serial.println(micros()-tt);
release(block); } // End update()
#endif
}

@ -32,7 +32,7 @@
* Power or dBFS (full scale). Output can be bin by bin or a pointer to * Power or dBFS (full scale). Output can be bin by bin or a pointer to
* the output array is available. Several window functions are provided by * the output array is available. Several window functions are provided by
* in-class design, or a custom window can be provided from the INO. * in-class design, or a custom window can be provided from the INO.
* *
* Functions (See comments below and #defines above: * Functions (See comments below and #defines above:
* bool available() * bool available()
* float read(unsigned int binNumber) * float read(unsigned int binNumber)
@ -43,14 +43,15 @@
* float* getWindow(void) * float* getWindow(void)
* void putWindow(float *pwin) * void putWindow(float *pwin)
* void setOutputType(int _type) * void setOutputType(int _type)
* * void setNAverage(int nAverage)
*
* Timing, max is longest update() time: * Timing, max is longest update() time:
* T3.6 Windowed, RMS out, 1016 uSec max * T3.6 Windowed, Power Out, 975 uSec
* T3.6 Windowed, Power Out, 975 uSec max * T3.6 Windowed, RMS out, 1016 uSec
* T3.6 Windowed, dBFS out, 1591 uSec max * T3.6 Windowed, dBFS out, 1591 uSec
* No Window saves 60 uSec on T3.6 for any output. * No Window saves 60 uSec on T3.6 for any output.
* T4.0 Windowed, RMS Out, 149 uSec * T4.0 Windowed, Ave=1, Power Out, 156 uSec
* * T4.0 Windowed, Ave=1, dBFS Out, 302 uSec
* Scaling: * Scaling:
* Full scale for floating point DSP is a nebulous concept. Normally the * Full scale for floating point DSP is a nebulous concept. Normally the
* full scale is -1.0 to +1.0. This is an unscaled FFT and for a sine * full scale is -1.0 to +1.0. This is an unscaled FFT and for a sine
@ -62,7 +63,7 @@
* no matter how it is scaled, but this factor needs to be considered * no matter how it is scaled, but this factor needs to be considered
* when building the INO. * when building the INO.
*/ */
// Fixed float/int problem in read(first, last). RSL 3 Mar 21 // Fixed float/int problem in read(first, last). RSL 3 Mar 21
#ifndef analyze_fft1024_F32_h_ #ifndef analyze_fft1024_F32_h_
#define analyze_fft1024_F32_h_ #define analyze_fft1024_F32_h_
@ -71,6 +72,9 @@
#include "AudioStream_F32.h" #include "AudioStream_F32.h"
#include "arm_math.h" #include "arm_math.h"
#include "mathDSP_F32.h" #include "mathDSP_F32.h"
#if defined(__IMXRT1062__)
#include "arm_const_structs.h"
#endif
#define FFT_RMS 0 #define FFT_RMS 0
#define FFT_POWER 1 #define FFT_POWER 1
@ -84,25 +88,37 @@
class AudioAnalyzeFFT1024_F32 : public AudioStream_F32 { class AudioAnalyzeFFT1024_F32 : public AudioStream_F32 {
//GUI: inputs:1, outputs:0 //this line used for automatic generation of GUI node //GUI: inputs:1, outputs:0 //this line used for automatic generation of GUI node
//GUI: shortName:AnalyzeFFT1024 //GUI: shortName:FFT1024
public: public:
AudioAnalyzeFFT1024_F32() : AudioStream_F32(1, inputQueueArray) { AudioAnalyzeFFT1024_F32() : AudioStream_F32(1, inputQueueArray) {
arm_cfft_radix4_init_f32(&fft_inst, 1024, 0, 1); // __MK20DX128__ T_LC; __MKL26Z64__ T3.0; __MK20DX256__T3.1 and T3.2
useHanningWindow(); // Revisit this for more flexibility <<<<< // __MK64FX512__) T3.5; __MK66FX1M0__ T3.6; __IMXRT1062__ T4.0 and T4.1
#if defined(__IMXRT1062__)
// Teensy4 core library has the right files for new FFT
// arm CMSIS library has predefined structures of type arm_cfft_instance_f32
Sfft = arm_cfft_sR_f32_len1024; // This is one of the structures
#else
arm_cfft_radix4_init_f32(&fft_inst, 1024, 0, 1); // for T3.x
#endif
// This is always 128 block size. Any sample rate. No use of "setings"
// Bob: Revisit this to use CMSIS fast fft in place of cfft. faster?
useHanningWindow();
} }
// Inform that the output is available for read()
bool available() { bool available() {
if (outputflag == true) { if (outputflag == true) {
outputflag = false; outputflag = false;
return true; return true;
} }
return false; return false;
} }
// Output data from a single bin is transferred
float read(unsigned int binNumber) { float read(unsigned int binNumber) {
if (binNumber>511 || binNumber<0) return 0.0; if (binNumber>511 || binNumber<0) return 0.0;
return output[binNumber]; return output[binNumber];
} }
// Return sum of several bins. Normally use with power output. // Return sum of several bins. Normally use with power output.
// This produces the equivalent of bigger bins. // This produces the equivalent of bigger bins.
@ -147,8 +163,8 @@ public:
return 0; return 0;
} }
// Fast pointer transfer. Be aware that the data will go away // Fast pointer transfer. Be aware that the data will go away
// after the next 512 data points occur. // after the next 512 data points occur.
float* getData(void) { float* getData(void) {
return output; return output;
} }
@ -164,16 +180,22 @@ public:
for(int i=0; i<1024; i++) for(int i=0; i<1024; i++)
*p++ = *pwin++; *p++ = *pwin++;
} }
// Output RMS (default) Power or dBFS // Output RMS (default) Power or dBFS
void setOutputType(int _type) { void setOutputType(int _type) {
outputType = _type; outputType = _type;
} }
// Output power (non-coherent) averaging
void setNAverage(int _nAverage) {
nAverage = _nAverage;
}
virtual void update(void); virtual void update(void);
private: private:
float output[512]; float output[512];
float sumsq[512];
float window[1024]; float window[1024];
float *pWin = window; float *pWin = window;
audio_block_f32_t *blocklist[8]; audio_block_f32_t *blocklist[8];
@ -181,8 +203,16 @@ private:
uint8_t state = 0; uint8_t state = 0;
bool outputflag = false; bool outputflag = false;
audio_block_f32_t *inputQueueArray[1]; audio_block_f32_t *inputQueueArray[1];
arm_cfft_radix4_instance_f32 fft_inst; #if defined(__IMXRT1062__)
// For T4.x
// const static arm_cfft_instance_f32 arm_cfft_sR_f32_len1024;
arm_cfft_instance_f32 Sfft;
#else
arm_cfft_radix4_instance_f32 fft_inst;
#endif
int outputType = FFT_RMS; //Same type as I16 version init int outputType = FFT_RMS; //Same type as I16 version init
int nAverage = 1;
int count = 0; // used to average for nAverage of powers
// The Hann window is a good all-around window // The Hann window is a good all-around window
void useHanningWindow(void) { void useHanningWindow(void) {
@ -199,8 +229,8 @@ private:
float kx = 0.006141921; // 2*PI/1023 float kx = 0.006141921; // 2*PI/1023
int ix = (float) i; int ix = (float) i;
window[i] = 0.35875 - window[i] = 0.35875 -
0.48829*cosf( kx*ix) + 0.48829*cosf( kx*ix) +
0.14128*cosf(2.0f*kx*ix) - 0.14128*cosf(2.0f*kx*ix) -
0.01168*cosf(3.0f*kx*ix); 0.01168*cosf(3.0f*kx*ix);
} }
} }
@ -228,8 +258,7 @@ private:
xn2 = 0.00000382215877f*xn2*xn2; xn2 = 0.00000382215877f*xn2*xn2;
window[511 - n]=kbes*(mathEqualizer.i0f(beta*sqrtf(1.0-xn2))); window[511 - n]=kbes*(mathEqualizer.i0f(beta*sqrtf(1.0-xn2)));
window[512 + n] = window[511 - n]; window[512 + n] = window[511 - n];
}
} }
} };
};
#endif #endif

@ -5,10 +5,11 @@
// on audio connected to the Left Line-In pin. By changing code, // on audio connected to the Left Line-In pin. By changing code,
// a synthetic sine wave can be input instead. // a synthetic sine wave can be input instead.
// //
// The first 40 (of 512) frequency analysis bins are printed to // The power output from 512 frequency analysis bins are printed to
// the Arduino Serial Monitor. // the Arduino Serial Monitor. The format is selectable.
// Output power averaging is an option
// //
// T4.0: Uses 6.1% processor and 9 F32 memory blocks, both max. // T4.0: Uses 11.5% processor and 9 F32 memory blocks, both max.
// //
// This example code is in the public domain. // This example code is in the public domain.
@ -25,13 +26,12 @@ const int myInput = AUDIO_INPUT_LINEIN;
AudioSynthSineCosine_F32 sinewave; AudioSynthSineCosine_F32 sinewave;
AudioAnalyzeFFT1024_F32 myFFT; AudioAnalyzeFFT1024_F32 myFFT;
AudioOutputI2S_F32 audioOutput; // audio shield: headphones & line-out NU AudioOutputI2S_F32 audioOutput; // audio shield: headphones & line-out NU
// Connect either the live input or synthesized sine wave // Connect either the live input or synthesized sine wave
// AudioConnection_F32 patchCord1(audioInput, 0, myFFT, 0); // AudioConnection_F32 patchCord1(audioInput, 0, myFFT, 0);
AudioConnection_F32 patchCord1(sinewave, 0, myFFT, 0); AudioConnection_F32 patchCord1(sinewave, 0, myFFT, 0);
AudioControlSGTL5000 audioShield;
AudioControlSGTL5000 audioShield; uint32_t ct = 0;
uint32_t count = 0; uint32_t count = 0;
void setup() { void setup() {
@ -45,50 +45,52 @@ void setup() {
audioShield.inputSelect(myInput); audioShield.inputSelect(myInput);
audioShield.volume(0.5); audioShield.volume(0.5);
// Create a synthetic sine wave, for testing
// To use this, edit the connections above
// sinewave.frequency(1033.99f); // Bin 24 T3.x
// sinewave.frequency(1033.59375f); // Bin 24 T4.x at 44100
// sinewave.frequency(1055.0f); // Bin 24.5, demonstrates windowing
sinewave.frequency(1076.0f);
sinewave.amplitude(1.0f);
// Set windowing function // Set windowing function
// myFFT.windowFunction(NULL); // myFFT.windowFunction(AudioWindowNone);
// myFFT.windowFunction(AudioWindowNone); // Same as NULL
// myFFT.windowFunction(AudioWindowHanning1024); // default // myFFT.windowFunction(AudioWindowHanning1024); // default
// The next Kaiser window needs a dB peak sidelobe number // The next Kaiser window needs a dB peak sidelobe number
myFFT.windowFunction(AudioWindowKaiser1024, 70.0f); // myFFT.windowFunction(AudioWindowKaiser1024, 70.0f);
// myFFT.windowFunction(AudioWindowBlackmanHarris1024); myFFT.windowFunction(AudioWindowBlackmanHarris1024);
// To print the window function: // To print the window function:
// float* pw=myFFT.getWindow(); // float* pw=myFFT.getWindow();
// for(int jj=0; jj<1024; jj++) // for(int jj=0; jj<1024; jj++)
// Serial.println(*pw++, 6); // Serial.println(*pw++, 6);
// Create a synthetic sine wave, for testing myFFT.setNAverage(1);
// To use this, edit the connections above
sinewave.frequency(1034.0); // Bin 24
// sinewave.frequency(1055.0f); // Bin 24.5, demonstrates windowing
myFFT.setOutputType(FFT_DBFS); // FFT_RMS or FFT_POWER or FFT_DBFS myFFT.setOutputType(FFT_DBFS); // FFT_RMS or FFT_POWER or FFT_DBFS
} }
void loop() { void loop() {
if (myFFT.available()) { if (myFFT.available() /*&& ++ct == 4*/ ) {
// each time new FFT data is available // each time new FFT data is available
// print it all to the Arduino Serial Monitor // print it all to the Arduino Serial Monitor
Serial.print("FFT: "); Serial.println("FFT Output: ");
for (int i=0; i<40; i++) { for (int i=0; i<512; i++) {
Serial.print(myFFT.read(i), 2); Serial.print(i);
Serial.print(","); Serial.print(",");
} Serial.println(myFFT.read(i), 3);
}
Serial.println(); Serial.println();
} }
/* if(count++ == 3000) { /*
Serial.print("CPU: Percent Usage, Max: "); if(count++<200) {
Serial.print(AudioProcessorUsage()); Serial.print("CPU: Max Percent Usage: ");
Serial.print(", ");
Serial.println(AudioProcessorUsageMax()); Serial.println(AudioProcessorUsageMax());
Serial.print(" Float 32 Memory: "); Serial.print(" Max Float 32 Memory: ");
Serial.print(AudioMemoryUsage_F32());
Serial.print(", ");
Serial.println(AudioMemoryUsageMax_F32()); Serial.println(AudioMemoryUsageMax_F32());
} }
delay(2); */
*/ delay(500);
} }

Loading…
Cancel
Save