Browse Source

Add proper API to FFT objects

dds
PaulStoffregen 10 years ago
parent
commit
3422e984ac
6 changed files with 122 additions and 90 deletions
  1. +0
    -37
      analyze_fft1024.cpp
  2. +30
    -4
      analyze_fft1024.h
  3. +0
    -11
      analyze_fft256.cpp
  4. +32
    -6
      analyze_fft256.h
  5. +20
    -14
      examples/Analysis/FFT/FFT.ino
  6. +40
    -18
      gui/list.html

+ 0
- 37
analyze_fft1024.cpp View File

@@ -27,18 +27,7 @@
#include "analyze_fft1024.h"
#include "sqrt_integer.h"
#include "utility/dspinst.h"
#include "arm_math.h"

// TODO: this should be a class member, so more than one FFT can be used
static arm_cfft_radix4_instance_q15 fft_inst;

void AudioAnalyzeFFT1024::init(void)
{
// TODO: replace this with static const version
arm_cfft_radix4_init_q15(&fft_inst, 1024, 0, 1);
//state = 0;
//outputflag = false;
}

// 140312 - PAH - slightly faster copy
static void copy_to_fft_buffer(void *destination, const void *source)
@@ -133,32 +122,6 @@ void AudioAnalyzeFFT1024::update(void)
state = 4;
break;
}
/*
copy_to_fft_buffer(buffer, prevblock->data);
copy_to_fft_buffer(buffer+256, block->data);
if (window) apply_window_to_fft_buffer(buffer, window);
arm_cfft_radix4_q15(&fft_inst, buffer);
if (count == 0) {
for (int i=0; i < 128; i++) {
uint32_t tmp = *((uint32_t *)buffer + i);
uint32_t magsq = multiply_16tx16t_add_16bx16b(tmp, tmp);
sum[i] = magsq / naverage;
}
} else {
for (int i=0; i < 128; i++) {
uint32_t tmp = *((uint32_t *)buffer + i);
uint32_t magsq = multiply_16tx16t_add_16bx16b(tmp, tmp);
sum[i] += magsq / naverage;
}
}
if (++count == naverage) {
count = 0;
for (int i=0; i < 128; i++) {
output[i] = sqrt_uint32_approx(sum[i]);
}
outputflag = true;
}
*/
}



+ 30
- 4
analyze_fft1024.h View File

@@ -28,6 +28,7 @@
#define analyze_fft1024_h_

#include "AudioStream.h"
#include "arm_math.h"

