ERIS CORE
eris_analyze_fft1024.h
Go to the documentation of this file.
1 /* Audio Library for Teensy 3.X
2  * Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com
3  *
4  * Development of this audio library was funded by PJRC.COM, LLC by sales of
5  * Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop
6  * open source software by purchasing Teensy or other PJRC products.
7  *
8  * Permission is hereby granted, free of charge, to any person obtaining a copy
9  * of this software and associated documentation files (the "Software"), to deal
10  * in the Software without restriction, including without limitation the rights
11  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12  * copies of the Software, and to permit persons to whom the Software is
13  * furnished to do so, subject to the following conditions:
14  *
15  * The above copyright notice, development funding notice, and this permission
16  * notice shall be included in all copies or substantial portions of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24  * THE SOFTWARE.
25  */
26 
27 /**
28  * @file eris_analyze_fft1024.h
29  * @author Brian Monkaba (brian.monkaba@gmail.com)
30  * @brief
31  * @version 0.1
32  * @date 2021-08-24
33  *
34  * @copyright portions Copyright (c) 2021
35  *
36  */
37 
38 
39 
40 #ifndef erisanalyze_fft1024_h_
41 #define erisanalyze_fft1024_h_
42 
43 #include "Arduino.h"
44 #include "AudioStream.h"
45 #include "arm_math.h"
46 #include "analyze_fft1024.h"
47 #include <stdlib.h>
48 #include "data_windows_f32.h"
49 
50 #define ENABLE_F32_FFT
51 
53 
54 
55 typedef struct FFTReadRangeStruct{
56 
57  float startFrequency; // Provided by the caller
58  float stopFrequency; // Provided by the caller
59 
60  //uint32_t peakFFTResult; //real and imag packed data can be used to calc phase
63  float peakValue;
64  float avgValueFast; //used to calc moving average convergence / divergence (MACD)
65  float avgValueSlow; //by comparing a short and long moving average; slow transient detection
66  float transientValue; //difference between the peak and fast peak values
67  float phase;
68  uint16_t cqtBin; // Provided by the caller
69  uint16_t startBin;
70  uint16_t stopBin;
71  uint16_t peakBin;
72 } FFTReadRange __attribute__ ((aligned(32)));;
73 
74 
76 {
77 public:
79  sample_block(0), outputflag(false ) {
80  #ifdef ENABLE_F32_FFT
81  arm_cfft_radix4_init_f32(&fft_inst,1024, 0, 1);
82  window_f32 = AudioWindowBlackman1024_f32;//AudioWindowHamming1024_f32;//AudioWindowHamming1024_f32;//AudioWindowBlackman1024_f32;//AudioWindowKaiser12_1024_f32;//
83  window = NULL;
84  #else
85  arm_cfft_radix4_init_q15(&fft_inst, 1024, 0, 1);
86  window_f32 = NULL;
87  window = AudioWindowHanning1024;
88  #endif
89  short_name="fft1024";
90  unum_inputs=1;
91  unum_outputs=0;
92  category="analyze-function";
93  enabled = false;
94  outputflag = false;
95  is_analyzed = true; //default state of true prevents potential analysis without available data on startup
96  sample_block=0;
98  MEM_STEP = 0x010;
99  subsample_by = 4;
100  BLOCKS_PER_FFT = (1024 / AUDIO_BLOCK_SAMPLES) * subsample_by;
102  subsample_lowfreqrange = 16;//ratio should be 2:1; bw is fc of the low range
103  subsample_highfreqrange = 8;// l,
104  ssr = SS_HIGHFREQ;
105  //memset(&output_packed,0,sizeof(uint32_t)*512);
106  memset(&buffer,0,sizeof(int16_t)*2048);
107  #ifdef ENABLE_F32_FFT
108  //memset(&output,0,sizeof(float32_t)*1024);
109  arm_fill_f32(0,(float32_t*)output,1024);
110  //memset(&tmp_buffer,0,sizeof(float32_t)*2048);
111  arm_fill_f32(0,(float32_t*)tmp_buffer,2048);
112  arm_fill_f32(0,(float32_t*)phase,512);
113  #else
114  memset(&output,0,sizeof(uint16_t)*512);
115  memset(&tmp_buffer,0,sizeof(int16_t)*2048);
116  #endif
117  }
118  //FAT Audio
119  void reset(){
120  sample_block=0;
121  SAMPLING_INDEX=0;
122  };
123 
124  void enableFFT(bool enable_state){
125  reset();
126  enabled = enable_state;
127  }
128 
129  void configSubsample(uint16_t subsample,subsample_range range){
130  //subsampling provides more resolution in the lower frequency range
131  //...in exchange for lower bandwidth
132  //values between 16 (high range) & 48 (low range) are suitable for guitar
133  if (range == SS_LOWFREQ) subsample_lowfreqrange = subsample;
134  else if (range == SS_HIGHFREQ) subsample_highfreqrange = subsample;
135  outputflag = false;
136  reset();
137  }
138 
140  ssr = range;
141  outputflag = false;
142  reset();
143  }
144 
146  if (ssr == SS_LOWFREQ) ssr = SS_HIGHFREQ;
147  else if (ssr == SS_HIGHFREQ) ssr = SS_LOWFREQ;
148  outputflag = false;
149  reset();
150  }
151 
152  bool available() {
153  if (outputflag == true) {
154  outputflag = false;
155  return true;
156  }
157  return false;
158  }
159  float read(unsigned int binNumber) {
160  if (binNumber > 511) return 0.0;
161  #ifdef ENABLE_F32_FFT
162  return output[binNumber];
163  #else
164  return (float)(output[binNumber]) * (1.0 / 16384.0);
165  #endif
166  }
167  float read(unsigned int binFirst, unsigned int binLast,FFTReadRange *fftRR = NULL) {
168  float32_t maxf = 0;
169  float32_t powerf = 0;
170  uint32_t peak_index;
171  uint32_t span;
172 
173  span = binLast - binFirst;
174  if(span<1) return 0.0;
175 
176  if (binFirst > binLast) {
177  unsigned int tmp = binLast;
178  binLast = binFirst;
179  binFirst = tmp;
180  }
181  if(fftRR){
182  fftRR->peakBin = 0;
183  fftRR->peakFrequency = 0;
184  fftRR->peakValue = 0;
185  }
186  if (binFirst > 510) return 0.0;
187  if (binLast > 511) binLast = 511;
188  arm_power_f32((float32_t*)&output[binFirst], span, &powerf);
189  arm_max_f32((float32_t*)&output[binFirst], span, &maxf, &peak_index);
190  if(fftRR){
191  arm_sqrt_f32((powerf)/(32768.0/subsample_by),&fftRR->peakValue);
192  fftRR->peakBin = peak_index + binFirst;
193  if(fftRR->phase < phase[fftRR->peakBin]){
194  fftRR->phase = phase[fftRR->peakBin];
195  }else fftRR->phase = phase[fftRR->peakBin];
196  }
197  return maxf;
198  }
199  float read(FFTReadRange *fftRR){
200  return read(fftRR->startFrequency, fftRR->stopFrequency, fftRR);
201  }
202 
203  float read(float freq_from, float freq_to, FFTReadRange *fftRR = NULL) {
204  //convert f to bin
205  float bw;
206  float bin_size;
207  unsigned int start_bin;
208  unsigned int stop_bin;
209 
210  bw = AUDIO_SAMPLE_RATE_EXACT/2.0/(float)subsample_by;
211  bin_size = bw/1024.0f;
212  start_bin = (unsigned int)(freq_from / bin_size);
213  stop_bin = (unsigned int)(freq_to / bin_size);
214  if (start_bin > 511 || stop_bin > 511){
215  start_bin = 0;
216  stop_bin = 0;
217  }
218  /*
219  Serial.print(F("fft read: "));
220  Serial.print(subsample_by);Serial.print(F(","));
221  Serial.print(bw);Serial.print(F(","));
222  Serial.print(freq_from);Serial.print(F(","));
223  Serial.print(freq_to);Serial.print(F(","));
224  Serial.print(bin_size);Serial.print(F(","));
225  Serial.print(start_bin);Serial.print(F(","));
226  Serial.println(stop_bin);
227  */
228  if(fftRR){
229  fftRR->startBin = start_bin;
230  fftRR->stopBin = stop_bin;
231  fftRR->startFrequency = freq_from;
232  fftRR->stopFrequency = freq_to;
233  }
234 
235  if (freq_to > bw){
236  if(fftRR){
237  int16_t cqtBin;
238  cqtBin = fftRR->cqtBin;
239  //memset(&fftRR,0,sizeof(FFTReadRange));
240  fftRR->cqtBin = cqtBin;
241  fftRR->peakBin=0;
242  fftRR->estimatedFrequency=0;
243  fftRR->peakFrequency=0;
244  fftRR->peakValue=0;
245  fftRR->avgValueFast=0; //used to calc moving average convergence / divergence (MACD)
246  fftRR->avgValueSlow=0; //by comparing a short and long moving average; slow transient detection
247  fftRR->transientValue=0; //difference between the peak and fast peak values
248  }
249  return 0;
250  }
251 
252  float rval = read(start_bin,stop_bin,fftRR);
253  if(fftRR){
254 
255  //from the peak bin calc the freq
256  fftRR->peakFrequency = (fftRR->peakBin * bin_size) - (bin_size/2.0); //center of the fft bin
257  if (fftRR->peakFrequency < 0) fftRR->peakFrequency = 0.01;
258  float ratio = 1;
259  if ((fftRR->peakBin > 1) && (fftRR->peakBin < 510)){
260  //from the balance of the side lobes, estimate the actual frequency
261  float lobeFrequency = 1;
262  if ((output[fftRR->peakBin+1]-output[fftRR->peakBin-1]) > 0.1 && (output[fftRR->peakBin] > 0)){
263  //pos lobe
264  ratio = (output[fftRR->peakBin+1] - output[fftRR->peakBin-1] + output[fftRR->peakBin+1]) / (1.0 * output[fftRR->peakBin]);
265  lobeFrequency = ((fftRR->peakBin+1) * bin_size) - bin_size/2;
266  } else if (output[fftRR->peakBin-1] > 0.1 && (output[fftRR->peakBin] > 0)){
267  //neg lobe
268  ratio = (output[fftRR->peakBin-1]-output[fftRR->peakBin+1] + output[fftRR->peakBin-1]) / (1.0 * output[fftRR->peakBin]);
269  lobeFrequency = ((fftRR->peakBin-1) * bin_size) - bin_size/2;
270  }
271  fftRR->estimatedFrequency = (fftRR->peakFrequency * (1-ratio)) + (lobeFrequency * ratio);
272  //clamp estimate
273  if (fftRR->estimatedFrequency > fftRR->stopFrequency)fftRR->estimatedFrequency = fftRR->stopFrequency;
274  if (fftRR->estimatedFrequency < fftRR->startFrequency)fftRR->estimatedFrequency = fftRR->startFrequency;
275  }
276  if (ssr == SS_HIGHFREQ) ratio = 0.5;
277  else ratio = 0.995;
278  fftRR->avgValueFast = (fftRR->avgValueFast * ratio) + (fftRR->peakValue * (1.0f -ratio)); //used to calc moving average convergence / divergence (MACD)
279  //fftRR->avgValueFast = fftRR->peakValue;
280  ratio = 0.285;
281  fftRR->avgValueSlow = (fftRR->avgValueSlow * ratio) + (fftRR->avgValueFast * (1.0f -ratio)); //by comparing a short and long moving average; slow transient detection
282  if(fftRR->peakValue > fftRR->avgValueFast) fftRR->avgValueFast = (fftRR->avgValueFast/7.0 )+(fftRR->peakValue/3.0);
283  if (fftRR->avgValueFast > 0){
284  fftRR->transientValue = (fftRR->transientValue + (fftRR->avgValueFast - fftRR->avgValueSlow)/(0.00001 + fftRR->avgValueFast))* 0.5;
285  } else fftRR->transientValue = 0;
286  fftRR->transientValue *= 0.95;
287  fftRR->avgValueFast *= 0.998;
288  fftRR->avgValueSlow *= 0.998;
289  }
290 
291  return rval;
292  }
293 
294  //comparison function used for qsort of FFTReadRange arrays (used for finding cqt peaks)
295  static int compare_fftrr_value(const void *p, const void *q) {
296  if (((const FFTReadRange *)p)->avgValueFast > ((const FFTReadRange *)q)->avgValueFast) return -1;
297  return 1;
298  }
299 
300  static int compare_fftrr_cqt_bin(const void *p, const void *q) {
301  if (((const FFTReadRange *)p)->cqtBin <= ((const FFTReadRange *)q)->cqtBin) return 1;
302  return -1;
303  }
304 
305  static void sort_fftrr_by_value(FFTReadRange *a, size_t n) {
306  //helper function used to sort FFTReadRange arrays (used for CQT)
307  qsort(a, n, sizeof(*a), compare_fftrr_value);
308  }
309 
310  static void sort_fftrr_by_cqt_bin(FFTReadRange *a, size_t n) {
311  //helper function used to sort FFTReadRange arrays (used for CQT)
312  qsort(a, n, sizeof(*a), compare_fftrr_cqt_bin);
313  }
314 
315  void averageTogether(uint8_t n) {
316  // not implemented yet (may never be, 86 Hz output rate is ok)
317  }
318  void windowFunction(const int16_t *w) {
319  window = w;
320  }
322  //takes the energy of the side lobes for any bins above a threshold
323  for (uint16_t i = 1; i < 1024 - 1; i++){
324  if ((output[i-1] < output[i])&&(output[i] > output[i+1])){
325  output[i] += output[i-1] + output[i+1];
326  output[i-1] = 0;
327  output[i+1] = 0;
328 
329  }
330  }
331  }
332  bool capture(void);
333  void analyze(void);
334 
335  virtual void update(void);
336  //uint32_t output_packed[512] __attribute__ ((aligned (4))); //16bit real and imag packed data
337 private:
338  void init(void);
339  void copy_to_fft_buffer(void *destination, const void *source,int subsample);
340  const int16_t *window;
341  const float32_t *window_f32;
342 public:
343  static const char* short_name_lookup;
344  uint16_t sample_block;
345  bool enabled; //FAT Audio
346  uint16_t MEM_STEP;
349  uint16_t BLOCKS_PER_FFT;
354  volatile float32_t output[1024] __attribute__ ((aligned (4)));
355 private:
356  volatile bool outputflag;
357  volatile bool is_analyzed = false;
359  #ifdef ENABLE_F32_FFT
360  arm_cfft_radix4_instance_f32 fft_inst;
361  volatile float32_t tmp_buffer[2048] __attribute__ ((aligned (4)));
362  float32_t phase[512] __attribute__ ((aligned (4)));
363  #else
364  arm_cfft_radix4_instance_q15 fft_inst;
365  int16_t tmp_buffer[2048] __attribute__ ((aligned (4)));
366  uint16_t output[512] __attribute__ ((aligned (4)));
367  #endif
368  int16_t buffer[2048] __attribute__ ((aligned (2)));
369 };
370 
371 #endif
const char PROGMEM p[][16]
Definition: Eris.h:247
uint8_t unum_outputs
Definition: AudioStream.h:187
uint8_t unum_inputs
Definition: AudioStream.h:186
const char * short_name
Definition: AudioStream.h:184
const char * category
Definition: AudioStream.h:185
void windowFunction(const int16_t *w)
float read(float freq_from, float freq_to, FFTReadRange *fftRR=NULL)
arm_cfft_radix4_instance_q15 fft_inst
void copy_to_fft_buffer(void *destination, const void *source, int subsample)
static int compare_fftrr_value(const void *p, const void *q)
int16_t buffer[2048] __attribute__((aligned(2)))
volatile float32_t output[1024] __attribute__((aligned(4)))
void enableFFT(bool enable_state)
int16_t tmp_buffer[2048] __attribute__((aligned(4)))
float32_t phase[512] __attribute__((aligned(4)))
static void sort_fftrr_by_cqt_bin(FFTReadRange *a, size_t n)
static int compare_fftrr_cqt_bin(const void *p, const void *q)
float read(unsigned int binFirst, unsigned int binLast, FFTReadRange *fftRR=NULL)
static void sort_fftrr_by_value(FFTReadRange *a, size_t n)
audio_block_t * inputQueueArray[1]
arm_cfft_radix4_instance_f32 fft_inst
float read(unsigned int binNumber)
void configSubsample(uint16_t subsample, subsample_range range)
void setActiveRange(subsample_range range)
volatile float32_t tmp_buffer[2048] __attribute__((aligned(4)))
static const char * short_name_lookup
float read(FFTReadRange *fftRR)
uint16_t output[512] __attribute__((aligned(4)))
float phase
erisAudioAnalyzeFFT1024 __attribute__
uint16_t cqtBin
float avgValueFast
subsample_range
@ SS_LOWFREQ
@ SS_HIGHFREQ