| @@ -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; | |||
| } | |||
| */ | |||
| } | |||
| @@ -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 | |||
| @@ -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) | |||
| @@ -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 | |||
| @@ -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(); | |||
| } | |||
| @@ -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> | |||