// windows.c
extern "C" {
@@ -47,10 +48,10 @@ extern const int16_t AudioWindowTukey1024[];
class AudioAnalyzeFFT1024 : public AudioStream
{
public:
AudioAnalyzeFFT1024(uint8_t navg = 1, const int16_t *win = AudioWindowHanning1024)
: AudioStream(1, inputQueueArray), window(win),
state(0), outputflag(false) { init(); }
AudioAnalyzeFFT1024() : AudioStream(1, inputQueueArray),
window(AudioWindowHanning1024), state(0), outputflag(false) {
arm_cfft_radix4_init_q15(&fft_inst, 1024, 0, 1);
}
bool available() {
if (outputflag == true) {
outputflag = false;
@@ -58,6 +59,30 @@ public:
}
return false;
}
float read(unsigned int binNumber) {
if (binNumber > 511) return 0.0;
return (float)(output[binNumber]) * (1.0 / 16384.0);
}
float read(unsigned int binFirst, unsigned int binLast) {
if (binFirst > binLast) {
unsigned int tmp = binLast;
binLast = binFirst;
binFirst = tmp;
}
if (binFirst > 511) return 0.0;
if (binLast > 511) binLast = 511;
uint32_t sum = 0;
do {
sum += output[binFirst++];
} while (binFirst < binLast);
return (float)sum * (1.0 / 16384.0);
}
void averageTogether(uint8_t n) {
// not implemented yet (may never be, 86 Hz output rate is ok)
}
void windowFunction(const int16_t *w) {
window = w;
}
virtual void update(void);
uint16_t output[512] __attribute__ ((aligned (4)));
private:
@@ -71,6 +96,7 @@ private:
//uint8_t naverage;
volatile bool outputflag;
audio_block_t *inputQueueArray[1];
arm_cfft_radix4_instance_q15 fft_inst;
};

#endif

+ 0
- 11
analyze_fft256.cpp View File

@@ -27,18 +27,7 @@
#include "analyze_fft256.h"
#include "sqrt_integer.h"
#include "utility/dspinst.h"
#include "arm_math.h"

// TODO: this should be a class member, so more than one FFT can be used
static arm_cfft_radix4_instance_q15 fft_inst;

void AudioAnalyzeFFT256::init(void)
{
// TODO: replace this with static const version
arm_cfft_radix4_init_q15(&fft_inst, 256, 0, 1);
//state = 0;
//outputflag = false;
}

// 140312 - PAH - slightly faster copy
static void copy_to_fft_buffer(void *destination, const void *source)

+ 32
- 6
analyze_fft256.h View File

@@ -28,6 +28,7 @@
#define analyze_fft256_h_

#include "AudioStream.h"
#include "arm_math.h"

// windows.c
extern "C" {
@@ -47,10 +48,11 @@ extern const int16_t AudioWindowTukey256[];
class AudioAnalyzeFFT256 : public AudioStream
{
public:
AudioAnalyzeFFT256(uint8_t navg = 8, const int16_t *win = AudioWindowHanning256)
: AudioStream(1, inputQueueArray), window(win),
prevblock(NULL), count(0), naverage(navg), outputflag(false) { init(); }

AudioAnalyzeFFT256() : AudioStream(1, inputQueueArray),
window(AudioWindowHanning256), prevblock(NULL), count(0),
naverage(8), outputflag(false) {
arm_cfft_radix4_init_q15(&fft_inst, 256, 0, 1);
}
bool available() {
if (outputflag == true) {
outputflag = false;
@@ -58,11 +60,34 @@ public:
}
return false;
}
float read(unsigned int binNumber) {
if (binNumber > 127) return 0.0;
return (float)(output[binNumber]) * (1.0 / 16384.0);
}
float read(unsigned int binFirst, unsigned int binLast) {
if (binFirst > binLast) {
unsigned int tmp = binLast;
binLast = binFirst;
binFirst = tmp;
}
if (binFirst > 127) return 0.0;
if (binLast > 127) binLast = 127;
uint32_t sum = 0;
do {
sum += output[binFirst++];
} while (binFirst < binLast);
return (float)sum * (1.0 / 16384.0);
}
void averageTogether(uint8_t n) {
if (n == 0) n == 1;
naverage = n;
}
void windowFunction(const int16_t *w) {
window = w;
}
virtual void update(void);
//uint32_t cycles;
uint16_t output[128] __attribute__ ((aligned (4)));
private:
void init(void);
const int16_t *window;
audio_block_t *prevblock;
int16_t buffer[512] __attribute__ ((aligned (4)));
@@ -71,6 +96,7 @@ private:
uint8_t naverage;
bool outputflag;
audio_block_t *inputQueueArray[1];
arm_cfft_radix4_instance_q15 fft_inst;
};

#endif

+ 20
- 14
examples/Analysis/FFT/FFT.ino View File

@@ -9,21 +9,17 @@ const int myInput = AUDIO_INPUT_LINEIN;
// 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
AudioAnalyzeFFT256 myFFT(20);
AudioOutputI2S audioOutput; // audio shield: headphones & line-out
AudioInputI2S audioInput; // audio shield: mic or line-in
AudioSynthWaveformSine sinewave;
AudioAnalyzeFFT1024 myFFT;
AudioOutputI2S audioOutput; // audio shield: headphones & line-out

// Create Audio connections between the components
//
AudioConnection c1(audioInput, 0, audioOutput, 0);
AudioConnection c2(audioInput, 0, myFFT, 0);
AudioConnection c3(audioInput, 1, audioOutput, 1);
// Connect either the live input or synthesized sine wave
AudioConnection patchCord1(audioInput, 0, myFFT, 0);
//AudioConnection patchCord1(sinewave, 0, myFFT, 0);

// Create an object to control the audio shield.
//
AudioControlSGTL5000 audioShield;


void setup() {
// Audio connections require memory to work. For more
// detailed information, see the MemoryAndCpuUsage example
@@ -33,6 +29,15 @@ void setup() {
audioShield.enable();
audioShield.inputSelect(myInput);
audioShield.volume(0.6);

// 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() {
@@ -40,9 +45,10 @@ void loop() {
// each time new FFT data is available
// print it all to the Arduino Serial Monitor
Serial.print("FFT: ");
for (int i=0; i<128; i++) {
Serial.print(myFFT.output[i]);
Serial.print(",");
for (int i=0; i<40; i++) {
Serial.print(myFFT.read(i));
//Serial.print(myFFT.output[i]);
Serial.print(" ");
}
Serial.println();
}

+ 40
- 18
gui/list.html View File

@@ -1512,23 +1512,35 @@
<tr class=top><th>Port</th><th>Purpose</th></tr>
<tr class=odd><td align=center>In 0</td><td>Signal to convert to frequency bins</td></tr>
</table>
<h3>Parameters</h3>
<table class=doc align=center cellpadding=3>
<tr class=top><th>Name</th><th>Type</th><th>Function</th></tr>
<tr class=odd><td align=center>Num</td><td>Integer</td><td># FFTs to average</td></tr>
<tr class=odd><td align=center>Window</td><td>Array</td><td>Window method to use</td></tr>
</table>
<p>Extra description... Section only present if object has params</p>
<h3>Functions</h3>
<p class=func><span class=keyword>available</span>();</p>
<p class=desc>Returns true each time the FFT analysis produces new output data.
</p>
<p class=func><span class=keyword>read</span>(binNumber);</p>
<p class=desc>Read a single frequency bin, from 0 to 127. The result is scaled
so 1.0 represents a full scale sine wave.
</p>
<p class=func><span class=keyword>read</span>(firstBin, lastBin);</p>
<p class=desc>Read several frequency bins, returning their sum. The higher
audio octaves are represented by many bins, which are typically read
as a group for audio visualization.
</p>
<p class=func><span class=keyword>averageTogether</span>(number);</p>
<p class=desc>New data is produced very radidly, approximately 344 times
per second. Multiple outputs can be averaged together, so available()
returns true at a slower rate.
</p>
<p class=func><span class=keyword>windowFunction</span>(window);</p>
<p class=desc>Set the window function to be used. AudioWindowHanning256
is the default. Windowing may be disabled by NULL, but windowing
should be used for all non-periodic (music) signals, and all periodic
signals that are not exact integer division of the sample rate.
</p>

<h3>Notes</h3>
<p>The raw 16 bit output data bins may be access with myFFT.output[num], where
num is 0 to 127.</p>
<p>TODO: caveats about spectral leakage vs frequency precision for arbitrary signals</p>
<p>Known bug in the library limits to only a single instance of this object.
This bug will be fixed someday... allowing 2 or more FFTs to run on simultaneously.</p>
<p>Window Types:
<ul>
<li><span class=literal>AudioWindowHanning256</span> (default)</li>
@@ -1574,24 +1586,34 @@
<tr class=top><th>Port</th><th>Purpose</th></tr>
<tr class=odd><td align=center>In 0</td><td>Signal to convert to frequency bins</td></tr>
</table>
<h3>Parameters</h3>
<table class=doc align=center cellpadding=3>
<tr class=top><th>Name</th><th>Type</th><th>Function</th></tr>
<tr class=odd><td align=center>Num</td><td>Integer</td><td># FFTs to average</td></tr>
<tr class=odd><td align=center>Window</td><td>Array</td><td>Window method to use</td></tr>
</table>
<p>Extra description... Section only present if object has params</p>
<h3>Functions</h3>
<p class=func><span class=keyword>available</span>();</p>
<p class=desc>Returns true each time the FFT analysis produces new output data.
</p>
<p class=func><span class=keyword>read</span>(binNumber);</p>
<p class=desc>Read a single frequency bin, from 0 to 511. The result is scaled
so 1.0 represents a full scale sine wave.
</p>
<p class=func><span class=keyword>read</span>(firstBin, lastBin);</p>
<p class=desc>Read several frequency bins, returning their sum. The higher
audio octaves are represented by many bins, which are typically read
as a group for audio visualization.
</p>
<p class=func><span class=keyword>averageTogether</span>(number);</p>
<p class=desc>This function does nothing. The 1024 point FFT always
updates at approximately 86 times per second.
</p>
<p class=func><span class=keyword>windowFunction</span>(window);</p>
<p class=desc>Set the window function to be used. AudioWindowHanning1024
is the default. Windowing may be disabled by NULL, but windowing
should be used for all non-periodic (music) signals, and all periodic
signals that are not exact integer division of the sample rate.
</p>
<h3>Notes</h3>
<p>1024 point FFT uses approx 50% of the CPU power on Teensy 3.1</p>
<p>The raw 16 bit output data bins may be access with myFFT.output[num], where
num is 0 to 511.</p>
<p>TODO: caveats about spectral leakage vs frequency precision for arbitrary signals</p>
<p>Known bug in the library limits to only a single instance of this object.
This bug will be fixed someday... but 2 instances may not run due to CPU limitation</p>
<p>Window Types:
<ul>
<li><span class=literal>AudioWindowHanning1024</span> (default)</li>

Loading…
Cancel
Save